@open-everywhere/provider-slack 0.1.3 → 0.1.5

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.
Files changed (96) hide show
  1. package/NOTICE +34 -4
  2. package/agent/access/slack-attachment-fetch.ts +3 -1
  3. package/agent/app-manifest.ts +48 -10
  4. package/agent/channels/slack-commands.ts +5 -1
  5. package/agent/channels/slack-events.ts +13 -4
  6. package/agent/channels/slack-front.ts +44 -7
  7. package/agent/channels/slack-read.ts +9 -3
  8. package/agent/channels/slack-setup-page.ts +20 -0
  9. package/agent/channels/slack-web-api.ts +34 -7
  10. package/agent/channels/slack.ts +11 -3
  11. package/agent/contributions.ts +1 -0
  12. package/agent/docs-generated.ts +1 -1
  13. package/agent/ops.ts +29 -61
  14. package/agent/register.ts +27 -1
  15. package/agent/setup/claim.ts +343 -0
  16. package/agent/setup/credentials.ts +265 -0
  17. package/agent/setup/eve-credentials.ts +77 -0
  18. package/agent/setup/install-state.ts +248 -0
  19. package/agent/setup/page.ts +579 -0
  20. package/agent/setup/slack-api-setup.ts +176 -0
  21. package/agent/status.ts +94 -0
  22. package/dist/agent/access/slack-attachment-fetch.d.ts.map +1 -1
  23. package/dist/agent/access/slack-attachment-fetch.js +3 -1
  24. package/dist/agent/access/slack-attachment-fetch.js.map +1 -1
  25. package/dist/agent/app-manifest.d.ts +4 -4
  26. package/dist/agent/app-manifest.d.ts.map +1 -1
  27. package/dist/agent/app-manifest.js +48 -10
  28. package/dist/agent/app-manifest.js.map +1 -1
  29. package/dist/agent/channels/slack-commands.d.ts.map +1 -1
  30. package/dist/agent/channels/slack-commands.js +5 -1
  31. package/dist/agent/channels/slack-commands.js.map +1 -1
  32. package/dist/agent/channels/slack-events.d.ts +1 -1
  33. package/dist/agent/channels/slack-events.d.ts.map +1 -1
  34. package/dist/agent/channels/slack-events.js +13 -4
  35. package/dist/agent/channels/slack-events.js.map +1 -1
  36. package/dist/agent/channels/slack-front.d.ts +4 -1
  37. package/dist/agent/channels/slack-front.d.ts.map +1 -1
  38. package/dist/agent/channels/slack-front.js +40 -7
  39. package/dist/agent/channels/slack-front.js.map +1 -1
  40. package/dist/agent/channels/slack-read.d.ts +6 -1
  41. package/dist/agent/channels/slack-read.d.ts.map +1 -1
  42. package/dist/agent/channels/slack-read.js +9 -3
  43. package/dist/agent/channels/slack-read.js.map +1 -1
  44. package/dist/agent/channels/slack-setup-page.d.ts +3 -0
  45. package/dist/agent/channels/slack-setup-page.d.ts.map +1 -0
  46. package/dist/agent/channels/slack-setup-page.js +13 -0
  47. package/dist/agent/channels/slack-setup-page.js.map +1 -0
  48. package/dist/agent/channels/slack-web-api.d.ts +3 -0
  49. package/dist/agent/channels/slack-web-api.d.ts.map +1 -1
  50. package/dist/agent/channels/slack-web-api.js +26 -6
  51. package/dist/agent/channels/slack-web-api.js.map +1 -1
  52. package/dist/agent/channels/slack.d.ts.map +1 -1
  53. package/dist/agent/channels/slack.js +11 -3
  54. package/dist/agent/channels/slack.js.map +1 -1
  55. package/dist/agent/contributions.d.ts +5 -0
  56. package/dist/agent/contributions.d.ts.map +1 -1
  57. package/dist/agent/contributions.js +1 -0
  58. package/dist/agent/contributions.js.map +1 -1
  59. package/dist/agent/docs-generated.js +1 -1
  60. package/dist/agent/docs-generated.js.map +1 -1
  61. package/dist/agent/ops.d.ts +1 -1
  62. package/dist/agent/ops.d.ts.map +1 -1
  63. package/dist/agent/ops.js +29 -47
  64. package/dist/agent/ops.js.map +1 -1
  65. package/dist/agent/register.d.ts.map +1 -1
  66. package/dist/agent/register.js +26 -2
  67. package/dist/agent/register.js.map +1 -1
  68. package/dist/agent/setup/claim.d.ts +73 -0
  69. package/dist/agent/setup/claim.d.ts.map +1 -0
  70. package/dist/agent/setup/claim.js +298 -0
  71. package/dist/agent/setup/claim.js.map +1 -0
  72. package/dist/agent/setup/credentials.d.ts +56 -0
  73. package/dist/agent/setup/credentials.d.ts.map +1 -0
  74. package/dist/agent/setup/credentials.js +214 -0
  75. package/dist/agent/setup/credentials.js.map +1 -0
  76. package/dist/agent/setup/eve-credentials.d.ts +22 -0
  77. package/dist/agent/setup/eve-credentials.d.ts.map +1 -0
  78. package/dist/agent/setup/eve-credentials.js +73 -0
  79. package/dist/agent/setup/eve-credentials.js.map +1 -0
  80. package/dist/agent/setup/install-state.d.ts +139 -0
  81. package/dist/agent/setup/install-state.d.ts.map +1 -0
  82. package/dist/agent/setup/install-state.js +196 -0
  83. package/dist/agent/setup/install-state.js.map +1 -0
  84. package/dist/agent/setup/page.d.ts +29 -0
  85. package/dist/agent/setup/page.d.ts.map +1 -0
  86. package/dist/agent/setup/page.js +460 -0
  87. package/dist/agent/setup/page.js.map +1 -0
  88. package/dist/agent/setup/slack-api-setup.d.ts +62 -0
  89. package/dist/agent/setup/slack-api-setup.d.ts.map +1 -0
  90. package/dist/agent/setup/slack-api-setup.js +138 -0
  91. package/dist/agent/setup/slack-api-setup.js.map +1 -0
  92. package/dist/agent/status.d.ts +4 -0
  93. package/dist/agent/status.d.ts.map +1 -0
  94. package/dist/agent/status.js +87 -0
  95. package/dist/agent/status.js.map +1 -0
  96. package/package.json +38 -4
