@cursor/july 0.1.54 → 0.1.61

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 (293) hide show
  1. package/dist/bin/agent-serve.js +30 -23
  2. package/dist/channels/github/github-channel.d.ts +12 -0
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +67 -15
  5. package/dist/channels/github/types.d.ts +27 -1
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/origin/cursor-account.d.ts +22 -0
  8. package/dist/channels/origin/cursor-account.d.ts.map +1 -0
  9. package/dist/channels/origin/cursor-account.js +35 -0
  10. package/dist/channels/origin/events.d.ts +10 -0
  11. package/dist/channels/origin/events.d.ts.map +1 -0
  12. package/dist/channels/origin/events.js +27 -0
  13. package/dist/channels/origin/index.d.ts +29 -0
  14. package/dist/channels/origin/index.d.ts.map +1 -0
  15. package/dist/channels/origin/index.js +28 -0
  16. package/dist/channels/origin/origin-channel.d.ts +68 -0
  17. package/dist/channels/origin/origin-channel.d.ts.map +1 -0
  18. package/dist/channels/origin/origin-channel.js +567 -0
  19. package/dist/channels/origin/origin-webhook.d.ts +38 -0
  20. package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
  21. package/dist/channels/origin/origin-webhook.js +151 -0
  22. package/dist/channels/origin/types.d.ts +152 -0
  23. package/dist/channels/origin/types.d.ts.map +1 -0
  24. package/dist/channels/origin/types.js +1 -0
  25. package/dist/channels/slack/doctor.d.ts +17 -1
  26. package/dist/channels/slack/doctor.d.ts.map +1 -1
  27. package/dist/channels/slack/doctor.js +186 -10
  28. package/dist/docs/404.html +2 -2
  29. package/dist/docs/ab.html +4 -4
  30. package/dist/docs/assets/{app.DAKcDclS.js → app.CW3KJh72.js} +1 -1
  31. package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
  32. package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
  33. package/dist/docs/assets/chunks/{theme.B47GzJjc.js → theme.Bz9AodBA.js} +2 -2
  34. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
  35. package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
  36. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
  37. package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
  38. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
  39. package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
  40. package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
  41. package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
  42. package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
  43. package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
  44. package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.js → reference_cli.md.Cl14Jfo3.js} +10 -2
  45. package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.lean.js → reference_cli.md.Cl14Jfo3.lean.js} +1 -1
  46. package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +1 -0
  47. package/dist/docs/assets/{scaffolding-agents.md.mkc3B_ZW.lean.js → scaffolding-agents.md.CrWSA0Ko.lean.js} +1 -1
  48. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.js +14 -0
  49. package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.lean.js +1 -0
  50. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.js +4 -0
  51. package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.lean.js +1 -0
  52. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.js +8 -0
  53. package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.lean.js +1 -0
  54. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.js +2 -0
  55. package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.lean.js +1 -0
  56. package/dist/docs/assets/templates_triage.md.DWuQ1bZz.js +9 -0
  57. package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
  58. package/dist/docs/building-with-agents.html +4 -4
  59. package/dist/docs/concepts.html +5 -5
  60. package/dist/docs/deployment.html +4 -4
  61. package/dist/docs/evals.html +4 -4
  62. package/dist/docs/example-agents/approval-buddy.html +4 -4
  63. package/dist/docs/example-agents/benny.html +4 -4
  64. package/dist/docs/example-agents/bugbot.html +4 -4
  65. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  66. package/dist/docs/example-agents/codeowners-review.html +4 -4
  67. package/dist/docs/example-agents/concierge.html +4 -4
  68. package/dist/docs/example-agents/fsd.html +5 -5
  69. package/dist/docs/example-agents/index.html +4 -4
  70. package/dist/docs/example-agents/knowledge-base.html +4 -4
  71. package/dist/docs/example-agents/oncall.html +4 -4
  72. package/dist/docs/example-agents/security-reviewer.html +5 -5
  73. package/dist/docs/example-agents/slack-agent.html +4 -4
  74. package/dist/docs/example-agents/weather-agent.html +4 -4
  75. package/dist/docs/guides/agent-to-agent.html +4 -4
  76. package/dist/docs/guides/cloud-runtime.html +4 -4
  77. package/dist/docs/guides/convert-automation.html +36 -0
  78. package/dist/docs/guides/github.html +7 -7
  79. package/dist/docs/guides/human-in-the-loop.html +4 -4
  80. package/dist/docs/guides/mcp-oauth.html +4 -4
  81. package/dist/docs/guides/opentelemetry.html +4 -4
  82. package/dist/docs/guides/slack.html +4 -4
  83. package/dist/docs/guides/webhooks.html +4 -4
  84. package/dist/docs/hashmap.json +1 -1
  85. package/dist/docs/hillclimbing.html +4 -4
  86. package/dist/docs/index.html +6 -6
  87. package/dist/docs/quickstart.html +4 -4
  88. package/dist/docs/reference/agent-config.html +4 -4
  89. package/dist/docs/reference/artifacts.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +14 -6
  92. package/dist/docs/reference/connections.html +4 -4
  93. package/dist/docs/reference/hooks.html +4 -4
  94. package/dist/docs/reference/http-api.html +4 -4
  95. package/dist/docs/reference/instructions.html +4 -4
  96. package/dist/docs/reference/playground.html +4 -4
  97. package/dist/docs/reference/project-layout.html +4 -4
  98. package/dist/docs/reference/prompt.html +4 -4
  99. package/dist/docs/reference/schedules.html +4 -4
  100. package/dist/docs/reference/sessions.html +4 -4
  101. package/dist/docs/reference/skills.html +4 -4
  102. package/dist/docs/reference/subagents.html +4 -4
  103. package/dist/docs/reference/tools.html +4 -4
  104. package/dist/docs/scaffolding-agents.html +5 -5
  105. package/dist/docs/storage.html +4 -4
  106. package/dist/docs/templates/agentic-owners.html +38 -0
  107. package/dist/docs/templates/demo.html +28 -0
  108. package/dist/docs/templates/pr-autofixer.html +32 -0
  109. package/dist/docs/templates/security-reviewer.html +26 -0
  110. package/dist/docs/templates/triage.html +14 -16
  111. package/dist/docs/troubleshooting.html +4 -4
  112. package/dist/index.d.ts +3 -3
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +2 -2
  115. package/dist/internal/cli-ax.d.ts +2 -0
  116. package/dist/internal/cli-ax.d.ts.map +1 -1
  117. package/dist/internal/cli-ax.js +30 -1
  118. package/dist/internal/cli-deploy.js +1 -1
  119. package/dist/internal/cli-slack.d.ts +4 -0
  120. package/dist/internal/cli-slack.d.ts.map +1 -1
  121. package/dist/internal/cli-slack.js +46 -20
  122. package/dist/internal/cursor-event-relay.d.ts +59 -11
  123. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  124. package/dist/internal/cursor-event-relay.js +133 -26
  125. package/dist/internal/discovery.d.ts.map +1 -1
  126. package/dist/internal/discovery.js +11 -2
  127. package/dist/internal/distribution.d.ts.map +1 -1
  128. package/dist/internal/distribution.js +1 -0
  129. package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
  130. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  131. package/dist/internal/handleAgentServeTrigger.js +97 -19
  132. package/dist/internal/init-interview.d.ts +67 -0
  133. package/dist/internal/init-interview.d.ts.map +1 -0
  134. package/dist/internal/init-interview.js +300 -0
  135. package/dist/internal/init-project.d.ts +7 -0
  136. package/dist/internal/init-project.d.ts.map +1 -1
  137. package/dist/internal/init-project.js +99 -43
  138. package/dist/internal/mcp-endpoint.d.ts +7 -0
  139. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  140. package/dist/internal/mcp-endpoint.js +7 -0
  141. package/dist/internal/server.d.ts.map +1 -1
  142. package/dist/internal/server.js +159 -57
  143. package/dist/internal/session-engine.d.ts.map +1 -1
  144. package/dist/internal/session-engine.js +20 -21
  145. package/dist/internal/session-links.d.ts +14 -0
  146. package/dist/internal/session-links.d.ts.map +1 -0
  147. package/dist/internal/session-links.js +69 -0
  148. package/dist/internal/sessions-client.d.ts.map +1 -1
  149. package/dist/internal/sessions-client.js +1 -0
  150. package/dist/internal/slack-provision-client.js +1 -1
  151. package/dist/playground/assets/index-ClbuaLpm.css +1 -0
  152. package/dist/playground/assets/index-SqkR13k_.js +85 -0
  153. package/dist/playground/index.html +2 -2
  154. package/dist/types.d.ts +19 -12
  155. package/dist/types.d.ts.map +1 -1
  156. package/docs/README.md +15 -0
  157. package/docs/example-agents/fsd.md +3 -0
  158. package/docs/example-agents/security-reviewer.md +3 -0
  159. package/docs/guides/convert-automation.md +176 -0
  160. package/docs/guides/github.md +6 -2
  161. package/docs/reference/cli.md +36 -10
  162. package/docs/scaffolding-agents.md +3 -0
  163. package/docs/templates/agentic-owners.md +97 -0
  164. package/docs/templates/demo.md +84 -0
  165. package/docs/templates/pr-autofixer.md +136 -0
  166. package/docs/templates/security-reviewer.md +89 -0
  167. package/docs/templates/triage.md +59 -101
  168. package/package.json +9 -1
  169. package/skills/github/SKILL.md +4 -5
  170. package/src/bin/agent-serve.ts +13 -6
  171. package/src/channels/github/github-channel.ts +95 -18
  172. package/src/channels/github/types.ts +31 -0
  173. package/src/channels/origin/cursor-account.ts +64 -0
  174. package/src/channels/origin/events.ts +41 -0
  175. package/src/channels/origin/index.ts +29 -0
  176. package/src/channels/origin/origin-channel.ts +784 -0
  177. package/src/channels/origin/origin-webhook.ts +202 -0
  178. package/src/channels/origin/types.ts +186 -0
  179. package/src/channels/slack/doctor.ts +218 -5
  180. package/src/index.ts +3 -1
  181. package/src/internal/cli-ax.ts +33 -1
  182. package/src/internal/cli-deploy.ts +2 -2
  183. package/src/internal/cli-slack.ts +68 -19
  184. package/src/internal/cursor-event-relay.ts +193 -31
  185. package/src/internal/discovery.ts +15 -5
  186. package/src/internal/distribution.ts +1 -0
  187. package/src/internal/handleAgentServeTrigger.ts +129 -19
  188. package/src/internal/init-interview.ts +384 -0
  189. package/src/internal/init-project.ts +146 -46
  190. package/src/internal/mcp-endpoint.ts +7 -0
  191. package/src/internal/server.ts +188 -58
  192. package/src/internal/session-engine.ts +5 -21
  193. package/src/internal/session-links.ts +88 -0
  194. package/src/internal/sessions-client.ts +1 -0
  195. package/src/internal/slack-provision-client.ts +1 -1
  196. package/src/types.ts +19 -12
  197. package/templates/agentic-owners/README.md +73 -0
  198. package/templates/agentic-owners/agent/agent.ts +15 -0
  199. package/templates/agentic-owners/agent/channels/github.ts +85 -0
  200. package/templates/agentic-owners/agent/instructions.md +34 -0
  201. package/templates/agentic-owners/agent/lib/config.ts +13 -0
  202. package/templates/agentic-owners/agent/lib/github.ts +241 -0
  203. package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
  204. package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
  205. package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
  206. package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
  207. package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
  208. package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
  209. package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
  210. package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
  211. package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
  212. package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
  213. package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
  214. package/templates/agentic-owners/agent/lib/review.ts +777 -0
  215. package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
  216. package/templates/agentic-owners/evals/evals.config.ts +5 -0
  217. package/templates/agentic-owners/evals/owners.eval.ts +116 -0
  218. package/templates/agentic-owners/package.json +20 -0
  219. package/templates/agentic-owners/tsconfig.json +12 -0
  220. package/templates/demo/README.md +33 -0
  221. package/templates/demo/agent/agent.ts +15 -0
  222. package/templates/demo/agent/channels/github.ts +50 -0
  223. package/templates/demo/agent/channels/queue.ts +30 -0
  224. package/templates/demo/agent/instructions.md +8 -0
  225. package/templates/demo/agent/lib/collect.ts +78 -0
  226. package/templates/demo/agent/tools/record_demo.ts +40 -0
  227. package/templates/demo/evals/demo.eval.ts +33 -0
  228. package/templates/demo/evals/evals.config.ts +5 -0
  229. package/templates/demo/package.json +18 -0
  230. package/templates/demo/tsconfig.json +12 -0
  231. package/templates/pr-autofixer/README.md +44 -0
  232. package/templates/pr-autofixer/agent/agent.ts +22 -0
  233. package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
  234. package/templates/pr-autofixer/agent/channels/github.ts +167 -0
  235. package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
  236. package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
  237. package/templates/pr-autofixer/agent/instructions.md +83 -0
  238. package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
  239. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
  240. package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
  241. package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
  242. package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
  243. package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
  244. package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
  245. package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
  246. package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
  247. package/templates/pr-autofixer/agent/storage.ts +7 -0
  248. package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
  249. package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
  250. package/templates/pr-autofixer/evals/evals.config.ts +5 -0
  251. package/templates/pr-autofixer/init.json +25 -0
  252. package/templates/pr-autofixer/package.json +18 -0
  253. package/templates/pr-autofixer/tsconfig.json +12 -0
  254. package/templates/security-reviewer/README.md +31 -0
  255. package/templates/security-reviewer/agent/agent.ts +15 -0
  256. package/templates/security-reviewer/agent/channels/github.ts +46 -0
  257. package/templates/security-reviewer/agent/instructions.md +22 -0
  258. package/templates/security-reviewer/agent/lib/repos.ts +5 -0
  259. package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
  260. package/templates/security-reviewer/evals/evals.config.ts +5 -0
  261. package/templates/security-reviewer/evals/review.eval.ts +53 -0
  262. package/templates/security-reviewer/init.json +25 -0
  263. package/templates/security-reviewer/package.json +18 -0
  264. package/templates/security-reviewer/tsconfig.json +12 -0
  265. package/templates/triage/README.md +27 -29
  266. package/templates/triage/agent/agent.ts +1 -0
  267. package/templates/triage/agent/channels/queue.ts +47 -0
  268. package/templates/triage/agent/channels/webhook.ts +50 -0
  269. package/templates/triage/agent/instructions.md +24 -13
  270. package/templates/triage/agent/lib/actuate.ts +759 -0
  271. package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
  272. package/templates/triage/agent/lib/tracker.ts +5 -0
  273. package/templates/triage/agent/lib/types.ts +52 -0
  274. package/templates/triage/agent/lib/verify-webhook.ts +81 -0
  275. package/templates/triage/agent/skills/triage-rubric.md +6 -5
  276. package/templates/triage/agent/tools/record_triage.ts +24 -5
  277. package/templates/triage/evals/triage.eval.ts +25 -3
  278. package/templates/triage/overlays/jira/README.md +21 -0
  279. package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
  280. package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
  281. package/templates/triage/overlays/linear/README.md +21 -0
  282. package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
  283. package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
  284. package/templates/triage/package.json +1 -1
  285. package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
  286. package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
  287. package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
  288. package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
  289. package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
  290. package/dist/playground/assets/index-BHAPd9KT.js +0 -85
  291. package/dist/playground/assets/index-DpKH0HcT.css +0 -1
  292. package/templates/triage/agent/channels/intake.ts +0 -20
  293. package/templates/triage/agent/channels/slack.ts +0 -3
