diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b5ec756b..853d39fa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Docs: https://docs.clawd.bot - CLI: avoid duplicating --profile/--dev flags when formatting commands. - Auth: dedupe codex-cli profiles when tokens match custom openai-codex entries. (#1264) — thanks @odrobnik. - Agents: avoid misclassifying context-window-too-small errors as context overflow. (#1266) — thanks @humanwritten. +- Slack: resolve Bolt default-export shapes for monitor startup. (#1208) — thanks @24601. ## 2026.1.19-3 diff --git a/src/gateway/protocol/schema/frames.ts b/src/gateway/protocol/schema/frames.ts index dfaac1c26..f9cae661c 100644 --- a/src/gateway/protocol/schema/frames.ts +++ b/src/gateway/protocol/schema/frames.ts @@ -39,14 +39,16 @@ export const ConnectParamsSchema = Type.Object( permissions: Type.Optional(Type.Record(NonEmptyString, Type.Boolean())), role: Type.Optional(NonEmptyString), scopes: Type.Optional(Type.Array(NonEmptyString)), - device: Type.Object( - { - id: NonEmptyString, - publicKey: NonEmptyString, - signature: NonEmptyString, - signedAt: Type.Integer({ minimum: 0 }), - }, - { additionalProperties: false }, + device: Type.Optional( + Type.Object( + { + id: NonEmptyString, + publicKey: NonEmptyString, + signature: NonEmptyString, + signedAt: Type.Integer({ minimum: 0 }), + }, + { additionalProperties: false }, + ), ), auth: Type.Optional( Type.Object( diff --git a/src/gateway/server.cron.test.ts b/src/gateway/server.cron.test.ts index ca701e67f..48269cca7 100644 --- a/src/gateway/server.cron.test.ts +++ b/src/gateway/server.cron.test.ts @@ -185,6 +185,7 @@ describe("gateway server cron", () => { test("accepts jobId for cron.update", async () => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-cron-")); testState.cronStorePath = path.join(dir, "cron", "jobs.json"); + testState.cronEnabled = false; await fs.mkdir(path.dirname(testState.cronStorePath), { recursive: true }); await fs.writeFile(testState.cronStorePath, JSON.stringify({ version: 1, jobs: [] })); @@ -218,6 +219,7 @@ describe("gateway server cron", () => { await server.close(); await rmTempDir(dir); testState.cronStorePath = undefined; + testState.cronEnabled = undefined; }); test("disables cron jobs via enabled:false patches", async () => { diff --git a/src/slack/monitor/provider.ts b/src/slack/monitor/provider.ts index 2ff3d9f18..3cdb0f2de 100644 --- a/src/slack/monitor/provider.ts +++ b/src/slack/monitor/provider.ts @@ -27,20 +27,21 @@ import { normalizeAllowList } from "./allow-list.js"; import type { MonitorSlackOpts } from "./types.js"; type SlackBoltNamespace = typeof import("@slack/bolt"); +type SlackBoltDefault = SlackBoltNamespace | SlackBoltNamespace["App"]; -const slackBoltDefault = SlackBoltDefault as unknown; +const slackBoltDefaultImport = SlackBoltDefault as SlackBoltDefault | undefined; +const slackBoltModuleDefault = (SlackBoltModule as { default?: SlackBoltDefault }).default; +const slackBoltDefault = slackBoltDefaultImport ?? slackBoltModuleDefault; const slackBoltNamespace = - (typeof slackBoltDefault === "object" && slackBoltDefault - ? ("default" in slackBoltDefault - ? (slackBoltDefault as { default?: unknown }).default - : slackBoltDefault) - : undefined) as SlackBoltNamespace | undefined; + typeof slackBoltDefault === "object" && slackBoltDefault + ? (slackBoltDefault as SlackBoltNamespace) + : typeof slackBoltModuleDefault === "object" && slackBoltModuleDefault + ? (slackBoltModuleDefault as SlackBoltNamespace) + : undefined; // Bun allows named imports from CJS; Node ESM doesn't. Resolve default/module shapes for compatibility. -const App = - ((typeof slackBoltDefault === "function" - ? slackBoltDefault - : slackBoltNamespace?.App) ?? - SlackBoltModule.App) as SlackBoltNamespace["App"]; +const App = ((typeof slackBoltDefault === "function" ? slackBoltDefault : undefined) ?? + slackBoltNamespace?.App ?? + SlackBoltModule.App) as SlackBoltNamespace["App"]; const HTTPReceiver = (slackBoltNamespace?.HTTPReceiver ?? SlackBoltModule.HTTPReceiver) as SlackBoltNamespace["HTTPReceiver"]; function parseApiAppIdFromAppToken(raw?: string) { @@ -132,6 +133,13 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024; const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false; + if (!App) { + throw new Error("Slack Bolt App export missing; check @slack/bolt installation."); + } + if (slackMode === "http" && !HTTPReceiver) { + throw new Error("Slack Bolt HTTPReceiver export missing; check @slack/bolt installation."); + } + const receiver = slackMode === "http" ? new HTTPReceiver({