package/dist/agent/ops.js CHANGED
@@ -1,57 +1,34 @@
1
- // registerSlackOps — the LIGHT ops-plane half of the slack bundle's boot wiring: the quickstart
2
- // surface-goal step (and its part of the hosted `deploy` plan), the paste-ready app manifest, and
3
- // the telemetry credential-presence probe. Split out of register.ts so the STANDALONE ops MCP entry
4
- // (the app's scripts/ops-mcp.ts, which serves preflight/quickstart with no agent boot) can register
5
- // it without pulling the whole channel graph. registerSlackProvider() calls this too, so the agent
6
- // runtime carries the same registrations.
1
+ // registerSlackOps — the LIGHT ops-plane half of the slack bundle's boot wiring: the paste-ready
2
+ // app manifest and the telemetry credential-presence probe. Split out of register.ts so the
3
+ // STANDALONE ops MCP entry (the app's scripts/ops-mcp.ts, which serves the setup tools with no
4
+ // agent boot) can register it without pulling the whole channel graph. registerSlackProvider()
5
+ // calls this too, so the agent runtime carries the same registrations.
7
6
  //
8
7
  // It imports core through the LIGHT door (`adapter-api-ops.js`), not the main barrel: the main
9
8
  // barrel loads most of core, and that one import was most of the ops MCP server's start time.
10
9
  //
11
10
  // This file (not core) owns the Slack env-key knowledge — SLACK_BOT_TOKEN / SLACK_SIGNING_SECRET
12
- // are the bundle's vendor fingerprints (boundary-test.ts rule 6). The bundle reads both from the
13
- // ENVIRONMENT (slack-web-api.ts, slack-front.ts), never from the credential store, so a hosted
14
- // deploy sets them as Vercel environment variables.
15
- import { registerQuickstartSurfaceGoal, registerSurfaceAppManifest, registerSurfaceCredentialProbe, } from "@open-everywhere/core/agent/adapter-api-ops.js";
11
+ // are the bundle's vendor fingerprints (boundary-test.ts rule 6).
12
+ //
13
+ // 🔴 THE QUICKSTART GOAL STEP IS GONE (OPE-2233). This file used to register a "slack" step and a
14
+ // Slack leg of a hosted `deploy` plan with core's `registerQuickstartSurfaceGoal`. That plan — and
15
+ // its `quickstart` MCP tool — are retired: the setup engine's own Slack step
16
+ // (packages/create/src/steps/slack.ts) is the one ordering, and `SLACK_APP_MANIFEST_STEPS` below
17
+ // already carries the vendor-console clicks, the ones about pointing the app at a live host
18
+ // included. Nothing was dropped except the duplicate — and the retired step's own honesty about
19
+ // `/setup` moved with it, to that engine step's `detail`.
20
+ //
21
+ // 🔴 THERE ARE TWO HOMES SINCE OPE-2231, and this file reads only one of them. The bundle resolves
22
+ // both secrets ENV-FIRST and then from the deployment's encrypted credential store, which is where a
23
+ // `/setup` run puts them (agent/setup/credentials.ts). This file's checks stay ENV-ONLY, and that is
24
+ // a limitation rather than a statement about where the secrets live: the credential-probe seam is
25
+ // SYNC, and reading the store is async. So a `/setup`-configured deployment still reports Slack as
26
+ // unconfigured here. Every surface that says so says WHY, rather than claiming — as it did before —
27
+ // that the credential store is not read at all.
28
+ import { registerSurfaceAppManifest, registerSurfaceCredentialProbe } from "@open-everywhere/core/agent/adapter-api-ops.js";
16
29
  import { SLACK_APP_CONSOLE_URL, SLACK_APP_MANIFEST_COLLECT, SLACK_APP_MANIFEST_STEPS, SLACK_APP_NAME_MAX, slackAppManifestTemplate, } from "./app-manifest.js";
