@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
@@ -0,0 +1,138 @@
1
+ // THE TWO SLACK CALLS A `/setup` RUN MAKES (OPE-2231) — create the app, then install it.
2
+ //
3
+ // Both go through `channels/slack-web-api.ts`, the ONE seam a Slack Web API call leaves this host
4
+ // through (OPE-1211), so both carry the 429/`Retry-After` backoff and neither becomes a new
5
+ // uncovered bypass in `slack-web-api-seam-test.ts`'s inventory.
6
+ //
7
+ // 1. `apps.manifest.create` — docs.slack.dev/reference/methods/apps.manifest.create. Authenticated by
8
+ // an APP CONFIGURATION TOKEN the operator pastes (api.slack.com/apps → "Your App Configuration
9
+ // Tokens", scope `app_configurations:write`, ~12 h life). It takes the manifest this bundle ships
10
+ // (`agent/app-manifest.ts`) with this deployment's host substituted, and returns the app's
11
+ // `app_id`, `client_id`, `client_secret`, `signing_secret` and `oauth_authorize_url`.
12
+ //
13
+ // 2. `oauth.v2.access` — docs.slack.dev/reference/methods/oauth.v2.access. Exchanges the `code` the
14
+ // install redirect brought back for the workspace bot `access_token`, and reports `team.id` and
15
+ // `authed_user.id`. Authenticated by the app's CLIENT CREDENTIALS IN THE BODY, so it is the one
16
+ // call in this tree that sends no `Authorization` header (`unauthenticated: true` — see the seam).
17
+ //
18
+ // 🔴 NOTHING HERE LOGS, AUDITS, RETURNS IN AN ERROR, OR OTHERWISE RENDERS A VALUE IT RECEIVED. Each
19
+ // function returns a typed record the caller stores straight into the vault, and each failure is a
20
+ // {@link SlackSetupApiError} carrying Slack's own `error` CODE and nothing else. Slack's error codes
21
+ // are a closed vocabulary (`invalid_auth`, `token_expired`, `invalid_manifest`, …) and are the one
22
+ // thing an operator needs to act; the response body that carried them is dropped unread, because on
23
+ // the manifest call that body is the credential bundle.
24
+ import { callSlackWebApi } from "../channels/slack-web-api.js";
25
+ /**
26
+ * A Slack call that failed. `code` is Slack's own `error` string when it gave one, else a fixed
27
+ * local classification — never a message assembled from a response. The page renders this; see the
28
+ * header on why it carries no payload.
29
+ */
30
+ export class SlackSetupApiError extends Error {
31
+ code;
32
+ constructor(operation, code) {
33
+ super(`Slack refused ${operation}: ${code}`);
34
+ this.name = "SlackSetupApiError";
35
+ this.code = code;
36
+ }
37
+ }
38
+ /** Read one non-empty string field, or throw a shaped error naming the FIELD, never the payload. */
39
+ function requireString(operation, res, key) {
40
+ const v = res[key];
41
+ if (typeof v !== "string" || v.length === 0)
42
+ throw new SlackSetupApiError(operation, `missing_field:${key}`);
43
+ return v;
44
+ }
45
+ /** Slack's `error` code off an `ok:false` envelope, or a fixed stand-in when it named none. */
46
+ function slackErrorCode(res) {
47
+ const e = res.error;
48
+ return typeof e === "string" && e.length > 0 ? e : "unknown_error";
49
+ }
50
+ /**
51
+ * Create the Slack app from this bundle's shipped manifest.
52
+ *
53
+ * `configToken` is the pasted app configuration token and is used ONCE, here, as the bearer. It is
54
+ * never stored: it expires in about 12 hours and `/setup` has no later use for it, so holding it
55
+ * would be a credential kept past its purpose.
56
+ *
57
+ * `manifest` is the already-substituted manifest JSON string (the caller resolves the host, so this
58
+ * function has no opinion about where the deployment lives).
59
+ */
60
+ export async function createSlackApp(input, deps = {}) {
61
+ const call = deps.call ?? callSlackWebApi;
62
+ let res;
63
+ try {
64
+ res = (await call({
65
+ botToken: input.configToken,
66
+ operation: "apps.manifest.create",
67
+ body: { manifest: input.manifest },
68
+ }));
69
+ }
70
+ catch {
71
+ // A throw from the seam is a transport failure or a non-2xx — the caught value can carry Slack's
72
+ // response body, so it is dropped rather than inspected. See the header.
73
+ throw new SlackSetupApiError("apps.manifest.create", "request_failed");
74
+ }
75
+ if (res.ok !== true)
76
+ throw new SlackSetupApiError("apps.manifest.create", slackErrorCode(res));
77
+ const credentials = res.credentials;
78
+ if (typeof credentials !== "object" || credentials === null) {
79
+ throw new SlackSetupApiError("apps.manifest.create", "missing_field:credentials");
80
+ }
81
+ const c = credentials;
82
+ return {
83
+ appId: requireString("apps.manifest.create", res, "app_id"),
84
+ clientId: requireString("apps.manifest.create", c, "client_id"),
85
+ clientSecret: requireString("apps.manifest.create", c, "client_secret"),
86
+ signingSecret: requireString("apps.manifest.create", c, "signing_secret"),
87
+ oauthAuthorizeUrl: requireString("apps.manifest.create", res, "oauth_authorize_url"),
88
+ };
89
+ }
90
+ /**
91
+ * Exchange the install `code` for the workspace bot token.
92
+ *
93
+ * NO `Authorization` HEADER — Slack authenticates this one by `client_id` + `client_secret` in the
94
+ * body (see the module header and the seam's own note). The `redirect_uri` is sent back because Slack
95
+ * requires it to MATCH the one the authorize step used; omitting it when one was used is an
96
+ * `invalid_grant`-class refusal rather than a laxer check.
97
+ */
98
+ export async function exchangeSlackOauthCode(input, deps = {}) {
99
+ const call = deps.call ?? callSlackWebApi;
100
+ let res;
101
+ try {
102
+ res = (await call({
103
+ unauthenticated: true,
104
+ operation: "oauth.v2.access",
105
+ body: {
106
+ client_id: input.clientId,
107
+ client_secret: input.clientSecret,
108
+ code: input.code,
109
+ redirect_uri: input.redirectUri,
110
+ },
111
+ }));
112
+ }
113
+ catch {
114
+ throw new SlackSetupApiError("oauth.v2.access", "request_failed");
115
+ }
116
+ if (res.ok !== true)
117
+ throw new SlackSetupApiError("oauth.v2.access", slackErrorCode(res));
118
+ const team = res.team;
119
+ const authedUser = res.authed_user;
120
+ if (typeof team !== "object" || team === null)
121
+ throw new SlackSetupApiError("oauth.v2.access", "missing_field:team");
122
+ if (typeof authedUser !== "object" || authedUser === null) {
123
+ throw new SlackSetupApiError("oauth.v2.access", "missing_field:authed_user");
124
+ }
125
+ const t = team;
126
+ const u = authedUser;
127
+ const teamName = typeof t.name === "string" && t.name.length > 0 ? t.name : undefined;
128
+ return {
129
+ // `access_token` on an `oauth.v2.access` response IS the bot token for a bot-scoped install —
130
+ // Slack puts the USER token under `authed_user.access_token`, which this flow never asks for and
131
+ // never reads. Taking the wrong one here would store a token with the installer's own access.
132
+ botToken: requireString("oauth.v2.access", res, "access_token"),
133
+ teamId: requireString("oauth.v2.access", t, "id"),
134
+ installerUserId: requireString("oauth.v2.access", u, "id"),
135
+ ...(teamName ? { teamName } : {}),
136
+ };
137
+ }
138
+ //# sourceMappingURL=slack-api-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-api-setup.js","sourceRoot":"","sources":["../../../agent/setup/slack-api-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type SurfaceStatus } from "@open-everywhere/core/agent/adapter-api.js";
2
+ /** The bundle's answer for `surfaces.slack`. */
3
+ export declare function slackSurfaceStatus(env?: NodeJS.ProcessEnv): Promise<Partial<SurfaceStatus>>;
4
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../agent/status.ts"],"names":[],"mappings":"AAcA,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,4CAA4C,CAAC;AA8DpD,gDAAgD;AAChD,wBAAsB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAU9G"}
@@ -0,0 +1,87 @@
1
+ // The Slack arm of the deployment-status surface (OPE-2230) — what `GET /eve/v1/oe/status` reports
2
+ // under `surfaces.slack`.
3
+ //
4
+ // It lives in the BUNDLE, not in core, for the same reason the telemetry credential probe does:
5
+ // `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` are this vendor's fingerprints, and core carries
6
+ // none of them (`scripts/boundary-test.ts` rule 6).
7
+ //
8
+ // It lives in `status.ts` beside `register.ts` rather than in `ops.ts` because it READS THE
9
+ // OPERATIONAL STORE. `ops.ts` imports core through the light door so the standalone ops MCP server
10
+ // can register it without booting an agent; reaching the store from there would undo that.
11
+ //
12
+ // 🔴 EVERY ANSWER IS A VERDICT, AND EVERY VERDICT IS ONE THIS BUNDLE CAN DEFEND. The route is
13
+ // public, so nothing here returns a token, a team id, a channel key, a count, or a timestamp.
14
+ import { deploymentOrg, getOperationalStore, surfaceEventVerifiedAt, unifiedDomainSessionKey, } from "@open-everywhere/core/agent/adapter-api.js";
15
+ const set = (v) => v !== undefined && v.trim() !== "";
16
+ /**
17
+ * The two `channel_session` key shapes the LEGACY evidence can be found under.
18
+ *
19
+ * A session row is keyed by the supervisor grain's `registryKey`
20
+ * (`packages/core/agent/channels/unified-supervisor.ts` `supervisorGrainFor`), and that key is NOT
21
+ * the surface key whenever window topology resolves a domain for the channel:
22
+ *
23
+ * • topology off, or no domain → the fully-qualified surface key, `slack/<team>/<channel>`;
24
+ * • topology on, `solo` mode → `oe-domain::slack/<team>/<channel>` (the windowId IS the
25
+ * surface key, so the prefix still carries the platform);
26
+ * • topology on, `unified` mode → `oe-domain::<domain name>`;
27
+ * • a NAMED window group → `oe-domain::<group name>`;
28
+ * • a DM seat → the DM pseudo-domain's key.
29
+ *
30
+ * The last three carry no platform marker, so no prefix can find them. That is why the PRIMARY
31
+ * evidence is now the signature gate's own stamp (OPE-2242, below), which no key shape can hide.
32
+ * These prefixes stay only for a deployment whose Slack traffic all predates the stamp.
33
+ */
34
+ // Built with core's OWN key builder, never a copied prefix literal: a change to the key shape then
35
+ // reaches this reporter instead of silently making it match nothing again.
36
+ const SLACK_SESSION_PREFIXES = ["slack/", unifiedDomainSessionKey("slack/")];
37
+ /**
38
+ * Has this deployment ever VERIFIED a signed Slack event?
39
+ *
40
+ * Two kinds of evidence, checked in order:
41
+ *
42
+ * 1. THE STAMP (OPE-2242). `slack-front.ts` calls core's `stampSurfaceEventVerified("slack")`
43
+ * right after its `v0=` HMAC verify passes, on every delivery and before any classification.
44
+ * It is a deployment-level `instance_meta` row, so it reads the same for every domain topology:
45
+ * `unified` mode, a named window group and a DM-only deployment included.
46
+ * 2. A LEGACY SESSION ROW. A `channel_session` row is written by a turn, and a turn is reached only
47
+ * after the same gate accepts the delivery, so a row is proof too. It covers a deployment whose
48
+ * traffic predates the stamp, in the two key shapes above that carry the platform.
49
+ *
50
+ * Neither is proof about the CURRENT signing secret: a rotation, or a restored database, leaves
51
+ * both behind.
52
+ *
53
+ * 🔴 ONE-DIRECTIONAL ON PURPOSE. No evidence means NO EVIDENCE, not "no": a workspace that has not
54
+ * yet sent the app an event, a store that cannot be read, or a deployment whose store was just
55
+ * migrated writes nothing. "unknown" is the honest answer there, and reporting "no" would train the
56
+ * reader to ignore the field on exactly the deployments it is meant to help.
57
+ */
58
+ async function eventsVerified() {
59
+ if ((await surfaceEventVerifiedAt("slack")) !== null)
60
+ return "yes";
61
+ try {
62
+ const store = getOperationalStore();
63
+ const org = deploymentOrg();
64
+ for (const prefix of SLACK_SESSION_PREFIXES) {
65
+ const rows = await store.listChannelSessions(org, prefix);
66
+ if (rows.length > 0)
67
+ return "yes";
68
+ }
69
+ return "unknown";
70
+ }
71
+ catch {
72
+ return "unknown";
73
+ }
74
+ }
75
+ /** The bundle's answer for `surfaces.slack`. */
76
+ export async function slackSurfaceStatus(env = process.env) {
77
+ const token = set(env.SLACK_BOT_TOKEN);
78
+ return {
79
+ credentials: token && set(env.SLACK_SIGNING_SECRET) ? "yes" : "no",
80
+ // Slack issues a bot token only when the app is installed to a workspace, so holding one is
81
+ // evidence of an install. It is NOT evidence that the install is still live — a revoked app
82
+ // leaves the stale token in place — and the detail in the contract page says so.
83
+ installed: token ? "yes" : "no",
84
+ eventsVerified: await eventsVerified(),
85
+ };
86
+ }
87
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../agent/status.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,0BAA0B;AAC1B,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,oDAAoD;AACpD,EAAE;AACF,4FAA4F;AAC5F,mGAAmG;AACnG,2FAA2F;AAC3F,EAAE;AACF,8FAA8F;AAC9F,8FAA8F;AAE9F,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GAExB,MAAM,4CAA4C,CAAC;AAEpD,MAAM,GAAG,GAAG,CAAC,CAAqB,EAAW,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAEnF;;;;;;;;;;;;;;;;;GAiBG;AACH,mGAAmG;AACnG,2EAA2E;AAC3E,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAU,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,KAAK,UAAU,cAAc;IAC3B,IAAI,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACnE,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,MAAM,IAAI,sBAAsB,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1D,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;QACpC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IAC3E,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACvC,OAAO;QACL,WAAW,EAAE,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QAClE,4FAA4F;QAC5F,4FAA4F;QAC5F,iFAAiF;QACjF,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;QAC/B,cAAc,EAAE,MAAM,cAAc,EAAE;KACvC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-everywhere/provider-slack",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Open Everywhere Slack provider — Slack channel and DM surfaces, identity mapping, approval cards and slash commands.",
5
5
  "keywords": [
6
6
  "agent",
@@ -96,6 +96,10 @@
96
96
  "types": "./dist/agent/channels/slack-route-hold.d.ts",
97
97
  "default": "./dist/agent/channels/slack-route-hold.js"
98
98
  },
