deepline 0.3.461 → 0.3.462
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/bundling-sources/sdk/src/client-core.ts +7593 -0
- package/dist/bundling-sources/sdk/src/client.ts +8 -7366
- package/dist/bundling-sources/sdk/src/http-core.ts +1239 -0
- package/dist/bundling-sources/sdk/src/http.ts +6 -1231
- package/dist/bundling-sources/sdk/src/index.ts +4 -1
- package/dist/bundling-sources/sdk/src/play.ts +5 -5
- package/dist/bundling-sources/sdk/src/plays/client-core.ts +437 -0
- package/dist/bundling-sources/sdk/src/plays/client.ts +4 -421
- package/dist/bundling-sources/sdk/src/plays/input-example.ts +3 -4
- package/dist/bundling-sources/sdk/src/plays/play-description.ts +1 -1
- package/dist/bundling-sources/sdk/src/plays/run-request.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/server.ts +43 -0
- package/dist/bundling-sources/sdk/src/types.ts +15 -5
- package/dist/bundling-sources/shared_libs/integration-contracts/billing-entitlements.ts +15 -0
- package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +0 -307
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +3 -0
- package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +3 -2
- package/dist/bundling-sources/shared_libs/monitors/package.json +2 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +34 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +59 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +94 -1
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +11 -1
- package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-failure-codes.ts +24 -0
- package/dist/bundling-sources/shared_libs/observability/dlq.ts +32 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +57 -3
- package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +66 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-column-storage.ts +36 -7
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +250 -11
- package/dist/bundling-sources/shared_libs/play-runtime/array-input-store.ts +319 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +991 -359
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +110 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +155 -6
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +110 -6
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +127 -13
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +57 -1
- package/dist/bundling-sources/shared_libs/play-runtime/free-company-lookup-batching.ts +218 -0
- package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +42 -5
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +92 -31
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +69 -14
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +93 -23
- package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +15 -4
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +22 -18
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +5 -0
- package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +190 -14
- package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +131 -0
- package/dist/bundling-sources/shared_libs/play-runtime/modal-runtime-config.ts +77 -2
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +271 -2
- package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +51 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/contracts.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +27 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/provider-text-for-log.ts +63 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +19 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +112 -16
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +104 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +265 -151
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +35 -14
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-control-transport.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-labels.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +136 -70
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +237 -36
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +148 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +285 -32
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +251 -56
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +29 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/sandbox-create-outcome.ts +27 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +22 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +1397 -382
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-array-input-receipts.ts +920 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +38 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +26 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +30 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +14 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +31 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-transition.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-compute-usage.ts +20 -7
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +12 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/index.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +1496 -320
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +31 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1389 -129
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +162 -23
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +499 -115
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +377 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-ledger-reconciliation.ts +117 -8
- package/dist/bundling-sources/shared_libs/play-runtime/secret-resolution-retry-policy.ts +25 -0
- package/dist/bundling-sources/shared_libs/play-runtime/terminal-runtime-performance.ts +391 -0
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/mapped-calls.ts +171 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +16 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +26 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +16 -2
- package/dist/bundling-sources/shared_libs/play-runtime/transport-error-diagnostics.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-storage-contract.ts +55 -5
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +41 -1
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +4 -0
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +9 -3
- package/dist/bundling-sources/shared_libs/plays/deploy/manifest-contract.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +3 -2
- package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +2 -2
- package/dist/bundling-sources/shared_libs/plays/run-output-summary.ts +66 -0
- package/dist/bundling-sources/shared_libs/plays/script-entry.ts +1 -1
- package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +43 -1
- package/dist/bundling-sources/shared_libs/product-notifications/content.ts +9 -0
- package/dist/bundling-sources/shared_libs/product-notifications/email.ts +91 -13
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +3 -0
- package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +41 -24
- package/dist/bundling-sources/shared_libs/security/attested-client-ip.ts +91 -0
- package/dist/bundling-sources/shared_libs/security/internal-actor.ts +0 -11
- package/dist/cli/{auth-6HJXNWUG.mjs → auth-JDLEY7VU.mjs} +5 -4
- package/dist/cli/auth-UXKY6TCC.js +41 -0
- package/dist/cli/{billing-6P5HO6QG.js → billing-HXAXB3XZ.js} +152 -78
- package/dist/cli/{billing-MJ42N7YF.mjs → billing-SDHLBLMV.mjs} +84 -10
- package/dist/cli/{chunk-5MDIZX7M.js → chunk-3ACMRZRV.js} +8 -6
- package/dist/cli/{chunk-FGLKOWSC.mjs → chunk-3KZCFOZ6.mjs} +1 -1
- package/dist/cli/chunk-4KQ6SIBC.mjs +1038 -0
- package/dist/cli/{chunk-Z2GUFYT6.js → chunk-5LEAXRHV.js} +11 -11
- package/dist/cli/{chunk-MEAIOXJL.mjs → chunk-6NNVJDVR.mjs} +7 -5
- package/dist/cli/{chunk-NMJ4D2WO.js → chunk-6PMCRYDA.js} +216 -91
- package/dist/cli/{chunk-RY4IVOU3.mjs → chunk-6RNQGWFD.mjs} +16 -806
- package/dist/cli/{chunk-EBU7LQ64.js → chunk-6V7KVRR5.js} +3 -3
- package/dist/cli/{chunk-OZBWRICM.js → chunk-7LFIMWMW.js} +2 -2
- package/dist/cli/chunk-7MJ6VI2T.mjs +19 -0
- package/dist/cli/chunk-7SWNPNML.js +1039 -0
- package/dist/cli/{chunk-IHHLTHE7.mjs → chunk-7U3XNPOA.mjs} +267 -38
- package/dist/cli/{chunk-5FDKJQZE.js → chunk-7VZHXVOQ.js} +1 -1
- package/dist/cli/{chunk-R64BGGRQ.mjs → chunk-AT636LLU.mjs} +2 -2
- package/dist/cli/{chunk-MDJPEGVN.js → chunk-BGTPIMSM.js} +392 -261
- package/dist/cli/{chunk-KVJ5VHW5.mjs → chunk-CKSOY47X.mjs} +2 -2
- package/dist/cli/{chunk-7DH2FD7Y.mjs → chunk-D464JW3N.mjs} +5 -85
- package/dist/cli/chunk-D6DXOUTO.mjs +827 -0
- package/dist/cli/{describe-2WQY7ZCY.js → chunk-EM4TXCCI.js} +27 -44
- package/dist/cli/chunk-F6QV42MC.mjs +91 -0
- package/dist/cli/chunk-FKVYR3QA.mjs +1039 -0
- package/dist/cli/{chunk-FGMX4HUZ.js → chunk-GEGW5HPZ.js} +29 -27
- package/dist/cli/{chunk-TNMBPMMC.js → chunk-HUB3CCM2.js} +13 -4
- package/dist/cli/{chunk-ACCR7POB.js → chunk-I6VHOU5O.js} +27 -27
- package/dist/cli/{chunk-Z2XUZC34.js → chunk-IAYSWZDJ.js} +188 -76
- package/dist/cli/chunk-JF3TLIMQ.js +19 -0
- package/dist/cli/{chunk-HDFRJ4IT.mjs → chunk-JVHOS3K3.mjs} +177 -65
- package/dist/cli/{chunk-V7REJRDZ.mjs → chunk-LFXLQEZN.mjs} +1 -1
- package/dist/cli/{chunk-CRYOUPEB.js → chunk-LROWFPGN.js} +16 -16
- package/dist/cli/{chunk-SLEUWLJE.mjs → chunk-MSY22NQW.mjs} +5 -5
- package/dist/cli/{chunk-BGY2PLP7.mjs → chunk-MUUQZIOF.mjs} +23 -49
- package/dist/cli/{chunk-FCAZQ7WV.js → chunk-NBLSCHKK.js} +52 -50
- package/dist/cli/{chunk-QAJAJPKK.mjs → chunk-NHRWZPWZ.mjs} +4 -2
- package/dist/cli/{chunk-KXMNM4AQ.js → chunk-NMU3VR5U.js} +4 -5
- package/dist/cli/{chunk-QIW7TVV6.mjs → chunk-O2RX5A66.mjs} +14 -12
- package/dist/cli/{chunk-SAZFZQRI.js → chunk-OMQ23IXL.js} +2 -2
- package/dist/cli/{chunk-5IYOUBFZ.js → chunk-OPJAJBD4.js} +28 -54
- package/dist/cli/{chunk-FJZILBL7.mjs → chunk-PHSEINNH.mjs} +5 -5
- package/dist/cli/{chunk-3DNKFH2G.mjs → chunk-PKHVSIJG.mjs} +19 -1
- package/dist/cli/{chunk-WIYWSYDS.mjs → chunk-Q2EIZKVX.mjs} +3 -3
- package/dist/cli/{chunk-5FY72YOV.js → chunk-QJZ6WNWD.js} +8 -8
- package/dist/cli/{chunk-EIMG75BT.mjs → chunk-S3A4IXBQ.mjs} +4 -5
- package/dist/cli/{chunk-3BMLICF7.mjs → chunk-S42YIQNE.mjs} +13 -8
- package/dist/cli/{chunk-RFONC4WJ.mjs → chunk-S6UAJR6V.mjs} +3 -3
- package/dist/cli/{chunk-G7L27KZB.mjs → chunk-TKAQR3IB.mjs} +31 -21
- package/dist/cli/{chunk-QWRHGGUH.js → chunk-UAJFWWZE.js} +20 -2
- package/dist/cli/{chunk-VZBCEA5S.mjs → chunk-UDRPEZ5F.mjs} +223 -92
- package/dist/cli/{chunk-KCCOQ5G5.js → chunk-ULK7JGGI.js} +511 -282
- package/dist/cli/chunk-V335HGX5.js +1038 -0
- package/dist/cli/{chunk-MGXEUZHL.mjs → chunk-V3MEA7NB.mjs} +7 -5
- package/dist/cli/{chunk-O6QOMHCH.js → chunk-VFGVXXZP.js} +37 -35
- package/dist/cli/chunk-VLEVVDWG.js +91 -0
- package/dist/cli/{chunk-R2GLGLDR.mjs → chunk-VPHN2HAL.mjs} +216 -91
- package/dist/cli/{chunk-CFUTOWE4.js → chunk-WG3KGAII.js} +8 -88
- package/dist/cli/{describe-FRAVCY5F.mjs → chunk-WIY6U6GD.mjs} +19 -36
- package/dist/cli/{chunk-PNATXKLP.js → chunk-XANRM3QA.js} +6 -6
- package/dist/cli/{chunk-KGQNIA37.js → chunk-XQCTG5OO.js} +44 -34
- package/dist/cli/{chunk-T3MVIPHG.mjs → chunk-XZAFIOUQ.mjs} +1 -1
- package/dist/cli/chunk-Y4DPU4OM.js +827 -0
- package/dist/cli/chunk-YT5EODJO.js +920 -0
- package/dist/cli/{chunk-SCIR5JXW.js → chunk-ZGQSFYD2.js} +56 -51
- package/dist/cli/{chunk-QEYHTIV4.mjs → chunk-ZUK6LIJB.mjs} +10 -1
- package/dist/cli/command-docs.d.mts +5 -0
- package/dist/cli/command-docs.d.ts +5 -0
- package/dist/cli/command-docs.js +2 -2
- package/dist/cli/command-docs.mjs +1 -1
- package/dist/cli/{csv-NX7NO3O6.js → csv-C56GSCEU.js} +13 -12
- package/dist/cli/{csv-DQ6GW5B5.mjs → csv-L54JS7TF.mjs} +4 -3
- package/dist/cli/{db-R72DFMP6.js → db-MOGRMZTA.js} +30 -27
- package/dist/cli/{db-2YP33M4K.mjs → db-YVBUW2DX.mjs} +10 -7
- package/dist/cli/describe-KYEULWPG.js +48 -0
- package/dist/cli/describe-NLZKKT7C.mjs +48 -0
- package/dist/cli/describe-core.d.mts +153 -5
- package/dist/cli/describe-core.d.ts +153 -5
- package/dist/cli/describe-core.js +9 -5
- package/dist/cli/describe-core.mjs +10 -6
- package/dist/cli/{executions-ZLHD4QIT.js → executions-A7L4ULPP.js} +9 -7
- package/dist/cli/{executions-WFBU7L2Q.mjs → executions-ZBGSTT37.mjs} +7 -5
- package/dist/cli/{feedback-CN3HJFVF.mjs → feedback-G6GU37FD.mjs} +6 -5
- package/dist/cli/{feedback-NDV6TPHG.js → feedback-OHLY7RYD.js} +10 -9
- package/dist/cli/getting-started.d.mts +6 -6
- package/dist/cli/getting-started.d.ts +6 -6
- package/dist/cli/getting-started.js +2 -2
- package/dist/cli/getting-started.mjs +1 -1
- package/dist/cli/git-P76N4NDQ.js +25 -0
- package/dist/cli/{git-QAD6E3HW.mjs → git-PKZW3QD5.mjs} +5 -4
- package/dist/cli/index.js +152 -142
- package/dist/cli/index.mjs +56 -46
- package/dist/cli/{legacy-noops-KVC5QJ74.js → legacy-noops-5LM3D724.js} +8 -7
- package/dist/cli/{legacy-noops-TPTKNBI2.mjs → legacy-noops-LJJYG6YA.mjs} +5 -4
- package/dist/cli/{monitors-SV57JFLO.mjs → monitors-72QV4ALO.mjs} +19 -6
- package/dist/cli/{monitors-KCROV6PJ.js → monitors-UX3H5DC2.js} +103 -90
- package/dist/cli/{org-KX6QIR7A.mjs → org-6RRRKOCP.mjs} +9 -6
- package/dist/cli/{org-NJYX5743.js → org-IF7ISUVB.js} +70 -67
- package/dist/cli/pages-3SU3HKL7.js +252 -0
- package/dist/cli/pages-DXDYYKEK.mjs +252 -0
- package/dist/cli/{play-IQQ57L7Z.mjs → play-AHASKRLA.mjs} +512 -1508
- package/dist/cli/{play-32L7EQN4.js → play-EUIUVLUH.js} +1090 -2086
- package/dist/cli/{providers-4WEC2EQN.mjs → providers-GWGE3WY3.mjs} +7 -5
- package/dist/cli/{providers-ZQV6QLVQ.js → providers-V5WU4VXJ.js} +10 -8
- package/dist/cli/{quickstart-RQ76RIUE.mjs → quickstart-OXKQ5ZST.mjs} +5 -4
- package/dist/cli/{quickstart-HKUSFADF.js → quickstart-QP65I5PF.js} +13 -12
- package/dist/cli/run-6MM72IGL.mjs +443 -0
- package/dist/cli/run-F3FTYS56.js +443 -0
- package/dist/cli/search-OYKOOI7A.js +32 -0
- package/dist/cli/{search-ZRAVFBZC.mjs → search-ZMEBVJZB.mjs} +8 -6
- package/dist/cli/{secrets-J3G5Y43U.js → secrets-ACND5N6A.js} +16 -14
- package/dist/cli/{secrets-CMJOVNVH.mjs → secrets-XRCJFF3L.mjs} +7 -5
- package/dist/cli/{sessions-MDQYY5CB.mjs → sessions-RTK3CEW6.mjs} +5 -4
- package/dist/cli/{sessions-WY7VN4AE.js → sessions-ZGRJPCFF.js} +6 -5
- package/dist/cli/{settings-HTNTFKCE.js → settings-PD5KWQQZ.js} +80 -77
- package/dist/cli/{settings-4RWZPCT5.mjs → settings-XDRBPRDE.mjs} +14 -11
- package/dist/cli/{setup-P3GYYZWG.mjs → setup-3HPYAOCC.mjs} +12 -10
- package/dist/cli/{setup-QGCAP62X.js → setup-E2Q6PXHC.js} +53 -51
- package/dist/cli/{skills-7NAE4ICL.mjs → skills-OOQFMPOV.mjs} +7 -6
- package/dist/cli/skills-XYWV52KB.js +31 -0
- package/dist/cli/tools-ASCDZJCK.js +55 -0
- package/dist/cli/tools-C5ZT3UIC.mjs +55 -0
- package/dist/cli/update-5RQSEIFM.js +33 -0
- package/dist/cli/{update-GODLRKNB.mjs → update-ZWJTADIW.mjs} +9 -8
- package/dist/cli/{workflow-4BXOUVEO.mjs → workflow-23NMPAJ7.mjs} +10 -8
- package/dist/cli/{workflow-AO3A6LXN.js → workflow-TCXU5YJV.js} +20 -18
- package/dist/client-DS2IWYZF.d.ts +10 -0
- package/dist/client-DTwnz_AW.d.mts +10 -0
- package/dist/{client-CYqG23K1.d.ts → client-core-B_6HAIBy.d.ts} +385 -12
- package/dist/{client-BbU5_j7n.d.mts → client-core-DzBSlY1U.d.mts} +385 -12
- package/dist/{compiler-manifest-CWz_sNOQ.d.ts → compiler-manifest-Dcyd3-Zd.d.mts} +43 -1
- package/dist/{compiler-manifest-CWz_sNOQ.d.mts → compiler-manifest-Dcyd3-Zd.d.ts} +43 -1
- package/dist/errors-d-sTrZhe.d.mts +142 -0
- package/dist/errors-yQwfgrcX.d.ts +142 -0
- package/dist/{index-BF2JB_Ug.d.mts → index-C6oXchjN.d.mts} +1 -1
- package/dist/{index-4NPXDzlD.d.ts → index-P56zy_z5.d.ts} +1 -1
- package/dist/index.d.mts +11 -147
- package/dist/index.d.ts +11 -147
- package/dist/index.js +15 -11
- package/dist/index.mjs +14 -10
- package/dist/install-integrity.json +141 -104
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/bundle-play-file.mjs +2 -1
- package/dist/plays/play-description.d.mts +2 -2
- package/dist/plays/play-description.d.ts +2 -2
- package/dist/plays/play-description.js +2 -2
- package/dist/plays/play-description.mjs +1 -1
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/dist/server.d.mts +17 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.js +76 -0
- package/dist/server.mjs +76 -0
- package/package.json +6 -1
- package/dist/cli/auth-IACERYQH.js +0 -40
- package/dist/cli/chunk-PQUUWFXY.js +0 -95
- package/dist/cli/chunk-YBWAG72P.mjs +0 -95
- package/dist/cli/chunk-YQDUODZW.js +0 -1710
- package/dist/cli/git-JVI36ZFV.js +0 -24
- package/dist/cli/search-75X5LTDE.js +0 -30
- package/dist/cli/skills-BKJ53JOQ.js +0 -30
- package/dist/cli/tools-COWMIEJ6.mjs +0 -45
- package/dist/cli/tools-TASOCE7I.js +0 -45
- package/dist/cli/update-SJIKVME6.js +0 -32
|
@@ -1,1235 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Handles authentication, retries with exponential backoff, localhost failover
|
|
5
|
-
* (tries both `localhost` and `127.0.0.1`), and structured error mapping.
|
|
6
|
-
*
|
|
7
|
-
* ## Retry behavior
|
|
8
|
-
*
|
|
9
|
-
* - **Max attempts**: `1 + maxRetries` (default: 4 total attempts)
|
|
10
|
-
* - **Backoff**: exponential — 1s, 2s, 4s, 8s, ... capped at 30s
|
|
11
|
-
* - **Total automatic retry wait**: 30s per request; a longer upstream
|
|
12
|
-
* `Retry-After` is returned to the caller instead of blocking the request
|
|
13
|
-
* - **Retryable**: network errors, timeouts, HTTP 429 (rate limit)
|
|
14
|
-
* - **Not retryable**: HTTP 401 (auth error), HTTP 403 and other 4xx API errors
|
|
15
|
-
*
|
|
16
|
-
* ## Localhost failover
|
|
17
|
-
*
|
|
18
|
-
* Local development hosts try loopback variants. This handles DNS resolution
|
|
19
|
-
* differences across platforms.
|
|
20
|
-
*
|
|
21
|
-
* @module
|
|
22
|
-
*/
|
|
23
|
-
import type { ResolvedConfig } from './types';
|
|
24
|
-
import {
|
|
25
|
-
AuthError,
|
|
26
|
-
DeeplineError,
|
|
27
|
-
RateLimitError,
|
|
28
|
-
ToolExecutionError,
|
|
29
|
-
ToolRateLimitError,
|
|
30
|
-
} from './errors';
|
|
31
|
-
import {
|
|
32
|
-
deserializeToolExecutionFailure,
|
|
33
|
-
serializeToolExecutionFailure,
|
|
34
|
-
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
35
|
-
} from '../../shared_libs/plays/tool-execution-error';
|
|
36
|
-
import { SDK_API_CONTRACT, SDK_VERSION } from './version';
|
|
37
|
-
import { apiErrorCodeFromResponse, apiErrorMessage } from './api-error';
|
|
38
|
-
import type { LiveEventEnvelope } from './types';
|
|
39
|
-
import {
|
|
40
|
-
agentSessionIdFromEnvironment,
|
|
41
|
-
detectAgentRuntime,
|
|
42
|
-
isCoworkLikeSandbox,
|
|
43
|
-
} from './agent-runtime';
|
|
1
|
+
/** HTTP transport with the local SDK skills diagnostic enabled. */
|
|
2
|
+
import { HttpClient as HttpClientCore } from './http-core';
|
|
44
3
|
import { readSdkSkillsLocalVersion } from './skills-version';
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
ABSURD_RELEASE_OVERRIDE_HEADER,
|
|
48
|
-
RETAINED_PIPELINE_RECEIVERS_HEADER,
|
|
49
|
-
COORDINATOR_INTERNAL_TOKEN_HEADER,
|
|
50
|
-
COORDINATOR_URL_OVERRIDE_HEADER,
|
|
51
|
-
PREFERRED_RECEIPT_GATEWAY_MACHINE_ID_HEADER,
|
|
52
|
-
RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER,
|
|
53
|
-
SYNTHETIC_RUN_HEADER,
|
|
54
|
-
WORKER_CALLBACK_URL_OVERRIDE_HEADER,
|
|
55
|
-
} from '../../shared_libs/play-runtime/coordinator-headers.js';
|
|
56
|
-
import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '../../shared_libs/play-runtime/test-runtime-seams.js';
|
|
57
|
-
import {
|
|
58
|
-
EXECUTE_RETRY_POLICY_HEADER,
|
|
59
|
-
parseExecuteRetryPolicy,
|
|
60
|
-
} from '../../shared_libs/integration-contracts/execute-retry-contract.js';
|
|
61
|
-
|
|
62
|
-
const MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
|
|
63
|
-
const COWORK_NETWORK_HINT =
|
|
64
|
-
'Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.';
|
|
65
|
-
const REQUEST_TIMEOUT_MARKER = Symbol('deeplineRequestTimeout');
|
|
66
|
-
const REQUEST_ABORT_MARKER = Symbol('deeplineRequestAbort');
|
|
67
|
-
const MAX_AUTOMATIC_RETRY_WAIT_MS = 30_000;
|
|
68
|
-
|
|
69
|
-
interface RequestOptions {
|
|
70
|
-
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
71
|
-
body?: unknown;
|
|
72
|
-
formData?: FormData | (() => FormData);
|
|
73
|
-
headers?: Record<string, string>;
|
|
74
|
-
/** Per-request timeout override in milliseconds. */
|
|
75
|
-
timeout?: number;
|
|
76
|
-
/** Cancel this request, including body reads, failover, and retry waits. */
|
|
77
|
-
signal?: AbortSignal;
|
|
78
|
-
/**
|
|
79
|
-
* Retry HTTP 408/425/5xx JSON API responses. Use only for idempotent requests;
|
|
80
|
-
* most mutating API calls must fail loudly after the first server response.
|
|
81
|
-
*/
|
|
82
|
-
retryApiErrors?: boolean;
|
|
83
|
-
/**
|
|
84
|
-
* Only explicit server-authorized responses may retry; transport failures
|
|
85
|
-
* never retry unless `retryAmbiguousDelivery` is also set.
|
|
86
|
-
*/
|
|
87
|
-
retryFromExecutePolicy?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* The server declared this tool safe to resend after a timeout or lost
|
|
90
|
-
* response, so those failures retry and stay retryable when exhausted.
|
|
91
|
-
*/
|
|
92
|
-
retryAmbiguousDelivery?: boolean;
|
|
93
|
-
/** Retry budget enabled only by an explicit safe execute-policy response. */
|
|
94
|
-
executePolicyMaxRetries?: number;
|
|
95
|
-
/** Per-request retry override. Use 0 for possibly delivered mutating calls. */
|
|
96
|
-
maxRetries?: number;
|
|
97
|
-
/** Disable localhost/127.0.0.1 failover for non-idempotent requests. */
|
|
98
|
-
exactUrlOnly?: boolean;
|
|
99
|
-
/**
|
|
100
|
-
* Return a server-owned 429 envelope as a DeeplineError instead of replacing
|
|
101
|
-
* it with the transport's generic RateLimitError. Lifecycle mutations use
|
|
102
|
-
* this when their response explains whether the upstream change applied.
|
|
103
|
-
*/
|
|
104
|
-
preserveRateLimitResponse?: boolean;
|
|
105
|
-
/** Enables endpoint-specific structured tool failure mapping. */
|
|
106
|
-
toolId?: string;
|
|
107
|
-
onResponse?: (response: Response) => void;
|
|
108
|
-
/** Observation after the entire response body arrives, before error mapping. */
|
|
109
|
-
onResponseBody?: (observation: {
|
|
110
|
-
receivedAtMs: number;
|
|
111
|
-
httpStatus: number;
|
|
112
|
-
body: unknown;
|
|
113
|
-
}) => void;
|
|
114
|
-
allowNotFound?: boolean;
|
|
115
|
-
idempotencyKey?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
interface StreamOptions {
|
|
119
|
-
method?: 'GET' | 'POST';
|
|
120
|
-
/** An unkeyed mutation must not try another URL after a lost response. */
|
|
121
|
-
exactUrlOnly?: boolean;
|
|
122
|
-
body?: unknown;
|
|
123
|
-
headers?: Record<string, string>;
|
|
124
|
-
signal?: AbortSignal;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
type RequestAbortTaggedError = Error & {
|
|
128
|
-
[REQUEST_TIMEOUT_MARKER]?: number;
|
|
129
|
-
[REQUEST_ABORT_MARKER]?: true;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
function requestCancelledError(signal: AbortSignal): RequestAbortTaggedError {
|
|
133
|
-
const error = withErrorCause(new Error('Request aborted'), signal.reason);
|
|
134
|
-
(error as RequestAbortTaggedError)[REQUEST_ABORT_MARKER] = true;
|
|
135
|
-
return error;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function normalizeRequestAbortError(
|
|
139
|
-
error: unknown,
|
|
140
|
-
input: { timeoutMs: number; timedOut: boolean },
|
|
141
|
-
): Error {
|
|
142
|
-
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
143
|
-
const tagged = normalized as RequestAbortTaggedError;
|
|
144
|
-
if (input.timedOut) {
|
|
145
|
-
tagged[REQUEST_TIMEOUT_MARKER] = input.timeoutMs;
|
|
146
|
-
} else if (isAbortLikeError(normalized)) {
|
|
147
|
-
tagged[REQUEST_ABORT_MARKER] = true;
|
|
148
|
-
}
|
|
149
|
-
return normalized;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Merge caller-supplied headers over everything the transport already derived
|
|
154
|
-
* from the environment, so an explicitly passed header always wins.
|
|
155
|
-
*
|
|
156
|
-
* HTTP header names are case-insensitive, so an existing header is removed by
|
|
157
|
-
* case-insensitive match before the incoming name is set. Without
|
|
158
|
-
* that, `Authorization` and `authorization` would both survive into the record
|
|
159
|
-
* and `new Headers(record)` would concatenate the two values.
|
|
160
|
-
*/
|
|
161
|
-
function applyExplicitHeaders(
|
|
162
|
-
headers: Record<string, string>,
|
|
163
|
-
explicit: Record<string, string> | undefined,
|
|
164
|
-
): Record<string, string> {
|
|
165
|
-
if (!explicit) return headers;
|
|
166
|
-
for (const [rawName, value] of Object.entries(explicit)) {
|
|
167
|
-
const name = rawName.trim().toLowerCase();
|
|
168
|
-
if (!name) continue;
|
|
169
|
-
for (const existing of Object.keys(headers)) {
|
|
170
|
-
if (existing.toLowerCase() === name) delete headers[existing];
|
|
171
|
-
}
|
|
172
|
-
headers[rawName.trim()] = value;
|
|
173
|
-
}
|
|
174
|
-
return headers;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function isAbortLikeError(error: Error): boolean {
|
|
178
|
-
const name = error.name.toLowerCase();
|
|
179
|
-
const message = error.message.toLowerCase();
|
|
180
|
-
return (
|
|
181
|
-
name === 'aborterror' ||
|
|
182
|
-
message.includes('operation was aborted') ||
|
|
183
|
-
message === 'aborted'
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function withErrorCause<T extends Error>(error: T, cause: unknown): T {
|
|
188
|
-
if (cause !== null && cause !== undefined) {
|
|
189
|
-
Object.defineProperty(error, 'cause', {
|
|
190
|
-
value: cause,
|
|
191
|
-
configurable: true,
|
|
192
|
-
writable: true,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
return error;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function mapNetworkError(
|
|
199
|
-
baseUrl: string,
|
|
200
|
-
error: Error | null,
|
|
201
|
-
targetLabel: string,
|
|
202
|
-
): {
|
|
203
|
-
message: string;
|
|
204
|
-
code: string;
|
|
205
|
-
details?: Record<string, unknown>;
|
|
206
|
-
} {
|
|
207
|
-
const tagged = error as RequestAbortTaggedError | null;
|
|
208
|
-
const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
|
|
209
|
-
if (typeof timeoutMs === 'number') {
|
|
210
|
-
return {
|
|
211
|
-
message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
|
|
212
|
-
code: 'NETWORK_TIMEOUT',
|
|
213
|
-
details: { timeoutMs, target: targetLabel },
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
if (tagged?.[REQUEST_ABORT_MARKER]) {
|
|
217
|
-
return {
|
|
218
|
-
message: `Request to ${targetLabel} was aborted before it completed.`,
|
|
219
|
-
code: 'NETWORK_ABORTED',
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
message: error?.message
|
|
224
|
-
? `Unable to connect to ${baseUrl}. ${error.message}`
|
|
225
|
-
: `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
|
|
226
|
-
code: 'NETWORK_ERROR',
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function describeRequestTarget(path: string): string {
|
|
231
|
-
const toolExecuteMatch = path.match(
|
|
232
|
-
/^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/,
|
|
233
|
-
);
|
|
234
|
-
if (toolExecuteMatch) {
|
|
235
|
-
return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
|
|
236
|
-
}
|
|
237
|
-
return 'Deepline';
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function networkDiagnosticUrl(value: string): string | undefined {
|
|
241
|
-
try {
|
|
242
|
-
const url = new URL(value);
|
|
243
|
-
if (url.protocol !== 'https:' && url.protocol !== 'http:') return undefined;
|
|
244
|
-
// Diagnostics never retain URL credentials, query values, or fragments.
|
|
245
|
-
return `${url.origin}${url.pathname}`;
|
|
246
|
-
} catch {
|
|
247
|
-
// Preserve the original transport failure for malformed request URLs.
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function providerLabelFromToolId(toolId: string): string {
|
|
253
|
-
const normalized = toolId.trim().toLowerCase();
|
|
254
|
-
const provider = normalized.split(/[_:./-]+/)[0];
|
|
255
|
-
return provider || normalized || 'provider';
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
259
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Low-level HTTP client used internally by {@link DeeplineClient}.
|
|
264
|
-
*
|
|
265
|
-
* You typically don't construct this directly — it's created automatically
|
|
266
|
-
* when you instantiate `DeeplineClient` or call `Deepline.connect()`.
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```typescript
|
|
270
|
-
* // Internal usage pattern:
|
|
271
|
-
* const http = new HttpClient(resolvedConfig);
|
|
272
|
-
* const tools = await http.get<{ tools: ToolDefinition[] }>('/api/v2/tools');
|
|
273
|
-
* const result = await http.post('/api/v2/integrations/apollo/execute', { payload: input });
|
|
274
|
-
* ```
|
|
275
|
-
*/
|
|
276
|
-
export class HttpClient {
|
|
277
|
-
constructor(private config: ResolvedConfig) {}
|
|
278
|
-
|
|
279
|
-
private cleanDiagnosticHeader(
|
|
280
|
-
value: string | null | undefined,
|
|
281
|
-
): string | null {
|
|
282
|
-
const normalized = String(value ?? '')
|
|
283
|
-
.replace(/[\u0000-\u001f\u007f]/g, ' ')
|
|
284
|
-
.trim()
|
|
285
|
-
.slice(0, MAX_DIAGNOSTIC_HEADER_LENGTH);
|
|
286
|
-
return normalized || null;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
private environment(): Readonly<Record<string, string | undefined>> {
|
|
290
|
-
return (
|
|
291
|
-
this.config.environment ??
|
|
292
|
-
(typeof process !== 'undefined' ? process.env : {})
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private readSkillsVersionHeader(): string | null {
|
|
297
|
-
const explicit = this.cleanDiagnosticHeader(
|
|
298
|
-
this.environment().DEEPLINE_SKILLS_VERSION,
|
|
299
|
-
);
|
|
300
|
-
if (explicit) return explicit;
|
|
301
|
-
try {
|
|
302
|
-
return this.cleanDiagnosticHeader(
|
|
303
|
-
readSdkSkillsLocalVersion(this.config.baseUrl),
|
|
304
|
-
);
|
|
305
|
-
} catch {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private authHeaders(extra?: Record<string, string>): Record<string, string> {
|
|
311
|
-
const env = this.environment();
|
|
312
|
-
const transportHeaders: Record<string, string> = {
|
|
313
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
314
|
-
'User-Agent': `deepline-ts-sdk/${SDK_VERSION}`,
|
|
315
|
-
'X-Deepline-Client-Family': 'sdk',
|
|
316
|
-
'X-Deepline-CLI-Family': 'sdk',
|
|
317
|
-
'X-Deepline-Agent-Runtime': detectAgentRuntime(
|
|
318
|
-
this.config.environment ? { env, detectCowork: false } : {},
|
|
319
|
-
),
|
|
320
|
-
'X-Deepline-CLI-Version': SDK_VERSION,
|
|
321
|
-
'X-Deepline-SDK-Version': SDK_VERSION,
|
|
322
|
-
'X-Deepline-API-Contract': SDK_API_CONTRACT,
|
|
323
|
-
'X-Deepline-Run-Result-Shape': 'canonical',
|
|
324
|
-
};
|
|
325
|
-
const headers = deploymentProtectionHeaders(env);
|
|
326
|
-
const skillsVersion = this.readSkillsVersionHeader();
|
|
327
|
-
if (skillsVersion) {
|
|
328
|
-
headers['X-Deepline-Skills-Version'] = skillsVersion;
|
|
329
|
-
}
|
|
330
|
-
const agentSessionId = agentSessionIdFromEnvironment(env);
|
|
331
|
-
if (agentSessionId) {
|
|
332
|
-
headers['X-Deepline-Agent-Session-Id'] = agentSessionId;
|
|
333
|
-
}
|
|
334
|
-
const playArtifactR2Prefix = env.DEEPLINE_PLAY_ARTIFACT_R2_PREFIX;
|
|
335
|
-
if (playArtifactR2Prefix) {
|
|
336
|
-
headers['x-deepline-play-artifact-r2-prefix'] = playArtifactR2Prefix;
|
|
337
|
-
}
|
|
338
|
-
const coordinatorUrl = env.DEEPLINE_COORDINATOR_URL;
|
|
339
|
-
const coordinatorInternalToken = env.DEEPLINE_INTERNAL_TOKEN;
|
|
340
|
-
const absurdReleaseOverride = env.DEEPLINE_ABSURD_RELEASE;
|
|
341
|
-
const preferredReceiptGatewayMachineId =
|
|
342
|
-
env.DEEPLINE_PREFERRED_RECEIPT_GATEWAY_MACHINE_ID;
|
|
343
|
-
if (coordinatorUrl?.trim()) {
|
|
344
|
-
headers[COORDINATOR_URL_OVERRIDE_HEADER] = coordinatorUrl.trim();
|
|
345
|
-
}
|
|
346
|
-
const workerCallbackUrl = env.DEEPLINE_WORKER_CALLBACK_URL;
|
|
347
|
-
if (workerCallbackUrl?.trim()) {
|
|
348
|
-
headers[WORKER_CALLBACK_URL_OVERRIDE_HEADER] = workerCallbackUrl.trim();
|
|
349
|
-
}
|
|
350
|
-
const runtimeSchedulerSchema = env.DEEPLINE_RUNTIME_SCHEDULER_SCHEMA;
|
|
351
|
-
if (runtimeSchedulerSchema?.trim()) {
|
|
352
|
-
headers[RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER] =
|
|
353
|
-
runtimeSchedulerSchema.trim();
|
|
354
|
-
}
|
|
355
|
-
// Automated test harnesses (e.g. tests/gauntlet-e2e-tests) set DEEPLINE_SYNTHETIC_RUN
|
|
356
|
-
// so their intentionally failing plays do not page the SDK CLI error
|
|
357
|
-
// channel. Honored server-side only in non-prod (see plays/run route).
|
|
358
|
-
const syntheticRun = env.DEEPLINE_SYNTHETIC_RUN;
|
|
359
|
-
if (syntheticRun && syntheticRun.trim() && syntheticRun.trim() !== '0') {
|
|
360
|
-
headers[SYNTHETIC_RUN_HEADER] = '1';
|
|
361
|
-
}
|
|
362
|
-
// Test-only runtime seam. The server validates both environment and value
|
|
363
|
-
// before honoring this header.
|
|
364
|
-
const runtimeTestFault = env.DEEPLINE_TEST_RUNTIME_FAULT;
|
|
365
|
-
if (runtimeTestFault?.trim()) {
|
|
366
|
-
headers[PLAY_RUNTIME_TEST_FAULT_HEADER] = runtimeTestFault.trim();
|
|
367
|
-
}
|
|
368
|
-
const retainedReceivers =
|
|
369
|
-
env.DEEPLINE_RUNTIME_RETAINED_PIPELINE_RECEIVERS?.trim();
|
|
370
|
-
if (retainedReceivers && coordinatorInternalToken?.trim()) {
|
|
371
|
-
headers[RETAINED_PIPELINE_RECEIVERS_HEADER] = retainedReceivers;
|
|
372
|
-
headers[COORDINATOR_INTERNAL_TOKEN_HEADER] =
|
|
373
|
-
coordinatorInternalToken.trim();
|
|
374
|
-
}
|
|
375
|
-
if (absurdReleaseOverride?.trim() && coordinatorInternalToken?.trim()) {
|
|
376
|
-
headers[ABSURD_RELEASE_OVERRIDE_HEADER] = absurdReleaseOverride.trim();
|
|
377
|
-
}
|
|
378
|
-
if (
|
|
379
|
-
preferredReceiptGatewayMachineId?.trim() &&
|
|
380
|
-
coordinatorInternalToken?.trim()
|
|
381
|
-
) {
|
|
382
|
-
headers[PREFERRED_RECEIPT_GATEWAY_MACHINE_ID_HEADER] =
|
|
383
|
-
preferredReceiptGatewayMachineId.trim();
|
|
384
|
-
}
|
|
385
|
-
if (
|
|
386
|
-
coordinatorInternalToken?.trim() &&
|
|
387
|
-
(coordinatorUrl?.trim() ||
|
|
388
|
-
workerCallbackUrl?.trim() ||
|
|
389
|
-
runtimeTestFault?.trim() ||
|
|
390
|
-
absurdReleaseOverride?.trim() ||
|
|
391
|
-
preferredReceiptGatewayMachineId?.trim())
|
|
392
|
-
) {
|
|
393
|
-
headers[COORDINATOR_INTERNAL_TOKEN_HEADER] =
|
|
394
|
-
coordinatorInternalToken.trim();
|
|
395
|
-
}
|
|
396
|
-
// Explicit client-option headers (e.g. `deepline plays run --header`) are
|
|
397
|
-
// applied last so an operator can override an env-derived header without
|
|
398
|
-
// rewriting the environment. Authorization of these headers stays
|
|
399
|
-
// server-side.
|
|
400
|
-
// Preserve layer precedence without leaving differently-cased duplicates
|
|
401
|
-
// for Fetch to concatenate. Environment-derived headers still supersede
|
|
402
|
-
// per-request headers, and explicit client-option headers remain last.
|
|
403
|
-
return applyExplicitHeaders(
|
|
404
|
-
applyExplicitHeaders(
|
|
405
|
-
applyExplicitHeaders(transportHeaders, extra),
|
|
406
|
-
headers,
|
|
407
|
-
),
|
|
408
|
-
this.config.headers,
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Send an HTTP request with automatic retries and error handling.
|
|
414
|
-
*
|
|
415
|
-
* @typeParam T - Expected response body type
|
|
416
|
-
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
417
|
-
* @param options - HTTP method, body, headers, and timeout
|
|
418
|
-
* @returns Parsed JSON response body
|
|
419
|
-
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
420
|
-
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
421
|
-
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
422
|
-
*/
|
|
423
|
-
async request<T = unknown>(
|
|
424
|
-
path: string,
|
|
425
|
-
options?: RequestOptions,
|
|
426
|
-
): Promise<T> {
|
|
427
|
-
const baseUrl = this.config.baseUrl;
|
|
428
|
-
const url = `${baseUrl}${path}`;
|
|
429
|
-
const method = options?.method ?? 'GET';
|
|
430
|
-
const requestStartedAt = Date.now();
|
|
431
|
-
let fetchAttempts = 0;
|
|
432
|
-
let lastRequestUrl = url;
|
|
433
|
-
let networkFailedAt: number | undefined;
|
|
434
|
-
let networkPhase:
|
|
435
|
-
| 'before_request'
|
|
436
|
-
| 'retry_wait'
|
|
437
|
-
| 'awaiting_headers'
|
|
438
|
-
| 'reading_body' = 'before_request';
|
|
439
|
-
|
|
440
|
-
const headers = this.authHeaders(options?.headers);
|
|
441
|
-
|
|
442
|
-
if (options?.body !== undefined) {
|
|
443
|
-
applyExplicitHeaders(headers, { 'Content-Type': 'application/json' });
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
let lastError: Error | null = null;
|
|
447
|
-
const candidateUrls = options?.exactUrlOnly
|
|
448
|
-
? [url]
|
|
449
|
-
: buildCandidateUrls(url);
|
|
450
|
-
let retryAfterDelayMs: number | null = null;
|
|
451
|
-
let totalRetryWaitMs = 0;
|
|
452
|
-
|
|
453
|
-
let maxRetries = options?.maxRetries ?? this.config.maxRetries;
|
|
454
|
-
|
|
455
|
-
requestAttempts: for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
456
|
-
if (options?.signal?.aborted) {
|
|
457
|
-
lastError = requestCancelledError(options.signal);
|
|
458
|
-
networkFailedAt = Date.now();
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
if (attempt > 0) {
|
|
462
|
-
const backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 30_000);
|
|
463
|
-
const delayMs =
|
|
464
|
-
retryAfterDelayMs === null
|
|
465
|
-
? backoffMs
|
|
466
|
-
: Math.max(backoffMs, retryAfterDelayMs);
|
|
467
|
-
retryAfterDelayMs = null;
|
|
468
|
-
if (totalRetryWaitMs + delayMs > MAX_AUTOMATIC_RETRY_WAIT_MS) {
|
|
469
|
-
// Honor the upstream delay by returning its original error. Never
|
|
470
|
-
// retry sooner than requested, and never let automatic retries hold
|
|
471
|
-
// the caller for an unbounded series of Retry-After sleeps.
|
|
472
|
-
break requestAttempts;
|
|
473
|
-
}
|
|
474
|
-
totalRetryWaitMs += delayMs;
|
|
475
|
-
networkPhase = 'retry_wait';
|
|
476
|
-
try {
|
|
477
|
-
await sleep(delayMs, options?.signal);
|
|
478
|
-
} catch (error) {
|
|
479
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
480
|
-
networkFailedAt = Date.now();
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
for (const candidateUrl of candidateUrls) {
|
|
486
|
-
if (options?.signal?.aborted) {
|
|
487
|
-
lastError = requestCancelledError(options.signal);
|
|
488
|
-
networkFailedAt = Date.now();
|
|
489
|
-
break requestAttempts;
|
|
490
|
-
}
|
|
491
|
-
const controller = new AbortController();
|
|
492
|
-
const timeoutMs = options?.timeout ?? this.config.timeout;
|
|
493
|
-
let requestTimedOut = false;
|
|
494
|
-
const timeoutId = setTimeout(() => {
|
|
495
|
-
requestTimedOut = true;
|
|
496
|
-
controller.abort();
|
|
497
|
-
}, timeoutMs);
|
|
498
|
-
const abortRequest = () => controller.abort();
|
|
499
|
-
options?.signal?.addEventListener('abort', abortRequest, {
|
|
500
|
-
once: true,
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
networkPhase = 'before_request';
|
|
504
|
-
try {
|
|
505
|
-
const requestBody =
|
|
506
|
-
options?.formData !== undefined
|
|
507
|
-
? typeof options.formData === 'function'
|
|
508
|
-
? options.formData()
|
|
509
|
-
: options.formData
|
|
510
|
-
: options?.body !== undefined
|
|
511
|
-
? JSON.stringify(options.body)
|
|
512
|
-
: undefined;
|
|
513
|
-
fetchAttempts += 1;
|
|
514
|
-
lastRequestUrl = candidateUrl;
|
|
515
|
-
networkPhase = 'awaiting_headers';
|
|
516
|
-
const response = await fetch(candidateUrl, {
|
|
517
|
-
method,
|
|
518
|
-
headers,
|
|
519
|
-
body: requestBody,
|
|
520
|
-
signal: controller.signal,
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
networkPhase = 'reading_body';
|
|
524
|
-
options?.onResponse?.(response);
|
|
525
|
-
if (response.status === 404 && options?.allowNotFound) {
|
|
526
|
-
clearTimeout(timeoutId);
|
|
527
|
-
return null as T;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Receiving headers is not completion: a stalled response body must
|
|
531
|
-
// remain inside the same request deadline and abort its connection.
|
|
532
|
-
const body = await response.text();
|
|
533
|
-
const receivedAtMs = Date.now();
|
|
534
|
-
clearTimeout(timeoutId);
|
|
535
|
-
const parsed = parseResponseBody(body);
|
|
536
|
-
options?.onResponseBody?.({
|
|
537
|
-
receivedAtMs,
|
|
538
|
-
httpStatus: response.status,
|
|
539
|
-
body: parsed,
|
|
540
|
-
});
|
|
541
|
-
const responseRetryAllowed =
|
|
542
|
-
options?.retryFromExecutePolicy !== true ||
|
|
543
|
-
parseExecuteRetryPolicy(
|
|
544
|
-
response.headers.get(EXECUTE_RETRY_POLICY_HEADER),
|
|
545
|
-
) === 'transient-http-safe';
|
|
546
|
-
if (
|
|
547
|
-
options?.retryFromExecutePolicy === true &&
|
|
548
|
-
responseRetryAllowed &&
|
|
549
|
-
options.executePolicyMaxRetries !== undefined
|
|
550
|
-
) {
|
|
551
|
-
maxRetries = options.executePolicyMaxRetries;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const structuredToolError =
|
|
555
|
-
options?.toolId && !response.ok
|
|
556
|
-
? deserializeToolExecutionFailure(
|
|
557
|
-
apiErrorMessage(parsed, response.status),
|
|
558
|
-
isRecord(parsed)
|
|
559
|
-
? (parsed as Record<string, unknown>).tool_error
|
|
560
|
-
: null,
|
|
561
|
-
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
562
|
-
)
|
|
563
|
-
: null;
|
|
564
|
-
if (structuredToolError) {
|
|
565
|
-
if (response.status === 429) {
|
|
566
|
-
const retryAfter = parseRetryAfter(response);
|
|
567
|
-
const failure =
|
|
568
|
-
serializeToolExecutionFailure(structuredToolError);
|
|
569
|
-
lastError =
|
|
570
|
-
failure === null
|
|
571
|
-
? structuredToolError
|
|
572
|
-
: new ToolRateLimitError(structuredToolError.message, {
|
|
573
|
-
...failure,
|
|
574
|
-
retryAfterMs:
|
|
575
|
-
failure.retryAfterMs ?? Math.max(0, retryAfter),
|
|
576
|
-
});
|
|
577
|
-
if (responseRetryAllowed && attempt < maxRetries) {
|
|
578
|
-
retryAfterDelayMs = retryAfter;
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
|
-
throw lastError;
|
|
582
|
-
}
|
|
583
|
-
throw structuredToolError;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (response.status === 429) {
|
|
587
|
-
if (
|
|
588
|
-
options?.preserveRateLimitResponse &&
|
|
589
|
-
isMonitorRateLimitEnvelope(parsed)
|
|
590
|
-
) {
|
|
591
|
-
throw new DeeplineError(
|
|
592
|
-
apiErrorMessage(parsed, response.status),
|
|
593
|
-
response.status,
|
|
594
|
-
apiErrorCodeFromResponse(parsed),
|
|
595
|
-
{ response: parsed },
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
const retryAfter = parseRetryAfter(response);
|
|
599
|
-
lastError = new RateLimitError(retryAfter);
|
|
600
|
-
if (responseRetryAllowed && attempt < maxRetries) {
|
|
601
|
-
retryAfterDelayMs = retryAfter;
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
throw lastError;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (
|
|
608
|
-
response.status === 401 &&
|
|
609
|
-
!isProviderOriginatedHttpError(parsed)
|
|
610
|
-
) {
|
|
611
|
-
throw new AuthError(apiErrorMessage(parsed, response.status));
|
|
612
|
-
}
|
|
613
|
-
if (!response.ok) {
|
|
614
|
-
const retryableApiError =
|
|
615
|
-
options?.retryApiErrors === true &&
|
|
616
|
-
responseRetryAllowed &&
|
|
617
|
-
isRetryableApiErrorResponse(response.status);
|
|
618
|
-
// A 5xx that escaped a Worker can return Cloudflare's default HTML
|
|
619
|
-
// error page. Never surface raw HTML to CLI users: replace the
|
|
620
|
-
// message with a structured summary and store a short flag in
|
|
621
|
-
// details instead of the full HTML body. JSON-envelope handling
|
|
622
|
-
// below is unchanged.
|
|
623
|
-
const htmlError = detectHtmlErrorBody(
|
|
624
|
-
body,
|
|
625
|
-
response.headers.get('content-type'),
|
|
626
|
-
);
|
|
627
|
-
if (htmlError) {
|
|
628
|
-
lastError = new DeeplineError(
|
|
629
|
-
htmlError.message(response.status),
|
|
630
|
-
response.status,
|
|
631
|
-
'API_ERROR',
|
|
632
|
-
{
|
|
633
|
-
htmlErrorPage: true,
|
|
634
|
-
...(htmlError.title ? { title: htmlError.title } : {}),
|
|
635
|
-
...(htmlError.workerThrewException
|
|
636
|
-
? { workerThrewException: true }
|
|
637
|
-
: {}),
|
|
638
|
-
},
|
|
639
|
-
);
|
|
640
|
-
if (retryableApiError && attempt < maxRetries) {
|
|
641
|
-
retryAfterDelayMs = parseOptionalRetryAfter(response);
|
|
642
|
-
break;
|
|
643
|
-
}
|
|
644
|
-
throw lastError;
|
|
645
|
-
}
|
|
646
|
-
const errorValue =
|
|
647
|
-
typeof parsed === 'object' && parsed && 'error' in parsed
|
|
648
|
-
? (parsed as Record<string, unknown>).error
|
|
649
|
-
: undefined;
|
|
650
|
-
const msg =
|
|
651
|
-
typeof errorValue === 'string'
|
|
652
|
-
? errorValue
|
|
653
|
-
: errorValue &&
|
|
654
|
-
typeof errorValue === 'object' &&
|
|
655
|
-
'message' in errorValue &&
|
|
656
|
-
typeof (errorValue as Record<string, unknown>).message ===
|
|
657
|
-
'string'
|
|
658
|
-
? (errorValue as Record<string, string>).message
|
|
659
|
-
: typeof parsed === 'object' &&
|
|
660
|
-
parsed &&
|
|
661
|
-
'message' in parsed &&
|
|
662
|
-
typeof (parsed as Record<string, unknown>).message ===
|
|
663
|
-
'string'
|
|
664
|
-
? (parsed as Record<string, string>).message
|
|
665
|
-
: `HTTP ${response.status}`;
|
|
666
|
-
const apiErrorCode = apiErrorCodeFromResponse(parsed);
|
|
667
|
-
lastError = new DeeplineError(msg, response.status, apiErrorCode, {
|
|
668
|
-
response: parsed,
|
|
669
|
-
});
|
|
670
|
-
if (retryableApiError && attempt < maxRetries) {
|
|
671
|
-
retryAfterDelayMs = parseOptionalRetryAfter(response);
|
|
672
|
-
break;
|
|
673
|
-
}
|
|
674
|
-
throw lastError;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// Vercel's deployment-protection interstitial is served as a 2xx
|
|
678
|
-
// HTML page, so it never reaches the !ok branch above. Without this
|
|
679
|
-
// the caller receives a string where it expected JSON and reports
|
|
680
|
-
// whatever field it found missing — an expired `vercel login` shows
|
|
681
|
-
// up as an unrelated validation error several layers away.
|
|
682
|
-
if (typeof parsed === 'string') {
|
|
683
|
-
const interstitial = detectHtmlErrorBody(
|
|
684
|
-
body,
|
|
685
|
-
response.headers.get('content-type'),
|
|
686
|
-
);
|
|
687
|
-
if (interstitial) {
|
|
688
|
-
throw new DeeplineError(
|
|
689
|
-
`${this.config.baseUrl} returned a deployment-protection page ` +
|
|
690
|
-
'instead of JSON. Set VERCEL_PROTECTION_BYPASS_TOKEN, or run ' +
|
|
691
|
-
'`vercel login` if the CLI session expired.',
|
|
692
|
-
response.status,
|
|
693
|
-
'DEPLOYMENT_PROTECTED',
|
|
694
|
-
{ htmlErrorPage: true },
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
return parsed as T;
|
|
700
|
-
} catch (error) {
|
|
701
|
-
clearTimeout(timeoutId);
|
|
702
|
-
networkFailedAt = Date.now();
|
|
703
|
-
if (options?.signal?.aborted) {
|
|
704
|
-
lastError = requestCancelledError(options.signal);
|
|
705
|
-
break requestAttempts;
|
|
706
|
-
}
|
|
707
|
-
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
708
|
-
throw error;
|
|
709
|
-
}
|
|
710
|
-
if (error instanceof RateLimitError) {
|
|
711
|
-
lastError = error;
|
|
712
|
-
break;
|
|
713
|
-
}
|
|
714
|
-
lastError = normalizeRequestAbortError(error, {
|
|
715
|
-
timeoutMs,
|
|
716
|
-
timedOut: requestTimedOut,
|
|
717
|
-
});
|
|
718
|
-
if (
|
|
719
|
-
options?.retryFromExecutePolicy &&
|
|
720
|
-
options.retryAmbiguousDelivery !== true
|
|
721
|
-
) {
|
|
722
|
-
break requestAttempts;
|
|
723
|
-
}
|
|
724
|
-
} finally {
|
|
725
|
-
clearTimeout(timeoutId);
|
|
726
|
-
options?.signal?.removeEventListener('abort', abortRequest);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (attempt < maxRetries) continue;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
if (lastError instanceof DeeplineError) {
|
|
734
|
-
throw lastError;
|
|
735
|
-
}
|
|
736
|
-
const mappedNetworkError = mapNetworkError(
|
|
737
|
-
baseUrl,
|
|
738
|
-
lastError,
|
|
739
|
-
describeRequestTarget(path),
|
|
740
|
-
);
|
|
741
|
-
const failedAt = networkFailedAt ?? Date.now();
|
|
742
|
-
const diagnosticUrl = networkDiagnosticUrl(lastRequestUrl);
|
|
743
|
-
const networkDetails = {
|
|
744
|
-
...mappedNetworkError.details,
|
|
745
|
-
request: {
|
|
746
|
-
method,
|
|
747
|
-
...(diagnosticUrl === undefined ? {} : { url: diagnosticUrl }),
|
|
748
|
-
failedAtUtc: new Date(failedAt).toISOString(),
|
|
749
|
-
// Elapsed time covers the entire logical request, including retries.
|
|
750
|
-
elapsedMs: Math.max(0, failedAt - requestStartedAt),
|
|
751
|
-
attempts: fetchAttempts,
|
|
752
|
-
phase: networkPhase,
|
|
753
|
-
},
|
|
754
|
-
};
|
|
755
|
-
if (options?.toolId) {
|
|
756
|
-
const code = mappedNetworkError.code;
|
|
757
|
-
throw withErrorCause(
|
|
758
|
-
new ToolExecutionError(
|
|
759
|
-
withCoworkNetworkHint(mappedNetworkError.message),
|
|
760
|
-
{
|
|
761
|
-
toolId: options.toolId,
|
|
762
|
-
provider: providerLabelFromToolId(options.toolId),
|
|
763
|
-
operation: options.toolId,
|
|
764
|
-
code,
|
|
765
|
-
origin: 'deepline',
|
|
766
|
-
category: 'network',
|
|
767
|
-
// A direct client timeout has ambiguous delivery. Fail closed unless
|
|
768
|
-
// the server returns the authoritative action delivery contract.
|
|
769
|
-
retryable: options.retryAmbiguousDelivery === true,
|
|
770
|
-
statusCode: null,
|
|
771
|
-
requestId: null,
|
|
772
|
-
retryAfterMs: null,
|
|
773
|
-
networkKind:
|
|
774
|
-
code === 'NETWORK_TIMEOUT'
|
|
775
|
-
? 'timeout'
|
|
776
|
-
: code === 'NETWORK_ABORTED'
|
|
777
|
-
? 'unknown'
|
|
778
|
-
: 'unavailable',
|
|
779
|
-
networkScope: 'client_to_deepline',
|
|
780
|
-
details: networkDetails,
|
|
781
|
-
publicDetails: options.idempotencyKey
|
|
782
|
-
? { idempotencyKey: options.idempotencyKey }
|
|
783
|
-
: null,
|
|
784
|
-
},
|
|
785
|
-
),
|
|
786
|
-
lastError,
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
throw withErrorCause(
|
|
790
|
-
new DeeplineError(
|
|
791
|
-
withCoworkNetworkHint(mappedNetworkError.message),
|
|
792
|
-
undefined,
|
|
793
|
-
mappedNetworkError.code,
|
|
794
|
-
networkDetails,
|
|
795
|
-
),
|
|
796
|
-
lastError,
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Send a GET request.
|
|
802
|
-
*
|
|
803
|
-
* @typeParam T - Expected response body type
|
|
804
|
-
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
805
|
-
*/
|
|
806
|
-
async get<T = unknown>(
|
|
807
|
-
path: string,
|
|
808
|
-
options?: Pick<
|
|
809
|
-
RequestOptions,
|
|
810
|
-
| 'retryApiErrors'
|
|
811
|
-
| 'timeout'
|
|
812
|
-
| 'signal'
|
|
813
|
-
| 'maxRetries'
|
|
814
|
-
| 'exactUrlOnly'
|
|
815
|
-
| 'toolId'
|
|
816
|
-
| 'onResponse'
|
|
817
|
-
| 'onResponseBody'
|
|
818
|
-
| 'allowNotFound'
|
|
819
|
-
| 'idempotencyKey'
|
|
820
|
-
| 'headers'
|
|
821
|
-
>,
|
|
822
|
-
): Promise<T> {
|
|
823
|
-
return this.request<T>(path, {
|
|
824
|
-
method: 'GET',
|
|
825
|
-
retryApiErrors: true,
|
|
826
|
-
...options,
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
async *streamSse<TEvent extends LiveEventEnvelope = LiveEventEnvelope>(
|
|
831
|
-
path: string,
|
|
832
|
-
options?: StreamOptions,
|
|
833
|
-
): AsyncGenerator<TEvent> {
|
|
834
|
-
const url = `${this.config.baseUrl}${path}`;
|
|
835
|
-
const method = options?.method ?? 'GET';
|
|
836
|
-
const headers = this.authHeaders({
|
|
837
|
-
Accept: 'text/event-stream',
|
|
838
|
-
...options?.headers,
|
|
839
|
-
});
|
|
840
|
-
if (options?.body !== undefined) {
|
|
841
|
-
applyExplicitHeaders(headers, { 'Content-Type': 'application/json' });
|
|
842
|
-
}
|
|
843
|
-
let lastError: Error | null = null;
|
|
844
|
-
|
|
845
|
-
const candidateUrls = options?.exactUrlOnly
|
|
846
|
-
? [url]
|
|
847
|
-
: buildCandidateUrls(url);
|
|
848
|
-
for (const candidateUrl of candidateUrls) {
|
|
849
|
-
try {
|
|
850
|
-
const response = await fetch(candidateUrl, {
|
|
851
|
-
method,
|
|
852
|
-
headers,
|
|
853
|
-
body:
|
|
854
|
-
options?.body !== undefined
|
|
855
|
-
? JSON.stringify(options.body)
|
|
856
|
-
: undefined,
|
|
857
|
-
signal: options?.signal,
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
if (!response.ok) {
|
|
861
|
-
const body = await response.text();
|
|
862
|
-
const parsed = parseResponseBody(body);
|
|
863
|
-
if (
|
|
864
|
-
response.status === 401 &&
|
|
865
|
-
!isProviderOriginatedHttpError(parsed)
|
|
866
|
-
) {
|
|
867
|
-
throw new AuthError(apiErrorMessage(parsed, response.status));
|
|
868
|
-
}
|
|
869
|
-
const htmlError = detectHtmlErrorBody(
|
|
870
|
-
body,
|
|
871
|
-
response.headers.get('content-type'),
|
|
872
|
-
);
|
|
873
|
-
if (htmlError) {
|
|
874
|
-
throw new DeeplineError(
|
|
875
|
-
htmlError.message(response.status),
|
|
876
|
-
response.status,
|
|
877
|
-
'API_ERROR',
|
|
878
|
-
{
|
|
879
|
-
htmlErrorPage: true,
|
|
880
|
-
...(htmlError.title ? { title: htmlError.title } : {}),
|
|
881
|
-
...(htmlError.workerThrewException
|
|
882
|
-
? { workerThrewException: true }
|
|
883
|
-
: {}),
|
|
884
|
-
},
|
|
885
|
-
);
|
|
886
|
-
}
|
|
887
|
-
throw new DeeplineError(
|
|
888
|
-
apiErrorMessage(parsed, response.status),
|
|
889
|
-
response.status,
|
|
890
|
-
apiErrorCodeFromResponse(parsed),
|
|
891
|
-
{ response: parsed },
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
if (!response.body) {
|
|
895
|
-
throw new DeeplineError('SSE response did not include a body.');
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
yield* decodeSseStream<TEvent>(response.body);
|
|
899
|
-
return;
|
|
900
|
-
} catch (error) {
|
|
901
|
-
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
902
|
-
throw error;
|
|
903
|
-
}
|
|
904
|
-
const normalized =
|
|
905
|
-
error instanceof Error ? error : new Error(String(error));
|
|
906
|
-
if (isAbortLikeError(normalized) && options?.signal?.aborted) {
|
|
907
|
-
throw new DeeplineError(
|
|
908
|
-
`Stream from ${this.config.baseUrl} was aborted by the caller.`,
|
|
909
|
-
undefined,
|
|
910
|
-
'ABORTED',
|
|
911
|
-
);
|
|
912
|
-
}
|
|
913
|
-
lastError = normalized;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (lastError && isAbortLikeError(lastError)) {
|
|
918
|
-
throw new DeeplineError(
|
|
919
|
-
withCoworkNetworkHint(
|
|
920
|
-
`Unable to stream from ${this.config.baseUrl}. The remote stream was interrupted.`,
|
|
921
|
-
),
|
|
922
|
-
undefined,
|
|
923
|
-
'PLAY_STREAM_NETWORK_ABORTED',
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
throw new DeeplineError(
|
|
927
|
-
withCoworkNetworkHint(
|
|
928
|
-
lastError?.message
|
|
929
|
-
? `Unable to stream from ${this.config.baseUrl}. ${lastError.message}`
|
|
930
|
-
: `Unable to stream from ${this.config.baseUrl}.`,
|
|
931
|
-
),
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
/**
|
|
936
|
-
* Send a POST request with a JSON body.
|
|
937
|
-
*
|
|
938
|
-
* @typeParam T - Expected response body type
|
|
939
|
-
* @param path - API path
|
|
940
|
-
* @param body - Request body (will be JSON-serialized)
|
|
941
|
-
*/
|
|
942
|
-
async post<T = unknown>(
|
|
943
|
-
path: string,
|
|
944
|
-
body: unknown,
|
|
945
|
-
headers?: Record<string, string>,
|
|
946
|
-
options?: Pick<
|
|
947
|
-
RequestOptions,
|
|
948
|
-
| 'retryApiErrors'
|
|
949
|
-
| 'retryFromExecutePolicy'
|
|
950
|
-
| 'retryAmbiguousDelivery'
|
|
951
|
-
| 'executePolicyMaxRetries'
|
|
952
|
-
| 'timeout'
|
|
953
|
-
| 'signal'
|
|
954
|
-
| 'maxRetries'
|
|
955
|
-
| 'exactUrlOnly'
|
|
956
|
-
| 'toolId'
|
|
957
|
-
| 'onResponse'
|
|
958
|
-
| 'onResponseBody'
|
|
959
|
-
| 'allowNotFound'
|
|
960
|
-
| 'idempotencyKey'
|
|
961
|
-
>,
|
|
962
|
-
): Promise<T> {
|
|
963
|
-
return this.request<T>(path, {
|
|
964
|
-
method: 'POST',
|
|
965
|
-
body,
|
|
966
|
-
headers,
|
|
967
|
-
...options,
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
async postFormData<T = unknown>(
|
|
972
|
-
path: string,
|
|
973
|
-
formData: FormData | (() => FormData),
|
|
974
|
-
headers?: Record<string, string>,
|
|
975
|
-
): Promise<T> {
|
|
976
|
-
return this.request<T>(path, {
|
|
977
|
-
method: 'POST',
|
|
978
|
-
formData,
|
|
979
|
-
headers,
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
/**
|
|
984
|
-
* Send a PATCH request with a JSON body.
|
|
985
|
-
*
|
|
986
|
-
* @typeParam T - Expected response body type
|
|
987
|
-
* @param path - API path
|
|
988
|
-
* @param body - JSON-serializable request body
|
|
989
|
-
*/
|
|
990
|
-
async patch<T = unknown>(
|
|
991
|
-
path: string,
|
|
992
|
-
body?: unknown,
|
|
993
|
-
headers?: Record<string, string>,
|
|
994
|
-
): Promise<T> {
|
|
995
|
-
return this.request<T>(path, {
|
|
996
|
-
method: 'PATCH',
|
|
997
|
-
body,
|
|
998
|
-
headers,
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
async put<T = unknown>(
|
|
1003
|
-
path: string,
|
|
1004
|
-
body?: unknown,
|
|
1005
|
-
headers?: Record<string, string>,
|
|
1006
|
-
): Promise<T> {
|
|
1007
|
-
return this.request<T>(path, { method: 'PUT', body, headers });
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
/**
|
|
1011
|
-
* Send a DELETE request.
|
|
1012
|
-
*
|
|
1013
|
-
* @typeParam T - Expected response body type
|
|
1014
|
-
* @param path - API path
|
|
1015
|
-
*/
|
|
1016
|
-
async delete<T = unknown>(path: string): Promise<T> {
|
|
1017
|
-
return this.request<T>(path, { method: 'DELETE' });
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function parseResponseBody(body: string): unknown {
|
|
1022
|
-
try {
|
|
1023
|
-
return JSON.parse(body);
|
|
1024
|
-
} catch {
|
|
1025
|
-
return body;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
/**
|
|
1030
|
-
* A provider can reject its own credential with HTTP 401 while the caller's
|
|
1031
|
-
* Deepline API key remains valid. The API envelope is authoritative in that
|
|
1032
|
-
* case, so preserve it as a regular DeeplineError rather than prompting the
|
|
1033
|
-
* caller to replace DEEPLINE_API_KEY.
|
|
1034
|
-
*/
|
|
1035
|
-
function isProviderOriginatedHttpError(parsed: unknown): boolean {
|
|
1036
|
-
const response = asRecord(parsed);
|
|
1037
|
-
const error = asRecord(response?.error);
|
|
1038
|
-
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
1039
|
-
const code = error?.code ?? response?.code;
|
|
1040
|
-
return failureOrigin === 'provider' || code === 'UPSTREAM_BLOCKED';
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
/**
|
|
1044
|
-
* A monitor deployment may receive a safe, actionable 429 from Deepline after
|
|
1045
|
-
* an upstream lifecycle attempt. Do not treat a CDN/WAF 429 as that envelope:
|
|
1046
|
-
* it can be arbitrary HTML or text and must retain normal RateLimitError
|
|
1047
|
-
* behavior.
|
|
1048
|
-
*/
|
|
1049
|
-
function isMonitorRateLimitEnvelope(parsed: unknown): boolean {
|
|
1050
|
-
const response = asRecord(parsed);
|
|
1051
|
-
const error = asRecord(response?.error);
|
|
1052
|
-
return (
|
|
1053
|
-
(error?.code === 'monitor_upstream_rate_limited' ||
|
|
1054
|
-
error?.code === 'monitor_preview_rate_limited') &&
|
|
1055
|
-
typeof error.message === 'string'
|
|
1056
|
-
);
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
1060
|
-
return typeof value === 'object' && value !== null
|
|
1061
|
-
? (value as Record<string, unknown>)
|
|
1062
|
-
: null;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
function isRetryableApiErrorResponse(status: number): boolean {
|
|
1066
|
-
return status === 408 || status === 425 || (status >= 500 && status < 600);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
/**
|
|
1070
|
-
* Detect a raw Cloudflare HTML error page in an error response body.
|
|
1071
|
-
*
|
|
1072
|
-
* A 5xx that escaped a Worker can return Cloudflare's default HTML error page
|
|
1073
|
-
* ("Worker threw exception" / error code 1042). We must never surface raw HTML
|
|
1074
|
-
* to CLI users — it lands verbatim in `DeeplineError.message`/`details`. When
|
|
1075
|
-
* detected, we synthesize a structured message (status + extracted <title>) and
|
|
1076
|
-
* preserve the literal token `Worker threw exception` when present, since SDK
|
|
1077
|
-
* retry classifiers (`isTransientPlayRunReadError`) regex-match on it.
|
|
1078
|
-
*/
|
|
1079
|
-
function detectHtmlErrorBody(
|
|
1080
|
-
body: string,
|
|
1081
|
-
contentType?: string | null,
|
|
1082
|
-
): {
|
|
1083
|
-
title?: string;
|
|
1084
|
-
workerThrewException: boolean;
|
|
1085
|
-
message: (status: number) => string;
|
|
1086
|
-
} | null {
|
|
1087
|
-
const trimmed = body.trim();
|
|
1088
|
-
const lower = trimmed.toLowerCase();
|
|
1089
|
-
const isHtml =
|
|
1090
|
-
(contentType ?? '').toLowerCase().includes('text/html') ||
|
|
1091
|
-
lower.startsWith('<!doctype') ||
|
|
1092
|
-
lower.startsWith('<html');
|
|
1093
|
-
if (!isHtml) {
|
|
1094
|
-
return null;
|
|
1095
|
-
}
|
|
1096
|
-
const titleMatch = trimmed.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
1097
|
-
const title = titleMatch?.[1]?.replace(/\s+/g, ' ').trim() || undefined;
|
|
1098
|
-
const workerThrewException = /worker threw exception/i.test(trimmed);
|
|
1099
|
-
return {
|
|
1100
|
-
title,
|
|
1101
|
-
workerThrewException,
|
|
1102
|
-
message: (status: number): string => {
|
|
1103
|
-
// Detail segments joined with ': ', then the suppression note appended
|
|
1104
|
-
// in parens so it never reads as another colon-delimited segment.
|
|
1105
|
-
const segments = [`HTTP ${status}`];
|
|
1106
|
-
if (workerThrewException) {
|
|
1107
|
-
segments.push('Worker threw exception');
|
|
1108
|
-
}
|
|
1109
|
-
if (title) {
|
|
1110
|
-
segments.push(title);
|
|
1111
|
-
}
|
|
1112
|
-
return `${segments.join(': ')} (Cloudflare HTML error page suppressed)`;
|
|
1113
|
-
},
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
/** Parse the `Retry-After` header as milliseconds. Falls back to 5000ms. */
|
|
1118
|
-
function parseRetryAfter(response: Response): number {
|
|
1119
|
-
return parseOptionalRetryAfter(response) ?? 5000;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
function parseOptionalRetryAfter(response: Response): number | null {
|
|
1123
|
-
const header = response.headers.get('retry-after');
|
|
1124
|
-
if (header) {
|
|
1125
|
-
const seconds = Number(header);
|
|
1126
|
-
if (Number.isFinite(seconds) && seconds > 0) {
|
|
1127
|
-
return seconds * 1000;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
return null;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
/**
|
|
1134
|
-
* For local development URLs, generate loopback variants to handle platform DNS
|
|
1135
|
-
* differences.
|
|
1136
|
-
*/
|
|
1137
|
-
function buildCandidateUrls(url: string): string[] {
|
|
1138
|
-
try {
|
|
1139
|
-
const parsed = new URL(url);
|
|
1140
|
-
const candidates = [url];
|
|
1141
|
-
if (parsed.hostname === 'localhost') {
|
|
1142
|
-
const loopback = new URL(url);
|
|
1143
|
-
loopback.hostname = '127.0.0.1';
|
|
1144
|
-
candidates.push(loopback.toString());
|
|
1145
|
-
}
|
|
1146
|
-
return [...new Set(candidates)];
|
|
1147
|
-
} catch {
|
|
1148
|
-
return [url];
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
async function* decodeSseStream<TEvent extends LiveEventEnvelope>(
|
|
1153
|
-
body: ReadableStream<Uint8Array>,
|
|
1154
|
-
): AsyncGenerator<TEvent> {
|
|
1155
|
-
const reader = body.getReader();
|
|
1156
|
-
const decoder = new TextDecoder();
|
|
1157
|
-
let buffered = '';
|
|
1158
|
-
|
|
1159
|
-
try {
|
|
1160
|
-
while (true) {
|
|
1161
|
-
const { value, done } = await reader.read();
|
|
1162
|
-
if (done) break;
|
|
1163
|
-
buffered += decoder.decode(value, { stream: true });
|
|
1164
|
-
const frames = buffered.split(/\r?\n\r?\n/);
|
|
1165
|
-
buffered = frames.pop() ?? '';
|
|
1166
|
-
for (const frame of frames) {
|
|
1167
|
-
const event = decodeSseFrame<TEvent>(frame);
|
|
1168
|
-
if (event) {
|
|
1169
|
-
yield event;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
buffered += decoder.decode();
|
|
1175
|
-
const event = decodeSseFrame<TEvent>(buffered);
|
|
1176
|
-
if (event) {
|
|
1177
|
-
yield event;
|
|
1178
|
-
}
|
|
1179
|
-
} finally {
|
|
1180
|
-
reader.releaseLock();
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function decodeSseFrame<TEvent extends LiveEventEnvelope>(
|
|
1185
|
-
frame: string,
|
|
1186
|
-
): TEvent | null {
|
|
1187
|
-
const data = frame
|
|
1188
|
-
.split(/\r?\n/)
|
|
1189
|
-
.filter((line) => line.startsWith('data:'))
|
|
1190
|
-
.map((line) => line.slice('data:'.length).trimStart())
|
|
1191
|
-
.join('\n')
|
|
1192
|
-
.trim();
|
|
1193
|
-
if (!data) {
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
const parsed = JSON.parse(data) as Partial<LiveEventEnvelope>;
|
|
1197
|
-
if (
|
|
1198
|
-
!parsed ||
|
|
1199
|
-
typeof parsed !== 'object' ||
|
|
1200
|
-
typeof parsed.cursor !== 'string' ||
|
|
1201
|
-
typeof parsed.streamId !== 'string' ||
|
|
1202
|
-
typeof parsed.scope !== 'string' ||
|
|
1203
|
-
typeof parsed.type !== 'string' ||
|
|
1204
|
-
typeof parsed.at !== 'string'
|
|
1205
|
-
) {
|
|
1206
|
-
return null;
|
|
1207
|
-
}
|
|
1208
|
-
return parsed as TEvent;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
1212
|
-
return new Promise((resolve, reject) => {
|
|
1213
|
-
if (signal?.aborted) {
|
|
1214
|
-
reject(requestCancelledError(signal));
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
const timeoutId = setTimeout(() => {
|
|
1218
|
-
signal?.removeEventListener('abort', abort);
|
|
1219
|
-
resolve();
|
|
1220
|
-
}, ms);
|
|
1221
|
-
const abort = () => {
|
|
1222
|
-
clearTimeout(timeoutId);
|
|
1223
|
-
signal?.removeEventListener('abort', abort);
|
|
1224
|
-
reject(requestCancelledError(signal!));
|
|
1225
|
-
};
|
|
1226
|
-
signal?.addEventListener('abort', abort, { once: true });
|
|
1227
|
-
});
|
|
1228
|
-
}
|
|
4
|
+
import type { ResolvedConfig } from './types';
|
|
1229
5
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
6
|
+
export class HttpClient extends HttpClientCore {
|
|
7
|
+
constructor(config: ResolvedConfig) {
|
|
8
|
+
super(config, readSdkSkillsLocalVersion);
|
|
1233
9
|
}
|
|
1234
|
-
return `${message}\n${COWORK_NETWORK_HINT}`;
|
|
1235
10
|
}
|