17
- const set = (v) => v !== undefined && v !== "";
18
- /** The env vars the Slack surface reads on a deployment. */
19
- const SLACK_HOSTED_ENV = ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"];
20
- /** The steps of the hosted `deploy` plan that need the deployed host. */
21
- function slackAfterDeploySteps() {
22
- return [
23
- {
24
- id: "slack-urls",
25
- title: "Point the Slack app at the deployment",
26
- status: "todo",
27
- detail: "Slack verifies the Event Subscriptions URL by sending it a signed challenge, so the verification fails until a deploy that " +
28
- "holds SLACK_SIGNING_SECRET is up. Fetch the manifest with the real host, then in the Slack app: App Manifest → paste it → " +
29
- "Save Changes. That sets all three URLs (Event Subscriptions and Interactivity: https://<host>/eve/v1/slack; the /oe slash " +
30
- "command: https://<host>/eve/v1/slack/commands). Then Event Subscriptions → Retry (or re-save) until Slack shows Verified. " +
31
- "Verified is the only check that proves the deployment holds SLACK_SIGNING_SECRET: the wizard's --host probe gets the same 401 without it.",
32
- command: 'surface_manifest { surface: "slack", baseUrl: "https://<host>" }',
33
- },
34
- ];
35
- }
36
- /** Register the slack ops-plane arms (quickstart goal step + app manifest + telemetry presence probe). */
30
+ /** Register the slack ops-plane arms (app manifest + telemetry presence probe). */
37
31
  export function registerSlackOps() {
38
- registerQuickstartSurfaceGoal("slack", (env) => {
39
- const ok = set(env.SLACK_BOT_TOKEN) && set(env.SLACK_SIGNING_SECRET);
40
- const missing = [
41
- ...(set(env.SLACK_BOT_TOKEN) ? [] : ["SLACK_BOT_TOKEN"]),
42
- ...(set(env.SLACK_SIGNING_SECRET) ? [] : ["SLACK_SIGNING_SECRET"]),
43
- ];
44
- return {
45
- id: "slack",
46
- title: "Slack app",
47
- status: ok ? "done" : "todo",
48
- detail: "Create a Slack app from the manifest the ops tool `surface_manifest { surface: \"slack\" }` returns (before the first deploy " +
49
- "it keeps the YOUR-DEPLOY-URL placeholder), install it, then copy the bot token and the signing secret. Slack reserves app " +
50
- "creation for a workspace admin, so this step needs a person. The bundle reads both values from the environment, not the " +
51
- `credential store.${ok ? "" : ` Missing: ${missing.join(", ")}.`}`,
52
- command: ok ? undefined : `scaffold_env { values: { ${missing.map((n) => `${n}: "<value>"`).join(", ")} } } — the person pastes each value; never echo one`,
53
- };
54
- }, { env: SLACK_HOSTED_ENV, afterDeploy: slackAfterDeploySteps });
55
32
  // THE PASTE-READY APP MANIFEST (OPE-1603). Minting the Slack app stays human, so the ops plane's
56
33
  // job is to hand the operator the exact document. The bundle owns the document (agent/
57
34
  // app-manifest.ts); core substitutes this deployment's host into it.
@@ -63,6 +40,11 @@ export function registerSlackOps() {
63
40
  template: (opts) => slackAppManifestTemplate(opts),
64
41
  appNameMax: SLACK_APP_NAME_MAX,
65
42
  });
43
+ // THE PRESENCE PROBE IS ENV-ONLY, AND THAT IS A KNOWN GAP (OPE-2231). `registerSurfaceCredentialProbe`
44
+ // takes `() => boolean`; resolving the credential store is async, so a `/setup`-configured deployment
45
+ // reports Slack as having no credentials here while the agent is working. Widening the seam to
46
+ // `() => boolean | Promise<boolean>` touches every reader of the probe registry in core's
47
+ // observability/telemetry.ts, so it is a core change with its own review rather than a line here.
66
48
  registerSurfaceCredentialProbe("slack", () => Boolean(process.env.SLACK_BOT_TOKEN));
67
49
  }
68
50
  //# sourceMappingURL=ops.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,4FAA4F;AAC5F,+FAA+F;AAC/F,+FAA+F;AAC/F,uEAAuE;AACvE,EAAE;AACF,+FAA+F;AAC/F,8FAA8F;AAC9F,EAAE;AACF,iGAAiG;AACjG,kEAAkE;AAClE,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,6EAA6E;AAC7E,iGAAiG;AACjG,4FAA4F;AAC5F,gGAAgG;AAChG,0DAA0D;AAC1D,EAAE;AACF,mGAAmG;AACnG,qGAAqG;AACrG,qGAAqG;AACrG,kGAAkG;AAClG,mGAAmG;AACnG,oGAAoG;AACpG,gDAAgD;AAEhD,OAAO,EAAE,0BAA0B,EAAE,8BAA8B,EAAE,MAAM,gDAAgD,CAAC;AAC5H,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAE3B,mFAAmF;AACnF,MAAM,UAAU,gBAAgB;IAC9B,iGAAiG;IACjG,uFAAuF;IACvF,qEAAqE;IACrE,0BAA0B,CAAC,OAAO,EAAE;QAClC,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,qBAAqB;QACjC,KAAK,EAAE,wBAAwB;QAC/B,OAAO,EAAE,0BAA0B;QACnC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC;QAClD,UAAU,EAAE,kBAAkB;KAC/B,CAAC,CAAC;IACH,uGAAuG;IACvG,sGAAsG;IACtG,+FAA+F;IAC/F,0FAA0F;IAC1F,kGAAkG;IAClG,8BAA8B,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;AACtF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AAqEA;;+DAE+D;AAC/D,wBAAgB,qBAAqB,IAAI,IAAI,CAuE5C"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AA+EA;;+DAE+D;AAC/D,wBAAgB,qBAAqB,IAAI,IAAI,CAuF5C"}
@@ -25,9 +25,15 @@
25
25
  // internal-channel gate, the DM-partner membership verdict, the
26
26
  // readable-channel list, and the channel object the routine/webhook
27
27
  // dispatchers `receive()` into) — core channels/slack-bridge.ts
28
- // • ops arms — quickstart surface-goal step + telemetry creds probe (./ops.ts)
29
- import { registerIdentityMapper, registerCapabilityManifest, registerSurfaceParity, registerProviderEvents, registerPostArm, registerWakeOriginCodec, registerReadAdapter, registerSeatCatchUp, registerReactionSurface, registerBotDisplayName, registerAccountDisplayName, registerAccountDirectory, registerSlackBridge, registerProviderDocs, registerWebhookVerifier, } from "@open-everywhere/core/agent/adapter-api.js";
28
+ // • ops arms — the paste-ready app manifest + telemetry creds probe (./ops.ts)
29
+ // • setup-code boot notice — logs the one-time `/setup` claim code while Slack is not installed
30
+ // (OPE-2231; ./setup/claim.ts)
31
+ // • setup bridge arms — the install record core's status reader asks for, and the reopen the
32
+ // `reopen_slack_setup` control verb calls (OPE-2231)
33
+ // • status reporter — the `surfaces.slack` arm of GET /eve/v1/oe/status (./status.ts)
34
+ import { registerIdentityMapper, registerCapabilityManifest, registerSurfaceParity, registerProviderEvents, registerPostArm, registerWakeOriginCodec, registerReadAdapter, registerSeatCatchUp, registerReactionSurface, registerBotDisplayName, registerAccountDisplayName, registerAccountDirectory, registerSlackBridge, registerProviderDocs, registerWebhookVerifier, registerBootNotice, registerSurfaceStatusReporter, } from "@open-everywhere/core/agent/adapter-api.js";
30
35
  import { PROVIDER_DOCS } from "./docs-generated.js";
