@spinabot/brigade 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +51 -1
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.d.ts +4 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.js +22 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
- package/dist/agents/channels/discord/account-config.d.ts +117 -0
- package/dist/agents/channels/discord/account-config.d.ts.map +1 -0
- package/dist/agents/channels/discord/account-config.js +260 -0
- package/dist/agents/channels/discord/account-config.js.map +1 -0
- package/dist/agents/channels/discord/adapter.d.ts +56 -0
- package/dist/agents/channels/discord/adapter.d.ts.map +1 -0
- package/dist/agents/channels/discord/adapter.js +526 -0
- package/dist/agents/channels/discord/adapter.js.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts +43 -0
- package/dist/agents/channels/discord/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-authorize.js +71 -0
- package/dist/agents/channels/discord/approval-authorize.js.map +1 -0
- package/dist/agents/channels/discord/approval-native.d.ts +68 -0
- package/dist/agents/channels/discord/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/discord/approval-native.js +81 -0
- package/dist/agents/channels/discord/approval-native.js.map +1 -0
- package/dist/agents/channels/discord/command-menu.d.ts +49 -0
- package/dist/agents/channels/discord/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/discord/command-menu.js +73 -0
- package/dist/agents/channels/discord/command-menu.js.map +1 -0
- package/dist/agents/channels/discord/components.d.ts +97 -0
- package/dist/agents/channels/discord/components.d.ts.map +1 -0
- package/dist/agents/channels/discord/components.js +131 -0
- package/dist/agents/channels/discord/components.js.map +1 -0
- package/dist/agents/channels/discord/connection.d.ts +387 -0
- package/dist/agents/channels/discord/connection.d.ts.map +1 -0
- package/dist/agents/channels/discord/connection.js +786 -0
- package/dist/agents/channels/discord/connection.js.map +1 -0
- package/dist/agents/channels/discord/draft-stream.d.ts +92 -0
- package/dist/agents/channels/discord/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/discord/draft-stream.js +213 -0
- package/dist/agents/channels/discord/draft-stream.js.map +1 -0
- package/dist/agents/channels/discord/format.d.ts +55 -0
- package/dist/agents/channels/discord/format.d.ts.map +1 -0
- package/dist/agents/channels/discord/format.js +247 -0
- package/dist/agents/channels/discord/format.js.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts +220 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.js +351 -0
- package/dist/agents/channels/discord/inbound-extras.js.map +1 -0
- package/dist/agents/channels/discord/index.d.ts +14 -0
- package/dist/agents/channels/discord/index.d.ts.map +1 -0
- package/dist/agents/channels/discord/index.js +14 -0
- package/dist/agents/channels/discord/index.js.map +1 -0
- package/dist/agents/channels/discord/media.d.ts +85 -0
- package/dist/agents/channels/discord/media.d.ts.map +1 -0
- package/dist/agents/channels/discord/media.js +242 -0
- package/dist/agents/channels/discord/media.js.map +1 -0
- package/dist/agents/channels/discord/module.d.ts +15 -0
- package/dist/agents/channels/discord/module.d.ts.map +1 -0
- package/dist/agents/channels/discord/module.js +22 -0
- package/dist/agents/channels/discord/module.js.map +1 -0
- package/dist/agents/channels/discord/plugin.d.ts +73 -0
- package/dist/agents/channels/discord/plugin.d.ts.map +1 -0
- package/dist/agents/channels/discord/plugin.js +303 -0
- package/dist/agents/channels/discord/plugin.js.map +1 -0
- package/dist/agents/channels/discord/probe.d.ts +93 -0
- package/dist/agents/channels/discord/probe.d.ts.map +1 -0
- package/dist/agents/channels/discord/probe.js +158 -0
- package/dist/agents/channels/discord/probe.js.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts +42 -0
- package/dist/agents/channels/discord/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/discord/reasoning-lane.js +68 -0
- package/dist/agents/channels/discord/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +65 -7
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/manager.d.ts.map +1 -1
- package/dist/agents/channels/manager.js +18 -0
- package/dist/agents/channels/manager.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +4 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +4 -0
- package/dist/agents/channels/sdk.js.map +1 -1
- package/dist/agents/channels/slack/account-config.d.ts +172 -0
- package/dist/agents/channels/slack/account-config.d.ts.map +1 -0
- package/dist/agents/channels/slack/account-config.js +353 -0
- package/dist/agents/channels/slack/account-config.js.map +1 -0
- package/dist/agents/channels/slack/account-registry.d.ts +45 -0
- package/dist/agents/channels/slack/account-registry.d.ts.map +1 -0
- package/dist/agents/channels/slack/account-registry.js +58 -0
- package/dist/agents/channels/slack/account-registry.js.map +1 -0
- package/dist/agents/channels/slack/adapter.d.ts +66 -0
- package/dist/agents/channels/slack/adapter.d.ts.map +1 -0
- package/dist/agents/channels/slack/adapter.js +547 -0
- package/dist/agents/channels/slack/adapter.js.map +1 -0
- package/dist/agents/channels/slack/approval-authorize.d.ts +43 -0
- package/dist/agents/channels/slack/approval-authorize.d.ts.map +1 -0
- package/dist/agents/channels/slack/approval-authorize.js +71 -0
- package/dist/agents/channels/slack/approval-authorize.js.map +1 -0
- package/dist/agents/channels/slack/approval-native.d.ts +70 -0
- package/dist/agents/channels/slack/approval-native.d.ts.map +1 -0
- package/dist/agents/channels/slack/approval-native.js +85 -0
- package/dist/agents/channels/slack/approval-native.js.map +1 -0
- package/dist/agents/channels/slack/blocks.d.ts +125 -0
- package/dist/agents/channels/slack/blocks.d.ts.map +1 -0
- package/dist/agents/channels/slack/blocks.js +145 -0
- package/dist/agents/channels/slack/blocks.js.map +1 -0
- package/dist/agents/channels/slack/command-menu.d.ts +44 -0
- package/dist/agents/channels/slack/command-menu.d.ts.map +1 -0
- package/dist/agents/channels/slack/command-menu.js +66 -0
- package/dist/agents/channels/slack/command-menu.js.map +1 -0
- package/dist/agents/channels/slack/connection.d.ts +422 -0
- package/dist/agents/channels/slack/connection.d.ts.map +1 -0
- package/dist/agents/channels/slack/connection.js +1042 -0
- package/dist/agents/channels/slack/connection.js.map +1 -0
- package/dist/agents/channels/slack/directory-live.d.ts +129 -0
- package/dist/agents/channels/slack/directory-live.d.ts.map +1 -0
- package/dist/agents/channels/slack/directory-live.js +148 -0
- package/dist/agents/channels/slack/directory-live.js.map +1 -0
- package/dist/agents/channels/slack/draft-stream.d.ts +93 -0
- package/dist/agents/channels/slack/draft-stream.d.ts.map +1 -0
- package/dist/agents/channels/slack/draft-stream.js +218 -0
- package/dist/agents/channels/slack/draft-stream.js.map +1 -0
- package/dist/agents/channels/slack/format.d.ts +41 -0
- package/dist/agents/channels/slack/format.d.ts.map +1 -0
- package/dist/agents/channels/slack/format.js +271 -0
- package/dist/agents/channels/slack/format.js.map +1 -0
- package/dist/agents/channels/slack/inbound-extras.d.ts +179 -0
- package/dist/agents/channels/slack/inbound-extras.d.ts.map +1 -0
- package/dist/agents/channels/slack/inbound-extras.js +257 -0
- package/dist/agents/channels/slack/inbound-extras.js.map +1 -0
- package/dist/agents/channels/slack/index.d.ts +15 -0
- package/dist/agents/channels/slack/index.d.ts.map +1 -0
- package/dist/agents/channels/slack/index.js +15 -0
- package/dist/agents/channels/slack/index.js.map +1 -0
- package/dist/agents/channels/slack/media.d.ts +90 -0
- package/dist/agents/channels/slack/media.d.ts.map +1 -0
- package/dist/agents/channels/slack/media.js +215 -0
- package/dist/agents/channels/slack/media.js.map +1 -0
- package/dist/agents/channels/slack/module.d.ts +26 -0
- package/dist/agents/channels/slack/module.d.ts.map +1 -0
- package/dist/agents/channels/slack/module.js +67 -0
- package/dist/agents/channels/slack/module.js.map +1 -0
- package/dist/agents/channels/slack/plugin.d.ts +69 -0
- package/dist/agents/channels/slack/plugin.d.ts.map +1 -0
- package/dist/agents/channels/slack/plugin.js +318 -0
- package/dist/agents/channels/slack/plugin.js.map +1 -0
- package/dist/agents/channels/slack/probe.d.ts +72 -0
- package/dist/agents/channels/slack/probe.d.ts.map +1 -0
- package/dist/agents/channels/slack/probe.js +103 -0
- package/dist/agents/channels/slack/probe.js.map +1 -0
- package/dist/agents/channels/slack/proxy-agent.d.ts +30 -0
- package/dist/agents/channels/slack/proxy-agent.d.ts.map +1 -0
- package/dist/agents/channels/slack/proxy-agent.js +44 -0
- package/dist/agents/channels/slack/proxy-agent.js.map +1 -0
- package/dist/agents/channels/slack/reasoning-lane.d.ts +42 -0
- package/dist/agents/channels/slack/reasoning-lane.d.ts.map +1 -0
- package/dist/agents/channels/slack/reasoning-lane.js +68 -0
- package/dist/agents/channels/slack/reasoning-lane.js.map +1 -0
- package/dist/agents/channels/slack/user-directory.d.ts +69 -0
- package/dist/agents/channels/slack/user-directory.d.ts.map +1 -0
- package/dist/agents/channels/slack/user-directory.js +94 -0
- package/dist/agents/channels/slack/user-directory.js.map +1 -0
- package/dist/agents/channels/slack/webhook.d.ts +89 -0
- package/dist/agents/channels/slack/webhook.d.ts.map +1 -0
- package/dist/agents/channels/slack/webhook.js +228 -0
- package/dist/agents/channels/slack/webhook.js.map +1 -0
- package/dist/agents/channels/telegram/format.d.ts.map +1 -1
- package/dist/agents/channels/telegram/format.js +17 -1
- package/dist/agents/channels/telegram/format.js.map +1 -1
- package/dist/agents/channels/telegram/webhook.d.ts.map +1 -1
- package/dist/agents/channels/telegram/webhook.js +7 -1
- package/dist/agents/channels/telegram/webhook.js.map +1 -1
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +10 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/tools/cron-tool.d.ts.map +1 -1
- package/dist/agents/tools/cron-tool.js +4 -1
- package/dist/agents/tools/cron-tool.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/core/auth-bridge.d.ts +1 -0
- package/dist/core/auth-bridge.d.ts.map +1 -1
- package/dist/core/auth-bridge.js +46 -1
- package/dist/core/auth-bridge.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +40 -5
- package/dist/core/server.js.map +1 -1
- package/dist/cron/isolated-agent/run-executor.d.ts +11 -0
- package/dist/cron/isolated-agent/run-executor.d.ts.map +1 -1
- package/dist/cron/isolated-agent/run-executor.js +20 -4
- package/dist/cron/isolated-agent/run-executor.js.map +1 -1
- package/dist/cron/types.d.ts +8 -0
- package/dist/cron/types.d.ts.map +1 -1
- package/dist/system-prompt/assembler.d.ts.map +1 -1
- package/dist/system-prompt/assembler.js +4 -2
- package/dist/system-prompt/assembler.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack `ChannelPlugin` — the multi-WORKSPACE contract surface.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `telegram/plugin.ts`: wraps `createSlackAdapter()` (the per-connection
|
|
5
|
+
* implementation) with the lifecycle adapters the `ChannelPluginManager`
|
|
6
|
+
* consumes, so an operator can run MORE THAN ONE Slack workspace at once via:
|
|
7
|
+
*
|
|
8
|
+
* channels.slack = {
|
|
9
|
+
* enabled: true,
|
|
10
|
+
* accounts: [
|
|
11
|
+
* { id: "acme", botToken: "xoxb-AAA", appToken: "xapp-AAA" },
|
|
12
|
+
* { id: "labs", botToken: "xoxb-BBB", appToken: "xapp-BBB" },
|
|
13
|
+
* ],
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* - `config.listAccountIds` / `resolveAccount` → multi-workspace discovery
|
|
17
|
+
* - `gateway.startAccount` / `stopAccount` → per-workspace app lifecycle
|
|
18
|
+
* - `outbound.sendText` / `sendMedia` → routes by `target.accountId`
|
|
19
|
+
* - per-account approval-dispatcher registration → an exec-gate prompt raised
|
|
20
|
+
* by a turn on (slack, labs) replies on (slack, labs), not the default
|
|
21
|
+
*
|
|
22
|
+
* Per-account state lives in a `Map<accountId, AccountRuntime>` held in this
|
|
23
|
+
* closure — one app connection per account, partitioned token resolution per
|
|
24
|
+
* `channels.slack.accounts[].botToken`. Inbound dispatch reuses the shared
|
|
25
|
+
* `runChannelInboundPipeline` so the multi-workspace path carries the identical
|
|
26
|
+
* ACL + debounce + abort + approval-reply + approval-callback surface as the
|
|
27
|
+
* legacy single-adapter manager.
|
|
28
|
+
*
|
|
29
|
+
* The legacy single-account `createSlackAdapter` (started by the legacy
|
|
30
|
+
* `startChannels` manager) STEPS ASIDE when >1 account is configured — its
|
|
31
|
+
* `isConfigured` returns false for the default account in that case (mirrors
|
|
32
|
+
* Telegram), so the two paths never double-start an app.
|
|
33
|
+
*/
|
|
34
|
+
// Channel SDK barrel — the SINGLE import surface for the multi-account
|
|
35
|
+
// `ChannelPlugin` contract + every sub-adapter type + the shared inbound
|
|
36
|
+
// pipeline + the approval router + the gateway boot args. A multi-account
|
|
37
|
+
// channel authors entirely from here.
|
|
38
|
+
import { buildBundledCommands, createInboundPipelineContext, createSubsystemLogger, registerChannelApprovalDispatcher, removeChannelApprovalDispatcher, runChannelInboundPipeline, SLACK_CHANNEL_META, } from "../sdk.js";
|
|
39
|
+
import { listSlackAccountIds, resolveSlackAccount, resolveSlackBotToken, SLACK_CHANNEL_ID, SLACK_DEFAULT_ACCOUNT_ID, } from "./account-config.js";
|
|
40
|
+
import { registerSlackAccountSink, removeSlackAccountSink } from "./account-registry.js";
|
|
41
|
+
import { createSlackAdapter, SLACK_CAPABILITIES } from "./adapter.js";
|
|
42
|
+
import { probeSlack } from "./probe.js";
|
|
43
|
+
const log = createSubsystemLogger("channels/slack/plugin");
|
|
44
|
+
// Single source of truth for the channel's user-facing metadata lives in the
|
|
45
|
+
// import-light `bundled-channel-metas` module (re-exported via the SDK barrel),
|
|
46
|
+
// so the registry / markdown gate can read it without loading this adapter.
|
|
47
|
+
// `SLACK_CHANNEL_META.id` is the same canonical `"slack"` string as
|
|
48
|
+
// `SLACK_CHANNEL_ID`.
|
|
49
|
+
const SLACK_META = SLACK_CHANNEL_META;
|
|
50
|
+
/** Build the per-account approval capability — the native Block Kit prompt + approver gate. */
|
|
51
|
+
function buildApprovalCapability(adapter, accountId) {
|
|
52
|
+
return {
|
|
53
|
+
async sendApprovalPrompt(params) {
|
|
54
|
+
// Delegate to the adapter's own native prompt (Block Kit buttons). The
|
|
55
|
+
// adapter throws when the approval id can't be encoded, so the router
|
|
56
|
+
// falls back to its text prompt — mirror that here.
|
|
57
|
+
const cap = adapter.approvalCapability?.sendApprovalPrompt;
|
|
58
|
+
if (!cap)
|
|
59
|
+
throw new Error("slack adapter has no approval prompt");
|
|
60
|
+
await cap({ ...params, accountId });
|
|
61
|
+
},
|
|
62
|
+
authorizeApprover(p) {
|
|
63
|
+
const cap = adapter.approvalCapability?.authorizeApprover;
|
|
64
|
+
if (!cap)
|
|
65
|
+
return { authorized: true };
|
|
66
|
+
return cap(p);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Construct the plugin instance, capturing per-account runtime state in closure. */
|
|
71
|
+
export function createSlackPlugin(deps) {
|
|
72
|
+
const accountRuntimes = new Map();
|
|
73
|
+
const startAccount = async (ctx) => {
|
|
74
|
+
const accountId = ctx.accountId || SLACK_DEFAULT_ACCOUNT_ID;
|
|
75
|
+
// Re-entrant start (the plugin-manager's restart loop) — stop the prior
|
|
76
|
+
// adapter, then build fresh.
|
|
77
|
+
const existing = accountRuntimes.get(accountId);
|
|
78
|
+
if (existing) {
|
|
79
|
+
try {
|
|
80
|
+
await existing.adapter.stop();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* best-effort */
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
existing.abort.abort("restart");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* best-effort */
|
|
90
|
+
}
|
|
91
|
+
removeChannelApprovalDispatcher(SLACK_CHANNEL_ID, accountId);
|
|
92
|
+
removeSlackAccountSink(accountId);
|
|
93
|
+
accountRuntimes.delete(accountId);
|
|
94
|
+
}
|
|
95
|
+
const cfg = deps.loadConfig();
|
|
96
|
+
const factory = deps.adapterFactory ?? defaultSlackAdapterFactory;
|
|
97
|
+
const adapter = factory({ accountId });
|
|
98
|
+
// Per-account abort derived from the gateway's parent abort.
|
|
99
|
+
const accountAbort = new AbortController();
|
|
100
|
+
const parent = ctx.signal;
|
|
101
|
+
if (parent) {
|
|
102
|
+
if (parent.aborted)
|
|
103
|
+
accountAbort.abort();
|
|
104
|
+
else
|
|
105
|
+
parent.addEventListener("abort", () => accountAbort.abort(), { once: true });
|
|
106
|
+
}
|
|
107
|
+
const pipelineRunTurn = (turn) => deps.runTurn(turn);
|
|
108
|
+
// Bundled channel commands so `/help` etc. work on the multi-account path.
|
|
109
|
+
const commandMap = new Map();
|
|
110
|
+
for (const c of buildBundledCommands(adapter)) {
|
|
111
|
+
commandMap.set(c.name.toLowerCase(), c);
|
|
112
|
+
}
|
|
113
|
+
const pipeline = createInboundPipelineContext({
|
|
114
|
+
adapter,
|
|
115
|
+
config: cfg,
|
|
116
|
+
agentId: deps.defaultAgentId,
|
|
117
|
+
runTurn: pipelineRunTurn,
|
|
118
|
+
commandMap,
|
|
119
|
+
parentAbort: accountAbort.signal,
|
|
120
|
+
});
|
|
121
|
+
const startCtx = {
|
|
122
|
+
signal: accountAbort.signal,
|
|
123
|
+
log: (msg, meta) => log.info(`[${accountId}] ${msg}`, meta),
|
|
124
|
+
onInbound: async (msg) => {
|
|
125
|
+
// Re-read the active config per inbound so policy edits land without
|
|
126
|
+
// restarting the app. Stamp the accountId so the shared pipeline keys
|
|
127
|
+
// ACL + approval-route per account.
|
|
128
|
+
pipeline.config = deps.loadConfig();
|
|
129
|
+
const stamped = msg.accountId ? msg : { ...msg, accountId };
|
|
130
|
+
await runChannelInboundPipeline(pipeline, stamped);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
try {
|
|
134
|
+
await adapter.start(startCtx);
|
|
135
|
+
accountRuntimes.set(accountId, { adapter, pipeline, abort: accountAbort });
|
|
136
|
+
// Bridge this started adapter to its events-mode webhook route. The route
|
|
137
|
+
// (registered by the module at boot) resolves the sink through this
|
|
138
|
+
// registry at request time, so an event POSTed for THIS workspace's path
|
|
139
|
+
// reaches THIS adapter. Only adapters that carry `feedWebhookEvent` (the
|
|
140
|
+
// real Slack adapter does; a bare test fake may not) are bridged.
|
|
141
|
+
const sinkCapable = adapter;
|
|
142
|
+
if (typeof sinkCapable.feedWebhookEvent === "function") {
|
|
143
|
+
const feed = sinkCapable.feedWebhookEvent.bind(adapter);
|
|
144
|
+
registerSlackAccountSink(accountId, { feedWebhookEvent: feed });
|
|
145
|
+
}
|
|
146
|
+
// Per-account approval dispatcher — native Block Kit prompt + per-account
|
|
147
|
+
// routing. Without this an exec-gate prompt from a turn on (slack, labs)
|
|
148
|
+
// would fall through to the channel default.
|
|
149
|
+
registerChannelApprovalDispatcher(SLACK_CHANNEL_ID, accountId, {
|
|
150
|
+
sendText: (conversationId, text, opts) => adapter.sendText(conversationId, text, { ...(opts ?? {}), accountId }),
|
|
151
|
+
prettyName: "Slack",
|
|
152
|
+
approvalCapability: buildApprovalCapability(adapter, accountId),
|
|
153
|
+
getApprovalContext: () => ({ runtime: ctx.runtime, cfg: deps.loadConfig() }),
|
|
154
|
+
});
|
|
155
|
+
log.info("slack account started", { accountId });
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
log.warn("slack account failed to start", {
|
|
159
|
+
accountId,
|
|
160
|
+
error: err instanceof Error ? err.message : String(err),
|
|
161
|
+
});
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const stopAccount = async (ctx) => {
|
|
166
|
+
const runtime = accountRuntimes.get(ctx.accountId);
|
|
167
|
+
if (!runtime)
|
|
168
|
+
return;
|
|
169
|
+
accountRuntimes.delete(ctx.accountId);
|
|
170
|
+
// Drop the per-account dispatcher + webhook sink BEFORE adapter.stop() so a
|
|
171
|
+
// late in-flight bridge / event POST can't ask a torn-down app to act.
|
|
172
|
+
removeChannelApprovalDispatcher(SLACK_CHANNEL_ID, ctx.accountId);
|
|
173
|
+
removeSlackAccountSink(ctx.accountId);
|
|
174
|
+
try {
|
|
175
|
+
runtime.abort.abort("stop-requested");
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* best-effort */
|
|
179
|
+
}
|
|
180
|
+
// Clear pending debounce slots so a flush can't fire after stop.
|
|
181
|
+
for (const slot of runtime.pipeline.pendingDispatches.values())
|
|
182
|
+
clearTimeout(slot.timer);
|
|
183
|
+
runtime.pipeline.pendingDispatches.clear();
|
|
184
|
+
try {
|
|
185
|
+
await runtime.adapter.stop();
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
log.warn("slack account stop threw", {
|
|
189
|
+
accountId: ctx.accountId,
|
|
190
|
+
error: err instanceof Error ? err.message : String(err),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const logoutAccount = async (ctx) => {
|
|
195
|
+
try {
|
|
196
|
+
await stopAccount(ctx);
|
|
197
|
+
return { ok: true };
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
id: SLACK_CHANNEL_ID,
|
|
205
|
+
meta: SLACK_META,
|
|
206
|
+
capabilities: SLACK_CAPABILITIES,
|
|
207
|
+
startedAccountIds: () => [...accountRuntimes.keys()],
|
|
208
|
+
getAdapter: (accountId) => accountRuntimes.get(accountId)?.adapter,
|
|
209
|
+
probeAccount: async (accountId, cfg) => {
|
|
210
|
+
const token = resolveSlackBotToken(cfg, accountId);
|
|
211
|
+
const result = await probeSlack({ token });
|
|
212
|
+
// Surface the started adapter's liveness signal alongside the auth.test
|
|
213
|
+
// reachability check (observability only — never changes `ok`).
|
|
214
|
+
const live = accountRuntimes.get(accountId)?.adapter;
|
|
215
|
+
if (live && typeof live.lastEventAt === "function") {
|
|
216
|
+
return { ...result, lastEventAt: live.lastEventAt() };
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
},
|
|
220
|
+
config: {
|
|
221
|
+
listAccountIds: (cfg) => listSlackAccountIds(cfg),
|
|
222
|
+
resolveAccount: (cfg, accountId) => resolveSlackAccount(cfg, accountId ?? undefined),
|
|
223
|
+
defaultAccountId: () => SLACK_DEFAULT_ACCOUNT_ID,
|
|
224
|
+
isEnabled: (account) => account.enabled,
|
|
225
|
+
},
|
|
226
|
+
gateway: {
|
|
227
|
+
startAccount,
|
|
228
|
+
stopAccount,
|
|
229
|
+
logoutAccount,
|
|
230
|
+
},
|
|
231
|
+
outbound: {
|
|
232
|
+
sendText: async (params) => {
|
|
233
|
+
const accountId = params.target.accountId || SLACK_DEFAULT_ACCOUNT_ID;
|
|
234
|
+
const runtime = accountRuntimes.get(accountId);
|
|
235
|
+
if (!runtime) {
|
|
236
|
+
return { ok: false, error: `slack account "${accountId}" is not running` };
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const sent = await runtime.adapter.sendText(params.target.to, params.text, {
|
|
240
|
+
accountId,
|
|
241
|
+
...(params.target.threadId !== undefined ? { threadId: params.target.threadId } : {}),
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
ok: true,
|
|
245
|
+
...(sent && typeof sent === "object" && sent.messageId !== undefined
|
|
246
|
+
? { messageId: sent.messageId }
|
|
247
|
+
: {}),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
sendMedia: async (params) => {
|
|
255
|
+
const accountId = params.target.accountId || SLACK_DEFAULT_ACCOUNT_ID;
|
|
256
|
+
const runtime = accountRuntimes.get(accountId);
|
|
257
|
+
if (!runtime || !runtime.adapter.sendMedia) {
|
|
258
|
+
return { ok: false, error: `slack account "${accountId}" cannot send media right now` };
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
await runtime.adapter.sendMedia(params.target.to, {
|
|
262
|
+
kind: params.mediaType ?? "document",
|
|
263
|
+
path: params.mediaUrl,
|
|
264
|
+
...(params.caption !== undefined ? { caption: params.caption } : {}),
|
|
265
|
+
});
|
|
266
|
+
return { ok: true };
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
sendReaction: async (params) => {
|
|
273
|
+
const accountId = params.target.accountId || SLACK_DEFAULT_ACCOUNT_ID;
|
|
274
|
+
const runtime = accountRuntimes.get(accountId);
|
|
275
|
+
if (!runtime || !runtime.adapter.react) {
|
|
276
|
+
return { ok: false, error: `slack account "${accountId}" cannot react right now` };
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
await runtime.adapter.react(params.target.to, params.messageId, params.emoji);
|
|
280
|
+
return { ok: true };
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
actions: {
|
|
288
|
+
handleAction: async (params) => {
|
|
289
|
+
const accountId = params.accountId || params.target.accountId || SLACK_DEFAULT_ACCOUNT_ID;
|
|
290
|
+
const runtime = accountRuntimes.get(accountId);
|
|
291
|
+
if (!runtime || !runtime.adapter.handleAction) {
|
|
292
|
+
return { ok: false, error: `slack account "${accountId}" cannot perform message actions` };
|
|
293
|
+
}
|
|
294
|
+
return runtime.adapter.handleAction({
|
|
295
|
+
conversationId: params.target.to,
|
|
296
|
+
action: params.action,
|
|
297
|
+
accountId,
|
|
298
|
+
...(params.signal ? { signal: params.signal } : {}),
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
secrets: {
|
|
303
|
+
secretTargetRegistryEntries: [
|
|
304
|
+
{ path: "channels.slack.botToken", description: "Slack bot token (single-workspace)" },
|
|
305
|
+
{ path: "channels.slack.appToken", description: "Slack app-level token (Socket Mode, single-workspace)" },
|
|
306
|
+
{ path: "channels.slack.signingSecret", description: "Slack signing secret (Events API mode)" },
|
|
307
|
+
{ path: "channels.slack.accounts.*.botToken", description: "Slack bot token (per workspace)" },
|
|
308
|
+
{ path: "channels.slack.accounts.*.appToken", description: "Slack app-level token (per workspace)" },
|
|
309
|
+
{ path: "channels.slack.accounts.*.signingSecret", description: "Slack signing secret (per workspace)" },
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/** Default adapter factory — threads the per-account scope. */
|
|
315
|
+
function defaultSlackAdapterFactory(args) {
|
|
316
|
+
return createSlackAdapter({ accountId: args.accountId });
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../../src/agents/channels/slack/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack status / doctor probe — a lightweight `auth.test` reachability check.
|
|
3
|
+
*
|
|
4
|
+
* Slack is token-based and stateless on disk (Socket Mode keeps no local creds
|
|
5
|
+
* the status command can stat), so "is this channel actually working?" can only
|
|
6
|
+
* be answered by asking Slack. This probe does the cheapest possible call — a
|
|
7
|
+
* single `auth.test` over plain HTTPS (no `@slack/web-api`, no socket) — and
|
|
8
|
+
* reports the bot's identity + the workspace it's installed in so
|
|
9
|
+
* `brigade channels status` and `brigade doctor` can show real Slack health.
|
|
10
|
+
*
|
|
11
|
+
* It deliberately does NOT import `@slack/web-api`: a status check must stay
|
|
12
|
+
* fast + dependency-light, and `auth.test` is a trivial POST. The bot token is
|
|
13
|
+
* never logged; it rides in the `Authorization` header which is built locally
|
|
14
|
+
* and discarded.
|
|
15
|
+
*
|
|
16
|
+
* Returns a structured result the caller renders; never throws — a network
|
|
17
|
+
* failure / invalid token surfaces as `{ ok: false, error }` so the status
|
|
18
|
+
* command degrades gracefully instead of crashing. Slack mirror of
|
|
19
|
+
* `telegram/probe.ts`.
|
|
20
|
+
*/
|
|
21
|
+
/** Bot identity surfaced by the probe (from `auth.test`). */
|
|
22
|
+
export interface SlackProbeBot {
|
|
23
|
+
/** Bot user id (`U…` / `W…`). */
|
|
24
|
+
id?: string;
|
|
25
|
+
/** Bot/user handle Slack returns. */
|
|
26
|
+
name?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Workspace identity surfaced by the probe (from `auth.test`). */
|
|
29
|
+
export interface SlackProbeTeam {
|
|
30
|
+
/** Workspace (team) id (`T…`). */
|
|
31
|
+
id?: string;
|
|
32
|
+
/** Workspace display name. */
|
|
33
|
+
name?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Structured probe result. `ok` true ⇒ token valid + Slack reachable. */
|
|
36
|
+
export interface SlackProbeResult {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
/** HTTP status of the `auth.test` call, when one came back. */
|
|
39
|
+
status?: number;
|
|
40
|
+
/** Operator-facing error line when `ok` is false. */
|
|
41
|
+
error?: string;
|
|
42
|
+
/** Round-trip time in ms. */
|
|
43
|
+
elapsedMs: number;
|
|
44
|
+
/** Bot identity (populated on success). */
|
|
45
|
+
bot?: SlackProbeBot;
|
|
46
|
+
/** Workspace identity (populated on success). */
|
|
47
|
+
team?: SlackProbeTeam;
|
|
48
|
+
/**
|
|
49
|
+
* Epoch ms of the most recent inbound event seen by the STARTED adapter for
|
|
50
|
+
* this account, when one is running (liveness signal — `auth.test` proves the
|
|
51
|
+
* token but not that the events stream is flowing). `null` when no inbound has
|
|
52
|
+
* arrived yet; `undefined` when the probe couldn't consult a live adapter
|
|
53
|
+
* (e.g. a cold status check before the channel started). Observability only —
|
|
54
|
+
* a stale value never means "unhealthy" (a quiet channel is idle, not down).
|
|
55
|
+
*/
|
|
56
|
+
lastEventAt?: number | null;
|
|
57
|
+
}
|
|
58
|
+
export interface SlackProbeArgs {
|
|
59
|
+
/** The resolved bot token (`xoxb-…`). NEVER logged. */
|
|
60
|
+
token: string;
|
|
61
|
+
/** Injectable fetch (defaults to global fetch) — lets tests stub the call. */
|
|
62
|
+
fetchImpl?: typeof fetch;
|
|
63
|
+
/** Probe timeout in ms (default 8s). */
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Run an `auth.test` probe. Resolves to a structured result describing whether
|
|
68
|
+
* the token is valid + Slack is reachable, plus the bot + workspace identity.
|
|
69
|
+
* Never rejects.
|
|
70
|
+
*/
|
|
71
|
+
export declare function probeSlack(args: SlackProbeArgs): Promise<SlackProbeResult>;
|
|
72
|
+
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/slack/probe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC7B,iCAAiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC9B,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,iDAAiD;IACjD,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC9B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiFhF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack status / doctor probe — a lightweight `auth.test` reachability check.
|
|
3
|
+
*
|
|
4
|
+
* Slack is token-based and stateless on disk (Socket Mode keeps no local creds
|
|
5
|
+
* the status command can stat), so "is this channel actually working?" can only
|
|
6
|
+
* be answered by asking Slack. This probe does the cheapest possible call — a
|
|
7
|
+
* single `auth.test` over plain HTTPS (no `@slack/web-api`, no socket) — and
|
|
8
|
+
* reports the bot's identity + the workspace it's installed in so
|
|
9
|
+
* `brigade channels status` and `brigade doctor` can show real Slack health.
|
|
10
|
+
*
|
|
11
|
+
* It deliberately does NOT import `@slack/web-api`: a status check must stay
|
|
12
|
+
* fast + dependency-light, and `auth.test` is a trivial POST. The bot token is
|
|
13
|
+
* never logged; it rides in the `Authorization` header which is built locally
|
|
14
|
+
* and discarded.
|
|
15
|
+
*
|
|
16
|
+
* Returns a structured result the caller renders; never throws — a network
|
|
17
|
+
* failure / invalid token surfaces as `{ ok: false, error }` so the status
|
|
18
|
+
* command degrades gracefully instead of crashing. Slack mirror of
|
|
19
|
+
* `telegram/probe.ts`.
|
|
20
|
+
*/
|
|
21
|
+
const SLACK_AUTH_TEST_URL = "https://slack.com/api/auth.test";
|
|
22
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 8_000;
|
|
23
|
+
/**
|
|
24
|
+
* Run an `auth.test` probe. Resolves to a structured result describing whether
|
|
25
|
+
* the token is valid + Slack is reachable, plus the bot + workspace identity.
|
|
26
|
+
* Never rejects.
|
|
27
|
+
*/
|
|
28
|
+
export async function probeSlack(args) {
|
|
29
|
+
const started = Date.now();
|
|
30
|
+
const token = (args.token ?? "").trim();
|
|
31
|
+
if (!token) {
|
|
32
|
+
return { ok: false, error: "no Slack bot token configured", elapsedMs: 0 };
|
|
33
|
+
}
|
|
34
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
35
|
+
const timeoutMs = args.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
38
|
+
if (typeof timer.unref === "function")
|
|
39
|
+
timer.unref();
|
|
40
|
+
try {
|
|
41
|
+
const res = await doFetch(SLACK_AUTH_TEST_URL, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${token}`,
|
|
45
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
46
|
+
},
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
});
|
|
49
|
+
const elapsedMs = Date.now() - started;
|
|
50
|
+
let body = null;
|
|
51
|
+
try {
|
|
52
|
+
body = (await res.json());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
body = null;
|
|
56
|
+
}
|
|
57
|
+
// Slack always returns HTTP 200 for API calls; success/failure rides on the
|
|
58
|
+
// JSON `ok` flag + an `error` code string (e.g. `invalid_auth`).
|
|
59
|
+
if (!res.ok || !body?.ok) {
|
|
60
|
+
const code = body?.error ?? "";
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
status: res.status,
|
|
64
|
+
error: code === "invalid_auth" || code === "not_authed" || code === "account_inactive"
|
|
65
|
+
? "Slack rejected the bot token — reinstall the app and paste a fresh `xoxb-` token."
|
|
66
|
+
: code
|
|
67
|
+
? `Slack auth.test failed (${code}).`
|
|
68
|
+
: `Slack auth.test failed (HTTP ${res.status}).`,
|
|
69
|
+
elapsedMs,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
status: res.status,
|
|
75
|
+
elapsedMs,
|
|
76
|
+
bot: {
|
|
77
|
+
...(typeof body.user_id === "string" ? { id: body.user_id } : {}),
|
|
78
|
+
...(typeof body.user === "string" ? { name: body.user } : {}),
|
|
79
|
+
},
|
|
80
|
+
team: {
|
|
81
|
+
...(typeof body.team_id === "string" ? { id: body.team_id } : {}),
|
|
82
|
+
...(typeof body.team === "string" ? { name: body.team } : {}),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
const elapsedMs = Date.now() - started;
|
|
88
|
+
const aborted = controller.signal.aborted;
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
error: aborted
|
|
92
|
+
? `Slack auth.test timed out after ${timeoutMs}ms`
|
|
93
|
+
: err instanceof Error
|
|
94
|
+
? err.message
|
|
95
|
+
: String(err),
|
|
96
|
+
elapsedMs,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../../../src/agents/channels/slack/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy agent builder for the Slack SDKs.
|
|
3
|
+
*
|
|
4
|
+
* Both `@slack/web-api` (`WebClient`) and `@slack/socket-mode`
|
|
5
|
+
* (`SocketModeClient`) accept an `agent` option that is a standard Node
|
|
6
|
+
* `http(s).Agent` (they drive HTTP via `node-fetch` + open the Socket Mode
|
|
7
|
+
* websocket via `ws`, both of which honour an injected agent). To route Slack
|
|
8
|
+
* through a proxy we build the matching agent from the proxy URL scheme:
|
|
9
|
+
*
|
|
10
|
+
* - `http://` / `https://` → an HTTP CONNECT proxy via `https-proxy-agent`.
|
|
11
|
+
* - `socks` / `socks4(a)` / `socks5(h)` → a SOCKS proxy via `socks-proxy-agent`.
|
|
12
|
+
*
|
|
13
|
+
* Both packages are lazy-imported (only paid for when a proxy is configured) and
|
|
14
|
+
* produce an `http.Agent` the Slack SDKs use as-is — the analogue of Telegram's
|
|
15
|
+
* undici dispatcher, reshaped to the agent seam the Slack SDKs expose. A
|
|
16
|
+
* malformed URL / missing module throws; the caller logs + connects directly.
|
|
17
|
+
*/
|
|
18
|
+
/** True when a proxy URL scheme names a SOCKS proxy (needs the SOCKS agent). */
|
|
19
|
+
export declare function isSocksProxyScheme(scheme: string | undefined): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Build a Node `http.Agent` that tunnels through `proxyUrl`. SOCKS schemes use
|
|
22
|
+
* `socks-proxy-agent`; everything else (http/https) uses `https-proxy-agent`.
|
|
23
|
+
* The returned agent is handed to `new WebClient(token, { agent })` and to the
|
|
24
|
+
* `SocketModeClient({ appToken, clientOptions: { agent } })` construction.
|
|
25
|
+
*
|
|
26
|
+
* @throws if the proxy URL is malformed or a module can't be loaded — the caller
|
|
27
|
+
* logs + falls back to a direct connection.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildSlackProxyAgent(proxyUrl: string): Promise<unknown>;
|
|
30
|
+
//# sourceMappingURL=proxy-agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-agent.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/slack/proxy-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAItE;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAS7E"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy agent builder for the Slack SDKs.
|
|
3
|
+
*
|
|
4
|
+
* Both `@slack/web-api` (`WebClient`) and `@slack/socket-mode`
|
|
5
|
+
* (`SocketModeClient`) accept an `agent` option that is a standard Node
|
|
6
|
+
* `http(s).Agent` (they drive HTTP via `node-fetch` + open the Socket Mode
|
|
7
|
+
* websocket via `ws`, both of which honour an injected agent). To route Slack
|
|
8
|
+
* through a proxy we build the matching agent from the proxy URL scheme:
|
|
9
|
+
*
|
|
10
|
+
* - `http://` / `https://` → an HTTP CONNECT proxy via `https-proxy-agent`.
|
|
11
|
+
* - `socks` / `socks4(a)` / `socks5(h)` → a SOCKS proxy via `socks-proxy-agent`.
|
|
12
|
+
*
|
|
13
|
+
* Both packages are lazy-imported (only paid for when a proxy is configured) and
|
|
14
|
+
* produce an `http.Agent` the Slack SDKs use as-is — the analogue of Telegram's
|
|
15
|
+
* undici dispatcher, reshaped to the agent seam the Slack SDKs expose. A
|
|
16
|
+
* malformed URL / missing module throws; the caller logs + connects directly.
|
|
17
|
+
*/
|
|
18
|
+
/** True when a proxy URL scheme names a SOCKS proxy (needs the SOCKS agent). */
|
|
19
|
+
export function isSocksProxyScheme(scheme) {
|
|
20
|
+
if (!scheme)
|
|
21
|
+
return false;
|
|
22
|
+
const s = scheme.toLowerCase();
|
|
23
|
+
return s === "socks" || s === "socks4" || s === "socks4a" || s === "socks5" || s === "socks5h";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a Node `http.Agent` that tunnels through `proxyUrl`. SOCKS schemes use
|
|
27
|
+
* `socks-proxy-agent`; everything else (http/https) uses `https-proxy-agent`.
|
|
28
|
+
* The returned agent is handed to `new WebClient(token, { agent })` and to the
|
|
29
|
+
* `SocketModeClient({ appToken, clientOptions: { agent } })` construction.
|
|
30
|
+
*
|
|
31
|
+
* @throws if the proxy URL is malformed or a module can't be loaded — the caller
|
|
32
|
+
* logs + falls back to a direct connection.
|
|
33
|
+
*/
|
|
34
|
+
export async function buildSlackProxyAgent(proxyUrl) {
|
|
35
|
+
const url = new URL(proxyUrl); // throws on a malformed URL → caller catches
|
|
36
|
+
const scheme = url.protocol.replace(/:$/, "").toLowerCase();
|
|
37
|
+
if (isSocksProxyScheme(scheme)) {
|
|
38
|
+
const { SocksProxyAgent } = await import("socks-proxy-agent");
|
|
39
|
+
return new SocksProxyAgent(proxyUrl);
|
|
40
|
+
}
|
|
41
|
+
const { HttpsProxyAgent } = await import("https-proxy-agent");
|
|
42
|
+
return new HttpsProxyAgent(proxyUrl);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=proxy-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-agent.js","sourceRoot":"","sources":["../../../../src/agents/channels/slack/proxy-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC5D,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/B,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,SAAS,CAAC;AAChG,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IAC1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,6CAA6C;IAC5E,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC9D,OAAO,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC9D,OAAO,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack reasoning-lane split (OPTIONAL, default OFF).
|
|
3
|
+
*
|
|
4
|
+
* Brigade strips `<think>…</think>` reasoning from every channel reply via the
|
|
5
|
+
* shared `sanitizeReplyForChannel` — recipients see only the final answer. For
|
|
6
|
+
* Slack an operator can OPT IN (config `channels.slack.surfaceReasoning: true`)
|
|
7
|
+
* to ALSO receive the reasoning trace as a separate, prefixed message.
|
|
8
|
+
*
|
|
9
|
+
* This module is the pure splitter: given the raw agent reply, it returns
|
|
10
|
+
* `{ reasoningText?, answerText }`. When surfacing is OFF the pipeline never
|
|
11
|
+
* calls this and behavior is byte-identical to today (strip + send answer). When
|
|
12
|
+
* ON, the pipeline sends `reasoningText` first (a `🧠 Reasoning:` block) then the
|
|
13
|
+
* normal sanitized answer.
|
|
14
|
+
*
|
|
15
|
+
* The answer half is computed with the SAME sanitizer the default path uses, so
|
|
16
|
+
* enabling reasoning never changes what the answer message contains — it only
|
|
17
|
+
* ADDS the reasoning message in front.
|
|
18
|
+
*
|
|
19
|
+
* Pure / deterministic / dependency-light (re-uses `sanitizeReplyForChannel`).
|
|
20
|
+
* Slack mirror of `telegram/reasoning-lane.ts`.
|
|
21
|
+
*/
|
|
22
|
+
/** Prefix on the reasoning message so the recipient knows it's the trace. */
|
|
23
|
+
export declare const REASONING_PREFIX = "\uD83E\uDDE0 Reasoning:\n";
|
|
24
|
+
export interface SlackReasoningSplit {
|
|
25
|
+
/** The extracted reasoning trace (already prefixed), or undefined when none. */
|
|
26
|
+
reasoningText?: string;
|
|
27
|
+
/** The user-facing answer (sanitized exactly as the default path produces). */
|
|
28
|
+
answerText: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extract the concatenated text INSIDE `<think>…</think>` blocks. Handles
|
|
32
|
+
* multiple blocks and an unclosed trailing block (model truncated mid-thought).
|
|
33
|
+
* Returns "" when there's no reasoning content.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractReasoning(text: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Split a raw agent reply into an optional reasoning message + the sanitized
|
|
38
|
+
* answer. The answer is identical to `sanitizeReplyForChannel(raw)`; the
|
|
39
|
+
* reasoning is only populated when a `<think>` block carried content.
|
|
40
|
+
*/
|
|
41
|
+
export declare function splitSlackReasoning(raw: string): SlackReasoningSplit;
|
|
42
|
+
//# sourceMappingURL=reasoning-lane.d.ts.map
|