@open-everywhere/provider-slack 0.8.1 → 0.9.0
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/agent/channels/slack-agent-session.ts +100 -63
- package/agent/channels/slack-events.ts +1 -1
- package/agent/channels/slack-front.ts +15 -8
- package/agent/channels/slack-identity.ts +8 -0
- package/agent/channels/slack-web-api.ts +3 -3
- package/agent/channels/slack-working-signs.ts +71 -0
- package/agent/channels/slack.ts +36 -50
- package/agent/docs-generated.ts +36 -16
- package/agent/ops.ts +4 -0
- package/agent/register.ts +4 -2
- package/agent/setup/claim.ts +9 -8
- package/agent/setup/credentials.ts +82 -79
- package/agent/setup/eve-credentials.ts +2 -2
- package/agent/setup/page.ts +3 -3
- package/agent/status.ts +12 -4
- package/dist/agent/channels/slack-agent-session.d.ts +19 -3
- package/dist/agent/channels/slack-agent-session.d.ts.map +1 -1
- package/dist/agent/channels/slack-agent-session.js +102 -65
- package/dist/agent/channels/slack-agent-session.js.map +1 -1
- package/dist/agent/channels/slack-events.js +1 -1
- package/dist/agent/channels/slack-events.js.map +1 -1
- package/dist/agent/channels/slack-front.d.ts.map +1 -1
- package/dist/agent/channels/slack-front.js +16 -9
- package/dist/agent/channels/slack-front.js.map +1 -1
- package/dist/agent/channels/slack-identity.d.ts +7 -0
- package/dist/agent/channels/slack-identity.d.ts.map +1 -1
- package/dist/agent/channels/slack-identity.js +2 -0
- package/dist/agent/channels/slack-identity.js.map +1 -1
- package/dist/agent/channels/slack-web-api.js +3 -3
- package/dist/agent/channels/slack-web-api.js.map +1 -1
- package/dist/agent/channels/slack-working-signs.d.ts +34 -0
- package/dist/agent/channels/slack-working-signs.d.ts.map +1 -0
- package/dist/agent/channels/slack-working-signs.js +63 -0
- package/dist/agent/channels/slack-working-signs.js.map +1 -0
- package/dist/agent/channels/slack.d.ts.map +1 -1
- package/dist/agent/channels/slack.js +28 -46
- package/dist/agent/channels/slack.js.map +1 -1
- package/dist/agent/docs-generated.d.ts.map +1 -1
- package/dist/agent/docs-generated.js +36 -16
- package/dist/agent/docs-generated.js.map +1 -1
- package/dist/agent/ops.d.ts.map +1 -1
- package/dist/agent/ops.js +4 -1
- package/dist/agent/ops.js.map +1 -1
- package/dist/agent/register.d.ts.map +1 -1
- package/dist/agent/register.js +4 -2
- package/dist/agent/register.js.map +1 -1
- package/dist/agent/setup/claim.d.ts.map +1 -1
- package/dist/agent/setup/claim.js +9 -8
- package/dist/agent/setup/claim.js.map +1 -1
- package/dist/agent/setup/credentials.d.ts +21 -20
- package/dist/agent/setup/credentials.d.ts.map +1 -1
- package/dist/agent/setup/credentials.js +71 -75
- package/dist/agent/setup/credentials.js.map +1 -1
- package/dist/agent/setup/eve-credentials.js +2 -2
- package/dist/agent/setup/eve-credentials.js.map +1 -1
- package/dist/agent/setup/page.js +3 -3
- package/dist/agent/setup/page.js.map +1 -1
- package/dist/agent/status.d.ts +10 -2
- package/dist/agent/status.d.ts.map +1 -1
- package/dist/agent/status.js +12 -4
- package/dist/agent/status.js.map +1 -1
- package/package.json +6 -2
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
// WHERE THE SLACK BUNDLE'S TWO SECRETS COME FROM (OPE-2231) — the signing secret that verifies
|
|
2
2
|
// Slack's requests, and the bot token that signs ours.
|
|
3
3
|
//
|
|
4
|
-
// Before this module there was one answer:
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// Before this module there was one answer: the SLACK_SIGNING_SECRET and SLACK_BOT_TOKEN env vars,
|
|
5
|
+
// read inline at each call site. A `/setup` run now captures both into the credential store, so there
|
|
6
|
+
// are two homes and an order is needed.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
8
|
+
// THE OPERATOR SECRET COMES FIRST (OPE-2650). The first home is the operator secret of the same name
|
|
9
|
+
// — `oe secret set SLACK_BOT_TOKEN`, read through core's `operatorSecret`, which takes the db store
|
|
10
|
+
// first and the env var as its warned on-ramp. The second home is the value `/setup` stored under the
|
|
11
|
+
// `slack-app:` refs. Before OPE-2650 the first home was the env var alone (the OPE-2231 ruling: "env
|
|
12
|
+
// as fallback (env wins if set, for existing installs)"). The reason for that order holds unchanged:
|
|
13
13
|
//
|
|
14
14
|
// • EVERY deployment that exists today holds these two in env and works. This change must not be
|
|
15
|
-
// able to alter which Slack app a working deployment speaks as.
|
|
16
|
-
// somebody started and abandoned, or a second workspace's install, would silently take over
|
|
17
|
-
// live deployment's Slack identity at the next cold start, with a 401 loop as the symptom and
|
|
15
|
+
// able to alter which Slack app a working deployment speaks as. `/setup`-first could: a `/setup`
|
|
16
|
+
// run somebody started and abandoned, or a second workspace's install, would silently take over
|
|
17
|
+
// a live deployment's Slack identity at the next cold start, with a 401 loop as the symptom and
|
|
18
18
|
// nothing in the logs saying a different secret had been picked.
|
|
19
|
-
// •
|
|
20
|
-
// resolved to before
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// • A fresh hosted install sets neither env var (the deploy button asks for one value, the
|
|
25
|
-
// credential encryption key), so it reads the store, which is where `/setup` put them.
|
|
19
|
+
// • Operator-secret-first cannot do that. A deployment with the env vars set and no stored operator
|
|
20
|
+
// secret resolves to exactly the value it resolved to before, every time. An operator who stores
|
|
21
|
+
// the value with `oe secret set` chose it on purpose, so it wins the same way the env var did.
|
|
22
|
+
// • A fresh hosted install sets neither (the deploy button asks for one value, the credential
|
|
23
|
+
// encryption key), so it reads the `/setup` value.
|
|
26
24
|
//
|
|
27
|
-
// The cost, stated plainly: an operator who runs `/setup` on a deployment that still
|
|
28
|
-
//
|
|
29
|
-
// visible instead of mysterious — the
|
|
25
|
+
// The cost, stated plainly: an operator who runs `/setup` on a deployment that still holds the
|
|
26
|
+
// operator secrets will find the `/setup` credentials ignored. {@link slackCredentialSourceNotice} is
|
|
27
|
+
// what makes that visible instead of mysterious — the resolver logs it once per name.
|
|
30
28
|
//
|
|
31
29
|
// MEMOISED PER PROCESS WITH A SHORT TTL, store reads only. The signature check runs on every inbound
|
|
32
30
|
// Slack request and the bot token on every outbound call, so a vault round trip per request is not
|
|
@@ -44,26 +42,30 @@
|
|
|
44
42
|
//
|
|
45
43
|
// A TTL bounds that to {@link HIT_TTL_MS} without giving up the per-request cost, which is what the
|
|
46
44
|
// memo was for. It is deliberately not a cache invalidated by the reopen path: a reopen runs in ONE
|
|
47
|
-
// process and these memos live in all of them, so nothing a reopen does can reach them. Only time can.
|
|
48
|
-
//
|
|
45
|
+
// process and these memos live in all of them, so nothing a reopen does can reach them. Only time can. The operator-secret
|
|
46
|
+
// arm has its own thirty-second memo in core (`operatorSecret`). A MISS is remembered only BRIEFLY ({@link MISS_TTL_MS}) — long enough that an
|
|
49
47
|
// unauthenticated POST to the public Slack route cannot drive one database query per request, short
|
|
50
48
|
// enough that a process which booted before `/setup` finished still picks the credentials up without a
|
|
51
49
|
// restart.
|
|
52
50
|
|
|
53
|
-
import { encryptedKvVault, type EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
|
|
51
|
+
import { encryptedKvVault, operatorSecret, type EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
|
|
54
52
|
import { SLACK_BOT_TOKEN_REF, SLACK_SIGNING_SECRET_REF } from "./install-state.js";
|
|
55
53
|
|
|
56
|
-
/** The
|
|
57
|
-
const
|
|
58
|
-
/** The
|
|
59
|
-
const
|
|
54
|
+
/** The operator secret holding the app's request-signing secret — the pre-OPE-2231 home, still first. */
|
|
55
|
+
const SLACK_SIGNING_SECRET_NAME = "SLACK_SIGNING_SECRET";
|
|
56
|
+
/** The operator secret holding the workspace bot token — the pre-OPE-2231 home, still first. */
|
|
57
|
+
const SLACK_BOT_TOKEN_NAME = "SLACK_BOT_TOKEN";
|
|
60
58
|
|
|
61
|
-
/** Which home answered. Reported by {@link resolveSlackSecret} for the suite and the notice.
|
|
62
|
-
|
|
59
|
+
/** Which home answered. Reported by {@link resolveSlackSecret} for the suite and the notice.
|
|
60
|
+
* `operator` is the operator secret (`oe secret set`, with env as its on-ramp, OPE-2650); `store` is
|
|
61
|
+
* the value a `/setup` run captured. */
|
|
62
|
+
export type SlackSecretSource = "operator" | "store";
|
|
63
63
|
|
|
64
|
-
/** Injectable seams for tests — the live defaults are the real vault and
|
|
64
|
+
/** Injectable seams for tests — the live defaults are the real vault and {@link operatorSecret}. */
|
|
65
65
|
export interface SlackCredentialDeps {
|
|
66
66
|
readonly vault?: EncryptedKvVault;
|
|
67
|
+
/** TEST SEAM: a synthetic stand-in for the operator-secret answer, read by name. Absent on the live
|
|
68
|
+
* path, which resolves through {@link operatorSecret} (the db store, then env as the on-ramp). */
|
|
67
69
|
readonly env?: NodeJS.ProcessEnv;
|
|
68
70
|
/** The clock the hit and miss TTLs are read against. Tests advance it to prove a cached value stops
|
|
69
71
|
* being reused; the live default is `Date.now`. */
|
|
@@ -140,35 +142,31 @@ export function resetSlackCredentialNotices(): void {
|
|
|
140
142
|
* say. The `/setup` page renders this before a run so the operator learns it BEFORE capturing
|
|
141
143
|
* credentials the deployment will ignore, and {@link resolveSlackSecret} logs it once per name.
|
|
142
144
|
*/
|
|
143
|
-
function slackCredentialSourceNotice(
|
|
145
|
+
function slackCredentialSourceNotice(name: string): string {
|
|
144
146
|
return (
|
|
145
|
-
`[open-everywhere:slack-setup] ${
|
|
146
|
-
`IGNORES the one
|
|
147
|
-
`alter which Slack app an existing deployment speaks as. To use the
|
|
147
|
+
`[open-everywhere:slack-setup] ${name} is set as an operator secret (or as its env on-ramp), so this deployment uses that ` +
|
|
148
|
+
`value and IGNORES the one /setup stored. That is the documented order (OPE-2231, OPE-2650): the operator secret wins, so ` +
|
|
149
|
+
`landing this change cannot alter which Slack app an existing deployment speaks as. To use the /setup value instead, run ` +
|
|
150
|
+
`\`oe secret rm ${name}\`, unset the env var of the same name, and redeploy.`
|
|
148
151
|
);
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
/**
|
|
152
|
-
* Resolve one Slack secret:
|
|
155
|
+
* Resolve one Slack secret: the operator secret if set, else the `/setup` value, else `undefined`.
|
|
153
156
|
*
|
|
154
157
|
* `undefined` is a real answer, not an error — it means this deployment has not been through `/setup`
|
|
155
|
-
* and holds no
|
|
158
|
+
* and holds no operator secret either. Each caller fails in its own way (the front route 401s; an outbound
|
|
156
159
|
* call throws the message eve's primitive always threw), which is what keeps this function free of
|
|
157
160
|
* any opinion about what a missing credential should do.
|
|
158
161
|
*/
|
|
159
162
|
export async function resolveSlackSecret(
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
name: string,
|
|
164
|
+
fromOperator: string,
|
|
162
165
|
ref: string,
|
|
163
166
|
deps: SlackCredentialDeps = {},
|
|
164
167
|
): Promise<ResolvedSlackSecret | undefined> {
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
// runtime reads can be named in `scripts/env-read-allowlist.ts`; a read through a PARAMETER
|
|
168
|
-
// (`env[envName]`) resolves to nothing, so the gate cannot tell which variable it reaches and
|
|
169
|
-
// refuses the site. The two public helpers below each do their own read with a module-level const
|
|
170
|
-
// as the key, which keeps both names visible to the allowlist — and leaves this function with no
|
|
171
|
-
// environment access at all.
|
|
168
|
+
// The operator-secret value is passed in, not read here, so a suite drives this function with no
|
|
169
|
+
// store and no environment. The two public helpers below do the read.
|
|
172
170
|
|
|
173
171
|
// An injected vault means a test driving a specific store state — bypass the memo entirely, so a
|
|
174
172
|
// test can rewrite a row and see the effect without reaching for a reset.
|
|
@@ -180,10 +178,10 @@ export async function resolveSlackSecret(
|
|
|
180
178
|
const cached = memoApplies ? memo.get(ref) : undefined;
|
|
181
179
|
const memoised = cached !== undefined && cached.until > now ? cached.value : undefined;
|
|
182
180
|
|
|
183
|
-
if (
|
|
184
|
-
// Only say the
|
|
185
|
-
// the operator who ran `/setup` and is about to wonder why nothing changed, not for
|
|
186
|
-
// deployment that has always used env.
|
|
181
|
+
if (fromOperator.length > 0) {
|
|
182
|
+
// Only say the operator secret is shadowing a `/setup` value when one is ACTUALLY stored — the
|
|
183
|
+
// notice is for the operator who ran `/setup` and is about to wonder why nothing changed, not for
|
|
184
|
+
// every deployment that has always used env.
|
|
187
185
|
//
|
|
188
186
|
// 🔴 THE CHECK IS MARKED DONE WHETHER OR NOT IT WARNED, and that is the whole reason this is a
|
|
189
187
|
// separate set from `noticed`. Keying the guard off "have I warned?" meant a deployment with the
|
|
@@ -191,15 +189,15 @@ export async function resolveSlackSecret(
|
|
|
191
189
|
// that it had looked, so it re-read the vault on EVERY resolution: one store round trip per
|
|
192
190
|
// inbound Slack request, for an answer that was always going to be the env value. One read per
|
|
193
191
|
// name per process, then never again.
|
|
194
|
-
if (!shadowChecked.has(
|
|
195
|
-
shadowChecked.add(
|
|
192
|
+
if (!shadowChecked.has(name)) {
|
|
193
|
+
shadowChecked.add(name);
|
|
196
194
|
const stored = memoised ?? (await readStored(ref, vault)); // best-effort; a store failure says nothing
|
|
197
195
|
if (stored !== undefined) {
|
|
198
|
-
noticed.add(
|
|
199
|
-
console.warn(slackCredentialSourceNotice(
|
|
196
|
+
noticed.add(name);
|
|
197
|
+
console.warn(slackCredentialSourceNotice(name));
|
|
200
198
|
}
|
|
201
199
|
}
|
|
202
|
-
return { value:
|
|
200
|
+
return { value: fromOperator, source: "operator" };
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
if (memoised !== undefined) return { value: memoised, source: "store" };
|
|
@@ -235,48 +233,53 @@ async function readStored(ref: string, injected: EncryptedKvVault | undefined):
|
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
235
|
|
|
236
|
+
/**
|
|
237
|
+
* The operator-secret answer for `name`, trimmed, or `""` when it is set nowhere. A test passes
|
|
238
|
+
* `deps.env` as a synthetic stand-in; the live path reads {@link operatorSecret} (the db store, then
|
|
239
|
+
* env as the on-ramp).
|
|
240
|
+
*/
|
|
241
|
+
async function fromOperatorSecret(name: string, deps: SlackCredentialDeps): Promise<string> {
|
|
242
|
+
const value = deps.env !== undefined ? deps.env[name] : await operatorSecret(name);
|
|
243
|
+
return (value ?? "").trim();
|
|
244
|
+
}
|
|
245
|
+
|
|
238
246
|
/** The app's request-signing secret, or `undefined` when this deployment holds none. */
|
|
239
247
|
export async function slackSigningSecret(deps: SlackCredentialDeps = {}): Promise<string | undefined> {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
return (await resolveSlackSecret(SLACK_SIGNING_SECRET_ENV, fromEnv, SLACK_SIGNING_SECRET_REF, deps))?.value;
|
|
248
|
+
const fromOperator = await fromOperatorSecret(SLACK_SIGNING_SECRET_NAME, deps);
|
|
249
|
+
return (await resolveSlackSecret(SLACK_SIGNING_SECRET_NAME, fromOperator, SLACK_SIGNING_SECRET_REF, deps))?.value;
|
|
243
250
|
}
|
|
244
251
|
|
|
245
252
|
/** The workspace bot token, or `undefined` when this deployment holds none. */
|
|
246
253
|
export async function slackBotToken(deps: SlackCredentialDeps = {}): Promise<string | undefined> {
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
return (await resolveSlackSecret(SLACK_BOT_TOKEN_ENV, fromEnv, SLACK_BOT_TOKEN_REF, deps))?.value;
|
|
254
|
+
const fromOperator = await fromOperatorSecret(SLACK_BOT_TOKEN_NAME, deps);
|
|
255
|
+
return (await resolveSlackSecret(SLACK_BOT_TOKEN_NAME, fromOperator, SLACK_BOT_TOKEN_REF, deps))?.value;
|
|
250
256
|
}
|
|
251
257
|
|
|
252
258
|
/**
|
|
253
259
|
* Does this deployment hold a Slack bot token in EITHER home? The telemetry heartbeat's
|
|
254
|
-
* credential-presence probe (OPE-2241). Same order as {@link slackBotToken}:
|
|
260
|
+
* credential-presence probe (OPE-2241). Same order as {@link slackBotToken}: the operator secret
|
|
261
|
+
* first, then the `/setup` value.
|
|
255
262
|
*
|
|
256
|
-
* 🔴 A SET
|
|
257
|
-
* when the
|
|
258
|
-
* a process that does no Slack work a store round trip
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* through the same memo as every outbound call.
|
|
263
|
+
* 🔴 A SET OPERATOR SECRET ANSWERS WITHOUT THE `/setup` READ. `slackBotToken` reads the `/setup` value
|
|
264
|
+
* once per process even when the operator secret is set, for the shadow notice. On the heartbeat path
|
|
265
|
+
* that read would cost a process that does no Slack work a second store round trip. So the operator
|
|
266
|
+
* secret is checked here first, and only an empty one reads the `/setup` value, through the same memo
|
|
267
|
+
* as every outbound call.
|
|
262
268
|
*/
|
|
263
269
|
export async function slackCredentialsConfigured(deps: SlackCredentialDeps = {}): Promise<boolean> {
|
|
264
|
-
|
|
265
|
-
if ((env[SLACK_BOT_TOKEN_ENV] ?? "").trim().length > 0) return true;
|
|
270
|
+
if ((await fromOperatorSecret(SLACK_BOT_TOKEN_NAME, deps)).length > 0) return true;
|
|
266
271
|
return (await slackBotToken(deps)) !== undefined;
|
|
267
272
|
}
|
|
268
273
|
|
|
269
274
|
/**
|
|
270
|
-
* Is
|
|
271
|
-
* page's "this
|
|
272
|
-
*
|
|
273
|
-
* Unrolled rather than looped over the two names for the same reason {@link resolveSlackSecret} takes
|
|
274
|
-
* its env value as a parameter: the gate resolves a const key and nothing else.
|
|
275
|
+
* Is each operator secret set, for the ONE caller that needs to ask without resolving — the `/setup`
|
|
276
|
+
* page's "this operator secret will shadow what you are about to store" notice.
|
|
275
277
|
*/
|
|
276
|
-
export function
|
|
277
|
-
|
|
278
|
+
export async function slackOperatorSecretPresent(
|
|
279
|
+
deps: SlackCredentialDeps = {},
|
|
280
|
+
): Promise<{ readonly signingSecret: boolean; readonly botToken: boolean }> {
|
|
278
281
|
return {
|
|
279
|
-
signingSecret: (
|
|
280
|
-
botToken: (
|
|
282
|
+
signingSecret: (await fromOperatorSecret(SLACK_SIGNING_SECRET_NAME, deps)).length > 0,
|
|
283
|
+
botToken: (await fromOperatorSecret(SLACK_BOT_TOKEN_NAME, deps)).length > 0,
|
|
281
284
|
};
|
|
282
285
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// handler, and eve verifies AGAIN — `verifyInbound` in eve's `slackChannel.js`:
|
|
6
6
|
//
|
|
7
7
|
// signingSecret: credentials?.signingSecret ?? (credentials?.webhookVerifier ? undefined
|
|
8
|
-
// :
|
|
8
|
+
// : <the SLACK_SIGNING_SECRET env var>)
|
|
9
9
|
//
|
|
10
10
|
// `channels/slack.ts` passed NO `credentials`, so eve resolved the secret from `process.env` and
|
|
11
11
|
// nowhere else. Moving OE's own read to the credential store therefore produced a deployment where
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
//
|
|
22
22
|
// `webhookVerifier` rather than `signingSecret`: eve takes `signingSecret` as a plain string, which
|
|
23
23
|
// means reading it once at module load. Supplying the verifier instead also makes eve SKIP its
|
|
24
|
-
//
|
|
24
|
+
// env fallback for SLACK_SIGNING_SECRET entirely (see the expression above), so there is no path
|
|
25
25
|
// left on which eve and OE can disagree about which secret this request is held to.
|
|
26
26
|
|
|
27
27
|
import { slackSignatureMatches, SLACK_SIGNATURE_HEADER, SLACK_TIMESTAMP_HEADER } from "../channels/slack-signature.js";
|
package/agent/setup/page.ts
CHANGED
|
@@ -384,9 +384,9 @@ function manifestBotScopes(): string {
|
|
|
384
384
|
//
|
|
385
385
|
// An earlier draft warned on screen 2 that a set `SLACK_BOT_TOKEN` / `SLACK_SIGNING_SECRET` would
|
|
386
386
|
// shadow whatever the run was about to store. That warning is now unreachable, and it was the wrong
|
|
387
|
-
// remedy anyway: `slackSetupOpen` (claim.ts) CLOSES the page entirely when either
|
|
388
|
-
//
|
|
389
|
-
// would ever read. A warning the operator can click past is worse than a page that refuses — it ends
|
|
387
|
+
// remedy anyway: `slackSetupOpen` (claim.ts) CLOSES the page entirely when either operator secret is
|
|
388
|
+
// set (stored, or as its env var on-ramp), because the resolver puts the operator secret first and a
|
|
389
|
+
// run on such a deployment would write credentials nothing would ever read. A warning the operator can click past is worse than a page that refuses — it ends
|
|
390
390
|
// with a Slack app created, credentials stored, the page locked, and no behaviour change at all.
|
|
391
391
|
//
|
|
392
392
|
// The resolver still carries the warning for the case this page cannot cover: a var set AFTER a
|
package/agent/status.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
unifiedDomainSessionKey,
|
|
20
20
|
type SurfaceStatus,
|
|
21
21
|
} from "@open-everywhere/core/agent/adapter-api.js";
|
|
22
|
+
import { slackBotToken, slackSigningSecret, type SlackCredentialDeps } from "./setup/credentials.js";
|
|
22
23
|
|
|
23
24
|
const set = (v: string | undefined): boolean => v !== undefined && v.trim() !== "";
|
|
24
25
|
|
|
@@ -80,11 +81,18 @@ async function eventsVerified(): Promise<SurfaceStatus["eventsVerified"]> {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* The bundle's answer for `surfaces.slack`.
|
|
86
|
+
*
|
|
87
|
+
* The two credentials resolve through the bundle's own resolver (`setup/credentials.ts`): the
|
|
88
|
+
* operator secret first (OPE-2650, with env as its on-ramp), then the value a `/setup` run stored. So
|
|
89
|
+
* a deployment configured by `/setup` alone reports `credentials: "yes"`. Before OPE-2650 this read
|
|
90
|
+
* the two env vars only and reported such a deployment as `no`.
|
|
91
|
+
*/
|
|
92
|
+
export async function slackSurfaceStatus(deps: SlackCredentialDeps = {}): Promise<Partial<SurfaceStatus>> {
|
|
93
|
+
const token = set(await slackBotToken(deps));
|
|
86
94
|
return {
|
|
87
|
-
credentials: token && set(
|
|
95
|
+
credentials: token && set(await slackSigningSecret(deps)) ? "yes" : "no",
|
|
88
96
|
// Slack issues a bot token only when the app is installed to a workspace, so holding one is
|
|
89
97
|
// evidence of an install. It is NOT evidence that the install is still live — a revoked app
|
|
90
98
|
// leaves the stale token in place — and the detail in the contract page says so.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cancelAnchoredTurn, type AttachSessionForCancel, type CancelResult, type SurfaceSessionPinner, type SurfaceSessionSetter } from "@open-everywhere/core/agent/adapter-api.js";
|
|
1
|
+
import { cancelAnchoredTurn, type AttachSessionForCancel, type CancelResult, type SurfaceSessionPinner, type SurfaceSessionSetter, type SurfaceSessionStepper } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
2
|
import { type SlackEventEnvelope } from "./slack-events.js";
|
|
3
3
|
/** Slack's event for a press of the session's Stop button. */
|
|
4
4
|
export declare const AGENT_SESSION_STOPPED_EVENT = "agent_session_stopped";
|
|
@@ -44,12 +44,28 @@ export declare function slackAgentSessionOf(attributes: Readonly<Record<string,
|
|
|
44
44
|
* `suspended` long after the delivery's auth is gone. Ids only, never text. Pure.
|
|
45
45
|
*/
|
|
46
46
|
export declare const slackSurfaceSessionPin: SurfaceSessionPinner;
|
|
47
|
-
/** The status line a DM turn shows
|
|
47
|
+
/** The status line a DM turn shows at its start — the mention door's own words (slack-working-signs.ts). */
|
|
48
48
|
export declare const SLACK_DM_WORKING_STATUS = "Working...";
|
|
49
49
|
/** Test-only: forget which sign failures were already logged. */
|
|
50
50
|
export declare function _resetSignWarningsForTest(): void;
|
|
51
|
-
/** The
|
|
51
|
+
/** The message a DM-door delivery's signs ride, or undefined when the door stamped none. Pure. */
|
|
52
|
+
export declare function slackWorkingSignsOf(attributes: Readonly<Record<string, unknown>>): {
|
|
53
|
+
readonly channelId: string;
|
|
54
|
+
readonly threadTs: string;
|
|
55
|
+
readonly teamId?: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* The Slack setter core's turn-anchor hook calls (registered in register.ts). It sets the agent
|
|
59
|
+
* session's state for a 1:1 DM, and the working signs for every DM-door delivery. It answers the id
|
|
60
|
+
* of the conversation it named: the agent session, else the message the signs ride.
|
|
61
|
+
*/
|
|
52
62
|
export declare function slackSurfaceSessionSetter(form?: SlackFormCall): SurfaceSessionSetter;
|
|
63
|
+
/**
|
|
64
|
+
* The Slack stepper (OPE-2632): each step of a DM-door turn shows its code-owned label on the status
|
|
65
|
+
* line, or a status verb — the mention door's own `actions.requested` text (slack-working-signs.ts
|
|
66
|
+
* `slackStepStatus`). A delivery with no sign stamp shows nothing here.
|
|
67
|
+
*/
|
|
68
|
+
export declare function slackSurfaceSessionStepper(form?: SlackFormCall): SurfaceSessionStepper;
|
|
53
69
|
/** One press of the Stop button, as the route reads it. */
|
|
54
70
|
export interface ParsedSessionStop {
|
|
55
71
|
readonly channelId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-agent-session.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack-agent-session.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-agent-session.ts"],"names":[],"mappings":"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"}
|