@@ -0,0 +1,38 @@
1
+ export declare const V1ED_SIGNATURE_VERSION = "v1ed";
2
+ export declare const DEFAULT_ORIGIN_API_BASE_URL = "https://origin.cursor.com";
3
+ export type OriginJwk = Record<string, unknown>;
4
+ export type OriginWebhookHeaders = {
5
+ "webhook-id"?: string;
6
+ "webhook-timestamp"?: string;
7
+ "webhook-signature"?: string;
8
+ };
9
+ export declare function buildV1edSignedContentDigest(args: {
10
+ webhookId: string;
11
+ timestampSeconds: number;
12
+ body: string | Buffer;
13
+ }): string;
14
+ /**
15
+ * Verify a delivery's Standard Webhooks headers against the Origin signing
16
+ * JWKS. Owns the whole check: header presence, timestamp parsing, the replay
17
+ * window, and Ed25519 verification against any provided key.
18
+ */
19
+ export declare function verifyV1edWebhookPayload(args: {
20
+ publicJwks: readonly OriginJwk[];
21
+ body: string | Buffer;
22
+ headers: OriginWebhookHeaders;
23
+ nowMs?: number;
24
+ }): boolean;
25
+ /** Fetch (and cache for 5 minutes) the Origin signing JWKS. */
26
+ export declare function fetchOriginJwks(apiBaseUrl: string, fetchImpl?: typeof fetch): Promise<readonly OriginJwk[]>;
27
+ export interface VerifyOriginWebhookOptions {
28
+ apiBaseUrl?: string;
29
+ fetchImpl?: typeof fetch;
30
+ nowMs?: number;
31
+ }
32
+ /**
33
+ * Verify a Standard-Webhooks v1ed Origin payload. Throws on failure.
34
+ */
35
+ export declare function verifyOriginWebhook(body: string | Buffer, headers: OriginWebhookHeaders, options?: VerifyOriginWebhookOptions): Promise<void>;
36
+ /** Test helper: drop the in-process JWKS cache. */
37
+ export declare function resetOriginWebhookKeysCache(): void;
38
+ //# sourceMappingURL=origin-webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin-webhook.d.ts","sourceRoot":"","sources":["../../../src/channels/origin/origin-webhook.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAC7C,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AAavE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,GAAG,MAAM,CAUT;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CA6DV;AAMD,+DAA+D;AAC/D,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CA2B/B;AAED,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,EAAE,oBAAoB,EAC7B,OAAO,CAAC,EAAE,0BAA0B,GACnC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,mDAAmD;AACnD,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
@@ -0,0 +1,151 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ /**
11
+ * Origin webhook verification (Standard Webhooks `v1ed`).
12
+ *
13
+ * Direct Origin HTTP deliveries must verify the Ed25519 signature
14
+ * (`webhook-id`, `webhook-timestamp`, `webhook-signature`) against Origin's
15
+ * JWKS. Cursor-events relay deliveries skip this — the platform already
16
+ * verified the webhook before enqueueing.
17
+ *
18
+ * The signed content matches the Standard Webhooks `v1` scheme
19
+ * (`${webhook-id}.${webhook-timestamp}.${body}`) but is hashed to a lowercase
20
+ * hex SHA-256 before signing. Lifted from
21
+ * `factory/agentic-owners/agent/lib/origin-webhook.ts` /
22
+ * `backend/server/src/origin/webhooks/asymmetricWebhookSignature.ts`.
23
+ */
24
+ import { createHash, createPublicKey, verify as verifyEd25519, } from "node:crypto";
25
+ export const V1ED_SIGNATURE_VERSION = "v1ed";
26
+ export const DEFAULT_ORIGIN_API_BASE_URL = "https://origin.cursor.com";
27
+ /** ±5 minutes, the Standard Webhooks replay-window convention. */
28
+ const V1ED_TIMESTAMP_TOLERANCE_SECONDS = 5 * 60;
29
+ /**
30
+ * The sender emits exactly one v1ed entry; key rotation is handled by trying
31
+ * multiple public keys, never multiple signatures.
32
+ */
33
+ const MAX_V1ED_CANDIDATE_SIGNATURES = 1;
34
+ const JWKS_CACHE_TTL_MS = 5 * 60 * 1000;
35
+ export function buildV1edSignedContentDigest(args) {
36
+ const prefix = Buffer.from(`${args.webhookId}.${args.timestampSeconds}.`, "utf8");
37
+ const body = typeof args.body === "string" ? Buffer.from(args.body, "utf8") : args.body;
38
+ return createHash("sha256")
39
+ .update(Buffer.concat([prefix, body]))
40
+ .digest("hex");
41
+ }
42
+ /**
43
+ * Verify a delivery's Standard Webhooks headers against the Origin signing
44
+ * JWKS. Owns the whole check: header presence, timestamp parsing, the replay
45
+ * window, and Ed25519 verification against any provided key.
46
+ */
47
+ export function verifyV1edWebhookPayload(args) {
48
+ var _a;
49
+ const webhookId = args.headers["webhook-id"];
50
+ const webhookTimestamp = args.headers["webhook-timestamp"];
51
+ const signatureHeader = args.headers["webhook-signature"];
52
+ if (webhookId === undefined ||
53
+ webhookTimestamp === undefined ||
54
+ signatureHeader === undefined) {
55
+ return false;
56
+ }
57
+ const timestampSeconds = Number.parseInt(webhookTimestamp, 10);
58
+ if (Number.isNaN(timestampSeconds)) {
59
+ return false;
60
+ }
61
+ const nowSeconds = Math.floor(((_a = args.nowMs) !== null && _a !== void 0 ? _a : Date.now()) / 1000);
62
+ if (Math.abs(nowSeconds - timestampSeconds) > V1ED_TIMESTAMP_TOLERANCE_SECONDS) {
63
+ return false;
64
+ }
65
+ const digestBytes = Buffer.from(buildV1edSignedContentDigest({
66
+ webhookId,
67
+ timestampSeconds,
68
+ body: args.body,
69
+ }), "utf8");
70
+ const candidateSignatures = signatureHeader
71
+ .split(/\s+/)
72
+ .filter((entry) => entry.startsWith(`${V1ED_SIGNATURE_VERSION},`))
73
+ .map((entry) => entry.slice(V1ED_SIGNATURE_VERSION.length + 1))
74
+ .slice(0, MAX_V1ED_CANDIDATE_SIGNATURES);
75
+ if (candidateSignatures.length === 0) {
76
+ return false;
77
+ }
78
+ for (const jwk of args.publicJwks) {
79
+ let publicKey;
80
+ try {
81
+ publicKey = createPublicKey({
82
+ key: jwk,
83
+ format: "jwk",
84
+ });
85
+ }
86
+ catch (_b) {
87
+ continue;
88
+ }
89
+ for (const candidate of candidateSignatures) {
90
+ const signature = Buffer.from(candidate, "base64");
91
+ try {
92
+ if (verifyEd25519(null, digestBytes, publicKey, signature)) {
93
+ return true;
94
+ }
95
+ }
96
+ catch (_c) {
97
+ // Malformed signature bytes or a non-Ed25519 key: try the next pair.
98
+ }
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ let jwksCache;
104
+ /** Fetch (and cache for 5 minutes) the Origin signing JWKS. */
105
+ export function fetchOriginJwks(apiBaseUrl_1) {
106
+ return __awaiter(this, arguments, void 0, function* (apiBaseUrl, fetchImpl = fetch) {
107
+ const now = Date.now();
108
+ if (jwksCache !== undefined &&
109
+ jwksCache.apiBaseUrl === apiBaseUrl &&
110
+ now - jwksCache.fetchedAtMs < JWKS_CACHE_TTL_MS) {
111
+ return jwksCache.keys;
112
+ }
113
+ const response = yield fetchImpl(`${apiBaseUrl.replace(/\/+$/, "")}/v1/origin/keys`);
114
+ if (!response.ok) {
115
+ throw new Error(`Origin JWKS fetch failed: status=${response.status}`);
116
+ }
117
+ const body = yield response.json().catch(() => undefined);
118
+ const keys = typeof body === "object" &&
119
+ body !== null &&
120
+ Array.isArray(body.keys)
121
+ ? body.keys
122
+ : [];
123
+ if (keys.length === 0) {
124
+ throw new Error("Origin JWKS fetch returned no keys");
125
+ }
126
+ jwksCache = { apiBaseUrl, keys, fetchedAtMs: now };
127
+ return keys;
128
+ });
129
+ }
130
+ /**
131
+ * Verify a Standard-Webhooks v1ed Origin payload. Throws on failure.
132
+ */
133
+ export function verifyOriginWebhook(body, headers, options) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ var _a, _b;
136
+ const apiBaseUrl = (_a = options === null || options === void 0 ? void 0 : options.apiBaseUrl) !== null && _a !== void 0 ? _a : DEFAULT_ORIGIN_API_BASE_URL;
137
+ const keys = yield fetchOriginJwks(apiBaseUrl, (_b = options === null || options === void 0 ? void 0 : options.fetchImpl) !== null && _b !== void 0 ? _b : fetch);
138
+ if (!verifyV1edWebhookPayload({
139
+ publicJwks: keys,
140
+ body,
141
+ headers,
142
+ nowMs: options === null || options === void 0 ? void 0 : options.nowMs,
143
+ })) {
144
+ throw new Error("Origin webhook signature verification failed");
145
+ }
146
+ });
147
+ }
148
+ /** Test helper: drop the in-process JWKS cache. */
149
+ export function resetOriginWebhookKeysCache() {
150
+ jwksCache = undefined;
151
+ }
@@ -0,0 +1,152 @@
1
+ import type { AgentCloudOptions, ArtifactsApi, AuthContext, AuthPolicy, ChannelStartArgs, HostContext } from "../../types.js";
2
+ export interface OriginActor {
3
+ id: string;
4
+ kind?: string;
5
+ }
6
+ export interface OriginRepositoryRef {
7
+ owner: string;
8
+ name: string;
9
+ fullName: string;
10
+ }
11
+ export interface OriginPullRequestEvent {
12
+ type: string;
13
+ number: number;
14
+ url: string;
15
+ title?: string;
16
+ body?: string | null;
17
+ draft?: boolean;
18
+ merged?: boolean;
19
+ authorId?: string;
20
+ headRef?: string;
21
+ headSha?: string;
22
+ baseRef?: string;
23
+ baseSha?: string;
24
+ }
25
+ export interface OriginComment {
26
+ id: string;
27
+ body: string;
28
+ author?: OriginActor;
29
+ }
30
+ export interface OriginReview {
31
+ id?: string;
32
+ verdict?: string;
33
+ author?: OriginActor;
34
+ }
35
+ export interface OriginEventContext {
36
+ deliveryId?: string;
37
+ eventType: string;
38
+ repository: OriginRepositoryRef;
39
+ pullRequestNumber: number;
40
+ pullRequestUrl: string;
41
+ sender?: OriginActor;
42
+ host: HostContext;
43
+ artifacts: ArtifactsApi;
44
+ }
45
+ export type OriginTurnDispatch = {
46
+ auth: AuthContext;
47
+ message?: string;
48
+ title?: string;
49
+ context?: readonly string[];
50
+ };
51
+ export type OriginTaskDispatch = {
52
+ task: () => Promise<void>;
53
+ };
54
+ export type OriginDispatch = OriginTurnDispatch | OriginTaskDispatch;
55
+ export type OriginDispatchResult = OriginDispatch | null | Promise<OriginDispatch | null>;
56
+ export type OriginPullRequestHook = (ctx: OriginEventContext, pullRequest: OriginPullRequestEvent) => OriginDispatchResult;
57
+ export type OriginCommentHook = (ctx: OriginEventContext, comment: OriginComment) => OriginDispatchResult;
58
+ export type OriginReviewHook = (ctx: OriginEventContext, review: OriginReview) => OriginDispatchResult;
59
+ export type OriginEventHook = (ctx: OriginEventContext, event: {
60
+ eventType: string;
61
+ payload: unknown;
62
+ }) => OriginDispatchResult;
63
+ export type OriginCloudOptionsResolver = (ctx: OriginEventContext, pullRequest: OriginPullRequestEvent) => AgentCloudOptions | undefined | Promise<AgentCloudOptions | undefined>;
64
+ /**
65
+ * Origin delivery envelope. Public payloads are proto3 camelCase
66
+ * (`pullRequest`, `repository.owner.slug`).
67
+ */
68
+ export interface OriginWebhookEnvelope {
69
+ deliveryId?: string;
70
+ appId?: string;
71
+ installationId?: string;
72
+ sender?: OriginActor;
73
+ event: {
74
+ id?: string;
75
+ type: string;
76
+ eventTime?: string;
77
+ payload?: unknown;
78
+ };
79
+ }
80
+ export interface OriginChannelOptions {
81
+ /**
82
+ * Use the signed-in Cursor account for inbound Origin events via
83
+ * `/v0/scm-events` (`provider: "origin"`). `true` takes repository scope
84
+ * from `serve --cursor-events --repo`; `{ repos }` makes the channel
85
+ * self-contained. Origin identity is the Cursor user — there is no
86
+ * separate OAuth principal, and this does not mint a GitHub token.
87
+ */
88
+ cursorAccount?: boolean | {
89
+ repos: readonly string[];
90
+ };
91
+ /**
92
+ * Origin app id this channel accepts (`envelope.appId`). v1ed only proves
93
+ * Origin signed the body with the platform JWKS; it does not bind the
94
+ * delivery to one app (any Origin app can aim signed POSTs at a public
95
+ * URL). Required for non-loopback HTTP, same role as GitHub's per-channel
96
+ * webhook secret. Falls back to `AGENT_SERVE_ORIGIN_APP_ID`.
97
+ */
98
+ appId?: string | readonly string[];
99
+ /**
100
+ * Optional Origin installation id bind (`envelope.installationId`). When
101
+ * set, HTTP deliveries for any other installation are rejected.
102
+ */
103
+ installationId?: string | readonly string[];
104
+ /**
105
+ * Optional HTTP/dispatch repo allowlist (`owner/name`). `{ cursorAccount:
106
+ * { repos } }` is also an allowlist. Events outside the set are acked
107
+ * without starting a turn. Omit to accept every repo the bound app sends.
108
+ */
109
+ repos?: readonly string[];
110
+ /**
111
+ * Route auth chain. Defaults to allow-all when {@link appId} (or
112
+ * `AGENT_SERVE_ORIGIN_APP_ID`) is set, because v1ed plus that app-id bind
113
+ * is the request auth. Without an app id, production stays loopback-strict
114
+ * (GitHub's no-secret posture); `--dev` / skip-verify admits loopback only.
115
+ */
116
+ auth?: AuthPolicy[];
117
+ /** Route path under `/v1/channels/<name>`. Defaults to `/`. */
118
+ path?: string;
119
+ /** Origin API base used to fetch `/v1/origin/keys` for v1ed verify. */
120
+ apiBaseUrl?: string | (() => string | undefined);
121
+ /**
122
+ * Pin the Origin event types this channel dispatches on (published as
123
+ * `meta.originWebhookEvents`). Defaults to the set derived from hooks.
124
+ */
125
+ webhookEvents?: readonly string[];
126
+ /**
127
+ * Decide whether an inbound pull-request event starts a turn. Return
128
+ * `{ auth: defaultOriginAuth(ctx) }` to dispatch, `null` to ignore.
129
+ * Without this hook the channel dispatches on created / published /
130
+ * head_ref.pushed / base_ref.updated.
131
+ */
132
+ onPullRequest?: OriginPullRequestHook;
133
+ /** Opt-in `pull_request.comment.*` handler. No default dispatch. */
134
+ onComment?: OriginCommentHook;
135
+ /** Opt-in `pull_request.review.*` handler. No default dispatch. */
136
+ onReview?: OriginReviewHook;
137
+ /**
138
+ * Catch-all for Origin event types without a typed hook. NOT a fallback:
139
+ * events with a typed hook never reach this one.
140
+ */
141
+ onEvent?: OriginEventHook;
142
+ /**
143
+ * Per-session cloud options. Pass `true` to attach the PR repo using the
144
+ * pull request URL, or a resolver for custom cloud options.
145
+ */
146
+ cloud?: boolean | AgentCloudOptions | OriginCloudOptionsResolver;
147
+ /** Called once when the server mounts this channel. */
148
+ onStart?: (args: ChannelStartArgs) => void | Promise<void>;
149
+ /** Called when the server shuts down. */
150
+ onStop?: () => void | Promise<void>;
151
+ }
152
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/channels/origin/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,mBAAmB,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAErE,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,IAAI,GACJ,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;AAEnC,MAAM,MAAM,qBAAqB,GAAG,CAClC,GAAG,EAAE,kBAAkB,EACvB,WAAW,EAAE,sBAAsB,KAChC,oBAAoB,CAAC;AAE1B,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,kBAAkB,EACvB,OAAO,EAAE,aAAa,KACnB,oBAAoB,CAAC;AAE1B,MAAM,MAAM,gBAAgB,GAAG,CAC7B,GAAG,EAAE,kBAAkB,EACvB,MAAM,EAAE,YAAY,KACjB,oBAAoB,CAAC;AAE1B,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,KAC3C,oBAAoB,CAAC;AAE1B,MAAM,MAAM,0BAA0B,GAAG,CACvC,GAAG,EAAE,kBAAkB,EACvB,WAAW,EAAE,sBAAsB,KAChC,iBAAiB,GAAG,SAAS,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE;QACL,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;IACvD;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC5C;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC;IACpB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD;;;OAGG;IACH,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,oEAAoE;IACpE,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,iBAAiB,GAAG,0BAA0B,CAAC;IACjE,uDAAuD;IACvD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,9 @@
1
1
  /**
2
- * Slack channel doctor — verify env, Socket Mode app token, auth.test.
2
+ * Slack channel doctor — verify env, Socket Mode app token, auth.test, and
3
+ * (when watched channels are supplied) that the bot token's workspace can
4
+ * actually see, join, and read those channels.
3
5
  */