36
+ import { slackSurfaceStatus } from "./status.js";
31
37
  import { SLACK_SURFACE_PARITY } from "./surface-parity.js";
32
38
  import { SLACK_PROVIDER_EVENTS } from "./provider-events.js";
33
39
  import { bindSlack, partnerFactsFromInfo } from "./channels/slack-identity.js";
@@ -40,6 +46,8 @@ import { slackForm, fetchUserDisplayName, fetchChannelDisplayName, fetchUserProf
40
46
  import slackChannel from "./channels/slack.js";
41
47
  import { slackWebhookVerifier } from "./channels/slack-webhook-verifier.js";
42
48
  import { registerSlackOps } from "./ops.js";
49
+ import { announceSlackSetupCode } from "./setup/claim.js";
50
+ import { reopenSlackSetup, slackInstallState } from "./setup/install-state.js";
43
51
  /** Perform ALL of the slack bundle's registrations. Idempotent (each register call replaces);
44
52
  * cheap and side-effect-free beyond the registry writes (every live Slack call inside the
45
53
  * registered arms resolves its token lazily at call time). */
@@ -102,8 +110,18 @@ export function registerSlackProvider() {
102
110
  confirmChannelInternal,
103
111
  partnerFactsFromInfo,
104
112
  channel: slackChannel,
113
+ // THE TWO OPE-2231 SETUP ARMS. `installState` is what core's status reader (and OPE-2230's
114
+ // status endpoint through it) asks for — ids and timestamps, never a credential. `reopenSetup`
115
+ // is the admin recovery path the `reopen_slack_setup` control verb calls. Both ride the bridge
116
+ // because core must not import this bundle, and both fail closed to "no Slack install" when a
117
+ // bundle registers neither.
118
+ installState: slackInstallState,
119
+ reopenSetup: reopenSlackSetup,
105
120
  });
106
121
  registerSlackOps();
122
+ // The deployment-status surface's slack arm (OPE-2230) — HERE and not in ops.ts, because it
123
+ // reads the operational store and ops.ts is the light-door half the standalone MCP server loads.
124
+ registerSurfaceStatusReporter("slack", () => slackSurfaceStatus());
107
125
  // Webhook-verifier plane (OPE-164, populated for slack by OPE-197): Slack's `v0=` HMAC scheme, so
108
126
  // a GENERIC hook can declare `source: "slack"` and be verified the way Slack signs. Changes no
109
127
  // existing route — `/eve/v1/slack` and `/eve/v1/slack/commands` keep their own inline gate over
@@ -114,5 +132,11 @@ export function registerSlackProvider() {
114
132
  // compiled into ./docs-generated.ts because docs/ is not on disk at runtime). De-select the bundle
115
133
  // and the module leaves the import graph; registerProviderDocs re-checks the selection gate anyway.
116
134
  registerProviderDocs("slack", PROVIDER_DOCS);
135
+ // THE SETUP-CODE BOOT NOTICE (OPE-2231, core boot/notices.ts). While no Slack install is recorded,
136
+ // the deployment logs a one-time setup code so whoever can read its logs can claim `/setup`. The
137
+ // notice is REGISTERED here — a map write, as cheap as every line above — and RUN by the app's boot
138
+ // checks, because minting the code reads the encrypted vault and this function is sync. Silent once
139
+ // an install exists: a deployment past setup keeps no live code in its logs.
140
+ registerBootNotice("slack-setup-code", announceSlackSetupCode);
117
141
  }
118
142
  //# sourceMappingURL=register.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.js","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { type EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
