@cursor/bdk 0.2.3 → 0.2.4

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 (577) hide show
  1. package/dist/bin/agent-serve.js +9 -1
  2. package/dist/channels/slack/attachments.d.ts +2 -1
  3. package/dist/channels/slack/attachments.d.ts.map +1 -1
  4. package/dist/channels/slack/attachments.js +19 -9
  5. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  6. package/dist/channels/slack/dispatch.js +26 -24
  7. package/dist/channels/slack/thread-context.d.ts +21 -11
  8. package/dist/channels/slack/thread-context.d.ts.map +1 -1
  9. package/dist/channels/slack/thread-context.js +75 -27
  10. package/dist/docs/404.html +2 -2
  11. package/dist/docs/assets/{app.BOIHhrKi.js → app.4kQ8Db_q.js} +4 -4
  12. package/dist/docs/assets/chunks/@localSearchIndexroot.CvuAJ0My.js +1 -0
  13. package/dist/docs/assets/chunks/{VPLocalSearchBox.D49zVZqM.js → VPLocalSearchBox.DY1RwB8Q.js} +1 -1
  14. package/dist/docs/assets/chunks/{arc.hmjb-sc4.js → arc.CxE_fSPK.js} +1 -1
  15. package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.CxEI1KqM.js → architectureDiagram-Q4EWVU46.CPX426ui.js} +1 -1
  16. package/dist/docs/assets/chunks/{baseUniq.DHHvBWoV.js → baseUniq.CB52JrKG.js} +1 -1
  17. package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.DnXW-3bu.js → blockDiagram-DXYQGD6D.J0cNaQCL.js} +1 -1
  18. package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.e-47O2jM.js → c4Diagram-AHTNJAMY.DJn3vEGT.js} +1 -1
  19. package/dist/docs/assets/chunks/channel.BoNycAoN.js +1 -0
  20. package/dist/docs/assets/chunks/{chunk-4BX2VUAB.3ry4pYXB.js → chunk-4BX2VUAB.Dao618F6.js} +1 -1
  21. package/dist/docs/assets/chunks/{chunk-4TB4RGXK.DGIZFRx6.js → chunk-4TB4RGXK.QW8Rhjai.js} +1 -1
  22. package/dist/docs/assets/chunks/{chunk-55IACEB6.DKBnqg9U.js → chunk-55IACEB6.DfRLLKF_.js} +1 -1
  23. package/dist/docs/assets/chunks/{chunk-EDXVE4YY.BIU8Ue5k.js → chunk-EDXVE4YY.CIV2ApKQ.js} +1 -1
  24. package/dist/docs/assets/chunks/{chunk-FMBD7UC4.DP-TFhjI.js → chunk-FMBD7UC4.BEheSuxp.js} +1 -1
  25. package/dist/docs/assets/chunks/{chunk-OYMX7WX6.Ux0OYj5v.js → chunk-OYMX7WX6.CUN88ENB.js} +1 -1
  26. package/dist/docs/assets/chunks/{chunk-QZHKN3VN.BzHMwYox.js → chunk-QZHKN3VN.CgqCs8b_.js} +1 -1
  27. package/dist/docs/assets/chunks/{chunk-YZCP3GAM.D4OGAsSP.js → chunk-YZCP3GAM.iCH0nnTJ.js} +1 -1
  28. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.C4x07Gkj.js +1 -0
  29. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.C4x07Gkj.js +1 -0
  30. package/dist/docs/assets/chunks/clone.Db22cH_-.js +1 -0
  31. package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.Jq_7BUNx.js → cose-bilkent-S5V4N54A.BLcUxhWE.js} +1 -1
  32. package/dist/docs/assets/chunks/{dagre-KV5264BT.Di-skR2x.js → dagre-KV5264BT.Ep7LofOm.js} +1 -1
  33. package/dist/docs/assets/chunks/{diagram-5BDNPKRD.DeREHIdH.js → diagram-5BDNPKRD.Bzf4GSQ4.js} +1 -1
  34. package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.C7Kcqxro.js → diagram-G4DWMVQ6.DD6Jh-eM.js} +1 -1
  35. package/dist/docs/assets/chunks/{diagram-MMDJMWI5.BK3eohyo.js → diagram-MMDJMWI5.oOIl8bWd.js} +1 -1
  36. package/dist/docs/assets/chunks/{diagram-TYMM5635.C6TyymXT.js → diagram-TYMM5635.DHs85Ade.js} +1 -1
  37. package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.DYGG-gGx.js → erDiagram-SMLLAGMA.BIb5ghCk.js} +1 -1
  38. package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.C9nBcSV4.js → flowDiagram-DWJPFMVM.DV4paY9k.js} +1 -1
  39. package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.wAg_ymsp.js → ganttDiagram-T4ZO3ILL.AyEKA6_v.js} +1 -1
  40. package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.B87j9Oeg.js → gitGraphDiagram-UUTBAWPF.BltiCDFo.js} +1 -1
  41. package/dist/docs/assets/chunks/{graph.CuFMj31w.js → graph.DzuEYGdC.js} +1 -1
  42. package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.P_mLGumP.js → infoDiagram-42DDH7IO.BV2l981J.js} +1 -1
  43. package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DyuL_MsT.js → ishikawaDiagram-UXIWVN3A.BA4Izf-m.js} +1 -1
  44. package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.CtP09IoJ.js → journeyDiagram-VCZTEJTY.DjaFbFS3.js} +1 -1
  45. package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.BoeSYPZo.js → kanban-definition-6JOO6SKY.CfTFieDs.js} +1 -1
  46. package/dist/docs/assets/chunks/{layout.B0lMMtNo.js → layout.DzfWqTBV.js} +1 -1
  47. package/dist/docs/assets/chunks/{linear.HxRBiAFX.js → linear.BO6mpulV.js} +1 -1
  48. package/dist/docs/assets/chunks/{min.BGlFFHwS.js → min.BGgTadgT.js} +1 -1
  49. package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.JyLBD9M8.js → mindmap-definition-QFDTVHPH.DPuIXVWY.js} +1 -1
  50. package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.DnSmNZF8.js → pieDiagram-DEJITSTG.BOnl3bBg.js} +1 -1
  51. package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.CUCnbfCa.js → quadrantDiagram-34T5L4WZ.DNB6Ckdc.js} +1 -1
  52. package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.py0IVUn5.js → requirementDiagram-MS252O5E.D5-Qo7vb.js} +1 -1
  53. package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.Bkc-HWYp.js → sankeyDiagram-XADWPNL6.8t65cesf.js} +1 -1
  54. package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.Dqn8Mlv2.js → sequenceDiagram-FGHM5R23.CaG07BdS.js} +1 -1
  55. package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.JikkoQUU.js → stateDiagram-FHFEXIEX.TKOweTB6.js} +1 -1
  56. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.CuqmyQc4.js +1 -0
  57. package/dist/docs/assets/chunks/{theme.b6HQ6zG_.js → theme.Dx-JhpTB.js} +2 -2
  58. package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.BvpSXiBj.js → timeline-definition-GMOUNBTQ.fE53zQ2s.js} +1 -1
  59. package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.CU7MVnZx.js → vennDiagram-DHZGUBPP.DJsZZaJg.js} +1 -1
  60. package/dist/docs/assets/chunks/{wardley-RL74JXVD.BKwC4ctG.js → wardley-RL74JXVD.844kyrjl.js} +1 -1
  61. package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.BfaF6Uhe.js → wardleyDiagram-NUSXRM2D.CMZ-Ru_t.js} +1 -1
  62. package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.Bg5dcpj4.js → xychartDiagram-5P7HB3ND.XXrhBc7a.js} +1 -1
  63. package/dist/docs/assets/{deployment.md.BJ8i7Q6p.js → deployment.md.UhnJIMr-.js} +2 -2
  64. package/dist/docs/assets/{deployment.md.BJ8i7Q6p.lean.js → deployment.md.UhnJIMr-.lean.js} +1 -1
  65. package/dist/docs/assets/{guides_jev.md.BNDm14Gy.js → guides_jev.md.CRcgmjDb.js} +2 -2
  66. package/dist/docs/assets/{guides_jev.md.BNDm14Gy.lean.js → guides_jev.md.CRcgmjDb.lean.js} +1 -1
  67. package/dist/docs/assets/{reference_agent-config.md.DzGI777n.js → reference_agent-config.md.Cj5uBKp4.js} +1 -1
  68. package/dist/docs/assets/{reference_extensions.md.CPPrkWlJ.js → reference_extensions.md.pa2tN-e4.js} +4 -3
  69. package/dist/docs/assets/{reference_extensions.md.CPPrkWlJ.lean.js → reference_extensions.md.pa2tN-e4.lean.js} +1 -1
  70. package/dist/docs/building-with-agents.html +35 -35
  71. package/dist/docs/deployment.html +37 -37
  72. package/dist/docs/deployment.md +4 -1
  73. package/dist/docs/evals.html +35 -35
  74. package/dist/docs/guides/agent-to-agent.html +35 -35
  75. package/dist/docs/guides/bitbucket.html +35 -35
  76. package/dist/docs/guides/cloud-agents.html +35 -35
  77. package/dist/docs/guides/convert-automation.html +35 -35
  78. package/dist/docs/guides/github.html +35 -35
  79. package/dist/docs/guides/gitlab.html +35 -35
  80. package/dist/docs/guides/grokbot-agents.html +35 -35
  81. package/dist/docs/guides/hooks.html +35 -35
  82. package/dist/docs/guides/improve.html +35 -35
  83. package/dist/docs/guides/jev.html +37 -37
  84. package/dist/docs/guides/jev.md +7 -0
  85. package/dist/docs/guides/mcp-oauth.html +35 -35
  86. package/dist/docs/guides/opentelemetry.html +35 -35
  87. package/dist/docs/guides/slack.html +35 -35
  88. package/dist/docs/guides/webhooks.html +35 -35
  89. package/dist/docs/hashmap.json +1 -1
  90. package/dist/docs/hillclimbing.html +35 -35
  91. package/dist/docs/index.html +35 -35
  92. package/dist/docs/llms-full.txt +25 -2
  93. package/dist/docs/quickstart.html +35 -35
  94. package/dist/docs/reference/agent-config.html +37 -37
  95. package/dist/docs/reference/agent-config.md +1 -1
  96. package/dist/docs/reference/artifacts.html +35 -35
  97. package/dist/docs/reference/channels.html +35 -35
  98. package/dist/docs/reference/cli.html +35 -35
  99. package/dist/docs/reference/connections.html +35 -35
  100. package/dist/docs/reference/evals.html +35 -35
  101. package/dist/docs/reference/extensions.html +39 -38
  102. package/dist/docs/reference/extensions.md +13 -0
  103. package/dist/docs/reference/hooks.html +35 -35
  104. package/dist/docs/reference/http-api.html +35 -35
  105. package/dist/docs/reference/instructions.html +35 -35
  106. package/dist/docs/reference/playground.html +35 -35
  107. package/dist/docs/reference/project-layout.html +35 -35
  108. package/dist/docs/reference/prompt.html +35 -35
  109. package/dist/docs/reference/schedules.html +35 -35
  110. package/dist/docs/reference/sessions.html +35 -35
  111. package/dist/docs/reference/skills.html +35 -35
  112. package/dist/docs/reference/subagents.html +35 -35
  113. package/dist/docs/reference/tools.html +35 -35
  114. package/dist/docs/templates/agentic-owners.html +35 -35
  115. package/dist/docs/templates/security-reviewer.html +35 -35
  116. package/dist/docs/templates/thermo-quality-review.html +35 -35
  117. package/dist/docs/templates/thermo-review.html +35 -35
  118. package/dist/docs/templates/triage.html +35 -35
  119. package/dist/docs/troubleshooting.html +35 -35
  120. package/dist/extensions/jev/extension.d.ts +3 -0
  121. package/dist/extensions/jev/extension.d.ts.map +1 -1
  122. package/dist/extensions/jev/extension.js +10 -1
  123. package/dist/extensions/jev/lib/evaluate.d.ts +4 -0
  124. package/dist/extensions/jev/lib/evaluate.d.ts.map +1 -1
  125. package/dist/extensions/jev/lib/evaluate.js +6 -2
  126. package/dist/extensions.d.ts +4 -2
  127. package/dist/extensions.d.ts.map +1 -1
  128. package/dist/extensions.js +5 -2
  129. package/dist/internal/cli-deploy.d.ts.map +1 -1
  130. package/dist/internal/cli-deploy.js +19 -5
  131. package/dist/internal/discovery/agent-config.d.ts +6 -5
  132. package/dist/internal/discovery/agent-config.d.ts.map +1 -1
  133. package/dist/internal/discovery/agent-config.js +29 -28
  134. package/dist/internal/discovery/agent.js +1 -1
  135. package/dist/internal/discovery/extensions.d.ts.map +1 -1
  136. package/dist/internal/discovery/extensions.js +12 -0
  137. package/dist/internal/hosting.d.ts +8 -0
  138. package/dist/internal/hosting.d.ts.map +1 -1
  139. package/dist/internal/hosting.js +24 -0
  140. package/dist/types.d.ts +18 -2
  141. package/dist/types.d.ts.map +1 -1
  142. package/docs/deployment.md +4 -1
  143. package/docs/guides/jev.md +7 -0
  144. package/docs/reference/agent-config.md +1 -1
  145. package/docs/reference/extensions.md +13 -0
  146. package/package.json +1 -9
  147. package/dist/docs/assets/chunks/@localSearchIndexroot.D5L-b34G.js +0 -1
  148. package/dist/docs/assets/chunks/channel.BbOGaJQP.js +0 -1
  149. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.Db58HCb1.js +0 -1
  150. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.Db58HCb1.js +0 -1
  151. package/dist/docs/assets/chunks/clone.Co4MBflG.js +0 -1
  152. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.CMsKhrW0.js +0 -1
  153. package/src/artifacts.ts +0 -68
  154. package/src/bin/agent-serve.ts +0 -1854
  155. package/src/channels/bitbucket/api.ts +0 -891
  156. package/src/channels/bitbucket/binding.ts +0 -417
  157. package/src/channels/bitbucket/bitbucket-channel.ts +0 -162
  158. package/src/channels/bitbucket/bitbucket-webhook.ts +0 -519
  159. package/src/channels/bitbucket/checks.ts +0 -146
  160. package/src/channels/bitbucket/cursor-account.ts +0 -106
  161. package/src/channels/bitbucket/defaults.ts +0 -100
  162. package/src/channels/bitbucket/events.ts +0 -74
  163. package/src/channels/bitbucket/index.ts +0 -51
  164. package/src/channels/bitbucket/replay.ts +0 -421
  165. package/src/channels/bitbucket/state.ts +0 -59
  166. package/src/channels/bitbucket/types.ts +0 -261
  167. package/src/channels/change-monitors/change-monitors-channel.ts +0 -63
  168. package/src/channels/change-monitors/index.ts +0 -8
  169. package/src/channels/change-monitors/types.ts +0 -42
  170. package/src/channels/checks.ts +0 -185
  171. package/src/channels/deployments/deployments-channel.ts +0 -224
  172. package/src/channels/deployments/index.ts +0 -25
  173. package/src/channels/deployments/types.ts +0 -92
  174. package/src/channels/github/api.ts +0 -526
  175. package/src/channels/github/auth.ts +0 -293
  176. package/src/channels/github/binding.ts +0 -233
  177. package/src/channels/github/checks.ts +0 -145
  178. package/src/channels/github/cursor-account.ts +0 -303
  179. package/src/channels/github/defaults.ts +0 -168
  180. package/src/channels/github/events.ts +0 -67
  181. package/src/channels/github/forward.ts +0 -359
  182. package/src/channels/github/github-channel.ts +0 -2334
  183. package/src/channels/github/index.ts +0 -54
  184. package/src/channels/github/progress.ts +0 -365
  185. package/src/channels/github/replay.ts +0 -372
  186. package/src/channels/github/state.ts +0 -124
  187. package/src/channels/github/types.ts +0 -590
  188. package/src/channels/gitlab/api.ts +0 -329
  189. package/src/channels/gitlab/binding.ts +0 -231
  190. package/src/channels/gitlab/checks.ts +0 -228
  191. package/src/channels/gitlab/cursor-account.ts +0 -105
  192. package/src/channels/gitlab/defaults.ts +0 -80
  193. package/src/channels/gitlab/events.ts +0 -37
  194. package/src/channels/gitlab/gitlab-channel.ts +0 -160
  195. package/src/channels/gitlab/gitlab-webhook.ts +0 -509
  196. package/src/channels/gitlab/index.ts +0 -44
  197. package/src/channels/gitlab/replay.ts +0 -328
  198. package/src/channels/gitlab/state.ts +0 -55
  199. package/src/channels/gitlab/types.ts +0 -247
  200. package/src/channels/issues/index.ts +0 -9
  201. package/src/channels/issues/issues-channel.ts +0 -45
  202. package/src/channels/issues/types.ts +0 -38
  203. package/src/channels/origin/api.ts +0 -507
  204. package/src/channels/origin/binding.ts +0 -114
  205. package/src/channels/origin/checks.ts +0 -250
  206. package/src/channels/origin/cursor-account.ts +0 -58
  207. package/src/channels/origin/defaults.ts +0 -140
  208. package/src/channels/origin/events.ts +0 -35
  209. package/src/channels/origin/index.ts +0 -35
  210. package/src/channels/origin/origin-channel.ts +0 -815
  211. package/src/channels/origin/origin-webhook.ts +0 -296
  212. package/src/channels/origin/reactions.ts +0 -72
  213. package/src/channels/origin/state.ts +0 -54
  214. package/src/channels/origin/types.ts +0 -295
  215. package/src/channels/slack/agentic-delivery.ts +0 -634
  216. package/src/channels/slack/agentic-posted.ts +0 -135
  217. package/src/channels/slack/agentic-signals.ts +0 -231
  218. package/src/channels/slack/api.ts +0 -761
  219. package/src/channels/slack/approvals.ts +0 -279
  220. package/src/channels/slack/assistant.ts +0 -76
  221. package/src/channels/slack/attachments.ts +0 -596
  222. package/src/channels/slack/auth.ts +0 -127
  223. package/src/channels/slack/blocks.ts +0 -131
  224. package/src/channels/slack/bot-mentions.ts +0 -318
  225. package/src/channels/slack/channel-watch.ts +0 -314
  226. package/src/channels/slack/cli-project.ts +0 -249
  227. package/src/channels/slack/code-fences.ts +0 -171
  228. package/src/channels/slack/constants.ts +0 -38
  229. package/src/channels/slack/credentials.ts +0 -88
  230. package/src/channels/slack/defaults.ts +0 -714
  231. package/src/channels/slack/dispatch.ts +0 -1060
  232. package/src/channels/slack/doctor.ts +0 -338
  233. package/src/channels/slack/eval-directive.ts +0 -306
  234. package/src/channels/slack/event-id-dedupe.ts +0 -30
  235. package/src/channels/slack/external-policy.ts +0 -75
  236. package/src/channels/slack/footer.ts +0 -106
  237. package/src/channels/slack/inbound.ts +0 -416
  238. package/src/channels/slack/index.ts +0 -53
  239. package/src/channels/slack/init.ts +0 -169
  240. package/src/channels/slack/interactive.ts +0 -406
  241. package/src/channels/slack/links.ts +0 -23
  242. package/src/channels/slack/live-delivery.ts +0 -489
  243. package/src/channels/slack/log.ts +0 -70
  244. package/src/channels/slack/manifest.ts +0 -240
  245. package/src/channels/slack/message-body.ts +0 -82
  246. package/src/channels/slack/nudge-carrier.ts +0 -224
  247. package/src/channels/slack/nudge-store.ts +0 -334
  248. package/src/channels/slack/paint-start.ts +0 -93
  249. package/src/channels/slack/placeholder.ts +0 -358
  250. package/src/channels/slack/pr-ref.ts +0 -74
  251. package/src/channels/slack/progress-delivery.ts +0 -138
  252. package/src/channels/slack/reasoning-card.ts +0 -125
  253. package/src/channels/slack/reasoning-chip.ts +0 -41
  254. package/src/channels/slack/recording-sink.ts +0 -167
  255. package/src/channels/slack/redact.ts +0 -6
  256. package/src/channels/slack/reply-options.ts +0 -279
  257. package/src/channels/slack/setup.ts +0 -125
  258. package/src/channels/slack/slack-channel.ts +0 -744
  259. package/src/channels/slack/socket-mode.ts +0 -140
  260. package/src/channels/slack/stop.ts +0 -25
  261. package/src/channels/slack/stream.ts +0 -66
  262. package/src/channels/slack/thread-context.ts +0 -485
  263. package/src/channels/slack/tool-approval-events.ts +0 -233
  264. package/src/channels/slack/tool-cards.ts +0 -198
  265. package/src/channels/slack/tool-chip.ts +0 -34
  266. package/src/channels/slack/types.ts +0 -787
  267. package/src/channels/slack/upload.ts +0 -137
  268. package/src/channels.ts +0 -543
  269. package/src/connections.ts +0 -153
  270. package/src/dashboard-session-url.ts +0 -30
  271. package/src/evals/assertions.ts +0 -688
  272. package/src/evals/expect.ts +0 -250
  273. package/src/evals/judge.ts +0 -329
  274. package/src/evals/loaders.ts +0 -111
  275. package/src/evals/matchers.ts +0 -223
  276. package/src/evals/reporters.ts +0 -257
  277. package/src/evals/results.ts +0 -130
  278. package/src/evals/run-facts.ts +0 -306
  279. package/src/evals.ts +0 -709
  280. package/src/extensions/cursor-cloud-agents/extension.ts +0 -131
  281. package/src/extensions/cursor-cloud-agents/lib/backend.ts +0 -386
  282. package/src/extensions/cursor-cloud-agents/lib/channels.ts +0 -11
  283. package/src/extensions/cursor-cloud-agents/lib/fleet.ts +0 -98
  284. package/src/extensions/cursor-cloud-agents/lib/inputs.ts +0 -5
  285. package/src/extensions/cursor-cloud-agents/lib/repos.ts +0 -80
  286. package/src/extensions/cursor-cloud-agents/lib/transcript.ts +0 -19
  287. package/src/extensions/cursor-cloud-agents/skills/handoff.md +0 -37
  288. package/src/extensions/cursor-cloud-agents/tools/cancel.ts +0 -21
  289. package/src/extensions/cursor-cloud-agents/tools/dump.ts +0 -47
  290. package/src/extensions/cursor-cloud-agents/tools/get.ts +0 -18
  291. package/src/extensions/cursor-cloud-agents/tools/launch.ts +0 -62
  292. package/src/extensions/cursor-cloud-agents/tools/list.ts +0 -31
  293. package/src/extensions/cursor-cloud-agents/tools/reply.ts +0 -36
  294. package/src/extensions/cursor-grokbot-agents/extension.ts +0 -132
  295. package/src/extensions/cursor-grokbot-agents/lib/agents.ts +0 -42
  296. package/src/extensions/cursor-grokbot-agents/lib/backend.ts +0 -208
  297. package/src/extensions/cursor-grokbot-agents/lib/contacts.ts +0 -74
  298. package/src/extensions/cursor-grokbot-agents/lib/conversation.ts +0 -260
  299. package/src/extensions/cursor-grokbot-agents/lib/inputs.ts +0 -21
  300. package/src/extensions/cursor-grokbot-agents/lib/lock.ts +0 -26
  301. package/src/extensions/cursor-grokbot-agents/skills/consult.md +0 -40
  302. package/src/extensions/cursor-grokbot-agents/tools/ask.ts +0 -32
  303. package/src/extensions/cursor-grokbot-agents/tools/check.ts +0 -25
  304. package/src/extensions/cursor-grokbot-agents/tools/interrupt.ts +0 -17
  305. package/src/extensions/cursor-grokbot-agents/tools/list.ts +0 -26
  306. package/src/extensions/improve/extension.ts +0 -69
  307. package/src/extensions/improve/skills/yourself.ts +0 -50
  308. package/src/extensions/jev/extension.ts +0 -95
  309. package/src/extensions/jev/lib/evaluate.ts +0 -266
  310. package/src/extensions/jev/skills/gated-write.md +0 -25
  311. package/src/extensions/jev/skills/questions.md +0 -33
  312. package/src/extensions/jev/tools/evaluate.ts +0 -82
  313. package/src/extensions/slack-communication/evals/harness.ts +0 -705
  314. package/src/extensions/slack-communication/evals/slack-communication.eval.ts +0 -830
  315. package/src/extensions/slack-communication/evals/slack-placeholder.eval.ts +0 -172
  316. package/src/extensions/slack-communication/extension.ts +0 -44
  317. package/src/extensions/slack-communication/skills/slack-voice.ts +0 -56
  318. package/src/extensions/slack-communication/tools/delete-message.ts +0 -33
  319. package/src/extensions/slack-communication/tools/edit-message.ts +0 -42
  320. package/src/extensions/slack-communication/tools/post-card.ts +0 -28
  321. package/src/extensions/slack-communication/tools/post-file.ts +0 -82
  322. package/src/extensions/slack-communication/tools/post-update.ts +0 -40
  323. package/src/extensions/slack-communication/tools/react.ts +0 -30
  324. package/src/extensions/slack-communication/tools/set-status.ts +0 -21
  325. package/src/extensions.ts +0 -179
  326. package/src/files-backends/agent-store-presigned-url.ts +0 -11
  327. package/src/files-backends/cursor-hosted.ts +0 -1212
  328. package/src/files-backends/local-fs.ts +0 -163
  329. package/src/files.ts +0 -293
  330. package/src/filesystem.ts +0 -2005
  331. package/src/hooks.ts +0 -30
  332. package/src/index.ts +0 -259
  333. package/src/internal/advertise-tools.ts +0 -370
  334. package/src/internal/approval-gate.ts +0 -103
  335. package/src/internal/artifacts-store.ts +0 -836
  336. package/src/internal/as-of.ts +0 -257
  337. package/src/internal/auth.ts +0 -46
  338. package/src/internal/authored-alias-hooks.ts +0 -138
  339. package/src/internal/authored-loaders.ts +0 -188
  340. package/src/internal/bounded-int.ts +0 -24
  341. package/src/internal/bounded.ts +0 -25
  342. package/src/internal/brand.ts +0 -28
  343. package/src/internal/browser-origin-guard.ts +0 -67
  344. package/src/internal/builtin-tools/artifacts.ts +0 -62
  345. package/src/internal/builtin-tools/index.ts +0 -53
  346. package/src/internal/builtin-tools/reminders.ts +0 -145
  347. package/src/internal/channel-dispatch.ts +0 -129
  348. package/src/internal/channel-route-dispatch.ts +0 -58
  349. package/src/internal/channel-state.ts +0 -83
  350. package/src/internal/chat-client.ts +0 -594
  351. package/src/internal/cli-ax.ts +0 -2318
  352. package/src/internal/cli-bitbucket.ts +0 -438
  353. package/src/internal/cli-convert-automation.ts +0 -335
  354. package/src/internal/cli-cursor.ts +0 -282
  355. package/src/internal/cli-deploy.ts +0 -2519
  356. package/src/internal/cli-docs.ts +0 -192
  357. package/src/internal/cli-github.ts +0 -806
  358. package/src/internal/cli-gitlab.ts +0 -452
  359. package/src/internal/cli-images.ts +0 -672
  360. package/src/internal/cli-manifest.ts +0 -56
  361. package/src/internal/cli-mcp-oauth.ts +0 -396
  362. package/src/internal/cli-mcp.ts +0 -808
  363. package/src/internal/cli-skills.ts +0 -98
  364. package/src/internal/cli-slack-manual.ts +0 -418
  365. package/src/internal/cli-slack.ts +0 -874
  366. package/src/internal/cloud-merge.ts +0 -51
  367. package/src/internal/cloud-turn-cost.ts +0 -159
  368. package/src/internal/connection-env.ts +0 -106
  369. package/src/internal/continuation-channel.ts +0 -233
  370. package/src/internal/continuation-identity.ts +0 -935
  371. package/src/internal/conversation-mirror.ts +0 -423
  372. package/src/internal/convert-automation/convert-workflow.ts +0 -1377
  373. package/src/internal/convert-automation/slug.ts +0 -29
  374. package/src/internal/convert-automation/types.ts +0 -212
  375. package/src/internal/cron.ts +0 -76
  376. package/src/internal/cursor/account-mcp.ts +0 -692
  377. package/src/internal/cursor/backend-client.ts +0 -984
  378. package/src/internal/cursor/bound-secret-file.ts +0 -101
  379. package/src/internal/cursor/credentials.ts +0 -560
  380. package/src/internal/cursor/cursor-api-transport.ts +0 -65
  381. package/src/internal/cursor/github-credentials.ts +0 -228
  382. package/src/internal/cursor/hosted-bound-secrets.ts +0 -71
  383. package/src/internal/cursor/hosted-deployment-secrets.ts +0 -100
  384. package/src/internal/cursor/hosted-store-secrets.ts +0 -152
  385. package/src/internal/cursor/session-token.ts +0 -111
  386. package/src/internal/cursor/store-api-client.ts +0 -323
  387. package/src/internal/cursor-account-mcp-auth.ts +0 -303
  388. package/src/internal/cursor-agent-template.ts +0 -50
  389. package/src/internal/cursor-deployment-relay.ts +0 -315
  390. package/src/internal/cursor-event-relay.ts +0 -675
  391. package/src/internal/cursor-relay-core.ts +0 -563
  392. package/src/internal/deferred-channel-session.ts +0 -61
  393. package/src/internal/deploy-client.ts +0 -1574
  394. package/src/internal/deploy-manifest.ts +0 -361
  395. package/src/internal/deploy-source.ts +0 -144
  396. package/src/internal/describe-error.ts +0 -32
  397. package/src/internal/dev-mode.ts +0 -20
  398. package/src/internal/discovery/agent-config.ts +0 -288
  399. package/src/internal/discovery/agent.ts +0 -381
  400. package/src/internal/discovery/channels.ts +0 -100
  401. package/src/internal/discovery/connections.ts +0 -516
  402. package/src/internal/discovery/context.ts +0 -38
  403. package/src/internal/discovery/cursor-plugin.ts +0 -628
  404. package/src/internal/discovery/extension-overlay.ts +0 -247
  405. package/src/internal/discovery/extensions.ts +0 -490
  406. package/src/internal/discovery/fs.ts +0 -103
  407. package/src/internal/discovery/hooks.ts +0 -63
  408. package/src/internal/discovery/info.ts +0 -174
  409. package/src/internal/discovery/instructions.ts +0 -98
  410. package/src/internal/discovery/modules.ts +0 -107
  411. package/src/internal/discovery/names.ts +0 -45
  412. package/src/internal/discovery/project.ts +0 -319
  413. package/src/internal/discovery/reminders.ts +0 -65
  414. package/src/internal/discovery/runtime.ts +0 -158
  415. package/src/internal/discovery/schedules.ts +0 -120
  416. package/src/internal/discovery/seed-files.ts +0 -34
  417. package/src/internal/discovery/services.ts +0 -151
  418. package/src/internal/discovery/skills.ts +0 -174
  419. package/src/internal/discovery/subagent.ts +0 -138
  420. package/src/internal/discovery/tools.ts +0 -170
  421. package/src/internal/discovery.ts +0 -27
  422. package/src/internal/distribution.ts +0 -126
  423. package/src/internal/docs-site.ts +0 -248
  424. package/src/internal/duration.ts +0 -37
  425. package/src/internal/env-file.ts +0 -83
  426. package/src/internal/eval-judge-model.ts +0 -142
  427. package/src/internal/eval-run-store.ts +0 -585
  428. package/src/internal/eval-runner.ts +0 -823
  429. package/src/internal/evals-client.ts +0 -475
  430. package/src/internal/event-log.ts +0 -342
  431. package/src/internal/event-mapper.ts +0 -811
  432. package/src/internal/expected-delivery-failure.ts +0 -34
  433. package/src/internal/factory-resource-channel.ts +0 -103
  434. package/src/internal/filesystem/errors.ts +0 -207
  435. package/src/internal/filesystem/format.ts +0 -253
  436. package/src/internal/filesystem/merge.ts +0 -192
  437. package/src/internal/filesystem/nearest.ts +0 -112
  438. package/src/internal/filesystem/scratch.ts +0 -239
  439. package/src/internal/filesystem/sdk-schemas.ts +0 -204
  440. package/src/internal/filesystem/tools.ts +0 -804
  441. package/src/internal/filesystem/walk.ts +0 -366
  442. package/src/internal/fixtures/units-server.ts +0 -52
  443. package/src/internal/framework-file-storage.ts +0 -357
  444. package/src/internal/framework-storage-selection.ts +0 -156
  445. package/src/internal/frontmatter.ts +0 -108
  446. package/src/internal/github-fanout.ts +0 -144
  447. package/src/internal/guest-network.ts +0 -391
  448. package/src/internal/handleAgentServeTrigger.ts +0 -286
  449. package/src/internal/host-files.ts +0 -341
  450. package/src/internal/host-kv.ts +0 -81
  451. package/src/internal/host-otel.ts +0 -231
  452. package/src/internal/host-platforms.ts +0 -170
  453. package/src/internal/hosted-admission-adapter.ts +0 -47
  454. package/src/internal/hosted-admission-context.ts +0 -37
  455. package/src/internal/hosted-catch-protocol.ts +0 -112
  456. package/src/internal/hosted-catch.ts +0 -179
  457. package/src/internal/hosted-delivery-protocol.ts +0 -824
  458. package/src/internal/hosted-delivery.ts +0 -1410
  459. package/src/internal/hosted-execution-diag.ts +0 -291
  460. package/src/internal/hosted-execution-flush.ts +0 -136
  461. package/src/internal/hosted-execution-latency.ts +0 -301
  462. package/src/internal/hosted-managed-automation.ts +0 -116
  463. package/src/internal/hosting.ts +0 -114
  464. package/src/internal/http-channel-define.ts +0 -25
  465. package/src/internal/http-channel.ts +0 -669
  466. package/src/internal/http-control-plane-session.ts +0 -110
  467. package/src/internal/ids.ts +0 -39
  468. package/src/internal/incoming-headers-to-fetch.ts +0 -45
  469. package/src/internal/init-interview.ts +0 -360
  470. package/src/internal/init-project.ts +0 -711
  471. package/src/internal/init-scaffold.ts +0 -182
  472. package/src/internal/inline-stdio-define-stub.ts +0 -4
  473. package/src/internal/inline-stdio-mcp.ts +0 -230
  474. package/src/internal/install-cursor-skills.ts +0 -604
  475. package/src/internal/is-record.ts +0 -3
  476. package/src/internal/json-schema.ts +0 -95
  477. package/src/internal/legacy-command-guard.ts +0 -66
  478. package/src/internal/listen-port.ts +0 -85
  479. package/src/internal/local-control-plane.ts +0 -442
  480. package/src/internal/local-env.ts +0 -59
  481. package/src/internal/local-ingress.ts +0 -90
  482. package/src/internal/log-ring.ts +0 -59
  483. package/src/internal/logs-client.ts +0 -477
  484. package/src/internal/map-pool.ts +0 -39
  485. package/src/internal/mcp-endpoint.ts +0 -747
  486. package/src/internal/mcp-host.ts +0 -509
  487. package/src/internal/mcp-oauth-on-demand.ts +0 -337
  488. package/src/internal/mcp-oauth-persist.ts +0 -118
  489. package/src/internal/mcp-oauth.ts +0 -549
  490. package/src/internal/net.ts +0 -29
  491. package/src/internal/open-browser.ts +0 -41
  492. package/src/internal/otel/attrs.ts +0 -16
  493. package/src/internal/otel/collector.ts +0 -937
  494. package/src/internal/otel/config.ts +0 -287
  495. package/src/internal/otel/http.ts +0 -122
  496. package/src/internal/otel/mcp.ts +0 -56
  497. package/src/internal/otel/propagation.ts +0 -17
  498. package/src/internal/otel/runtime.ts +0 -441
  499. package/src/internal/package-inline-stdio.ts +0 -114
  500. package/src/internal/peer-connections.ts +0 -96
  501. package/src/internal/platform-schedule-sync.ts +0 -75
  502. package/src/internal/platform-timers.ts +0 -365
  503. package/src/internal/playground/static.ts +0 -278
  504. package/src/internal/playground/toolchain.ts +0 -270
  505. package/src/internal/playground-proxy.ts +0 -564
  506. package/src/internal/process-memory-telemetry.ts +0 -334
  507. package/src/internal/project-layout.ts +0 -31
  508. package/src/internal/prompt-context.ts +0 -116
  509. package/src/internal/reminder-control-plane-protocol.ts +0 -482
  510. package/src/internal/reminder-runner.ts +0 -1559
  511. package/src/internal/reminder-schedule.ts +0 -104
  512. package/src/internal/reminder-store.ts +0 -176
  513. package/src/internal/request-error-status.ts +0 -95
  514. package/src/internal/request-headers.ts +0 -21
  515. package/src/internal/resolve-prod-target.ts +0 -334
  516. package/src/internal/resolved-connections.ts +0 -190
  517. package/src/internal/router.ts +0 -143
  518. package/src/internal/run-client.ts +0 -354
  519. package/src/internal/runtime-dispatch-runner.ts +0 -50
  520. package/src/internal/sanitize-mcp-tool-name.ts +0 -31
  521. package/src/internal/schedule-runner.ts +0 -154
  522. package/src/internal/scm/pr-url.ts +0 -245
  523. package/src/internal/sdk-runner.ts +0 -959
  524. package/src/internal/serve-restart.ts +0 -113
  525. package/src/internal/server.ts +0 -3147
  526. package/src/internal/session-cost.ts +0 -208
  527. package/src/internal/session-engine.ts +0 -5622
  528. package/src/internal/session-links.ts +0 -84
  529. package/src/internal/session-run-log.ts +0 -535
  530. package/src/internal/session-store.ts +0 -307
  531. package/src/internal/session-urls.ts +0 -32
  532. package/src/internal/sessions-client.ts +0 -239
  533. package/src/internal/skill-catalog.ts +0 -66
  534. package/src/internal/skills-sync.ts +0 -55
  535. package/src/internal/slack-provision-client.ts +0 -544
  536. package/src/internal/slack-thread-key.ts +0 -11
  537. package/src/internal/sleep.ts +0 -3
  538. package/src/internal/start-log-store.ts +0 -164
  539. package/src/internal/steering.ts +0 -17
  540. package/src/internal/storage-coordinator.ts +0 -800
  541. package/src/internal/storage-roles.ts +0 -86
  542. package/src/internal/storage.ts +0 -1
  543. package/src/internal/store-api-protocol.ts +0 -205
  544. package/src/internal/store-mirror.ts +0 -73
  545. package/src/internal/stream-progress.ts +0 -294
  546. package/src/internal/terminal-style.ts +0 -133
  547. package/src/internal/testing/factory-resource-fixtures.ts +0 -84
  548. package/src/internal/testing/scm-continuation-fixtures.ts +0 -285
  549. package/src/internal/tool-catalog.ts +0 -104
  550. package/src/internal/tool-policy.ts +0 -222
  551. package/src/internal/tool-result.ts +0 -50
  552. package/src/internal/trajectory.ts +0 -418
  553. package/src/internal/turn-cost.ts +0 -136
  554. package/src/internal/turn-governor.ts +0 -145
  555. package/src/internal/turn-lease-store.ts +0 -144
  556. package/src/internal/turn-outbox.ts +0 -77
  557. package/src/internal/update-check.ts +0 -517
  558. package/src/internal/v2-hosting.ts +0 -52
  559. package/src/internal/workspace-sync.ts +0 -114
  560. package/src/internal/workspace.ts +0 -505
  561. package/src/kv/memory-kv.ts +0 -94
  562. package/src/kv.ts +0 -18
  563. package/src/memory.ts +0 -487
  564. package/src/otel-public.ts +0 -24
  565. package/src/otel.ts +0 -320
  566. package/src/prompt.ts +0 -80
  567. package/src/reminders.ts +0 -65
  568. package/src/schedules.ts +0 -31
  569. package/src/skills.ts +0 -27
  570. package/src/storage-backends/cursor-hosted-v2.ts +0 -204
  571. package/src/storage-backends/cursor-hosted.ts +0 -388
  572. package/src/storage-backends/file-kv.ts +0 -276
  573. package/src/storage-protocol.ts +0 -261
  574. package/src/storage.ts +0 -408
  575. package/src/tools.ts +0 -166
  576. package/src/types.ts +0 -3776
  577. /package/dist/docs/assets/{reference_agent-config.md.DzGI777n.lean.js → reference_agent-config.md.Cj5uBKp4.lean.js} +0 -0