99
+ "./agent/channels/slack-setup-page.js": {
100
+ "types": "./dist/agent/channels/slack-setup-page.d.ts",
101
+ "default": "./dist/agent/channels/slack-setup-page.js"
102
+ },
99
103
  "./agent/channels/slack-shortcut.js": {
100
104
  "types": "./dist/agent/channels/slack-shortcut.d.ts",
101
105
  "default": "./dist/agent/channels/slack-shortcut.js"
@@ -148,6 +152,34 @@
148
152
  "types": "./dist/agent/register.d.ts",
149
153
  "default": "./dist/agent/register.js"
150
154
  },
155
+ "./agent/setup/claim.js": {
156
+ "types": "./dist/agent/setup/claim.d.ts",
157
+ "default": "./dist/agent/setup/claim.js"
158
+ },
159
+ "./agent/setup/credentials.js": {
160
+ "types": "./dist/agent/setup/credentials.d.ts",
161
+ "default": "./dist/agent/setup/credentials.js"
162
+ },
163
+ "./agent/setup/eve-credentials.js": {
164
+ "types": "./dist/agent/setup/eve-credentials.d.ts",
165
+ "default": "./dist/agent/setup/eve-credentials.js"
166
+ },
167
+ "./agent/setup/install-state.js": {
168
+ "types": "./dist/agent/setup/install-state.d.ts",
169
+ "default": "./dist/agent/setup/install-state.js"
170
+ },
171
+ "./agent/setup/page.js": {
172
+ "types": "./dist/agent/setup/page.d.ts",
173
+ "default": "./dist/agent/setup/page.js"
174
+ },
175
+ "./agent/setup/slack-api-setup.js": {
176
+ "types": "./dist/agent/setup/slack-api-setup.d.ts",
177
+ "default": "./dist/agent/setup/slack-api-setup.js"
178
+ },
179
+ "./agent/status.js": {
180
+ "types": "./dist/agent/status.d.ts",
181
+ "default": "./dist/agent/status.js"
182
+ },
151
183
  "./agent/surface-parity.js": {
152
184
  "types": "./dist/agent/surface-parity.d.ts",
153
185
  "default": "./dist/agent/surface-parity.js"
@@ -158,7 +190,7 @@
158
190
  "prepack": "npm run build",
159
191
  "typecheck": "tsc --noEmit -p tsconfig.json",
160
192
  "test": "npm run test:chain",
161
- "test:chain": "npm run test:slack-e2e && npm run test:slack-context && npm run test:slack-wake-backscroll && npm run test:slack-identity && npm run test:slack-spend-gate && npm run test:slack-wake-mapping && npm run test:slack-tenant-fence && npm run test:slack-wake-drop-hold && npm run test:slack-engagement && npm run test:slack-grant-card &&npm run test:slack-supervisor-reply && npm run test:slack-replier-label && npm run test:slack-normalized-wake && npm run test:slack-wake-trust && npm run test:slack-nonmember-author && npm run test:slack-contextline-trust && npm run test:slack-dm-seat && npm run test:slack-dm-divert && npm run test:slack-shortcut && npm run test:slack-enterprise-rung && npm run test:slack-attachments && npm run test:slack-commands && npm run test:slack-reaction-ingest && npm run test:slack-chat-audit && npm run test:slack-scope-audit && npm run test:slack-app-manifest && npm run test:slack-workspace-roster && npm run test:slack-webhook-verifier && npm run test:slack-input-response && npm run test:slack-approval-card && npm run test:slack-mrkdwn && npm run test:slack-native-wake-heal && npm run test:slack-rate-limit && npm run test:slack-web-api-seam",
193
+ "test:chain": "tsx ../../../scripts/chain/run-chain.ts",
162
194
  "test:slack-webhook-verifier": "NODE_NO_WARNINGS=1 tsx scripts/slack-webhook-verifier-test.ts",
163
195
  "test:slack-workspace-roster": "NODE_NO_WARNINGS=1 tsx scripts/slack-workspace-roster-test.ts",
164
196
  "test:slack-scope-audit": "NODE_NO_WARNINGS=1 tsx scripts/slack-scope-audit-test.ts",
@@ -192,10 +224,12 @@
192
224
  "test:slack-mrkdwn": "NODE_NO_WARNINGS=1 tsx scripts/slack-mrkdwn-test.ts",
193
225
  "test:slack-rate-limit": "NODE_NO_WARNINGS=1 tsx scripts/slack-rate-limit-test.ts",
194
226
  "test:slack-web-api-seam": "NODE_NO_WARNINGS=1 tsx scripts/slack-web-api-seam-test.ts",
195
- "test:slack-wake-drop-hold": "NODE_NO_WARNINGS=1 tsx scripts/slack-wake-drop-hold-test.ts"
227
+ "test:slack-setup": "NODE_NO_WARNINGS=1 tsx scripts/slack-setup-test.ts",
228
+ "test:slack-wake-drop-hold": "NODE_NO_WARNINGS=1 tsx scripts/slack-wake-drop-hold-test.ts",
229
+ "test:slack-surface-evidence": "NODE_NO_WARNINGS=1 tsx scripts/slack-surface-evidence-test.ts"
196
230
  },
197
231
  "dependencies": {
198
- "@open-everywhere/core": "0.1.3",
232
+ "@open-everywhere/core": "0.1.5",
199
233
  "undici": "^7.28.0",
200
234
  "zod": "4.5.4"
201
235
  },