mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(slack): hash token cache keys
This commit is contained in:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Slack/messages: serialize write-client requests and whole outbound sends per target so rapid multi-message Slack replies preserve send order. Fixes #69101. (#69105) Thanks @nightq and @ztexydt-cqh.
|
||||
- Slack/messages: keep Slack bot tokens out of internal message-ordering and DM cache keys.
|
||||
- Slack/exec approvals: resolve native approval button clicks over the Gateway instead of delivering `/approve ...` as plain agent text, preserving retry buttons if Gateway resolution fails. Fixes #71023. (#71025) Thanks @marusan03.
|
||||
- Browser/tool: expose browser doctor diagnostics to agents and extend `openclaw doctor` browser readiness notes for managed Chromium launch prerequisites. (#62948, #62936) Thanks @seanc-dev.
|
||||
- Slack/files: return non-image `download-file` results as local file paths instead of image payloads, and include Slack file IDs in inbound file placeholders so agents can call `download-file`. Fixes #71212. Thanks @teamrazo.
|
||||
|
||||
@@ -14,6 +14,7 @@ vi.mock("@slack/web-api", () => {
|
||||
|
||||
let createSlackWebClient: typeof import("./client.js").createSlackWebClient;
|
||||
let createSlackWriteClient: typeof import("./client.js").createSlackWriteClient;
|
||||
let createSlackTokenCacheKey: typeof import("./client.js").createSlackTokenCacheKey;
|
||||
let getSlackWriteClient: typeof import("./client.js").getSlackWriteClient;
|
||||
let clearSlackWriteClientCacheForTest: typeof import("./client.js").clearSlackWriteClientCacheForTest;
|
||||
let resolveSlackWebClientOptions: typeof import("./client.js").resolveSlackWebClientOptions;
|
||||
@@ -27,6 +28,7 @@ beforeAll(async () => {
|
||||
({
|
||||
createSlackWebClient,
|
||||
createSlackWriteClient,
|
||||
createSlackTokenCacheKey,
|
||||
getSlackWriteClient,
|
||||
clearSlackWriteClientCacheForTest,
|
||||
resolveSlackWebClientOptions,
|
||||
@@ -121,6 +123,17 @@ describe("slack web client config", () => {
|
||||
expect(second).not.toBe(first);
|
||||
expect(WebClient).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("builds stable non-secret token cache keys", () => {
|
||||
const token = "xoxb-sensitive-token";
|
||||
const first = createSlackTokenCacheKey(token);
|
||||
const second = createSlackTokenCacheKey(token);
|
||||
|
||||
expect(first).toBe(second);
|
||||
expect(first).toMatch(/^sha256:/);
|
||||
expect(first).not.toContain(token);
|
||||
expect(createSlackTokenCacheKey("xoxb-other-token")).not.toBe(first);
|
||||
});
|
||||
});
|
||||
|
||||
describe("slack proxy agent", () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { type WebClientOptions, WebClient } from "@slack/web-api";
|
||||
import { resolveSlackWebClientOptions, resolveSlackWriteClientOptions } from "./client-options.js";
|
||||
|
||||
@@ -19,21 +20,26 @@ export function createSlackWriteClient(token: string, options: WebClientOptions
|
||||
return new WebClient(token, resolveSlackWriteClientOptions(options));
|
||||
}
|
||||
|
||||
export function createSlackTokenCacheKey(token: string): string {
|
||||
return `sha256:${createHash("sha256").update(token).digest("base64url")}`;
|
||||
}
|
||||
|
||||
export function getSlackWriteClient(token: string): WebClient {
|
||||
const cached = slackWriteClientCache.get(token);
|
||||
const tokenKey = createSlackTokenCacheKey(token);
|
||||
const cached = slackWriteClientCache.get(tokenKey);
|
||||
if (cached) {
|
||||
slackWriteClientCache.delete(token);
|
||||
slackWriteClientCache.set(token, cached);
|
||||
slackWriteClientCache.delete(tokenKey);
|
||||
slackWriteClientCache.set(tokenKey, cached);
|
||||
return cached;
|
||||
}
|
||||
const client = createSlackWriteClient(token);
|
||||
if (slackWriteClientCache.size >= SLACK_WRITE_CLIENT_CACHE_MAX) {
|
||||
const oldestToken = slackWriteClientCache.keys().next().value;
|
||||
if (oldestToken) {
|
||||
slackWriteClientCache.delete(oldestToken);
|
||||
const oldestTokenKey = slackWriteClientCache.keys().next().value;
|
||||
if (oldestTokenKey) {
|
||||
slackWriteClientCache.delete(oldestTokenKey);
|
||||
}
|
||||
}
|
||||
slackWriteClientCache.set(token, client);
|
||||
slackWriteClientCache.set(tokenKey, client);
|
||||
return client;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { SlackTokenSource } from "./accounts.js";
|
||||
import { resolveSlackAccount } from "./accounts.js";
|
||||
import { buildSlackBlocksFallbackText } from "./blocks-fallback.js";
|
||||
import { validateSlackBlocksArray } from "./blocks-input.js";
|
||||
import { getSlackWriteClient } from "./client.js";
|
||||
import { createSlackTokenCacheKey, getSlackWriteClient } from "./client.js";
|
||||
import { markdownToSlackMrkdwnChunks } from "./format.js";
|
||||
import { SLACK_TEXT_LIMIT } from "./limits.js";
|
||||
import { loadOutboundMediaFromUrl } from "./runtime-api.js";
|
||||
@@ -188,7 +188,9 @@ function createSlackSendQueueKey(params: {
|
||||
}): string {
|
||||
const isUserId = params.recipient.kind === "user" || /^U[A-Z0-9]+$/i.test(params.recipient.id);
|
||||
const recipientKey = `${isUserId ? "user" : params.recipient.kind}:${params.recipient.id}`;
|
||||
return `${params.accountId}:${params.token}:${recipientKey}:${params.threadTs ?? ""}`;
|
||||
return `${params.accountId}:${createSlackTokenCacheKey(params.token)}:${recipientKey}:${
|
||||
params.threadTs ?? ""
|
||||
}`;
|
||||
}
|
||||
|
||||
async function runQueuedSlackSend<T>(key: string, task: () => Promise<T>): Promise<T> {
|
||||
@@ -215,7 +217,9 @@ function createSlackDmCacheKey(params: {
|
||||
token: string;
|
||||
recipientId: string;
|
||||
}): string {
|
||||
return `${params.accountId ?? "default"}:${params.token}:${params.recipientId}`;
|
||||
return `${params.accountId ?? "default"}:${createSlackTokenCacheKey(params.token)}:${
|
||||
params.recipientId
|
||||
}`;
|
||||
}
|
||||
|
||||
function setSlackDmChannelCache(key: string, channelId: string): void {
|
||||
|
||||
Reference in New Issue
Block a user