@cursor/bdk 0.2.9 → 0.2.10
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/{reference_skills.md.ClkMoQsV.js → reference_skills.md.C0guGcH0.js} +3 -3
- 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 +38 -38
- 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.js +410 -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-DXm_VXAQ.css +1 -0
- package/dist/playground/assets/{index-Co4CUVTQ.js → index-JekUQERK.js} +45 -45
- 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
- /package/dist/docs/assets/{reference_skills.md.ClkMoQsV.lean.js → reference_skills.md.C0guGcH0.lean.js} +0 -0
|
@@ -10,16 +10,6 @@
|
|
|
10
10
|
* {@link FileConflictError}, and the backend reports
|
|
11
11
|
* `host.files.capabilities.conflictDetection`.
|
|
12
12
|
*/
|
|
13
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var _a;
|
|
23
13
|
import { createHash } from "node:crypto";
|
|
24
14
|
import { FileConflictError, } from "../files.js";
|
|
25
15
|
import { mapBounded } from "../internal/bounded.js";
|
|
@@ -40,9 +30,9 @@ export const AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR = "AGENT_SERVE_STORE_SOURCE_ID"
|
|
|
40
30
|
*/
|
|
41
31
|
export const AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_ENV_VAR = "AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID";
|
|
42
32
|
/** Cap on the per-process etag cache; a miss costs a lookup, not correctness. */
|
|
43
|
-
const MAX_REMEMBERED_ETAGS =
|
|
33
|
+
const MAX_REMEMBERED_ETAGS = 1_000;
|
|
44
34
|
/** Bounded directory page for point lookups. */
|
|
45
|
-
const DIRECTORY_PAGE_SIZE =
|
|
35
|
+
const DIRECTORY_PAGE_SIZE = 1_000;
|
|
46
36
|
/** Paths requested in each {@link FileSink.getMany} batch. */
|
|
47
37
|
const READ_PRESIGN_BATCH_MAX = 100;
|
|
48
38
|
/** Concurrent downloads inside one {@link FileSink.getMany} batch. */
|
|
@@ -54,7 +44,7 @@ const MAX_DIRECTORY_PAGES = 20;
|
|
|
54
44
|
* could not carry (~1M entries at the page size above). Past it, fail loudly:
|
|
55
45
|
* a silently partial listing would be mistaken for the whole scope.
|
|
56
46
|
*/
|
|
57
|
-
const MAX_LIST_WALK_PAGES =
|
|
47
|
+
const MAX_LIST_WALK_PAGES = 1_000;
|
|
58
48
|
/**
|
|
59
49
|
* True on Cursor-managed hosting with a provisioned file store.
|
|
60
50
|
*
|
|
@@ -70,7 +60,6 @@ export function isCursorHostedFilesAvailable(env = process.env) {
|
|
|
70
60
|
* deployments always use their deployment-scoped store instead.
|
|
71
61
|
*/
|
|
72
62
|
export function isCursorUserStoreFilesAvailable(env = process.env) {
|
|
73
|
-
var _a;
|
|
74
63
|
if (isCursorHostedFilesAvailable(env)) {
|
|
75
64
|
return false;
|
|
76
65
|
}
|
|
@@ -78,7 +67,7 @@ export function isCursorUserStoreFilesAvailable(env = process.env) {
|
|
|
78
67
|
if (env.VITEST === "true") {
|
|
79
68
|
return false;
|
|
80
69
|
}
|
|
81
|
-
const key =
|
|
70
|
+
const key = env.CURSOR_API_KEY?.trim();
|
|
82
71
|
return key !== undefined && key !== "";
|
|
83
72
|
}
|
|
84
73
|
/**
|
|
@@ -109,26 +98,22 @@ export function cursorHostedFiles(options = {}) {
|
|
|
109
98
|
* Storage bindings and transport selection are resolved for each operation
|
|
110
99
|
* so platform updates do not require recreating the sink.
|
|
111
100
|
*/
|
|
112
|
-
function withFiles(call) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
// store or transport stays on the compatibility path.
|
|
121
|
-
if (sourceId === resolveFrameworkStoreSourceIdSync()) {
|
|
122
|
-
return yield call({ wire: "flush", store: "framework" });
|
|
123
|
-
}
|
|
124
|
-
if (sourceId === resolveDeploymentStoreSourceIdSync()) {
|
|
125
|
-
return yield call({ wire: "flush", store: "deployment" });
|
|
126
|
-
}
|
|
101
|
+
async function withFiles(call) {
|
|
102
|
+
const sourceId = resolveStoreSourceId(options);
|
|
103
|
+
const protocol = options.resolveStoreProtocol?.() ?? resolveStoreProtocolSync();
|
|
104
|
+
if (storeProtocolEnablesFactoryFlush(protocol)) {
|
|
105
|
+
// The batched transport addresses managed stores by role. An unknown
|
|
106
|
+
// store or transport stays on the compatibility path.
|
|
107
|
+
if (sourceId === resolveFrameworkStoreSourceIdSync()) {
|
|
108
|
+
return await call({ wire: "flush", store: "framework" });
|
|
127
109
|
}
|
|
128
|
-
|
|
129
|
-
wire: "
|
|
130
|
-
|
|
131
|
-
|
|
110
|
+
if (sourceId === resolveDeploymentStoreSourceIdSync()) {
|
|
111
|
+
return await call({ wire: "flush", store: "deployment" });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return await call({
|
|
115
|
+
wire: "v0",
|
|
116
|
+
path: (suffix = "") => `/${encodeURIComponent(sourceId)}${suffix}`,
|
|
132
117
|
});
|
|
133
118
|
}
|
|
134
119
|
/**
|
|
@@ -148,77 +133,76 @@ export function cursorHostedFiles(options = {}) {
|
|
|
148
133
|
* delete race resolves itself on retry, where the presign returns 404
|
|
149
134
|
* and this correctly reads as a miss.
|
|
150
135
|
*/
|
|
151
|
-
function downloadPresigned(key, url) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
redirect: "manual",
|
|
156
|
-
});
|
|
157
|
-
if (response.status === 404) {
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
if (!response.ok) {
|
|
161
|
-
throw new Error(`cursorHostedFiles: download of "${key}" failed (HTTP ${response.status})`);
|
|
162
|
-
}
|
|
163
|
-
const etag = normalizeEtag(response.headers.get("etag"));
|
|
164
|
-
if (etag !== undefined) {
|
|
165
|
-
rememberEtag(key, etag);
|
|
166
|
-
}
|
|
167
|
-
return Object.assign({ body: new Uint8Array(yield response.arrayBuffer()) }, (etag === undefined ? {} : { etag }));
|
|
136
|
+
async function downloadPresigned(key, url) {
|
|
137
|
+
const response = await resolveCursorApiFetch(options)(assertPresignedUrlSafe(options, url), {
|
|
138
|
+
method: "GET",
|
|
139
|
+
redirect: "manual",
|
|
168
140
|
});
|
|
141
|
+
if (response.status === 404) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
throw new Error(`cursorHostedFiles: download of "${key}" failed (HTTP ${response.status})`);
|
|
146
|
+
}
|
|
147
|
+
const etag = normalizeEtag(response.headers.get("etag"));
|
|
148
|
+
if (etag !== undefined) {
|
|
149
|
+
rememberEtag(key, etag);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
body: new Uint8Array(await response.arrayBuffer()),
|
|
153
|
+
...(etag === undefined ? {} : { etag }),
|
|
154
|
+
};
|
|
169
155
|
}
|
|
170
156
|
/** One page of a directory listing. */
|
|
171
|
-
function directoryPage(files, directory, pageToken) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
const query = new URLSearchParams({
|
|
157
|
+
async function directoryPage(files, directory, pageToken) {
|
|
158
|
+
if (files.wire === "flush") {
|
|
159
|
+
return await flushPost(options, "/list", {
|
|
160
|
+
store: files.store,
|
|
177
161
|
path: directory,
|
|
178
|
-
limit:
|
|
162
|
+
limit: DIRECTORY_PAGE_SIZE,
|
|
163
|
+
...(pageToken === undefined ? {} : { pageToken }),
|
|
179
164
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
165
|
+
}
|
|
166
|
+
const query = new URLSearchParams({
|
|
167
|
+
path: directory,
|
|
168
|
+
limit: String(DIRECTORY_PAGE_SIZE),
|
|
184
169
|
});
|
|
170
|
+
if (pageToken !== undefined) {
|
|
171
|
+
query.set("pageToken", pageToken);
|
|
172
|
+
}
|
|
173
|
+
return await storeGet(options, files.path(`?${query.toString()}`));
|
|
185
174
|
}
|
|
186
175
|
/** The unscoped whole-store listing. */
|
|
187
|
-
function unscopedListing(files) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return yield storeGet(options, files.path());
|
|
195
|
-
});
|
|
176
|
+
async function unscopedListing(files) {
|
|
177
|
+
if (files.wire === "flush") {
|
|
178
|
+
return await flushPost(options, "/list", {
|
|
179
|
+
store: files.store,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return await storeGet(options, files.path());
|
|
196
183
|
}
|
|
197
|
-
function listEntries(prefix) {
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (typeof file.path !== "string" || !file.path.startsWith(prefix)) {
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
out.push(toEntry(file, file.path));
|
|
184
|
+
async function listEntries(prefix) {
|
|
185
|
+
return await withFiles(async (files) => {
|
|
186
|
+
const raw = await unscopedListing(files);
|
|
187
|
+
if (raw.complete === true) {
|
|
188
|
+
const out = [];
|
|
189
|
+
for (const file of raw.files ?? []) {
|
|
190
|
+
if (typeof file.path !== "string" || !file.path.startsWith(prefix)) {
|
|
191
|
+
continue;
|
|
209
192
|
}
|
|
210
|
-
|
|
211
|
-
// consumers depend on it (chunk keys are zero-padded so key order
|
|
212
|
-
// is append order). The server happens to scan in order today; do
|
|
213
|
-
// not let that be the only thing holding the contract.
|
|
214
|
-
return out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
193
|
+
out.push(toEntry(file, file.path));
|
|
215
194
|
}
|
|
216
|
-
// The
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
return
|
|
221
|
-
}
|
|
195
|
+
// The FileSink contract promises key order, and transcript-chunk
|
|
196
|
+
// consumers depend on it (chunk keys are zero-padded so key order
|
|
197
|
+
// is append order). The server happens to scan in order today; do
|
|
198
|
+
// not let that be the only thing holding the contract.
|
|
199
|
+
return out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
200
|
+
}
|
|
201
|
+
// The unscoped listing was truncated (`complete: false`) and has no
|
|
202
|
+
// continuation to follow. `list()` promises the whole scope, so rather
|
|
203
|
+
// than lie or fail, walk the paginated per-directory form, which does
|
|
204
|
+
// paginate and reports immediate subdirectories to recurse into.
|
|
205
|
+
return await walkDirectoryTree(files, prefix);
|
|
222
206
|
});
|
|
223
207
|
}
|
|
224
208
|
/**
|
|
@@ -229,49 +213,46 @@ export function cursorHostedFiles(options = {}) {
|
|
|
229
213
|
* store fails loudly rather than returning a silently partial answer the
|
|
230
214
|
* caller would mistake for the whole scope.
|
|
231
215
|
*/
|
|
232
|
-
function walkDirectoryTree(files, prefix) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
216
|
+
async function walkDirectoryTree(files, prefix) {
|
|
217
|
+
const slash = prefix.lastIndexOf("/");
|
|
218
|
+
const pending = [slash === -1 ? "" : prefix.slice(0, slash)];
|
|
219
|
+
const queued = new Set(pending);
|
|
220
|
+
const out = [];
|
|
221
|
+
let pageBudget = MAX_LIST_WALK_PAGES;
|
|
222
|
+
while (pending.length > 0) {
|
|
223
|
+
const directory = pending.pop();
|
|
224
|
+
let pageToken;
|
|
225
|
+
do {
|
|
226
|
+
if (pageBudget <= 0) {
|
|
227
|
+
throw new Error("cursorHostedFiles: the store is too large to list; scope the listing with a narrower prefix");
|
|
228
|
+
}
|
|
229
|
+
pageBudget -= 1;
|
|
230
|
+
const page = await directoryPage(files, directory, pageToken);
|
|
231
|
+
for (const file of page.files ?? []) {
|
|
232
|
+
if (typeof file.path === "string" && file.path.startsWith(prefix)) {
|
|
233
|
+
out.push(toEntry(file, file.path));
|
|
246
234
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
235
|
+
}
|
|
236
|
+
// Every page can surface different subdirectories, so collect from
|
|
237
|
+
// all of them, deduped — and descend only into branches that can
|
|
238
|
+
// still hold matches: a descendant of the prefix, or (at the first
|
|
239
|
+
// level) a directory the prefix's basename component narrows to.
|
|
240
|
+
for (const subdir of page.directories ?? []) {
|
|
241
|
+
if (queued.has(subdir)) {
|
|
242
|
+
continue;
|
|
253
243
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// level) a directory the prefix's basename component narrows to.
|
|
258
|
-
for (const subdir of (_b = page.directories) !== null && _b !== void 0 ? _b : []) {
|
|
259
|
-
if (queued.has(subdir)) {
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (`${subdir}/`.startsWith(prefix) || prefix.startsWith(`${subdir}/`)) {
|
|
263
|
-
queued.add(subdir);
|
|
264
|
-
pending.push(subdir);
|
|
265
|
-
}
|
|
244
|
+
if (`${subdir}/`.startsWith(prefix) || prefix.startsWith(`${subdir}/`)) {
|
|
245
|
+
queued.add(subdir);
|
|
246
|
+
pending.push(subdir);
|
|
266
247
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
248
|
+
}
|
|
249
|
+
pageToken =
|
|
250
|
+
page.complete === false && page.nextPageToken !== undefined
|
|
251
|
+
? page.nextPageToken
|
|
252
|
+
: undefined;
|
|
253
|
+
} while (pageToken !== undefined);
|
|
254
|
+
}
|
|
255
|
+
return out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
275
256
|
}
|
|
276
257
|
/** Shared decode: remember the etag as a side effect, then shape the entry. */
|
|
277
258
|
function toEntry(file, key) {
|
|
@@ -279,7 +260,12 @@ export function cursorHostedFiles(options = {}) {
|
|
|
279
260
|
rememberEtag(key, file.etag);
|
|
280
261
|
}
|
|
281
262
|
const mtimeMs = typeof file.updatedAt === "string" ? Date.parse(file.updatedAt) : undefined;
|
|
282
|
-
return
|
|
263
|
+
return {
|
|
264
|
+
key,
|
|
265
|
+
size: typeof file.sizeBytes === "number" ? file.sizeBytes : 0,
|
|
266
|
+
...(typeof file.etag === "string" && file.etag !== "" ? { etag: file.etag } : {}),
|
|
267
|
+
...(mtimeMs === undefined || Number.isNaN(mtimeMs) ? {} : { mtimeMs }),
|
|
268
|
+
};
|
|
283
269
|
}
|
|
284
270
|
/**
|
|
285
271
|
* One key's current entry, via the bounded directory listing.
|
|
@@ -289,53 +275,62 @@ export function cursorHostedFiles(options = {}) {
|
|
|
289
275
|
* the store got. `?path=` is scoped to one directory and paginates, and the
|
|
290
276
|
* scan stops as soon as the key appears.
|
|
291
277
|
*/
|
|
292
|
-
function statKey(key) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
for (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (file.path === key) {
|
|
303
|
-
return toEntry(file, key);
|
|
304
|
-
}
|
|
278
|
+
async function statKey(key) {
|
|
279
|
+
const slash = key.lastIndexOf("/");
|
|
280
|
+
const directory = slash === -1 ? "" : key.slice(0, slash);
|
|
281
|
+
return await withFiles(async (files) => {
|
|
282
|
+
let pageToken;
|
|
283
|
+
for (let page = 0; page < MAX_DIRECTORY_PAGES; page += 1) {
|
|
284
|
+
const raw = await directoryPage(files, directory, pageToken);
|
|
285
|
+
for (const file of raw.files ?? []) {
|
|
286
|
+
if (file.path === key) {
|
|
287
|
+
return toEntry(file, key);
|
|
305
288
|
}
|
|
306
|
-
// `complete` is false exactly when another page is waiting.
|
|
307
|
-
if (raw.complete !== false || raw.nextPageToken === undefined) {
|
|
308
|
-
return undefined;
|
|
309
|
-
}
|
|
310
|
-
pageToken = raw.nextPageToken;
|
|
311
289
|
}
|
|
312
|
-
|
|
313
|
-
|
|
290
|
+
// `complete` is false exactly when another page is waiting.
|
|
291
|
+
if (raw.complete !== false || raw.nextPageToken === undefined) {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
pageToken = raw.nextPageToken;
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
314
297
|
});
|
|
315
298
|
}
|
|
316
299
|
/**
|
|
317
300
|
* Resolve upload instructions for one attempt. A response without a URL
|
|
318
301
|
* reports that the file cannot use the single-part upload path.
|
|
319
302
|
*/
|
|
320
|
-
function presignUploads(files, specs) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
// and the missing url surfaces as that key's own error.
|
|
332
|
-
return {
|
|
333
|
-
files: ((_a = response.ops) !== null && _a !== void 0 ? _a : []).map(op => (Object.assign(Object.assign({ path: op.path }, (typeof op.url === "string" && op.url !== "" ? { url: op.url } : {})), { headers: op.headers }))),
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
return yield storePost(options, files.path("/upload"), {
|
|
337
|
-
files: specs.map(spec => (Object.assign({ path: spec.key, sha: createHash("sha256").update(spec.body).digest("hex"), size: spec.body.byteLength }, spec.precondition))),
|
|
303
|
+
async function presignUploads(files, specs) {
|
|
304
|
+
if (files.wire === "flush") {
|
|
305
|
+
const response = await flushPost(options, "/flush", {
|
|
306
|
+
store: files.store,
|
|
307
|
+
ops: specs.map(spec => ({
|
|
308
|
+
op: "put",
|
|
309
|
+
path: spec.key,
|
|
310
|
+
sha: createHash("sha256").update(spec.body).digest("hex"),
|
|
311
|
+
size: spec.body.byteLength,
|
|
312
|
+
...spec.precondition,
|
|
313
|
+
})),
|
|
338
314
|
});
|
|
315
|
+
// Index-aligned with the request ops, entries without a `url` included:
|
|
316
|
+
// a refused op (oversize) must keep its slot so the by-path/positional
|
|
317
|
+
// instruction matching below cannot hand its sibling's presign to it,
|
|
318
|
+
// and the missing url surfaces as that key's own error.
|
|
319
|
+
return {
|
|
320
|
+
files: (response.ops ?? []).map(op => ({
|
|
321
|
+
path: op.path,
|
|
322
|
+
...(typeof op.url === "string" && op.url !== "" ? { url: op.url } : {}),
|
|
323
|
+
headers: op.headers,
|
|
324
|
+
})),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return await storePost(options, files.path("/upload"), {
|
|
328
|
+
files: specs.map(spec => ({
|
|
329
|
+
path: spec.key,
|
|
330
|
+
sha: createHash("sha256").update(spec.body).digest("hex"),
|
|
331
|
+
size: spec.body.byteLength,
|
|
332
|
+
...spec.precondition,
|
|
333
|
+
})),
|
|
339
334
|
});
|
|
340
335
|
}
|
|
341
336
|
/**
|
|
@@ -344,56 +339,52 @@ export function cursorHostedFiles(options = {}) {
|
|
|
344
339
|
* disturbs its batch siblings. Resolving the instructions is shared, so
|
|
345
340
|
* that request failing rejects the whole attempt.
|
|
346
341
|
*/
|
|
347
|
-
function attemptUploads(files, specs) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
342
|
+
async function attemptUploads(files, specs) {
|
|
343
|
+
const presigned = await presignUploads(files, specs);
|
|
344
|
+
return await Promise.all(specs.map(async (spec, index) => {
|
|
345
|
+
// Match by path, falling back to position for servers that answer
|
|
346
|
+
// in request order without echoing the path.
|
|
347
|
+
const instruction = presigned.files?.find(file => file.path === spec.key) ?? presigned.files?.[index];
|
|
348
|
+
if (instruction?.url === undefined) {
|
|
349
|
+
// Files past the single-part threshold come back with a multipart
|
|
350
|
+
// plan instead of a url; this backend does not implement multipart.
|
|
351
|
+
return {
|
|
352
|
+
status: "error",
|
|
353
|
+
error: new Error(`cursorHostedFiles: no single-part upload url for "${spec.key}" (too large?)`),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const response = await resolveCursorApiFetch(options)(assertPresignedUrlSafe(options, instruction.url), {
|
|
358
|
+
method: "PUT",
|
|
359
|
+
headers: instruction.headers ?? {},
|
|
360
|
+
// A view over bytes is a valid fetch body; the DOM lib types
|
|
361
|
+
// are narrower than the runtime, and copying into a Blob would
|
|
362
|
+
// double the memory for every upload.
|
|
363
|
+
body: spec.body,
|
|
364
|
+
// A redirect would re-send the body somewhere the presign did
|
|
365
|
+
// not authorize.
|
|
366
|
+
redirect: "manual",
|
|
367
|
+
});
|
|
368
|
+
if (response.status === 412) {
|
|
369
|
+
observedEtags.delete(spec.key);
|
|
370
|
+
return { status: "conflict" };
|
|
371
|
+
}
|
|
372
|
+
if (!response.ok) {
|
|
359
373
|
return {
|
|
360
374
|
status: "error",
|
|
361
|
-
error: new Error(`cursorHostedFiles:
|
|
375
|
+
error: new Error(`cursorHostedFiles: upload of "${spec.key}" failed (HTTP ${response.status})`),
|
|
362
376
|
};
|
|
363
377
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
headers: (_d = instruction.headers) !== null && _d !== void 0 ? _d : {},
|
|
368
|
-
// A view over bytes is a valid fetch body; the DOM lib types
|
|
369
|
-
// are narrower than the runtime, and copying into a Blob would
|
|
370
|
-
// double the memory for every upload.
|
|
371
|
-
body: spec.body,
|
|
372
|
-
// A redirect would re-send the body somewhere the presign did
|
|
373
|
-
// not authorize.
|
|
374
|
-
redirect: "manual",
|
|
375
|
-
});
|
|
376
|
-
if (response.status === 412) {
|
|
377
|
-
observedEtags.delete(spec.key);
|
|
378
|
-
return { status: "conflict" };
|
|
379
|
-
}
|
|
380
|
-
if (!response.ok) {
|
|
381
|
-
return {
|
|
382
|
-
status: "error",
|
|
383
|
-
error: new Error(`cursorHostedFiles: upload of "${spec.key}" failed (HTTP ${response.status})`),
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
const etag = normalizeEtag(response.headers.get("etag"));
|
|
387
|
-
if (etag !== undefined) {
|
|
388
|
-
rememberEtag(spec.key, etag);
|
|
389
|
-
}
|
|
390
|
-
return { status: "ok", result: etag === undefined ? {} : { etag } };
|
|
391
|
-
}
|
|
392
|
-
catch (error) {
|
|
393
|
-
return { status: "error", error };
|
|
378
|
+
const etag = normalizeEtag(response.headers.get("etag"));
|
|
379
|
+
if (etag !== undefined) {
|
|
380
|
+
rememberEtag(spec.key, etag);
|
|
394
381
|
}
|
|
395
|
-
|
|
396
|
-
|
|
382
|
+
return { status: "ok", result: etag === undefined ? {} : { etag } };
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return { status: "error", error };
|
|
386
|
+
}
|
|
387
|
+
}));
|
|
397
388
|
}
|
|
398
389
|
/**
|
|
399
390
|
* The full single-key write flow: precondition resolution, one shared
|
|
@@ -401,46 +392,43 @@ export function cursorHostedFiles(options = {}) {
|
|
|
401
392
|
* `put` promises — including the one statKey-and-retry when an optimistic
|
|
402
393
|
* create loses to an existing object.
|
|
403
394
|
*/
|
|
404
|
-
function settlePut(files, entry, first) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
return replaced.result;
|
|
443
|
-
});
|
|
395
|
+
async function settlePut(files, entry, first) {
|
|
396
|
+
if (first.status === "error") {
|
|
397
|
+
throw first.error;
|
|
398
|
+
}
|
|
399
|
+
if (first.status === "ok") {
|
|
400
|
+
return first.result;
|
|
401
|
+
}
|
|
402
|
+
// Conflict. Explicit `ifMatch` (etag or create-only) and cached-etag
|
|
403
|
+
// writes are single-attempt: the lost precondition is the caller's.
|
|
404
|
+
const requested = entry.options?.ifMatch;
|
|
405
|
+
if (requested !== undefined) {
|
|
406
|
+
throw new FileConflictError(entry.key, {
|
|
407
|
+
storedEtag: requested ?? undefined,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (entry.cachedEtagUsed === true) {
|
|
411
|
+
// Someone else wrote between our etag observation and this PUT.
|
|
412
|
+
throw new FileConflictError(entry.key, { storedEtag: entry.staleEtag });
|
|
413
|
+
}
|
|
414
|
+
// The optimistic create lost: learn the current etag once and replace.
|
|
415
|
+
const found = await statKey(entry.key);
|
|
416
|
+
if (found?.etag === undefined) {
|
|
417
|
+
// Existed a moment ago (the create lost) but stat sees nothing: a
|
|
418
|
+
// delete raced this write. Surface the conflict; a retry from the
|
|
419
|
+
// caller lands on the fresh create-only path.
|
|
420
|
+
throw new FileConflictError(entry.key, {});
|
|
421
|
+
}
|
|
422
|
+
const [replaced] = await attemptUploads(files, [
|
|
423
|
+
{ key: entry.key, body: entry.body, precondition: { etag: found.etag } },
|
|
424
|
+
]);
|
|
425
|
+
if (replaced === undefined || replaced.status === "conflict") {
|
|
426
|
+
throw new FileConflictError(entry.key, { storedEtag: found?.etag });
|
|
427
|
+
}
|
|
428
|
+
if (replaced.status === "error") {
|
|
429
|
+
throw replaced.error;
|
|
430
|
+
}
|
|
431
|
+
return replaced.result;
|
|
444
432
|
}
|
|
445
433
|
/**
|
|
446
434
|
* Precondition for a write, and whether it came from the etag cache (a
|
|
@@ -448,8 +436,7 @@ export function cursorHostedFiles(options = {}) {
|
|
|
448
436
|
* one stat-and-retry).
|
|
449
437
|
*/
|
|
450
438
|
function preconditionFor(entry) {
|
|
451
|
-
|
|
452
|
-
const requested = (_a = entry.options) === null || _a === void 0 ? void 0 : _a.ifMatch;
|
|
439
|
+
const requested = entry.options?.ifMatch;
|
|
453
440
|
// The store rejects unconditional writes, so every put carries a
|
|
454
441
|
// precondition. `null` is create-only and an explicit etag is a caller
|
|
455
442
|
// assertion.
|
|
@@ -477,269 +464,266 @@ export function cursorHostedFiles(options = {}) {
|
|
|
477
464
|
// No local copy: bytes live in the store, so `materializedPath()` is
|
|
478
465
|
// `undefined` and the harness reaches files through `host.files`.
|
|
479
466
|
capabilities: { etags: true },
|
|
480
|
-
put(key, body, putOptions) {
|
|
481
|
-
return
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
467
|
+
async put(key, body, putOptions) {
|
|
468
|
+
return await withFiles(async (files) => {
|
|
469
|
+
const entry = { key, body, options: putOptions };
|
|
470
|
+
const resolved = preconditionFor(entry);
|
|
471
|
+
const [first] = await attemptUploads(files, [
|
|
472
|
+
{ key, body, precondition: resolved.precondition },
|
|
473
|
+
]);
|
|
474
|
+
if (first === undefined) {
|
|
475
|
+
throw new Error(`cursorHostedFiles: upload of "${key}" vanished`);
|
|
476
|
+
}
|
|
477
|
+
return await settlePut(files, {
|
|
478
|
+
...entry,
|
|
479
|
+
cachedEtagUsed: resolved.cachedEtagUsed,
|
|
480
|
+
staleEtag: resolved.staleEtag,
|
|
481
|
+
}, first);
|
|
493
482
|
});
|
|
494
483
|
},
|
|
495
|
-
putMany(entries) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
484
|
+
async putMany(entries) {
|
|
485
|
+
if (entries.length === 0) {
|
|
486
|
+
return [];
|
|
487
|
+
}
|
|
488
|
+
return await withFiles(async (files) => {
|
|
489
|
+
const resolved = entries.map(entry => ({
|
|
490
|
+
entry,
|
|
491
|
+
...preconditionFor(entry),
|
|
492
|
+
}));
|
|
493
|
+
let firsts;
|
|
494
|
+
try {
|
|
495
|
+
// One presign call for the whole batch — the point of putMany.
|
|
496
|
+
firsts = await attemptUploads(files, resolved.map(({ entry, precondition }) => ({
|
|
497
|
+
key: entry.key,
|
|
498
|
+
body: entry.body,
|
|
499
|
+
precondition,
|
|
500
|
+
})));
|
|
499
501
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
catch (error) {
|
|
512
|
-
// The shared presign failed: every entry failed the same way.
|
|
513
|
-
return entries.map(() => ({
|
|
502
|
+
catch (error) {
|
|
503
|
+
// The shared presign failed: every entry failed the same way.
|
|
504
|
+
return entries.map(() => ({
|
|
505
|
+
status: "rejected",
|
|
506
|
+
reason: error,
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
509
|
+
return await Promise.all(resolved.map(async ({ entry, cachedEtagUsed, staleEtag }, index) => {
|
|
510
|
+
const first = firsts[index];
|
|
511
|
+
if (first === undefined) {
|
|
512
|
+
return {
|
|
514
513
|
status: "rejected",
|
|
515
|
-
reason:
|
|
516
|
-
}
|
|
514
|
+
reason: new Error(`cursorHostedFiles: upload of "${entry.key}" vanished`),
|
|
515
|
+
};
|
|
517
516
|
}
|
|
518
|
-
return yield Promise.all(resolved.map((_a, index_1) => __awaiter(this, [_a, index_1], void 0, function* ({ entry, cachedEtagUsed, staleEtag }, index) {
|
|
519
|
-
const first = firsts[index];
|
|
520
|
-
if (first === undefined) {
|
|
521
|
-
return {
|
|
522
|
-
status: "rejected",
|
|
523
|
-
reason: new Error(`cursorHostedFiles: upload of "${entry.key}" vanished`),
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
try {
|
|
527
|
-
return {
|
|
528
|
-
status: "fulfilled",
|
|
529
|
-
value: yield settlePut(files, Object.assign(Object.assign({}, entry), { cachedEtagUsed, staleEtag }), first),
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
catch (error) {
|
|
533
|
-
return { status: "rejected", reason: error };
|
|
534
|
-
}
|
|
535
|
-
})));
|
|
536
|
-
}));
|
|
537
|
-
});
|
|
538
|
-
},
|
|
539
|
-
get(key) {
|
|
540
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
541
|
-
return yield withFiles((files) => __awaiter(this, void 0, void 0, function* () {
|
|
542
|
-
var _a, _b;
|
|
543
|
-
// A path with no live object is absent, not a storage failure.
|
|
544
|
-
let presigned;
|
|
545
517
|
try {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
paths: [key],
|
|
551
|
-
})
|
|
552
|
-
: yield storePost(options, files.path("/download"), {
|
|
553
|
-
paths: [key],
|
|
554
|
-
});
|
|
518
|
+
return {
|
|
519
|
+
status: "fulfilled",
|
|
520
|
+
value: await settlePut(files, { ...entry, cachedEtagUsed, staleEtag }, first),
|
|
521
|
+
};
|
|
555
522
|
}
|
|
556
523
|
catch (error) {
|
|
557
|
-
|
|
558
|
-
return undefined;
|
|
559
|
-
}
|
|
560
|
-
throw error;
|
|
561
|
-
}
|
|
562
|
-
const url = (_b = (_a = presigned.files) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.url;
|
|
563
|
-
if (url === undefined) {
|
|
564
|
-
return undefined;
|
|
524
|
+
return { status: "rejected", reason: error };
|
|
565
525
|
}
|
|
566
|
-
return yield downloadPresigned(key, url);
|
|
567
526
|
}));
|
|
568
527
|
});
|
|
569
528
|
},
|
|
570
|
-
|
|
571
|
-
return
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
// This transport cannot distinguish one missing key from a failed
|
|
579
|
-
// batch, so preserve exact absence semantics with bounded reads.
|
|
580
|
-
return yield mapBounded([...keys], GET_MANY_DOWNLOAD_CONCURRENCY, (key) => __awaiter(this, void 0, void 0, function* () {
|
|
581
|
-
var _a, _b;
|
|
582
|
-
const presigned = yield storePost(options, files.path("/download"), { paths: [key] }).catch((error) => {
|
|
583
|
-
if (error instanceof StoreApiError && error.status === 404) {
|
|
584
|
-
return undefined;
|
|
585
|
-
}
|
|
586
|
-
throw error;
|
|
587
|
-
});
|
|
588
|
-
const url = (_b = (_a = presigned === null || presigned === void 0 ? void 0 : presigned.files) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.url;
|
|
589
|
-
if (url === undefined) {
|
|
590
|
-
return undefined;
|
|
591
|
-
}
|
|
592
|
-
return yield downloadPresigned(key, url);
|
|
593
|
-
}));
|
|
594
|
-
}
|
|
595
|
-
const urls = new Map();
|
|
596
|
-
for (let from = 0; from < keys.length; from += READ_PRESIGN_BATCH_MAX) {
|
|
597
|
-
const chunk = keys.slice(from, from + READ_PRESIGN_BATCH_MAX);
|
|
598
|
-
let presigned;
|
|
599
|
-
try {
|
|
600
|
-
presigned = yield flushPost(options, "/read", {
|
|
529
|
+
async get(key) {
|
|
530
|
+
return await withFiles(async (files) => {
|
|
531
|
+
// A path with no live object is absent, not a storage failure.
|
|
532
|
+
let presigned;
|
|
533
|
+
try {
|
|
534
|
+
presigned =
|
|
535
|
+
files.wire === "flush"
|
|
536
|
+
? await flushPost(options, "/read", {
|
|
601
537
|
store: files.store,
|
|
602
|
-
paths:
|
|
538
|
+
paths: [key],
|
|
539
|
+
})
|
|
540
|
+
: await storePost(options, files.path("/download"), {
|
|
541
|
+
paths: [key],
|
|
603
542
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
// omitted entries); a 404 is the store itself — not
|
|
609
|
-
// provisioned, or deleted under us — which get() reads as
|
|
610
|
-
// absence for every key.
|
|
611
|
-
return keys.map(() => undefined);
|
|
612
|
-
}
|
|
613
|
-
throw error;
|
|
614
|
-
}
|
|
615
|
-
for (const entry of (_a = presigned.files) !== null && _a !== void 0 ? _a : []) {
|
|
616
|
-
if (entry.path !== undefined && entry.url !== undefined) {
|
|
617
|
-
urls.set(entry.path, entry.url);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
if (error instanceof StoreApiError && error.status === 404) {
|
|
546
|
+
return undefined;
|
|
620
547
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
paths: [key],
|
|
641
|
-
}).catch((error) => {
|
|
642
|
-
// Store-level 404 (deleted between the batch and this read):
|
|
643
|
-
// absence, same as get().
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
550
|
+
const url = presigned.files?.[0]?.url;
|
|
551
|
+
if (url === undefined) {
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
return await downloadPresigned(key, url);
|
|
555
|
+
});
|
|
556
|
+
},
|
|
557
|
+
async getMany(keys) {
|
|
558
|
+
if (keys.length === 0) {
|
|
559
|
+
return [];
|
|
560
|
+
}
|
|
561
|
+
const bodies = await withFiles(async (files) => {
|
|
562
|
+
if (files.wire !== "flush") {
|
|
563
|
+
// This transport cannot distinguish one missing key from a failed
|
|
564
|
+
// batch, so preserve exact absence semantics with bounded reads.
|
|
565
|
+
return await mapBounded([...keys], GET_MANY_DOWNLOAD_CONCURRENCY, async (key) => {
|
|
566
|
+
const presigned = await storePost(options, files.path("/download"), { paths: [key] }).catch((error) => {
|
|
644
567
|
if (error instanceof StoreApiError && error.status === 404) {
|
|
645
568
|
return undefined;
|
|
646
569
|
}
|
|
647
570
|
throw error;
|
|
648
571
|
});
|
|
649
|
-
const
|
|
650
|
-
if (
|
|
572
|
+
const url = presigned?.files?.[0]?.url;
|
|
573
|
+
if (url === undefined) {
|
|
651
574
|
return undefined;
|
|
652
575
|
}
|
|
653
|
-
return
|
|
654
|
-
})
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
576
|
+
return await downloadPresigned(key, url);
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
const urls = new Map();
|
|
580
|
+
for (let from = 0; from < keys.length; from += READ_PRESIGN_BATCH_MAX) {
|
|
581
|
+
const chunk = keys.slice(from, from + READ_PRESIGN_BATCH_MAX);
|
|
582
|
+
let presigned;
|
|
583
|
+
try {
|
|
584
|
+
presigned = await flushPost(options, "/read", {
|
|
585
|
+
store: files.store,
|
|
586
|
+
paths: chunk,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
if (error instanceof StoreApiError && error.status === 404) {
|
|
591
|
+
// The read route never 404s per path (absent paths are
|
|
592
|
+
// omitted entries); a 404 is the store itself — not
|
|
593
|
+
// provisioned, or deleted under us — which get() reads as
|
|
594
|
+
// absence for every key.
|
|
595
|
+
return keys.map(() => undefined);
|
|
596
|
+
}
|
|
597
|
+
throw error;
|
|
666
598
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
599
|
+
for (const entry of presigned.files ?? []) {
|
|
600
|
+
if (entry.path !== undefined && entry.url !== undefined) {
|
|
601
|
+
urls.set(entry.path, entry.url);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return await mapBounded([...keys], GET_MANY_DOWNLOAD_CONCURRENCY, async (key) => {
|
|
606
|
+
const url = urls.get(key);
|
|
607
|
+
if (url !== undefined) {
|
|
608
|
+
return await downloadPresigned(key, url);
|
|
609
|
+
}
|
|
610
|
+
// A miss in the batch is NOT yet a definitive absence: the
|
|
611
|
+
// server echoes each path *normalized* (trimmed, backslashes
|
|
612
|
+
// folded, empty/"." segments dropped), so a key whose
|
|
613
|
+
// normalized form differs from what was sent never matches the
|
|
614
|
+
// echo even though its object exists. `get()` is immune (it
|
|
615
|
+
// reads the response positionally), so settle every batch miss
|
|
616
|
+
// through the single-path read — keeping getMany's absence
|
|
617
|
+
// semantics byte-identical to get()'s. Genuine absences pay one
|
|
618
|
+
// extra request; every batch caller reads keys it just listed
|
|
619
|
+
// or was told exist, so real misses are rare (delete races,
|
|
620
|
+
// store loss).
|
|
621
|
+
const presigned = await flushPost(options, "/read", {
|
|
673
622
|
store: files.store,
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
623
|
+
paths: [key],
|
|
624
|
+
}).catch((error) => {
|
|
625
|
+
// Store-level 404 (deleted between the batch and this read):
|
|
626
|
+
// absence, same as get().
|
|
627
|
+
if (error instanceof StoreApiError && error.status === 404) {
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
throw error;
|
|
677
631
|
});
|
|
678
|
-
const
|
|
679
|
-
if (
|
|
680
|
-
|
|
681
|
-
throw new FileConflictError(key, Object.assign({}, (outcome.etag === undefined
|
|
682
|
-
? {}
|
|
683
|
-
: { storedEtag: (_b = normalizeEtag(outcome.etag)) !== null && _b !== void 0 ? _b : outcome.etag })));
|
|
632
|
+
const singleUrl = presigned?.files?.[0]?.url;
|
|
633
|
+
if (singleUrl === undefined) {
|
|
634
|
+
return undefined;
|
|
684
635
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
636
|
+
return await downloadPresigned(key, singleUrl);
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
return bodies;
|
|
640
|
+
},
|
|
641
|
+
async delete(key) {
|
|
642
|
+
const handled = await withFiles(async (files) => {
|
|
643
|
+
if (files.wire !== "flush") {
|
|
644
|
+
return false;
|
|
690
645
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
646
|
+
// With an observed etag the flush delete is If-Match on that version.
|
|
647
|
+
// Without one, the server tombstones whatever live object occupies
|
|
648
|
+
// the key (no directory walk here) and an absent key settles as
|
|
649
|
+
// `already_deleted`.
|
|
650
|
+
const cached = observedEtags.get(key);
|
|
651
|
+
const response = await flushPost(options, "/flush", {
|
|
652
|
+
store: files.store,
|
|
653
|
+
ops: [
|
|
654
|
+
{
|
|
655
|
+
op: "delete",
|
|
656
|
+
path: key,
|
|
657
|
+
...(cached === undefined ? {} : { etag: cached }),
|
|
658
|
+
},
|
|
659
|
+
],
|
|
660
|
+
});
|
|
661
|
+
const outcome = response.ops?.[0];
|
|
662
|
+
if (outcome?.status === "conflict") {
|
|
663
|
+
observedEtags.delete(key);
|
|
664
|
+
throw new FileConflictError(key, {
|
|
665
|
+
...(outcome.etag === undefined
|
|
666
|
+
? {}
|
|
667
|
+
: { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
|
|
668
|
+
});
|
|
698
669
|
}
|
|
699
|
-
|
|
700
|
-
|
|
670
|
+
observedEtags.delete(key);
|
|
671
|
+
return true;
|
|
672
|
+
});
|
|
673
|
+
if (handled) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (observedEtags.get(key) === undefined) {
|
|
677
|
+
// Deletes are conditional too, so learn the current etag first. A key
|
|
678
|
+
// we have never seen is already absent as far as the caller cares.
|
|
679
|
+
const found = await statKey(key);
|
|
680
|
+
if (found?.etag === undefined) {
|
|
701
681
|
return;
|
|
702
682
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
observedEtags.delete(key);
|
|
716
|
-
throw new FileConflictError(key, Object.assign({}, (outcome.etag === undefined
|
|
717
|
-
? {}
|
|
718
|
-
: { storedEtag: (_b = normalizeEtag(outcome.etag)) !== null && _b !== void 0 ? _b : outcome.etag })));
|
|
719
|
-
}
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
const raw = yield storePost(options, files.path("/delete"), {
|
|
723
|
-
files: [{ path: key, etag }],
|
|
683
|
+
}
|
|
684
|
+
const etag = observedEtags.get(key);
|
|
685
|
+
if (etag === undefined) {
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
await withFiles(async (files) => {
|
|
689
|
+
if (files.wire === "flush") {
|
|
690
|
+
// The protocol pin appeared between the two withFiles calls; the
|
|
691
|
+
// cached etag is enough for the conditional flush delete.
|
|
692
|
+
const response = await flushPost(options, "/flush", {
|
|
693
|
+
store: files.store,
|
|
694
|
+
ops: [{ op: "delete", path: key, etag }],
|
|
724
695
|
});
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const outcome = ((_c = raw.files) !== null && _c !== void 0 ? _c : []).find(entry => entry.path === key);
|
|
728
|
-
if ((outcome === null || outcome === void 0 ? void 0 : outcome.status) === "conflict") {
|
|
696
|
+
const outcome = response.ops?.[0];
|
|
697
|
+
if (outcome?.status === "conflict") {
|
|
729
698
|
observedEtags.delete(key);
|
|
730
|
-
throw new FileConflictError(key,
|
|
731
|
-
|
|
732
|
-
|
|
699
|
+
throw new FileConflictError(key, {
|
|
700
|
+
...(outcome.etag === undefined
|
|
701
|
+
? {}
|
|
702
|
+
: { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
|
|
703
|
+
});
|
|
733
704
|
}
|
|
734
|
-
|
|
735
|
-
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const raw = await storePost(options, files.path("/delete"), {
|
|
708
|
+
files: [{ path: key, etag }],
|
|
709
|
+
});
|
|
710
|
+
// The API answers 200 with a per-file status, so a lost race would look
|
|
711
|
+
// like success unless the outcome is read.
|
|
712
|
+
const outcome = (raw.files ?? []).find(entry => entry.path === key);
|
|
713
|
+
if (outcome?.status === "conflict") {
|
|
714
|
+
observedEtags.delete(key);
|
|
715
|
+
throw new FileConflictError(key, {
|
|
716
|
+
...(outcome.etag === undefined
|
|
717
|
+
? {}
|
|
718
|
+
: { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
|
|
719
|
+
});
|
|
720
|
+
}
|
|
736
721
|
});
|
|
722
|
+
observedEtags.delete(key);
|
|
737
723
|
},
|
|
738
724
|
list: listEntries,
|
|
739
|
-
stat(key) {
|
|
740
|
-
return
|
|
741
|
-
return yield statKey(key);
|
|
742
|
-
});
|
|
725
|
+
async stat(key) {
|
|
726
|
+
return await statKey(key);
|
|
743
727
|
},
|
|
744
728
|
};
|
|
745
729
|
}
|
|
@@ -755,7 +739,7 @@ function assertPresignedUrlSafe(options, raw) {
|
|
|
755
739
|
try {
|
|
756
740
|
url = new URL(raw);
|
|
757
741
|
}
|
|
758
|
-
catch
|
|
742
|
+
catch {
|
|
759
743
|
throw new Error("cursorHostedFiles: presigned URL is not a valid URL");
|
|
760
744
|
}
|
|
761
745
|
if (url.protocol === "https:" &&
|
|
@@ -777,56 +761,53 @@ function assertPresignedUrlSafe(options, raw) {
|
|
|
777
761
|
* of silently writing to another location.
|
|
778
762
|
*/
|
|
779
763
|
function resolveStoreSourceId(options) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
764
|
+
const id = (options.resolveStoreSourceId?.() ??
|
|
765
|
+
options.storeSourceId ??
|
|
766
|
+
resolveDeploymentStoreSourceIdSync(process.env))?.trim();
|
|
783
767
|
if (id === undefined || id === "") {
|
|
784
768
|
throw new Error(`cursorHostedFiles requires ${AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR} (set on Cursor-managed deployments)`);
|
|
785
769
|
}
|
|
786
770
|
return id;
|
|
787
771
|
}
|
|
788
|
-
const DEV_PRESIGN_HOSTS = new Set((
|
|
772
|
+
const DEV_PRESIGN_HOSTS = new Set((process.env.CURSOR_HOSTED_FILES_DEV_PRESIGN_HOSTS ?? "")
|
|
789
773
|
.split(",")
|
|
790
774
|
.map(host => host.trim().toLowerCase())
|
|
791
775
|
.filter(host => host.length > 0));
|
|
792
776
|
/** Send a file request with the deployment pod credential. */
|
|
793
|
-
function storePost(options, path, body) {
|
|
794
|
-
return
|
|
795
|
-
return yield postJson(options, `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`, body);
|
|
796
|
-
});
|
|
777
|
+
async function storePost(options, path, body) {
|
|
778
|
+
return await postJson(options, `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`, body);
|
|
797
779
|
}
|
|
798
|
-
function storeGet(options, path) {
|
|
799
|
-
return
|
|
800
|
-
return yield sendJson(options, `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`, "GET");
|
|
801
|
-
});
|
|
780
|
+
async function storeGet(options, path) {
|
|
781
|
+
return await sendJson(options, `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`, "GET");
|
|
802
782
|
}
|
|
803
783
|
/** Send a batched file request with the deployment pod credential. */
|
|
804
|
-
function flushPost(options, path, body) {
|
|
805
|
-
return
|
|
806
|
-
return yield postJson(options, `${resolveCursorApiBaseUrl(options)}${AGENTSDK_STORE_PATH}${path}`, body);
|
|
807
|
-
});
|
|
784
|
+
async function flushPost(options, path, body) {
|
|
785
|
+
return await postJson(options, `${resolveCursorApiBaseUrl(options)}${AGENTSDK_STORE_PATH}${path}`, body);
|
|
808
786
|
}
|
|
809
|
-
function postJson(options, url, body) {
|
|
810
|
-
return
|
|
811
|
-
return yield sendJson(options, url, "POST", body);
|
|
812
|
-
});
|
|
787
|
+
async function postJson(options, url, body) {
|
|
788
|
+
return await sendJson(options, url, "POST", body);
|
|
813
789
|
}
|
|
814
|
-
function sendJson(options, url, method, body) {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
catch (_a) {
|
|
823
|
-
throw new Error(`cursorHostedFiles ${url} returned non-JSON (${response.status})`);
|
|
824
|
-
}
|
|
825
|
-
if (!response.ok) {
|
|
826
|
-
throw new StoreApiError(response.status, `cursorHostedFiles ${url}: ${describeError(parsed, response.status)}`);
|
|
827
|
-
}
|
|
828
|
-
return parsed;
|
|
790
|
+
async function sendJson(options, url, method, body) {
|
|
791
|
+
const response = await resolveCursorApiFetch(options)(url, {
|
|
792
|
+
method,
|
|
793
|
+
headers: {
|
|
794
|
+
Authorization: `Bearer ${resolveCursorApiKey(options)}`,
|
|
795
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
796
|
+
},
|
|
797
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
829
798
|
});
|
|
799
|
+
const text = await response.text();
|
|
800
|
+
let parsed;
|
|
801
|
+
try {
|
|
802
|
+
parsed = text.length === 0 ? {} : JSON.parse(text);
|
|
803
|
+
}
|
|
804
|
+
catch {
|
|
805
|
+
throw new Error(`cursorHostedFiles ${url} returned non-JSON (${response.status})`);
|
|
806
|
+
}
|
|
807
|
+
if (!response.ok) {
|
|
808
|
+
throw new StoreApiError(response.status, `cursorHostedFiles ${url}: ${describeError(parsed, response.status)}`);
|
|
809
|
+
}
|
|
810
|
+
return parsed;
|
|
830
811
|
}
|
|
831
812
|
/**
|
|
832
813
|
* A non-2xx answer from the files API, carrying the status so a caller can
|