@@ -1,2519 +0,0 @@
1
- /**
2
- * `agent-sdk deploy | deployments | deployment | stop | rollback | delete | rotate-token |
3
- * secrets set|list|unset` — Heroku-style deploys
4
- * onto Cursor's managed agent-serve hosting.
5
- *
6
- * All intelligence is server-side (backend/server/src/agentServe/); these
7
- * handlers resolve parameters (credentials, team, slug, the project's
8
- * `hosting` / `architecture` manifest block), call the v1 agent-serve or
9
- * v2 agentsdk management API through `deploy-client.ts`, poll status, and
10
- * render.
11
- */
12
-
13
- import { basename, resolve } from "node:path";
14
- import { catchAuthDeclaration, isPublicEndpointPolicy } from "../channels.js";
15
- import {
16
- type AgentArchitecture,
17
- type AgentHostingOptions,
18
- isCursorAccountTransport,
19
- } from "../types.js";
20
- import { CursorBackendClient } from "./cursor/backend-client.js";
21
- import { cursorExternalApiUrl, resolveApiKey, SIGN_IN_HINT } from "./cursor/credentials.js";
22
- import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
23
- import { readDeployTemplate } from "./cursor-agent-template.js";
24
- import {
25
- type AgentkitCancelRunsResult,
26
- type AgentkitDeployAccepted,
27
- type AgentkitDeployComponent,
28
- type AgentkitDeploymentListItem,
29
- type AgentkitDeployStatus,
30
- type AgentkitStopAccepted,
31
- AgentServeDeployClient,
32
- type DeleteAccepted,
33
- type DeployAccepted,
34
- DeployApiError,
35
- type Deployment,
36
- type DeploymentDetail,
37
- type DeploymentProgress,
38
- type DeploySource,
39
- deploymentServiceAccountId,
40
- type SecretInfo,
41
- type StopAccepted,
42
- slugFromDirectoryName,
43
- TERMINAL_AGENTKIT_DEPLOY_STATUSES,
44
- TERMINAL_DEPLOY_STATUSES,
45
- validateDeploymentSlug,
46
- } from "./deploy-client.js";
47
- import { type GitRunner, inferDeployGitSource } from "./deploy-source.js";
48
- import { describeError } from "./describe-error.js";
49
- import {
50
- type ChildAgentDir,
51
- formatDiagnostics,
52
- hasErrorDiagnostics,
53
- isAgentProjectDir,
54
- listChildAgentDirs,
55
- loadAgentProject,
56
- } from "./discovery.js";
57
- import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
58
- import {
59
- isReservedSecretName,
60
- isValidCursorEventRepo,
61
- validateSettableSecretName,
62
- } from "./hosting.js";
63
- import { deploymentArchitecture, rejectV2ForLegacyCommand } from "./legacy-command-guard.js";
64
- import { deployProgressLabel, sanitizeCustomerError } from "./logs-client.js";
65
- import { sleep } from "./sleep.js";
66
- import { stderrPalette } from "./terminal-style.js";
67
-
68
- const DEFAULT_POLL_INTERVAL_MS = 3_000;
69
- const DEFAULT_DEPLOY_TIMEOUT_MS = 10 * 60 * 1000;
70
- /** `--multi-tenant` HTTP deploy is Anysphere-only (team id 1). */
71
- const MULTI_TENANT_HTTP_DEPLOY_TEAM_ID = 1;
72
-
73
- /** One agent directory selected for deploy (single or multi-agent parent). */
74
- export type DeployTarget = ChildAgentDir;
75
-
76
- /**
77
- * Interactive / test seam: pick which agents under a multi-agent parent to
78
- * deploy. Return `null` to cancel.
79
- */
80
- export type ChooseDeployTargets = (
81
- agents: readonly DeployTarget[],
82
- ) => Promise<DeployTarget[] | null>;
83
-
84
- export interface DeployCliOptions {
85
- dir?: string;
86
- slug?: string;
87
- /**
88
- * deploy `--all`: when `--dir` is a multi-agent parent, deploy every
89
- * child agent (skips the interactive picker).
90
- */
91
- all?: boolean;
92
- /** `--team <id>`; resolved from the account when omitted. */
93
- team?: string;
94
- json?: boolean;
95
- noWait?: boolean;
96
- /** v2 stop: also request cancellation for every active session run. */
97
- cancelRuns?: boolean;
98
- apiKey?: string;
99
- /**
100
- * deploy `--repo <url>`: https git repo the control plane builds from.
101
- * Optional — inferred from `git remote origin` when omitted inside an
102
- * agent project checkout.
103
- */
104
- repo?: string;
105
- /**
106
- * deploy `--ref <ref>`: git ref to build. Optional — inferred from the
107
- * current branch (or HEAD SHA if detached) when omitted; otherwise the
108
- * server uses the repo's default branch.
109
- */
110
- ref?: string;
111
- /**
112
- * deploy `--path <agentPath>`: agent directory relative to the repo root.
113
- * Optional — inferred from `--dir` vs the git toplevel when omitted.
114
- */
115
- path?: string;
116
- /** deploy `--cursor-events-repo <owner/name>` (repeatable). */
117
- cursorEventsRepos?: string[];
118
- /** deploy `--allow-domain <domain>` (repeatable; unioned with the manifest). */
119
- allowDomains?: string[];
120
- /**
121
- * deploy `--multi-tenant`: add a release to an existing Cursor-managed
122
- * (multi-tenant) product. Hidden Anysphere-only flag. Cannot create a
123
- * new multi-tenant application.
124
- */
125
- multiTenant?: boolean;
126
- /** Test seams. */
127
- fetchImpl?: typeof fetch;
128
- /** Inject the connector-auth browser opener (tests; also forces interactive). */
129
- openBrowserImpl?: (url: string) => void;
130
- sleepImpl?: (ms: number) => Promise<void>;
131
- connectorPollIntervalMs?: number;
132
- connectorPollTimeoutMs?: number;
133
- /** Inject git probing for deploy source inference (tests). */
134
- git?: GitRunner;
135
- /** Inject the multi-agent picker (tests / non-interactive hosts). */
136
- chooseTargets?: ChooseDeployTargets;
137
- pollIntervalMs?: number;
138
- timeoutMs?: number;
139
- out?: (text: string) => void;
140
- err?: (text: string) => void;
141
- /** `secrets set` value input (hidden prompt on a TTY, lines otherwise). */
142
- stdin?: SecretInputStream;
143
- /**
144
- * Opt in to `NAME=VALUE` argv secrets. Default is names only (hidden
145
- * prompt / stdin) so values never land in shell history or agent-captured
146
- * terminals.
147
- */
148
- fromArgv?: boolean;
149
- }
150
-
151
- /** Minimal stdin surface `secrets set` reads values from (test-injectable). */
152
- export type SecretInputStream = NodeJS.ReadableStream & {
153
- isTTY?: boolean;
154
- setRawMode?: (mode: boolean) => void;
155
- };
156
-
157
- export interface DeployCliContext {
158
- client: AgentServeDeployClient;
159
- teamId: number;
160
- apiKey: string;
161
- backendUrl: string;
162
- json: boolean;
163
- noWait: boolean;
164
- pollIntervalMs: number;
165
- timeoutMs: number;
166
- out: (text: string) => void;
167
- err: (text: string) => void;
168
- }
169
-
170
- export interface ResolveDeployAuthOptions {
171
- team?: string;
172
- apiKey?: string;
173
- fetchImpl?: typeof fetch;
174
- /** Quiet team-resolution chatter (default: write to stderr). */
175
- err?: (text: string) => void;
176
- }
177
-
178
- export interface ResolvedDeployAuth {
179
- client: AgentServeDeployClient;
180
- teamId: number;
181
- /** The resolved account credential, for dashboard RPCs beside the deploy API. */
182
- apiKey: string;
183
- backendUrl: string;
184
- }
185
-
186
- /**
187
- * Credentials → deploy client → team id. Shared by deploy commands and
188
- * `--prod` AX routing. Returns an exit code (error already rendered) on
189
- * failure.
190
- */
191
- export async function resolveDeployAuth(
192
- options: ResolveDeployAuthOptions,
193
- ): Promise<ResolvedDeployAuth | number> {
194
- const err = options.err ?? ((text: string) => void process.stderr.write(text));
195
-
196
- const resolved = await resolveApiKey(
197
- options.apiKey === undefined ? undefined : { apiKey: options.apiKey },
198
- );
199
- if (resolved === undefined) {
200
- err(`Not signed in to Cursor. ${SIGN_IN_HINT}\n`);
201
- return 1;
202
- }
203
- // The deployment management routes live on the external API host
204
- // (api.cursor.com), not the default backend (api2.cursor.sh) — see
205
- // cursorExternalApiUrl. GetMe below stays on the resolved backend.
206
- const client = new AgentServeDeployClient({
207
- backendUrl: cursorExternalApiUrl(),
208
- apiKey: resolved.apiKey,
209
- fetchImpl: options.fetchImpl,
210
- });
211
-
212
- let teamId: number;
213
- if (options.team !== undefined) {
214
- teamId = Number.parseInt(options.team, 10);
215
- if (Number.isNaN(teamId) || teamId <= 0) {
216
- err(`Invalid --team: ${options.team} (expected a positive team id)\n`);
217
- return 1;
218
- }
219
- } else {
220
- const account = new CursorBackendClient({
221
- backendUrl: resolved.backendUrl,
222
- apiKey: resolved.apiKey,
223
- fetchImpl: options.fetchImpl,
224
- });
225
- let identity: Awaited<ReturnType<CursorBackendClient["getMe"]>>;
226
- try {
227
- identity = await account.getMe();
228
- } catch (error) {
229
- err(
230
- `Could not resolve your team from the Cursor account: ${describeError(error)}\nPass --team <id> explicitly.\n`,
231
- );
232
- return 1;
233
- }
234
- if (identity.teamId === undefined || identity.teamId <= 0) {
235
- err(
236
- "This Cursor account has no default team; pass --team <id> (find it in the Cursor dashboard URL).\n",
237
- );
238
- return 1;
239
- }
240
- teamId = identity.teamId;
241
- const dim = stderrPalette().dim;
242
- err(
243
- `${dim(
244
- `Using team ${teamId}${identity.teamName === undefined ? "" : ` (${identity.teamName})`}`,
245
- )}\n${dim(" the account's team — pass --team to override.")}\n`,
246
- );
247
- }
248
-
249
- return {
250
- client,
251
- teamId,
252
- apiKey: resolved.apiKey,
253
- backendUrl: resolved.backendUrl,
254
- };
255
- }
256
-
257
- /**
258
- * Shared preamble: credentials → client → team id. Returns an exit code
259
- * (with the error already rendered) when resolution fails.
260
- */
261
- export async function prepare(options: DeployCliOptions): Promise<DeployCliContext | number> {
262
- const out = options.out ?? ((text: string) => void process.stdout.write(text));
263
- const err = options.err ?? ((text: string) => void process.stderr.write(text));
264
-
265
- const auth = await resolveDeployAuth({
266
- team: options.team,
267
- apiKey: options.apiKey,
268
- fetchImpl: options.fetchImpl,
269
- err,
270
- });
271
- if (typeof auth === "number") {
272
- return auth;
273
- }
274
-
275
- return {
276
- client: auth.client,
277
- teamId: auth.teamId,
278
- apiKey: auth.apiKey,
279
- backendUrl: auth.backendUrl,
280
- json: options.json === true,
281
- noWait: options.noWait === true,
282
- pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
283
- timeoutMs: options.timeoutMs ?? DEFAULT_DEPLOY_TIMEOUT_MS,
284
- out,
285
- err,
286
- };
287
- }
288
-
289
- // ============================================================================
290
- // deploy
291
- // ============================================================================
292
-
293
- export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
294
- const ctx = await prepare(options);
295
- if (typeof ctx === "number") {
296
- return ctx;
297
- }
298
-
299
- if (options.multiTenant === true && ctx.teamId !== MULTI_TENANT_HTTP_DEPLOY_TEAM_ID) {
300
- ctx.err("--multi-tenant is not available for this team.\n");
301
- return 1;
302
- }
303
-
304
- const targets = await resolveDeployTargets(options, ctx);
305
- if (typeof targets === "number") {
306
- return targets;
307
- }
308
-
309
- if (targets.length === 1) {
310
- return deployOne(ctx, options, targets[0]!);
311
- }
312
-
313
- // Multi-agent parent: when every child declares `architecture: "v2"`,
314
- // the v2 control plane models sibling agents as release components of
315
- // ONE application, so deploy them together (one app, one component per
316
- // child) instead of minting a separate application per child. Mixed and
317
- // v1 parents keep the per-child sequential deploys.
318
- const manifests: DeployManifest[] = [];
319
- for (const target of targets) {
320
- manifests.push(await readDeployManifest(target.dir));
321
- }
322
- if (manifests.every(manifest => manifest.architecture === "v2")) {
323
- return deployAgentsdkApplication(ctx, options, targets, {
324
- // `persistent` is per child agent, like `agentPath`: each release
325
- // component carries its own.
326
- componentPersistent: Object.fromEntries(
327
- targets.map((target, index) => [target.slug, manifests[index]?.persistent === true]),
328
- ),
329
- componentImage: Object.fromEntries(
330
- targets.flatMap((target, index) =>
331
- manifests[index]?.image === undefined ? [] : [[target.slug, manifests[index].image]],
332
- ),
333
- ),
334
- hasScmChannels: manifests.some(manifest => manifest.hasScmChannels),
335
- declaredSecretNames: [
336
- ...new Set(manifests.flatMap(manifest => manifest.hosting?.secretNames ?? [])),
337
- ],
338
- hasDeploymentsChannels: manifests.some(manifest => manifest.hasDeploymentsChannels),
339
- });
340
- }
341
- if (!ctx.json && manifests.some(manifest => manifest.architecture === "v2")) {
342
- ctx.err(
343
- `warning: agents under this parent mix architectures; deploying each separately (v2 children become standalone single-component applications).\n`,
344
- );
345
- }
346
-
347
- // --path is per-agent (relative to the repo root). When deploying several
348
- // agents, ignore a shared --path so each target can infer its own path.
349
- let deployOptions = options;
350
- if (options.path !== undefined) {
351
- if (!ctx.json) {
352
- ctx.err(
353
- `warning: ignoring --path ${JSON.stringify(options.path)} for a multi-agent deploy; each agent infers its own path from git.\n`,
354
- );
355
- }
356
- deployOptions = { ...options, path: undefined };
357
- }
358
-
359
- // Multi-agent batch: deploy sequentially; --json emits an array.
360
- const jsonResults: unknown[] = [];
361
- let failures = 0;
362
- for (const [index, target] of targets.entries()) {
363
- if (!ctx.json) {
364
- ctx.err(`\n=== [${index + 1}/${targets.length}] Deploying ${target.slug} ===\n`);
365
- }
366
- const before = jsonResults.length;
367
- const code = await deployOne(ctx, deployOptions, target, {
368
- jsonCollect: ctx.json ? jsonResults : undefined,
369
- });
370
- if (code !== 0) {
371
- failures += 1;
372
- if (ctx.json && jsonResults.length === before) {
373
- jsonResults.push({ slug: target.slug, error: "deploy failed" });
374
- }
375
- }
376
- }
377
- if (ctx.json) {
378
- ctx.out(`${JSON.stringify(jsonResults, null, 2)}\n`);
379
- } else if (failures > 0) {
380
- ctx.err(`\n${failures} of ${targets.length} deploy(s) failed.\n`);
381
- } else {
382
- ctx.err(`\nDeployed ${targets.length} agents.\n`);
383
- }
384
- return failures === 0 ? 0 : 1;
385
- }
386
-
387
- /**
388
- * Resolve which agent directory(ies) to deploy from `--dir` / `--slug` /
389
- * `--all`, with an interactive picker when a multi-agent parent is on a TTY.
390
- */
391
- async function resolveDeployTargets(
392
- options: DeployCliOptions,
393
- ctx: DeployCliContext,
394
- ): Promise<DeployTarget[] | number> {
395
- const dir = resolve(options.dir ?? ".");
396
-
397
- if (options.slug !== undefined) {
398
- const slugError = validateDeploymentSlug(options.slug);
399
- if (slugError !== undefined) {
400
- ctx.err(`${slugError}\n`);
401
- return 1;
402
- }
403
- }
404
- if (options.all === true && options.slug !== undefined) {
405
- ctx.err("Pass either --slug or --all, not both.\n");
406
- return 1;
407
- }
408
-
409
- if (await isAgentProjectDir(dir)) {
410
- if (options.all === true) {
411
- ctx.err(
412
- `--all requires a multi-agent parent directory (several child agents under --dir); ${dir} is a single agent project.\n`,
413
- );
414
- return 1;
415
- }
416
- let slug: string;
417
- if (options.slug !== undefined) {
418
- slug = options.slug;
419
- } else {
420
- const derived = slugFromDirectoryName(basename(dir));
421
- if (derived === undefined) {
422
- ctx.err(
423
- `Could not derive a deployment slug from the directory name ${JSON.stringify(basename(dir))}; pass --slug.\n`,
424
- );
425
- return 1;
426
- }
427
- slug = derived;
428
- if (!ctx.json) {
429
- ctx.err(`Deploying as "${slug}" (from the directory name).\n`);
430
- }
431
- }
432
- return [{ slug, dir }];
433
- }
434
-
435
- let children: ChildAgentDir[];
436
- try {
437
- children = await listChildAgentDirs(dir);
438
- } catch (error) {
439
- ctx.err(`${describeError(error)}\n`);
440
- return 1;
441
- }
442
- // Deploy slugs are lowercase-only; normalize directory names the same way
443
- // single-agent deploy does so MixedCase child dirs still deploy.
444
- children = children.flatMap(child => {
445
- const slug = slugFromDirectoryName(basename(child.dir));
446
- if (slug === undefined) {
447
- return [];
448
- }
449
- return [{ slug, dir: child.dir }];
450
- });
451
-
452
- if (children.length === 0) {
453
- if (options.all === true) {
454
- ctx.err(`--all requires child agent projects under --dir; none found in ${dir}.\n`);
455
- return 1;
456
- }
457
- // Bare-slug deploy against a non-agent directory (still requires --repo).
458
- let slug: string;
459
- if (options.slug !== undefined) {
460
- slug = options.slug;
461
- } else {
462
- const derived = slugFromDirectoryName(basename(dir));
463
- if (derived === undefined) {
464
- ctx.err(
465
- `No agent projects under ${dir}, and could not derive a slug from ${JSON.stringify(basename(dir))}; pass --slug or --dir <agent>.\n`,
466
- );
467
- return 1;
468
- }
469
- slug = derived;
470
- if (!ctx.json) {
471
- ctx.err(`Deploying as "${slug}" (no agent project at ${dir}).\n`);
472
- }
473
- }
474
- return [{ slug, dir }];
475
- }
476
-
477
- if (children.length === 1) {
478
- const only = children[0]!;
479
- if (options.all === true) {
480
- // One child is fine for --all (degenerate multi-agent parent).
481
- } else if (options.slug !== undefined && options.slug.toLowerCase() !== only.slug) {
482
- ctx.err(`No agent "${options.slug}" under ${dir}; found "${only.slug}".\n`);
483
- return 1;
484
- }
485
- if (!ctx.json) {
486
- ctx.err(`Deploying "${only.slug}" (only agent under ${basename(dir)}).\n`);
487
- }
488
- return [only];
489
- }
490
-
491
- // Multiple children.
492
- if (options.all === true) {
493
- if (!ctx.json) {
494
- ctx.err(
495
- `Deploying all ${children.length} agents under ${basename(dir)}: ${children.map(c => c.slug).join(", ")}.\n`,
496
- );
497
- }
498
- return [...children];
499
- }
500
-
501
- if (options.slug !== undefined) {
502
- const wanted = options.slug.toLowerCase();
503
- const match = children.find(child => child.slug === wanted);
504
- if (match === undefined) {
505
- ctx.err(
506
- `No agent "${options.slug}" under ${dir}.\n` +
507
- formatAgentList(children) +
508
- `Pass --slug <name> or --all.\n`,
509
- );
510
- return 1;
511
- }
512
- return [match];
513
- }
514
-
515
- // An all-v2 parent deploys as ONE application (siblings are components
516
- // of a single app on the v2 control plane), so "deploy the parent" is
517
- // unambiguous and needs no picker or --all. A child that is v1 — or
518
- // fails to load — keeps the interactive picker below.
519
- if (await allChildrenAreV2(children)) {
520
- if (!ctx.json) {
521
- ctx.err(
522
- `Deploying all ${children.length} v2 agents under ${basename(dir)} as one application (pass --slug <name> to deploy a single agent standalone).\n`,
523
- );
524
- }
525
- return [...children];
526
- }
527
-
528
- const chooser =
529
- options.chooseTargets ??
530
- (agents =>
531
- promptChooseDeployTargets(agents, {
532
- err: ctx.err,
533
- stdin: options.stdin ?? process.stdin,
534
- interactive:
535
- ctx.json !== true &&
536
- (options.stdin ?? process.stdin).isTTY === true &&
537
- process.stderr.isTTY === true,
538
- }));
539
- const chosen = await chooser(children);
540
- if (chosen === null) {
541
- ctx.err("Deploy cancelled.\n");
542
- return 1;
543
- }
544
- if (chosen.length === 0) {
545
- ctx.err(
546
- `Multiple agents under ${dir}:\n` +
547
- formatAgentList(children) +
548
- `Pass --slug <name> or --all (or re-run on a TTY to pick interactively).\n`,
549
- );
550
- return 1;
551
- }
552
- return chosen;
553
- }
554
-
555
- function formatAgentList(agents: readonly DeployTarget[]): string {
556
- return agents.map((agent, i) => ` ${i + 1}) ${agent.slug}\n`).join("");
557
- }
558
-
559
- /**
560
- * True when every child agent project loads and declares
561
- * `architecture: "v2"`. Broken projects count as not-v2 so target
562
- * resolution falls back to the picker instead of failing the whole deploy
563
- * on a child the user might not even select.
564
- */
565
- async function allChildrenAreV2(children: readonly DeployTarget[]): Promise<boolean> {
566
- for (const child of children) {
567
- try {
568
- if ((await readDeployManifest(child.dir)).architecture !== "v2") {
569
- return false;
570
- }
571
- } catch {
572
- return false;
573
- }
574
- }
575
- return true;
576
- }
577
-
578
- /**
579
- * TTY picker for multi-agent parents. Non-interactive callers get `[]`
580
- * so {@link resolveDeployTargets} can print the --slug/--all hint.
581
- */
582
- export async function promptChooseDeployTargets(
583
- agents: readonly DeployTarget[],
584
- options: {
585
- err: (text: string) => void;
586
- stdin: SecretInputStream;
587
- interactive: boolean;
588
- },
589
- ): Promise<DeployTarget[] | null> {
590
- options.err(`Multiple agents (${agents.length}):\n` + formatAgentList(agents) + ` a) all\n`);
591
- if (!options.interactive) {
592
- return [];
593
- }
594
- options.err('Deploy which? Enter a number, slug, "all", or "q" to cancel: ');
595
- const readLine = createLineReader(options.stdin);
596
- // createLineReader pauses until data; ensure stdin is flowing on a TTY.
597
- options.stdin.resume?.();
598
- const answer = (await readLine())?.trim().toLowerCase();
599
- if (answer === undefined || answer === "q" || answer === "quit") {
600
- return null;
601
- }
602
- if (answer === "a" || answer === "all" || answer === "*") {
603
- return [...agents];
604
- }
605
- const asIndex = Number.parseInt(answer, 10);
606
- if (
607
- !Number.isNaN(asIndex) &&
608
- String(asIndex) === answer &&
609
- asIndex >= 1 &&
610
- asIndex <= agents.length
611
- ) {
612
- return [agents[asIndex - 1]!];
613
- }
614
- const bySlug = agents.find(agent => agent.slug.toLowerCase() === answer);
615
- if (bySlug !== undefined) {
616
- return [bySlug];
617
- }
618
- options.err(
619
- `Unrecognized choice ${JSON.stringify(answer)} — expected 1–${agents.length}, a slug, "all", or "q". Aborting.\n`,
620
- );
621
- return null;
622
- }
623
-
624
- async function deployOne(
625
- ctx: DeployCliContext,
626
- options: DeployCliOptions,
627
- target: DeployTarget,
628
- extras?: { jsonCollect?: unknown[] },
629
- ): Promise<number> {
630
- const { dir, slug } = target;
631
-
632
- // Fill --repo/--ref/--path from the local git checkout when omitted so
633
- // `agent-sdk deploy` inside an agent directory is enough. Only probe git
634
- // for real agent projects (avoids ambient monorepo remotes when someone
635
- // deploys a bare --slug). Explicit flags always win.
636
- const isAgent = await isAgentProjectDir(dir);
637
- const needsGitInference =
638
- isAgent &&
639
- (options.repo === undefined || options.ref === undefined || options.path === undefined);
640
- const inferred = needsGitInference
641
- ? await inferDeployGitSource(dir, {
642
- ...(options.git === undefined ? {} : { git: options.git }),
643
- })
644
- : {};
645
- const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
646
- const gitRef = options.ref ?? inferred.gitRef;
647
- const agentPath = options.path ?? inferred.agentPath;
648
-
649
- if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
650
- ctx.err(`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`);
651
- return 1;
652
- }
653
- if (gitRepoUrl === undefined) {
654
- ctx.err(
655
- `Could not resolve an https:// git repository URL for this deploy.\n` +
656
- ` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`,
657
- );
658
- return 1;
659
- }
660
- for (const repo of options.cursorEventsRepos ?? []) {
661
- if (!isValidCursorEventRepo(repo)) {
662
- ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
663
- return 1;
664
- }
665
- }
666
-
667
- if (!ctx.json && gitRepoUrl !== undefined) {
668
- const inferredBits: string[] = [];
669
- if (options.repo === undefined && inferred.gitRepoUrl !== undefined) {
670
- inferredBits.push("repo");
671
- }
672
- if (options.ref === undefined && inferred.gitRef !== undefined) {
673
- inferredBits.push("ref");
674
- }
675
- if (options.path === undefined && inferred.agentPath !== undefined) {
676
- inferredBits.push("path");
677
- }
678
- ctx.err(
679
- ` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
680
- (agentPath === undefined ? "" : ` path: ${agentPath}\n`) +
681
- (inferredBits.length === 0
682
- ? ""
683
- : ` (inferred ${inferredBits.join(", ")} from git; pass --repo/--ref/--path to override)\n`),
684
- );
685
- }
686
-
687
- // The package manifest (`defineAgent({ hosting })`) is the source of
688
- // truth for egress domains and expected secret names; flags override or
689
- // augment it. Directories without an agent project have no manifest. The same load supplies the
690
- // cursorAccount connection refs for the post-deploy connector auth and
691
- // the channels declared public via `publicEndpoint()`.
692
- const manifest = isAgent ? await readDeployManifest(dir) : undefined;
693
- const hosting = manifest?.hosting;
694
- const egressAllowedDomains = [
695
- ...new Set([...(hosting?.egressDomains ?? []), ...(options.allowDomains ?? [])]),
696
- ];
697
- const publicRoutePaths = (manifest?.publicChannelIds ?? []).map(
698
- channelId => `/v1/channels/${channelId}`,
699
- );
700
- if (!ctx.json && manifest?.httpChannelPublicEndpoint === true) {
701
- ctx.err(
702
- `warning: publicEndpoint() on the built-in HTTP channel has no effect on hosting — only custom channels (/v1/channels/…) can be public; the session API always requires the alias token.\n`,
703
- );
704
- }
705
- if (!ctx.json && publicRoutePaths.length > 0) {
706
- // Security-relevant surface change: say exactly what will be reachable
707
- // without the alias token before the deploy is accepted.
708
- ctx.err(
709
- ` public: ${publicRoutePaths.join(", ")} (served WITHOUT the alias token — these channels authenticate requests themselves)\n`,
710
- );
711
- }
712
- const deadSecretChannels = manifest?.publicChannelIdsWithDeadSecretPolicies ?? [];
713
- if (!ctx.json && deadSecretChannels.length > 0) {
714
- ctx.err(
715
- `warning: ${deadSecretChannels
716
- .map(channelId => `/v1/channels/${channelId}`)
717
- .join(
718
- ", ",
719
- )}: publicEndpoint() sits beside sharedSecretAuth()/hmacSignatureAuth() — auth chains are any-of, so every request is admitted and those secret policies never run. Drop publicEndpoint() to enforce them, or drop them to make the intent explicit.\n`,
720
- );
721
- }
722
-
723
- if (manifest?.architecture === "v2") {
724
- if (!ctx.json) {
725
- ctx.err(
726
- ` architecture: v2${manifest.persistent ? " (persistent)" : ""}${manifest.image === undefined ? "" : `\n image: ${manifest.image}`}\n`,
727
- );
728
- }
729
- return deployOneAgentsdk(ctx, extras, {
730
- persistent: manifest.persistent,
731
- image: manifest.image,
732
- slug,
733
- gitRepoUrl,
734
- gitRef,
735
- agentPath,
736
- repositories: options.cursorEventsRepos,
737
- hasScmChannels: manifest.hasScmChannels,
738
- declaredSecretNames: hosting?.secretNames,
739
- hasDeploymentsChannels: manifest.hasDeploymentsChannels,
740
- multiTenant: options.multiTenant === true,
741
- });
742
- }
743
-
744
- if (options.multiTenant === true) {
745
- ctx.err(
746
- "--multi-tenant is only valid for architecture: v2 deploys (Cursor-managed products).\n",
747
- );
748
- return 1;
749
- }
750
-
751
- // Today's only source shape: the control plane builds from a git ref.
752
- // See DeploySource for the artifact seam.
753
- const source: DeploySource = {
754
- kind: "git-ref",
755
- gitRepoUrl,
756
- ...(gitRef === undefined ? {} : { gitRef }),
757
- ...(agentPath === undefined ? {} : { agentPath }),
758
- ...(options.cursorEventsRepos === undefined || options.cursorEventsRepos.length === 0
759
- ? {}
760
- : { cursorEventRepos: options.cursorEventsRepos }),
761
- ...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
762
- publicRoutePaths: publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
763
- // Deliberately NO manifest here: the control plane captures it from the
764
- // provisioned engine (`GET /v1/manifest` after the health probe), which
765
- // is the single write path for deploy manifests. `july manifest` remains
766
- // available for local visibility only.
767
- };
768
-
769
- let accepted: DeployAccepted;
770
- try {
771
- accepted = await ctx.client.deploy({
772
- teamId: ctx.teamId,
773
- slug,
774
- source,
775
- template: readDeployTemplate(dir),
776
- });
777
- } catch (error) {
778
- return renderApiError(error, ctx.err);
779
- }
780
-
781
- const aliasUrl = ctx.client.aliasUrl(accepted.engineAlias);
782
- if (!ctx.json && accepted.aliasToken !== undefined) {
783
- ctx.out(aliasTokenBlock(accepted.aliasToken));
784
- }
785
-
786
- if (ctx.noWait) {
787
- if (extras?.jsonCollect !== undefined) {
788
- extras.jsonCollect.push(accepted);
789
- } else if (ctx.json) {
790
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
791
- } else {
792
- ctx.out(
793
- `Deploy accepted: ${slug} (team ${ctx.teamId}, generation ${accepted.generation}, ${accepted.deploymentKind})\n` +
794
- ` alias: ${aliasUrl}\n` +
795
- `Check progress with \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.\n`,
796
- );
797
- }
798
- return 0;
799
- }
800
-
801
- let detail: DeploymentDetail;
802
- try {
803
- detail = await pollUntil(ctx, slug, TERMINAL_DEPLOY_STATUSES, () =>
804
- ctx.client.getDeployment({ teamId: ctx.teamId, slug }),
805
- );
806
- } catch (error) {
807
- return renderApiError(error, ctx.err);
808
- }
809
-
810
- warnMissingSecrets(ctx, slug, hosting?.secretNames, detail.secretNames);
811
-
812
- const payload =
813
- accepted.aliasToken === undefined ? detail : { ...detail, aliasToken: accepted.aliasToken };
814
-
815
- if (extras?.jsonCollect !== undefined) {
816
- extras.jsonCollect.push(payload);
817
- return detail.status === "running" ? 0 : 1;
818
- }
819
-
820
- if (ctx.json) {
821
- ctx.out(`${JSON.stringify(payload, null, 2)}\n`);
822
- return detail.status === "running" ? 0 : 1;
823
- }
824
-
825
- if (detail.status !== "running") {
826
- ctx.err(`Deployment ${slug} ended ${detail.status}${deployFailureDetail(detail.lastError)}\n`);
827
- return 1;
828
- }
829
- const deployedPublicRoutes = detail.publicRoutePaths ?? [];
830
- ctx.out(
831
- `Deployed ${slug} (team ${ctx.teamId})\n` +
832
- ` status: running\n` +
833
- ` generation: ${detail.generation}\n` +
834
- ` kind: ${detail.deploymentKind}\n` +
835
- ` alias: ${aliasUrl}\n` +
836
- ` playground: ${aliasUrl}/playground (sign in with the alias token)\n` +
837
- ` open: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n` +
838
- // Paste-ready webhook URLs: these subtrees skip the alias token, so
839
- // the channel handler must verify deliveries (webhook signatures).
840
- deployedPublicRoutes
841
- .map(path => ` public: ${aliasUrl}${path} (no alias token)\n`)
842
- .join(""),
843
- );
844
-
845
- await runPostDeployConnectorAuth(ctx, options, {
846
- slug,
847
- connections: manifest?.cursorAccountConnections ?? [],
848
- serviceAccountId: deploymentServiceAccountId(detail),
849
- });
850
- return 0;
851
- }
852
-
853
- /**
854
- * Multi-agent parent whose children all declare `architecture: "v2"`:
855
- * deploy ONE application whose release carries a component per child. The
856
- * application slug derives from the parent directory name; each component
857
- * key is the child's slug and its path is the child directory relative to
858
- * the repo root (a shared --path names the parent's directory in the repo).
859
- */
860
- async function deployAgentsdkApplication(
861
- ctx: DeployCliContext,
862
- options: DeployCliOptions,
863
- targets: readonly DeployTarget[],
864
- declarations: {
865
- /** Per child slug: `persistent: true` on that agent's manifest. */
866
- componentPersistent: Record<string, boolean>;
867
- /** Per child slug: `hosting.image` on that agent's manifest. */
868
- componentImage: Record<string, string>;
869
- hasScmChannels: boolean;
870
- declaredSecretNames: string[];
871
- hasDeploymentsChannels: boolean;
872
- },
873
- ): Promise<number> {
874
- const parentDir = resolve(options.dir ?? ".");
875
- const slug = slugFromDirectoryName(basename(parentDir));
876
- if (slug === undefined) {
877
- ctx.err(
878
- `Could not derive an application slug from the parent directory name ${JSON.stringify(basename(parentDir))} — rename it (deploy slugs use lowercase letters, digits, '-' or '_').\n`,
879
- );
880
- return 1;
881
- }
882
-
883
- const needsGitInference =
884
- options.repo === undefined || options.ref === undefined || options.path === undefined;
885
- const inferred = needsGitInference
886
- ? await inferDeployGitSource(parentDir, {
887
- ...(options.git === undefined ? {} : { git: options.git }),
888
- })
889
- : {};
890
- const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
891
- const gitRef = options.ref ?? inferred.gitRef;
892
- const parentPath = options.path ?? inferred.agentPath;
893
-
894
- if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
895
- ctx.err(`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`);
896
- return 1;
897
- }
898
- if (gitRepoUrl === undefined) {
899
- ctx.err(
900
- `Could not resolve an https:// git repository URL for this deploy.\n` +
901
- ` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`,
902
- );
903
- return 1;
904
- }
905
- for (const repo of options.cursorEventsRepos ?? []) {
906
- if (!isValidCursorEventRepo(repo)) {
907
- ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
908
- return 1;
909
- }
910
- }
911
-
912
- const components: AgentkitDeployComponent[] = targets.map(target => ({
913
- componentKey: target.slug,
914
- agentPath: [parentPath, basename(target.dir)]
915
- .filter((part): part is string => part !== undefined && part !== "")
916
- .join("/"),
917
- persistent: declarations.componentPersistent[target.slug] === true,
918
- image: declarations.componentImage[target.slug],
919
- }));
920
-
921
- if (!ctx.json) {
922
- ctx.err(
923
- `Deploying "${slug}" as one v2 application with ${targets.length} components (slug from the parent directory name).\n` +
924
- ` architecture: v2\n` +
925
- ` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
926
- components
927
- .map(
928
- component =>
929
- ` component: ${component.componentKey} (${component.agentPath ?? "."})${component.persistent ? " persistent" : ""}${component.image === undefined ? "" : ` image ${component.image}`}\n`,
930
- )
931
- .join(""),
932
- );
933
- }
934
-
935
- return deployOneAgentsdk(ctx, undefined, {
936
- // Multi-component: the flag travels on each component instead.
937
- persistent: false,
938
- slug,
939
- gitRepoUrl,
940
- gitRef,
941
- agentPath: undefined,
942
- components,
943
- repositories: options.cursorEventsRepos,
944
- hasScmChannels: declarations.hasScmChannels,
945
- declaredSecretNames: declarations.declaredSecretNames,
946
- hasDeploymentsChannels: declarations.hasDeploymentsChannels,
947
- multiTenant: options.multiTenant === true,
948
- });
949
- }
950
-
951
- /**
952
- * `architecture: "v2"` deploy: POST `/internal/agentsdk/deployments` and
953
- * poll the matching GET. The v2 accept path starts freeze bake; it does
954
- * not mint an engine alias or alias token.
955
- */
956
- async function deployOneAgentsdk(
957
- ctx: DeployCliContext,
958
- extras: { jsonCollect?: unknown[] } | undefined,
959
- args: {
960
- slug: string;
961
- gitRepoUrl: string;
962
- gitRef: string | undefined;
963
- agentPath: string | undefined;
964
- components?: AgentkitDeployComponent[];
965
- repositories: string[] | undefined;
966
- /** Any component declares a GitHub/Origin channel. */
967
- hasScmChannels: boolean;
968
- /** Union of application component secret declarations. */
969
- declaredSecretNames?: string[];
970
- /** Any component declares a deployments channel (same repo scope). */
971
- hasDeploymentsChannels?: boolean;
972
- /** Redeploy an existing Cursor-managed product. */
973
- multiTenant: boolean;
974
- /**
975
- * `persistent: true` in a single agent's manifest. Multi-component
976
- * deploys pass `false` here and flag each component instead.
977
- */
978
- persistent: boolean;
979
- /** `hosting.image` of a single agent; components carry their own. */
980
- image?: string;
981
- },
982
- ): Promise<number> {
983
- const { slug } = args;
984
- const componentsLine =
985
- args.components === undefined
986
- ? ""
987
- : ` components: ${args.components.map(component => component.componentKey).join(", ")}\n`;
988
- if (args.multiTenant && !ctx.json) {
989
- ctx.err(" multi-tenant: existing product\n");
990
- }
991
- let accepted: AgentkitDeployAccepted;
992
- try {
993
- accepted = await ctx.client.deployAgentkit({
994
- teamId: ctx.teamId,
995
- slug,
996
- gitRepoUrl: args.gitRepoUrl,
997
- gitRef: args.gitRef,
998
- agentPath: args.agentPath,
999
- components: args.components,
1000
- repositories: args.repositories,
1001
- ...(args.multiTenant ? { multiTenant: true } : {}),
1002
- persistent: args.persistent,
1003
- image: args.image,
1004
- });
1005
- } catch (error) {
1006
- return renderApiError(error, ctx.err, {
1007
- multiTenant: args.multiTenant,
1008
- teamId: ctx.teamId,
1009
- });
1010
- }
1011
-
1012
- if (ctx.noWait) {
1013
- if (extras?.jsonCollect !== undefined) {
1014
- extras.jsonCollect.push(accepted);
1015
- } else if (ctx.json) {
1016
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1017
- } else {
1018
- ctx.out(
1019
- `Deploy accepted: ${slug} (team ${ctx.teamId}, v2)\n` +
1020
- ` application: ${accepted.applicationId}\n` +
1021
- ` release: ${accepted.releaseId}\n` +
1022
- componentsLine,
1023
- );
1024
- }
1025
- return 0;
1026
- }
1027
-
1028
- let detail: AgentkitDeployStatus;
1029
- try {
1030
- detail = await pollUntil(ctx, slug, TERMINAL_AGENTKIT_DEPLOY_STATUSES, () =>
1031
- ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }),
1032
- );
1033
- } catch (error) {
1034
- return renderApiError(error, ctx.err);
1035
- }
1036
-
1037
- if (extras?.jsonCollect !== undefined) {
1038
- extras.jsonCollect.push(detail);
1039
- return detail.status === "running" ? 0 : 1;
1040
- }
1041
-
1042
- if (ctx.json) {
1043
- ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
1044
- return detail.status === "running" ? 0 : 1;
1045
- }
1046
-
1047
- if (detail.status !== "running") {
1048
- ctx.err(`Deployment ${slug} ended ${detail.status}${deployFailureDetail(detail.lastError)}\n`);
1049
- return 1;
1050
- }
1051
- warnMissingSecrets(ctx, slug, args.declaredSecretNames, detail.secretNames);
1052
- const watchesRepositories = args.hasScmChannels || args.hasDeploymentsChannels === true;
1053
- ctx.out(
1054
- `Deployed ${slug} (team ${ctx.teamId})\n` +
1055
- ` status: running\n` +
1056
- ` architecture: v2\n` +
1057
- ` application: ${detail.applicationId}\n` +
1058
- ` release: ${detail.releaseId}\n` +
1059
- (args.components === undefined
1060
- ? ""
1061
- : ` components: ${args.components
1062
- .map(component => component.componentKey)
1063
- .join(", ")}\n`) +
1064
- (detail.repositories === undefined || !watchesRepositories
1065
- ? ""
1066
- : ` watching: ${detail.repositories.length === 0 ? "(nothing)" : detail.repositories.join(", ")}\n`),
1067
- );
1068
- // An agent with GitHub/Origin/deployments channels bound to no repository
1069
- // scope gets no deliveries at all (deployments channels receive the
1070
- // watched repos' deployment events), which is otherwise invisible here.
1071
- if (watchesRepositories && detail.repositories?.length === 0) {
1072
- ctx.err(
1073
- `warning: ${slug} watches no repositories, so its GitHub/Origin/deployments channels receive no events.\n` +
1074
- ` Redeploy with --cursor-events-repo <owner>/<name> to set the scope.\n`,
1075
- );
1076
- }
1077
- return 0;
1078
- }
1079
-
1080
- /**
1081
- * Post-deploy connector authorization for `cursorAccount` connections: the
1082
- * agent's connectors execute with tokens owned by the deployment's service
1083
- * account, which start out unauthorized. On a TTY this runs the dashboard's
1084
- * Authenticate flow inline (browser OAuth per connector, already-authorized
1085
- * ones skipped); otherwise it reports status only. Never affects the deploy
1086
- * outcome — anything left unauthorized just gets a playground-page pointer.
1087
- */
1088
- async function runPostDeployConnectorAuth(
1089
- ctx: DeployCliContext,
1090
- options: DeployCliOptions,
1091
- args: {
1092
- slug: string;
1093
- connections: DeployManifest["cursorAccountConnections"];
1094
- serviceAccountId: string | undefined;
1095
- },
1096
- ): Promise<void> {
1097
- if (args.connections.length === 0) {
1098
- return;
1099
- }
1100
-
1101
- const { serviceAccountId } = args;
1102
- if (serviceAccountId === undefined) {
1103
- ctx.err(
1104
- `warning: this agent uses Cursor account connectors, but the deployment has no service account; redeploy to mint one.\n`,
1105
- );
1106
- return;
1107
- }
1108
-
1109
- const backendClient = new CursorBackendClient({
1110
- backendUrl: ctx.backendUrl,
1111
- apiKey: ctx.apiKey,
1112
- fetchImpl: options.fetchImpl,
1113
- });
1114
-
1115
- const interactive =
1116
- options.openBrowserImpl !== undefined ||
1117
- (process.stdout.isTTY === true && process.stdin.isTTY === true);
1118
-
1119
- let anyUnauthorized = false;
1120
- for (const ref of args.connections) {
1121
- ctx.out(
1122
- `\nThis agent uses Cursor account connectors (connection "${ref.name}"${
1123
- ref.servers === undefined || ref.servers.length === 0 ? "" : `: ${ref.servers.join(", ")}`
1124
- }).\n`,
1125
- );
1126
- try {
1127
- const result = await authorizeCursorAccountConnectors({
1128
- backendClient,
1129
- teamId: ctx.teamId,
1130
- slug: args.slug,
1131
- serviceAccountId,
1132
- connectionName: ref.name,
1133
- servers: ref.servers,
1134
- interactive,
1135
- out: ctx.out,
1136
- err: ctx.err,
1137
- openBrowserImpl: options.openBrowserImpl,
1138
- sleep: options.sleepImpl,
1139
- pollIntervalMs: options.connectorPollIntervalMs,
1140
- pollTimeoutMs: options.connectorPollTimeoutMs,
1141
- });
1142
- if (result.pending.length > 0 || result.errored.length > 0) {
1143
- anyUnauthorized = true;
1144
- if (result.pending.length > 0) {
1145
- ctx.out(`Connectors not authorized yet: ${result.pending.join(", ")}.\n`);
1146
- }
1147
- }
1148
- } catch (error) {
1149
- anyUnauthorized = true;
1150
- ctx.err(
1151
- `warning: could not check connector authorization for "${ref.name}": ${describeError(error)}\n`,
1152
- );
1153
- }
1154
- }
1155
-
1156
- if (anyUnauthorized) {
1157
- ctx.out(
1158
- `You can authorize connectors at the playground page (cursor.com dashboard → Deployed Agents → ${args.slug}) or with \`${CLI} mcp oauth <connection>\`.\n`,
1159
- );
1160
- }
1161
- }
1162
-
1163
- interface DeployManifest {
1164
- hosting: AgentHostingOptions | undefined;
1165
- architecture: AgentArchitecture | undefined;
1166
- persistent: boolean;
1167
- /** `hosting.image`, the team container image this agent runs in. */
1168
- image: string | undefined;
1169
- /** `cursorAccount` connections declared by the agent. */
1170
- cursorAccountConnections: Array<{
1171
- name: string;
1172
- servers: string[] | undefined;
1173
- }>;
1174
- /**
1175
- * Channels whose authored auth chain contains `publicEndpoint()`: their
1176
- * `/v1/channels/<id>` subtree is served through the stable alias WITHOUT
1177
- * `X-Agent-Alias-Token` (the channel authenticates deliveries itself).
1178
- */
1179
- publicChannelIds: string[];
1180
- /**
1181
- * Public channels whose auth chain also carries `sharedSecretAuth` /
1182
- * `hmacSignatureAuth`. Chains are any-of, so `publicEndpoint()` admits
1183
- * every request and those secret policies never gate anything — locally
1184
- * or on the control plane; surfaced as a warning.
1185
- */
1186
- publicChannelIdsWithDeadSecretPolicies: string[];
1187
- /**
1188
- * `publicEndpoint()` on the built-in HTTP channel — a no-op on hosting
1189
- * (only `/v1/channels/…` subtrees can be public); surfaced as a warning.
1190
- */
1191
- httpChannelPublicEndpoint: boolean;
1192
- /**
1193
- * The agent has GitHub/Origin channels, so it only receives deliveries
1194
- * while the deployment is bound to at least one `owner/name` scope.
1195
- */
1196
- hasScmChannels: boolean;
1197
- /**
1198
- * The agent has deployments channels; deploy-source edges derive from
1199
- * the same watched-repo set as GitHub/Origin, so it too receives
1200
- * nothing while the deployment watches no repositories.
1201
- */
1202
- hasDeploymentsChannels: boolean;
1203
- }
1204
-
1205
- /**
1206
- * Read the deploy-relevant bits of the project manifest at `dir` in one
1207
- * load: the `hosting` block, the agent's cursorAccount connections, and the
1208
- * channels declared public via `publicEndpoint()`.
1209
- */
1210
- async function readDeployManifest(dir: string): Promise<DeployManifest> {
1211
- const project = await loadAgentProject(dir);
1212
- // Fail here, before anything is dispatched: a project with error
1213
- // diagnostics (e.g. agent/storage.ts on architecture v2) would only be
1214
- // refused later by `serve` inside the hosted build, wasting a bake.
1215
- if (hasErrorDiagnostics(project)) {
1216
- throw new Error(
1217
- `Refusing to deploy ${project.name} — it has errors:\n${formatDiagnostics(
1218
- project.diagnostics.filter(d => d.severity === "error"),
1219
- )}`,
1220
- );
1221
- }
1222
- return {
1223
- hosting: project.agent.hosting,
1224
- architecture: project.agent.architecture,
1225
- persistent: project.agent.persistent === true,
1226
- image: project.agent.hosting?.image,
1227
- cursorAccountConnections: project.agent.connections.flatMap(connection =>
1228
- isCursorAccountTransport(connection.transport)
1229
- ? [{ name: connection.name, servers: connection.transport.servers }]
1230
- : [],
1231
- ),
1232
- publicChannelIds: project.channels
1233
- .filter(channel => (channel.definition.auth ?? []).some(isPublicEndpointPolicy))
1234
- .map(channel => channel.id),
1235
- publicChannelIdsWithDeadSecretPolicies: project.channels
1236
- .filter(channel => {
1237
- const auth = channel.definition.auth ?? [];
1238
- return (
1239
- auth.some(isPublicEndpointPolicy) &&
1240
- auth.some(policy => {
1241
- const kind = catchAuthDeclaration(policy)?.kind;
1242
- return kind !== undefined && kind !== "public";
1243
- })
1244
- );
1245
- })
1246
- .map(channel => channel.id),
1247
- httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(isPublicEndpointPolicy),
1248
- hasScmChannels: project.channels.some(channel => {
1249
- const kind = channel.definition.meta?.kind;
1250
- return kind === "github" || kind === "origin";
1251
- }),
1252
- hasDeploymentsChannels: project.channels.some(
1253
- channel => channel.definition.meta?.kind === "deployments",
1254
- ),
1255
- };
1256
- }
1257
-
1258
- /**
1259
- * Friendly post-deploy nudge: the manifest declares secrets the agent
1260
- * expects, but the deployment does not have them all set yet.
1261
- */
1262
- function warnMissingSecrets(
1263
- ctx: DeployCliContext,
1264
- slug: string,
1265
- declared: string[] | undefined,
1266
- set: string[] | undefined,
1267
- ): void {
1268
- if (declared === undefined || declared.length === 0) {
1269
- return;
1270
- }
1271
- const setNames = new Set(set ?? []);
1272
- // Platform-managed names (CURSOR_*, hosted storage) are injected by the
1273
- // control plane and never appear in secrets list — do not warn on them.
1274
- const missing = declared.filter(name => !setNames.has(name) && !isReservedSecretName(name));
1275
- if (missing.length === 0) {
1276
- return;
1277
- }
1278
- ctx.err(
1279
- `warning: secrets declared in the agent manifest but not set on ${slug}: ${missing.join(", ")}\n` +
1280
- ` set them with \`${CLI} secrets set ${slug} ${missing.join(" ")}\`\n`,
1281
- );
1282
- }
1283
-
1284
- // ============================================================================
1285
- // deployments (list)
1286
- // ============================================================================
1287
-
1288
- export async function cmdDeployments(options: DeployCliOptions): Promise<number> {
1289
- const ctx = await prepare(options);
1290
- if (typeof ctx === "number") {
1291
- return ctx;
1292
- }
1293
- let deployments: Deployment[];
1294
- try {
1295
- deployments = await ctx.client.listDeployments({ teamId: ctx.teamId });
1296
- } catch (error) {
1297
- return renderApiError(error, ctx.err);
1298
- }
1299
- let applications: AgentkitDeploymentListItem[] = [];
1300
- try {
1301
- applications = await ctx.client.listAgentkitDeployments({
1302
- teamId: ctx.teamId,
1303
- });
1304
- } catch (error) {
1305
- // Older control planes 404 this route; keep listing v1.
1306
- if (!(error instanceof DeployApiError && error.status === 404)) {
1307
- return renderApiError(error, ctx.err);
1308
- }
1309
- }
1310
- if (ctx.json) {
1311
- // Empty v2 list keeps `{ deployments }` for v1-only parsers.
1312
- ctx.out(
1313
- `${JSON.stringify(
1314
- applications.length === 0 ? { deployments } : { deployments, applications },
1315
- null,
1316
- 2,
1317
- )}\n`,
1318
- );
1319
- return 0;
1320
- }
1321
- if (deployments.length === 0 && applications.length === 0) {
1322
- ctx.out(`No deployments on team ${ctx.teamId}. Create one with \`${CLI} deploy\`.\n`);
1323
- return 0;
1324
- }
1325
- ctx.out(
1326
- `${renderTable(
1327
- ["SLUG", "ARCH", "STATUS", "GEN", "KIND", "UPDATED"],
1328
- [
1329
- ...deployments.map(d => [
1330
- d.slug,
1331
- "v1",
1332
- d.status,
1333
- String(d.generation),
1334
- d.deploymentKind,
1335
- d.desiredUpdatedAt ?? "",
1336
- ]),
1337
- ...applications.map(application => [
1338
- application.slug,
1339
- "v2",
1340
- application.status,
1341
- "-",
1342
- "application",
1343
- application.deployments
1344
- .map(row => row.desiredUpdatedAt)
1345
- .sort()
1346
- .at(-1) ?? "",
1347
- ]),
1348
- ],
1349
- )}\n`,
1350
- );
1351
- return 0;
1352
- }
1353
-
1354
- // ============================================================================
1355
- // deployment <slug> (detail)
1356
- // ============================================================================
1357
-
1358
- export async function cmdDeploymentStatus(
1359
- slug: string | undefined,
1360
- options: DeployCliOptions,
1361
- ): Promise<number> {
1362
- if (slug === undefined || slug === "") {
1363
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1364
- `Usage: ${CLI} deployment <slug> [--team <id>] [--json]\n`,
1365
- );
1366
- return 1;
1367
- }
1368
- const ctx = await prepare(options);
1369
- if (typeof ctx === "number") {
1370
- return ctx;
1371
- }
1372
- // Same dispatch as `stop`: v1 409s architecture-v2 slugs, so the v2
1373
- // GET goes first. A v2 404 (missing slug, or an older backend) falls
1374
- // through to the v1 agent-serve detail.
1375
- try {
1376
- const detail = await ctx.client.getAgentkitDeploy({
1377
- teamId: ctx.teamId,
1378
- slug,
1379
- });
1380
- renderAgentkitDeploymentStatus(ctx, detail);
1381
- return 0;
1382
- } catch (error) {
1383
- if (!(error instanceof DeployApiError) || error.status !== 404) {
1384
- return renderApiError(error, ctx.err);
1385
- }
1386
- }
1387
- let detail: DeploymentDetail;
1388
- try {
1389
- detail = await ctx.client.getDeployment({ teamId: ctx.teamId, slug });
1390
- } catch (error) {
1391
- return renderApiError(error, ctx.err);
1392
- }
1393
- if (ctx.json) {
1394
- ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
1395
- return 0;
1396
- }
1397
- const aliasUrl = ctx.client.aliasUrl(detail.engineAlias);
1398
- const lines = [`${detail.slug} (team ${detail.teamId})`, ` status: ${detail.status}`];
1399
- if (detail.progress !== undefined) {
1400
- lines.push(` progress: ${detail.progress}`);
1401
- }
1402
- if (
1403
- detail.statusMessage !== null &&
1404
- detail.statusMessage !== undefined &&
1405
- detail.statusMessage.trim() !== ""
1406
- ) {
1407
- lines.push(` step: ${detail.statusMessage}`);
1408
- }
1409
- lines.push(
1410
- ` generation: ${detail.generation}`,
1411
- ` kind: ${detail.deploymentKind}`,
1412
- ` alias: ${aliasUrl}`,
1413
- ` playground: ${aliasUrl}/playground`,
1414
- );
1415
- if (detail.gitRepoUrl !== null && detail.gitRepoUrl !== undefined) {
1416
- lines.push(` repo: ${detail.gitRepoUrl} @ ${detail.gitRef ?? ""}`);
1417
- }
1418
- if (detail.serviceAccountId !== undefined && detail.serviceAccountId !== null) {
1419
- lines.push(` service account: ${detail.serviceAccountId}`);
1420
- }
1421
- if (detail.agentStoreSourceId !== undefined) {
1422
- // The durable file store's address: GET /v0/agent-serve-files/<id> lists
1423
- // it, and the pod sees it as AGENT_SERVE_STORE_SOURCE_ID.
1424
- lines.push(` file store: ${detail.agentStoreSourceId}`);
1425
- }
1426
- if (detail.egressAllowedDomains !== undefined && detail.egressAllowedDomains.length > 0) {
1427
- lines.push(` egress: ${detail.egressAllowedDomains.join(", ")}`);
1428
- }
1429
- if (detail.publicRoutePaths !== undefined && detail.publicRoutePaths.length > 0) {
1430
- // Served through the alias WITHOUT X-Agent-Alias-Token (the channel
1431
- // authenticates deliveries itself).
1432
- for (const path of detail.publicRoutePaths) {
1433
- lines.push(` public: ${aliasUrl}${path} (no alias token)`);
1434
- }
1435
- }
1436
- if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
1437
- lines.push(` secrets: ${detail.secretNames.join(", ")}`);
1438
- }
1439
- if (detail.podCredentialMaskedKey !== undefined && detail.podCredentialMaskedKey !== null) {
1440
- lines.push(` pod credential: ${detail.podCredentialMaskedKey}`);
1441
- }
1442
- if (detail.engineUrl !== null && detail.engineUrl !== undefined) {
1443
- lines.push(` engine: ${detail.engineUrl}`);
1444
- }
1445
- if (
1446
- detail.engineAccess !== null &&
1447
- detail.engineAccess !== undefined &&
1448
- typeof detail.engineAccess === "object"
1449
- ) {
1450
- lines.push(
1451
- ` engine access: ${detail.engineAccess.url} (expires ${detail.engineAccess.expiresAt})`,
1452
- );
1453
- }
1454
- if (detail.lastError !== null && detail.lastError !== undefined) {
1455
- lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
1456
- }
1457
- if (detail.desiredUpdatedAt !== undefined) {
1458
- lines.push(` updated: ${detail.desiredUpdatedAt}`);
1459
- }
1460
- ctx.out(`${lines.join("\n")}\n`);
1461
- return 0;
1462
- }
1463
-
1464
- function renderAgentkitDeploymentStatus(ctx: DeployCliContext, detail: AgentkitDeployStatus): void {
1465
- if (ctx.json) {
1466
- ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
1467
- return;
1468
- }
1469
- const lines = [
1470
- `${detail.slug} (team ${detail.teamId})`,
1471
- ` status: ${detail.status}`,
1472
- ` architecture: v2`,
1473
- ` application: ${detail.applicationId}`,
1474
- ` release: ${detail.releaseId}`,
1475
- ` progress: ${detail.progress}`,
1476
- ];
1477
- if (detail.statusMessage !== null && detail.statusMessage.trim() !== "") {
1478
- lines.push(` step: ${detail.statusMessage}`);
1479
- }
1480
- if (detail.repositories !== undefined) {
1481
- lines.push(
1482
- ` watching: ${
1483
- detail.repositories.length === 0 ? "(nothing)" : detail.repositories.join(", ")
1484
- }`,
1485
- );
1486
- }
1487
- if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
1488
- lines.push(` secrets: ${detail.secretNames.join(", ")}`);
1489
- }
1490
- if (detail.lastError !== null && detail.lastError !== undefined) {
1491
- lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
1492
- }
1493
- ctx.out(`${lines.join("\n")}\n`);
1494
- }
1495
-
1496
- // ============================================================================
1497
- // stop <slug>
1498
- // ============================================================================
1499
-
1500
- export async function cmdStop(
1501
- slug: string | undefined,
1502
- options: DeployCliOptions,
1503
- ): Promise<number> {
1504
- if (slug === undefined || slug === "") {
1505
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1506
- `Usage: ${CLI} stop <slug> [--team <id>] [--json] [--no-wait]\n`,
1507
- );
1508
- return 1;
1509
- }
1510
- const ctx = await prepare(options);
1511
- if (typeof ctx === "number") {
1512
- return ctx;
1513
- }
1514
- // Stop takes only a slug, so there is no manifest to dispatch on (unlike
1515
- // deploy). The v2 agentsdk control plane goes first: v1 endpoints refuse
1516
- // `architecture: "v2"` slugs with a 409 rather than a 404, so v1-first
1517
- // could not fall through. A v2 404 (including an older backend without
1518
- // the `/internal/agentsdk` mount) falls back to the v1 stop.
1519
- let architecture: "v1" | "v2" = "v2";
1520
- let accepted: StopAccepted | AgentkitStopAccepted;
1521
- try {
1522
- accepted = await ctx.client.stopAgentkit({
1523
- teamId: ctx.teamId,
1524
- slug,
1525
- cancelRuns: options.cancelRuns,
1526
- });
1527
- } catch (error) {
1528
- if (!(error instanceof DeployApiError) || error.status !== 404) {
1529
- return renderApiError(error, ctx.err);
1530
- }
1531
- if (options.cancelRuns === true) {
1532
- ctx.err(
1533
- `Canceling active runs while stopping is supported only for Agent SDK v2 deployments.\n`,
1534
- );
1535
- return 1;
1536
- }
1537
- try {
1538
- accepted = await ctx.client.stopDeployment({ teamId: ctx.teamId, slug });
1539
- architecture = "v1";
1540
- } catch (v1Error) {
1541
- return renderApiError(v1Error, ctx.err);
1542
- }
1543
- }
1544
- const cancellation = "cancellation" in accepted ? accepted.cancellation : undefined;
1545
- const stoppedGlobally = "scope" in accepted && accepted.scope === "global";
1546
- if (ctx.noWait) {
1547
- if (ctx.json) {
1548
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1549
- } else {
1550
- ctx.out(
1551
- stoppedGlobally
1552
- ? `Stop accepted for ${slug} across all tenants (status: ${accepted.status}).\n`
1553
- : `Stop accepted for ${slug} (status: ${accepted.status}).\n`,
1554
- );
1555
- }
1556
- return cancellation?.failed === 0 || cancellation === undefined ? 0 : 1;
1557
- }
1558
- let detail: DeploymentDetail | AgentkitDeployStatus;
1559
- try {
1560
- detail =
1561
- architecture === "v2"
1562
- ? await pollUntil(ctx, slug, new Set(["stopped"]), () =>
1563
- ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }),
1564
- )
1565
- : await pollUntil(ctx, slug, new Set(["stopped"]), () =>
1566
- ctx.client.getDeployment({ teamId: ctx.teamId, slug }),
1567
- );
1568
- } catch (error) {
1569
- return renderApiError(error, ctx.err);
1570
- }
1571
- if (ctx.json) {
1572
- ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
1573
- } else if (stoppedGlobally) {
1574
- ctx.out(
1575
- `Stopped ${slug} across all tenants (team ${ctx.teamId}); resume with \`${CLI} start ${slug}\`.\n`,
1576
- );
1577
- } else {
1578
- ctx.out(`Stopped ${slug} (team ${ctx.teamId}).\n`);
1579
- }
1580
- if (cancellation !== undefined) {
1581
- return writeCancellationStarted(ctx, cancellation, slug);
1582
- }
1583
- return 0;
1584
- }
1585
-
1586
- function writeCancellationStarted(
1587
- ctx: { out: (text: string) => void; err: (text: string) => void },
1588
- cancellation: AgentkitCancelRunsResult,
1589
- slug: string,
1590
- ): number {
1591
- ctx.out(`Started cancellation across ${cancellation.batchesStarted} deployment batch(es).\n`);
1592
- if (cancellation.sessionsMarkedFailed !== undefined) {
1593
- ctx.out(`Marked ${cancellation.sessionsMarkedFailed} factory session(s) failed.\n`);
1594
- }
1595
- if (cancellation.failed > 0) {
1596
- ctx.err(
1597
- `Failed to start ${cancellation.failed} cancellation batch(es); retry with \`${CLI} cancel-runs ${slug}\`.\n`,
1598
- );
1599
- return 1;
1600
- }
1601
- return 0;
1602
- }
1603
-
1604
- export async function cmdStart(
1605
- slug: string | undefined,
1606
- options: DeployCliOptions,
1607
- ): Promise<number> {
1608
- if (slug === undefined || slug === "") {
1609
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1610
- `Usage: ${CLI} start <slug> [--team <id>] [--json]\n`,
1611
- );
1612
- return 1;
1613
- }
1614
- const ctx = await prepare(options);
1615
- if (typeof ctx === "number") {
1616
- return ctx;
1617
- }
1618
- try {
1619
- const accepted = await ctx.client.startAgentkit({
1620
- teamId: ctx.teamId,
1621
- slug,
1622
- });
1623
- if (ctx.json) {
1624
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1625
- } else {
1626
- ctx.out(`Started ${slug} (team ${ctx.teamId}).\n`);
1627
- }
1628
- return 0;
1629
- } catch (error) {
1630
- return renderApiError(error, ctx.err);
1631
- }
1632
- }
1633
-
1634
- export async function cmdKill(
1635
- slug: string | undefined,
1636
- options: DeployCliOptions,
1637
- ): Promise<number> {
1638
- if (slug === undefined || slug === "") {
1639
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1640
- `Usage: ${CLI} kill <slug> [--team <id>] [--json]\n`,
1641
- );
1642
- return 1;
1643
- }
1644
- const ctx = await prepare(options);
1645
- if (typeof ctx === "number") {
1646
- return ctx;
1647
- }
1648
- let accepted: AgentkitStopAccepted;
1649
- try {
1650
- accepted = await ctx.client.killAgentkit({ teamId: ctx.teamId, slug });
1651
- } catch (error) {
1652
- return renderApiError(error, ctx.err);
1653
- }
1654
- if (ctx.json) {
1655
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1656
- } else {
1657
- ctx.out(
1658
- accepted.scope === "global"
1659
- ? `Killed ${slug} across all tenants (team ${ctx.teamId}): deploys locked, admission stopped.\n`
1660
- : `Killed ${slug} (team ${ctx.teamId}): deploys locked, admission stopped.\n`,
1661
- );
1662
- ctx.out(
1663
- `Resume traffic with \`${CLI} unlock-deploys ${slug}\` then \`${CLI} start ${slug}\`.\n`,
1664
- );
1665
- ctx.out(`Unlock deploys with \`${CLI} unlock-deploys ${slug}\`.\n`);
1666
- }
1667
- if (accepted.cancellation !== undefined) {
1668
- return writeCancellationStarted(ctx, accepted.cancellation, slug);
1669
- }
1670
- return 0;
1671
- }
1672
-
1673
- export async function cmdUnlockDeploys(
1674
- slug: string | undefined,
1675
- options: DeployCliOptions,
1676
- ): Promise<number> {
1677
- if (slug === undefined || slug === "") {
1678
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1679
- `Usage: ${CLI} unlock-deploys <slug> [--team <id>] [--json]\n`,
1680
- );
1681
- return 1;
1682
- }
1683
- const ctx = await prepare(options);
1684
- if (typeof ctx === "number") {
1685
- return ctx;
1686
- }
1687
- try {
1688
- const accepted = await ctx.client.unlockAgentkitDeploys({
1689
- teamId: ctx.teamId,
1690
- slug,
1691
- });
1692
- if (ctx.json) {
1693
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1694
- } else {
1695
- ctx.out(`Unlocked deploys for ${slug} (team ${ctx.teamId}).\n`);
1696
- }
1697
- return 0;
1698
- } catch (error) {
1699
- return renderApiError(error, ctx.err);
1700
- }
1701
- }
1702
-
1703
- export async function cmdCancelRuns(
1704
- slug: string | undefined,
1705
- options: DeployCliOptions,
1706
- ): Promise<number> {
1707
- if (slug === undefined || slug === "") {
1708
- (options.err ?? ((text: string) => void process.stderr.write(text)))(
1709
- `Usage: ${CLI} cancel-runs <slug> [--team <id>] [--json]\n`,
1710
- );
1711
- return 1;
1712
- }
1713
- const ctx = await prepare(options);
1714
- if (typeof ctx === "number") {
1715
- return ctx;
1716
- }
1717
- let result: AgentkitCancelRunsResult;
1718
- try {
1719
- result = await ctx.client.cancelAgentkitRuns({
1720
- teamId: ctx.teamId,
1721
- slug,
1722
- });
1723
- } catch (error) {
1724
- return renderApiError(error, ctx.err);
1725
- }
1726
- if (ctx.json) {
1727
- ctx.out(`${JSON.stringify(result, null, 2)}\n`);
1728
- } else {
1729
- ctx.out(`Started cancellation across ${result.batchesStarted} deployment batch(es).\n`);
1730
- if (result.sessionsMarkedFailed !== undefined) {
1731
- ctx.out(`Marked ${result.sessionsMarkedFailed} factory session(s) failed.\n`);
1732
- }
1733
- }
1734
- if (result.failed > 0) {
1735
- ctx.err(`Failed to start ${result.failed} cancellation batch(es).\n`);
1736
- return 1;
1737
- }
1738
- return 0;
1739
- }
1740
-
1741
- // ============================================================================
1742
- // event-repos <slug> --repo <owner/name>...
1743
- // ============================================================================
1744
-
1745
- export async function cmdEventRepos(
1746
- slug: string | undefined,
1747
- repositories: string[],
1748
- options: DeployCliOptions,
1749
- ): Promise<number> {
1750
- if (slug === undefined || slug === "") {
1751
- (options.err ?? ((text: string) => void process.stderr.write(text)))(
1752
- `Usage: ${CLI} event-repos <slug> --repo <owner/name>... [--team <id>] [--json]\n`,
1753
- );
1754
- return 1;
1755
- }
1756
- const ctx = await prepare(options);
1757
- if (typeof ctx === "number") {
1758
- return ctx;
1759
- }
1760
- let configured: string[];
1761
- try {
1762
- configured = await ctx.client.setAgentkitEventRepositories({
1763
- teamId: ctx.teamId,
1764
- slug,
1765
- repositories,
1766
- });
1767
- } catch (error) {
1768
- return renderApiError(error, ctx.err);
1769
- }
1770
- if (ctx.json) {
1771
- ctx.out(`${JSON.stringify({ repositories: configured }, null, 2)}\n`);
1772
- } else if (configured.length === 0) {
1773
- ctx.out(`${slug} now watches no repositories.\n`);
1774
- } else {
1775
- ctx.out(`${slug} now watches ${configured.join(", ")}.\n`);
1776
- }
1777
- return 0;
1778
- }
1779
-
1780
- // ============================================================================
1781
- // upgrade <slug>
1782
- // ============================================================================
1783
-
1784
- /** Release ids are uuids; anything else the user typed is a version. */
1785
- const RELEASE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1786
-
1787
- export async function cmdUpgrade(
1788
- args: { slug: string | undefined; release: string | undefined },
1789
- options: DeployCliOptions,
1790
- ): Promise<number> {
1791
- return cmdPinRelease(args, options, {
1792
- usage: `Usage: ${CLI} upgrade <slug> [--release <id|version>] [--team <id>] [--json]\n`,
1793
- pin: (client, request) => client.upgradeAgentkitInstall(request),
1794
- success: (slug, releaseId) => `${slug} now runs release ${releaseId}.\n`,
1795
- });
1796
- }
1797
-
1798
- export async function cmdRollback(
1799
- args: { slug: string | undefined; release: string | undefined },
1800
- options: DeployCliOptions,
1801
- ): Promise<number> {
1802
- return cmdPinRelease(args, options, {
1803
- usage: `Usage: ${CLI} rollback <slug> [--release <id|version>] [--team <id>] [--json]\n`,
1804
- pin: (client, request) => client.rollbackAgentkitInstall(request),
1805
- success: (slug, releaseId) => `${slug} rolled back to release ${releaseId}.\n`,
1806
- });
1807
- }
1808
-
1809
- async function cmdPinRelease(
1810
- args: { slug: string | undefined; release: string | undefined },
1811
- options: DeployCliOptions,
1812
- pin: {
1813
- usage: string;
1814
- pin: (
1815
- client: AgentServeDeployClient,
1816
- request: {
1817
- teamId: number;
1818
- slug: string;
1819
- releaseId?: string;
1820
- version?: string;
1821
- },
1822
- ) => Promise<string>;
1823
- success: (slug: string, releaseId: string) => string;
1824
- },
1825
- ): Promise<number> {
1826
- const { slug, release } = args;
1827
- if (slug === undefined || slug === "") {
1828
- (options.err ?? ((text: string) => void process.stderr.write(text)))(pin.usage);
1829
- return 1;
1830
- }
1831
- const ctx = await prepare(options);
1832
- if (typeof ctx === "number") {
1833
- return ctx;
1834
- }
1835
- const selector = release?.trim() ?? "";
1836
- let releaseId: string;
1837
- try {
1838
- releaseId = await pin.pin(ctx.client, {
1839
- teamId: ctx.teamId,
1840
- slug,
1841
- ...(selector === ""
1842
- ? {}
1843
- : RELEASE_ID_RE.test(selector)
1844
- ? { releaseId: selector }
1845
- : { version: selector }),
1846
- });
1847
- } catch (error) {
1848
- return renderApiError(error, ctx.err);
1849
- }
1850
- if (ctx.json) {
1851
- ctx.out(`${JSON.stringify({ releaseId }, null, 2)}\n`);
1852
- } else {
1853
- ctx.out(pin.success(slug, releaseId));
1854
- }
1855
- return 0;
1856
- }
1857
-
1858
- // ============================================================================
1859
- // delete <slug>
1860
- // ============================================================================
1861
-
1862
- export async function cmdDelete(
1863
- slug: string | undefined,
1864
- options: DeployCliOptions,
1865
- ): Promise<number> {
1866
- if (slug === undefined || slug === "") {
1867
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1868
- `Usage: ${CLI} delete <slug> [--team <id>] [--json] [--no-wait]\n`,
1869
- );
1870
- return 1;
1871
- }
1872
- const ctx = await prepare(options);
1873
- if (typeof ctx === "number") {
1874
- return ctx;
1875
- }
1876
- // Same dispatch as `stop`: v1 409s architecture-v2 slugs, so the v2
1877
- // DELETE goes first. A v2 404 (missing slug, or an older backend)
1878
- // falls through to the v1 agent-serve delete.
1879
- let architecture: "v1" | "v2" = "v2";
1880
- let accepted: DeleteAccepted | AgentkitStopAccepted;
1881
- try {
1882
- accepted = await ctx.client.deleteAgentkit({ teamId: ctx.teamId, slug });
1883
- } catch (error) {
1884
- if (!(error instanceof DeployApiError) || error.status !== 404) {
1885
- return renderApiError(error, ctx.err);
1886
- }
1887
- try {
1888
- accepted = await ctx.client.deleteDeployment({
1889
- teamId: ctx.teamId,
1890
- slug,
1891
- });
1892
- architecture = "v1";
1893
- } catch (v1Error) {
1894
- return renderApiError(v1Error, ctx.err);
1895
- }
1896
- }
1897
- if (ctx.noWait) {
1898
- if (ctx.json) {
1899
- ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1900
- } else {
1901
- ctx.out(`Delete accepted for ${slug} (status: ${accepted.status}).\n`);
1902
- }
1903
- return 0;
1904
- }
1905
- try {
1906
- await pollUntilGone(ctx, slug, () =>
1907
- architecture === "v2"
1908
- ? ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug })
1909
- : ctx.client.getDeployment({ teamId: ctx.teamId, slug }),
1910
- );
1911
- } catch (error) {
1912
- return renderApiError(error, ctx.err);
1913
- }
1914
- if (ctx.json) {
1915
- ctx.out(`${JSON.stringify({ slug, status: "deleted" }, null, 2)}\n`);
1916
- } else {
1917
- ctx.out(`Deleted ${slug} (team ${ctx.teamId}).\n`);
1918
- }
1919
- return 0;
1920
- }
1921
-
1922
- // ============================================================================
1923
- // rotate-token <slug>
1924
- // ============================================================================
1925
-
1926
- export async function cmdRotateToken(
1927
- slug: string | undefined,
1928
- options: DeployCliOptions,
1929
- ): Promise<number> {
1930
- if (slug === undefined || slug === "") {
1931
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
1932
- `Usage: ${CLI} rotate-token <slug> [--team <id>]\n`,
1933
- );
1934
- return 1;
1935
- }
1936
- const ctx = await prepare(options);
1937
- if (typeof ctx === "number") {
1938
- return ctx;
1939
- }
1940
- let aliasToken: string;
1941
- try {
1942
- const architecture = await deploymentArchitecture({
1943
- client: ctx.client,
1944
- teamId: ctx.teamId,
1945
- slug,
1946
- });
1947
- aliasToken =
1948
- architecture === "v2"
1949
- ? await ctx.client.rotateAgentsdkAliasToken({
1950
- teamId: ctx.teamId,
1951
- slug,
1952
- })
1953
- : await ctx.client.rotateAliasToken({
1954
- teamId: ctx.teamId,
1955
- slug,
1956
- });
1957
- } catch (error) {
1958
- return renderApiError(error, ctx.err);
1959
- }
1960
- if (ctx.json) {
1961
- ctx.out(`${JSON.stringify({ aliasToken }, null, 2)}\n`);
1962
- return 0;
1963
- }
1964
- ctx.out(
1965
- `Rotated the alias token for ${slug}. The old token stopped working immediately.\n${aliasTokenBlock(aliasToken)}`,
1966
- );
1967
- return 0;
1968
- }
1969
-
1970
- // ============================================================================
1971
- // secrets set|list|unset
1972
- // ============================================================================
1973
-
1974
- async function secretDeploymentArchitecture(
1975
- ctx: DeployCliContext,
1976
- slug: string,
1977
- ): Promise<"v1" | "v2"> {
1978
- return deploymentArchitecture({
1979
- client: ctx.client,
1980
- teamId: ctx.teamId,
1981
- slug,
1982
- });
1983
- }
1984
-
1985
- export async function cmdSecretsSet(
1986
- slug: string | undefined,
1987
- specs: string[],
1988
- options: DeployCliOptions,
1989
- ): Promise<number> {
1990
- const usageErr = options.err ?? ((t: string) => void process.stderr.write(t));
1991
- if (slug === undefined || slug === "" || specs.length === 0) {
1992
- usageErr(
1993
- `Usage: ${CLI} secrets set <slug> NAME [NAME2 ...] [--team <id>] [--json]\n` +
1994
- ` Enter values at the hidden prompt, or pipe one line per NAME.\n` +
1995
- ` Opt-in (leaks into argv / agent terminals): NAME=VALUE … --from-argv\n`,
1996
- );
1997
- return 1;
1998
- }
1999
-
2000
- // Parse and validate every name before prompting for any value, so a
2001
- // typo in the last name cannot waste typed-in secrets.
2002
- const parsed: Array<{ name: string; value?: string }> = [];
2003
- for (const spec of specs) {
2004
- const eq = spec.indexOf("=");
2005
- const name = eq === -1 ? spec : spec.slice(0, eq);
2006
- const nameError = validateSettableSecretName(name);
2007
- if (nameError !== undefined) {
2008
- usageErr(`${nameError}\n`);
2009
- return 1;
2010
- }
2011
- parsed.push(eq === -1 ? { name } : { name, value: spec.slice(eq + 1) });
2012
- }
2013
- const hasArgvValues = parsed.some(entry => entry.value !== undefined);
2014
- if (hasArgvValues && options.fromArgv !== true) {
2015
- usageErr(
2016
- `Refusing NAME=VALUE on argv (shows up in shell history and agent-captured terminals).\n` +
2017
- `Pass names only:\n` +
2018
- ` ${CLI} secrets set ${slug} NAME\n` +
2019
- `Hidden prompt on a TTY; one stdin line per NAME when piped.\n` +
2020
- `Or redirect a file: ${CLI} secrets set ${slug} NAME < ./secret.txt\n`,
2021
- );
2022
- return 1;
2023
- }
2024
- if (hasArgvValues) {
2025
- usageErr(
2026
- `warning: --from-argv puts secret values in argv (shell history and agent-captured terminals).\n`,
2027
- );
2028
- }
2029
-
2030
- const ctx = await prepare(options);
2031
- if (typeof ctx === "number") {
2032
- return ctx;
2033
- }
2034
-
2035
- const stdin = options.stdin ?? process.stdin;
2036
- const readValue = createSecretValueReader(stdin, ctx.err);
2037
- const secrets: Array<{ name: string; value: string }> = [];
2038
- for (const entry of parsed) {
2039
- secrets.push({
2040
- name: entry.name,
2041
- value: entry.value ?? (await readValue(entry.name)),
2042
- });
2043
- }
2044
-
2045
- let secretNames: string[];
2046
- let architecture: "v1" | "v2";
2047
- try {
2048
- architecture = await secretDeploymentArchitecture(ctx, slug);
2049
- secretNames =
2050
- architecture === "v2"
2051
- ? await ctx.client.setAgentkitSecrets({
2052
- teamId: ctx.teamId,
2053
- slug,
2054
- secrets,
2055
- })
2056
- : await ctx.client.setSecrets({
2057
- teamId: ctx.teamId,
2058
- slug,
2059
- secrets,
2060
- });
2061
- } catch (error) {
2062
- return renderApiError(error, ctx.err);
2063
- }
2064
- if (ctx.json) {
2065
- ctx.out(`${JSON.stringify({ secretNames }, null, 2)}\n`);
2066
- return 0;
2067
- }
2068
- ctx.out(
2069
- `Set ${secrets.length} secret${secrets.length === 1 ? "" : "s"} on ${slug}.\n` +
2070
- `Secrets now set: ${secretNames.join(", ")}\n` +
2071
- (architecture === "v2"
2072
- ? `The next task picks them up; no redeploy is required.\n`
2073
- : `The engine picks them up on its next deploy.\n`),
2074
- );
2075
- return 0;
2076
- }
2077
-
2078
- export async function cmdSecretsList(
2079
- slug: string | undefined,
2080
- options: DeployCliOptions,
2081
- ): Promise<number> {
2082
- if (slug === undefined || slug === "") {
2083
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
2084
- `Usage: ${CLI} secrets list <slug> [--team <id>] [--json]\n`,
2085
- );
2086
- return 1;
2087
- }
2088
- const ctx = await prepare(options);
2089
- if (typeof ctx === "number") {
2090
- return ctx;
2091
- }
2092
- let secrets: SecretInfo[];
2093
- try {
2094
- const architecture = await secretDeploymentArchitecture(ctx, slug);
2095
- secrets =
2096
- architecture === "v2"
2097
- ? await ctx.client.listAgentkitSecrets({ teamId: ctx.teamId, slug })
2098
- : await ctx.client.listSecrets({ teamId: ctx.teamId, slug });
2099
- } catch (error) {
2100
- return renderApiError(error, ctx.err);
2101
- }
2102
- if (ctx.json) {
2103
- ctx.out(`${JSON.stringify({ secrets }, null, 2)}\n`);
2104
- return 0;
2105
- }
2106
- if (secrets.length === 0) {
2107
- ctx.out(`No secrets set on ${slug}. Add one with \`${CLI} secrets set ${slug} NAME\`.\n`);
2108
- return 0;
2109
- }
2110
- ctx.out(
2111
- `${renderTable(
2112
- ["NAME", "CREATED"],
2113
- secrets.map(secret => [secret.name, secret.createdAt]),
2114
- )}\n`,
2115
- );
2116
- return 0;
2117
- }
2118
-
2119
- export async function cmdSecretsUnset(
2120
- slug: string | undefined,
2121
- name: string | undefined,
2122
- options: DeployCliOptions,
2123
- ): Promise<number> {
2124
- if (slug === undefined || slug === "" || name === undefined || name === "") {
2125
- (options.err ?? ((t: string) => void process.stderr.write(t)))(
2126
- `Usage: ${CLI} secrets unset <slug> NAME [--team <id>]\n`,
2127
- );
2128
- return 1;
2129
- }
2130
- const ctx = await prepare(options);
2131
- if (typeof ctx === "number") {
2132
- return ctx;
2133
- }
2134
- try {
2135
- const architecture = await secretDeploymentArchitecture(ctx, slug);
2136
- if (architecture === "v2") {
2137
- await ctx.client.deleteAgentkitSecret({
2138
- teamId: ctx.teamId,
2139
- slug,
2140
- name,
2141
- });
2142
- } else {
2143
- await ctx.client.deleteSecret({ teamId: ctx.teamId, slug, name });
2144
- }
2145
- } catch (error) {
2146
- if (error instanceof DeployApiError && error.status === 404) {
2147
- ctx.err(
2148
- `No secret named ${name} on deployment ${slug} — check \`${CLI} secrets list ${slug}\` (and the slug / --team).\n`,
2149
- );
2150
- return 1;
2151
- }
2152
- return renderApiError(error, ctx.err);
2153
- }
2154
- if (ctx.json) {
2155
- ctx.out(`${JSON.stringify({ removed: name }, null, 2)}\n`);
2156
- } else {
2157
- ctx.out(`Removed secret ${name} from ${slug}.\n`);
2158
- }
2159
- return 0;
2160
- }
2161
-
2162
- /**
2163
- * Returns a reader for interactive secret values: hidden (no-echo) prompt
2164
- * when `stdin` is a TTY, otherwise one line per secret from the piped
2165
- * stream. One reader per command so piped lines are consumed in order.
2166
- */
2167
- function createSecretValueReader(
2168
- stdin: SecretInputStream,
2169
- err: (text: string) => void,
2170
- ): (name: string) => Promise<string> {
2171
- if (stdin.isTTY === true) {
2172
- return name => promptHidden(stdin, err, `Value for ${name}: `);
2173
- }
2174
- const readLine = createLineReader(stdin);
2175
- return async name => {
2176
- const line = await readLine();
2177
- if (line === undefined) {
2178
- throw new DeployApiError(
2179
- `stdin ended before a value for ${name} was provided (pipe one line per secret).`,
2180
- 0,
2181
- );
2182
- }
2183
- return line;
2184
- };
2185
- }
2186
-
2187
- /** Longest mask run drawn while typing a secret; the count carries the rest. */
2188
- const SECRET_MASK_MAX_WIDTH = 24;
2189
-
2190
- /**
2191
- * One line of typing feedback for the hidden prompt. Echoing the value would
2192
- * leak it, so the line carries a bounded mask plus the exact character count —
2193
- * enough to see that keystrokes, backspaces, and pasted values landed. The
2194
- * mask is clamped to the terminal width so the redraw stays on one line.
2195
- */
2196
- function renderSecretPromptLine(
2197
- prompt: string,
2198
- length: number,
2199
- dim: (text: string) => string,
2200
- ): string {
2201
- const count = `${length} char${length === 1 ? "" : "s"}`;
2202
- const room = (process.stderr.columns ?? 80) - prompt.length - count.length - 2;
2203
- const maskWidth = Math.max(0, Math.min(length, SECRET_MASK_MAX_WIDTH, room));
2204
- // `·` over `•`: the rest of the CLI's progress output already uses it, and
2205
- // fonts that ship no bullet glyph draw a tofu box in its place.
2206
- const mask = "·".repeat(maskWidth);
2207
- const body = mask === "" ? count : `${mask} ${count}`;
2208
- return `\r\u001b[2K${prompt}${dim(body)}`;
2209
- }
2210
-
2211
- /**
2212
- * Raw-mode hidden prompt: never echoes the value, redraws a mask + character
2213
- * count as it is typed, supports backspace and Ctrl-C.
2214
- */
2215
- function promptHidden(
2216
- stdin: SecretInputStream,
2217
- err: (text: string) => void,
2218
- prompt: string,
2219
- ): Promise<string> {
2220
- return new Promise((resolvePromise, reject) => {
2221
- const { dim } = stderrPalette();
2222
- let value = "";
2223
- const draw = (): void => {
2224
- err(renderSecretPromptLine(prompt, value.length, dim));
2225
- };
2226
- draw();
2227
- stdin.setRawMode?.(true);
2228
- stdin.resume();
2229
- const cleanup = (): void => {
2230
- stdin.off("data", onData);
2231
- stdin.setRawMode?.(false);
2232
- stdin.pause();
2233
- draw();
2234
- err("\n");
2235
- };
2236
- const onData = (chunk: Buffer | string): void => {
2237
- const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2238
- for (const ch of text) {
2239
- if (ch === "\r" || ch === "\n") {
2240
- cleanup();
2241
- resolvePromise(value);
2242
- return;
2243
- }
2244
- if (ch === "\u0003") {
2245
- cleanup();
2246
- reject(new DeployApiError("Aborted.", 0));
2247
- return;
2248
- }
2249
- if (ch === "\u007f" || ch === "\b") {
2250
- value = value.slice(0, -1);
2251
- continue;
2252
- }
2253
- value += ch;
2254
- }
2255
- // One redraw per chunk keeps a pasted value from flooding the terminal.
2256
- draw();
2257
- };
2258
- stdin.on("data", onData);
2259
- });
2260
- }
2261
-
2262
- /** Buffered line reader over a piped stream; undefined once it ends. */
2263
- export function createLineReader(stream: NodeJS.ReadableStream): () => Promise<string | undefined> {
2264
- let buffer = "";
2265
- let ended = false;
2266
- const waiters: Array<(line: string | undefined) => void> = [];
2267
-
2268
- const drain = (): void => {
2269
- while (waiters.length > 0) {
2270
- const newline = buffer.indexOf("\n");
2271
- if (newline !== -1) {
2272
- const line = buffer.slice(0, newline).replace(/\r$/, "");
2273
- buffer = buffer.slice(newline + 1);
2274
- waiters.shift()?.(line);
2275
- continue;
2276
- }
2277
- if (!ended) {
2278
- return;
2279
- }
2280
- // Stream ended: a trailing unterminated line still counts once.
2281
- const rest = buffer;
2282
- buffer = "";
2283
- waiters.shift()?.(rest === "" ? undefined : rest.replace(/\r$/, ""));
2284
- }
2285
- };
2286
-
2287
- stream.on("data", (chunk: Buffer | string) => {
2288
- buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
2289
- drain();
2290
- });
2291
- stream.on("end", () => {
2292
- ended = true;
2293
- drain();
2294
- });
2295
- stream.resume();
2296
-
2297
- return () =>
2298
- new Promise(resolvePromise => {
2299
- waiters.push(resolvePromise);
2300
- drain();
2301
- });
2302
- }
2303
-
2304
- // ============================================================================
2305
- // Polling + rendering helpers
2306
- // ============================================================================
2307
-
2308
- /**
2309
- * Poll deployment status every `pollIntervalMs` until it lands in
2310
- * `terminal` (single-line progress on a TTY, one line per transition
2311
- * otherwise). Prefers customer `statusMessage` / `progress` over raw
2312
- * status so create → checkout → start is visible. Throws DeployApiError
2313
- * on request failures or timeout.
2314
- */
2315
- async function pollUntil<
2316
- T extends {
2317
- status: string;
2318
- progress?: DeploymentProgress;
2319
- statusMessage?: string | null;
2320
- },
2321
- >(
2322
- ctx: DeployCliContext,
2323
- slug: string,
2324
- terminal: ReadonlySet<T["status"]>,
2325
- fetchDetail: () => Promise<T>,
2326
- ): Promise<T> {
2327
- const startedAt = Date.now();
2328
- const tty = process.stderr.isTTY === true;
2329
- let lastLabel: string | undefined;
2330
- let sawProgress = false;
2331
-
2332
- const report = (detail: T): void => {
2333
- if (ctx.json) {
2334
- return;
2335
- }
2336
- const label = deployProgressLabel(detail);
2337
- const elapsed = Math.round((Date.now() - startedAt) / 1000);
2338
- if (tty) {
2339
- ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
2340
- sawProgress = true;
2341
- } else if (label !== lastLabel) {
2342
- ctx.err(`${slug}: ${label}\n`);
2343
- }
2344
- lastLabel = label;
2345
- };
2346
- const finishProgressLine = (): void => {
2347
- if (tty && sawProgress) {
2348
- ctx.err("\n");
2349
- }
2350
- };
2351
-
2352
- for (;;) {
2353
- let detail: T;
2354
- try {
2355
- detail = await fetchDetail();
2356
- } catch (error: unknown) {
2357
- // Capacity / rate-limit on GET is not a bake failure. The v2 accept
2358
- // already started freeze; keep polling until the wait budget expires.
2359
- if (isTransientDeployStatusError(error) && Date.now() - startedAt < ctx.timeoutMs) {
2360
- if (!ctx.json) {
2361
- const elapsed = Math.round((Date.now() - startedAt) / 1000);
2362
- const label = "Waiting for status (server busy)";
2363
- if (tty) {
2364
- ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
2365
- sawProgress = true;
2366
- } else if (label !== lastLabel) {
2367
- ctx.err(`${slug}: ${label}\n`);
2368
- }
2369
- lastLabel = label;
2370
- }
2371
- const waitMs = transientDeployStatusWaitMs(error, ctx.pollIntervalMs);
2372
- const remaining = ctx.timeoutMs - (Date.now() - startedAt);
2373
- await sleep(Math.max(0, Math.min(waitMs, remaining)));
2374
- continue;
2375
- }
2376
- finishProgressLine();
2377
- throw error;
2378
- }
2379
- report(detail);
2380
- if (terminal.has(detail.status)) {
2381
- finishProgressLine();
2382
- return detail;
2383
- }
2384
- if (Date.now() - startedAt >= ctx.timeoutMs) {
2385
- finishProgressLine();
2386
- const last = deployProgressLabel(detail);
2387
- throw new DeployApiError(
2388
- `Timed out after ${Math.round(ctx.timeoutMs / 1000)}s waiting for ${slug} (last: ${last}). It may still converge — check \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.`,
2389
- 0,
2390
- );
2391
- }
2392
- await sleep(ctx.pollIntervalMs);
2393
- }
2394
- }
2395
-
2396
- /** Load-shed / rate-limit on status GET — the deploy itself may still be baking. */
2397
- function isTransientDeployStatusError(error: unknown): error is DeployApiError {
2398
- return error instanceof DeployApiError && (error.status === 503 || error.status === 429);
2399
- }
2400
-
2401
- function transientDeployStatusWaitMs(error: DeployApiError, pollIntervalMs: number): number {
2402
- if (
2403
- error.status === 429 &&
2404
- error.retryAfterSeconds !== undefined &&
2405
- error.retryAfterSeconds > 0
2406
- ) {
2407
- return Math.max(pollIntervalMs, error.retryAfterSeconds * 1000);
2408
- }
2409
- return pollIntervalMs;
2410
- }
2411
-
2412
- /**
2413
- * Wait until GET 404s (the delete reconciler purged the record). Treats
2414
- * other errors as failures. Throws DeployApiError on timeout.
2415
- */
2416
- async function pollUntilGone(
2417
- ctx: DeployCliContext,
2418
- slug: string,
2419
- getDetail: () => Promise<{
2420
- status: string;
2421
- progress?: DeploymentProgress;
2422
- statusMessage?: string | null;
2423
- }>,
2424
- ): Promise<void> {
2425
- const startedAt = Date.now();
2426
- const tty = process.stderr.isTTY === true;
2427
- let sawProgress = false;
2428
- const report = (label: string): void => {
2429
- if (ctx.json) {
2430
- return;
2431
- }
2432
- const elapsed = Math.round((Date.now() - startedAt) / 1000);
2433
- if (tty) {
2434
- ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
2435
- sawProgress = true;
2436
- }
2437
- };
2438
- const finishProgressLine = (): void => {
2439
- if (tty && sawProgress) {
2440
- ctx.err("\n");
2441
- }
2442
- };
2443
-
2444
- for (;;) {
2445
- try {
2446
- const detail = await getDetail();
2447
- report(deployProgressLabel(detail));
2448
- } catch (error: unknown) {
2449
- if (error instanceof DeployApiError && error.status === 404) {
2450
- finishProgressLine();
2451
- return;
2452
- }
2453
- finishProgressLine();
2454
- throw error;
2455
- }
2456
- if (Date.now() - startedAt >= ctx.timeoutMs) {
2457
- finishProgressLine();
2458
- throw new DeployApiError(
2459
- `Timed out after ${Math.round(ctx.timeoutMs / 1000)}s waiting for ${slug} to disappear. It may still be deleting — check \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.`,
2460
- 0,
2461
- );
2462
- }
2463
- await sleep(ctx.pollIntervalMs);
2464
- }
2465
- }
2466
-
2467
- function aliasTokenBlock(aliasToken: string): string {
2468
- return (
2469
- `\n Alias token (shown ONCE — save it now, it cannot be retrieved again):\n\n` +
2470
- ` ${aliasToken}\n\n` +
2471
- ` Send it as the X-Agent-Alias-Token header on requests to the alias URL,\n` +
2472
- ` or paste it into the hosted playground (<alias>/playground) sign-in gate.\n` +
2473
- ` Lost tokens: \`${CLI} rotate-token <slug>\` mints a replacement.\n\n`
2474
- );
2475
- }
2476
-
2477
- /**
2478
- * `: <reason>` suffix for a deploy that ended in a non-running state, or
2479
- * empty when the control plane recorded no reason.
2480
- */
2481
- function deployFailureDetail(lastError: string | null | undefined): string {
2482
- if (lastError === null || lastError === undefined || lastError.trim() === "") {
2483
- return "";
2484
- }
2485
- return `: ${sanitizeCustomerError(lastError.trim())}`;
2486
- }
2487
-
2488
- export function renderApiError(
2489
- error: unknown,
2490
- err: (text: string) => void,
2491
- context?: { multiTenant?: boolean; teamId?: number },
2492
- ): number {
2493
- if (error instanceof DeployApiError) {
2494
- err(`${error.message}\n`);
2495
- if (error.status === 409 && error.message.includes("application multiTenant does not match")) {
2496
- if (context?.multiTenant === true) {
2497
- err("This slug is a single-tenant application. Redeploy it without --multi-tenant.\n");
2498
- } else if (context?.teamId === MULTI_TENANT_HTTP_DEPLOY_TEAM_ID) {
2499
- err(
2500
- "This slug is a Cursor-managed (multi-tenant) product. Redeploy it with --multi-tenant.\n",
2501
- );
2502
- }
2503
- }
2504
- return 1;
2505
- }
2506
- throw error;
2507
- }
2508
-
2509
- export function renderTable(header: string[], rows: string[][]): string {
2510
- const widths = header.map((h, i) =>
2511
- Math.max(h.length, ...rows.map(row => (row[i] ?? "").length)),
2512
- );
2513
- const renderRow = (row: string[]): string =>
2514
- row
2515
- .map((cell, i) => cell.padEnd(widths[i] ?? 0))
2516
- .join(" ")
2517
- .trimEnd();
2518
- return [renderRow(header), ...rows.map(renderRow)].join("\n");
2519
- }