6
+ import { callSlackApi } from "./api.js";
4
7
  import type { SlackCredentials } from "./types.js";
5
8
  export interface SlackDoctorCheck {
6
9
  id: string;
@@ -14,11 +17,24 @@ export interface SlackDoctorResult {
14
17
  bot: string;
15
18
  app: string;
16
19
  };
20
+ /**
21
+ * Things a bot token cannot prove. Event subscriptions are app-level
22
+ * config, so a fully green doctor can still be a silent agent.
23
+ */
24
+ notes?: string[];
17
25
  }
18
26
  export interface SlackDoctorOptions {
19
27
  credentials?: SlackCredentials;
20
28
  /** Per-agent env prefix (e.g. `MY_AGENT`). */
21
29
  envPrefix?: string;
30
+ /**
31
+ * Watched channel ids (`engagement.channelPosts.allow`). Each is checked
32
+ * for visibility, bot membership, and history scope.
33
+ */
34
+ channels?: readonly string[];
35
+ /** Test seams. */
36
+ callImpl?: typeof callSlackApi;
37
+ fetchImpl?: typeof fetch;
22
38
  }
23
39
  export declare function runSlackDoctor(options?: SlackDoctorOptions): Promise<SlackDoctorResult>;
24
40
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/doctor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,cAAc,CAClC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CA0G5B"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/doctor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAoC,MAAM,UAAU,CAAC;AAE1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,kBAAkB;IAClB,QAAQ,CAAC,EAAE,OAAO,YAAY,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AA4ID,wBAAsB,cAAc,CAClC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAsK5B"}
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Slack channel doctor — verify env, Socket Mode app token, auth.test.
2
+ * Slack channel doctor — verify env, Socket Mode app token, auth.test, and
3
+ * (when watched channels are supplied) that the bot token's workspace can
4
+ * actually see, join, and read those channels.
3
5
  */