2
+ import { type SlackInstallStateDeps } from "./install-state.js";
3
+ /** The cookie the page sets once the code matches. `__Host-` pins it to this exact host with no
4
+ * domain and a `/` path, which a subdomain cannot overwrite. */
5
+ export declare const SETUP_COOKIE_NAME = "__Host-oe-setup";
6
+ /** How long a claimed run stays claimed. Long enough to walk the page and Slack's OAuth consent,
7
+ * short enough that an unattended browser on a shared machine is not a standing claim. */
8
+ export declare const SETUP_COOKIE_TTL_MS: number;
9
+ /** Injectable seams for tests. `now` is the clock the cookie's expiry is read against. */
10
+ export interface SlackClaimDeps extends SlackInstallStateDeps {
11
+ readonly vault?: EncryptedKvVault;
12
+ readonly now?: () => number;
13
+ /** Override `process.env` — the lock reads it to close on an env-configured deployment. */
14
+ readonly env?: NodeJS.ProcessEnv;
15
+ /**
16
+ * Use the lock-verdict cache even though a vault is injected. TEST SEAM ONLY.
17
+ *
18
+ * 🔴 SAME REASON AS `SlackCredentialDeps.memo`. An injected vault normally bypasses the cache, which
19
+ * the suite leans on — a test writes an install record and the next call sees it. That also made
20
+ * {@link LOCK_TTL_MS} undrivable by any test, and a TTL nobody can drive is a claim nobody has
21
+ * checked. This one gates a PUBLIC route, so it is the last place to take that on faith. Nothing in
22
+ * `agent/**` passes it.
23
+ */
24
+ readonly lockCache?: boolean;
25
+ }
26
+ /** Test seam: forget cached claim secrets. */
27
+ export declare function resetSlackClaimSecretCache(): void;
28
+ /**
29
+ * The deployment's setup code, minting it on first use. SECRET — the ONLY place it may be rendered
30
+ * is the boot log line in {@link announceSlackSetupCode}.
31
+ */
32
+ export declare function slackSetupCode(deps?: SlackClaimDeps): Promise<string>;
33
+ /**
34
+ * Is `/setup` OPEN right now? True only while NO install is recorded.
35
+ *
36
+ * Every route on the page calls this FIRST and 404s on false, so a locked deployment's `/setup` is
37
+ * indistinguishable from a deployment that never had the page — the same posture
38
+ * `credentials/intake-page.ts` takes on a disarmed vault.
39
+ */
40
+ export declare function slackSetupOpen(deps?: SlackClaimDeps): Promise<boolean>;
41
+ /** Test seam: forget the cached lock verdict. */
42
+ export declare function resetSlackSetupLockCache(): void;
43
+ /**
44
+ * The boot notice (core `boot/notices.ts`): log the setup code ONCE per process, while the page is
45
+ * open. Silent whenever {@link slackSetupOpen} is false — an install is recorded, OR this deployment
46
+ * is configured by environment variable and would ignore anything a run stored. Both cases would be
47
+ * emitting a live claim credential for a page that will refuse every request.
48
+ *
49
+ * 🔴 THIS IS THE ONE PLACE THE CODE IS RENDERED. It goes to the log and nowhere else: not to the
50
+ * audit detail (`appendAudit` details are read back by admin views), not into an error message, not
51
+ * into the page. Minting it is also what this call does first, so the code exists from the first
52
+ * boot rather than from the first visit.
53
+ */
54
+ export declare function announceSlackSetupCode(deps?: SlackClaimDeps): Promise<void>;
55
+ /** Mint the signed cookie VALUE for a run that just presented the right code. CAPABILITY-BEARING. */
56
+ export declare function mintSetupCookie(deps?: SlackClaimDeps): Promise<string>;
57
+ /** Verify a presented cookie value. True only for a well-formed, correctly-signed, unexpired one. */
58
+ export declare function verifySetupCookie(value: string | undefined, deps?: SlackClaimDeps): Promise<boolean>;
59
+ /**
60
+ * Does `presented` equal the deployment's setup code? CONSTANT-TIME, length-prechecked.
61
+ *
62
+ * Never reports WHY it said no, and the caller never tells the holder either: a wrong code and a
63
+ * right code on a locked deployment render the same page.
64
+ */
65
+ export declare function setupCodeMatches(presented: string, deps?: SlackClaimDeps): Promise<boolean>;
66
+ /** The `Set-Cookie` header value for a claimed run. `HttpOnly` (no script reads it), `Secure` (the
67
+ * page is HTTPS-only), `SameSite=Lax` — Lax rather than Strict because the run RETURNS from Slack's
68
+ * OAuth consent as a top-level cross-site GET, which Strict would strip the cookie from, breaking
69
+ * the callback for the very person who claimed the run. */
70
+ export declare function setupCookieHeader(value: string): string;
71
+ /** Read the setup cookie out of a request's `Cookie` header, or `undefined`. */
72
+ export declare function setupCookieFrom(req: Request): string | undefined;
73
+ //# sourceMappingURL=claim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim.d.ts","sourceRoot":"","sources":["../../../agent/setup/claim.ts"],"names":[],"mappings":"AA0DA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAiE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAG/H;iEACiE;AACjE,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;2FAC2F;AAC3F,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AASlD,0FAA0F;AAC1F,MAAM,WAAW,cAAe,SAAQ,qBAAqB;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,2FAA2F;IAC3F,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAmDD,8CAA8C;AAC9C,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAQD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAE/E;AAID;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyChF;AAQD,iDAAiD;AACjD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrF;AA0BD,qGAAqG;AACrG,wBAAsB,eAAe,CAAC,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAKhF;AAED,qGAAqG;AACrG,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyB9G;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAIrG;AAED;;;4DAG4D;AAC5D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAWhE"}
@@ -0,0 +1,298 @@
1
+ // WHO MAY RUN `/setup` (OPE-2231) — the claim, the cookie, and the lock.
2
+ //
3
+ // THE PROBLEM. `/setup` hands whoever completes it a Slack app wired to THIS deployment and a bot
4
+ // token for the workspace that installs it. An unclaimed setup page is first-come: the first
5
+ // stranger to find a fresh deployment owns the agent. So the page needs an identity assertion before
6
+ // it will talk, and the deployment has nothing to assert one with — no users, no admins, no surface
7
+ // (Slack is what setup is for), and no operator-set secret on the hosted path, where the one env
8
+ // value the deploy button asks for is the credential encryption key.
9
+ //
10
+ // THE ANSWER, AND IT IS THE RATIFIED ONE (OPE-2231; "No other auth design"). The deployment proves
11
+ // it owns its own LOGS, which is a thing only its operator can read:
12
+ //
13
+ // 1. On boot, while no install is recorded, the deployment mints a one-time SETUP CODE and logs it
14
+ // once (`vercel logs` / `docker logs`; a coding agent reads it). The code lives in the vault, so
15
+ // every instance logs the same one and a restart does not invalidate a code in use.
16
+ // 2. `/setup` asks for the code once. A match sets a short-lived signed COOKIE; every later step
17
+ // in the run presents the cookie, so the code is typed once and never rides a URL.
18
+ // 3. The page LOCKS once an install is recorded (install-state.ts) and answers 404 from then on.
19
+ // An admin reopens it with the control call (`reopen_slack_setup`), never by presenting a code.
20
+ //
21
+ // 🔴 WHAT THIS IS AND IS NOT WORTH. The trust root is READ ACCESS TO THE DEPLOYMENT'S LOGS — a
22
+ // PLATFORM permission (`vercel logs`, `docker logs`), not any role this product defines. It is the
23
+ // right root for the hosted path, because whoever holds it can already read every secret the platform
24
+ // holds. It is honestly weaker than an account login in one way worth stating: a platform where log
25
+ // access is granted more widely than deploy access grants setup to everyone with log access, for as
26
+ // long as the page is open. Note which permission that is — it is NOT the OE `admin` role that the
27
+ // reopen verb requires, and the two sets need not overlap.
28
+ //
29
+ // TWO THINGS BOUND THAT WINDOW, and both are load-bearing:
30
+ // • the page closes at the first successful install, and {@link announceSlackSetupCode} stops
31
+ // EMITTING a code from then on. The logs are append-only history, so a post-install deployment's
32
+ // OLD log lines still contain the code it emitted before the install — which is why the INSTALL
33
+ // ITSELF destroys that code and the claim cookie key (`setup/page.ts`, before it records the
34
+ // install), and why a reopen rotates both BEFORE it deletes the record (`reopenSlackSetup`, which
35
+ // throws rather than opening the page if it cannot). The strings in those old log lines are dead
36
+ // the moment the install completes, and a reopen can only ever be entered with a code minted
37
+ // after it.
38
+ // • the lock FAILS CLOSED. Only an absent install record opens the page; a store fault, an
39
+ // unparseable row or a record from a future build all keep it shut. See {@link slackSetupOpen}.
40
+ // • the page is also closed on a deployment configured the OLD way, by environment variable. That
41
+ // one is not a nicety: without it every existing deployment in the world would upgrade into an
42
+ // open setup page it never asked for. See {@link slackSetupOpen}.
43
+ //
44
+ // 🔴 THE CODE IS A SECRET AND THE LOG LINE IS ITS ONLY RENDERING. It is never put in an error, never
45
+ // echoed back by the form, never written to the audit detail, and never returned by any function
46
+ // here but the two that exist to mint or compare it. A failed code attempt renders the same generic
47
+ // page a right-but-locked attempt does.
48
+ //
49
+ // WHY A SECOND COOKIE CODEC AND NOT core's PAGE-TOKEN CODEC. `credentials/page-token.ts` signs a
50
+ // capability bound to ONE ROW in `credential_intake` — its claim set is `{v, aud, org, id, rb, exp}`
51
+ // and `id`/`rb` are the row and the principal the act is attributed to. A setup cookie binds no row
52
+ // and no principal (there is no principal yet — that is what setup produces), rides a `Set-Cookie`
53
+ // rather than a URL, and is presented many times rather than consumed once. Bending the row codec
54
+ // around that would mean two meanings for `id` and a THIRD audience in a closed union whose N×N
55
+ // replay matrix is a suite (`webhook-reveal-test.ts`), for a token that has no row to replay onto.
56
+ // So: its own codec, its own key, and the two cannot verify each other's tokens in either direction
57
+ // because neither key is the other's and neither payload satisfies the other's shape check.
58
+ import { encryptedKvVault, } from "@open-everywhere/core/agent/adapter-api.js";
59
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
60
+ import { SLACK_COOKIE_KEY_REF, SLACK_SETUP_CODE_REF, readInstallRecord } from "./install-state.js";
61
+ import { slackSecretEnvPresent } from "./credentials.js";
62
+ /** The cookie the page sets once the code matches. `__Host-` pins it to this exact host with no
63
+ * domain and a `/` path, which a subdomain cannot overwrite. */
64
+ export const SETUP_COOKIE_NAME = "__Host-oe-setup";
65
+ /** How long a claimed run stays claimed. Long enough to walk the page and Slack's OAuth consent,
66
+ * short enough that an unattended browser on a shared machine is not a standing claim. */
67
+ export const SETUP_COOKIE_TTL_MS = 30 * 60 * 1000;
68
+ /** The setup code's length in bytes before encoding — 16 bytes is 128 bits, which is not guessable
69
+ * and still short enough for a human to copy out of a log line in one go. */
70
+ const SETUP_CODE_BYTES = 16;
71
+ /** The cookie HMAC key's length in bytes. Nobody types this one, so it is a full 256-bit key. */
72
+ const COOKIE_KEY_BYTES = 32;
73
+ function vaultOf(deps) {
74
+ return deps.vault ?? encryptedKvVault();
75
+ }
76
+ // --- mint-on-first-use, into the Slack app's own namespace ------------------------------------------
77
+ //
78
+ // WHY NOT core's `getOrMintSelfSecret`. That function's contract is "an env var of the same NAME is
79
+ // the announced on-ramp", which is right for a secret an operator might already hold — and wrong for
80
+ // these two. Nobody sets a setup code or a cookie key by hand: there is nothing to migrate from, and
81
+ // an env name for an auto-minted value is a knob whose only use is to weaken it. So these mint into
82
+ // `slack-app:` beside the rest of the bundle's material, with no env name at all, and this helper
83
+ // carries the SAME race discipline that function documents: a per-key lock, a read-back rather than
84
+ // trust in the local candidate, and no memo of a fresh mint (the vault is last-write-wins with no
85
+ // put-if-absent, so two cold processes can briefly diverge and the next read converges).
86
+ async function mintedSecret(ref, bytes, vault, now, cache) {
87
+ // 🔴 AN EXISTING ROW IS MEMOISED BRIEFLY, A FRESH MINT NEVER IS, and both halves matter.
88
+ //
89
+ // WHY CACHE AT ALL. Both callers sit on the UNAUTHENTICATED `/setup` route: `verifySetupCookie`
90
+ // resolves the cookie key for any request carrying a non-empty cookie, and `setupCodeMatches` resolves
91
+ // the code for every claim POST. Uncached, that is one `stored_credential` SELECT plus one decrypt per
92
+ // request for as long as the page is open — the same exposure `LOCK_TTL_MS` and the credential
93
+ // resolver's `MISS_TTL_MS` exist to remove, left open on the one route most exposed to it.
94
+ //
95
+ // WHY NOT A FRESH MINT. A mint reflects only what THIS process wrote, not what a concurrent racer's
96
+ // later write did (the vault is last-write-wins with no put-if-absent). Caching it would pin a value
97
+ // that lost the race; leaving it uncached means the next resolution re-reads and converges.
98
+ const cached = cache ? secretCache.get(ref) : undefined;
99
+ if (cached !== undefined && cached.until > now())
100
+ return cached.value;
101
+ return vault.withLock(ref, async () => {
102
+ const stored = await vault.read(ref);
103
+ if (stored !== null) {
104
+ if (cache)
105
+ secretCache.set(ref, { value: stored, until: now() + SECRET_TTL_MS });
106
+ return stored;
107
+ }
108
+ const minted = randomBytes(bytes).toString("base64url");
109
+ await vault.write(ref, minted, { connector: "slack", createdBy: "host:slack-setup" });
110
+ const confirmed = await vault.read(ref);
111
+ return confirmed ?? minted;
112
+ });
113
+ }
114
+ /** How long a read-back claim secret is reused. Matched to {@link LOCK_TTL_MS}: a reopen's rotation has
115
+ * to take effect without a restart, and five seconds is the staleness this page already accepts. */
116
+ const SECRET_TTL_MS = 5_000;
117
+ /** Read-back claim secrets and when each stops being trusted. Bypassed for an injected vault. */
118
+ const secretCache = new Map();
119
+ /** Test seam: forget cached claim secrets. */
120
+ export function resetSlackClaimSecretCache() {
121
+ secretCache.clear();
122
+ }
123
+ /** The HMAC key for the claim cookie, minting it on first use. Never logged, never rendered. A full
124
+ * 32 bytes: it keys an HMAC rather than being typed by a human, so there is no reason to be short. */
125
+ async function cookieKey(deps) {
126
+ return mintedSecret(SLACK_COOKIE_KEY_REF, COOKIE_KEY_BYTES, vaultOf(deps), deps.now ?? Date.now, deps.vault === undefined);
127
+ }
128
+ /**
129
+ * The deployment's setup code, minting it on first use. SECRET — the ONLY place it may be rendered
130
+ * is the boot log line in {@link announceSlackSetupCode}.
131
+ */
132
+ export async function slackSetupCode(deps = {}) {
133
+ return mintedSecret(SLACK_SETUP_CODE_REF, SETUP_CODE_BYTES, vaultOf(deps), deps.now ?? Date.now, deps.vault === undefined);
134
+ }
135
+ // --- the lock ---------------------------------------------------------------------------------------
136
+ /**
137
+ * Is `/setup` OPEN right now? True only while NO install is recorded.
138
+ *
139
+ * Every route on the page calls this FIRST and 404s on false, so a locked deployment's `/setup` is
140
+ * indistinguishable from a deployment that never had the page — the same posture
141
+ * `credentials/intake-page.ts` takes on a disarmed vault.
142
+ */
143
+ export async function slackSetupOpen(deps = {}) {
144
+ // The cached verdict, while it is still inside its TTL. Skipped for an injected vault (a test
145
+ // driving a specific store state), and the env check below is a property read either way.
146
+ const cacheApplies = deps.vault === undefined || deps.lockCache === true;
147
+ if (cacheApplies && lockCache !== null && lockCache.until > (deps.now ?? Date.now)())
148
+ return lockCache.open;
149
+ // 🔴 AN ENV-CONFIGURED DEPLOYMENT IS CLOSED, and this arm is not a convenience — without it the
150
+ // whole upgrade population gets an OPEN setup page and a live claim code in its logs, forever.
151
+ //
152
+ // The lock is "an install record exists". A deployment that was configured the old way — the two
153
+ // env vars, set by hand — has never run `/setup`, so it has no record. Before this check, landing
154
+ // this feature would have opened `/setup` on every such deployment and logged a one-time code on
155
+ // every cold start indefinitely, with no install ever arriving to close it. Anyone who could read
156
+ // the deployment's logs could then install the app into a workspace THEY control, overwrite the
157
+ // stored credentials, lock the page behind themselves, and have `slackInstallFacts()` publish their
158
+ // workspace as this deployment's install.
159
+ //
160
+ // Closing on env presence costs nothing a real `/setup` operator wants: the resolver is env-first,
161
+ // so a deployment with either var set would IGNORE everything a run stored anyway — the page would
162
+ // have been a no-op with a credential write. An operator who wants to migrate onto the store unsets
163
+ // the vars and redeploys, which is what the runbook says and what makes the run meaningful.
164
+ // Screen 2's shadow notice still covers the case where one var is set after an install.
165
+ const env = slackSecretEnvPresent({ ...(deps.env ? { env: deps.env } : {}) });
166
+ if (env.botToken || env.signingSecret)
167
+ return false;
168
+ // 🔴 ONLY AN ABSENT ROW OPENS THE PAGE, and the earlier draft got this backwards. It read through
169
+ // `slackInstallState`, which collapses a store fault, a malformed row and an unknown record version
170
+ // into `undefined` — so any of those REOPENED `/setup`. That is not a safe default here: the setup
171
+ // code sits in the deployment's log lines from before the install, so a reopen is a window for
172
+ // whoever read them. The cost of failing closed is a page briefly unavailable during a store
173
+ // outage, recoverable by a retry or the control call; the other direction is not recoverable.
174
+ // `readInstallRecord` tells the three cases apart — see its doc comment.
175
+ const read = await readInstallRecord(deps);
176
+ const open = read.kind === "absent";
177
+ // Cached briefly, for the same reason the credential resolver caches a miss: this predicate runs on
178
+ // EVERY request to a PUBLIC route, so an uncached read let anyone who knew the host drive one
179
+ // `stored_credential` SELECT per request. A few seconds is enough to collapse that while keeping the
180
+ // page responsive to a `/setup` run or a reopen that just changed the answer.
181
+ if (cacheApplies)
182
+ lockCache = { open, until: (deps.now ?? Date.now)() + LOCK_TTL_MS };
183
+ return open;
184
+ }
185
+ /** How long the lock verdict is reused. Short, because a reopen must take effect without a restart. */
186
+ const LOCK_TTL_MS = 5_000;
187
+ /** The last lock verdict and when it stops being trusted. Bypassed entirely for an injected vault. */
188
+ let lockCache = null;
189
+ /** Test seam: forget the cached lock verdict. */
190
+ export function resetSlackSetupLockCache() {
191
+ lockCache = null;
192
+ }
193
+ /**
194
+ * The boot notice (core `boot/notices.ts`): log the setup code ONCE per process, while the page is
195
+ * open. Silent whenever {@link slackSetupOpen} is false — an install is recorded, OR this deployment
196
+ * is configured by environment variable and would ignore anything a run stored. Both cases would be
197
+ * emitting a live claim credential for a page that will refuse every request.
198
+ *
199
+ * 🔴 THIS IS THE ONE PLACE THE CODE IS RENDERED. It goes to the log and nowhere else: not to the
200
+ * audit detail (`appendAudit` details are read back by admin views), not into an error message, not
201
+ * into the page. Minting it is also what this call does first, so the code exists from the first
202
+ * boot rather than from the first visit.
203
+ */
204
+ export async function announceSlackSetupCode(deps = {}) {
205
+ if (!(await slackSetupOpen(deps)))
206
+ return;
207
+ const code = await slackSetupCode(deps);
208
+ console.log(`[open-everywhere:slack-setup] Slack is not installed on this deployment. Open /setup and enter this one-time setup code: ${code}\n` +
209
+ `[open-everywhere:slack-setup] The code stops being logged once the Slack install completes, and /setup locks at that point.`);
210
+ }
211
+ function b64urlEncode(buf) {
212
+ return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
213
+ }
214
+ function b64urlDecode(s) {
215
+ // Reject anything outside the base64url alphabet BEFORE decoding (Buffer is lax), so a malformed
216
+ // cookie fails cleanly rather than decoding to attacker-shaped bytes.
217
+ if (!/^[A-Za-z0-9_-]*$/.test(s))
218
+ return Buffer.alloc(0);
219
+ return Buffer.from(s.replace(/-/g, "+").replace(/_/g, "/"), "base64");
220
+ }
221
+ function sign(secret, payloadB64) {
222
+ return b64urlEncode(createHmac("sha256", secret).update(payloadB64, "utf8").digest());
223
+ }
224
+ /** Mint the signed cookie VALUE for a run that just presented the right code. CAPABILITY-BEARING. */
225
+ export async function mintSetupCookie(deps = {}) {
226
+ const now = (deps.now ?? Date.now)();
227
+ const claims = { v: 1, exp: now + SETUP_COOKIE_TTL_MS };
228
+ const payloadB64 = b64urlEncode(Buffer.from(JSON.stringify(claims), "utf8"));
229
+ return `${payloadB64}.${sign(await cookieKey(deps), payloadB64)}`;
230
+ }
231
+ /** Verify a presented cookie value. True only for a well-formed, correctly-signed, unexpired one. */
232
+ export async function verifySetupCookie(value, deps = {}) {
233
+ if (typeof value !== "string")
234
+ return false;
235
+ const dot = value.indexOf(".");
236
+ if (dot <= 0 || dot === value.length - 1)
237
+ return false;
238
+ const payloadB64 = value.slice(0, dot);
239
+ const sigB64 = value.slice(dot + 1);
240
+ const expected = sign(await cookieKey(deps), payloadB64);
241
+ const a = Buffer.from(sigB64);
242
+ const b = Buffer.from(expected);
243
+ if (a.length !== b.length || !timingSafeEqual(a, b))
244
+ return false;
245
+ let parsed;
246
+ try {
247
+ const raw = b64urlDecode(payloadB64);
248
+ if (raw.length === 0)
249
+ return false;
250
+ parsed = JSON.parse(raw.toString("utf8"));
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ if (typeof parsed !== "object" || parsed === null)
256
+ return false;
257
+ const c = parsed;
258
+ if (c.v !== 1)
259
+ return false;
260
+ if (typeof c.exp !== "number" || !Number.isFinite(c.exp))
261
+ return false;
262
+ return c.exp > (deps.now ?? Date.now)();
263
+ }
264
+ /**
265
+ * Does `presented` equal the deployment's setup code? CONSTANT-TIME, length-prechecked.
266
+ *
267
+ * Never reports WHY it said no, and the caller never tells the holder either: a wrong code and a
268
+ * right code on a locked deployment render the same page.
269
+ */
270
+ export async function setupCodeMatches(presented, deps = {}) {
271
+ const a = Buffer.from(presented.trim());
272
+ const b = Buffer.from(await slackSetupCode(deps));
273
+ return a.length === b.length && timingSafeEqual(a, b);
274
+ }
275
+ /** The `Set-Cookie` header value for a claimed run. `HttpOnly` (no script reads it), `Secure` (the
276
+ * page is HTTPS-only), `SameSite=Lax` — Lax rather than Strict because the run RETURNS from Slack's
277
+ * OAuth consent as a top-level cross-site GET, which Strict would strip the cookie from, breaking
278
+ * the callback for the very person who claimed the run. */
279
+ export function setupCookieHeader(value) {
280
+ return `${SETUP_COOKIE_NAME}=${value}; Path=/; Max-Age=${Math.floor(SETUP_COOKIE_TTL_MS / 1000)}; HttpOnly; Secure; SameSite=Lax`;
281
+ }
282
+ /** Read the setup cookie out of a request's `Cookie` header, or `undefined`. */
283
+ export function setupCookieFrom(req) {
284
+ const header = req.headers.get("cookie");
285
+ if (!header)
286
+ return undefined;
287
+ for (const part of header.split(";")) {
288
+ const eq = part.indexOf("=");
289
+ if (eq <= 0)
290
+ continue;
291
+ if (part.slice(0, eq).trim() !== SETUP_COOKIE_NAME)
292
+ continue;
293
+ const value = part.slice(eq + 1).trim();
294
+ return value.length > 0 ? value : undefined;
295
+ }
296
+ return undefined;
297
+ }
298
+ //# sourceMappingURL=claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim.js","sourceRoot":"","sources":["../../../agent/setup/claim.ts"],"names":[],"mappings":"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"}