mirror of https://github.com/openclaw/openclaw.git
fix(slack): guard Socket Mode listeners access during startup (openclaw#28702) thanks @Glucksberg
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: Glucksberg <80581902+Glucksberg@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
parent
b3f60a68a0
commit
3aad6c8bdb
|
|
@ -93,6 +93,7 @@ Docs: https://docs.openclaw.ai
|
|||
- File tools/tilde paths: expand `~/...` against the user home directory before workspace-root checks in host file read/write/edit paths, while preserving root-boundary enforcement so outside-root targets remain blocked. (#29779) Thanks @Glucksberg.
|
||||
- Slack/HTTP mode startup: treat Slack HTTP accounts as configured when `botToken` + `signingSecret` are present (without requiring `appToken`) in channel config/runtime status so webhook mode is not silently skipped. (#30567)
|
||||
- Slack/Usage footer formatting: wrap session keys in inline code in full response-usage footers so Slack does not parse colon-delimited session segments as emoji shortcodes. (#30258) Thanks @pushkarsingh32.
|
||||
- Slack/Socket Mode slash startup: treat `app.options()` registration as best-effort and fall back to static arg menus when listener registration fails, preventing Slack monitor startup crash loops on receiver init edge cases. (#21715)
|
||||
- Onboarding/Custom providers: raise default custom-provider model context window to the runtime hard minimum (16k) and auto-heal existing custom model entries below that threshold during reconfiguration, preventing immediate `Model context window too small (4096 tokens)` failures. (#21653) Thanks @r4jiv007.
|
||||
- Web UI/Assistant text: strip internal `<relevant-memories>...</relevant-memories>` scaffolding from rendered assistant messages (while preserving code-fence literals), preventing memory-context leakage in chat output for models that echo internal blocks. (#29851) Thanks @Valkster70.
|
||||
- Dashboard/Sessions: allow authenticated Control UI clients to delete and patch sessions while still blocking regular webchat clients from session mutation RPCs, fixing Dashboard session delete failures. (#21264) Thanks @jskoiz.
|
||||
|
|
|
|||
|
|
@ -435,6 +435,78 @@ describe("Slack native command argument menus", () => {
|
|||
expect(testHarness.optionsReceiverContexts[0]).toBe(testHarness.app);
|
||||
});
|
||||
|
||||
it("falls back to static menus when app.options() throws during registration", async () => {
|
||||
const commands = new Map<string, (args: unknown) => Promise<void>>();
|
||||
const actions = new Map<string, (args: unknown) => Promise<void>>();
|
||||
const postEphemeral = vi.fn().mockResolvedValue({ ok: true });
|
||||
const app = {
|
||||
client: { chat: { postEphemeral } },
|
||||
command: (name: string, handler: (args: unknown) => Promise<void>) => {
|
||||
commands.set(name, handler);
|
||||
},
|
||||
action: (id: string, handler: (args: unknown) => Promise<void>) => {
|
||||
actions.set(id, handler);
|
||||
},
|
||||
// Simulate Bolt throwing during options registration (e.g. receiver not initialized)
|
||||
options: () => {
|
||||
throw new Error("Cannot read properties of undefined (reading 'listeners')");
|
||||
},
|
||||
};
|
||||
const ctx = {
|
||||
cfg: { commands: { native: true, nativeSkills: false } },
|
||||
runtime: {},
|
||||
botToken: "bot-token",
|
||||
botUserId: "bot",
|
||||
teamId: "T1",
|
||||
allowFrom: ["*"],
|
||||
dmEnabled: true,
|
||||
dmPolicy: "open",
|
||||
groupDmEnabled: false,
|
||||
groupDmChannels: [],
|
||||
defaultRequireMention: true,
|
||||
groupPolicy: "open",
|
||||
useAccessGroups: false,
|
||||
channelsConfig: undefined,
|
||||
slashCommand: {
|
||||
enabled: true,
|
||||
name: "openclaw",
|
||||
ephemeral: true,
|
||||
sessionPrefix: "slack:slash",
|
||||
},
|
||||
textLimit: 4000,
|
||||
app,
|
||||
isChannelAllowed: () => true,
|
||||
resolveChannelName: async () => ({ name: "dm", type: "im" }),
|
||||
resolveUserName: async () => ({ name: "Ada" }),
|
||||
} as unknown;
|
||||
const account = {
|
||||
accountId: "acct",
|
||||
config: { commands: { native: true, nativeSkills: false } },
|
||||
} as unknown;
|
||||
|
||||
// Registration should not throw despite app.options() throwing
|
||||
await registerCommands(ctx, account);
|
||||
expect(commands.size).toBeGreaterThan(0);
|
||||
expect(actions.has("openclaw_cmdarg")).toBe(true);
|
||||
|
||||
// The /reportexternal command (140 choices) should fall back to static_select
|
||||
// instead of external_select since options registration failed
|
||||
const handler = commands.get("/reportexternal");
|
||||
expect(handler).toBeDefined();
|
||||
const respond = vi.fn().mockResolvedValue(undefined);
|
||||
const ack = vi.fn().mockResolvedValue(undefined);
|
||||
await handler!({
|
||||
command: createSlashCommand(),
|
||||
ack,
|
||||
respond,
|
||||
});
|
||||
expect(respond).toHaveBeenCalledTimes(1);
|
||||
const payload = respond.mock.calls[0]?.[0] as { blocks?: Array<{ type: string }> };
|
||||
const actionsBlock = findFirstActionsBlock(payload);
|
||||
// Should be static_select (fallback) not external_select
|
||||
expect(actionsBlock?.elements?.[0]?.type).toBe("static_select");
|
||||
});
|
||||
|
||||
it("shows a button menu when required args are omitted", async () => {
|
||||
const { respond } = await runCommandHandler(usageHandler);
|
||||
const actions = expectArgMenuLayout(respond);
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ export async function registerSlackMonitorSlashCommands(params: {
|
|||
|
||||
const supportsInteractiveArgMenus =
|
||||
typeof (ctx.app as { action?: unknown }).action === "function";
|
||||
const supportsExternalArgMenus = typeof (ctx.app as { options?: unknown }).options === "function";
|
||||
let supportsExternalArgMenus = typeof (ctx.app as { options?: unknown }).options === "function";
|
||||
|
||||
const slashCommand = resolveSlackSlashCommandConfig(
|
||||
ctx.slashCommand ?? account.config.slashCommand,
|
||||
|
|
@ -758,7 +758,17 @@ export async function registerSlackMonitorSlashCommands(params: {
|
|||
await ack({ options });
|
||||
});
|
||||
};
|
||||
registerArgOptions();
|
||||
// Treat external arg-menu registration as best-effort: if Bolt's app.options()
|
||||
// throws (e.g. from receiver init issues), disable external selects and fall back
|
||||
// to static_select/button menus instead of crashing the entire provider startup.
|
||||
try {
|
||||
registerArgOptions();
|
||||
} catch (err) {
|
||||
supportsExternalArgMenus = false;
|
||||
logVerbose(
|
||||
`slack: external arg-menu registration failed, falling back to static menus: ${String(err)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const registerArgAction = (actionId: string) => {
|
||||
(
|
||||
|
|
|
|||
Loading…
Reference in New Issue