4
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
5
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -12,17 +14,136 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
14
  };
13
15
  import { callSlackApi, resolveAppToken, resolveBotToken } from "./api.js";
14
16
  import { credentialsFromEnv, slackEnvKeys } from "./credentials.js";
17
+ /**
18
+ * Socket Mode only delivers bot events the app subscribes to. Missing
19
+ * subscriptions produce total silence with valid tokens, and cannot be read
20
+ * back from a bot token.
21
+ */
22
+ const REQUIRED_BOT_EVENTS = {
23
+ app_mention: "@mentions of the bot",
24
+ "message.im": "direct messages",
25
+ "message.channels": "public channel posts",
26
+ "message.groups": "private channel posts",
27
+ reaction_added: "reaction handlers (onReactionAdded)",
28
+ };
29
+ function eventSubscriptionNote() {
30
+ const lines = Object.entries(REQUIRED_BOT_EVENTS).map(([event, purpose]) => ` ${event} — ${purpose}`);
31
+ return [
32
+ "A bot token cannot read Event Subscriptions. If every check above passes",
33
+ "but the bot is silent, verify these bot events are subscribed on the app",
34
+ "(Slack app config -> Event Subscriptions -> Subscribe to bot events):",
35
+ ...lines,
36
+ ].join("\n");
37
+ }
38
+ function asRecord(value) {
39
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
40
+ return undefined;
41
+ }
42
+ return value;
43
+ }
44
+ function errorOf(response) {
45
+ return typeof response.error === "string" ? response.error : "unknown";
46
+ }
47
+ /**
48
+ * Visibility, membership, and history-scope checks for one watched channel.
49
+ * `channel_not_found` here is the classic wrong-workspace bot token: the
50
+ * token authenticates fine, it just belongs to a different installation.
51
+ */
52
+ function checkChannel(args) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ const { call, botToken, channelId } = args;
55
+ const checks = [];
56
+ let info;
57
+ try {
58
+ info = yield call({
59
+ botToken,
60
+ operation: "conversations.info",
61
+ body: { channel: channelId },
62
+ });
63
+ }
64
+ catch (error) {
65
+ checks.push({
66
+ id: `channel_visible:${channelId}`,
67
+ ok: false,
68
+ message: `conversations.info threw: ${error instanceof Error ? error.message : String(error)}`,
69
+ });
70
+ return checks;
71
+ }
72
+ if (info.ok !== true) {
73
+ const error = errorOf(info);
74
+ checks.push({
75
+ id: `channel_visible:${channelId}`,
76
+ ok: false,
77
+ message: error === "channel_not_found"
78
+ ? `${channelId} not found by this bot token — the token is probably installed on a different workspace, or the channel id is wrong`
79
+ : `conversations.info failed: ${error}`,
80
+ });
81
+ return checks;
82
+ }
83
+ const channel = asRecord(info.channel);
84
+ const name = typeof (channel === null || channel === void 0 ? void 0 : channel.name) === "string" ? channel.name : "?";
85
+ checks.push({
86
+ id: `channel_visible:${channelId}`,
87
+ ok: true,
88
+ message: `${channelId} visible (#${name})`,
89
+ });
90
+ const isMember = (channel === null || channel === void 0 ? void 0 : channel.is_member) === true;
91
+ checks.push({
92
+ id: `channel_member:${channelId}`,
93
+ ok: isMember,
94
+ message: isMember
95
+ ? `bot is a member of ${channelId}`
96
+ : `bot is NOT a member of ${channelId} — invite it to the channel`,
97
+ });
98
+ let history;
99
+ try {
100
+ history = yield call({
101
+ botToken,
102
+ operation: "conversations.history",
103
+ body: { channel: channelId, limit: 1 },
104
+ });
105
+ }
106
+ catch (error) {
107
+ checks.push({
108
+ id: `channel_history:${channelId}`,
109
+ ok: false,
110
+ message: `conversations.history threw: ${error instanceof Error ? error.message : String(error)}`,
111
+ });
112
+ return checks;
113
+ }
114
+ if (history.ok === true) {
115
+ checks.push({
116
+ id: `channel_history:${channelId}`,
117
+ ok: true,
118
+ message: `conversations.history ok for ${channelId}`,
119
+ });
120
+ return checks;
121
+ }
122
+ const error = errorOf(history);
123
+ const needed = typeof history.needed === "string" ? history.needed : undefined;
124
+ checks.push({
125
+ id: `channel_history:${channelId}`,
126
+ ok: false,
127
+ message: error === "missing_scope"
128
+ ? `conversations.history missing scope${needed === undefined ? "" : ` ${needed}`} — add it and reinstall the app`
129
+ : `conversations.history failed: ${error}`,
130
+ });
131
+ return checks;
132
+ });
133
+ }
15
134
  export function runSlackDoctor() {
16
135
  return __awaiter(this, arguments, void 0, function* (options = {}) {
17
- var _a, _b, _c, _d, _e;
136
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
18
137
  const credentials = (_a = options.credentials) !== null && _a !== void 0 ? _a : credentialsFromEnv(options.envPrefix);
19
138
  const envKeys = slackEnvKeys(options.envPrefix);
20
139
  const checks = [];
140
+ const call = (_b = options.callImpl) !== null && _b !== void 0 ? _b : callSlackApi;
141
+ const fetchImpl = (_c = options.fetchImpl) !== null && _c !== void 0 ? _c : fetch;
21
142
  let appTokenRaw;
22
143
  try {
23
144
  appTokenRaw = yield resolveAppToken(credentials);
24
145
  }
25
- catch (_f) {
146
+ catch (_k) {
26
147
  appTokenRaw = undefined;
27
148
  }
28
149
  const hasApp = appTokenRaw !== undefined && appTokenRaw !== "";
@@ -35,7 +156,7 @@ export function runSlackDoctor() {
35
156
  });
36
157
  if (hasApp && appTokenRaw !== undefined) {
37
158
  try {
38
- const response = yield fetch("https://slack.com/api/apps.connections.open", {
159
+ const response = yield fetchImpl("https://slack.com/api/apps.connections.open", {
39
160
  method: "POST",
40
161
  headers: {
41
162
  authorization: `Bearer ${appTokenRaw}`,
@@ -55,7 +176,7 @@ export function runSlackDoctor() {
55
176
  checks.push({
56
177
  id: "connections_open",
57
178
  ok: false,
58
- message: `apps.connections.open failed: ${String((_b = data.error) !== null && _b !== void 0 ? _b : "unknown")}`,
179
+ message: `apps.connections.open failed: ${String((_d = data.error) !== null && _d !== void 0 ? _d : "unknown")}`,
59
180
  });
60
181
  }
61
182
  }
@@ -71,7 +192,7 @@ export function runSlackDoctor() {
71
192
  try {
72
193
  botToken = yield resolveBotToken(credentials);
73
194
  }
74
- catch (_g) {
195
+ catch (_l) {
75
196
  botToken = undefined;
76
197
  }
77
198
  const hasToken = botToken !== undefined && botToken !== "";
@@ -80,25 +201,31 @@ export function runSlackDoctor() {
80
201
  ok: hasToken,
81
202
  message: hasToken ? `${envKeys.bot} present` : `${envKeys.bot} is missing`,
82
203
  });
204
+ let authOk = false;
205
+ let botUserId = "";
83
206
  if (hasToken) {
84
207
  try {
85
- const auth = yield callSlackApi({
208
+ const auth = yield call({
86
209
  botToken,
87
210
  operation: "auth.test",
88
211
  body: {},
89
212
  });
90
213
  if (auth.ok === true) {
214
+ authOk = true;
215
+ botUserId = typeof auth.user_id === "string" ? auth.user_id : "";
91
216
  checks.push({
92
217
  id: "auth_test",
93
218
  ok: true,
94
- message: `auth.test ok (team=${String((_c = auth.team) !== null && _c !== void 0 ? _c : "?")}, user=${String((_d = auth.user) !== null && _d !== void 0 ? _d : "?")})`,
219
+ message: `auth.test ok (team=${String((_e = auth.team) !== null && _e !== void 0 ? _e : "?")}` +
220
+ `, team_id=${String((_f = auth.team_id) !== null && _f !== void 0 ? _f : "?")}` +
221
+ `, user=${String((_g = auth.user) !== null && _g !== void 0 ? _g : "?")})`,
95
222
  });
96
223
  }
97
224
  else {
98
225
  checks.push({
99
226
  id: "auth_test",
100
227
  ok: false,
101
- message: `auth.test failed: ${String((_e = auth.error) !== null && _e !== void 0 ? _e : "unknown")}`,
228
+ message: `auth.test failed: ${String((_h = auth.error) !== null && _h !== void 0 ? _h : "unknown")}`,
102
229
  });
103
230
  }
104
231
  }
@@ -110,6 +237,55 @@ export function runSlackDoctor() {
110
237
  });
111
238
  }
112
239
  }
113
- return { ok: checks.every((check) => check.ok), checks, envKeys };
240
+ // The external-sender policy (`blockExternals`, on by default) classifies a
241
+ // sender as external whenever `users.info` fails — so a token that cannot
242
+ // look up users silently drops EVERY inbound event while all other checks
243
+ // stay green. Probe with the bot's own user id from auth.test.
244
+ if (authOk && botToken !== undefined && botUserId !== "") {
245
+ let info;
246
+ let threw;
247
+ try {
248
+ info = yield call({
249
+ botToken,
250
+ operation: "users.info",
251
+ body: { user: botUserId },
252
+ });
253
+ }
254
+ catch (error) {
255
+ threw = error instanceof Error ? error.message : String(error);
256
+ }
257
+ if ((info === null || info === void 0 ? void 0 : info.ok) === true) {
258
+ checks.push({
259
+ id: "users_read",
260
+ ok: true,
261
+ message: "users.info ok (users:read granted; external-sender policy can classify senders)",
262
+ });
263
+ }
264
+ else {
265
+ const error = threw !== null && threw !== void 0 ? threw : (info === undefined ? "unknown" : errorOf(info));
266
+ const hint = error === "missing_scope"
267
+ ? "add the users:read scope and reinstall the app"
268
+ : error === "user_not_found"
269
+ ? "the token cannot see its own workspace's users — probably a token from a different installation"
270
+ : "fix the token";
271
+ checks.push({
272
+ id: "users_read",
273
+ ok: false,
274
+ message: `users.info failed: ${error} — ${hint}. The external-sender policy fails open to "external" on users.info errors, so every inbound event is silently dropped.`,
275
+ });
276
+ }
277
+ }
278
+ const channels = (_j = options.channels) !== null && _j !== void 0 ? _j : [];
279
+ if (authOk && botToken !== undefined && channels.length > 0) {
280
+ for (const channelId of channels) {
281
+ checks.push(...(yield checkChannel({ call, botToken, channelId })));
282
+ }
283
+ }
284
+ return {
285
+ ok: checks.every((check) => check.ok),
286
+ checks,
287
+ envKeys,
288
+ notes: [eventSubscriptionNote()],
289
+ };
114
290
  });
115
291
  }