@cursor/bdk 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.js +3 -5
- package/dist/bin/agent-serve.js +803 -812
- package/dist/channels/bitbucket/api.js +305 -321
- package/dist/channels/bitbucket/binding.js +97 -61
- package/dist/channels/bitbucket/bitbucket-channel.d.ts +15 -1
- package/dist/channels/bitbucket/bitbucket-channel.d.ts.map +1 -1
- package/dist/channels/bitbucket/bitbucket-channel.js +95 -42
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts +11 -0
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts.map +1 -1
- package/dist/channels/bitbucket/bitbucket-webhook.js +214 -204
- package/dist/channels/bitbucket/checks.js +9 -21
- package/dist/channels/bitbucket/cursor-account.js +1 -1
- package/dist/channels/bitbucket/defaults.js +14 -4
- package/dist/channels/bitbucket/events.js +1 -2
- package/dist/channels/bitbucket/index.d.ts +6 -2
- package/dist/channels/bitbucket/index.d.ts.map +1 -1
- package/dist/channels/bitbucket/index.js +6 -2
- package/dist/channels/bitbucket/replay.js +80 -56
- package/dist/channels/bitbucket/state.js +16 -7
- package/dist/channels/bitbucket/types.d.ts +11 -2
- package/dist/channels/bitbucket/types.d.ts.map +1 -1
- package/dist/channels/checks.js +18 -28
- package/dist/channels/deployments/deployments-channel.js +26 -34
- package/dist/channels/github/api.js +216 -210
- package/dist/channels/github/auth.d.ts +5 -1
- package/dist/channels/github/auth.d.ts.map +1 -1
- package/dist/channels/github/auth.js +157 -174
- package/dist/channels/github/binding.js +48 -39
- package/dist/channels/github/checks.js +9 -14
- package/dist/channels/github/cursor-account.js +6 -11
- package/dist/channels/github/defaults.js +105 -130
- package/dist/channels/github/forward.js +13 -17
- package/dist/channels/github/github-channel.d.ts +43 -2
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +1228 -958
- package/dist/channels/github/progress.js +145 -146
- package/dist/channels/github/replay.js +30 -39
- package/dist/channels/github/state.js +40 -16
- package/dist/channels/github/types.d.ts +30 -6
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/gitlab/api.js +100 -114
- package/dist/channels/gitlab/binding.js +52 -33
- package/dist/channels/gitlab/checks.js +28 -29
- package/dist/channels/gitlab/cursor-account.js +1 -1
- package/dist/channels/gitlab/defaults.js +9 -3
- package/dist/channels/gitlab/gitlab-channel.d.ts +16 -1
- package/dist/channels/gitlab/gitlab-channel.d.ts.map +1 -1
- package/dist/channels/gitlab/gitlab-channel.js +95 -42
- package/dist/channels/gitlab/gitlab-webhook.d.ts +10 -1
- package/dist/channels/gitlab/gitlab-webhook.d.ts.map +1 -1
- package/dist/channels/gitlab/gitlab-webhook.js +282 -249
- package/dist/channels/gitlab/index.d.ts +6 -2
- package/dist/channels/gitlab/index.d.ts.map +1 -1
- package/dist/channels/gitlab/index.js +6 -2
- package/dist/channels/gitlab/replay.js +50 -27
- package/dist/channels/gitlab/state.js +14 -6
- package/dist/channels/gitlab/types.d.ts +13 -2
- package/dist/channels/gitlab/types.d.ts.map +1 -1
- package/dist/channels/origin/api.js +100 -116
- package/dist/channels/origin/binding.js +15 -16
- package/dist/channels/origin/checks.js +30 -28
- package/dist/channels/origin/defaults.js +50 -65
- package/dist/channels/origin/origin-channel.js +266 -272
- package/dist/channels/origin/origin-webhook.js +70 -88
- package/dist/channels/origin/reactions.js +9 -21
- package/dist/channels/origin/state.js +13 -5
- package/dist/channels/slack/agentic-delivery.js +426 -478
- package/dist/channels/slack/agentic-posted.js +9 -10
- package/dist/channels/slack/agentic-signals.js +2 -3
- package/dist/channels/slack/api.js +417 -427
- package/dist/channels/slack/approvals.js +1 -1
- package/dist/channels/slack/assistant.js +37 -47
- package/dist/channels/slack/attachments.js +188 -206
- package/dist/channels/slack/auth.js +8 -6
- package/dist/channels/slack/bot-mentions.js +31 -45
- package/dist/channels/slack/channel-watch.js +42 -48
- package/dist/channels/slack/cli-project.js +112 -137
- package/dist/channels/slack/code-fences.js +3 -5
- package/dist/channels/slack/constants.js +2 -2
- package/dist/channels/slack/credentials.js +23 -38
- package/dist/channels/slack/defaults.js +292 -331
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +533 -460
- package/dist/channels/slack/doctor.js +200 -214
- package/dist/channels/slack/eval-directive.js +79 -96
- package/dist/channels/slack/event-id-dedupe.js +1 -1
- package/dist/channels/slack/external-policy.js +26 -39
- package/dist/channels/slack/footer.js +28 -41
- package/dist/channels/slack/inbound.d.ts +1 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +50 -31
- package/dist/channels/slack/init.d.ts +1 -5
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +42 -59
- package/dist/channels/slack/interactive.d.ts +9 -3
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +160 -162
- package/dist/channels/slack/links.js +1 -2
- package/dist/channels/slack/live-delivery.js +172 -190
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +44 -23
- package/dist/channels/slack/message-body.js +2 -2
- package/dist/channels/slack/nudge-carrier.js +30 -42
- package/dist/channels/slack/nudge-store.js +164 -191
- package/dist/channels/slack/paint-start.js +66 -81
- package/dist/channels/slack/placeholder.js +123 -152
- package/dist/channels/slack/pr-ref.js +1 -1
- package/dist/channels/slack/progress-delivery.js +65 -78
- package/dist/channels/slack/reasoning-card.js +36 -49
- package/dist/channels/slack/recording-sink.js +12 -24
- package/dist/channels/slack/reply-options.js +7 -12
- package/dist/channels/slack/self-identity.js +27 -35
- package/dist/channels/slack/sender-name.d.ts +12 -3
- package/dist/channels/slack/sender-name.d.ts.map +1 -1
- package/dist/channels/slack/sender-name.js +57 -61
- package/dist/channels/slack/setup.js +2 -4
- package/dist/channels/slack/slack-channel.d.ts +6 -4
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +441 -308
- package/dist/channels/slack/socket-mode.js +38 -52
- package/dist/channels/slack/thread-context.js +143 -160
- package/dist/channels/slack/tool-approval-events.js +149 -166
- package/dist/channels/slack/tool-cards.js +6 -11
- package/dist/channels/slack/tool-chip.js +1 -2
- package/dist/channels/slack/types.d.ts +15 -17
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/channels/slack/upload.js +84 -97
- package/dist/channels/slack/user-info.js +27 -41
- package/dist/channels.js +9 -20
- package/dist/dashboard-session-url.js +3 -4
- package/dist/docs/404.html +2 -2
- package/dist/docs/assets/{app.BNhEdGa8.js → app.DXI7bwox.js} +4 -4
- package/dist/docs/assets/{building-with-agents.md.BPjPFxFg.js → building-with-agents.md.Df87rAe1.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CtLtpWcx.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DIdUV1Ph.js → VPLocalSearchBox.BrbO5mEe.js} +1 -1
- package/dist/docs/assets/chunks/{arc.lELI4_SR.js → arc.BOG1yms8.js} +1 -1
- package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.iCMdvuoM.js → architectureDiagram-Q4EWVU46.C0hsMZnZ.js} +1 -1
- package/dist/docs/assets/chunks/{baseUniq.C1-nDJBf.js → baseUniq.H044KjKs.js} +1 -1
- package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.ClemzBox.js → blockDiagram-DXYQGD6D.BsRW6GoB.js} +1 -1
- package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.BqTak6cR.js → c4Diagram-AHTNJAMY.B9HV5PFR.js} +1 -1
- package/dist/docs/assets/chunks/channel.CBwrU9rP.js +1 -0
- package/dist/docs/assets/chunks/{chunk-4BX2VUAB.CUS3qs_R.js → chunk-4BX2VUAB.CPoT_ajl.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-4TB4RGXK.dz5fdHcl.js → chunk-4TB4RGXK.DZBwWuCL.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-55IACEB6.D6BEBmve.js → chunk-55IACEB6.CZ3yTiyq.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-EDXVE4YY.B5uK4ID9.js → chunk-EDXVE4YY.6DfvHSd8.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-FMBD7UC4.PgOJgNQs.js → chunk-FMBD7UC4.CpBj8HfK.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-OYMX7WX6.CvkTrCii.js → chunk-OYMX7WX6.BdKo8YqS.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-QZHKN3VN.BnSfKYsB.js → chunk-QZHKN3VN.BnXNyxJx.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-YZCP3GAM.MZH6v5UC.js → chunk-YZCP3GAM.Cg1OcF8G.js} +1 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.CbC_mrvB.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.CbC_mrvB.js +1 -0
- package/dist/docs/assets/chunks/clone.BTvFyY1a.js +1 -0
- package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.BBbw_YZi.js → cose-bilkent-S5V4N54A.1SP8al9c.js} +1 -1
- package/dist/docs/assets/chunks/{dagre-KV5264BT.CNJB2l7E.js → dagre-KV5264BT.DS1L6hJt.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-5BDNPKRD.BQzlnFfq.js → diagram-5BDNPKRD.ChsausbG.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.DkgqqZMz.js → diagram-G4DWMVQ6.3XsptNWj.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-MMDJMWI5.DIT8L7H_.js → diagram-MMDJMWI5.DPA9Dk_P.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-TYMM5635.3xLPZWGJ.js → diagram-TYMM5635.BVswXWrB.js} +1 -1
- package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.5_IHz8jF.js → erDiagram-SMLLAGMA.BSak5OSP.js} +1 -1
- package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.BGUCXz8A.js → flowDiagram-DWJPFMVM.CQgN4TTz.js} +1 -1
- package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.DbfFavS1.js → ganttDiagram-T4ZO3ILL.GrT0kA4a.js} +1 -1
- package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.D2yKdeNW.js → gitGraphDiagram-UUTBAWPF.CdS7Ucll.js} +1 -1
- package/dist/docs/assets/chunks/{graph.CSCxwHba.js → graph.BVqKRmRo.js} +1 -1
- package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.FDeb6xHY.js → infoDiagram-42DDH7IO.C3C9FRto.js} +1 -1
- package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DbeYs9VL.js → ishikawaDiagram-UXIWVN3A.2FuAh_Zj.js} +1 -1
- package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.CWjKINpx.js → journeyDiagram-VCZTEJTY.CaHMfK4b.js} +1 -1
- package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.B4FrAlwz.js → kanban-definition-6JOO6SKY.CjhIUwcA.js} +1 -1
- package/dist/docs/assets/chunks/{layout.DFGxQ9ab.js → layout.CGU3gfSf.js} +1 -1
- package/dist/docs/assets/chunks/{linear.BmjZ-G59.js → linear.DaihpW_n.js} +1 -1
- package/dist/docs/assets/chunks/{min.DZSxulMW.js → min.BS-j8kHQ.js} +1 -1
- package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.CHLi3-aN.js → mindmap-definition-QFDTVHPH.BVJ1Bbnk.js} +1 -1
- package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.DteG-CkU.js → pieDiagram-DEJITSTG.Cz1WIv9r.js} +1 -1
- package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.D458ipN3.js → quadrantDiagram-34T5L4WZ.DKLFxcjy.js} +1 -1
- package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.DcFgUxst.js → requirementDiagram-MS252O5E.BHegiK7x.js} +1 -1
- package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.BwQAMmSA.js → sankeyDiagram-XADWPNL6.DYkY6yPc.js} +1 -1
- package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.B_Xdsrh9.js → sequenceDiagram-FGHM5R23.DYQ1QUrI.js} +1 -1
- package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.CdfDW6-C.js → stateDiagram-FHFEXIEX.Iru8BsFo.js} +1 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.BYXl4LW6.js +1 -0
- package/dist/docs/assets/chunks/{theme.sfdL3srp.js → theme.DaplWQhQ.js} +2 -2
- package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.56-4hXaC.js → timeline-definition-GMOUNBTQ.BwFA8hiX.js} +1 -1
- package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.CvZSQBO7.js → vennDiagram-DHZGUBPP.CZ60W1C_.js} +1 -1
- package/dist/docs/assets/chunks/{wardley-RL74JXVD.C9qc8k39.js → wardley-RL74JXVD.iXazD3Z7.js} +1 -1
- package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.D9s0nihi.js → wardleyDiagram-NUSXRM2D.B69D2r8-.js} +1 -1
- package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.BVJSpYrC.js → xychartDiagram-5P7HB3ND.CLPZEIU9.js} +1 -1
- package/dist/docs/assets/{deployment.md.BdqTiGdc.js → deployment.md.BYuEcDBv.js} +2 -2
- package/dist/docs/assets/{guides_bitbucket.md.BB6fqD-M.js → guides_bitbucket.md.CKUayYtD.js} +3 -3
- package/dist/docs/assets/{guides_bitbucket.md.BB6fqD-M.lean.js → guides_bitbucket.md.CKUayYtD.lean.js} +1 -1
- package/dist/docs/assets/{guides_github.md.Cr1Xlf-m.js → guides_github.md.TuNiZAOQ.js} +4 -4
- package/dist/docs/assets/{guides_github.md.Cr1Xlf-m.lean.js → guides_github.md.TuNiZAOQ.lean.js} +1 -1
- package/dist/docs/assets/{guides_gitlab.md.CMpmFi4M.js → guides_gitlab.md.CtNI4fSH.js} +4 -4
- package/dist/docs/assets/{guides_gitlab.md.CMpmFi4M.lean.js → guides_gitlab.md.CtNI4fSH.lean.js} +1 -1
- package/dist/docs/assets/guides_migrate.md.sCAvvlC6.js +7 -0
- package/dist/docs/assets/guides_migrate.md.sCAvvlC6.lean.js +1 -0
- package/dist/docs/assets/{reference_cli.md.GwugN3n-.js → reference_cli.md.dYCAL2lB.js} +4 -3
- package/dist/docs/assets/reference_cli.md.dYCAL2lB.lean.js +1 -0
- package/dist/docs/assets/troubleshooting.md.x-GBa7wN.js +1 -0
- package/dist/docs/assets/{troubleshooting.md.Bscj6ODe.lean.js → troubleshooting.md.x-GBa7wN.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +37 -37
- package/dist/docs/building-with-agents.md +2 -0
- package/dist/docs/deployment.html +38 -38
- package/dist/docs/deployment.md +7 -0
- package/dist/docs/evals.html +36 -36
- package/dist/docs/guides/agent-to-agent.html +35 -35
- package/dist/docs/guides/bitbucket.html +38 -38
- package/dist/docs/guides/bitbucket.md +9 -1
- package/dist/docs/guides/cloud-agents.html +35 -35
- package/dist/docs/guides/convert-automation.html +35 -35
- package/dist/docs/guides/github.html +39 -39
- package/dist/docs/guides/github.md +20 -4
- package/dist/docs/guides/gitlab.html +38 -38
- package/dist/docs/guides/gitlab.md +9 -1
- package/dist/docs/guides/grokbot-agents.html +35 -35
- package/dist/docs/guides/hooks.html +35 -35
- package/dist/docs/guides/improve.html +35 -35
- package/dist/docs/guides/jev.html +35 -35
- package/dist/docs/guides/mcp-oauth.html +35 -35
- package/dist/docs/guides/migrate.html +66 -0
- package/dist/docs/guides/migrate.md +122 -0
- package/dist/docs/guides/opentelemetry.html +35 -35
- package/dist/docs/guides/slack.html +35 -35
- package/dist/docs/guides/webhooks.html +35 -35
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +35 -35
- package/dist/docs/index.html +35 -35
- package/dist/docs/llms-full.txt +214 -6
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +35 -35
- package/dist/docs/reference/agent-config.html +35 -35
- package/dist/docs/reference/artifacts.html +35 -35
- package/dist/docs/reference/channels.html +35 -35
- package/dist/docs/reference/cli.html +39 -38
- package/dist/docs/reference/cli.md +39 -0
- package/dist/docs/reference/connections.html +35 -35
- package/dist/docs/reference/evals.html +35 -35
- package/dist/docs/reference/extensions.html +35 -35
- package/dist/docs/reference/hooks.html +35 -35
- package/dist/docs/reference/http-api.html +35 -35
- package/dist/docs/reference/instructions.html +35 -35
- package/dist/docs/reference/playground.html +35 -35
- package/dist/docs/reference/project-layout.html +35 -35
- package/dist/docs/reference/prompt.html +35 -35
- package/dist/docs/reference/schedules.html +35 -35
- package/dist/docs/reference/sessions.html +35 -35
- package/dist/docs/reference/skills.html +35 -35
- package/dist/docs/reference/subagents.html +35 -35
- package/dist/docs/reference/tools.html +35 -35
- package/dist/docs/templates/agentic-owners.html +35 -35
- package/dist/docs/templates/grokbot-agents.html +35 -35
- package/dist/docs/templates/security-reviewer.html +35 -35
- package/dist/docs/templates/thermo-quality-review.html +35 -35
- package/dist/docs/templates/thermo-review.html +35 -35
- package/dist/docs/templates/triage.html +35 -35
- package/dist/docs/troubleshooting.html +36 -36
- package/dist/docs/troubleshooting.md +1 -0
- package/dist/evals/assertions.js +20 -34
- package/dist/evals/expect.js +3 -6
- package/dist/evals/judge.js +5 -8
- package/dist/evals/loaders.js +45 -62
- package/dist/evals/matchers.js +2 -3
- package/dist/evals/reporters.js +43 -67
- package/dist/evals/results.js +1 -1
- package/dist/evals/run-facts.js +5 -6
- package/dist/evals.js +27 -15
- package/dist/extensions/cursor-cloud-agents/extension.js +4 -1
- package/dist/extensions/cursor-cloud-agents/lib/backend.js +100 -126
- package/dist/extensions/cursor-cloud-agents/lib/fleet.js +22 -38
- package/dist/extensions/cursor-cloud-agents/lib/repos.js +8 -10
- package/dist/extensions/cursor-cloud-agents/lib/transcript.js +2 -3
- package/dist/extensions/cursor-cloud-agents/lib/watch.js +52 -66
- package/dist/extensions/cursor-cloud-agents/reminders/cloud-agent-stop-watch.js +29 -40
- package/dist/extensions/cursor-cloud-agents/tools/cancel.js +6 -17
- package/dist/extensions/cursor-cloud-agents/tools/dump.js +25 -36
- package/dist/extensions/cursor-cloud-agents/tools/get.js +3 -14
- package/dist/extensions/cursor-cloud-agents/tools/launch.js +13 -21
- package/dist/extensions/cursor-cloud-agents/tools/list.js +8 -19
- package/dist/extensions/cursor-cloud-agents/tools/reply.js +5 -16
- package/dist/extensions/cursor-grokbot-agents/extension.js +4 -1
- package/dist/extensions/cursor-grokbot-agents/lib/agents.js +3 -3
- package/dist/extensions/cursor-grokbot-agents/lib/backend.js +53 -73
- package/dist/extensions/cursor-grokbot-agents/lib/contacts.js +10 -23
- package/dist/extensions/cursor-grokbot-agents/lib/conversation.js +112 -132
- package/dist/extensions/cursor-grokbot-agents/lib/lock.js +1 -2
- package/dist/extensions/cursor-grokbot-agents/tools/ask.js +5 -16
- package/dist/extensions/cursor-grokbot-agents/tools/check.js +4 -15
- package/dist/extensions/cursor-grokbot-agents/tools/interrupt.js +2 -13
- package/dist/extensions/cursor-grokbot-agents/tools/list.js +11 -23
- package/dist/extensions/jev/extension.js +3 -3
- package/dist/extensions/jev/lib/evaluate.js +43 -60
- package/dist/extensions/jev/tools/evaluate.js +6 -17
- package/dist/extensions/slack-communication/tools/post-file.js +1 -1
- package/dist/extensions.js +3 -5
- package/dist/files-backends/cursor-hosted.js +504 -523
- package/dist/files-backends/local-fs.js +72 -134
- package/dist/files.js +3 -3
- package/dist/filesystem.js +562 -861
- package/dist/index.js +16 -35
- package/dist/internal/advertise-tools.js +74 -86
- package/dist/internal/artifacts-store.js +298 -361
- package/dist/internal/as-of.js +2 -4
- package/dist/internal/authored-alias-hooks.js +6 -4
- package/dist/internal/authored-loaders.js +37 -52
- package/dist/internal/bounded-int.js +1 -2
- package/dist/internal/bounded.js +13 -24
- package/dist/internal/brand.js +1 -1
- package/dist/internal/browser-origin-guard.js +4 -4
- package/dist/internal/builtin-tools/artifacts.js +15 -27
- package/dist/internal/builtin-tools/index.js +2 -3
- package/dist/internal/builtin-tools/reminders.js +33 -52
- package/dist/internal/channel-architecture.d.ts +21 -0
- package/dist/internal/channel-architecture.d.ts.map +1 -0
- package/dist/internal/channel-architecture.js +41 -0
- package/dist/internal/channel-dispatch.js +3 -4
- package/dist/internal/channel-route-dispatch.js +40 -51
- package/dist/internal/channel-state.js +1 -1
- package/dist/internal/chat-client.js +319 -340
- package/dist/internal/cli-ax.js +1262 -1294
- package/dist/internal/cli-bitbucket.js +244 -272
- package/dist/internal/cli-convert-automation.js +137 -151
- package/dist/internal/cli-cursor.js +126 -147
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +1457 -1507
- package/dist/internal/cli-docs.js +130 -147
- package/dist/internal/cli-github.js +437 -476
- package/dist/internal/cli-gitlab.js +259 -288
- package/dist/internal/cli-images.js +266 -334
- package/dist/internal/cli-manifest.js +25 -37
- package/dist/internal/cli-mcp-oauth.js +228 -247
- package/dist/internal/cli-mcp.js +358 -383
- package/dist/internal/cli-migrate.js +73 -90
- package/dist/internal/cli-skills.js +35 -47
- package/dist/internal/cli-slack-manual.d.ts +0 -1
- package/dist/internal/cli-slack-manual.d.ts.map +1 -1
- package/dist/internal/cli-slack-manual.js +175 -204
- package/dist/internal/cli-slack.d.ts +1 -1
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +416 -456
- package/dist/internal/cloud-merge.js +7 -3
- package/dist/internal/cloud-turn-cost.js +8 -1
- package/dist/internal/connection-env.js +5 -6
- package/dist/internal/continuation-channel.js +10 -7
- package/dist/internal/continuation-identity.js +85 -67
- package/dist/internal/conversation-mirror.js +112 -131
- package/dist/internal/convert-automation/convert-workflow.js +21 -31
- package/dist/internal/cron.js +3 -3
- package/dist/internal/cursor/account-mcp.js +147 -146
- package/dist/internal/cursor/backend-client.js +290 -320
- package/dist/internal/cursor/bound-secret-file.js +3 -3
- package/dist/internal/cursor/credentials.js +127 -142
- package/dist/internal/cursor/cursor-api-transport.js +4 -8
- package/dist/internal/cursor/github-credentials.js +91 -108
- package/dist/internal/cursor/hosted-bound-secrets.js +3 -4
- package/dist/internal/cursor/hosted-deployment-secrets.js +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +5 -9
- package/dist/internal/cursor/session-token.js +36 -48
- package/dist/internal/cursor/store-api-client.js +77 -92
- package/dist/internal/cursor-account-mcp-auth.js +132 -149
- package/dist/internal/cursor-agent-template.js +1 -1
- package/dist/internal/cursor-deployment-relay.js +33 -54
- package/dist/internal/cursor-event-relay.js +65 -59
- package/dist/internal/cursor-relay-core.js +258 -286
- package/dist/internal/deferred-channel-session.js +3 -12
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +413 -475
- package/dist/internal/deploy-manifest.d.ts +17 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +71 -21
- package/dist/internal/deploy-source.js +31 -43
- package/dist/internal/discovery/agent-config.js +45 -59
- package/dist/internal/discovery/agent.js +276 -280
- package/dist/internal/discovery/channels.js +67 -80
- package/dist/internal/discovery/connections.js +286 -296
- package/dist/internal/discovery/context.js +2 -13
- package/dist/internal/discovery/cursor-plugin.js +223 -259
- package/dist/internal/discovery/extension-overlay.js +104 -131
- package/dist/internal/discovery/extensions.js +276 -281
- package/dist/internal/discovery/fs.js +56 -79
- package/dist/internal/discovery/hooks.js +39 -50
- package/dist/internal/discovery/info.js +17 -19
- package/dist/internal/discovery/instructions.js +58 -71
- package/dist/internal/discovery/modules.js +45 -58
- package/dist/internal/discovery/project.d.ts.map +1 -1
- package/dist/internal/discovery/project.js +192 -215
- package/dist/internal/discovery/reminders.js +41 -52
- package/dist/internal/discovery/runtime.js +1 -1
- package/dist/internal/discovery/schedules.js +71 -82
- package/dist/internal/discovery/seed-files.js +21 -32
- package/dist/internal/discovery/services.js +74 -96
- package/dist/internal/discovery/skills.js +77 -94
- package/dist/internal/discovery/subagent.js +79 -95
- package/dist/internal/discovery/tools.js +12 -23
- package/dist/internal/distribution.js +2 -2
- package/dist/internal/docs-site.js +30 -45
- package/dist/internal/duration.js +1 -1
- package/dist/internal/env-file.js +48 -64
- package/dist/internal/eval-judge-model.js +17 -28
- package/dist/internal/eval-run-store.js +265 -254
- package/dist/internal/eval-runner.d.ts +8 -0
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +406 -408
- package/dist/internal/eval-transport.d.ts +24 -0
- package/dist/internal/eval-transport.d.ts.map +1 -0
- package/dist/internal/eval-transport.js +22 -0
- package/dist/internal/evals-client.js +242 -246
- package/dist/internal/event-log.js +72 -120
- package/dist/internal/event-mapper.js +30 -31
- package/dist/internal/factory-resource-channel.js +16 -30
- package/dist/internal/filesystem/errors.js +6 -6
- package/dist/internal/filesystem/format.js +10 -13
- package/dist/internal/filesystem/merge.js +52 -94
- package/dist/internal/filesystem/nearest.js +15 -27
- package/dist/internal/filesystem/scratch.js +65 -85
- package/dist/internal/filesystem/tools.js +221 -241
- package/dist/internal/filesystem/walk.js +76 -143
- package/dist/internal/framework-file-storage.js +118 -139
- package/dist/internal/framework-storage-selection.js +21 -13
- package/dist/internal/frontmatter.js +4 -6
- package/dist/internal/github-fanout.js +50 -64
- package/dist/internal/guest-network.js +96 -109
- package/dist/internal/handleAgentServeTrigger.js +121 -126
- package/dist/internal/host-files.js +73 -88
- package/dist/internal/host-kv.js +21 -36
- package/dist/internal/host-otel.js +22 -19
- package/dist/internal/host-platforms.js +49 -63
- package/dist/internal/hosted-catch-protocol.d.ts +1 -0
- package/dist/internal/hosted-catch-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-catch-protocol.js +1 -0
- package/dist/internal/hosted-catch.d.ts +5 -3
- package/dist/internal/hosted-catch.d.ts.map +1 -1
- package/dist/internal/hosted-catch.js +84 -93
- package/dist/internal/hosted-delivery-protocol.js +32 -8
- package/dist/internal/hosted-delivery.d.ts +13 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +614 -584
- package/dist/internal/hosted-eval-cases.d.ts +21 -0
- package/dist/internal/hosted-eval-cases.d.ts.map +1 -0
- package/dist/internal/hosted-eval-cases.js +162 -0
- package/dist/internal/hosted-execution-diag.js +38 -30
- package/dist/internal/hosted-execution-flush.js +75 -66
- package/dist/internal/hosted-execution-latency.js +16 -8
- package/dist/internal/hosted-managed-automation.js +2 -2
- package/dist/internal/hosting.js +2 -3
- package/dist/internal/http-channel-define.js +4 -1
- package/dist/internal/http-channel.js +101 -112
- package/dist/internal/http-control-plane-session.d.ts +18 -1
- package/dist/internal/http-control-plane-session.d.ts.map +1 -1
- package/dist/internal/http-control-plane-session.js +66 -32
- package/dist/internal/incoming-headers-to-fetch.js +1 -2
- package/dist/internal/init-interview.js +73 -93
- package/dist/internal/init-project.js +182 -214
- package/dist/internal/init-scaffold.js +10 -21
- package/dist/internal/inline-stdio-mcp.js +80 -86
- package/dist/internal/install-cursor-skills.js +231 -273
- package/dist/internal/legacy-command-guard.js +18 -31
- package/dist/internal/listen-port.js +34 -45
- package/dist/internal/local-control-plane.js +293 -307
- package/dist/internal/local-env.js +1 -1
- package/dist/internal/local-ingress.js +15 -9
- package/dist/internal/log-ring.js +1 -2
- package/dist/internal/logs-client.js +207 -227
- package/dist/internal/map-pool.js +24 -38
- package/dist/internal/mcp-endpoint.js +204 -180
- package/dist/internal/mcp-host.js +165 -168
- package/dist/internal/mcp-oauth-on-demand.js +170 -207
- package/dist/internal/mcp-oauth-persist.js +15 -29
- package/dist/internal/mcp-oauth.js +209 -266
- package/dist/internal/named-webhook-secret.d.ts +39 -0
- package/dist/internal/named-webhook-secret.d.ts.map +1 -0
- package/dist/internal/named-webhook-secret.js +47 -0
- package/dist/internal/open-browser.js +2 -3
- package/dist/internal/otel/collector.js +104 -68
- package/dist/internal/otel/config.js +24 -37
- package/dist/internal/otel/http.js +5 -7
- package/dist/internal/otel/mcp.js +3 -12
- package/dist/internal/otel/runtime.js +69 -83
- package/dist/internal/package-inline-stdio.js +57 -65
- package/dist/internal/peer-connections.js +9 -5
- package/dist/internal/platform-schedule-sync.js +39 -54
- package/dist/internal/platform-timers.js +176 -210
- package/dist/internal/playground/static.js +1 -2
- package/dist/internal/playground/toolchain.js +37 -53
- package/dist/internal/playground-proxy.js +230 -270
- package/dist/internal/process-memory-telemetry.js +33 -51
- package/dist/internal/prompt-context.js +3 -6
- package/dist/internal/reminder-control-plane-protocol.js +46 -13
- package/dist/internal/reminder-runner.js +770 -785
- package/dist/internal/reminder-schedule.js +1 -1
- package/dist/internal/reminder-store.js +57 -78
- package/dist/internal/request-error-status.js +1 -3
- package/dist/internal/request-headers.js +2 -2
- package/dist/internal/resolve-prod-target.js +119 -135
- package/dist/internal/resolved-connections.js +16 -6
- package/dist/internal/run-client.js +221 -229
- package/dist/internal/runtime-dispatch-runner.js +18 -43
- package/dist/internal/schedule-runner.js +49 -58
- package/dist/internal/scm/pr-url.js +7 -6
- package/dist/internal/sdk-runner.js +388 -363
- package/dist/internal/serve-restart.js +26 -39
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +1283 -1244
- package/dist/internal/session-cost.js +7 -9
- package/dist/internal/session-engine.d.ts +3 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +2801 -2777
- package/dist/internal/session-links.js +12 -16
- package/dist/internal/session-run-log.js +134 -82
- package/dist/internal/session-store.js +133 -164
- package/dist/internal/sessions-client.js +97 -118
- package/dist/internal/skill-catalog.js +18 -23
- package/dist/internal/skills-sync.js +16 -30
- package/dist/internal/slack-provision-client.js +90 -116
- package/dist/internal/start-log-store.js +48 -67
- package/dist/internal/steering.js +1 -2
- package/dist/internal/storage-coordinator.js +160 -196
- package/dist/internal/store-mirror.js +16 -30
- package/dist/internal/stream-progress.js +6 -8
- package/dist/internal/terminal-style.js +3 -4
- package/dist/internal/testing/scm-continuation-fixtures.js +6 -2
- package/dist/internal/testing/slack-continuation-fixtures.d.ts +26 -0
- package/dist/internal/testing/slack-continuation-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/slack-continuation-fixtures.js +136 -0
- package/dist/internal/tool-catalog.js +11 -5
- package/dist/internal/tool-result.js +1 -1
- package/dist/internal/trajectory.js +19 -22
- package/dist/internal/turn-cost.js +52 -62
- package/dist/internal/turn-governor.js +5 -8
- package/dist/internal/turn-lease-store.js +85 -105
- package/dist/internal/turn-outbox.js +28 -43
- package/dist/internal/update-check.js +231 -259
- package/dist/internal/v2-hosting.js +1 -2
- package/dist/internal/workspace-sync.js +25 -41
- package/dist/internal/workspace.js +111 -139
- package/dist/kv/memory-kv.js +27 -51
- package/dist/memory.js +106 -148
- package/dist/otel.js +5 -8
- package/dist/playground/assets/{index-Co4CUVTQ.js → index-Be0-1hw2.js} +45 -45
- package/dist/playground/assets/index-Clz6uHj9.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/prompt.js +2 -4
- package/dist/storage-backends/cursor-hosted-v2.js +53 -83
- package/dist/storage-backends/cursor-hosted.js +105 -145
- package/dist/storage-backends/file-kv.js +100 -128
- package/dist/storage-protocol.js +3 -5
- package/dist/storage.js +17 -26
- package/dist/types.d.ts +68 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +20 -18
- package/docs/building-with-agents.md +2 -0
- package/docs/deployment.md +7 -0
- package/docs/guides/bitbucket.md +9 -1
- package/docs/guides/github.md +20 -4
- package/docs/guides/gitlab.md +9 -1
- package/docs/guides/migrate.md +127 -0
- package/docs/reference/cli.md +39 -0
- package/docs/troubleshooting.md +1 -0
- package/package.json +1 -1
- package/skills/debug/SKILL.md +1 -0
- package/skills/github/SKILL.md +23 -0
- package/skills/migrate-v2/SKILL.md +51 -10
- package/dist/docs/assets/chunks/@localSearchIndexroot.BJ8UZggu.js +0 -1
- package/dist/docs/assets/chunks/channel.BTJ5J9FI.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.D4BZ3cVV.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.D4BZ3cVV.js +0 -1
- package/dist/docs/assets/chunks/clone.62tX-O-1.js +0 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.DkTs1aEU.js +0 -1
- package/dist/docs/assets/reference_cli.md.GwugN3n-.lean.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Bscj6ODe.js +0 -1
- package/dist/playground/assets/index-C1g_PTTZ.css +0 -1
- /package/dist/docs/assets/{building-with-agents.md.BPjPFxFg.lean.js → building-with-agents.md.Df87rAe1.lean.js} +0 -0
- /package/dist/docs/assets/{deployment.md.BdqTiGdc.lean.js → deployment.md.BYuEcDBv.lean.js} +0 -0
|
@@ -9,15 +9,6 @@
|
|
|
9
9
|
* v2 agentsdk management API through `deploy-client.ts`, poll status, and
|
|
10
10
|
* render.
|
|
11
11
|
*/
|
|
12
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
12
|
import { basename, resolve } from "node:path";
|
|
22
13
|
import { catchAuthDeclaration, isPublicEndpointPolicy } from "../channels.js";
|
|
23
14
|
import { isCursorAccountTransport, } from "../types.js";
|
|
@@ -26,6 +17,7 @@ import { cursorExternalApiUrl, resolveApiKey, SIGN_IN_HINT } from "./cursor/cred
|
|
|
26
17
|
import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
|
|
27
18
|
import { readDeployTemplate } from "./cursor-agent-template.js";
|
|
28
19
|
import { AgentServeDeployClient, DeployApiError, deploymentServiceAccountId, slugFromDirectoryName, TERMINAL_AGENTKIT_DEPLOY_STATUSES, TERMINAL_DEPLOY_STATUSES, validateDeploymentSlug, } from "./deploy-client.js";
|
|
20
|
+
import { channelCatchSecretNames, channelCredentialSecretNames } from "./deploy-manifest.js";
|
|
29
21
|
import { inferDeployGitSource } from "./deploy-source.js";
|
|
30
22
|
import { describeError } from "./describe-error.js";
|
|
31
23
|
import { formatDiagnostics, hasErrorDiagnostics, isAgentProjectDir, listChildAgentDirs, loadAgentProject, } from "./discovery.js";
|
|
@@ -35,7 +27,7 @@ import { deploymentArchitecture, rejectV2ForLegacyCommand } from "./legacy-comma
|
|
|
35
27
|
import { deployProgressLabel, sanitizeCustomerError } from "./logs-client.js";
|
|
36
28
|
import { sleep } from "./sleep.js";
|
|
37
29
|
import { stderrPalette } from "./terminal-style.js";
|
|
38
|
-
const DEFAULT_POLL_INTERVAL_MS =
|
|
30
|
+
const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
39
31
|
const DEFAULT_DEPLOY_TIMEOUT_MS = 10 * 60 * 1000;
|
|
40
32
|
/** `--multi-tenant` HTTP deploy is Anysphere-only (team id 1). */
|
|
41
33
|
const MULTI_TENANT_HTTP_DEPLOY_TEAM_ID = 1;
|
|
@@ -44,328 +36,316 @@ const MULTI_TENANT_HTTP_DEPLOY_TEAM_ID = 1;
|
|
|
44
36
|
* `--prod` AX routing. Returns an exit code (error already rendered) on
|
|
45
37
|
* failure.
|
|
46
38
|
*/
|
|
47
|
-
export function resolveDeployAuth(options) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
export async function resolveDeployAuth(options) {
|
|
40
|
+
const err = options.err ?? ((text) => void process.stderr.write(text));
|
|
41
|
+
const resolved = await resolveApiKey(options.apiKey === undefined ? undefined : { apiKey: options.apiKey });
|
|
42
|
+
if (resolved === undefined) {
|
|
43
|
+
err(`Not signed in to Cursor. ${SIGN_IN_HINT}\n`);
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
// The deployment management routes live on the external API host
|
|
47
|
+
// (api.cursor.com), not the default backend (api2.cursor.sh) — see
|
|
48
|
+
// cursorExternalApiUrl. GetMe below stays on the resolved backend.
|
|
49
|
+
const client = new AgentServeDeployClient({
|
|
50
|
+
backendUrl: cursorExternalApiUrl(),
|
|
51
|
+
apiKey: resolved.apiKey,
|
|
52
|
+
fetchImpl: options.fetchImpl,
|
|
53
|
+
});
|
|
54
|
+
let teamId;
|
|
55
|
+
if (options.team !== undefined) {
|
|
56
|
+
teamId = Number.parseInt(options.team, 10);
|
|
57
|
+
if (Number.isNaN(teamId) || teamId <= 0) {
|
|
58
|
+
err(`Invalid --team: ${options.team} (expected a positive team id)\n`);
|
|
54
59
|
return 1;
|
|
55
60
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
backendUrl: cursorExternalApiUrl(),
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const account = new CursorBackendClient({
|
|
64
|
+
backendUrl: resolved.backendUrl,
|
|
61
65
|
apiKey: resolved.apiKey,
|
|
62
66
|
fetchImpl: options.fetchImpl,
|
|
63
67
|
});
|
|
64
|
-
let
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (Number.isNaN(teamId) || teamId <= 0) {
|
|
68
|
-
err(`Invalid --team: ${options.team} (expected a positive team id)\n`);
|
|
69
|
-
return 1;
|
|
70
|
-
}
|
|
68
|
+
let identity;
|
|
69
|
+
try {
|
|
70
|
+
identity = await account.getMe();
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
apiKey: resolved.apiKey,
|
|
76
|
-
fetchImpl: options.fetchImpl,
|
|
77
|
-
});
|
|
78
|
-
let identity;
|
|
79
|
-
try {
|
|
80
|
-
identity = yield account.getMe();
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
err(`Could not resolve your team from the Cursor account: ${describeError(error)}\nPass --team <id> explicitly.\n`);
|
|
84
|
-
return 1;
|
|
85
|
-
}
|
|
86
|
-
if (identity.teamId === undefined || identity.teamId <= 0) {
|
|
87
|
-
err("This Cursor account has no default team; pass --team <id> (find it in the Cursor dashboard URL).\n");
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
teamId = identity.teamId;
|
|
91
|
-
const dim = stderrPalette().dim;
|
|
92
|
-
err(`${dim(`Using team ${teamId}${identity.teamName === undefined ? "" : ` (${identity.teamName})`}`)}\n${dim(" the account's team — pass --team to override.")}\n`);
|
|
72
|
+
catch (error) {
|
|
73
|
+
err(`Could not resolve your team from the Cursor account: ${describeError(error)}\nPass --team <id> explicitly.\n`);
|
|
74
|
+
return 1;
|
|
93
75
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
76
|
+
if (identity.teamId === undefined || identity.teamId <= 0) {
|
|
77
|
+
err("This Cursor account has no default team; pass --team <id> (find it in the Cursor dashboard URL).\n");
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
teamId = identity.teamId;
|
|
81
|
+
const dim = stderrPalette().dim;
|
|
82
|
+
err(`${dim(`Using team ${teamId}${identity.teamName === undefined ? "" : ` (${identity.teamName})`}`)}\n${dim(" the account's team — pass --team to override.")}\n`);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
client,
|
|
86
|
+
teamId,
|
|
87
|
+
apiKey: resolved.apiKey,
|
|
88
|
+
backendUrl: resolved.backendUrl,
|
|
89
|
+
};
|
|
101
90
|
}
|
|
102
91
|
/**
|
|
103
92
|
* Shared preamble: credentials → client → team id. Returns an exit code
|
|
104
93
|
* (with the error already rendered) when resolution fails.
|
|
105
94
|
*/
|
|
106
|
-
export function prepare(options) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
fetchImpl: options.fetchImpl,
|
|
115
|
-
err,
|
|
116
|
-
});
|
|
117
|
-
if (typeof auth === "number") {
|
|
118
|
-
return auth;
|
|
119
|
-
}
|
|
120
|
-
return {
|
|
121
|
-
client: auth.client,
|
|
122
|
-
teamId: auth.teamId,
|
|
123
|
-
apiKey: auth.apiKey,
|
|
124
|
-
backendUrl: auth.backendUrl,
|
|
125
|
-
json: options.json === true,
|
|
126
|
-
noWait: options.noWait === true,
|
|
127
|
-
pollIntervalMs: (_c = options.pollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_POLL_INTERVAL_MS,
|
|
128
|
-
timeoutMs: (_d = options.timeoutMs) !== null && _d !== void 0 ? _d : DEFAULT_DEPLOY_TIMEOUT_MS,
|
|
129
|
-
out,
|
|
130
|
-
err,
|
|
131
|
-
};
|
|
95
|
+
export async function prepare(options) {
|
|
96
|
+
const out = options.out ?? ((text) => void process.stdout.write(text));
|
|
97
|
+
const err = options.err ?? ((text) => void process.stderr.write(text));
|
|
98
|
+
const auth = await resolveDeployAuth({
|
|
99
|
+
team: options.team,
|
|
100
|
+
apiKey: options.apiKey,
|
|
101
|
+
fetchImpl: options.fetchImpl,
|
|
102
|
+
err,
|
|
132
103
|
});
|
|
104
|
+
if (typeof auth === "number") {
|
|
105
|
+
return auth;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
client: auth.client,
|
|
109
|
+
teamId: auth.teamId,
|
|
110
|
+
apiKey: auth.apiKey,
|
|
111
|
+
backendUrl: auth.backendUrl,
|
|
112
|
+
json: options.json === true,
|
|
113
|
+
noWait: options.noWait === true,
|
|
114
|
+
pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
|
|
115
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_DEPLOY_TIMEOUT_MS,
|
|
116
|
+
out,
|
|
117
|
+
err,
|
|
118
|
+
};
|
|
133
119
|
}
|
|
134
120
|
// ============================================================================
|
|
135
121
|
// deploy
|
|
136
122
|
// ============================================================================
|
|
137
|
-
export function cmdDeploy(options) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
123
|
+
export async function cmdDeploy(options) {
|
|
124
|
+
const ctx = await prepare(options);
|
|
125
|
+
if (typeof ctx === "number") {
|
|
126
|
+
return ctx;
|
|
127
|
+
}
|
|
128
|
+
if (options.multiTenant === true && ctx.teamId !== MULTI_TENANT_HTTP_DEPLOY_TEAM_ID) {
|
|
129
|
+
ctx.err("--multi-tenant is not available for this team.\n");
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
const targets = await resolveDeployTargets(options, ctx);
|
|
133
|
+
if (typeof targets === "number") {
|
|
134
|
+
return targets;
|
|
135
|
+
}
|
|
136
|
+
if (targets.length === 1) {
|
|
137
|
+
return deployOne(ctx, options, targets[0]);
|
|
138
|
+
}
|
|
139
|
+
// Multi-agent parent: when every child declares `architecture: "v2"`,
|
|
140
|
+
// the v2 control plane models sibling agents as release components of
|
|
141
|
+
// ONE application, so deploy them together (one app, one component per
|
|
142
|
+
// child) instead of minting a separate application per child. Mixed and
|
|
143
|
+
// v1 parents keep the per-child sequential deploys.
|
|
144
|
+
const manifests = [];
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
manifests.push(await readDeployManifest(target.dir));
|
|
147
|
+
}
|
|
148
|
+
if (manifests.every(manifest => manifest.architecture === "v2")) {
|
|
149
|
+
return deployAgentsdkApplication(ctx, options, targets, {
|
|
150
|
+
// `persistent` is per child agent, like `agentPath`: each release
|
|
151
|
+
// component carries its own.
|
|
152
|
+
componentPersistent: Object.fromEntries(targets.map((target, index) => [target.slug, manifests[index]?.persistent === true])),
|
|
153
|
+
componentImage: Object.fromEntries(targets.flatMap((target, index) => manifests[index]?.image === undefined ? [] : [[target.slug, manifests[index].image]])),
|
|
154
|
+
hasScmChannels: manifests.some(manifest => manifest.hasScmChannels),
|
|
155
|
+
declaredSecretNames: declaredSecretNames(manifests),
|
|
156
|
+
hasDeploymentsChannels: manifests.some(manifest => manifest.hasDeploymentsChannels),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (!ctx.json && manifests.some(manifest => manifest.architecture === "v2")) {
|
|
160
|
+
ctx.err(`warning: agents under this parent mix architectures; deploying each separately (v2 children become standalone single-component applications).\n`);
|
|
161
|
+
}
|
|
162
|
+
// --path is per-agent (relative to the repo root). When deploying several
|
|
163
|
+
// agents, ignore a shared --path so each target can infer its own path.
|
|
164
|
+
let deployOptions = options;
|
|
165
|
+
if (options.path !== undefined) {
|
|
166
|
+
if (!ctx.json) {
|
|
167
|
+
ctx.err(`warning: ignoring --path ${JSON.stringify(options.path)} for a multi-agent deploy; each agent infers its own path from git.\n`);
|
|
178
168
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
169
|
+
deployOptions = { ...options, path: undefined };
|
|
170
|
+
}
|
|
171
|
+
// Multi-agent batch: deploy sequentially; --json emits an array.
|
|
172
|
+
const jsonResults = [];
|
|
173
|
+
let failures = 0;
|
|
174
|
+
for (const [index, target] of targets.entries()) {
|
|
175
|
+
if (!ctx.json) {
|
|
176
|
+
ctx.err(`\n=== [${index + 1}/${targets.length}] Deploying ${target.slug} ===\n`);
|
|
187
177
|
}
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const code = yield deployOne(ctx, deployOptions, target, {
|
|
197
|
-
jsonCollect: ctx.json ? jsonResults : undefined,
|
|
198
|
-
});
|
|
199
|
-
if (code !== 0) {
|
|
200
|
-
failures += 1;
|
|
201
|
-
if (ctx.json && jsonResults.length === before) {
|
|
202
|
-
jsonResults.push({ slug: target.slug, error: "deploy failed" });
|
|
203
|
-
}
|
|
178
|
+
const before = jsonResults.length;
|
|
179
|
+
const code = await deployOne(ctx, deployOptions, target, {
|
|
180
|
+
jsonCollect: ctx.json ? jsonResults : undefined,
|
|
181
|
+
});
|
|
182
|
+
if (code !== 0) {
|
|
183
|
+
failures += 1;
|
|
184
|
+
if (ctx.json && jsonResults.length === before) {
|
|
185
|
+
jsonResults.push({ slug: target.slug, error: "deploy failed" });
|
|
204
186
|
}
|
|
205
187
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
188
|
+
}
|
|
189
|
+
if (ctx.json) {
|
|
190
|
+
ctx.out(`${JSON.stringify(jsonResults, null, 2)}\n`);
|
|
191
|
+
}
|
|
192
|
+
else if (failures > 0) {
|
|
193
|
+
ctx.err(`\n${failures} of ${targets.length} deploy(s) failed.\n`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
ctx.err(`\nDeployed ${targets.length} agents.\n`);
|
|
197
|
+
}
|
|
198
|
+
return failures === 0 ? 0 : 1;
|
|
217
199
|
}
|
|
218
200
|
/**
|
|
219
201
|
* Resolve which agent directory(ies) to deploy from `--dir` / `--slug` /
|
|
220
202
|
* `--all`, with an interactive picker when a multi-agent parent is on a TTY.
|
|
221
203
|
*/
|
|
222
|
-
function resolveDeployTargets(options, ctx) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
if (yield isAgentProjectDir(localSourceDir)) {
|
|
231
|
-
dir = localSourceDir;
|
|
232
|
-
}
|
|
204
|
+
async function resolveDeployTargets(options, ctx) {
|
|
205
|
+
let dir = resolve(options.dir ?? ".");
|
|
206
|
+
// A repo-root deploy commonly names the local agent through --path. Use
|
|
207
|
+
// that project for manifest discovery unless --dir or --all chose a scope.
|
|
208
|
+
if (options.dir === undefined && options.path !== undefined && options.all !== true) {
|
|
209
|
+
const localSourceDir = resolve(options.path);
|
|
210
|
+
if (await isAgentProjectDir(localSourceDir)) {
|
|
211
|
+
dir = localSourceDir;
|
|
233
212
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
213
|
+
}
|
|
214
|
+
if (options.slug !== undefined) {
|
|
215
|
+
const slugError = validateDeploymentSlug(options.slug);
|
|
216
|
+
if (slugError !== undefined) {
|
|
217
|
+
ctx.err(`${slugError}\n`);
|
|
218
|
+
return 1;
|
|
240
219
|
}
|
|
241
|
-
|
|
242
|
-
|
|
220
|
+
}
|
|
221
|
+
if (options.all === true && options.slug !== undefined) {
|
|
222
|
+
ctx.err("Pass either --slug or --all, not both.\n");
|
|
223
|
+
return 1;
|
|
224
|
+
}
|
|
225
|
+
if (await isAgentProjectDir(dir)) {
|
|
226
|
+
if (options.all === true) {
|
|
227
|
+
ctx.err(`--all requires a multi-agent parent directory (several child agents under --dir); ${dir} is a single agent project.\n`);
|
|
243
228
|
return 1;
|
|
244
229
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
230
|
+
let slug;
|
|
231
|
+
if (options.slug !== undefined) {
|
|
232
|
+
slug = options.slug;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const derived = slugFromDirectoryName(basename(dir));
|
|
236
|
+
if (derived === undefined) {
|
|
237
|
+
ctx.err(`Could not derive a deployment slug from the directory name ${JSON.stringify(basename(dir))}; pass --slug.\n`);
|
|
248
238
|
return 1;
|
|
249
239
|
}
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
slug
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
const derived = slugFromDirectoryName(basename(dir));
|
|
256
|
-
if (derived === undefined) {
|
|
257
|
-
ctx.err(`Could not derive a deployment slug from the directory name ${JSON.stringify(basename(dir))}; pass --slug.\n`);
|
|
258
|
-
return 1;
|
|
259
|
-
}
|
|
260
|
-
slug = derived;
|
|
261
|
-
if (!ctx.json) {
|
|
262
|
-
ctx.err(`Deploying as "${slug}" (from the directory name).\n`);
|
|
263
|
-
}
|
|
240
|
+
slug = derived;
|
|
241
|
+
if (!ctx.json) {
|
|
242
|
+
ctx.err(`Deploying as "${slug}" (from the directory name).\n`);
|
|
264
243
|
}
|
|
265
|
-
return [{ slug, dir }];
|
|
266
244
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
245
|
+
return [{ slug, dir }];
|
|
246
|
+
}
|
|
247
|
+
let children;
|
|
248
|
+
try {
|
|
249
|
+
children = await listChildAgentDirs(dir);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
ctx.err(`${describeError(error)}\n`);
|
|
253
|
+
return 1;
|
|
254
|
+
}
|
|
255
|
+
// Deploy slugs are lowercase-only; normalize directory names the same way
|
|
256
|
+
// single-agent deploy does so MixedCase child dirs still deploy.
|
|
257
|
+
children = children.flatMap(child => {
|
|
258
|
+
const slug = slugFromDirectoryName(basename(child.dir));
|
|
259
|
+
if (slug === undefined) {
|
|
260
|
+
return [];
|
|
270
261
|
}
|
|
271
|
-
|
|
272
|
-
|
|
262
|
+
return [{ slug, dir: child.dir }];
|
|
263
|
+
});
|
|
264
|
+
if (children.length === 0) {
|
|
265
|
+
if (options.all === true) {
|
|
266
|
+
ctx.err(`--all requires child agent projects under --dir; none found in ${dir}.\n`);
|
|
273
267
|
return 1;
|
|
274
268
|
}
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (slug === undefined) {
|
|
280
|
-
return [];
|
|
281
|
-
}
|
|
282
|
-
return [{ slug, dir: child.dir }];
|
|
283
|
-
});
|
|
284
|
-
if (children.length === 0) {
|
|
285
|
-
if (options.all === true) {
|
|
286
|
-
ctx.err(`--all requires child agent projects under --dir; none found in ${dir}.\n`);
|
|
287
|
-
return 1;
|
|
288
|
-
}
|
|
289
|
-
// Bare-slug deploy against a non-agent directory (still requires --repo).
|
|
290
|
-
let slug;
|
|
291
|
-
if (options.slug !== undefined) {
|
|
292
|
-
slug = options.slug;
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
const derived = slugFromDirectoryName(basename(dir));
|
|
296
|
-
if (derived === undefined) {
|
|
297
|
-
ctx.err(`No agent projects under ${dir}, and could not derive a slug from ${JSON.stringify(basename(dir))}; pass --slug or --dir <agent>.\n`);
|
|
298
|
-
return 1;
|
|
299
|
-
}
|
|
300
|
-
slug = derived;
|
|
301
|
-
if (!ctx.json) {
|
|
302
|
-
ctx.err(`Deploying as "${slug}" (no agent project at ${dir}).\n`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return [{ slug, dir }];
|
|
269
|
+
// Bare-slug deploy against a non-agent directory (still requires --repo).
|
|
270
|
+
let slug;
|
|
271
|
+
if (options.slug !== undefined) {
|
|
272
|
+
slug = options.slug;
|
|
306
273
|
}
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
else if (options.slug !== undefined && options.slug.toLowerCase() !== only.slug) {
|
|
313
|
-
ctx.err(`No agent "${options.slug}" under ${dir}; found "${only.slug}".\n`);
|
|
274
|
+
else {
|
|
275
|
+
const derived = slugFromDirectoryName(basename(dir));
|
|
276
|
+
if (derived === undefined) {
|
|
277
|
+
ctx.err(`No agent projects under ${dir}, and could not derive a slug from ${JSON.stringify(basename(dir))}; pass --slug or --dir <agent>.\n`);
|
|
314
278
|
return 1;
|
|
315
279
|
}
|
|
280
|
+
slug = derived;
|
|
316
281
|
if (!ctx.json) {
|
|
317
|
-
ctx.err(`Deploying "${
|
|
282
|
+
ctx.err(`Deploying as "${slug}" (no agent project at ${dir}).\n`);
|
|
318
283
|
}
|
|
319
|
-
return [only];
|
|
320
284
|
}
|
|
321
|
-
|
|
285
|
+
return [{ slug, dir }];
|
|
286
|
+
}
|
|
287
|
+
if (children.length === 1) {
|
|
288
|
+
const only = children[0];
|
|
322
289
|
if (options.all === true) {
|
|
323
|
-
|
|
324
|
-
ctx.err(`Deploying all ${children.length} agents under ${basename(dir)}: ${children.map(c => c.slug).join(", ")}.\n`);
|
|
325
|
-
}
|
|
326
|
-
return [...children];
|
|
290
|
+
// One child is fine for --all (degenerate multi-agent parent).
|
|
327
291
|
}
|
|
328
|
-
if (options.slug !== undefined) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (match === undefined) {
|
|
332
|
-
ctx.err(`No agent "${options.slug}" under ${dir}.\n` +
|
|
333
|
-
formatAgentList(children) +
|
|
334
|
-
`Pass --slug <name> or --all.\n`);
|
|
335
|
-
return 1;
|
|
336
|
-
}
|
|
337
|
-
return [match];
|
|
292
|
+
else if (options.slug !== undefined && options.slug.toLowerCase() !== only.slug) {
|
|
293
|
+
ctx.err(`No agent "${options.slug}" under ${dir}; found "${only.slug}".\n`);
|
|
294
|
+
return 1;
|
|
338
295
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// unambiguous and needs no picker or --all. A child that is v1 — or
|
|
342
|
-
// fails to load — keeps the interactive picker below.
|
|
343
|
-
if (yield allChildrenAreV2(children)) {
|
|
344
|
-
if (!ctx.json) {
|
|
345
|
-
ctx.err(`Deploying all ${children.length} v2 agents under ${basename(dir)} as one application (pass --slug <name> to deploy a single agent standalone).\n`);
|
|
346
|
-
}
|
|
347
|
-
return [...children];
|
|
296
|
+
if (!ctx.json) {
|
|
297
|
+
ctx.err(`Deploying "${only.slug}" (only agent under ${basename(dir)}).\n`);
|
|
348
298
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}); });
|
|
356
|
-
const chosen = yield chooser(children);
|
|
357
|
-
if (chosen === null) {
|
|
358
|
-
ctx.err("Deploy cancelled.\n");
|
|
359
|
-
return 1;
|
|
299
|
+
return [only];
|
|
300
|
+
}
|
|
301
|
+
// Multiple children.
|
|
302
|
+
if (options.all === true) {
|
|
303
|
+
if (!ctx.json) {
|
|
304
|
+
ctx.err(`Deploying all ${children.length} agents under ${basename(dir)}: ${children.map(c => c.slug).join(", ")}.\n`);
|
|
360
305
|
}
|
|
361
|
-
|
|
362
|
-
|
|
306
|
+
return [...children];
|
|
307
|
+
}
|
|
308
|
+
if (options.slug !== undefined) {
|
|
309
|
+
const wanted = options.slug.toLowerCase();
|
|
310
|
+
const match = children.find(child => child.slug === wanted);
|
|
311
|
+
if (match === undefined) {
|
|
312
|
+
ctx.err(`No agent "${options.slug}" under ${dir}.\n` +
|
|
363
313
|
formatAgentList(children) +
|
|
364
|
-
`Pass --slug <name> or --all
|
|
314
|
+
`Pass --slug <name> or --all.\n`);
|
|
365
315
|
return 1;
|
|
366
316
|
}
|
|
367
|
-
return
|
|
368
|
-
}
|
|
317
|
+
return [match];
|
|
318
|
+
}
|
|
319
|
+
// An all-v2 parent deploys as ONE application (siblings are components
|
|
320
|
+
// of a single app on the v2 control plane), so "deploy the parent" is
|
|
321
|
+
// unambiguous and needs no picker or --all. A child that is v1 — or
|
|
322
|
+
// fails to load — keeps the interactive picker below.
|
|
323
|
+
if (await allChildrenAreV2(children)) {
|
|
324
|
+
if (!ctx.json) {
|
|
325
|
+
ctx.err(`Deploying all ${children.length} v2 agents under ${basename(dir)} as one application (pass --slug <name> to deploy a single agent standalone).\n`);
|
|
326
|
+
}
|
|
327
|
+
return [...children];
|
|
328
|
+
}
|
|
329
|
+
const chooser = options.chooseTargets ??
|
|
330
|
+
(agents => promptChooseDeployTargets(agents, {
|
|
331
|
+
err: ctx.err,
|
|
332
|
+
stdin: options.stdin ?? process.stdin,
|
|
333
|
+
interactive: ctx.json !== true &&
|
|
334
|
+
(options.stdin ?? process.stdin).isTTY === true &&
|
|
335
|
+
process.stderr.isTTY === true,
|
|
336
|
+
}));
|
|
337
|
+
const chosen = await chooser(children);
|
|
338
|
+
if (chosen === null) {
|
|
339
|
+
ctx.err("Deploy cancelled.\n");
|
|
340
|
+
return 1;
|
|
341
|
+
}
|
|
342
|
+
if (chosen.length === 0) {
|
|
343
|
+
ctx.err(`Multiple agents under ${dir}:\n` +
|
|
344
|
+
formatAgentList(children) +
|
|
345
|
+
`Pass --slug <name> or --all (or re-run on a TTY to pick interactively).\n`);
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
return chosen;
|
|
369
349
|
}
|
|
370
350
|
function formatAgentList(agents) {
|
|
371
351
|
return agents.map((agent, i) => ` ${i + 1}) ${agent.slug}\n`).join("");
|
|
@@ -376,240 +356,246 @@ function formatAgentList(agents) {
|
|
|
376
356
|
* resolution falls back to the picker instead of failing the whole deploy
|
|
377
357
|
* on a child the user might not even select.
|
|
378
358
|
*/
|
|
379
|
-
function allChildrenAreV2(children) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if ((yield readDeployManifest(child.dir)).architecture !== "v2") {
|
|
384
|
-
return false;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
catch (_a) {
|
|
359
|
+
async function allChildrenAreV2(children) {
|
|
360
|
+
for (const child of children) {
|
|
361
|
+
try {
|
|
362
|
+
if ((await readDeployManifest(child.dir)).architecture !== "v2") {
|
|
388
363
|
return false;
|
|
389
364
|
}
|
|
390
365
|
}
|
|
391
|
-
|
|
392
|
-
|
|
366
|
+
catch {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return true;
|
|
393
371
|
}
|
|
394
372
|
/**
|
|
395
373
|
* TTY picker for multi-agent parents. Non-interactive callers get `[]`
|
|
396
374
|
* so {@link resolveDeployTargets} can print the --slug/--all hint.
|
|
397
375
|
*/
|
|
398
|
-
export function promptChooseDeployTargets(agents, options) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const answer = (_c = (yield readLine())) === null || _c === void 0 ? void 0 : _c.trim().toLowerCase();
|
|
410
|
-
if (answer === undefined || answer === "q" || answer === "quit") {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
if (answer === "a" || answer === "all" || answer === "*") {
|
|
414
|
-
return [...agents];
|
|
415
|
-
}
|
|
416
|
-
const asIndex = Number.parseInt(answer, 10);
|
|
417
|
-
if (!Number.isNaN(asIndex) &&
|
|
418
|
-
String(asIndex) === answer &&
|
|
419
|
-
asIndex >= 1 &&
|
|
420
|
-
asIndex <= agents.length) {
|
|
421
|
-
return [agents[asIndex - 1]];
|
|
422
|
-
}
|
|
423
|
-
const bySlug = agents.find(agent => agent.slug.toLowerCase() === answer);
|
|
424
|
-
if (bySlug !== undefined) {
|
|
425
|
-
return [bySlug];
|
|
426
|
-
}
|
|
427
|
-
options.err(`Unrecognized choice ${JSON.stringify(answer)} — expected 1–${agents.length}, a slug, "all", or "q". Aborting.\n`);
|
|
376
|
+
export async function promptChooseDeployTargets(agents, options) {
|
|
377
|
+
options.err(`Multiple agents (${agents.length}):\n` + formatAgentList(agents) + ` a) all\n`);
|
|
378
|
+
if (!options.interactive) {
|
|
379
|
+
return [];
|
|
380
|
+
}
|
|
381
|
+
options.err('Deploy which? Enter a number, slug, "all", or "q" to cancel: ');
|
|
382
|
+
const readLine = createLineReader(options.stdin);
|
|
383
|
+
// createLineReader pauses until data; ensure stdin is flowing on a TTY.
|
|
384
|
+
options.stdin.resume?.();
|
|
385
|
+
const answer = (await readLine())?.trim().toLowerCase();
|
|
386
|
+
if (answer === undefined || answer === "q" || answer === "quit") {
|
|
428
387
|
return null;
|
|
429
|
-
}
|
|
388
|
+
}
|
|
389
|
+
if (answer === "a" || answer === "all" || answer === "*") {
|
|
390
|
+
return [...agents];
|
|
391
|
+
}
|
|
392
|
+
const asIndex = Number.parseInt(answer, 10);
|
|
393
|
+
if (!Number.isNaN(asIndex) &&
|
|
394
|
+
String(asIndex) === answer &&
|
|
395
|
+
asIndex >= 1 &&
|
|
396
|
+
asIndex <= agents.length) {
|
|
397
|
+
return [agents[asIndex - 1]];
|
|
398
|
+
}
|
|
399
|
+
const bySlug = agents.find(agent => agent.slug.toLowerCase() === answer);
|
|
400
|
+
if (bySlug !== undefined) {
|
|
401
|
+
return [bySlug];
|
|
402
|
+
}
|
|
403
|
+
options.err(`Unrecognized choice ${JSON.stringify(answer)} — expected 1–${agents.length}, a slug, "all", or "q". Aborting.\n`);
|
|
404
|
+
return null;
|
|
430
405
|
}
|
|
431
|
-
function deployOne(ctx, options, target, extras) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
406
|
+
async function deployOne(ctx, options, target, extras) {
|
|
407
|
+
const { dir, slug } = target;
|
|
408
|
+
// Fill --repo/--ref/--path from the local git checkout when omitted so
|
|
409
|
+
// `agent-sdk deploy` inside an agent directory is enough. Only probe git
|
|
410
|
+
// for real agent projects (avoids ambient monorepo remotes when someone
|
|
411
|
+
// deploys a bare --slug). Explicit flags always win.
|
|
412
|
+
const isAgent = await isAgentProjectDir(dir);
|
|
413
|
+
const needsGitInference = isAgent &&
|
|
414
|
+
(options.repo === undefined || options.ref === undefined || options.path === undefined);
|
|
415
|
+
const inferred = needsGitInference
|
|
416
|
+
? await inferDeployGitSource(dir, {
|
|
417
|
+
...(options.git === undefined ? {} : { git: options.git }),
|
|
418
|
+
})
|
|
419
|
+
: {};
|
|
420
|
+
const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
|
|
421
|
+
const gitRef = options.ref ?? inferred.gitRef;
|
|
422
|
+
const agentPath = options.path ?? inferred.agentPath;
|
|
423
|
+
if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
|
|
424
|
+
ctx.err(`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`);
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
if (gitRepoUrl === undefined) {
|
|
428
|
+
ctx.err(`Could not resolve an https:// git repository URL for this deploy.\n` +
|
|
429
|
+
` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`);
|
|
430
|
+
return 1;
|
|
431
|
+
}
|
|
432
|
+
for (const repo of options.cursorEventsRepos ?? []) {
|
|
433
|
+
if (!isValidCursorEventRepo(repo)) {
|
|
434
|
+
ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
|
|
450
435
|
return 1;
|
|
451
436
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
437
|
+
}
|
|
438
|
+
if (!ctx.json && gitRepoUrl !== undefined) {
|
|
439
|
+
const inferredBits = [];
|
|
440
|
+
if (options.repo === undefined && inferred.gitRepoUrl !== undefined) {
|
|
441
|
+
inferredBits.push("repo");
|
|
456
442
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
|
|
460
|
-
return 1;
|
|
461
|
-
}
|
|
443
|
+
if (options.ref === undefined && inferred.gitRef !== undefined) {
|
|
444
|
+
inferredBits.push("ref");
|
|
462
445
|
}
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
if (options.repo === undefined && inferred.gitRepoUrl !== undefined) {
|
|
466
|
-
inferredBits.push("repo");
|
|
467
|
-
}
|
|
468
|
-
if (options.ref === undefined && inferred.gitRef !== undefined) {
|
|
469
|
-
inferredBits.push("ref");
|
|
470
|
-
}
|
|
471
|
-
if (options.path === undefined && inferred.agentPath !== undefined) {
|
|
472
|
-
inferredBits.push("path");
|
|
473
|
-
}
|
|
474
|
-
ctx.err(` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
|
|
475
|
-
(agentPath === undefined ? "" : ` path: ${agentPath}\n`) +
|
|
476
|
-
(inferredBits.length === 0
|
|
477
|
-
? ""
|
|
478
|
-
: ` (inferred ${inferredBits.join(", ")} from git; pass --repo/--ref/--path to override)\n`));
|
|
479
|
-
}
|
|
480
|
-
// The package manifest (`defineAgent({ hosting })`) is the source of
|
|
481
|
-
// truth for egress domains and expected secret names; flags override or
|
|
482
|
-
// augment it. Directories without an agent project have no manifest. The same load supplies the
|
|
483
|
-
// cursorAccount connection refs for the post-deploy connector auth and
|
|
484
|
-
// the channels declared public via `publicEndpoint()`.
|
|
485
|
-
const manifest = isAgent ? yield readDeployManifest(dir) : undefined;
|
|
486
|
-
const hosting = manifest === null || manifest === void 0 ? void 0 : manifest.hosting;
|
|
487
|
-
const egressAllowedDomains = [
|
|
488
|
-
...new Set([...((_e = hosting === null || hosting === void 0 ? void 0 : hosting.egressDomains) !== null && _e !== void 0 ? _e : []), ...((_f = options.allowDomains) !== null && _f !== void 0 ? _f : [])]),
|
|
489
|
-
];
|
|
490
|
-
const publicRoutePaths = ((_g = manifest === null || manifest === void 0 ? void 0 : manifest.publicChannelIds) !== null && _g !== void 0 ? _g : []).map(channelId => `/v1/channels/${channelId}`);
|
|
491
|
-
if (!ctx.json && (manifest === null || manifest === void 0 ? void 0 : manifest.httpChannelPublicEndpoint) === true) {
|
|
492
|
-
ctx.err(`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`);
|
|
493
|
-
}
|
|
494
|
-
if (!ctx.json && publicRoutePaths.length > 0 && (manifest === null || manifest === void 0 ? void 0 : manifest.architecture) !== "v2") {
|
|
495
|
-
// Security-relevant surface change: say exactly what will be reachable
|
|
496
|
-
// without the alias token before the deploy is accepted. v2 registers
|
|
497
|
-
// these routes only after bake, so a relative path here would not be a
|
|
498
|
-
// live webhook URL — the running status prints the absolute one.
|
|
499
|
-
ctx.err(` public: ${publicRoutePaths.join(", ")} (served WITHOUT the alias token — these channels authenticate requests themselves)\n`);
|
|
500
|
-
}
|
|
501
|
-
const extensionEgress = (_h = manifest === null || manifest === void 0 ? void 0 : manifest.extensionEgress) !== null && _h !== void 0 ? _h : [];
|
|
502
|
-
if (!ctx.json && extensionEgress.length > 0) {
|
|
503
|
-
// Mounted packages widen the pod's egress without an agent.ts edit; name
|
|
504
|
-
// each one before the deploy is accepted.
|
|
505
|
-
ctx.err(` egress from extensions: ${extensionEgress
|
|
506
|
-
.map(entry => `${entry.extension} (${entry.egressDomains.join(", ")})`)
|
|
507
|
-
.join("; ")}\n`);
|
|
508
|
-
}
|
|
509
|
-
const deadSecretChannels = (_j = manifest === null || manifest === void 0 ? void 0 : manifest.publicChannelIdsWithDeadSecretPolicies) !== null && _j !== void 0 ? _j : [];
|
|
510
|
-
if (!ctx.json && deadSecretChannels.length > 0) {
|
|
511
|
-
ctx.err(`warning: ${deadSecretChannels
|
|
512
|
-
.map(channelId => `/v1/channels/${channelId}`)
|
|
513
|
-
.join(", ")}: 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`);
|
|
514
|
-
}
|
|
515
|
-
if ((manifest === null || manifest === void 0 ? void 0 : manifest.architecture) === "v2") {
|
|
516
|
-
if (!ctx.json) {
|
|
517
|
-
ctx.err(` architecture: v2${manifest.persistent ? " (persistent)" : ""}${manifest.image === undefined ? "" : `\n image: ${manifest.image}`}\n`);
|
|
518
|
-
}
|
|
519
|
-
return deployOneAgentsdk(ctx, extras, {
|
|
520
|
-
persistent: manifest.persistent,
|
|
521
|
-
image: manifest.image,
|
|
522
|
-
slug,
|
|
523
|
-
gitRepoUrl,
|
|
524
|
-
gitRef,
|
|
525
|
-
agentPath,
|
|
526
|
-
repositories: options.cursorEventsRepos,
|
|
527
|
-
hasScmChannels: manifest.hasScmChannels,
|
|
528
|
-
declaredSecretNames: hosting === null || hosting === void 0 ? void 0 : hosting.secretNames,
|
|
529
|
-
hasDeploymentsChannels: manifest.hasDeploymentsChannels,
|
|
530
|
-
multiTenant: options.multiTenant === true,
|
|
531
|
-
});
|
|
446
|
+
if (options.path === undefined && inferred.agentPath !== undefined) {
|
|
447
|
+
inferredBits.push("path");
|
|
532
448
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
449
|
+
ctx.err(` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
|
|
450
|
+
(agentPath === undefined ? "" : ` path: ${agentPath}\n`) +
|
|
451
|
+
(inferredBits.length === 0
|
|
452
|
+
? ""
|
|
453
|
+
: ` (inferred ${inferredBits.join(", ")} from git; pass --repo/--ref/--path to override)\n`));
|
|
454
|
+
}
|
|
455
|
+
// The package manifest (`defineAgent({ hosting })`) is the source of
|
|
456
|
+
// truth for egress domains and expected secret names; flags override or
|
|
457
|
+
// augment it. Directories without an agent project have no manifest. The same load supplies the
|
|
458
|
+
// cursorAccount connection refs for the post-deploy connector auth and
|
|
459
|
+
// the channels declared public via `publicEndpoint()`.
|
|
460
|
+
const manifest = isAgent ? await readDeployManifest(dir) : undefined;
|
|
461
|
+
const hosting = manifest?.hosting;
|
|
462
|
+
const egressAllowedDomains = [
|
|
463
|
+
...new Set([...(hosting?.egressDomains ?? []), ...(options.allowDomains ?? [])]),
|
|
464
|
+
];
|
|
465
|
+
const publicRoutePaths = (manifest?.publicChannelIds ?? []).map(channelId => `/v1/channels/${channelId}`);
|
|
466
|
+
if (!ctx.json && manifest?.httpChannelPublicEndpoint === true) {
|
|
467
|
+
ctx.err(`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`);
|
|
468
|
+
}
|
|
469
|
+
if (!ctx.json && publicRoutePaths.length > 0 && manifest?.architecture !== "v2") {
|
|
470
|
+
// Security-relevant surface change: say exactly what will be reachable
|
|
471
|
+
// without the alias token before the deploy is accepted. v2 registers
|
|
472
|
+
// these routes only after bake, so a relative path here would not be a
|
|
473
|
+
// live webhook URL — the running status prints the absolute one.
|
|
474
|
+
ctx.err(` public: ${publicRoutePaths.join(", ")} (served WITHOUT the alias token — these channels authenticate requests themselves)\n`);
|
|
475
|
+
}
|
|
476
|
+
const extensionEgress = manifest?.extensionEgress ?? [];
|
|
477
|
+
if (!ctx.json && extensionEgress.length > 0) {
|
|
478
|
+
// Mounted packages widen the pod's egress without an agent.ts edit; name
|
|
479
|
+
// each one before the deploy is accepted.
|
|
480
|
+
ctx.err(` egress from extensions: ${extensionEgress
|
|
481
|
+
.map(entry => `${entry.extension} (${entry.egressDomains.join(", ")})`)
|
|
482
|
+
.join("; ")}\n`);
|
|
483
|
+
}
|
|
484
|
+
const deadSecretChannels = manifest?.publicChannelIdsWithDeadSecretPolicies ?? [];
|
|
485
|
+
if (!ctx.json && deadSecretChannels.length > 0) {
|
|
486
|
+
ctx.err(`warning: ${deadSecretChannels
|
|
487
|
+
.map(channelId => `/v1/channels/${channelId}`)
|
|
488
|
+
.join(", ")}: 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`);
|
|
489
|
+
}
|
|
490
|
+
if (manifest?.architecture === "v2") {
|
|
491
|
+
if (!ctx.json) {
|
|
492
|
+
ctx.err(` architecture: v2${manifest.persistent ? " (persistent)" : ""}${manifest.image === undefined ? "" : `\n image: ${manifest.image}`}\n`);
|
|
536
493
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
494
|
+
return deployOneAgentsdk(ctx, extras, {
|
|
495
|
+
persistent: manifest.persistent,
|
|
496
|
+
image: manifest.image,
|
|
497
|
+
slug,
|
|
498
|
+
gitRepoUrl,
|
|
499
|
+
gitRef,
|
|
500
|
+
agentPath,
|
|
501
|
+
repositories: options.cursorEventsRepos,
|
|
502
|
+
hasScmChannels: manifest.hasScmChannels,
|
|
503
|
+
declaredSecretNames: declaredSecretNames([manifest]),
|
|
504
|
+
hasDeploymentsChannels: manifest.hasDeploymentsChannels,
|
|
505
|
+
multiTenant: options.multiTenant === true,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (options.multiTenant === true) {
|
|
509
|
+
ctx.err("--multi-tenant is only valid for architecture: v2 deploys (Cursor-managed products).\n");
|
|
510
|
+
return 1;
|
|
511
|
+
}
|
|
512
|
+
// Today's only source shape: the control plane builds from a git ref.
|
|
513
|
+
// See DeploySource for the artifact seam.
|
|
514
|
+
const source = {
|
|
515
|
+
kind: "git-ref",
|
|
516
|
+
gitRepoUrl,
|
|
517
|
+
...(gitRef === undefined ? {} : { gitRef }),
|
|
518
|
+
...(agentPath === undefined ? {} : { agentPath }),
|
|
519
|
+
...(options.cursorEventsRepos === undefined || options.cursorEventsRepos.length === 0
|
|
540
520
|
? {}
|
|
541
|
-
: { cursorEventRepos: options.cursorEventsRepos })
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
`Check progress with \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.\n`);
|
|
569
|
-
}
|
|
570
|
-
return 0;
|
|
571
|
-
}
|
|
572
|
-
let detail;
|
|
573
|
-
try {
|
|
574
|
-
detail = yield pollUntil(ctx, slug, TERMINAL_DEPLOY_STATUSES, () => ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
575
|
-
}
|
|
576
|
-
catch (error) {
|
|
577
|
-
return renderApiError(error, ctx.err);
|
|
578
|
-
}
|
|
579
|
-
warnMissingSecrets(ctx, slug, hosting === null || hosting === void 0 ? void 0 : hosting.secretNames, detail.secretNames);
|
|
580
|
-
const payload = accepted.aliasToken === undefined ? detail : Object.assign(Object.assign({}, detail), { aliasToken: accepted.aliasToken });
|
|
581
|
-
if ((extras === null || extras === void 0 ? void 0 : extras.jsonCollect) !== undefined) {
|
|
582
|
-
extras.jsonCollect.push(payload);
|
|
583
|
-
return detail.status === "running" ? 0 : 1;
|
|
521
|
+
: { cursorEventRepos: options.cursorEventsRepos }),
|
|
522
|
+
...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
|
|
523
|
+
publicRoutePaths: publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
|
|
524
|
+
// Deliberately NO manifest here: the control plane captures it from the
|
|
525
|
+
// provisioned engine (`GET /v1/manifest` after the health probe), which
|
|
526
|
+
// is the single write path for deploy manifests. `july manifest` remains
|
|
527
|
+
// available for local visibility only.
|
|
528
|
+
};
|
|
529
|
+
let accepted;
|
|
530
|
+
try {
|
|
531
|
+
accepted = await ctx.client.deploy({
|
|
532
|
+
teamId: ctx.teamId,
|
|
533
|
+
slug,
|
|
534
|
+
source,
|
|
535
|
+
template: readDeployTemplate(dir),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
return renderApiError(error, ctx.err);
|
|
540
|
+
}
|
|
541
|
+
const aliasUrl = ctx.client.aliasUrl(accepted.engineAlias);
|
|
542
|
+
if (!ctx.json && accepted.aliasToken !== undefined) {
|
|
543
|
+
ctx.out(aliasTokenBlock(accepted.aliasToken));
|
|
544
|
+
}
|
|
545
|
+
if (ctx.noWait) {
|
|
546
|
+
if (extras?.jsonCollect !== undefined) {
|
|
547
|
+
extras.jsonCollect.push(accepted);
|
|
584
548
|
}
|
|
585
|
-
if (ctx.json) {
|
|
586
|
-
ctx.out(`${JSON.stringify(
|
|
587
|
-
return detail.status === "running" ? 0 : 1;
|
|
549
|
+
else if (ctx.json) {
|
|
550
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
588
551
|
}
|
|
589
|
-
|
|
590
|
-
ctx.
|
|
591
|
-
|
|
552
|
+
else {
|
|
553
|
+
ctx.out(`Deploy accepted: ${slug} (team ${ctx.teamId}, generation ${accepted.generation}, ${accepted.deploymentKind})\n` +
|
|
554
|
+
` alias: ${aliasUrl}\n` +
|
|
555
|
+
`Check progress with \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.\n`);
|
|
592
556
|
}
|
|
593
|
-
const deployedPublicRoutes = (_k = detail.publicRoutePaths) !== null && _k !== void 0 ? _k : [];
|
|
594
|
-
ctx.out(`Deployed ${slug} (team ${ctx.teamId})\n` +
|
|
595
|
-
` status: running\n` +
|
|
596
|
-
` generation: ${detail.generation}\n` +
|
|
597
|
-
` kind: ${detail.deploymentKind}\n` +
|
|
598
|
-
` alias: ${aliasUrl}\n` +
|
|
599
|
-
` playground: ${aliasUrl}/playground (sign in with the alias token)\n` +
|
|
600
|
-
` open: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n` +
|
|
601
|
-
// Paste-ready webhook URLs: these subtrees skip the alias token, so
|
|
602
|
-
// the channel handler must verify deliveries (webhook signatures).
|
|
603
|
-
deployedPublicRoutes
|
|
604
|
-
.map(path => ` public: ${aliasUrl}${path} (no alias token)\n`)
|
|
605
|
-
.join(""));
|
|
606
|
-
yield runPostDeployConnectorAuth(ctx, options, {
|
|
607
|
-
slug,
|
|
608
|
-
connections: (_l = manifest === null || manifest === void 0 ? void 0 : manifest.cursorAccountConnections) !== null && _l !== void 0 ? _l : [],
|
|
609
|
-
serviceAccountId: deploymentServiceAccountId(detail),
|
|
610
|
-
});
|
|
611
557
|
return 0;
|
|
558
|
+
}
|
|
559
|
+
let detail;
|
|
560
|
+
try {
|
|
561
|
+
detail = await pollUntil(ctx, slug, TERMINAL_DEPLOY_STATUSES, () => ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
return renderApiError(error, ctx.err);
|
|
565
|
+
}
|
|
566
|
+
warnMissingSecrets(ctx, slug, declaredSecretNames([manifest]), detail.secretNames);
|
|
567
|
+
const payload = accepted.aliasToken === undefined ? detail : { ...detail, aliasToken: accepted.aliasToken };
|
|
568
|
+
if (extras?.jsonCollect !== undefined) {
|
|
569
|
+
extras.jsonCollect.push(payload);
|
|
570
|
+
return detail.status === "running" ? 0 : 1;
|
|
571
|
+
}
|
|
572
|
+
if (ctx.json) {
|
|
573
|
+
ctx.out(`${JSON.stringify(payload, null, 2)}\n`);
|
|
574
|
+
return detail.status === "running" ? 0 : 1;
|
|
575
|
+
}
|
|
576
|
+
if (detail.status !== "running") {
|
|
577
|
+
ctx.err(`Deployment ${slug} ended ${detail.status}${deployFailureDetail(detail.lastError)}\n`);
|
|
578
|
+
return 1;
|
|
579
|
+
}
|
|
580
|
+
const deployedPublicRoutes = detail.publicRoutePaths ?? [];
|
|
581
|
+
ctx.out(`Deployed ${slug} (team ${ctx.teamId})\n` +
|
|
582
|
+
` status: running\n` +
|
|
583
|
+
` generation: ${detail.generation}\n` +
|
|
584
|
+
` kind: ${detail.deploymentKind}\n` +
|
|
585
|
+
` alias: ${aliasUrl}\n` +
|
|
586
|
+
` playground: ${aliasUrl}/playground (sign in with the alias token)\n` +
|
|
587
|
+
` open: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n` +
|
|
588
|
+
// Paste-ready webhook URLs: these subtrees skip the alias token, so
|
|
589
|
+
// the channel handler must verify deliveries (webhook signatures).
|
|
590
|
+
deployedPublicRoutes
|
|
591
|
+
.map(path => ` public: ${aliasUrl}${path} (no alias token)\n`)
|
|
592
|
+
.join(""));
|
|
593
|
+
await runPostDeployConnectorAuth(ctx, options, {
|
|
594
|
+
slug,
|
|
595
|
+
connections: manifest?.cursorAccountConnections ?? [],
|
|
596
|
+
serviceAccountId: deploymentServiceAccountId(detail),
|
|
612
597
|
});
|
|
598
|
+
return 0;
|
|
613
599
|
}
|
|
614
600
|
/**
|
|
615
601
|
* Multi-agent parent whose children all declare `architecture: "v2"`:
|
|
@@ -618,158 +604,165 @@ function deployOne(ctx, options, target, extras) {
|
|
|
618
604
|
* key is the child's slug and its path is the child directory relative to
|
|
619
605
|
* the repo root (a shared --path names the parent's directory in the repo).
|
|
620
606
|
*/
|
|
621
|
-
function deployAgentsdkApplication(ctx, options, targets, declarations) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
607
|
+
async function deployAgentsdkApplication(ctx, options, targets, declarations) {
|
|
608
|
+
const parentDir = resolve(options.dir ?? ".");
|
|
609
|
+
const slug = slugFromDirectoryName(basename(parentDir));
|
|
610
|
+
if (slug === undefined) {
|
|
611
|
+
ctx.err(`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`);
|
|
612
|
+
return 1;
|
|
613
|
+
}
|
|
614
|
+
const needsGitInference = options.repo === undefined || options.ref === undefined || options.path === undefined;
|
|
615
|
+
const inferred = needsGitInference
|
|
616
|
+
? await inferDeployGitSource(parentDir, {
|
|
617
|
+
...(options.git === undefined ? {} : { git: options.git }),
|
|
618
|
+
})
|
|
619
|
+
: {};
|
|
620
|
+
const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
|
|
621
|
+
const gitRef = options.ref ?? inferred.gitRef;
|
|
622
|
+
const parentPath = options.path ?? inferred.agentPath;
|
|
623
|
+
if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
|
|
624
|
+
ctx.err(`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`);
|
|
625
|
+
return 1;
|
|
626
|
+
}
|
|
627
|
+
if (gitRepoUrl === undefined) {
|
|
628
|
+
ctx.err(`Could not resolve an https:// git repository URL for this deploy.\n` +
|
|
629
|
+
` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`);
|
|
630
|
+
return 1;
|
|
631
|
+
}
|
|
632
|
+
for (const repo of options.cursorEventsRepos ?? []) {
|
|
633
|
+
if (!isValidCursorEventRepo(repo)) {
|
|
634
|
+
ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
|
|
644
635
|
return 1;
|
|
645
636
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
repositories: options.cursorEventsRepos,
|
|
677
|
-
hasScmChannels: declarations.hasScmChannels,
|
|
678
|
-
declaredSecretNames: declarations.declaredSecretNames,
|
|
679
|
-
hasDeploymentsChannels: declarations.hasDeploymentsChannels,
|
|
680
|
-
multiTenant: options.multiTenant === true,
|
|
681
|
-
});
|
|
637
|
+
}
|
|
638
|
+
const components = targets.map(target => ({
|
|
639
|
+
componentKey: target.slug,
|
|
640
|
+
agentPath: [parentPath, basename(target.dir)]
|
|
641
|
+
.filter((part) => part !== undefined && part !== "")
|
|
642
|
+
.join("/"),
|
|
643
|
+
persistent: declarations.componentPersistent[target.slug] === true,
|
|
644
|
+
image: declarations.componentImage[target.slug],
|
|
645
|
+
}));
|
|
646
|
+
if (!ctx.json) {
|
|
647
|
+
ctx.err(`Deploying "${slug}" as one v2 application with ${targets.length} components (slug from the parent directory name).\n` +
|
|
648
|
+
` architecture: v2\n` +
|
|
649
|
+
` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
|
|
650
|
+
components
|
|
651
|
+
.map(component => ` component: ${component.componentKey} (${component.agentPath ?? "."})${component.persistent ? " persistent" : ""}${component.image === undefined ? "" : ` image ${component.image}`}\n`)
|
|
652
|
+
.join(""));
|
|
653
|
+
}
|
|
654
|
+
return deployOneAgentsdk(ctx, undefined, {
|
|
655
|
+
// Multi-component: the flag travels on each component instead.
|
|
656
|
+
persistent: false,
|
|
657
|
+
slug,
|
|
658
|
+
gitRepoUrl,
|
|
659
|
+
gitRef,
|
|
660
|
+
agentPath: undefined,
|
|
661
|
+
components,
|
|
662
|
+
repositories: options.cursorEventsRepos,
|
|
663
|
+
hasScmChannels: declarations.hasScmChannels,
|
|
664
|
+
declaredSecretNames: declarations.declaredSecretNames,
|
|
665
|
+
hasDeploymentsChannels: declarations.hasDeploymentsChannels,
|
|
666
|
+
multiTenant: options.multiTenant === true,
|
|
682
667
|
});
|
|
683
668
|
}
|
|
684
669
|
/**
|
|
685
670
|
* `architecture: "v2"` deploy: POST `/internal/agentsdk/deployments` and
|
|
686
671
|
* poll the matching GET.
|
|
687
672
|
*/
|
|
688
|
-
function deployOneAgentsdk(ctx, extras, args) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
componentsLine);
|
|
724
|
-
}
|
|
725
|
-
return 0;
|
|
726
|
-
}
|
|
727
|
-
let detail;
|
|
728
|
-
try {
|
|
729
|
-
detail = yield pollUntil(ctx, slug, TERMINAL_AGENTKIT_DEPLOY_STATUSES, () => ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }));
|
|
730
|
-
}
|
|
731
|
-
catch (error) {
|
|
732
|
-
return renderApiError(error, ctx.err);
|
|
733
|
-
}
|
|
734
|
-
const payload = Object.assign(Object.assign({}, detail), { aliasToken: accepted.aliasToken });
|
|
735
|
-
if ((extras === null || extras === void 0 ? void 0 : extras.jsonCollect) !== undefined) {
|
|
736
|
-
extras.jsonCollect.push(payload);
|
|
737
|
-
return detail.status === "running" ? 0 : 1;
|
|
738
|
-
}
|
|
739
|
-
if (ctx.json) {
|
|
740
|
-
ctx.out(`${JSON.stringify(payload, null, 2)}\n`);
|
|
741
|
-
return detail.status === "running" ? 0 : 1;
|
|
673
|
+
async function deployOneAgentsdk(ctx, extras, args) {
|
|
674
|
+
const { slug } = args;
|
|
675
|
+
const componentsLine = args.components === undefined
|
|
676
|
+
? ""
|
|
677
|
+
: ` components: ${args.components.map(component => component.componentKey).join(", ")}\n`;
|
|
678
|
+
if (args.multiTenant && !ctx.json) {
|
|
679
|
+
ctx.err(" multi-tenant: existing product\n");
|
|
680
|
+
}
|
|
681
|
+
let accepted;
|
|
682
|
+
try {
|
|
683
|
+
accepted = await ctx.client.deployAgentkit({
|
|
684
|
+
teamId: ctx.teamId,
|
|
685
|
+
slug,
|
|
686
|
+
gitRepoUrl: args.gitRepoUrl,
|
|
687
|
+
gitRef: args.gitRef,
|
|
688
|
+
agentPath: args.agentPath,
|
|
689
|
+
components: args.components,
|
|
690
|
+
repositories: args.repositories,
|
|
691
|
+
...(args.multiTenant ? { multiTenant: true } : {}),
|
|
692
|
+
persistent: args.persistent,
|
|
693
|
+
image: args.image,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
catch (error) {
|
|
697
|
+
return renderApiError(error, ctx.err, {
|
|
698
|
+
multiTenant: args.multiTenant,
|
|
699
|
+
teamId: ctx.teamId,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
if (!ctx.json && accepted.aliasToken !== undefined) {
|
|
703
|
+
ctx.out(aliasTokenBlock(accepted.aliasToken));
|
|
704
|
+
}
|
|
705
|
+
if (ctx.noWait) {
|
|
706
|
+
if (extras?.jsonCollect !== undefined) {
|
|
707
|
+
extras.jsonCollect.push(accepted);
|
|
742
708
|
}
|
|
743
|
-
if (
|
|
744
|
-
ctx.
|
|
745
|
-
return 1;
|
|
709
|
+
else if (ctx.json) {
|
|
710
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
746
711
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
` release: ${detail.releaseId}\n` +
|
|
754
|
-
` alias: ${ctx.client.aliasUrl(accepted.engineAlias)}\n` +
|
|
755
|
-
(args.components === undefined
|
|
756
|
-
? ""
|
|
757
|
-
: ` components: ${args.components
|
|
758
|
-
.map(component => component.componentKey)
|
|
759
|
-
.join(", ")}\n`) +
|
|
760
|
-
(detail.repositories === undefined || !watchesRepositories
|
|
761
|
-
? ""
|
|
762
|
-
: ` watching: ${detail.repositories.length === 0 ? "(nothing)" : detail.repositories.join(", ")}\n`) +
|
|
763
|
-
livePublicWebhookLines(ctx, detail));
|
|
764
|
-
// An agent with GitHub/Origin/deployments channels bound to no repository
|
|
765
|
-
// scope gets no deliveries at all (deployments channels receive the
|
|
766
|
-
// watched repos' deployment events), which is otherwise invisible here.
|
|
767
|
-
if (watchesRepositories && ((_a = detail.repositories) === null || _a === void 0 ? void 0 : _a.length) === 0) {
|
|
768
|
-
ctx.err(`warning: ${slug} watches no repositories, so its GitHub/Origin/deployments channels receive no events.\n` +
|
|
769
|
-
` Redeploy with --cursor-events-repo <owner>/<name> to set the scope.\n`);
|
|
712
|
+
else {
|
|
713
|
+
ctx.out(`Deploy accepted: ${slug} (team ${ctx.teamId}, v2)\n` +
|
|
714
|
+
` application: ${accepted.applicationId}\n` +
|
|
715
|
+
` release: ${accepted.releaseId}\n` +
|
|
716
|
+
` alias: ${ctx.client.aliasUrl(accepted.engineAlias)}\n` +
|
|
717
|
+
componentsLine);
|
|
770
718
|
}
|
|
771
719
|
return 0;
|
|
772
|
-
}
|
|
720
|
+
}
|
|
721
|
+
let detail;
|
|
722
|
+
try {
|
|
723
|
+
detail = await pollUntil(ctx, slug, TERMINAL_AGENTKIT_DEPLOY_STATUSES, () => ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }));
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
return renderApiError(error, ctx.err);
|
|
727
|
+
}
|
|
728
|
+
const payload = { ...detail, aliasToken: accepted.aliasToken };
|
|
729
|
+
if (extras?.jsonCollect !== undefined) {
|
|
730
|
+
extras.jsonCollect.push(payload);
|
|
731
|
+
return detail.status === "running" ? 0 : 1;
|
|
732
|
+
}
|
|
733
|
+
if (ctx.json) {
|
|
734
|
+
ctx.out(`${JSON.stringify(payload, null, 2)}\n`);
|
|
735
|
+
return detail.status === "running" ? 0 : 1;
|
|
736
|
+
}
|
|
737
|
+
if (detail.status !== "running") {
|
|
738
|
+
ctx.err(`Deployment ${slug} ended ${detail.status}${deployFailureDetail(detail.lastError)}\n`);
|
|
739
|
+
return 1;
|
|
740
|
+
}
|
|
741
|
+
warnMissingSecrets(ctx, slug, args.declaredSecretNames, detail.secretNames);
|
|
742
|
+
const watchesRepositories = args.hasScmChannels || args.hasDeploymentsChannels === true;
|
|
743
|
+
ctx.out(`Deployed ${slug} (team ${ctx.teamId})\n` +
|
|
744
|
+
` status: running\n` +
|
|
745
|
+
` architecture: v2\n` +
|
|
746
|
+
` application: ${detail.applicationId}\n` +
|
|
747
|
+
` release: ${detail.releaseId}\n` +
|
|
748
|
+
` alias: ${ctx.client.aliasUrl(accepted.engineAlias)}\n` +
|
|
749
|
+
(args.components === undefined
|
|
750
|
+
? ""
|
|
751
|
+
: ` components: ${args.components
|
|
752
|
+
.map(component => component.componentKey)
|
|
753
|
+
.join(", ")}\n`) +
|
|
754
|
+
(detail.repositories === undefined || !watchesRepositories
|
|
755
|
+
? ""
|
|
756
|
+
: ` watching: ${detail.repositories.length === 0 ? "(nothing)" : detail.repositories.join(", ")}\n`) +
|
|
757
|
+
livePublicWebhookLines(ctx, detail));
|
|
758
|
+
// An agent with GitHub/Origin/deployments channels bound to no repository
|
|
759
|
+
// scope gets no deliveries at all (deployments channels receive the
|
|
760
|
+
// watched repos' deployment events), which is otherwise invisible here.
|
|
761
|
+
if (watchesRepositories && detail.repositories?.length === 0) {
|
|
762
|
+
ctx.err(`warning: ${slug} watches no repositories, so its GitHub/Origin/deployments channels receive no events.\n` +
|
|
763
|
+
` Redeploy with --cursor-events-repo <owner>/<name> to set the scope.\n`);
|
|
764
|
+
}
|
|
765
|
+
return 0;
|
|
773
766
|
}
|
|
774
767
|
/**
|
|
775
768
|
* Post-deploy connector authorization for `cursorAccount` connections: the
|
|
@@ -779,113 +772,124 @@ function deployOneAgentsdk(ctx, extras, args) {
|
|
|
779
772
|
* ones skipped); otherwise it reports status only. Never affects the deploy
|
|
780
773
|
* outcome — anything left unauthorized just gets a playground-page pointer.
|
|
781
774
|
*/
|
|
782
|
-
function runPostDeployConnectorAuth(ctx, options, args) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
if (result.pending.length > 0 || result.errored.length > 0) {
|
|
819
|
-
anyUnauthorized = true;
|
|
820
|
-
if (result.pending.length > 0) {
|
|
821
|
-
ctx.out(`Connectors not authorized yet: ${result.pending.join(", ")}.\n`);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
catch (error) {
|
|
775
|
+
async function runPostDeployConnectorAuth(ctx, options, args) {
|
|
776
|
+
if (args.connections.length === 0) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const { serviceAccountId } = args;
|
|
780
|
+
if (serviceAccountId === undefined) {
|
|
781
|
+
ctx.err(`warning: this agent uses Cursor account connectors, but the deployment has no service account; redeploy to mint one.\n`);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const backendClient = new CursorBackendClient({
|
|
785
|
+
backendUrl: ctx.backendUrl,
|
|
786
|
+
apiKey: ctx.apiKey,
|
|
787
|
+
fetchImpl: options.fetchImpl,
|
|
788
|
+
});
|
|
789
|
+
const interactive = options.openBrowserImpl !== undefined ||
|
|
790
|
+
(process.stdout.isTTY === true && process.stdin.isTTY === true);
|
|
791
|
+
let anyUnauthorized = false;
|
|
792
|
+
for (const ref of args.connections) {
|
|
793
|
+
ctx.out(`\nThis agent uses Cursor account connectors (connection "${ref.name}"${ref.servers === undefined || ref.servers.length === 0 ? "" : `: ${ref.servers.join(", ")}`}).\n`);
|
|
794
|
+
try {
|
|
795
|
+
const result = await authorizeCursorAccountConnectors({
|
|
796
|
+
backendClient,
|
|
797
|
+
teamId: ctx.teamId,
|
|
798
|
+
slug: args.slug,
|
|
799
|
+
serviceAccountId,
|
|
800
|
+
connectionName: ref.name,
|
|
801
|
+
servers: ref.servers,
|
|
802
|
+
interactive,
|
|
803
|
+
out: ctx.out,
|
|
804
|
+
err: ctx.err,
|
|
805
|
+
openBrowserImpl: options.openBrowserImpl,
|
|
806
|
+
sleep: options.sleepImpl,
|
|
807
|
+
pollIntervalMs: options.connectorPollIntervalMs,
|
|
808
|
+
pollTimeoutMs: options.connectorPollTimeoutMs,
|
|
809
|
+
});
|
|
810
|
+
if (result.pending.length > 0 || result.errored.length > 0) {
|
|
826
811
|
anyUnauthorized = true;
|
|
827
|
-
|
|
812
|
+
if (result.pending.length > 0) {
|
|
813
|
+
ctx.out(`Connectors not authorized yet: ${result.pending.join(", ")}.\n`);
|
|
814
|
+
}
|
|
828
815
|
}
|
|
829
816
|
}
|
|
830
|
-
|
|
831
|
-
|
|
817
|
+
catch (error) {
|
|
818
|
+
anyUnauthorized = true;
|
|
819
|
+
ctx.err(`warning: could not check connector authorization for "${ref.name}": ${describeError(error)}\n`);
|
|
832
820
|
}
|
|
833
|
-
}
|
|
821
|
+
}
|
|
822
|
+
if (anyUnauthorized) {
|
|
823
|
+
ctx.out(`You can authorize connectors at the playground page (cursor.com dashboard → Deployed Agents → ${args.slug}) or with \`${CLI} mcp oauth <connection>\`.\n`);
|
|
824
|
+
}
|
|
834
825
|
}
|
|
835
826
|
/**
|
|
836
827
|
* Read the deploy-relevant bits of the project manifest at `dir` in one
|
|
837
828
|
* load: the `hosting` block, the agent's cursorAccount connections, and the
|
|
838
829
|
* channels declared public via `publicEndpoint()`.
|
|
839
830
|
*/
|
|
840
|
-
function readDeployManifest(dir) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
.
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
831
|
+
async function readDeployManifest(dir) {
|
|
832
|
+
const project = await loadAgentProject(dir);
|
|
833
|
+
// Fail here, before anything is dispatched: a project with error
|
|
834
|
+
// diagnostics (e.g. agent/storage.ts on architecture v2) would only be
|
|
835
|
+
// refused later by `serve` inside the hosted build, wasting a bake.
|
|
836
|
+
if (hasErrorDiagnostics(project)) {
|
|
837
|
+
throw new Error(`Refusing to deploy ${project.name} — it has errors:\n${formatDiagnostics(project.diagnostics.filter(d => d.severity === "error"))}`);
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
hosting: project.agent.hosting,
|
|
841
|
+
architecture: project.agent.architecture,
|
|
842
|
+
persistent: project.agent.persistent === true,
|
|
843
|
+
image: project.agent.hosting?.image,
|
|
844
|
+
cursorAccountConnections: project.agent.connections.flatMap(connection => isCursorAccountTransport(connection.transport)
|
|
845
|
+
? [{ name: connection.name, servers: connection.transport.servers }]
|
|
846
|
+
: []),
|
|
847
|
+
publicChannelIds: project.channels
|
|
848
|
+
.filter(channel => (channel.definition.auth ?? []).some(isPublicEndpointPolicy))
|
|
849
|
+
.map(channel => channel.id),
|
|
850
|
+
publicChannelIdsWithDeadSecretPolicies: project.channels
|
|
851
|
+
.filter(channel => {
|
|
852
|
+
const auth = channel.definition.auth ?? [];
|
|
853
|
+
return (auth.some(isPublicEndpointPolicy) &&
|
|
854
|
+
auth.some(policy => {
|
|
855
|
+
const kind = catchAuthDeclaration(policy)?.kind;
|
|
856
|
+
return kind !== undefined && kind !== "public";
|
|
857
|
+
}));
|
|
858
|
+
})
|
|
859
|
+
.map(channel => channel.id),
|
|
860
|
+
httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(isPublicEndpointPolicy),
|
|
861
|
+
catchSecretNames: [
|
|
862
|
+
...new Set(project.channels.flatMap(channel => channelCatchSecretNames(channel.definition))),
|
|
863
|
+
],
|
|
864
|
+
credentialSecretNames: [
|
|
865
|
+
...new Set(project.channels.flatMap(channel => channelCredentialSecretNames(channel.definition))),
|
|
866
|
+
],
|
|
867
|
+
hasScmChannels: project.channels.some(channel => {
|
|
868
|
+
const kind = channel.definition.meta?.kind;
|
|
869
|
+
return kind === "github" || kind === "origin";
|
|
870
|
+
}),
|
|
871
|
+
hasDeploymentsChannels: project.channels.some(channel => channel.definition.meta?.kind === "deployments"),
|
|
872
|
+
extensionEgress: (project.agent.extensions ?? []).flatMap(extension => {
|
|
873
|
+
const egressDomains = extension.hosting?.egressDomains ?? [];
|
|
874
|
+
return egressDomains.length === 0 ? [] : [{ extension: extension.name, egressDomains }];
|
|
875
|
+
}),
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Every secret name the deployment is expected to carry: the authored
|
|
880
|
+
* `hosting.secretNames` plus the names the control plane verifies caught
|
|
881
|
+
* requests with. Empty when the manifests declare none.
|
|
882
|
+
*/
|
|
883
|
+
function declaredSecretNames(manifests) {
|
|
884
|
+
return [
|
|
885
|
+
...new Set(manifests.flatMap(manifest => manifest === undefined
|
|
886
|
+
? []
|
|
887
|
+
: [
|
|
888
|
+
...(manifest.hosting?.secretNames ?? []),
|
|
889
|
+
...manifest.catchSecretNames,
|
|
890
|
+
...manifest.credentialSecretNames,
|
|
891
|
+
])),
|
|
892
|
+
];
|
|
889
893
|
}
|
|
890
894
|
/**
|
|
891
895
|
* Friendly post-deploy nudge: the manifest declares secrets the agent
|
|
@@ -895,7 +899,7 @@ function warnMissingSecrets(ctx, slug, declared, set) {
|
|
|
895
899
|
if (declared === undefined || declared.length === 0) {
|
|
896
900
|
return;
|
|
897
901
|
}
|
|
898
|
-
const setNames = new Set(set
|
|
902
|
+
const setNames = new Set(set ?? []);
|
|
899
903
|
// Platform-managed names (CURSOR_*, hosted storage) are injected by the
|
|
900
904
|
// control plane and never appear in secrets list — do not warn on them.
|
|
901
905
|
const missing = declared.filter(name => !setNames.has(name) && !isReservedSecretName(name));
|
|
@@ -908,160 +912,155 @@ function warnMissingSecrets(ctx, slug, declared, set) {
|
|
|
908
912
|
// ============================================================================
|
|
909
913
|
// deployments (list)
|
|
910
914
|
// ============================================================================
|
|
911
|
-
export function cmdDeployments(options) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
915
|
+
export async function cmdDeployments(options) {
|
|
916
|
+
const ctx = await prepare(options);
|
|
917
|
+
if (typeof ctx === "number") {
|
|
918
|
+
return ctx;
|
|
919
|
+
}
|
|
920
|
+
let deployments;
|
|
921
|
+
try {
|
|
922
|
+
deployments = await ctx.client.listDeployments({ teamId: ctx.teamId });
|
|
923
|
+
}
|
|
924
|
+
catch (error) {
|
|
925
|
+
return renderApiError(error, ctx.err);
|
|
926
|
+
}
|
|
927
|
+
let applications = [];
|
|
928
|
+
try {
|
|
929
|
+
applications = await ctx.client.listAgentkitDeployments({
|
|
930
|
+
teamId: ctx.teamId,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
// Older control planes 404 this route; keep listing v1.
|
|
935
|
+
if (!(error instanceof DeployApiError && error.status === 404)) {
|
|
922
936
|
return renderApiError(error, ctx.err);
|
|
923
937
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
catch (error) {
|
|
931
|
-
// Older control planes 404 this route; keep listing v1.
|
|
932
|
-
if (!(error instanceof DeployApiError && error.status === 404)) {
|
|
933
|
-
return renderApiError(error, ctx.err);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
if (ctx.json) {
|
|
937
|
-
// Empty v2 list keeps `{ deployments }` for v1-only parsers.
|
|
938
|
-
ctx.out(`${JSON.stringify(applications.length === 0 ? { deployments } : { deployments, applications }, null, 2)}\n`);
|
|
939
|
-
return 0;
|
|
940
|
-
}
|
|
941
|
-
if (deployments.length === 0 && applications.length === 0) {
|
|
942
|
-
ctx.out(`No deployments on team ${ctx.teamId}. Create one with \`${CLI} deploy\`.\n`);
|
|
943
|
-
return 0;
|
|
944
|
-
}
|
|
945
|
-
ctx.out(`${renderTable(["SLUG", "ARCH", "STATUS", "GEN", "KIND", "UPDATED"], [
|
|
946
|
-
...deployments.map(d => { var _a; return [
|
|
947
|
-
d.slug,
|
|
948
|
-
"v1",
|
|
949
|
-
d.status,
|
|
950
|
-
String(d.generation),
|
|
951
|
-
d.deploymentKind,
|
|
952
|
-
(_a = d.desiredUpdatedAt) !== null && _a !== void 0 ? _a : "",
|
|
953
|
-
]; }),
|
|
954
|
-
...applications.map(application => { var _a; return [
|
|
955
|
-
application.slug,
|
|
956
|
-
"v2",
|
|
957
|
-
application.status,
|
|
958
|
-
"-",
|
|
959
|
-
"application",
|
|
960
|
-
(_a = application.deployments
|
|
961
|
-
.map(row => row.desiredUpdatedAt)
|
|
962
|
-
.sort()
|
|
963
|
-
.at(-1)) !== null && _a !== void 0 ? _a : "",
|
|
964
|
-
]; }),
|
|
965
|
-
])}\n`);
|
|
938
|
+
}
|
|
939
|
+
if (ctx.json) {
|
|
940
|
+
// Empty v2 list keeps `{ deployments }` for v1-only parsers.
|
|
941
|
+
ctx.out(`${JSON.stringify(applications.length === 0 ? { deployments } : { deployments, applications }, null, 2)}\n`);
|
|
966
942
|
return 0;
|
|
967
|
-
}
|
|
943
|
+
}
|
|
944
|
+
if (deployments.length === 0 && applications.length === 0) {
|
|
945
|
+
ctx.out(`No deployments on team ${ctx.teamId}. Create one with \`${CLI} deploy\`.\n`);
|
|
946
|
+
return 0;
|
|
947
|
+
}
|
|
948
|
+
ctx.out(`${renderTable(["SLUG", "ARCH", "STATUS", "GEN", "KIND", "UPDATED"], [
|
|
949
|
+
...deployments.map(d => [
|
|
950
|
+
d.slug,
|
|
951
|
+
"v1",
|
|
952
|
+
d.status,
|
|
953
|
+
String(d.generation),
|
|
954
|
+
d.deploymentKind,
|
|
955
|
+
d.desiredUpdatedAt ?? "",
|
|
956
|
+
]),
|
|
957
|
+
...applications.map(application => [
|
|
958
|
+
application.slug,
|
|
959
|
+
"v2",
|
|
960
|
+
application.status,
|
|
961
|
+
"-",
|
|
962
|
+
"application",
|
|
963
|
+
application.deployments
|
|
964
|
+
.map(row => row.desiredUpdatedAt)
|
|
965
|
+
.sort()
|
|
966
|
+
.at(-1) ?? "",
|
|
967
|
+
]),
|
|
968
|
+
])}\n`);
|
|
969
|
+
return 0;
|
|
968
970
|
}
|
|
969
971
|
// ============================================================================
|
|
970
972
|
// deployment <slug> (detail)
|
|
971
973
|
// ============================================================================
|
|
972
|
-
export function cmdDeploymentStatus(slug, options) {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
catch (error) {
|
|
995
|
-
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
996
|
-
return renderApiError(error, ctx.err);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
let detail;
|
|
1000
|
-
try {
|
|
1001
|
-
detail = yield ctx.client.getDeployment({ teamId: ctx.teamId, slug });
|
|
1002
|
-
}
|
|
1003
|
-
catch (error) {
|
|
974
|
+
export async function cmdDeploymentStatus(slug, options) {
|
|
975
|
+
if (slug === undefined || slug === "") {
|
|
976
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} deployment <slug> [--team <id>] [--json]\n`);
|
|
977
|
+
return 1;
|
|
978
|
+
}
|
|
979
|
+
const ctx = await prepare(options);
|
|
980
|
+
if (typeof ctx === "number") {
|
|
981
|
+
return ctx;
|
|
982
|
+
}
|
|
983
|
+
// Same dispatch as `stop`: v1 409s architecture-v2 slugs, so the v2
|
|
984
|
+
// GET goes first. A v2 404 (missing slug, or an older backend) falls
|
|
985
|
+
// through to the v1 agent-serve detail.
|
|
986
|
+
try {
|
|
987
|
+
const detail = await ctx.client.getAgentkitDeploy({
|
|
988
|
+
teamId: ctx.teamId,
|
|
989
|
+
slug,
|
|
990
|
+
});
|
|
991
|
+
renderAgentkitDeploymentStatus(ctx, detail);
|
|
992
|
+
return 0;
|
|
993
|
+
}
|
|
994
|
+
catch (error) {
|
|
995
|
+
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
1004
996
|
return renderApiError(error, ctx.err);
|
|
1005
997
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
detail.statusMessage !== undefined &&
|
|
1017
|
-
detail.statusMessage.trim() !== "") {
|
|
1018
|
-
lines.push(` step: ${detail.statusMessage}`);
|
|
1019
|
-
}
|
|
1020
|
-
lines.push(` generation: ${detail.generation}`, ` kind: ${detail.deploymentKind}`, ` alias: ${aliasUrl}`, ` playground: ${aliasUrl}/playground`);
|
|
1021
|
-
if (detail.gitRepoUrl !== null && detail.gitRepoUrl !== undefined) {
|
|
1022
|
-
lines.push(` repo: ${detail.gitRepoUrl} @ ${(_b = detail.gitRef) !== null && _b !== void 0 ? _b : ""}`);
|
|
1023
|
-
}
|
|
1024
|
-
if (detail.serviceAccountId !== undefined && detail.serviceAccountId !== null) {
|
|
1025
|
-
lines.push(` service account: ${detail.serviceAccountId}`);
|
|
1026
|
-
}
|
|
1027
|
-
if (detail.agentStoreSourceId !== undefined) {
|
|
1028
|
-
// The durable file store's address: GET /v0/agent-serve-files/<id> lists
|
|
1029
|
-
// it, and the pod sees it as AGENT_SERVE_STORE_SOURCE_ID.
|
|
1030
|
-
lines.push(` file store: ${detail.agentStoreSourceId}`);
|
|
1031
|
-
}
|
|
1032
|
-
if (detail.egressAllowedDomains !== undefined && detail.egressAllowedDomains.length > 0) {
|
|
1033
|
-
lines.push(` egress: ${detail.egressAllowedDomains.join(", ")}`);
|
|
1034
|
-
}
|
|
1035
|
-
if (detail.publicRoutePaths !== undefined && detail.publicRoutePaths.length > 0) {
|
|
1036
|
-
// Served through the alias WITHOUT X-Agent-Alias-Token (the channel
|
|
1037
|
-
// authenticates deliveries itself).
|
|
1038
|
-
for (const path of detail.publicRoutePaths) {
|
|
1039
|
-
lines.push(` public: ${aliasUrl}${path} (no alias token)`);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
|
|
1043
|
-
lines.push(` secrets: ${detail.secretNames.join(", ")}`);
|
|
1044
|
-
}
|
|
1045
|
-
if (detail.podCredentialMaskedKey !== undefined && detail.podCredentialMaskedKey !== null) {
|
|
1046
|
-
lines.push(` pod credential: ${detail.podCredentialMaskedKey}`);
|
|
1047
|
-
}
|
|
1048
|
-
if (detail.engineUrl !== null && detail.engineUrl !== undefined) {
|
|
1049
|
-
lines.push(` engine: ${detail.engineUrl}`);
|
|
1050
|
-
}
|
|
1051
|
-
if (detail.engineAccess !== null &&
|
|
1052
|
-
detail.engineAccess !== undefined &&
|
|
1053
|
-
typeof detail.engineAccess === "object") {
|
|
1054
|
-
lines.push(` engine access: ${detail.engineAccess.url} (expires ${detail.engineAccess.expiresAt})`);
|
|
1055
|
-
}
|
|
1056
|
-
if (detail.lastError !== null && detail.lastError !== undefined) {
|
|
1057
|
-
lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
|
|
1058
|
-
}
|
|
1059
|
-
if (detail.desiredUpdatedAt !== undefined) {
|
|
1060
|
-
lines.push(` updated: ${detail.desiredUpdatedAt}`);
|
|
1061
|
-
}
|
|
1062
|
-
ctx.out(`${lines.join("\n")}\n`);
|
|
998
|
+
}
|
|
999
|
+
let detail;
|
|
1000
|
+
try {
|
|
1001
|
+
detail = await ctx.client.getDeployment({ teamId: ctx.teamId, slug });
|
|
1002
|
+
}
|
|
1003
|
+
catch (error) {
|
|
1004
|
+
return renderApiError(error, ctx.err);
|
|
1005
|
+
}
|
|
1006
|
+
if (ctx.json) {
|
|
1007
|
+
ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
|
|
1063
1008
|
return 0;
|
|
1064
|
-
}
|
|
1009
|
+
}
|
|
1010
|
+
const aliasUrl = ctx.client.aliasUrl(detail.engineAlias);
|
|
1011
|
+
const lines = [`${detail.slug} (team ${detail.teamId})`, ` status: ${detail.status}`];
|
|
1012
|
+
if (detail.progress !== undefined) {
|
|
1013
|
+
lines.push(` progress: ${detail.progress}`);
|
|
1014
|
+
}
|
|
1015
|
+
if (detail.statusMessage !== null &&
|
|
1016
|
+
detail.statusMessage !== undefined &&
|
|
1017
|
+
detail.statusMessage.trim() !== "") {
|
|
1018
|
+
lines.push(` step: ${detail.statusMessage}`);
|
|
1019
|
+
}
|
|
1020
|
+
lines.push(` generation: ${detail.generation}`, ` kind: ${detail.deploymentKind}`, ` alias: ${aliasUrl}`, ` playground: ${aliasUrl}/playground`);
|
|
1021
|
+
if (detail.gitRepoUrl !== null && detail.gitRepoUrl !== undefined) {
|
|
1022
|
+
lines.push(` repo: ${detail.gitRepoUrl} @ ${detail.gitRef ?? ""}`);
|
|
1023
|
+
}
|
|
1024
|
+
if (detail.serviceAccountId !== undefined && detail.serviceAccountId !== null) {
|
|
1025
|
+
lines.push(` service account: ${detail.serviceAccountId}`);
|
|
1026
|
+
}
|
|
1027
|
+
if (detail.agentStoreSourceId !== undefined) {
|
|
1028
|
+
// The durable file store's address: GET /v0/agent-serve-files/<id> lists
|
|
1029
|
+
// it, and the pod sees it as AGENT_SERVE_STORE_SOURCE_ID.
|
|
1030
|
+
lines.push(` file store: ${detail.agentStoreSourceId}`);
|
|
1031
|
+
}
|
|
1032
|
+
if (detail.egressAllowedDomains !== undefined && detail.egressAllowedDomains.length > 0) {
|
|
1033
|
+
lines.push(` egress: ${detail.egressAllowedDomains.join(", ")}`);
|
|
1034
|
+
}
|
|
1035
|
+
if (detail.publicRoutePaths !== undefined && detail.publicRoutePaths.length > 0) {
|
|
1036
|
+
// Served through the alias WITHOUT X-Agent-Alias-Token (the channel
|
|
1037
|
+
// authenticates deliveries itself).
|
|
1038
|
+
for (const path of detail.publicRoutePaths) {
|
|
1039
|
+
lines.push(` public: ${aliasUrl}${path} (no alias token)`);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
|
|
1043
|
+
lines.push(` secrets: ${detail.secretNames.join(", ")}`);
|
|
1044
|
+
}
|
|
1045
|
+
if (detail.podCredentialMaskedKey !== undefined && detail.podCredentialMaskedKey !== null) {
|
|
1046
|
+
lines.push(` pod credential: ${detail.podCredentialMaskedKey}`);
|
|
1047
|
+
}
|
|
1048
|
+
if (detail.engineUrl !== null && detail.engineUrl !== undefined) {
|
|
1049
|
+
lines.push(` engine: ${detail.engineUrl}`);
|
|
1050
|
+
}
|
|
1051
|
+
if (detail.engineAccess !== null &&
|
|
1052
|
+
detail.engineAccess !== undefined &&
|
|
1053
|
+
typeof detail.engineAccess === "object") {
|
|
1054
|
+
lines.push(` engine access: ${detail.engineAccess.url} (expires ${detail.engineAccess.expiresAt})`);
|
|
1055
|
+
}
|
|
1056
|
+
if (detail.lastError !== null && detail.lastError !== undefined) {
|
|
1057
|
+
lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
|
|
1058
|
+
}
|
|
1059
|
+
if (detail.desiredUpdatedAt !== undefined) {
|
|
1060
|
+
lines.push(` updated: ${detail.desiredUpdatedAt}`);
|
|
1061
|
+
}
|
|
1062
|
+
ctx.out(`${lines.join("\n")}\n`);
|
|
1063
|
+
return 0;
|
|
1065
1064
|
}
|
|
1066
1065
|
/**
|
|
1067
1066
|
* Absolute webhook URLs for channels the running release actually admits
|
|
@@ -1071,12 +1070,11 @@ export function cmdDeploymentStatus(slug, options) {
|
|
|
1071
1070
|
* the deployment URL is not this application's alias.
|
|
1072
1071
|
*/
|
|
1073
1072
|
function livePublicWebhookLines(ctx, detail) {
|
|
1074
|
-
var _a;
|
|
1075
1073
|
const engineAlias = detail.engineAlias;
|
|
1076
1074
|
if (detail.status !== "running" || engineAlias === undefined) {
|
|
1077
1075
|
return "";
|
|
1078
1076
|
}
|
|
1079
|
-
return (
|
|
1077
|
+
return (detail.publicRoutePaths ?? [])
|
|
1080
1078
|
.map(path => ` public: ${ctx.client.aliasUrl(engineAlias)}${path} (no alias token)\n`)
|
|
1081
1079
|
.join("");
|
|
1082
1080
|
}
|
|
@@ -1114,84 +1112,81 @@ function renderAgentkitDeploymentStatus(ctx, detail) {
|
|
|
1114
1112
|
// ============================================================================
|
|
1115
1113
|
// stop <slug>
|
|
1116
1114
|
// ============================================================================
|
|
1117
|
-
export function cmdStop(slug, options) {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
1144
|
-
return renderApiError(error, ctx.err);
|
|
1145
|
-
}
|
|
1146
|
-
if (options.cancelRuns === true) {
|
|
1147
|
-
ctx.err(`Canceling active runs while stopping is supported only for Agent SDK v2 deployments.\n`);
|
|
1148
|
-
return 1;
|
|
1149
|
-
}
|
|
1150
|
-
try {
|
|
1151
|
-
accepted = yield ctx.client.stopDeployment({ teamId: ctx.teamId, slug });
|
|
1152
|
-
architecture = "v1";
|
|
1153
|
-
}
|
|
1154
|
-
catch (v1Error) {
|
|
1155
|
-
return renderApiError(v1Error, ctx.err);
|
|
1156
|
-
}
|
|
1115
|
+
export async function cmdStop(slug, options) {
|
|
1116
|
+
if (slug === undefined || slug === "") {
|
|
1117
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} stop <slug> [--team <id>] [--json] [--no-wait]\n`);
|
|
1118
|
+
return 1;
|
|
1119
|
+
}
|
|
1120
|
+
const ctx = await prepare(options);
|
|
1121
|
+
if (typeof ctx === "number") {
|
|
1122
|
+
return ctx;
|
|
1123
|
+
}
|
|
1124
|
+
// Stop takes only a slug, so there is no manifest to dispatch on (unlike
|
|
1125
|
+
// deploy). The v2 agentsdk control plane goes first: v1 endpoints refuse
|
|
1126
|
+
// `architecture: "v2"` slugs with a 409 rather than a 404, so v1-first
|
|
1127
|
+
// could not fall through. A v2 404 (including an older backend without
|
|
1128
|
+
// the `/internal/agentsdk` mount) falls back to the v1 stop.
|
|
1129
|
+
let architecture = "v2";
|
|
1130
|
+
let accepted;
|
|
1131
|
+
try {
|
|
1132
|
+
accepted = await ctx.client.stopAgentkit({
|
|
1133
|
+
teamId: ctx.teamId,
|
|
1134
|
+
slug,
|
|
1135
|
+
cancelRuns: options.cancelRuns,
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
1140
|
+
return renderApiError(error, ctx.err);
|
|
1157
1141
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (ctx.json) {
|
|
1162
|
-
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1163
|
-
}
|
|
1164
|
-
else {
|
|
1165
|
-
ctx.out(stoppedGlobally
|
|
1166
|
-
? `Stop accepted for ${slug} across all tenants (status: ${accepted.status}).\n`
|
|
1167
|
-
: `Stop accepted for ${slug} (status: ${accepted.status}).\n`);
|
|
1168
|
-
}
|
|
1169
|
-
return (cancellation === null || cancellation === void 0 ? void 0 : cancellation.failed) === 0 || cancellation === undefined ? 0 : 1;
|
|
1142
|
+
if (options.cancelRuns === true) {
|
|
1143
|
+
ctx.err(`Canceling active runs while stopping is supported only for Agent SDK v2 deployments.\n`);
|
|
1144
|
+
return 1;
|
|
1170
1145
|
}
|
|
1171
|
-
let detail;
|
|
1172
1146
|
try {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
? yield pollUntil(ctx, slug, new Set(["stopped"]), () => ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }))
|
|
1176
|
-
: yield pollUntil(ctx, slug, new Set(["stopped"]), () => ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
1147
|
+
accepted = await ctx.client.stopDeployment({ teamId: ctx.teamId, slug });
|
|
1148
|
+
architecture = "v1";
|
|
1177
1149
|
}
|
|
1178
|
-
catch (
|
|
1179
|
-
return renderApiError(
|
|
1150
|
+
catch (v1Error) {
|
|
1151
|
+
return renderApiError(v1Error, ctx.err);
|
|
1180
1152
|
}
|
|
1153
|
+
}
|
|
1154
|
+
const cancellation = "cancellation" in accepted ? accepted.cancellation : undefined;
|
|
1155
|
+
const stoppedGlobally = "scope" in accepted && accepted.scope === "global";
|
|
1156
|
+
if (ctx.noWait) {
|
|
1181
1157
|
if (ctx.json) {
|
|
1182
|
-
ctx.out(`${JSON.stringify(
|
|
1183
|
-
}
|
|
1184
|
-
else if (stoppedGlobally) {
|
|
1185
|
-
ctx.out(`Stopped ${slug} across all tenants (team ${ctx.teamId}); resume with \`${CLI} start ${slug}\`.\n`);
|
|
1158
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1186
1159
|
}
|
|
1187
1160
|
else {
|
|
1188
|
-
ctx.out(
|
|
1161
|
+
ctx.out(stoppedGlobally
|
|
1162
|
+
? `Stop accepted for ${slug} across all tenants (status: ${accepted.status}).\n`
|
|
1163
|
+
: `Stop accepted for ${slug} (status: ${accepted.status}).\n`);
|
|
1189
1164
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1165
|
+
return cancellation?.failed === 0 || cancellation === undefined ? 0 : 1;
|
|
1166
|
+
}
|
|
1167
|
+
let detail;
|
|
1168
|
+
try {
|
|
1169
|
+
detail =
|
|
1170
|
+
architecture === "v2"
|
|
1171
|
+
? await pollUntil(ctx, slug, new Set(["stopped"]), () => ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug }))
|
|
1172
|
+
: await pollUntil(ctx, slug, new Set(["stopped"]), () => ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
1173
|
+
}
|
|
1174
|
+
catch (error) {
|
|
1175
|
+
return renderApiError(error, ctx.err);
|
|
1176
|
+
}
|
|
1177
|
+
if (ctx.json) {
|
|
1178
|
+
ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
|
|
1179
|
+
}
|
|
1180
|
+
else if (stoppedGlobally) {
|
|
1181
|
+
ctx.out(`Stopped ${slug} across all tenants (team ${ctx.teamId}); resume with \`${CLI} start ${slug}\`.\n`);
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
ctx.out(`Stopped ${slug} (team ${ctx.teamId}).\n`);
|
|
1185
|
+
}
|
|
1186
|
+
if (cancellation !== undefined) {
|
|
1187
|
+
return writeCancellationStarted(ctx, cancellation, slug);
|
|
1188
|
+
}
|
|
1189
|
+
return 0;
|
|
1195
1190
|
}
|
|
1196
1191
|
function writeCancellationStarted(ctx, cancellation, slug) {
|
|
1197
1192
|
ctx.out(`Started cancellation across ${cancellation.batchesStarted} deployment batch(es).\n`);
|
|
@@ -1204,510 +1199,475 @@ function writeCancellationStarted(ctx, cancellation, slug) {
|
|
|
1204
1199
|
}
|
|
1205
1200
|
return 0;
|
|
1206
1201
|
}
|
|
1207
|
-
export function cmdStart(slug, options) {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
slug,
|
|
1222
|
-
});
|
|
1223
|
-
if (ctx.json) {
|
|
1224
|
-
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1225
|
-
}
|
|
1226
|
-
else {
|
|
1227
|
-
ctx.out(`Started ${slug} (team ${ctx.teamId}).\n`);
|
|
1228
|
-
}
|
|
1229
|
-
return 0;
|
|
1230
|
-
}
|
|
1231
|
-
catch (error) {
|
|
1232
|
-
return renderApiError(error, ctx.err);
|
|
1233
|
-
}
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
export function cmdKill(slug, options) {
|
|
1237
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1238
|
-
var _a;
|
|
1239
|
-
if (slug === undefined || slug === "") {
|
|
1240
|
-
((_a = options.err) !== null && _a !== void 0 ? _a : ((t) => void process.stderr.write(t)))(`Usage: ${CLI} kill <slug> [--team <id>] [--json]\n`);
|
|
1241
|
-
return 1;
|
|
1242
|
-
}
|
|
1243
|
-
const ctx = yield prepare(options);
|
|
1244
|
-
if (typeof ctx === "number") {
|
|
1245
|
-
return ctx;
|
|
1246
|
-
}
|
|
1247
|
-
let accepted;
|
|
1248
|
-
try {
|
|
1249
|
-
accepted = yield ctx.client.killAgentkit({ teamId: ctx.teamId, slug });
|
|
1250
|
-
}
|
|
1251
|
-
catch (error) {
|
|
1252
|
-
return renderApiError(error, ctx.err);
|
|
1253
|
-
}
|
|
1202
|
+
export async function cmdStart(slug, options) {
|
|
1203
|
+
if (slug === undefined || slug === "") {
|
|
1204
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} start <slug> [--team <id>] [--json]\n`);
|
|
1205
|
+
return 1;
|
|
1206
|
+
}
|
|
1207
|
+
const ctx = await prepare(options);
|
|
1208
|
+
if (typeof ctx === "number") {
|
|
1209
|
+
return ctx;
|
|
1210
|
+
}
|
|
1211
|
+
try {
|
|
1212
|
+
const accepted = await ctx.client.startAgentkit({
|
|
1213
|
+
teamId: ctx.teamId,
|
|
1214
|
+
slug,
|
|
1215
|
+
});
|
|
1254
1216
|
if (ctx.json) {
|
|
1255
1217
|
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1256
1218
|
}
|
|
1257
1219
|
else {
|
|
1258
|
-
ctx.out(
|
|
1259
|
-
? `Killed ${slug} across all tenants (team ${ctx.teamId}): deploys locked, admission stopped.\n`
|
|
1260
|
-
: `Killed ${slug} (team ${ctx.teamId}): deploys locked, admission stopped.\n`);
|
|
1261
|
-
ctx.out(`Resume traffic with \`${CLI} unlock-deploys ${slug}\` then \`${CLI} start ${slug}\`.\n`);
|
|
1262
|
-
ctx.out(`Unlock deploys with \`${CLI} unlock-deploys ${slug}\`.\n`);
|
|
1263
|
-
}
|
|
1264
|
-
if (accepted.cancellation !== undefined) {
|
|
1265
|
-
return writeCancellationStarted(ctx, accepted.cancellation, slug);
|
|
1220
|
+
ctx.out(`Started ${slug} (team ${ctx.teamId}).\n`);
|
|
1266
1221
|
}
|
|
1267
1222
|
return 0;
|
|
1268
|
-
}
|
|
1223
|
+
}
|
|
1224
|
+
catch (error) {
|
|
1225
|
+
return renderApiError(error, ctx.err);
|
|
1226
|
+
}
|
|
1269
1227
|
}
|
|
1270
|
-
export function
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1228
|
+
export async function cmdKill(slug, options) {
|
|
1229
|
+
if (slug === undefined || slug === "") {
|
|
1230
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} kill <slug> [--team <id>] [--json]\n`);
|
|
1231
|
+
return 1;
|
|
1232
|
+
}
|
|
1233
|
+
const ctx = await prepare(options);
|
|
1234
|
+
if (typeof ctx === "number") {
|
|
1235
|
+
return ctx;
|
|
1236
|
+
}
|
|
1237
|
+
let accepted;
|
|
1238
|
+
try {
|
|
1239
|
+
accepted = await ctx.client.killAgentkit({ teamId: ctx.teamId, slug });
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
return renderApiError(error, ctx.err);
|
|
1243
|
+
}
|
|
1244
|
+
if (ctx.json) {
|
|
1245
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
ctx.out(accepted.scope === "global"
|
|
1249
|
+
? `Killed ${slug} across all tenants (team ${ctx.teamId}): deploys locked, admission stopped.\n`
|
|
1250
|
+
: `Killed ${slug} (team ${ctx.teamId}): deploys locked, admission stopped.\n`);
|
|
1251
|
+
ctx.out(`Resume traffic with \`${CLI} unlock-deploys ${slug}\` then \`${CLI} start ${slug}\`.\n`);
|
|
1252
|
+
ctx.out(`Unlock deploys with \`${CLI} unlock-deploys ${slug}\`.\n`);
|
|
1253
|
+
}
|
|
1254
|
+
if (accepted.cancellation !== undefined) {
|
|
1255
|
+
return writeCancellationStarted(ctx, accepted.cancellation, slug);
|
|
1256
|
+
}
|
|
1257
|
+
return 0;
|
|
1298
1258
|
}
|
|
1299
|
-
export function
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
teamId: ctx.teamId,
|
|
1314
|
-
slug,
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
catch (error) {
|
|
1318
|
-
return renderApiError(error, ctx.err);
|
|
1319
|
-
}
|
|
1259
|
+
export async function cmdUnlockDeploys(slug, options) {
|
|
1260
|
+
if (slug === undefined || slug === "") {
|
|
1261
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} unlock-deploys <slug> [--team <id>] [--json]\n`);
|
|
1262
|
+
return 1;
|
|
1263
|
+
}
|
|
1264
|
+
const ctx = await prepare(options);
|
|
1265
|
+
if (typeof ctx === "number") {
|
|
1266
|
+
return ctx;
|
|
1267
|
+
}
|
|
1268
|
+
try {
|
|
1269
|
+
const accepted = await ctx.client.unlockAgentkitDeploys({
|
|
1270
|
+
teamId: ctx.teamId,
|
|
1271
|
+
slug,
|
|
1272
|
+
});
|
|
1320
1273
|
if (ctx.json) {
|
|
1321
|
-
ctx.out(`${JSON.stringify(
|
|
1274
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1322
1275
|
}
|
|
1323
1276
|
else {
|
|
1324
|
-
ctx.out(`
|
|
1325
|
-
if (result.sessionsMarkedFailed !== undefined) {
|
|
1326
|
-
ctx.out(`Marked ${result.sessionsMarkedFailed} factory session(s) failed.\n`);
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
if (result.failed > 0) {
|
|
1330
|
-
ctx.err(`Failed to start ${result.failed} cancellation batch(es).\n`);
|
|
1331
|
-
return 1;
|
|
1277
|
+
ctx.out(`Unlocked deploys for ${slug} (team ${ctx.teamId}).\n`);
|
|
1332
1278
|
}
|
|
1333
1279
|
return 0;
|
|
1334
|
-
}
|
|
1280
|
+
}
|
|
1281
|
+
catch (error) {
|
|
1282
|
+
return renderApiError(error, ctx.err);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
export async function cmdCancelRuns(slug, options) {
|
|
1286
|
+
if (slug === undefined || slug === "") {
|
|
1287
|
+
(options.err ?? ((text) => void process.stderr.write(text)))(`Usage: ${CLI} cancel-runs <slug> [--team <id>] [--json]\n`);
|
|
1288
|
+
return 1;
|
|
1289
|
+
}
|
|
1290
|
+
const ctx = await prepare(options);
|
|
1291
|
+
if (typeof ctx === "number") {
|
|
1292
|
+
return ctx;
|
|
1293
|
+
}
|
|
1294
|
+
let result;
|
|
1295
|
+
try {
|
|
1296
|
+
result = await ctx.client.cancelAgentkitRuns({
|
|
1297
|
+
teamId: ctx.teamId,
|
|
1298
|
+
slug,
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
catch (error) {
|
|
1302
|
+
return renderApiError(error, ctx.err);
|
|
1303
|
+
}
|
|
1304
|
+
if (ctx.json) {
|
|
1305
|
+
ctx.out(`${JSON.stringify(result, null, 2)}\n`);
|
|
1306
|
+
}
|
|
1307
|
+
else {
|
|
1308
|
+
ctx.out(`Started cancellation across ${result.batchesStarted} deployment batch(es).\n`);
|
|
1309
|
+
if (result.sessionsMarkedFailed !== undefined) {
|
|
1310
|
+
ctx.out(`Marked ${result.sessionsMarkedFailed} factory session(s) failed.\n`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (result.failed > 0) {
|
|
1314
|
+
ctx.err(`Failed to start ${result.failed} cancellation batch(es).\n`);
|
|
1315
|
+
return 1;
|
|
1316
|
+
}
|
|
1317
|
+
return 0;
|
|
1335
1318
|
}
|
|
1336
1319
|
// ============================================================================
|
|
1337
1320
|
// event-repos <slug> --repo <owner/name>...
|
|
1338
1321
|
// ============================================================================
|
|
1339
|
-
export function cmdEventRepos(slug, repositories, options) {
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
}
|
|
1370
|
-
return 0;
|
|
1371
|
-
});
|
|
1322
|
+
export async function cmdEventRepos(slug, repositories, options) {
|
|
1323
|
+
if (slug === undefined || slug === "") {
|
|
1324
|
+
(options.err ?? ((text) => void process.stderr.write(text)))(`Usage: ${CLI} event-repos <slug> --repo <owner/name>... [--team <id>] [--json]\n`);
|
|
1325
|
+
return 1;
|
|
1326
|
+
}
|
|
1327
|
+
const ctx = await prepare(options);
|
|
1328
|
+
if (typeof ctx === "number") {
|
|
1329
|
+
return ctx;
|
|
1330
|
+
}
|
|
1331
|
+
let configured;
|
|
1332
|
+
try {
|
|
1333
|
+
configured = await ctx.client.setAgentkitEventRepositories({
|
|
1334
|
+
teamId: ctx.teamId,
|
|
1335
|
+
slug,
|
|
1336
|
+
repositories,
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
catch (error) {
|
|
1340
|
+
return renderApiError(error, ctx.err);
|
|
1341
|
+
}
|
|
1342
|
+
if (ctx.json) {
|
|
1343
|
+
ctx.out(`${JSON.stringify({ repositories: configured }, null, 2)}\n`);
|
|
1344
|
+
}
|
|
1345
|
+
else if (configured.length === 0) {
|
|
1346
|
+
ctx.out(`${slug} now watches no repositories.\n`);
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
ctx.out(`${slug} now watches ${configured.join(", ")}.\n`);
|
|
1350
|
+
}
|
|
1351
|
+
return 0;
|
|
1372
1352
|
}
|
|
1373
1353
|
// ============================================================================
|
|
1374
1354
|
// upgrade <slug>
|
|
1375
1355
|
// ============================================================================
|
|
1376
1356
|
/** Release ids are uuids; anything else the user typed is a version. */
|
|
1377
1357
|
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;
|
|
1378
|
-
export function cmdUpgrade(args, options) {
|
|
1379
|
-
return
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
success: (slug, releaseId) => `${slug} now runs release ${releaseId}.\n`,
|
|
1384
|
-
});
|
|
1358
|
+
export async function cmdUpgrade(args, options) {
|
|
1359
|
+
return cmdPinRelease(args, options, {
|
|
1360
|
+
usage: `Usage: ${CLI} upgrade <slug> [--release <id|version>] [--team <id>] [--json]\n`,
|
|
1361
|
+
pin: (client, request) => client.upgradeAgentkitInstall(request),
|
|
1362
|
+
success: (slug, releaseId) => `${slug} now runs release ${releaseId}.\n`,
|
|
1385
1363
|
});
|
|
1386
1364
|
}
|
|
1387
|
-
export function cmdRollback(args, options) {
|
|
1388
|
-
return
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
success: (slug, releaseId) => `${slug} rolled back to release ${releaseId}.\n`,
|
|
1393
|
-
});
|
|
1365
|
+
export async function cmdRollback(args, options) {
|
|
1366
|
+
return cmdPinRelease(args, options, {
|
|
1367
|
+
usage: `Usage: ${CLI} rollback <slug> [--release <id|version>] [--team <id>] [--json]\n`,
|
|
1368
|
+
pin: (client, request) => client.rollbackAgentkitInstall(request),
|
|
1369
|
+
success: (slug, releaseId) => `${slug} rolled back to release ${releaseId}.\n`,
|
|
1394
1370
|
});
|
|
1395
1371
|
}
|
|
1396
|
-
function cmdPinRelease(args, options, pin) {
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1372
|
+
async function cmdPinRelease(args, options, pin) {
|
|
1373
|
+
const { slug, release } = args;
|
|
1374
|
+
if (slug === undefined || slug === "") {
|
|
1375
|
+
(options.err ?? ((text) => void process.stderr.write(text)))(pin.usage);
|
|
1376
|
+
return 1;
|
|
1377
|
+
}
|
|
1378
|
+
const ctx = await prepare(options);
|
|
1379
|
+
if (typeof ctx === "number") {
|
|
1380
|
+
return ctx;
|
|
1381
|
+
}
|
|
1382
|
+
const selector = release?.trim() ?? "";
|
|
1383
|
+
let releaseId;
|
|
1384
|
+
try {
|
|
1385
|
+
releaseId = await pin.pin(ctx.client, {
|
|
1386
|
+
teamId: ctx.teamId,
|
|
1387
|
+
slug,
|
|
1388
|
+
...(selector === ""
|
|
1412
1389
|
? {}
|
|
1413
1390
|
: RELEASE_ID_RE.test(selector)
|
|
1414
1391
|
? { releaseId: selector }
|
|
1415
|
-
: { version: selector })
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1392
|
+
: { version: selector }),
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
catch (error) {
|
|
1396
|
+
return renderApiError(error, ctx.err);
|
|
1397
|
+
}
|
|
1398
|
+
if (ctx.json) {
|
|
1399
|
+
ctx.out(`${JSON.stringify({ releaseId }, null, 2)}\n`);
|
|
1400
|
+
}
|
|
1401
|
+
else {
|
|
1402
|
+
ctx.out(pin.success(slug, releaseId));
|
|
1403
|
+
}
|
|
1404
|
+
return 0;
|
|
1428
1405
|
}
|
|
1429
1406
|
// ============================================================================
|
|
1430
1407
|
// delete <slug>
|
|
1431
1408
|
// ============================================================================
|
|
1432
|
-
export function cmdDelete(slug, options) {
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
catch (error) {
|
|
1452
|
-
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
1453
|
-
return renderApiError(error, ctx.err);
|
|
1454
|
-
}
|
|
1455
|
-
try {
|
|
1456
|
-
accepted = yield ctx.client.deleteDeployment({
|
|
1457
|
-
teamId: ctx.teamId,
|
|
1458
|
-
slug,
|
|
1459
|
-
});
|
|
1460
|
-
architecture = "v1";
|
|
1461
|
-
}
|
|
1462
|
-
catch (v1Error) {
|
|
1463
|
-
return renderApiError(v1Error, ctx.err);
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
if (ctx.noWait) {
|
|
1467
|
-
if (ctx.json) {
|
|
1468
|
-
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1469
|
-
}
|
|
1470
|
-
else {
|
|
1471
|
-
ctx.out(`Delete accepted for ${slug} (status: ${accepted.status}).\n`);
|
|
1472
|
-
}
|
|
1473
|
-
return 0;
|
|
1474
|
-
}
|
|
1475
|
-
try {
|
|
1476
|
-
yield pollUntilGone(ctx, slug, () => architecture === "v2"
|
|
1477
|
-
? ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug })
|
|
1478
|
-
: ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
1479
|
-
}
|
|
1480
|
-
catch (error) {
|
|
1409
|
+
export async function cmdDelete(slug, options) {
|
|
1410
|
+
if (slug === undefined || slug === "") {
|
|
1411
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} delete <slug> [--team <id>] [--json] [--no-wait]\n`);
|
|
1412
|
+
return 1;
|
|
1413
|
+
}
|
|
1414
|
+
const ctx = await prepare(options);
|
|
1415
|
+
if (typeof ctx === "number") {
|
|
1416
|
+
return ctx;
|
|
1417
|
+
}
|
|
1418
|
+
// Same dispatch as `stop`: v1 409s architecture-v2 slugs, so the v2
|
|
1419
|
+
// DELETE goes first. A v2 404 (missing slug, or an older backend)
|
|
1420
|
+
// falls through to the v1 agent-serve delete.
|
|
1421
|
+
let architecture = "v2";
|
|
1422
|
+
let accepted;
|
|
1423
|
+
try {
|
|
1424
|
+
accepted = await ctx.client.deleteAgentkit({ teamId: ctx.teamId, slug });
|
|
1425
|
+
}
|
|
1426
|
+
catch (error) {
|
|
1427
|
+
if (!(error instanceof DeployApiError) || error.status !== 404) {
|
|
1481
1428
|
return renderApiError(error, ctx.err);
|
|
1482
1429
|
}
|
|
1483
|
-
if (ctx.json) {
|
|
1484
|
-
ctx.out(`${JSON.stringify({ slug, status: "deleted" }, null, 2)}\n`);
|
|
1485
|
-
}
|
|
1486
|
-
else {
|
|
1487
|
-
ctx.out(`Deleted ${slug} (team ${ctx.teamId}).\n`);
|
|
1488
|
-
}
|
|
1489
|
-
return 0;
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
// ============================================================================
|
|
1493
|
-
// rotate-token <slug>
|
|
1494
|
-
// ============================================================================
|
|
1495
|
-
export function cmdRotateToken(slug, options) {
|
|
1496
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1497
|
-
var _a;
|
|
1498
|
-
if (slug === undefined || slug === "") {
|
|
1499
|
-
((_a = options.err) !== null && _a !== void 0 ? _a : ((t) => void process.stderr.write(t)))(`Usage: ${CLI} rotate-token <slug> [--team <id>]\n`);
|
|
1500
|
-
return 1;
|
|
1501
|
-
}
|
|
1502
|
-
const ctx = yield prepare(options);
|
|
1503
|
-
if (typeof ctx === "number") {
|
|
1504
|
-
return ctx;
|
|
1505
|
-
}
|
|
1506
|
-
let aliasToken;
|
|
1507
1430
|
try {
|
|
1508
|
-
|
|
1509
|
-
client: ctx.client,
|
|
1431
|
+
accepted = await ctx.client.deleteDeployment({
|
|
1510
1432
|
teamId: ctx.teamId,
|
|
1511
1433
|
slug,
|
|
1512
1434
|
});
|
|
1513
|
-
|
|
1514
|
-
architecture === "v2"
|
|
1515
|
-
? yield ctx.client.rotateAgentsdkAliasToken({
|
|
1516
|
-
teamId: ctx.teamId,
|
|
1517
|
-
slug,
|
|
1518
|
-
})
|
|
1519
|
-
: yield ctx.client.rotateAliasToken({
|
|
1520
|
-
teamId: ctx.teamId,
|
|
1521
|
-
slug,
|
|
1522
|
-
});
|
|
1435
|
+
architecture = "v1";
|
|
1523
1436
|
}
|
|
1524
|
-
catch (
|
|
1525
|
-
return renderApiError(
|
|
1437
|
+
catch (v1Error) {
|
|
1438
|
+
return renderApiError(v1Error, ctx.err);
|
|
1526
1439
|
}
|
|
1440
|
+
}
|
|
1441
|
+
if (ctx.noWait) {
|
|
1527
1442
|
if (ctx.json) {
|
|
1528
|
-
ctx.out(`${JSON.stringify(
|
|
1529
|
-
|
|
1443
|
+
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1444
|
+
}
|
|
1445
|
+
else {
|
|
1446
|
+
ctx.out(`Delete accepted for ${slug} (status: ${accepted.status}).\n`);
|
|
1530
1447
|
}
|
|
1531
|
-
ctx.out(`Rotated the alias token for ${slug}. The old token stopped working immediately.\n${aliasTokenBlock(aliasToken)}`);
|
|
1532
1448
|
return 0;
|
|
1533
|
-
}
|
|
1449
|
+
}
|
|
1450
|
+
try {
|
|
1451
|
+
await pollUntilGone(ctx, slug, () => architecture === "v2"
|
|
1452
|
+
? ctx.client.getAgentkitDeploy({ teamId: ctx.teamId, slug })
|
|
1453
|
+
: ctx.client.getDeployment({ teamId: ctx.teamId, slug }));
|
|
1454
|
+
}
|
|
1455
|
+
catch (error) {
|
|
1456
|
+
return renderApiError(error, ctx.err);
|
|
1457
|
+
}
|
|
1458
|
+
if (ctx.json) {
|
|
1459
|
+
ctx.out(`${JSON.stringify({ slug, status: "deleted" }, null, 2)}\n`);
|
|
1460
|
+
}
|
|
1461
|
+
else {
|
|
1462
|
+
ctx.out(`Deleted ${slug} (team ${ctx.teamId}).\n`);
|
|
1463
|
+
}
|
|
1464
|
+
return 0;
|
|
1534
1465
|
}
|
|
1535
1466
|
// ============================================================================
|
|
1536
|
-
//
|
|
1467
|
+
// rotate-token <slug>
|
|
1537
1468
|
// ============================================================================
|
|
1538
|
-
function
|
|
1539
|
-
|
|
1540
|
-
|
|
1469
|
+
export async function cmdRotateToken(slug, options) {
|
|
1470
|
+
if (slug === undefined || slug === "") {
|
|
1471
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} rotate-token <slug> [--team <id>]\n`);
|
|
1472
|
+
return 1;
|
|
1473
|
+
}
|
|
1474
|
+
const ctx = await prepare(options);
|
|
1475
|
+
if (typeof ctx === "number") {
|
|
1476
|
+
return ctx;
|
|
1477
|
+
}
|
|
1478
|
+
let aliasToken;
|
|
1479
|
+
try {
|
|
1480
|
+
const architecture = await deploymentArchitecture({
|
|
1541
1481
|
client: ctx.client,
|
|
1542
1482
|
teamId: ctx.teamId,
|
|
1543
1483
|
slug,
|
|
1544
1484
|
});
|
|
1485
|
+
aliasToken =
|
|
1486
|
+
architecture === "v2"
|
|
1487
|
+
? await ctx.client.rotateAgentsdkAliasToken({
|
|
1488
|
+
teamId: ctx.teamId,
|
|
1489
|
+
slug,
|
|
1490
|
+
})
|
|
1491
|
+
: await ctx.client.rotateAliasToken({
|
|
1492
|
+
teamId: ctx.teamId,
|
|
1493
|
+
slug,
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
catch (error) {
|
|
1497
|
+
return renderApiError(error, ctx.err);
|
|
1498
|
+
}
|
|
1499
|
+
if (ctx.json) {
|
|
1500
|
+
ctx.out(`${JSON.stringify({ aliasToken }, null, 2)}\n`);
|
|
1501
|
+
return 0;
|
|
1502
|
+
}
|
|
1503
|
+
ctx.out(`Rotated the alias token for ${slug}. The old token stopped working immediately.\n${aliasTokenBlock(aliasToken)}`);
|
|
1504
|
+
return 0;
|
|
1505
|
+
}
|
|
1506
|
+
// ============================================================================
|
|
1507
|
+
// secrets set|list|unset
|
|
1508
|
+
// ============================================================================
|
|
1509
|
+
async function secretDeploymentArchitecture(ctx, slug) {
|
|
1510
|
+
return deploymentArchitecture({
|
|
1511
|
+
client: ctx.client,
|
|
1512
|
+
teamId: ctx.teamId,
|
|
1513
|
+
slug,
|
|
1545
1514
|
});
|
|
1546
1515
|
}
|
|
1547
|
-
export function cmdSecretsSet(slug, specs, options) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
const
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
if (nameError !== undefined) {
|
|
1566
|
-
usageErr(`${nameError}\n`);
|
|
1567
|
-
return 1;
|
|
1568
|
-
}
|
|
1569
|
-
parsed.push(eq === -1 ? { name } : { name, value: spec.slice(eq + 1) });
|
|
1570
|
-
}
|
|
1571
|
-
const hasArgvValues = parsed.some(entry => entry.value !== undefined);
|
|
1572
|
-
if (hasArgvValues && options.fromArgv !== true) {
|
|
1573
|
-
usageErr(`Refusing NAME=VALUE on argv (shows up in shell history and agent-captured terminals).\n` +
|
|
1574
|
-
`Pass names only:\n` +
|
|
1575
|
-
` ${CLI} secrets set ${slug} NAME\n` +
|
|
1576
|
-
`Hidden prompt on a TTY. One NAME reads all of stdin, including newlines:\n` +
|
|
1577
|
-
` ${CLI} secrets set ${slug} NAME < ./secret.pem\n` +
|
|
1578
|
-
`Several NAMEs take one stdin line each.\n`);
|
|
1516
|
+
export async function cmdSecretsSet(slug, specs, options) {
|
|
1517
|
+
const usageErr = options.err ?? ((t) => void process.stderr.write(t));
|
|
1518
|
+
if (slug === undefined || slug === "" || specs.length === 0) {
|
|
1519
|
+
usageErr(`Usage: ${CLI} secrets set <slug> NAME [NAME2 ...] [--team <id>] [--json]\n` +
|
|
1520
|
+
` Hidden prompt on a TTY. One NAME reads all of stdin (redirect a PEM).\n` +
|
|
1521
|
+
` Several NAMEs: one stdin line each.\n` +
|
|
1522
|
+
` Opt-in (leaks into argv / agent terminals): NAME=VALUE … --from-argv\n`);
|
|
1523
|
+
return 1;
|
|
1524
|
+
}
|
|
1525
|
+
// Parse and validate every name before prompting for any value, so a
|
|
1526
|
+
// typo in the last name cannot waste typed-in secrets.
|
|
1527
|
+
const parsed = [];
|
|
1528
|
+
for (const spec of specs) {
|
|
1529
|
+
const eq = spec.indexOf("=");
|
|
1530
|
+
const name = eq === -1 ? spec : spec.slice(0, eq);
|
|
1531
|
+
const nameError = validateSettableSecretName(name);
|
|
1532
|
+
if (nameError !== undefined) {
|
|
1533
|
+
usageErr(`${nameError}\n`);
|
|
1579
1534
|
return 1;
|
|
1580
1535
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1536
|
+
parsed.push(eq === -1 ? { name } : { name, value: spec.slice(eq + 1) });
|
|
1537
|
+
}
|
|
1538
|
+
const hasArgvValues = parsed.some(entry => entry.value !== undefined);
|
|
1539
|
+
if (hasArgvValues && options.fromArgv !== true) {
|
|
1540
|
+
usageErr(`Refusing NAME=VALUE on argv (shows up in shell history and agent-captured terminals).\n` +
|
|
1541
|
+
`Pass names only:\n` +
|
|
1542
|
+
` ${CLI} secrets set ${slug} NAME\n` +
|
|
1543
|
+
`Hidden prompt on a TTY. One NAME reads all of stdin, including newlines:\n` +
|
|
1544
|
+
` ${CLI} secrets set ${slug} NAME < ./secret.pem\n` +
|
|
1545
|
+
`Several NAMEs take one stdin line each.\n`);
|
|
1546
|
+
return 1;
|
|
1547
|
+
}
|
|
1548
|
+
if (hasArgvValues) {
|
|
1549
|
+
usageErr(`warning: --from-argv puts secret values in argv (shell history and agent-captured terminals).\n`);
|
|
1550
|
+
}
|
|
1551
|
+
const ctx = await prepare(options);
|
|
1552
|
+
if (typeof ctx === "number") {
|
|
1553
|
+
return ctx;
|
|
1554
|
+
}
|
|
1555
|
+
const stdin = options.stdin ?? process.stdin;
|
|
1556
|
+
const secrets = [];
|
|
1557
|
+
let secretNames;
|
|
1558
|
+
let architecture;
|
|
1559
|
+
try {
|
|
1560
|
+
const only = parsed.length === 1 ? parsed[0] : undefined;
|
|
1561
|
+
if (only !== undefined && only.value === undefined && stdin.isTTY !== true) {
|
|
1562
|
+
secrets.push({
|
|
1563
|
+
name: only.name,
|
|
1564
|
+
value: await readWholeStdinValue(stdin, only.name),
|
|
1565
|
+
});
|
|
1587
1566
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
let architecture;
|
|
1592
|
-
try {
|
|
1593
|
-
const only = parsed.length === 1 ? parsed[0] : undefined;
|
|
1594
|
-
if (only !== undefined && only.value === undefined && stdin.isTTY !== true) {
|
|
1567
|
+
else {
|
|
1568
|
+
const readValue = createSecretValueReader(stdin, ctx.err);
|
|
1569
|
+
for (const entry of parsed) {
|
|
1595
1570
|
secrets.push({
|
|
1596
|
-
name:
|
|
1597
|
-
value:
|
|
1571
|
+
name: entry.name,
|
|
1572
|
+
value: entry.value ?? (await readValue(entry.name)),
|
|
1598
1573
|
});
|
|
1599
1574
|
}
|
|
1600
|
-
else {
|
|
1601
|
-
const readValue = createSecretValueReader(stdin, ctx.err);
|
|
1602
|
-
for (const entry of parsed) {
|
|
1603
|
-
secrets.push({
|
|
1604
|
-
name: entry.name,
|
|
1605
|
-
value: (_c = entry.value) !== null && _c !== void 0 ? _c : (yield readValue(entry.name)),
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
architecture = yield secretDeploymentArchitecture(ctx, slug);
|
|
1610
|
-
secretNames =
|
|
1611
|
-
architecture === "v2"
|
|
1612
|
-
? yield ctx.client.setAgentkitSecrets({
|
|
1613
|
-
teamId: ctx.teamId,
|
|
1614
|
-
slug,
|
|
1615
|
-
secrets,
|
|
1616
|
-
})
|
|
1617
|
-
: yield ctx.client.setSecrets({
|
|
1618
|
-
teamId: ctx.teamId,
|
|
1619
|
-
slug,
|
|
1620
|
-
secrets,
|
|
1621
|
-
});
|
|
1622
|
-
}
|
|
1623
|
-
catch (error) {
|
|
1624
|
-
return renderApiError(error, ctx.err);
|
|
1625
1575
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
:
|
|
1576
|
+
architecture = await secretDeploymentArchitecture(ctx, slug);
|
|
1577
|
+
secretNames =
|
|
1578
|
+
architecture === "v2"
|
|
1579
|
+
? await ctx.client.setAgentkitSecrets({
|
|
1580
|
+
teamId: ctx.teamId,
|
|
1581
|
+
slug,
|
|
1582
|
+
secrets,
|
|
1583
|
+
})
|
|
1584
|
+
: await ctx.client.setSecrets({
|
|
1585
|
+
teamId: ctx.teamId,
|
|
1586
|
+
slug,
|
|
1587
|
+
secrets,
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
catch (error) {
|
|
1591
|
+
return renderApiError(error, ctx.err);
|
|
1592
|
+
}
|
|
1593
|
+
if (ctx.json) {
|
|
1594
|
+
ctx.out(`${JSON.stringify({ secretNames }, null, 2)}\n`);
|
|
1635
1595
|
return 0;
|
|
1636
|
-
}
|
|
1596
|
+
}
|
|
1597
|
+
ctx.out(`Set ${secrets.length} secret${secrets.length === 1 ? "" : "s"} on ${slug}.\n` +
|
|
1598
|
+
`Secrets now set: ${secretNames.join(", ")}\n` +
|
|
1599
|
+
(architecture === "v2"
|
|
1600
|
+
? `The next task picks them up; no redeploy is required.\n`
|
|
1601
|
+
: `The engine picks them up on its next deploy.\n`));
|
|
1602
|
+
return 0;
|
|
1637
1603
|
}
|
|
1638
|
-
export function cmdSecretsList(slug, options) {
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
}
|
|
1660
|
-
if (ctx.json) {
|
|
1661
|
-
ctx.out(`${JSON.stringify({ secrets }, null, 2)}\n`);
|
|
1662
|
-
return 0;
|
|
1663
|
-
}
|
|
1664
|
-
if (secrets.length === 0) {
|
|
1665
|
-
ctx.out(`No secrets set on ${slug}. Add one with \`${CLI} secrets set ${slug} NAME\`.\n`);
|
|
1666
|
-
return 0;
|
|
1667
|
-
}
|
|
1668
|
-
ctx.out(`${renderTable(["NAME", "CREATED"], secrets.map(secret => [secret.name, secret.createdAt]))}\n`);
|
|
1604
|
+
export async function cmdSecretsList(slug, options) {
|
|
1605
|
+
if (slug === undefined || slug === "") {
|
|
1606
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} secrets list <slug> [--team <id>] [--json]\n`);
|
|
1607
|
+
return 1;
|
|
1608
|
+
}
|
|
1609
|
+
const ctx = await prepare(options);
|
|
1610
|
+
if (typeof ctx === "number") {
|
|
1611
|
+
return ctx;
|
|
1612
|
+
}
|
|
1613
|
+
let secrets;
|
|
1614
|
+
try {
|
|
1615
|
+
const architecture = await secretDeploymentArchitecture(ctx, slug);
|
|
1616
|
+
secrets =
|
|
1617
|
+
architecture === "v2"
|
|
1618
|
+
? await ctx.client.listAgentkitSecrets({ teamId: ctx.teamId, slug })
|
|
1619
|
+
: await ctx.client.listSecrets({ teamId: ctx.teamId, slug });
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
return renderApiError(error, ctx.err);
|
|
1623
|
+
}
|
|
1624
|
+
if (ctx.json) {
|
|
1625
|
+
ctx.out(`${JSON.stringify({ secrets }, null, 2)}\n`);
|
|
1669
1626
|
return 0;
|
|
1670
|
-
}
|
|
1627
|
+
}
|
|
1628
|
+
if (secrets.length === 0) {
|
|
1629
|
+
ctx.out(`No secrets set on ${slug}. Add one with \`${CLI} secrets set ${slug} NAME\`.\n`);
|
|
1630
|
+
return 0;
|
|
1631
|
+
}
|
|
1632
|
+
ctx.out(`${renderTable(["NAME", "CREATED"], secrets.map(secret => [secret.name, secret.createdAt]))}\n`);
|
|
1633
|
+
return 0;
|
|
1671
1634
|
}
|
|
1672
|
-
export function cmdSecretsUnset(slug, name, options) {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
name,
|
|
1690
|
-
});
|
|
1691
|
-
}
|
|
1692
|
-
else {
|
|
1693
|
-
yield ctx.client.deleteSecret({ teamId: ctx.teamId, slug, name });
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
catch (error) {
|
|
1697
|
-
if (error instanceof DeployApiError && error.status === 404) {
|
|
1698
|
-
ctx.err(`No secret named ${name} on deployment ${slug} — check \`${CLI} secrets list ${slug}\` (and the slug / --team).\n`);
|
|
1699
|
-
return 1;
|
|
1700
|
-
}
|
|
1701
|
-
return renderApiError(error, ctx.err);
|
|
1702
|
-
}
|
|
1703
|
-
if (ctx.json) {
|
|
1704
|
-
ctx.out(`${JSON.stringify({ removed: name }, null, 2)}\n`);
|
|
1635
|
+
export async function cmdSecretsUnset(slug, name, options) {
|
|
1636
|
+
if (slug === undefined || slug === "" || name === undefined || name === "") {
|
|
1637
|
+
(options.err ?? ((t) => void process.stderr.write(t)))(`Usage: ${CLI} secrets unset <slug> NAME [--team <id>]\n`);
|
|
1638
|
+
return 1;
|
|
1639
|
+
}
|
|
1640
|
+
const ctx = await prepare(options);
|
|
1641
|
+
if (typeof ctx === "number") {
|
|
1642
|
+
return ctx;
|
|
1643
|
+
}
|
|
1644
|
+
try {
|
|
1645
|
+
const architecture = await secretDeploymentArchitecture(ctx, slug);
|
|
1646
|
+
if (architecture === "v2") {
|
|
1647
|
+
await ctx.client.deleteAgentkitSecret({
|
|
1648
|
+
teamId: ctx.teamId,
|
|
1649
|
+
slug,
|
|
1650
|
+
name,
|
|
1651
|
+
});
|
|
1705
1652
|
}
|
|
1706
1653
|
else {
|
|
1707
|
-
ctx.
|
|
1654
|
+
await ctx.client.deleteSecret({ teamId: ctx.teamId, slug, name });
|
|
1708
1655
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1656
|
+
}
|
|
1657
|
+
catch (error) {
|
|
1658
|
+
if (error instanceof DeployApiError && error.status === 404) {
|
|
1659
|
+
ctx.err(`No secret named ${name} on deployment ${slug} — check \`${CLI} secrets list ${slug}\` (and the slug / --team).\n`);
|
|
1660
|
+
return 1;
|
|
1661
|
+
}
|
|
1662
|
+
return renderApiError(error, ctx.err);
|
|
1663
|
+
}
|
|
1664
|
+
if (ctx.json) {
|
|
1665
|
+
ctx.out(`${JSON.stringify({ removed: name }, null, 2)}\n`);
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
ctx.out(`Removed secret ${name} from ${slug}.\n`);
|
|
1669
|
+
}
|
|
1670
|
+
return 0;
|
|
1711
1671
|
}
|
|
1712
1672
|
/**
|
|
1713
1673
|
* Returns a reader for interactive secret values: hidden (no-echo) prompt
|
|
@@ -1721,13 +1681,13 @@ function createSecretValueReader(stdin, err) {
|
|
|
1721
1681
|
return name => promptHidden(stdin, err, `Value for ${name}: `);
|
|
1722
1682
|
}
|
|
1723
1683
|
const readLine = createLineReader(stdin);
|
|
1724
|
-
return (name) =>
|
|
1725
|
-
const line =
|
|
1684
|
+
return async (name) => {
|
|
1685
|
+
const line = await readLine();
|
|
1726
1686
|
if (line === undefined) {
|
|
1727
1687
|
throw new DeployApiError(`stdin ended before a value for ${name} was provided (pipe one line per secret).`, 0);
|
|
1728
1688
|
}
|
|
1729
1689
|
return line;
|
|
1730
|
-
}
|
|
1690
|
+
};
|
|
1731
1691
|
}
|
|
1732
1692
|
/** Longest mask run drawn while typing a secret; the count carries the rest. */
|
|
1733
1693
|
const SECRET_MASK_MAX_WIDTH = 24;
|
|
@@ -1738,9 +1698,8 @@ const SECRET_MASK_MAX_WIDTH = 24;
|
|
|
1738
1698
|
* mask is clamped to the terminal width so the redraw stays on one line.
|
|
1739
1699
|
*/
|
|
1740
1700
|
function renderSecretPromptLine(prompt, length, dim) {
|
|
1741
|
-
var _a;
|
|
1742
1701
|
const count = `${length} char${length === 1 ? "" : "s"}`;
|
|
1743
|
-
const room = (
|
|
1702
|
+
const room = (process.stderr.columns ?? 80) - prompt.length - count.length - 2;
|
|
1744
1703
|
const maskWidth = Math.max(0, Math.min(length, SECRET_MASK_MAX_WIDTH, room));
|
|
1745
1704
|
// `·` over `•`: the rest of the CLI's progress output already uses it, and
|
|
1746
1705
|
// fonts that ship no bullet glyph draw a tofu box in its place.
|
|
@@ -1754,19 +1713,17 @@ function renderSecretPromptLine(prompt, length, dim) {
|
|
|
1754
1713
|
*/
|
|
1755
1714
|
function promptHidden(stdin, err, prompt) {
|
|
1756
1715
|
return new Promise((resolvePromise, reject) => {
|
|
1757
|
-
var _a;
|
|
1758
1716
|
const { dim } = stderrPalette();
|
|
1759
1717
|
let value = "";
|
|
1760
1718
|
const draw = () => {
|
|
1761
1719
|
err(renderSecretPromptLine(prompt, value.length, dim));
|
|
1762
1720
|
};
|
|
1763
1721
|
draw();
|
|
1764
|
-
|
|
1722
|
+
stdin.setRawMode?.(true);
|
|
1765
1723
|
stdin.resume();
|
|
1766
1724
|
const cleanup = () => {
|
|
1767
|
-
var _a;
|
|
1768
1725
|
stdin.off("data", onData);
|
|
1769
|
-
|
|
1726
|
+
stdin.setRawMode?.(false);
|
|
1770
1727
|
stdin.pause();
|
|
1771
1728
|
draw();
|
|
1772
1729
|
err("\n");
|
|
@@ -1801,14 +1758,12 @@ function promptHidden(stdin, err, prompt) {
|
|
|
1801
1758
|
* `echo value | secrets set slug NAME` still stores `value`. Internal
|
|
1802
1759
|
* newlines stay, which is what a redirected PEM needs.
|
|
1803
1760
|
*/
|
|
1804
|
-
function readWholeStdinValue(stdin, name) {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
return stripOneTrailingNewline(raw);
|
|
1811
|
-
});
|
|
1761
|
+
async function readWholeStdinValue(stdin, name) {
|
|
1762
|
+
const raw = await readStreamText(stdin);
|
|
1763
|
+
if (raw === "") {
|
|
1764
|
+
throw new DeployApiError(`stdin ended before a value for ${name} was provided.`, 0);
|
|
1765
|
+
}
|
|
1766
|
+
return stripOneTrailingNewline(raw);
|
|
1812
1767
|
}
|
|
1813
1768
|
function stripOneTrailingNewline(value) {
|
|
1814
1769
|
if (value.endsWith("\r\n")) {
|
|
@@ -1850,13 +1805,12 @@ export function createLineReader(stream) {
|
|
|
1850
1805
|
let ended = false;
|
|
1851
1806
|
const waiters = [];
|
|
1852
1807
|
const drain = () => {
|
|
1853
|
-
var _a, _b;
|
|
1854
1808
|
while (waiters.length > 0) {
|
|
1855
1809
|
const newline = buffer.indexOf("\n");
|
|
1856
1810
|
if (newline !== -1) {
|
|
1857
1811
|
const line = buffer.slice(0, newline).replace(/\r$/, "");
|
|
1858
1812
|
buffer = buffer.slice(newline + 1);
|
|
1859
|
-
|
|
1813
|
+
waiters.shift()?.(line);
|
|
1860
1814
|
continue;
|
|
1861
1815
|
}
|
|
1862
1816
|
if (!ended) {
|
|
@@ -1865,7 +1819,7 @@ export function createLineReader(stream) {
|
|
|
1865
1819
|
// Stream ended: a trailing unterminated line still counts once.
|
|
1866
1820
|
const rest = buffer;
|
|
1867
1821
|
buffer = "";
|
|
1868
|
-
|
|
1822
|
+
waiters.shift()?.(rest === "" ? undefined : rest.replace(/\r$/, ""));
|
|
1869
1823
|
}
|
|
1870
1824
|
};
|
|
1871
1825
|
stream.on("data", (chunk) => {
|
|
@@ -1892,74 +1846,72 @@ export function createLineReader(stream) {
|
|
|
1892
1846
|
* status so create → checkout → start is visible. Throws DeployApiError
|
|
1893
1847
|
* on request failures or timeout.
|
|
1894
1848
|
*/
|
|
1895
|
-
function pollUntil(ctx, slug, terminal, fetchDetail) {
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
if (
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
sawProgress = true;
|
|
1936
|
-
}
|
|
1937
|
-
else if (label !== lastLabel) {
|
|
1938
|
-
ctx.err(`${slug}: ${label}\n`);
|
|
1939
|
-
}
|
|
1940
|
-
lastLabel = label;
|
|
1849
|
+
async function pollUntil(ctx, slug, terminal, fetchDetail) {
|
|
1850
|
+
const startedAt = Date.now();
|
|
1851
|
+
const tty = process.stderr.isTTY === true;
|
|
1852
|
+
let lastLabel;
|
|
1853
|
+
let sawProgress = false;
|
|
1854
|
+
const report = (detail) => {
|
|
1855
|
+
if (ctx.json) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
const label = deployProgressLabel(detail);
|
|
1859
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1000);
|
|
1860
|
+
if (tty) {
|
|
1861
|
+
ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
|
|
1862
|
+
sawProgress = true;
|
|
1863
|
+
}
|
|
1864
|
+
else if (label !== lastLabel) {
|
|
1865
|
+
ctx.err(`${slug}: ${label}\n`);
|
|
1866
|
+
}
|
|
1867
|
+
lastLabel = label;
|
|
1868
|
+
};
|
|
1869
|
+
const finishProgressLine = () => {
|
|
1870
|
+
if (tty && sawProgress) {
|
|
1871
|
+
ctx.err("\n");
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
for (;;) {
|
|
1875
|
+
let detail;
|
|
1876
|
+
try {
|
|
1877
|
+
detail = await fetchDetail();
|
|
1878
|
+
}
|
|
1879
|
+
catch (error) {
|
|
1880
|
+
// Capacity / rate-limit on GET is not a bake failure. The v2 accept
|
|
1881
|
+
// already started freeze; keep polling until the wait budget expires.
|
|
1882
|
+
if (isTransientDeployStatusError(error) && Date.now() - startedAt < ctx.timeoutMs) {
|
|
1883
|
+
if (!ctx.json) {
|
|
1884
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1000);
|
|
1885
|
+
const label = "Waiting for status (server busy)";
|
|
1886
|
+
if (tty) {
|
|
1887
|
+
ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
|
|
1888
|
+
sawProgress = true;
|
|
1941
1889
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1890
|
+
else if (label !== lastLabel) {
|
|
1891
|
+
ctx.err(`${slug}: ${label}\n`);
|
|
1892
|
+
}
|
|
1893
|
+
lastLabel = label;
|
|
1946
1894
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
if (terminal.has(detail.status)) {
|
|
1952
|
-
finishProgressLine();
|
|
1953
|
-
return detail;
|
|
1954
|
-
}
|
|
1955
|
-
if (Date.now() - startedAt >= ctx.timeoutMs) {
|
|
1956
|
-
finishProgressLine();
|
|
1957
|
-
const last = deployProgressLabel(detail);
|
|
1958
|
-
throw new DeployApiError(`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}\`.`, 0);
|
|
1895
|
+
const waitMs = transientDeployStatusWaitMs(error, ctx.pollIntervalMs);
|
|
1896
|
+
const remaining = ctx.timeoutMs - (Date.now() - startedAt);
|
|
1897
|
+
await sleep(Math.max(0, Math.min(waitMs, remaining)));
|
|
1898
|
+
continue;
|
|
1959
1899
|
}
|
|
1960
|
-
|
|
1900
|
+
finishProgressLine();
|
|
1901
|
+
throw error;
|
|
1961
1902
|
}
|
|
1962
|
-
|
|
1903
|
+
report(detail);
|
|
1904
|
+
if (terminal.has(detail.status)) {
|
|
1905
|
+
finishProgressLine();
|
|
1906
|
+
return detail;
|
|
1907
|
+
}
|
|
1908
|
+
if (Date.now() - startedAt >= ctx.timeoutMs) {
|
|
1909
|
+
finishProgressLine();
|
|
1910
|
+
const last = deployProgressLabel(detail);
|
|
1911
|
+
throw new DeployApiError(`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}\`.`, 0);
|
|
1912
|
+
}
|
|
1913
|
+
await sleep(ctx.pollIntervalMs);
|
|
1914
|
+
}
|
|
1963
1915
|
}
|
|
1964
1916
|
/** Load-shed / rate-limit on status GET — the deploy itself may still be baking. */
|
|
1965
1917
|
function isTransientDeployStatusError(error) {
|
|
@@ -1977,46 +1929,44 @@ function transientDeployStatusWaitMs(error, pollIntervalMs) {
|
|
|
1977
1929
|
* Wait until GET 404s (the delete reconciler purged the record). Treats
|
|
1978
1930
|
* other errors as failures. Throws DeployApiError on timeout.
|
|
1979
1931
|
*/
|
|
1980
|
-
function pollUntilGone(ctx, slug, getDetail) {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
if (error instanceof DeployApiError && error.status === 404) {
|
|
2007
|
-
finishProgressLine();
|
|
2008
|
-
return;
|
|
2009
|
-
}
|
|
2010
|
-
finishProgressLine();
|
|
2011
|
-
throw error;
|
|
2012
|
-
}
|
|
2013
|
-
if (Date.now() - startedAt >= ctx.timeoutMs) {
|
|
1932
|
+
async function pollUntilGone(ctx, slug, getDetail) {
|
|
1933
|
+
const startedAt = Date.now();
|
|
1934
|
+
const tty = process.stderr.isTTY === true;
|
|
1935
|
+
let sawProgress = false;
|
|
1936
|
+
const report = (label) => {
|
|
1937
|
+
if (ctx.json) {
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
const elapsed = Math.round((Date.now() - startedAt) / 1000);
|
|
1941
|
+
if (tty) {
|
|
1942
|
+
ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
|
|
1943
|
+
sawProgress = true;
|
|
1944
|
+
}
|
|
1945
|
+
};
|
|
1946
|
+
const finishProgressLine = () => {
|
|
1947
|
+
if (tty && sawProgress) {
|
|
1948
|
+
ctx.err("\n");
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
for (;;) {
|
|
1952
|
+
try {
|
|
1953
|
+
const detail = await getDetail();
|
|
1954
|
+
report(deployProgressLabel(detail));
|
|
1955
|
+
}
|
|
1956
|
+
catch (error) {
|
|
1957
|
+
if (error instanceof DeployApiError && error.status === 404) {
|
|
2014
1958
|
finishProgressLine();
|
|
2015
|
-
|
|
1959
|
+
return;
|
|
2016
1960
|
}
|
|
2017
|
-
|
|
1961
|
+
finishProgressLine();
|
|
1962
|
+
throw error;
|
|
2018
1963
|
}
|
|
2019
|
-
|
|
1964
|
+
if (Date.now() - startedAt >= ctx.timeoutMs) {
|
|
1965
|
+
finishProgressLine();
|
|
1966
|
+
throw new DeployApiError(`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}\`.`, 0);
|
|
1967
|
+
}
|
|
1968
|
+
await sleep(ctx.pollIntervalMs);
|
|
1969
|
+
}
|
|
2020
1970
|
}
|
|
2021
1971
|
function aliasTokenBlock(aliasToken) {
|
|
2022
1972
|
return (`\n Alias token (shown ONCE — save it now, it cannot be retrieved again):\n\n` +
|
|
@@ -2038,10 +1988,10 @@ export function renderApiError(error, err, context) {
|
|
|
2038
1988
|
if (error instanceof DeployApiError) {
|
|
2039
1989
|
err(`${error.message}\n`);
|
|
2040
1990
|
if (error.status === 409 && error.message.includes("application multiTenant does not match")) {
|
|
2041
|
-
if (
|
|
1991
|
+
if (context?.multiTenant === true) {
|
|
2042
1992
|
err("This slug is a single-tenant application. Redeploy it without --multi-tenant.\n");
|
|
2043
1993
|
}
|
|
2044
|
-
else if (
|
|
1994
|
+
else if (context?.teamId === MULTI_TENANT_HTTP_DEPLOY_TEAM_ID) {
|
|
2045
1995
|
err("This slug is a Cursor-managed (multi-tenant) product. Redeploy it with --multi-tenant.\n");
|
|
2046
1996
|
}
|
|
2047
1997
|
}
|
|
@@ -2050,9 +2000,9 @@ export function renderApiError(error, err, context) {
|
|
|
2050
2000
|
throw error;
|
|
2051
2001
|
}
|
|
2052
2002
|
export function renderTable(header, rows) {
|
|
2053
|
-
const widths = header.map((h, i) => Math.max(h.length, ...rows.map(row =>
|
|
2003
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map(row => (row[i] ?? "").length)));
|
|
2054
2004
|
const renderRow = (row) => row
|
|
2055
|
-
.map((cell, i) =>
|
|
2005
|
+
.map((cell, i) => cell.padEnd(widths[i] ?? 0))
|
|
2056
2006
|
.join(" ")
|
|
2057
2007
|
.trimEnd();
|
|
2058
2008
|
return [renderRow(header), ...rows.map(renderRow)].join("\n");
|