@skaile/workspaces 6.5.0 → 6.6.0
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/CHANGELOG.md +8 -0
- package/dist/ai-sdk.js +9 -9
- package/dist/{asset-feeds-4I6Z4OI3.js → asset-feeds-WQ6YQPHV.js} +20 -20
- package/dist/{asset-feeds-4I6Z4OI3.js.map → asset-feeds-WQ6YQPHV.js.map} +1 -1
- package/dist/asset-manager/catalog-deployer.js +7 -7
- package/dist/asset-manager/index.js +12 -12
- package/dist/asset-manager/installer.js +8 -8
- package/dist/asset-manager/renderers.js +7 -7
- package/dist/bridge/harnesses/claude-sdk.js +10 -10
- package/dist/bridge/harnesses/codex.js +11 -11
- package/dist/bridge/harnesses/continue.js +10 -10
- package/dist/bridge/harnesses/echo.js +3 -3
- package/dist/bridge/harnesses/gemini.js +7 -7
- package/dist/bridge/harnesses/goose.js +10 -10
- package/dist/bridge/harnesses/omp.js +5 -5
- package/dist/bridge/harnesses/opencode.js +10 -10
- package/dist/bridge/harnesses/qwen.js +7 -7
- package/dist/bridge/index.js +15 -15
- package/dist/{chunk-J5JWLKLB.js → chunk-2TLPC6TQ.js} +11 -11
- package/dist/{chunk-J5JWLKLB.js.map → chunk-2TLPC6TQ.js.map} +1 -1
- package/dist/{chunk-2IAWVUW3.js → chunk-4BFEPZ6J.js} +4 -4
- package/dist/{chunk-2IAWVUW3.js.map → chunk-4BFEPZ6J.js.map} +1 -1
- package/dist/{chunk-KUADKMNX.js → chunk-4J7JQX4G.js} +4 -4
- package/dist/{chunk-KUADKMNX.js.map → chunk-4J7JQX4G.js.map} +1 -1
- package/dist/{chunk-USVUS4DJ.js → chunk-5ETYBC2X.js} +7 -7
- package/dist/{chunk-USVUS4DJ.js.map → chunk-5ETYBC2X.js.map} +1 -1
- package/dist/{chunk-X44XKBZX.js → chunk-5XHQBCB5.js} +3 -3
- package/dist/{chunk-X44XKBZX.js.map → chunk-5XHQBCB5.js.map} +1 -1
- package/dist/{chunk-OLHHN3NO.js → chunk-65UAC3ZR.js} +3 -3
- package/dist/{chunk-OLHHN3NO.js.map → chunk-65UAC3ZR.js.map} +1 -1
- package/dist/{chunk-7H4GTP7V.js → chunk-6A6HE5LW.js} +3 -3
- package/dist/{chunk-7H4GTP7V.js.map → chunk-6A6HE5LW.js.map} +1 -1
- package/dist/{chunk-UMLCKKIA.js → chunk-6ARPZHOV.js} +6 -6
- package/dist/{chunk-UMLCKKIA.js.map → chunk-6ARPZHOV.js.map} +1 -1
- package/dist/{chunk-K4DHYTUM.js → chunk-6RHWTZPT.js} +3 -3
- package/dist/{chunk-K4DHYTUM.js.map → chunk-6RHWTZPT.js.map} +1 -1
- package/dist/{chunk-IWKKTCXY.js → chunk-6SXSVKQY.js} +7 -5
- package/dist/chunk-6SXSVKQY.js.map +1 -0
- package/dist/{chunk-OBZ5WQ32.js → chunk-7I5PNB2G.js} +3 -3
- package/dist/{chunk-OBZ5WQ32.js.map → chunk-7I5PNB2G.js.map} +1 -1
- package/dist/{chunk-SRTWKZP2.js → chunk-7PVGND4J.js} +8 -8
- package/dist/{chunk-SRTWKZP2.js.map → chunk-7PVGND4J.js.map} +1 -1
- package/dist/{chunk-TROEC2Z2.js → chunk-7SJHSGUV.js} +16 -16
- package/dist/{chunk-TROEC2Z2.js.map → chunk-7SJHSGUV.js.map} +1 -1
- package/dist/{chunk-TNQLDEM6.js → chunk-AQNW2FET.js} +230 -35
- package/dist/chunk-AQNW2FET.js.map +1 -0
- package/dist/{chunk-6EKQENAP.js → chunk-B6N653ZG.js} +6 -6
- package/dist/{chunk-6EKQENAP.js.map → chunk-B6N653ZG.js.map} +1 -1
- package/dist/{chunk-NSIOBKDX.js → chunk-BSMW3PX2.js} +2 -2
- package/dist/{chunk-NSIOBKDX.js.map → chunk-BSMW3PX2.js.map} +1 -1
- package/dist/{chunk-HTGTNN2B.js → chunk-CMXDFTA2.js} +3 -3
- package/dist/{chunk-HTGTNN2B.js.map → chunk-CMXDFTA2.js.map} +1 -1
- package/dist/{chunk-2HS357RA.js → chunk-DI6J2PJB.js} +7 -7
- package/dist/{chunk-2HS357RA.js.map → chunk-DI6J2PJB.js.map} +1 -1
- package/dist/{chunk-T7HGEMNB.js → chunk-DS6UUCFG.js} +3 -3
- package/dist/{chunk-T7HGEMNB.js.map → chunk-DS6UUCFG.js.map} +1 -1
- package/dist/{chunk-23SG4JRV.js → chunk-E2EC6SAX.js} +3 -3
- package/dist/{chunk-23SG4JRV.js.map → chunk-E2EC6SAX.js.map} +1 -1
- package/dist/{chunk-ETF6Q3R3.js → chunk-EPXZQIC6.js} +3 -3
- package/dist/{chunk-ETF6Q3R3.js.map → chunk-EPXZQIC6.js.map} +1 -1
- package/dist/{chunk-SZTHSPRC.js → chunk-F4CZQD3T.js} +3 -3
- package/dist/{chunk-SZTHSPRC.js.map → chunk-F4CZQD3T.js.map} +1 -1
- package/dist/{chunk-AVFRFGM2.js → chunk-GNUA7XT2.js} +3 -2
- package/dist/chunk-GNUA7XT2.js.map +1 -0
- package/dist/{chunk-6Y3SC7O4.js → chunk-GTAHU4N4.js} +4 -4
- package/dist/{chunk-6Y3SC7O4.js.map → chunk-GTAHU4N4.js.map} +1 -1
- package/dist/{chunk-3KSLUVKB.js → chunk-GTGWRKMJ.js} +6 -6
- package/dist/{chunk-3KSLUVKB.js.map → chunk-GTGWRKMJ.js.map} +1 -1
- package/dist/{chunk-YRC2EFOU.js → chunk-HGZN6JLP.js} +4 -4
- package/dist/{chunk-YRC2EFOU.js.map → chunk-HGZN6JLP.js.map} +1 -1
- package/dist/{chunk-KYNCY3F3.js → chunk-HQAPUPBE.js} +3 -3
- package/dist/{chunk-KYNCY3F3.js.map → chunk-HQAPUPBE.js.map} +1 -1
- package/dist/{chunk-HPA3RP5E.js → chunk-I5IF3JCG.js} +3 -3
- package/dist/{chunk-HPA3RP5E.js.map → chunk-I5IF3JCG.js.map} +1 -1
- package/dist/{chunk-7EMS2GZO.js → chunk-J262X4JU.js} +12 -12
- package/dist/{chunk-7EMS2GZO.js.map → chunk-J262X4JU.js.map} +1 -1
- package/dist/{chunk-22ZX33H6.js → chunk-J4WKNJHO.js} +4 -4
- package/dist/{chunk-22ZX33H6.js.map → chunk-J4WKNJHO.js.map} +1 -1
- package/dist/{chunk-G4NCSYWR.js → chunk-JIQUF3X2.js} +4 -4
- package/dist/{chunk-G4NCSYWR.js.map → chunk-JIQUF3X2.js.map} +1 -1
- package/dist/{chunk-IS3NIZBX.js → chunk-JRVQV7WC.js} +11 -11
- package/dist/{chunk-IS3NIZBX.js.map → chunk-JRVQV7WC.js.map} +1 -1
- package/dist/{chunk-UZET67SO.js → chunk-LBJ4M5J2.js} +4 -4
- package/dist/{chunk-UZET67SO.js.map → chunk-LBJ4M5J2.js.map} +1 -1
- package/dist/{chunk-TGUMN5G7.js → chunk-MMOWIRHP.js} +8 -8
- package/dist/{chunk-TGUMN5G7.js.map → chunk-MMOWIRHP.js.map} +1 -1
- package/dist/{chunk-EO7KGX6P.js → chunk-MXI3XWV6.js} +4 -4
- package/dist/{chunk-EO7KGX6P.js.map → chunk-MXI3XWV6.js.map} +1 -1
- package/dist/{chunk-LE6QS4PG.js → chunk-N2PBAXAA.js} +3 -3
- package/dist/{chunk-LE6QS4PG.js.map → chunk-N2PBAXAA.js.map} +1 -1
- package/dist/{chunk-YBEJCQE5.js → chunk-O7QZI6MQ.js} +3 -3
- package/dist/{chunk-YBEJCQE5.js.map → chunk-O7QZI6MQ.js.map} +1 -1
- package/dist/{chunk-ZUU2PLA3.js → chunk-OY22B7E2.js} +3 -3
- package/dist/{chunk-ZUU2PLA3.js.map → chunk-OY22B7E2.js.map} +1 -1
- package/dist/{chunk-W5KCAWGN.js → chunk-PTJTOPE5.js} +3 -3
- package/dist/{chunk-W5KCAWGN.js.map → chunk-PTJTOPE5.js.map} +1 -1
- package/dist/{chunk-WFLQMAR5.js → chunk-RAU5QPI6.js} +5 -5
- package/dist/{chunk-WFLQMAR5.js.map → chunk-RAU5QPI6.js.map} +1 -1
- package/dist/{chunk-AFTZNR2W.js → chunk-RHCEX2EV.js} +3 -3
- package/dist/{chunk-AFTZNR2W.js.map → chunk-RHCEX2EV.js.map} +1 -1
- package/dist/{chunk-Z2X7UZEZ.js → chunk-SCRYXAHH.js} +6 -6
- package/dist/{chunk-Z2X7UZEZ.js.map → chunk-SCRYXAHH.js.map} +1 -1
- package/dist/{chunk-DKXNKFKP.js → chunk-SGI5MRKP.js} +75 -3
- package/dist/chunk-SGI5MRKP.js.map +1 -0
- package/dist/{chunk-KA2GLAPS.js → chunk-SHTXMERQ.js} +7 -7
- package/dist/{chunk-KA2GLAPS.js.map → chunk-SHTXMERQ.js.map} +1 -1
- package/dist/{chunk-SN663FCA.js → chunk-SZKCE7EC.js} +5 -5
- package/dist/{chunk-SN663FCA.js.map → chunk-SZKCE7EC.js.map} +1 -1
- package/dist/{chunk-S7DVT2BV.js → chunk-TCH5UDKT.js} +4 -4
- package/dist/{chunk-S7DVT2BV.js.map → chunk-TCH5UDKT.js.map} +1 -1
- package/dist/{chunk-THX5EE3K.js → chunk-TUFNY6OD.js} +3 -3
- package/dist/{chunk-THX5EE3K.js.map → chunk-TUFNY6OD.js.map} +1 -1
- package/dist/{chunk-EN5REMWV.js → chunk-U3UDOX6I.js} +4 -4
- package/dist/{chunk-EN5REMWV.js.map → chunk-U3UDOX6I.js.map} +1 -1
- package/dist/{chunk-3DA74PLW.js → chunk-USSP43U3.js} +3 -3
- package/dist/{chunk-3DA74PLW.js.map → chunk-USSP43U3.js.map} +1 -1
- package/dist/{chunk-U6KRTCNA.js → chunk-VJRL6ZK6.js} +3 -3
- package/dist/{chunk-U6KRTCNA.js.map → chunk-VJRL6ZK6.js.map} +1 -1
- package/dist/{chunk-B4BLJUYN.js → chunk-VXYZ5M7Z.js} +24 -18
- package/dist/{chunk-B4BLJUYN.js.map → chunk-VXYZ5M7Z.js.map} +1 -1
- package/dist/{chunk-E6UWI3TP.js → chunk-WW5NAU6N.js} +3 -3
- package/dist/{chunk-E6UWI3TP.js.map → chunk-WW5NAU6N.js.map} +1 -1
- package/dist/{chunk-J6YPKMMY.js → chunk-XD3RYAMR.js} +6 -6
- package/dist/{chunk-J6YPKMMY.js.map → chunk-XD3RYAMR.js.map} +1 -1
- package/dist/{chunk-DNXPXJ3X.js → chunk-XYCYGN3Q.js} +14 -14
- package/dist/{chunk-DNXPXJ3X.js.map → chunk-XYCYGN3Q.js.map} +1 -1
- package/dist/{chunk-B436SG5P.js → chunk-YN6N3RBM.js} +7 -7
- package/dist/{chunk-B436SG5P.js.map → chunk-YN6N3RBM.js.map} +1 -1
- package/dist/{chunk-TDPQOR3C.js → chunk-Z4FERR4U.js} +2 -2
- package/dist/{chunk-TDPQOR3C.js.map → chunk-Z4FERR4U.js.map} +1 -1
- package/dist/{claude-code-JY6CYEG7.js → claude-code-LZH7Y4Y5.js} +5 -5
- package/dist/{claude-code-JY6CYEG7.js.map → claude-code-LZH7Y4Y5.js.map} +1 -1
- package/dist/cli/index.js +91 -91
- package/dist/connectors/config.js +4 -4
- package/dist/connectors/index.js +18 -18
- package/dist/connectors/src/connector-manager.d.ts.map +1 -1
- package/dist/connectors/src/mount-ledger.d.ts.map +1 -1
- package/dist/{connectors-VLFOQNS6.js → connectors-HU5MHSUJ.js} +21 -21
- package/dist/{connectors-VLFOQNS6.js.map → connectors-HU5MHSUJ.js.map} +1 -1
- package/dist/core/index.js +6 -6
- package/dist/core/logging.js +1 -1
- package/dist/core/manifest.js +1 -1
- package/dist/core/publish-manifest.js +2 -2
- package/dist/core/runtime-assets.js +4 -4
- package/dist/core/src/logging/index.d.ts +1 -0
- package/dist/core/src/logging/index.d.ts.map +1 -1
- package/dist/core/src/logging/log-safe-text.d.ts +20 -0
- package/dist/core/src/logging/log-safe-text.d.ts.map +1 -0
- package/dist/core/src/manifest.d.ts.map +1 -1
- package/dist/core/src/workspace-config.d.ts +6 -0
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/workspace-config.js +3 -3
- package/dist/deploy/index.js +6 -6
- package/dist/discovery/index.js +3 -3
- package/dist/{ensure-sources-6O54DUQB.js → ensure-sources-CAMWJTMR.js} +24 -24
- package/dist/{ensure-sources-6O54DUQB.js.map → ensure-sources-CAMWJTMR.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +18 -18
- package/dist/factory-assets/connectors/deploy.js +18 -18
- package/dist/factory-assets/connectors/devserver.js +18 -18
- package/dist/factory-assets/connectors/flow/adapter.js +18 -18
- package/dist/factory-assets/connectors/flow/engine.js +7 -7
- package/dist/factory-assets/connectors/flow/run-flow.js +20 -20
- package/dist/factory-assets/connectors/flow.js +18 -18
- package/dist/factory-assets/connectors/git.js +18 -18
- package/dist/factory-assets/connectors/gmail.js +18 -18
- package/dist/factory-assets/connectors/googledrive.js +18 -18
- package/dist/factory-assets/connectors/local.js +18 -18
- package/dist/factory-assets/connectors/mattermost.js +18 -18
- package/dist/factory-assets/connectors/memory.js +18 -18
- package/dist/factory-assets/connectors/minio.js +18 -18
- package/dist/factory-assets/connectors/postgres.js +18 -18
- package/dist/factory-assets/connectors/s3.js +18 -18
- package/dist/factory-assets/connectors/sharepoint.js +18 -18
- package/dist/factory-assets/connectors/sqlite.js +18 -18
- package/dist/factory-assets/connectors/static-server.js +18 -18
- package/dist/factory-assets/connectors/tunnel.js +18 -18
- package/dist/factory-assets/connectors/webdav.js +18 -18
- package/dist/factory-assets/connectors/xstate-store.js +18 -18
- package/dist/factory-assets/connectors/xstate.js +18 -18
- package/dist/{flows-CIUPK7SB.js → flows-GVOVQGQW.js} +11 -11
- package/dist/{flows-CIUPK7SB.js.map → flows-GVOVQGQW.js.map} +1 -1
- package/dist/{helpers-YHDQHQEQ.js → helpers-WMCCODAV.js} +24 -24
- package/dist/{helpers-YHDQHQEQ.js.map → helpers-WMCCODAV.js.map} +1 -1
- package/dist/library/index.js +9 -9
- package/dist/library/install.js +3 -3
- package/dist/{library-publish-MUGUST43.js → library-publish-UQQMHTND.js} +49 -49
- package/dist/{library-publish-MUGUST43.js.map → library-publish-UQQMHTND.js.map} +1 -1
- package/dist/model-endpoint/index.js +9 -9
- package/dist/{open-library-BVQCB37E.js → open-library-OJ4HT2X4.js} +27 -27
- package/dist/{open-library-BVQCB37E.js.map → open-library-OJ4HT2X4.js.map} +1 -1
- package/dist/{open-registry-JCSIAKEC.js → open-registry-LWGIOJDK.js} +13 -13
- package/dist/{open-registry-JCSIAKEC.js.map → open-registry-LWGIOJDK.js.map} +1 -1
- package/dist/{paths-XLKCAFRZ.js → paths-FR2752HV.js} +9 -9
- package/dist/{paths-XLKCAFRZ.js.map → paths-FR2752HV.js.map} +1 -1
- package/dist/{plugin-store-NGRUTCJI.js → plugin-store-IALJUBN2.js} +9 -9
- package/dist/{plugin-store-NGRUTCJI.js.map → plugin-store-IALJUBN2.js.map} +1 -1
- package/dist/{project-BGYMLAS6.js → project-UQFCYHYS.js} +45 -45
- package/dist/{project-BGYMLAS6.js.map → project-UQFCYHYS.js.map} +1 -1
- package/dist/resolver/index.js +7 -7
- package/dist/runner/index.js +34 -34
- package/dist/runner/src/external-mcp.d.ts +22 -0
- package/dist/runner/src/external-mcp.d.ts.map +1 -1
- package/dist/runner/src/mcp-tools-snapshot.d.ts +13 -0
- package/dist/runner/src/mcp-tools-snapshot.d.ts.map +1 -0
- package/dist/runner/src/resources.d.ts +14 -0
- package/dist/runner/src/resources.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +12 -12
- package/dist/sdk/bridge.js +15 -15
- package/dist/sdk/core.js +6 -6
- package/dist/sdk/flow.js +7 -7
- package/dist/sdk/index.js +34 -34
- package/dist/sdk/resolver.js +7 -7
- package/dist/sdk/runner.js +34 -34
- package/dist/sdk/transport/ws/server.js +7 -7
- package/dist/sdk/transport/ws.js +7 -7
- package/dist/sdk/transport.js +7 -7
- package/dist/{serve-preflight-GQY5DZX6.js → serve-preflight-TYEKHC32.js} +27 -27
- package/dist/{serve-preflight-GQY5DZX6.js.map → serve-preflight-TYEKHC32.js.map} +1 -1
- package/dist/{setup-2NWSKSCO.js → setup-4JV2IMCB.js} +25 -25
- package/dist/{setup-2NWSKSCO.js.map → setup-4JV2IMCB.js.map} +1 -1
- package/dist/{skills-PXZOL2FZ.js → skills-6QTKFMLW.js} +12 -12
- package/dist/{skills-PXZOL2FZ.js.map → skills-6QTKFMLW.js.map} +1 -1
- package/dist/{source-XA3L4H6I.js → source-XYMMJVKR.js} +44 -44
- package/dist/{source-XA3L4H6I.js.map → source-XYMMJVKR.js.map} +1 -1
- package/dist/{source-manifest-publish-QEGMTS33.js → source-manifest-publish-SZFGGKB4.js} +46 -46
- package/dist/{source-manifest-publish-QEGMTS33.js.map → source-manifest-publish-SZFGGKB4.js.map} +1 -1
- package/dist/transport/index.js +7 -7
- package/dist/transport/ws/server.js +7 -7
- package/dist/transport/ws.js +7 -7
- package/dist/tui/index.js +34 -34
- package/dist/workspace-plugin/adapters/mcp.js +25 -25
- package/dist/workspace-plugin/adapters/omp.js +14 -14
- package/dist/workspace-plugin/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-AVFRFGM2.js.map +0 -1
- package/dist/chunk-DKXNKFKP.js.map +0 -1
- package/dist/chunk-IWKKTCXY.js.map +0 -1
- package/dist/chunk-TNQLDEM6.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { unresolvedProviderCredentials, getProviderPreset, providerCredentialEnvName } from './chunk-
|
|
1
|
+
import { unresolvedProviderCredentials, getProviderPreset, providerCredentialEnvName } from './chunk-BSMW3PX2.js';
|
|
2
2
|
import { globalSkaileDir } from './chunk-L5VSIPQD.js';
|
|
3
3
|
import { registryEntries, parseModelRef, isEntryEnabled, resolveEntryBaseUrl } from './chunk-RXGPFAMN.js';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
@@ -146,5 +146,5 @@ function codexLoginFallbackNotice(session, env) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
export { buildCodexConfigDocument, codexLoginFallbackNotice, codexLoginFile, codexProviderTableId, codexSessionHome, usesCodexOwnAuth, writeCodexConfigToml };
|
|
149
|
-
//# sourceMappingURL=chunk-
|
|
150
|
-
//# sourceMappingURL=chunk-
|
|
149
|
+
//# sourceMappingURL=chunk-65UAC3ZR.js.map
|
|
150
|
+
//# sourceMappingURL=chunk-65UAC3ZR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../bridge/src/codex/config-toml.ts","../bridge/src/codex/login-fallback.ts"],"names":["stringifyToml","join"],"mappings":"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","file":"chunk-OLHHN3NO.js","sourcesContent":["/**\n * Codex's `config.toml` adapter — the AI provider registry materialized into\n * the only provider surface the Codex CLI reads (plan §2.3, codex rows).\n *\n * Codex is a **file-shaped** harness: it takes its endpoints from\n * `$CODEX_HOME/config.toml`, never from per-request options. Both codex modes\n * therefore get a session-private `CODEX_HOME` holding a rendered document:\n *\n * - every addressable registry entry becomes `[model_providers.<id>]` with\n * `base_url`, `env_key`, `wire_api`, `query_params` and `http_headers`\n * mapping 1:1 onto the entry's `base_url`, `credential`, `surface`,\n * `query_params` and `headers`;\n * - `model_provider` and `model` at the top level name the selected entry.\n *\n * The rendered file never contains a credential **value** — `env_key` holds\n * the *name* of the per-entry variable (`SKAILE_AIP_<ID>_KEY`, minted by\n * {@link providerCredentialEnvName}), and the value rides in the child\n * environment. An `oauth` entry has no env carrier at all: it gets\n * `requires_openai_auth`, which routes codex to its own auth manager.\n *\n * **Surface coverage.** Codex names the wire protocol `wire_api` and, as of\n * codex-cli 0.154, accepts exactly one value: `responses`. `chat` was removed\n * (\"`wire_api = \\\"chat\\\"` is no longer supported\", openai/codex#7782), and an\n * unknown value fails the *whole* document at load time — one unaddressable\n * entry would take the rest of the registry down with it. Entries whose\n * surface codex cannot speak (`openai-completions`, `anthropic-messages`,\n * `bedrock-converse-stream`, `google-*`) are therefore left out and reported\n * in {@link CodexConfigBuild.skipped} for the harness to log.\n *\n * @docLink packages/bridge/harnesses#codex-harness\n */\n\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { globalSkaileDir } from \"@skaile/workspaces/core\";\nimport {\n type AIProviderEntry,\n type AIProviderLlmEntry,\n type AIProviderRegistry,\n type AIProviderSurface,\n isEntryEnabled,\n type MergedAIProviderRegistry,\n parseModelRef,\n registryEntries,\n type ResolvedModelRef,\n resolveEntryBaseUrl,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { stringify as stringifyToml } from \"smol-toml\";\nimport { getProviderPreset } from \"../provider-catalog.js\";\nimport { providerCredentialEnvName } from \"../provider-materialize/credential-env.js\";\n\n/**\n * `surface` → codex `wire_api`. Only the responses-flavoured surfaces have a\n * codex spelling; see the module docstring for why `openai-completions` is\n * absent rather than mapped to the removed `chat` value.\n */\nconst CODEX_WIRE_API: Partial<Record<AIProviderSurface, string>> = {\n \"openai-codex-responses\": \"responses\",\n \"openai-responses\": \"responses\",\n \"azure-openai-responses\": \"responses\",\n};\n\n/**\n * Provider ids codex reserves for its own built-ins. A `[model_providers.<id>]`\n * table under one of them fails the whole document (\"model_providers contains\n * reserved built-in provider IDs\"), so such an entry is rendered under\n * {@link codexProviderTableId}.\n */\nconst CODEX_RESERVED_PROVIDER_IDS: ReadonlySet<string> = new Set([\"openai\", \"ollama\", \"lmstudio\"]);\n\n/**\n * The `[model_providers.<id>]` table name for a registry entry: the entry id,\n * or `skaile-<id>` when codex reserves that id (the `openai` preset).\n */\nexport function codexProviderTableId(id: string): string {\n return CODEX_RESERVED_PROVIDER_IDS.has(id) ? `skaile-${id}` : id;\n}\n\n/** Credential kinds whose value the adapter hands over in an env var. */\nconst ENV_DELIVERED_CREDENTIALS: Record<string, true> = {\n api_key_header: true,\n bearer: true,\n command: true,\n};\n\n/** One `[model_providers.<id>]` table, in codex's own vocabulary. */\nexport interface CodexModelProviderTable {\n /** Display name. Codex rejects a provider table without one. */\n name: string;\n base_url?: string;\n /** Name of the variable carrying the credential — never the credential. */\n env_key?: string;\n wire_api?: string;\n /** Routes codex to its own auth manager (ChatGPT subscription / stored key). */\n requires_openai_auth?: boolean;\n query_params?: Record<string, string>;\n http_headers?: Record<string, string>;\n}\n\n/** The subset of codex's `config.toml` this adapter owns. */\nexport interface CodexConfigDocument {\n /** Table id the session addresses (see {@link codexProviderTableId}); absent when nothing rendered. */\n model_provider?: string;\n /** Provider-side model id. */\n model?: string;\n model_providers: Record<string, CodexModelProviderTable>;\n}\n\n/** An entry codex cannot address, and why. */\nexport interface CodexSkippedEntry {\n id: string;\n reason: string;\n}\n\n/** Result of {@link buildCodexConfigDocument}. */\nexport interface CodexConfigBuild {\n document: CodexConfigDocument;\n /** Selected registry entry id (not the table id), when it rendered. */\n providerId?: string;\n /** Provider-side model id of the selection, when the selector named one. */\n model?: string;\n /**\n * Entries left out of the document. Non-fatal: codex keeps its built-in\n * providers, so a session whose selection was skipped behaves exactly as it\n * did before the registry existed.\n */\n skipped: CodexSkippedEntry[];\n /** Notes from `modelRef` resolution (dangling role, disabled provider, …). */\n warnings: string[];\n /**\n * Set when the selector named an entry codex cannot address: the session\n * must stop with `reason`, not fall back to codex's built-in providers.\n */\n unaddressable?: { id: string; reason: string };\n}\n\n/**\n * Whether the session's selector named the entry `selected` resolved to, rather\n * than reaching it by registry order alone.\n */\nfunction selectionNamesEntry(\n input: CodexConfigInput,\n selected: ResolvedModelRef,\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n): boolean {\n if (selected.role && selected.modelId !== undefined) return true;\n const selector = input.modelRef?.trim() || input.model?.trim() || \"\";\n if (selector.startsWith(`${selected.providerId}/`)) return true;\n const entry = registryEntries(registry).find(([id]) => id === selected.providerId)?.[1] as\n | AIProviderLlmEntry\n | undefined;\n const modelId = selected.modelId;\n return (\n modelId !== undefined &&\n (entry?.model_map?.[modelId] !== undefined || entry?.capabilities?.[modelId] !== undefined)\n );\n}\n\n/** Inputs {@link buildCodexConfigDocument} reads off an `AgentConfig`. */\nexport interface CodexConfigInput {\n registry?: AIProviderRegistry | MergedAIProviderRegistry;\n /** `\"<providerId>/<modelId>\"`, a bare model id, or `\"@role\"`. */\n modelRef?: string;\n /** Legacy model field, used as the selector when `modelRef` is absent. */\n model?: string;\n /** Roles table (`skaile.yaml`), consulted for an `@role` selector. */\n roles?: Record<string, string>;\n}\n\n/**\n * Render one entry as a codex provider table, or explain why codex cannot\n * address it.\n */\nexport function renderCodexProviderTable(\n id: string,\n entry: AIProviderEntry,\n): { table: CodexModelProviderTable } | { skipped: string } {\n if (!isEntryEnabled(entry)) return { skipped: \"entry is disabled\" };\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\")\n return { skipped: `kind '${entry.kind}' is not a model endpoint` };\n const llm = entry as AIProviderLlmEntry;\n // `surface` and `base_url` may live on the catalog preset the entry names —\n // the catalog layer fills them during a merge, but an entry handed straight\n // to a harness has not necessarily been through one.\n const preset = entry.preset ? getProviderPreset(entry.preset) : undefined;\n const surface = llm.surface ?? preset?.surface;\n const wireApi = surface ? CODEX_WIRE_API[surface] : undefined;\n if (!wireApi)\n return {\n skipped: surface\n ? `codex has no wire_api for surface '${surface}'`\n : \"entry declares no surface\",\n };\n const credentialKind = entry.credential?.kind;\n const table: CodexModelProviderTable = { name: entry.label ?? id };\n const baseUrl = resolveEntryBaseUrl(entry) ?? preset?.base_url;\n if (baseUrl) table.base_url = baseUrl;\n if (credentialKind && ENV_DELIVERED_CREDENTIALS[credentialKind])\n table.env_key = providerCredentialEnvName(id);\n table.wire_api = wireApi;\n if (credentialKind === \"oauth\") table.requires_openai_auth = true;\n if (llm.query_params && Object.keys(llm.query_params).length > 0)\n table.query_params = { ...llm.query_params };\n if (entry.headers && Object.keys(entry.headers).length > 0)\n table.http_headers = { ...entry.headers };\n return { table };\n}\n\n/**\n * Build the document for a session: every addressable entry, plus the\n * selection when it resolved to one of them.\n */\nexport function buildCodexConfigDocument(input: CodexConfigInput): CodexConfigBuild {\n const document: CodexConfigDocument = { model_providers: {} };\n const skipped: CodexSkippedEntry[] = [];\n const entries = input.registry ? registryEntries(input.registry) : [];\n for (const [id, entry] of entries) {\n const rendered = renderCodexProviderTable(id, entry);\n if (\"skipped\" in rendered) skipped.push({ id, reason: rendered.skipped });\n else document.model_providers[codexProviderTableId(id)] = rendered.table;\n }\n\n if (!input.registry) return { document, skipped, warnings: [] };\n // An empty selector resolves the same way a bare one does — first entry in\n // registry order. A session that configured an endpoint and named no model\n // still means that endpoint, not codex's built-in default.\n const selected = parseModelRef(\n input.modelRef?.trim() || input.model?.trim() || \"\",\n input.registry,\n input.roles,\n );\n if (!selected) return { document, skipped, warnings: [] };\n const warnings = selected.warnings;\n const tableId = codexProviderTableId(selected.providerId);\n if (!document.model_providers[tableId]) {\n // A selector that names the entry (`ollama/llama3`, a role or a model its\n // `model_map` lists) and cannot be rendered must not fall through to\n // codex's built-in OpenAI: the prompt would go to a provider the user did\n // not pick. A bare model id that matched no entry only landed on the first\n // entry by registry order, so codex's own providers stay the right answer.\n const reason = skipped.find((s) => s.id === selected.providerId)?.reason;\n if (reason && selectionNamesEntry(input, selected, input.registry)) {\n return {\n document,\n skipped,\n warnings,\n unaddressable: { id: selected.providerId, reason },\n };\n }\n return { document, skipped, warnings };\n }\n document.model_provider = tableId;\n if (selected.providerModelId) document.model = selected.providerModelId;\n return {\n document,\n providerId: selected.providerId,\n ...(selected.providerModelId ? { model: selected.providerModelId } : {}),\n skipped,\n warnings,\n };\n}\n\n/** Serialize a document to TOML. Deterministic: registry order throughout. */\nexport function renderCodexConfigToml(document: CodexConfigDocument): string {\n const ordered: Record<string, unknown> = {};\n if (document.model_provider) ordered.model_provider = document.model_provider;\n if (document.model) ordered.model = document.model;\n ordered.model_providers = document.model_providers;\n return `${stringifyToml(ordered)}\\n`;\n}\n\n/**\n * The session-private codex home.\n *\n * Deliberately outside `cwd`: codex's `workspace-write` sandbox makes the\n * working tree writable by the model, and a config home inside it would let a\n * turn rewrite its own `base_url`. `$SKAILE_HOME` (honoured by\n * {@link globalSkaileDir}) relocates the whole tree, which is also the test\n * seam.\n */\nexport function codexSessionHome(config: { cwd: string; sessionId?: string }): string {\n const key =\n config.sessionId && /^[A-Za-z0-9._-]{1,128}$/.test(config.sessionId)\n ? config.sessionId\n : createHash(\"sha256\")\n .update(config.sessionId ?? config.cwd)\n .digest(\"hex\")\n .slice(0, 32);\n return join(globalSkaileDir(), \"codex\", key);\n}\n\n/**\n * Write `<home>/config.toml`, creating the home always. Atomic: a\n * partially written document would fail codex's config load for the whole\n * session.\n *\n * A document with no provider tables says nothing codex does not already\n * know, so nothing is written and a file left by an earlier render is\n * removed — a session that no longer carries a registry must not keep\n * answering to the one it used to have.\n *\n * @returns the absolute path written, or `undefined` when there was nothing\n * to render.\n */\nexport function writeCodexConfigToml(\n home: string,\n document: CodexConfigDocument,\n): string | undefined {\n const path = join(home, \"config.toml\");\n // codex exits when CODEX_HOME names a directory that does not exist, so the\n // home exists before the early return too.\n mkdirSync(home, { recursive: true, mode: 0o700 });\n if (Object.keys(document.model_providers).length === 0) {\n rmSync(path, { force: true });\n return undefined;\n }\n const temporary = join(home, \"config.toml.new\");\n rmSync(temporary, { force: true });\n writeFileSync(temporary, renderCodexConfigToml(document), { mode: 0o600, flag: \"wx\" });\n renameSync(temporary, path);\n return path;\n}\n","/**\n * The codex CLI's own credentials, and when they stand in for the registry's `openai` entry.\n *\n * An `openai` registry entry whose key variable is not exported cannot be rendered as a working\n * `[model_providers.*]` table. The omp harness then answers with its own login; codex does the\n * same (p1871): it drops the entry and runs on codex's built-in `openai` provider, which uses\n * the ChatGPT login (`~/.codex/auth.json`) or an exported `OPENAI_API_KEY`. Every other entry\n * whose key is missing still refuses, naming the variable to export.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type {\n AIProviderRegistry,\n MergedAIProviderRegistry,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { unresolvedProviderCredentials } from \"../provider-materialize/credential-env.js\";\nimport {\n buildCodexConfigDocument,\n type CodexConfigBuild,\n codexProviderTableId,\n} from \"./config-toml.js\";\n\n/**\n * The codex CLI's ChatGPT login file (`$CODEX_HOME/auth.json`, default `~/.codex/auth.json`) of\n * the user's environment, or `undefined` when there is none. `env` is the parent environment,\n * not the session's: the session's `CODEX_HOME` is the private one, which has no login.\n */\nexport function codexLoginFile(env: Record<string, string | undefined>): string | undefined {\n const home = env.CODEX_HOME?.trim() || join(env.HOME || homedir(), \".codex\");\n const file = join(home, \"auth.json\");\n return existsSync(file) ? resolve(file) : undefined;\n}\n\n/**\n * Whether the session runs on codex's own auth instead of the selected `openai` registry entry:\n * the entry is selected, its key variable (`env_key` of the rendered table) is not set in `env`,\n * and codex has a login file or an exported key to use instead.\n *\n * @param build - The rendered codex config of the session.\n * @param env - The environment the child gets, credential variables included.\n * @param loginFile - The codex login file, when there is one.\n */\nexport function usesCodexOwnAuth(\n build: CodexConfigBuild,\n env: Record<string, string | undefined>,\n loginFile: string | undefined,\n): boolean {\n if (build.providerId !== \"openai\") return false;\n const table = build.document.model_providers[codexProviderTableId(\"openai\")];\n if (!table?.env_key || table.requires_openai_auth || env[table.env_key]) return false;\n return Boolean(env.OPENAI_API_KEY || env.CODEX_API_KEY || loginFile);\n}\n\n/**\n * The line `run` and `repl` print when a codex session falls back to codex's own auth because\n * the `openai` entry has no key (the `omp` line of the same kind, p1871), or `undefined`.\n *\n * @param session - The session's registry and model selector.\n * @param env - Process environment plus the session's credential variables.\n */\nexport function codexLoginFallbackNotice(\n session: { registry?: AIProviderRegistry | MergedAIProviderRegistry; modelRef?: string },\n env: Record<string, string | undefined>,\n): string | undefined {\n if (!session.registry) return undefined;\n const build = buildCodexConfigDocument({\n registry: session.registry,\n ...(session.modelRef ? { modelRef: session.modelRef } : {}),\n });\n if (!usesCodexOwnAuth(build, env, codexLoginFile(env))) return undefined;\n const unresolved = unresolvedProviderCredentials(session.registry, {\n env,\n providerId: \"openai\",\n })[0];\n return `provider 'openai' is not used: ${unresolved?.reason ?? \"its credential is not set\"}; codex uses its own login or OPENAI_API_KEY instead (check with \\`skaile providers test openai\\`)`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../bridge/src/codex/config-toml.ts","../bridge/src/codex/login-fallback.ts"],"names":["stringifyToml","join"],"mappings":"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","file":"chunk-65UAC3ZR.js","sourcesContent":["/**\n * Codex's `config.toml` adapter — the AI provider registry materialized into\n * the only provider surface the Codex CLI reads (plan §2.3, codex rows).\n *\n * Codex is a **file-shaped** harness: it takes its endpoints from\n * `$CODEX_HOME/config.toml`, never from per-request options. Both codex modes\n * therefore get a session-private `CODEX_HOME` holding a rendered document:\n *\n * - every addressable registry entry becomes `[model_providers.<id>]` with\n * `base_url`, `env_key`, `wire_api`, `query_params` and `http_headers`\n * mapping 1:1 onto the entry's `base_url`, `credential`, `surface`,\n * `query_params` and `headers`;\n * - `model_provider` and `model` at the top level name the selected entry.\n *\n * The rendered file never contains a credential **value** — `env_key` holds\n * the *name* of the per-entry variable (`SKAILE_AIP_<ID>_KEY`, minted by\n * {@link providerCredentialEnvName}), and the value rides in the child\n * environment. An `oauth` entry has no env carrier at all: it gets\n * `requires_openai_auth`, which routes codex to its own auth manager.\n *\n * **Surface coverage.** Codex names the wire protocol `wire_api` and, as of\n * codex-cli 0.154, accepts exactly one value: `responses`. `chat` was removed\n * (\"`wire_api = \\\"chat\\\"` is no longer supported\", openai/codex#7782), and an\n * unknown value fails the *whole* document at load time — one unaddressable\n * entry would take the rest of the registry down with it. Entries whose\n * surface codex cannot speak (`openai-completions`, `anthropic-messages`,\n * `bedrock-converse-stream`, `google-*`) are therefore left out and reported\n * in {@link CodexConfigBuild.skipped} for the harness to log.\n *\n * @docLink packages/bridge/harnesses#codex-harness\n */\n\nimport { createHash } from \"node:crypto\";\nimport { mkdirSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { globalSkaileDir } from \"@skaile/workspaces/core\";\nimport {\n type AIProviderEntry,\n type AIProviderLlmEntry,\n type AIProviderRegistry,\n type AIProviderSurface,\n isEntryEnabled,\n type MergedAIProviderRegistry,\n parseModelRef,\n registryEntries,\n type ResolvedModelRef,\n resolveEntryBaseUrl,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { stringify as stringifyToml } from \"smol-toml\";\nimport { getProviderPreset } from \"../provider-catalog.js\";\nimport { providerCredentialEnvName } from \"../provider-materialize/credential-env.js\";\n\n/**\n * `surface` → codex `wire_api`. Only the responses-flavoured surfaces have a\n * codex spelling; see the module docstring for why `openai-completions` is\n * absent rather than mapped to the removed `chat` value.\n */\nconst CODEX_WIRE_API: Partial<Record<AIProviderSurface, string>> = {\n \"openai-codex-responses\": \"responses\",\n \"openai-responses\": \"responses\",\n \"azure-openai-responses\": \"responses\",\n};\n\n/**\n * Provider ids codex reserves for its own built-ins. A `[model_providers.<id>]`\n * table under one of them fails the whole document (\"model_providers contains\n * reserved built-in provider IDs\"), so such an entry is rendered under\n * {@link codexProviderTableId}.\n */\nconst CODEX_RESERVED_PROVIDER_IDS: ReadonlySet<string> = new Set([\"openai\", \"ollama\", \"lmstudio\"]);\n\n/**\n * The `[model_providers.<id>]` table name for a registry entry: the entry id,\n * or `skaile-<id>` when codex reserves that id (the `openai` preset).\n */\nexport function codexProviderTableId(id: string): string {\n return CODEX_RESERVED_PROVIDER_IDS.has(id) ? `skaile-${id}` : id;\n}\n\n/** Credential kinds whose value the adapter hands over in an env var. */\nconst ENV_DELIVERED_CREDENTIALS: Record<string, true> = {\n api_key_header: true,\n bearer: true,\n command: true,\n};\n\n/** One `[model_providers.<id>]` table, in codex's own vocabulary. */\nexport interface CodexModelProviderTable {\n /** Display name. Codex rejects a provider table without one. */\n name: string;\n base_url?: string;\n /** Name of the variable carrying the credential — never the credential. */\n env_key?: string;\n wire_api?: string;\n /** Routes codex to its own auth manager (ChatGPT subscription / stored key). */\n requires_openai_auth?: boolean;\n query_params?: Record<string, string>;\n http_headers?: Record<string, string>;\n}\n\n/** The subset of codex's `config.toml` this adapter owns. */\nexport interface CodexConfigDocument {\n /** Table id the session addresses (see {@link codexProviderTableId}); absent when nothing rendered. */\n model_provider?: string;\n /** Provider-side model id. */\n model?: string;\n model_providers: Record<string, CodexModelProviderTable>;\n}\n\n/** An entry codex cannot address, and why. */\nexport interface CodexSkippedEntry {\n id: string;\n reason: string;\n}\n\n/** Result of {@link buildCodexConfigDocument}. */\nexport interface CodexConfigBuild {\n document: CodexConfigDocument;\n /** Selected registry entry id (not the table id), when it rendered. */\n providerId?: string;\n /** Provider-side model id of the selection, when the selector named one. */\n model?: string;\n /**\n * Entries left out of the document. Non-fatal: codex keeps its built-in\n * providers, so a session whose selection was skipped behaves exactly as it\n * did before the registry existed.\n */\n skipped: CodexSkippedEntry[];\n /** Notes from `modelRef` resolution (dangling role, disabled provider, …). */\n warnings: string[];\n /**\n * Set when the selector named an entry codex cannot address: the session\n * must stop with `reason`, not fall back to codex's built-in providers.\n */\n unaddressable?: { id: string; reason: string };\n}\n\n/**\n * Whether the session's selector named the entry `selected` resolved to, rather\n * than reaching it by registry order alone.\n */\nfunction selectionNamesEntry(\n input: CodexConfigInput,\n selected: ResolvedModelRef,\n registry: AIProviderRegistry | MergedAIProviderRegistry,\n): boolean {\n if (selected.role && selected.modelId !== undefined) return true;\n const selector = input.modelRef?.trim() || input.model?.trim() || \"\";\n if (selector.startsWith(`${selected.providerId}/`)) return true;\n const entry = registryEntries(registry).find(([id]) => id === selected.providerId)?.[1] as\n | AIProviderLlmEntry\n | undefined;\n const modelId = selected.modelId;\n return (\n modelId !== undefined &&\n (entry?.model_map?.[modelId] !== undefined || entry?.capabilities?.[modelId] !== undefined)\n );\n}\n\n/** Inputs {@link buildCodexConfigDocument} reads off an `AgentConfig`. */\nexport interface CodexConfigInput {\n registry?: AIProviderRegistry | MergedAIProviderRegistry;\n /** `\"<providerId>/<modelId>\"`, a bare model id, or `\"@role\"`. */\n modelRef?: string;\n /** Legacy model field, used as the selector when `modelRef` is absent. */\n model?: string;\n /** Roles table (`skaile.yaml`), consulted for an `@role` selector. */\n roles?: Record<string, string>;\n}\n\n/**\n * Render one entry as a codex provider table, or explain why codex cannot\n * address it.\n */\nexport function renderCodexProviderTable(\n id: string,\n entry: AIProviderEntry,\n): { table: CodexModelProviderTable } | { skipped: string } {\n if (!isEntryEnabled(entry)) return { skipped: \"entry is disabled\" };\n if (entry.kind !== \"llm\" && entry.kind !== \"embed\")\n return { skipped: `kind '${entry.kind}' is not a model endpoint` };\n const llm = entry as AIProviderLlmEntry;\n // `surface` and `base_url` may live on the catalog preset the entry names —\n // the catalog layer fills them during a merge, but an entry handed straight\n // to a harness has not necessarily been through one.\n const preset = entry.preset ? getProviderPreset(entry.preset) : undefined;\n const surface = llm.surface ?? preset?.surface;\n const wireApi = surface ? CODEX_WIRE_API[surface] : undefined;\n if (!wireApi)\n return {\n skipped: surface\n ? `codex has no wire_api for surface '${surface}'`\n : \"entry declares no surface\",\n };\n const credentialKind = entry.credential?.kind;\n const table: CodexModelProviderTable = { name: entry.label ?? id };\n const baseUrl = resolveEntryBaseUrl(entry) ?? preset?.base_url;\n if (baseUrl) table.base_url = baseUrl;\n if (credentialKind && ENV_DELIVERED_CREDENTIALS[credentialKind])\n table.env_key = providerCredentialEnvName(id);\n table.wire_api = wireApi;\n if (credentialKind === \"oauth\") table.requires_openai_auth = true;\n if (llm.query_params && Object.keys(llm.query_params).length > 0)\n table.query_params = { ...llm.query_params };\n if (entry.headers && Object.keys(entry.headers).length > 0)\n table.http_headers = { ...entry.headers };\n return { table };\n}\n\n/**\n * Build the document for a session: every addressable entry, plus the\n * selection when it resolved to one of them.\n */\nexport function buildCodexConfigDocument(input: CodexConfigInput): CodexConfigBuild {\n const document: CodexConfigDocument = { model_providers: {} };\n const skipped: CodexSkippedEntry[] = [];\n const entries = input.registry ? registryEntries(input.registry) : [];\n for (const [id, entry] of entries) {\n const rendered = renderCodexProviderTable(id, entry);\n if (\"skipped\" in rendered) skipped.push({ id, reason: rendered.skipped });\n else document.model_providers[codexProviderTableId(id)] = rendered.table;\n }\n\n if (!input.registry) return { document, skipped, warnings: [] };\n // An empty selector resolves the same way a bare one does — first entry in\n // registry order. A session that configured an endpoint and named no model\n // still means that endpoint, not codex's built-in default.\n const selected = parseModelRef(\n input.modelRef?.trim() || input.model?.trim() || \"\",\n input.registry,\n input.roles,\n );\n if (!selected) return { document, skipped, warnings: [] };\n const warnings = selected.warnings;\n const tableId = codexProviderTableId(selected.providerId);\n if (!document.model_providers[tableId]) {\n // A selector that names the entry (`ollama/llama3`, a role or a model its\n // `model_map` lists) and cannot be rendered must not fall through to\n // codex's built-in OpenAI: the prompt would go to a provider the user did\n // not pick. A bare model id that matched no entry only landed on the first\n // entry by registry order, so codex's own providers stay the right answer.\n const reason = skipped.find((s) => s.id === selected.providerId)?.reason;\n if (reason && selectionNamesEntry(input, selected, input.registry)) {\n return {\n document,\n skipped,\n warnings,\n unaddressable: { id: selected.providerId, reason },\n };\n }\n return { document, skipped, warnings };\n }\n document.model_provider = tableId;\n if (selected.providerModelId) document.model = selected.providerModelId;\n return {\n document,\n providerId: selected.providerId,\n ...(selected.providerModelId ? { model: selected.providerModelId } : {}),\n skipped,\n warnings,\n };\n}\n\n/** Serialize a document to TOML. Deterministic: registry order throughout. */\nexport function renderCodexConfigToml(document: CodexConfigDocument): string {\n const ordered: Record<string, unknown> = {};\n if (document.model_provider) ordered.model_provider = document.model_provider;\n if (document.model) ordered.model = document.model;\n ordered.model_providers = document.model_providers;\n return `${stringifyToml(ordered)}\\n`;\n}\n\n/**\n * The session-private codex home.\n *\n * Deliberately outside `cwd`: codex's `workspace-write` sandbox makes the\n * working tree writable by the model, and a config home inside it would let a\n * turn rewrite its own `base_url`. `$SKAILE_HOME` (honoured by\n * {@link globalSkaileDir}) relocates the whole tree, which is also the test\n * seam.\n */\nexport function codexSessionHome(config: { cwd: string; sessionId?: string }): string {\n const key =\n config.sessionId && /^[A-Za-z0-9._-]{1,128}$/.test(config.sessionId)\n ? config.sessionId\n : createHash(\"sha256\")\n .update(config.sessionId ?? config.cwd)\n .digest(\"hex\")\n .slice(0, 32);\n return join(globalSkaileDir(), \"codex\", key);\n}\n\n/**\n * Write `<home>/config.toml`, creating the home always. Atomic: a\n * partially written document would fail codex's config load for the whole\n * session.\n *\n * A document with no provider tables says nothing codex does not already\n * know, so nothing is written and a file left by an earlier render is\n * removed — a session that no longer carries a registry must not keep\n * answering to the one it used to have.\n *\n * @returns the absolute path written, or `undefined` when there was nothing\n * to render.\n */\nexport function writeCodexConfigToml(\n home: string,\n document: CodexConfigDocument,\n): string | undefined {\n const path = join(home, \"config.toml\");\n // codex exits when CODEX_HOME names a directory that does not exist, so the\n // home exists before the early return too.\n mkdirSync(home, { recursive: true, mode: 0o700 });\n if (Object.keys(document.model_providers).length === 0) {\n rmSync(path, { force: true });\n return undefined;\n }\n const temporary = join(home, \"config.toml.new\");\n rmSync(temporary, { force: true });\n writeFileSync(temporary, renderCodexConfigToml(document), { mode: 0o600, flag: \"wx\" });\n renameSync(temporary, path);\n return path;\n}\n","/**\n * The codex CLI's own credentials, and when they stand in for the registry's `openai` entry.\n *\n * An `openai` registry entry whose key variable is not exported cannot be rendered as a working\n * `[model_providers.*]` table. The omp harness then answers with its own login; codex does the\n * same (p1871): it drops the entry and runs on codex's built-in `openai` provider, which uses\n * the ChatGPT login (`~/.codex/auth.json`) or an exported `OPENAI_API_KEY`. Every other entry\n * whose key is missing still refuses, naming the variable to export.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type {\n AIProviderRegistry,\n MergedAIProviderRegistry,\n} from \"@skaile/workspaces/core/provider-registry\";\nimport { unresolvedProviderCredentials } from \"../provider-materialize/credential-env.js\";\nimport {\n buildCodexConfigDocument,\n type CodexConfigBuild,\n codexProviderTableId,\n} from \"./config-toml.js\";\n\n/**\n * The codex CLI's ChatGPT login file (`$CODEX_HOME/auth.json`, default `~/.codex/auth.json`) of\n * the user's environment, or `undefined` when there is none. `env` is the parent environment,\n * not the session's: the session's `CODEX_HOME` is the private one, which has no login.\n */\nexport function codexLoginFile(env: Record<string, string | undefined>): string | undefined {\n const home = env.CODEX_HOME?.trim() || join(env.HOME || homedir(), \".codex\");\n const file = join(home, \"auth.json\");\n return existsSync(file) ? resolve(file) : undefined;\n}\n\n/**\n * Whether the session runs on codex's own auth instead of the selected `openai` registry entry:\n * the entry is selected, its key variable (`env_key` of the rendered table) is not set in `env`,\n * and codex has a login file or an exported key to use instead.\n *\n * @param build - The rendered codex config of the session.\n * @param env - The environment the child gets, credential variables included.\n * @param loginFile - The codex login file, when there is one.\n */\nexport function usesCodexOwnAuth(\n build: CodexConfigBuild,\n env: Record<string, string | undefined>,\n loginFile: string | undefined,\n): boolean {\n if (build.providerId !== \"openai\") return false;\n const table = build.document.model_providers[codexProviderTableId(\"openai\")];\n if (!table?.env_key || table.requires_openai_auth || env[table.env_key]) return false;\n return Boolean(env.OPENAI_API_KEY || env.CODEX_API_KEY || loginFile);\n}\n\n/**\n * The line `run` and `repl` print when a codex session falls back to codex's own auth because\n * the `openai` entry has no key (the `omp` line of the same kind, p1871), or `undefined`.\n *\n * @param session - The session's registry and model selector.\n * @param env - Process environment plus the session's credential variables.\n */\nexport function codexLoginFallbackNotice(\n session: { registry?: AIProviderRegistry | MergedAIProviderRegistry; modelRef?: string },\n env: Record<string, string | undefined>,\n): string | undefined {\n if (!session.registry) return undefined;\n const build = buildCodexConfigDocument({\n registry: session.registry,\n ...(session.modelRef ? { modelRef: session.modelRef } : {}),\n });\n if (!usesCodexOwnAuth(build, env, codexLoginFile(env))) return undefined;\n const unresolved = unresolvedProviderCredentials(session.registry, {\n env,\n providerId: \"openai\",\n })[0];\n return `provider 'openai' is not used: ${unresolved?.reason ?? \"its credential is not set\"}; codex uses its own login or OPENAI_API_KEY instead (check with \\`skaile providers test openai\\`)`;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { decodeBearerFromHeader, tokensEqual } from './chunk-32NA4TVC.js';
|
|
2
|
-
import { listenWithProbe } from './chunk-
|
|
2
|
+
import { listenWithProbe } from './chunk-6ARPZHOV.js';
|
|
3
3
|
import { createServer } from 'http';
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
5
|
|
|
@@ -256,5 +256,5 @@ var WebSocketServerTransport = class {
|
|
|
256
256
|
};
|
|
257
257
|
|
|
258
258
|
export { WebSocketServerTransport };
|
|
259
|
-
//# sourceMappingURL=chunk-
|
|
260
|
-
//# sourceMappingURL=chunk-
|
|
259
|
+
//# sourceMappingURL=chunk-6A6HE5LW.js.map
|
|
260
|
+
//# sourceMappingURL=chunk-6A6HE5LW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../transport/src/ws/server.ts"],"names":[],"mappings":";;;;;AAiDA,IAAM,oBAAA,GAAuB,GAAA;AAC7B,IAAM,wBAAA,GAA2B,CAAA;AAkBjC,SAAS,yBAAyB,GAAA,EAA6C;AAC7E,EAAA,IAAI,GAAA,CAAI,IAAA,KAAS,cAAA,EAAgB,OAAO,6BAAA;AAExC,EAAA,MAAM,WAAW,GAAA,CAAI,QAAA;AACrB,EAAA,IAAI,CAAC,YAAY,OAAO,QAAA,KAAa,YAAY,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACxE,IAAA,OAAO,4BAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,QAAA,CAAS,WAAA,KAAgB,QAAA,EAAU,OAAO,uCAAA;AACrD,EAAA,IAAI,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,EAAU,OAAO,qCAAA;AACnD,EAAA,IAAI,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,EAAU,OAAO,qCAAA;AAEnD,EAAA,MAAM,kBAAkB,GAAA,CAAI,eAAA;AAC5B,EAAA,IAAI,CAAC,mBAAmB,OAAO,eAAA,KAAoB,YAAY,KAAA,CAAM,OAAA,CAAQ,eAAe,CAAA,EAAG;AAC7F,IAAA,OAAO,mCAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AACtD,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AACtD,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AAEtD,EAAA,IACE,CAAC,GAAA,CAAI,cAAA,IACL,OAAO,GAAA,CAAI,cAAA,KAAmB,QAAA,IAC9B,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,EAChC;AACA,IAAA,OAAO,kCAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,GAAA,CAAI,WAAA,IAAe,OAAO,GAAA,CAAI,WAAA,KAAgB,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,EAAG;AAC7F,IAAA,OAAO,+BAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;AAyBO,IAAM,2BAAN,MAA0D;AAAA,EACvD,UAAA,GAAgC,IAAA;AAAA,EAChC,GAAA,GAA8B,IAAA;AAAA,EAC9B,YAAA,GAAiC,IAAA;AAAA,EACxB,IAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EAEA,WAAA;AAAA,EACA,eAAA,uBAAsB,GAAA,EAAqC;AAAA,EAC3D,eAAA,uBAAsB,GAAA,EAAgB;AAAA,EACtC,kBAAA,uBAAyB,GAAA,EAAgB;AAAA,EAE1D,YAAY,OAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,GAAA,GAAM,OAAA,CAAQ,KAAA,IAAS,OAAA,CAAQ,GAAA;AACpC,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAC3B,IAAA,IAAA,CAAK,WAAA,GAAc,QAAQ,WAAA,IAAe,oBAAA;AAC1C,IAAA,IAAA,CAAK,cAAA,GAAiB,QAAQ,cAAA,IAAkB,wBAAA;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,MAAA,GAAwB;AAC5B,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,CAAC,IAAA,EAAM,GAAA,KAAQ;AAC7C,MAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,cAAc,CAAA;AACnD,MAAA,GAAA,CAAI,IAAI,qBAAqB,CAAA;AAAA,IAC/B,CAAC,CAAA;AAKD,IAAA,MAAM,WAAW,IAAA,CAAK,SAAA;AACtB,IAAA,MAAM,GAAA,GAAM,IAAI,eAAA,CAAgB;AAAA,MAC9B,MAAA,EAAQ,UAAA;AAAA,MACR,YAAA,EAAc,QAAA,GACV,CAAC,IAAA,EAAM,EAAA,KAAO;AACZ,QAAA,MAAM,YAAY,sBAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,wBAAwB,CAAC,CAAA;AACnF,QAAA,IAAI,SAAA,KAAc,IAAA,IAAQ,WAAA,CAAY,SAAA,EAAW,QAAQ,CAAA,EAAG;AAC1D,UAAA,EAAA,CAAG,IAAI,CAAA;AAAA,QACT,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,IAAI,+CAA+C,CAAA;AACxD,UAAA,EAAA,CAAG,KAAA,EAAO,KAAK,cAAc,CAAA;AAAA,QAC/B;AAAA,MACF,CAAA,GACA;AAAA,KACL,CAAA;AACD,IAAA,GAAA,CAAI,GAAG,YAAA,EAAc,CAAC,OAAO,IAAA,CAAK,gBAAA,CAAiB,EAAE,CAAC,CAAA;AAItD,IAAA,MAAM,mBAAmB,MAAM;AAAA,IAAC,CAAA;AAChC,IAAA,GAAA,CAAI,EAAA,CAAG,SAAS,gBAAgB,CAAA;AAChC,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AAEX,IAAA,IAAI;AACF,MAAA,MAAM,eAAA,CAAgB,UAAA,EAAY,IAAA,CAAK,IAAA,EAAM,KAAK,IAAI,CAAA;AAAA,IACxD,SAAS,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,KAAA,EAAM;AACV,MAAA,IAAA,CAAK,GAAA,GAAM,IAAA;AACX,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA,IAAI,eAAe,KAAA,IAAS,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,YAAA,EAAc;AACtE,QAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,GAAK,CAAA,KAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,CAAA;AACzE,QAAA,MAAM,MAAA,CAAO,MAAA;AAAA,UACX,IAAI,KAAA;AAAA,YACF,GAAG,KAAK,CAAA,qEAAA;AAAA,WACV;AAAA,UACA,EAAE,MAAM,YAAA;AAAa,SACvB;AAAA,MACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,GAAA,CAAI,GAAA,CAAI,SAAS,gBAAgB,CAAA;AAAA,IACnC;AAEA,IAAA,MAAM,IAAA,GAAO,WAAW,OAAA,EAAQ;AAChC,IAAA,MAAM,YAAY,OAAO,IAAA,KAAS,YAAY,IAAA,GAAO,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACtE,IAAA,MAAM,IAAA,GAAO,KAAK,IAAA,IAAQ,gBAAA;AAC1B,IAAA,IAAA,CAAK,GAAA;AAAA,MACH,CAAA,8BAAA,EAAiC,SAAS,CAAA,EAAA,EAAK,IAAI,KAAK,IAAA,CAAK,SAAA,GAAY,mBAAmB,mBAAmB,CAAA,CAAA;AAAA,KACjH;AAEA,IAAA,MAAM,OAAA,GACJ,CAAC,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,IAAA,KAAS,SAAA,IAAa,IAAA,CAAK,IAAA,KAAS,IAAA,GACnD,WAAA,GACA,IAAA,CAAK,IAAA,CAAK,SAAS,GAAG,CAAA,IAAK,CAAC,IAAA,CAAK,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAClD,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,CAAA,GACb,IAAA,CAAK,IAAA;AACb,IAAA,IAAA,CAAK,WAAA,GAAc;AAAA,MACjB,GAAA,EAAK,CAAA,KAAA,EAAQ,OAAO,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,MACjC,IAAA,EAAM,SAAA;AAAA,MACN,aAAA,EAAe,KAAK,SAAA,KAAc;AAAA,KACnC,CAAA;AAAA,EACH;AAAA,EAEQ,iBAAiB,EAAA,EAAqB;AAC5C,IAAA,IAAI,KAAK,YAAA,EAAc;AAIrB,MAAA,IAAA,CAAK,IAAI,2CAA2C,CAAA;AACpD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,YAAA,CAAa,KAAA,CAAM,IAAA,EAAM,4BAA4B,CAAA;AAAA,MAC5D,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,CAAK,IAAI,8BAA8B,CAAA;AACvC,IAAA,IAAA,CAAK,YAAA,GAAe,EAAA;AACpB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,eAAA,EAAiB;AAC1C,MAAA,OAAA,EAAQ;AAAA,IACV;AAEA,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,cAAA,CAAe,EAAE,CAAA;AAE5C,IAAA,EAAA,CAAG,EAAA,CAAG,SAAA,EAAW,CAAC,IAAA,KAAS;AACzB,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAKtC,QAAA,IAAI,UAAU,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,SAAS,cAAA,EAAgB;AAC1E,UAAA,MAAM,MAAA,GAAS,yBAAyB,MAAM,CAAA;AAC9C,UAAA,IAAI,WAAW,IAAA,EAAM;AACnB,YAAA,IAAA,CAAK,GAAA,CAAI,CAAA,4CAAA,EAA+C,MAAM,CAAA,CAAE,CAAA;AAChE,YAAA,IAAA,CAAK,OAAA,CAAQ;AAAA,cACX,IAAA,EAAM,OAAA;AAAA,cACN,OAAA,EAAS,yBAAyB,MAAM,CAAA,CAAA;AAAA,cACxC,KAAA,EAAO;AAAA,aACR,CAAA;AACD,YAAA;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,GAAA,GAAM,MAAA;AACZ,QAAA,KAAA,MAAW,OAAA,IAAW,KAAK,eAAA,EAAiB;AAC1C,UAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,QACb;AAAA,MACF,SAAS,GAAA,EAAK;AACZ,QAAA,IAAA,CAAK,OAAA,CAAQ;AAAA,UACX,IAAA,EAAM,OAAA;AAAA,UACN,OAAA,EAAS,oBAAoB,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA;AAAA,UAC7E,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,IACF,CAAC,CAAA;AAED,IAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACnB,MAAA,aAAA,EAAc;AACd,MAAA,IAAA,CAAK,IAAI,iCAAiC,CAAA;AAC1C,MAAA,IAAI,IAAA,CAAK,iBAAiB,EAAA,EAAI;AAC5B,QAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,QAAA,KAAA,MAAW,OAAA,IAAW,KAAK,kBAAA,EAAoB;AAC7C,UAAA,OAAA,EAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAAe,EAAA,EAA2B;AAChD,IAAA,IAAI,IAAA,CAAK,cAAA,IAAkB,CAAA,EAAG,OAAO,MAAM;AAAA,IAAC,CAAA;AAC5C,IAAA,IAAI,MAAA,GAAS,CAAA;AACb,IAAA,MAAM,YAAY,MAAM;AACtB,MAAA,MAAA,GAAS,CAAA;AAAA,IACX,CAAA;AACA,IAAA,EAAA,CAAG,EAAA,CAAG,QAAQ,SAAS,CAAA;AACvB,IAAA,EAAA,CAAG,EAAA,CAAG,WAAW,SAAS,CAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,MAAA,IAAI,EAAA,CAAG,UAAA,KAAe,EAAA,CAAG,IAAA,EAAM;AAC/B,MAAA,IAAI,MAAA,IAAU,KAAK,cAAA,EAAgB;AACjC,QAAA,IAAA,CAAK,GAAA;AAAA,UACH,CAAA,wBAAA,EAA2B,MAAM,CAAA,eAAA,EAAkB,IAAA,CAAK,WAAW,CAAA,sBAAA;AAAA,SACrE;AACA,QAAA,aAAA,CAAc,KAAK,CAAA;AACnB,QAAA,EAAA,CAAG,SAAA,EAAU;AACb,QAAA;AAAA,MACF;AACA,MAAA,MAAA,EAAA;AACA,MAAA,IAAI;AACF,QAAA,EAAA,CAAG,IAAA,EAAK;AAAA,MACV,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF,CAAA,EAAG,KAAK,WAAW,CAAA;AACnB,IAAA,KAAA,CAAM,KAAA,IAAQ;AACd,IAAA,OAAO,MAAM,cAAc,KAAK,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAAA,IAC/B,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,IAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAChB,IAAA,IAAA,CAAK,GAAA,GAAM,IAAA;AACX,IAAA,MAAM,aAAa,IAAA,CAAK,UAAA;AACxB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,mBAAA,EAAoB;AAC/B,MAAA,MAAM,IAAI,QAAc,CAAC,OAAA,KAAY,WAAW,KAAA,CAAM,MAAM,OAAA,EAAS,CAAC,CAAA;AAAA,IACxE;AAAA,EACF;AAAA,EAEA,KAAK,KAAA,EAAyB;AAC5B,IAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EACpB;AAAA,EAEA,UAAU,OAAA,EAAsD;AAC9D,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,OAAO,CAAA;AAChC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,IACrC,CAAA;AAAA,EACF;AAAA,EAEA,UAAU,OAAA,EAAiC;AACzC,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,OAAO,CAAA;AAChC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,IACrC,CAAA;AAAA,EACF;AAAA,EAEA,aAAa,OAAA,EAAiC;AAC5C,IAAA,IAAA,CAAK,kBAAA,CAAmB,IAAI,OAAO,CAAA;AACnC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,kBAAA,CAAmB,OAAO,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,EACF;AAAA,EAEA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,YAAA,KAAiB,IAAA;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,SAAA,GAAgC;AAClC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,UAAA,EAAY,OAAA,EAAQ;AACtC,IAAA,OAAO,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,GAAO,KAAK,IAAA,GAAO,MAAA;AAAA,EACxD;AAAA,EAEQ,QAAQ,IAAA,EAAqB;AACnC,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,IAAA,CAAK,YAAA;AACpB,MAAA,IAAI,MAAA,IAAU,MAAA,CAAO,UAAA,KAAe,CAAA,EAAG;AACrC,QAAA,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,MAClC;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF","file":"chunk-7H4GTP7V.js","sourcesContent":["/**\n * WebSocketServerTransport — `ws`-based WebSocket server.\n *\n * Accepts one client at a time. Implements ServerTransport from @skaile/workspaces/types.\n * Built on the `ws` package + `node:http`, both of which run under Node and Bun.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport { listenWithProbe } from \"@skaile/workspaces/core\";\nimport type { AgentCommand, AgentEvent, ServerTransport } from \"@skaile/workspaces/types\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport { decodeBearerFromHeader, tokensEqual } from \"./auth.js\";\n\n/** @deprecated Moved to `@skaile/workspaces/core`, where `portIsHeld` replaces it. */\nexport { needsWildcardProbe } from \"@skaile/workspaces/core\";\n\n/**\n * Options for {@link WebSocketServerTransport}.\n *\n * @param port - TCP port to bind to; `0` picks a free port (read it back via `boundPort`).\n * @param onLog - Optional callback for internal log lines (client connect/disconnect events).\n * @param host - Address to bind. Default: every interface (Node's unspecified address).\n * `127.0.0.1` accepts local clients only.\n * @param authToken - When set, every upgrade must present this token via the\n * `skaile-bearer.<base64url>` subprotocol or the handshake is rejected with\n * HTTP 401. When unset, no auth is enforced (back-compatible default).\n * @param onListening - Called once the socket is bound, with the address a client\n * connects to (`url`) and whether it needs a token. For a plain-text ready line\n * (the `onLog` record carries the port only inside a log sentence).\n * @param heartbeatMs - Interval in milliseconds between pings to the active\n * client. Default: 30 000.\n * @param maxMissedPongs - Heartbeat ticks the client may go without a pong (or\n * any inbound frame) before its socket is terminated, so a client lost behind a\n * dead path (no FIN) fires `onDisconnect` instead of swallowing every event\n * sent to it. Default: 2, i.e. detection within 3 x `heartbeatMs`. `0` turns\n * the heartbeat off. Every WebSocket client answers pings on its own.\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport interface WebSocketServerOptions {\n port: number;\n host?: string;\n onLog?: (line: string) => void;\n authToken?: string;\n onListening?: (info: { url: string; port: number; tokenRequired: boolean }) => void;\n heartbeatMs?: number;\n maxMissedPongs?: number;\n}\n\nconst DEFAULT_HEARTBEAT_MS = 30_000;\nconst DEFAULT_MAX_MISSED_PONGS = 2;\n\n/**\n * Shape-validate a `session_init` frame at ingress before forwarding to the\n * runner. Protocol v3 requires the deterministic bootstrap envelope to carry\n * an identity tuple, a semver-shaped `protocolVersion`, a resolved config\n * object, and a `credentials` map keyed by `mounts` / `connectors`. Frames\n * that fail the guard are rejected on the wire so the runner never sees a\n * malformed boot envelope.\n *\n * Returns `null` when the frame is well-formed, or a human-readable reason\n * when it should be rejected.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"SessionInitCommand schema\".\n *\n * @internal\n */\nfunction validateSessionInitShape(cmd: Record<string, unknown>): string | null {\n if (cmd.type !== \"session_init\") return \"type must be 'session_init'\";\n\n const identity = cmd.identity as Record<string, unknown> | undefined;\n if (!identity || typeof identity !== \"object\" || Array.isArray(identity)) {\n return \"identity must be an object\";\n }\n if (typeof identity.containerId !== \"string\") return \"identity.containerId must be a string\";\n if (typeof identity.sessionId !== \"string\") return \"identity.sessionId must be a string\";\n if (typeof identity.projectId !== \"string\") return \"identity.projectId must be a string\";\n\n const protocolVersion = cmd.protocolVersion as Record<string, unknown> | undefined;\n if (!protocolVersion || typeof protocolVersion !== \"object\" || Array.isArray(protocolVersion)) {\n return \"protocolVersion must be an object\";\n }\n if (typeof protocolVersion.major !== \"number\") return \"protocolVersion.major must be a number\";\n if (typeof protocolVersion.minor !== \"number\") return \"protocolVersion.minor must be a number\";\n if (typeof protocolVersion.patch !== \"number\") return \"protocolVersion.patch must be a number\";\n\n if (\n !cmd.resolvedConfig ||\n typeof cmd.resolvedConfig !== \"object\" ||\n Array.isArray(cmd.resolvedConfig)\n ) {\n return \"resolvedConfig must be an object\";\n }\n\n if (!cmd.credentials || typeof cmd.credentials !== \"object\" || Array.isArray(cmd.credentials)) {\n return \"credentials must be an object\";\n }\n\n return null;\n}\n\n/**\n * `ServerTransport` implementation backed by the `ws` package on top of a\n * `node:http` server.\n *\n * Enforces a single-client policy: if a new connection arrives while a client is\n * already connected, the stale connection is closed with code 4001 and replaced.\n * Incoming frames are parsed as JSON `AgentCommand` messages and fanned out to\n * registered `onCommand` handlers; outgoing `AgentEvent` objects are JSON-serialised\n * before sending. Call `listen()` to start accepting connections and `close()` to\n * shut down cleanly — `close()` terminates the active socket immediately rather\n * than keeping the event loop alive.\n *\n * @example\n * ```ts\n * const server = new WebSocketServerTransport({ port: 8080 });\n * await server.listen();\n * server.onCommand((cmd) => console.log(cmd));\n * server.send({ type: 'agent_end' });\n * await server.close();\n * ```\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport class WebSocketServerTransport implements ServerTransport {\n private httpServer: HttpServer | null = null;\n private wss: WebSocketServer | null = null;\n private activeClient: WebSocket | null = null;\n private readonly port: number;\n private readonly log: (line: string) => void;\n private readonly host?: string;\n private readonly authToken?: string;\n private readonly heartbeatMs: number;\n private readonly maxMissedPongs: number;\n\n private readonly onListening: WebSocketServerOptions[\"onListening\"];\n private readonly commandHandlers = new Set<(command: AgentCommand) => void>();\n private readonly connectHandlers = new Set<() => void>();\n private readonly disconnectHandlers = new Set<() => void>();\n\n constructor(options: WebSocketServerOptions) {\n this.port = options.port;\n this.log = options.onLog ?? console.log;\n this.host = options.host;\n this.authToken = options.authToken;\n this.onListening = options.onListening;\n this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;\n }\n\n /** Start the server and accept connections. */\n async listen(): Promise<void> {\n const httpServer = createServer((_req, res) => {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"skaile agent server\");\n });\n // When an auth token is configured, reject the upgrade at handshake time\n // (HTTP 401) unless the client presents the matching bearer subprotocol.\n // Rejecting here — before `connection` — keeps unauthenticated sockets from\n // ever reaching the command handlers.\n const expected = this.authToken;\n const wss = new WebSocketServer({\n server: httpServer,\n verifyClient: expected\n ? (info, cb) => {\n const presented = decodeBearerFromHeader(info.req.headers[\"sec-websocket-protocol\"]);\n if (presented !== null && tokensEqual(presented, expected)) {\n cb(true);\n } else {\n this.log(\"[ws-server] rejecting unauthenticated upgrade\");\n cb(false, 401, \"Unauthorized\");\n }\n }\n : undefined,\n });\n wss.on(\"connection\", (ws) => this.handleConnection(ws));\n // `ws` re-emits the http server's errors on `wss`; unhandled during the bind,\n // that throws before the reject below runs and `listen()` hangs. Scoped to the\n // bind so a post-startup server error still surfaces loudly.\n const swallowBindError = () => {};\n wss.on(\"error\", swallowBindError);\n this.httpServer = httpServer;\n this.wss = wss;\n\n try {\n await listenWithProbe(httpServer, this.port, this.host);\n } catch (err) {\n wss.close();\n this.wss = null;\n this.httpServer = null;\n if (err instanceof Error && \"code\" in err && err.code === \"EADDRINUSE\") {\n const where = this.host ? `${this.host}:${this.port}` : `port ${this.port}`;\n throw Object.assign(\n new Error(\n `${where} is already in use: another process listens there. Pick another port.`,\n ),\n { code: \"EADDRINUSE\" },\n );\n }\n throw err;\n } finally {\n wss.off(\"error\", swallowBindError);\n }\n\n const addr = httpServer.address();\n const boundPort = typeof addr === \"object\" && addr ? addr.port : this.port;\n const bind = this.host ?? \"all interfaces\";\n this.log(\n `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? \"token required\" : \"no authentication\"})`,\n );\n // A wildcard bind is reachable on loopback; that is the URL worth printing.\n const urlHost =\n !this.host || this.host === \"0.0.0.0\" || this.host === \"::\"\n ? \"127.0.0.1\"\n : this.host.includes(\":\") && !this.host.startsWith(\"[\")\n ? `[${this.host}]`\n : this.host;\n this.onListening?.({\n url: `ws://${urlHost}:${boundPort}`,\n port: boundPort,\n tokenRequired: this.authToken !== undefined,\n });\n }\n\n private handleConnection(ws: WebSocket): void {\n if (this.activeClient) {\n // Close the stale connection instead of rejecting the new one.\n // This handles the race where waitForWsReady's probe hasn't\n // fully disconnected before the real gateway client connects.\n this.log(\"[ws-server] replacing previous connection\");\n try {\n this.activeClient.close(4001, \"Replaced by new connection\");\n } catch {\n // already closed\n }\n }\n this.log(\"[ws-server] client connected\");\n this.activeClient = ws;\n for (const handler of this.connectHandlers) {\n handler();\n }\n\n const stopHeartbeat = this.startHeartbeat(ws);\n\n ws.on(\"message\", (data) => {\n try {\n const parsed = JSON.parse(String(data)) as Record<string, unknown>;\n // Protocol v3: shape-validate `session_init` at ingress so the\n // runner never receives a malformed boot envelope. Other command\n // shapes pass through unchanged — invalid `AgentCommand`s surface\n // through the existing per-command handlers in serve.ts.\n if (parsed && typeof parsed === \"object\" && parsed.type === \"session_init\") {\n const reason = validateSessionInitShape(parsed);\n if (reason !== null) {\n this.log(`[ws-server] rejecting invalid session_init: ${reason}`);\n this.sendRaw({\n type: \"error\",\n message: `Invalid session_init: ${reason}`,\n fatal: false,\n });\n return;\n }\n }\n const cmd = parsed as unknown as AgentCommand;\n for (const handler of this.commandHandlers) {\n handler(cmd);\n }\n } catch (err) {\n this.sendRaw({\n type: \"error\",\n message: `Invalid message: ${err instanceof Error ? err.message : String(err)}`,\n fatal: false,\n });\n }\n });\n\n ws.on(\"close\", () => {\n stopHeartbeat();\n this.log(\"[ws-server] client disconnected\");\n if (this.activeClient === ws) {\n this.activeClient = null;\n for (const handler of this.disconnectHandlers) {\n handler();\n }\n }\n });\n }\n\n /**\n * Ping `ws` every {@link heartbeatMs}; terminate it after\n * {@link maxMissedPongs} unanswered ticks. `terminate()` makes `ws` emit\n * `close`, which runs the normal disconnect path. Returns the stop function.\n */\n private startHeartbeat(ws: WebSocket): () => void {\n if (this.maxMissedPongs <= 0) return () => {};\n let missed = 0;\n const markAlive = () => {\n missed = 0;\n };\n ws.on(\"pong\", markAlive);\n ws.on(\"message\", markAlive);\n const timer = setInterval(() => {\n if (ws.readyState !== ws.OPEN) return;\n if (missed >= this.maxMissedPongs) {\n this.log(\n `[ws-server] no pong for ${missed} heartbeats of ${this.heartbeatMs}ms; terminating client`,\n );\n clearInterval(timer);\n ws.terminate();\n return;\n }\n missed++;\n try {\n ws.ping();\n } catch {\n // socket closing underneath us; the close event cleans up\n }\n }, this.heartbeatMs);\n timer.unref?.();\n return () => clearInterval(timer);\n }\n\n /**\n * Stop the server.\n *\n * Terminates the active socket immediately — otherwise a still-connected\n * client would keep the event loop alive and block process shutdown.\n */\n async close(): Promise<void> {\n try {\n this.activeClient?.terminate();\n } catch {\n // already closed\n }\n this.activeClient = null;\n this.wss?.close();\n this.wss = null;\n const httpServer = this.httpServer;\n this.httpServer = null;\n if (httpServer) {\n httpServer.closeAllConnections();\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n send(event: AgentEvent): void {\n this.sendRaw(event);\n }\n\n onCommand(handler: (command: AgentCommand) => void): () => void {\n this.commandHandlers.add(handler);\n return () => {\n this.commandHandlers.delete(handler);\n };\n }\n\n onConnect(handler: () => void): () => void {\n this.connectHandlers.add(handler);\n return () => {\n this.connectHandlers.delete(handler);\n };\n }\n\n onDisconnect(handler: () => void): () => void {\n this.disconnectHandlers.add(handler);\n return () => {\n this.disconnectHandlers.delete(handler);\n };\n }\n\n get connected(): boolean {\n return this.activeClient !== null;\n }\n\n /** The port actually bound by `listen()` (resolves `port: 0`); `undefined` when not listening. */\n get boundPort(): number | undefined {\n const addr = this.httpServer?.address();\n return typeof addr === \"object\" && addr ? addr.port : undefined;\n }\n\n private sendRaw(data: unknown): void {\n try {\n const client = this.activeClient;\n if (client && client.readyState === 1) {\n client.send(JSON.stringify(data));\n }\n } catch {\n // Silently ignore send failures\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../transport/src/ws/server.ts"],"names":[],"mappings":";;;;;AAiDA,IAAM,oBAAA,GAAuB,GAAA;AAC7B,IAAM,wBAAA,GAA2B,CAAA;AAkBjC,SAAS,yBAAyB,GAAA,EAA6C;AAC7E,EAAA,IAAI,GAAA,CAAI,IAAA,KAAS,cAAA,EAAgB,OAAO,6BAAA;AAExC,EAAA,MAAM,WAAW,GAAA,CAAI,QAAA;AACrB,EAAA,IAAI,CAAC,YAAY,OAAO,QAAA,KAAa,YAAY,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACxE,IAAA,OAAO,4BAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,QAAA,CAAS,WAAA,KAAgB,QAAA,EAAU,OAAO,uCAAA;AACrD,EAAA,IAAI,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,EAAU,OAAO,qCAAA;AACnD,EAAA,IAAI,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,EAAU,OAAO,qCAAA;AAEnD,EAAA,MAAM,kBAAkB,GAAA,CAAI,eAAA;AAC5B,EAAA,IAAI,CAAC,mBAAmB,OAAO,eAAA,KAAoB,YAAY,KAAA,CAAM,OAAA,CAAQ,eAAe,CAAA,EAAG;AAC7F,IAAA,OAAO,mCAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AACtD,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AACtD,EAAA,IAAI,OAAO,eAAA,CAAgB,KAAA,KAAU,QAAA,EAAU,OAAO,wCAAA;AAEtD,EAAA,IACE,CAAC,GAAA,CAAI,cAAA,IACL,OAAO,GAAA,CAAI,cAAA,KAAmB,QAAA,IAC9B,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,EAChC;AACA,IAAA,OAAO,kCAAA;AAAA,EACT;AAEA,EAAA,IAAI,CAAC,GAAA,CAAI,WAAA,IAAe,OAAO,GAAA,CAAI,WAAA,KAAgB,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,EAAG;AAC7F,IAAA,OAAO,+BAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;AAyBO,IAAM,2BAAN,MAA0D;AAAA,EACvD,UAAA,GAAgC,IAAA;AAAA,EAChC,GAAA,GAA8B,IAAA;AAAA,EAC9B,YAAA,GAAiC,IAAA;AAAA,EACxB,IAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EAEA,WAAA;AAAA,EACA,eAAA,uBAAsB,GAAA,EAAqC;AAAA,EAC3D,eAAA,uBAAsB,GAAA,EAAgB;AAAA,EACtC,kBAAA,uBAAyB,GAAA,EAAgB;AAAA,EAE1D,YAAY,OAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,GAAA,GAAM,OAAA,CAAQ,KAAA,IAAS,OAAA,CAAQ,GAAA;AACpC,IAAA,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAC3B,IAAA,IAAA,CAAK,WAAA,GAAc,QAAQ,WAAA,IAAe,oBAAA;AAC1C,IAAA,IAAA,CAAK,cAAA,GAAiB,QAAQ,cAAA,IAAkB,wBAAA;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,MAAA,GAAwB;AAC5B,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,CAAC,IAAA,EAAM,GAAA,KAAQ;AAC7C,MAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,cAAc,CAAA;AACnD,MAAA,GAAA,CAAI,IAAI,qBAAqB,CAAA;AAAA,IAC/B,CAAC,CAAA;AAKD,IAAA,MAAM,WAAW,IAAA,CAAK,SAAA;AACtB,IAAA,MAAM,GAAA,GAAM,IAAI,eAAA,CAAgB;AAAA,MAC9B,MAAA,EAAQ,UAAA;AAAA,MACR,YAAA,EAAc,QAAA,GACV,CAAC,IAAA,EAAM,EAAA,KAAO;AACZ,QAAA,MAAM,YAAY,sBAAA,CAAuB,IAAA,CAAK,GAAA,CAAI,OAAA,CAAQ,wBAAwB,CAAC,CAAA;AACnF,QAAA,IAAI,SAAA,KAAc,IAAA,IAAQ,WAAA,CAAY,SAAA,EAAW,QAAQ,CAAA,EAAG;AAC1D,UAAA,EAAA,CAAG,IAAI,CAAA;AAAA,QACT,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,IAAI,+CAA+C,CAAA;AACxD,UAAA,EAAA,CAAG,KAAA,EAAO,KAAK,cAAc,CAAA;AAAA,QAC/B;AAAA,MACF,CAAA,GACA;AAAA,KACL,CAAA;AACD,IAAA,GAAA,CAAI,GAAG,YAAA,EAAc,CAAC,OAAO,IAAA,CAAK,gBAAA,CAAiB,EAAE,CAAC,CAAA;AAItD,IAAA,MAAM,mBAAmB,MAAM;AAAA,IAAC,CAAA;AAChC,IAAA,GAAA,CAAI,EAAA,CAAG,SAAS,gBAAgB,CAAA;AAChC,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AAEX,IAAA,IAAI;AACF,MAAA,MAAM,eAAA,CAAgB,UAAA,EAAY,IAAA,CAAK,IAAA,EAAM,KAAK,IAAI,CAAA;AAAA,IACxD,SAAS,GAAA,EAAK;AACZ,MAAA,GAAA,CAAI,KAAA,EAAM;AACV,MAAA,IAAA,CAAK,GAAA,GAAM,IAAA;AACX,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA,IAAI,eAAe,KAAA,IAAS,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,YAAA,EAAc;AACtE,QAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,GAAK,CAAA,KAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,CAAA;AACzE,QAAA,MAAM,MAAA,CAAO,MAAA;AAAA,UACX,IAAI,KAAA;AAAA,YACF,GAAG,KAAK,CAAA,qEAAA;AAAA,WACV;AAAA,UACA,EAAE,MAAM,YAAA;AAAa,SACvB;AAAA,MACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,GAAA,CAAI,GAAA,CAAI,SAAS,gBAAgB,CAAA;AAAA,IACnC;AAEA,IAAA,MAAM,IAAA,GAAO,WAAW,OAAA,EAAQ;AAChC,IAAA,MAAM,YAAY,OAAO,IAAA,KAAS,YAAY,IAAA,GAAO,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACtE,IAAA,MAAM,IAAA,GAAO,KAAK,IAAA,IAAQ,gBAAA;AAC1B,IAAA,IAAA,CAAK,GAAA;AAAA,MACH,CAAA,8BAAA,EAAiC,SAAS,CAAA,EAAA,EAAK,IAAI,KAAK,IAAA,CAAK,SAAA,GAAY,mBAAmB,mBAAmB,CAAA,CAAA;AAAA,KACjH;AAEA,IAAA,MAAM,OAAA,GACJ,CAAC,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,IAAA,KAAS,SAAA,IAAa,IAAA,CAAK,IAAA,KAAS,IAAA,GACnD,WAAA,GACA,IAAA,CAAK,IAAA,CAAK,SAAS,GAAG,CAAA,IAAK,CAAC,IAAA,CAAK,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAClD,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,CAAA,GACb,IAAA,CAAK,IAAA;AACb,IAAA,IAAA,CAAK,WAAA,GAAc;AAAA,MACjB,GAAA,EAAK,CAAA,KAAA,EAAQ,OAAO,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,MACjC,IAAA,EAAM,SAAA;AAAA,MACN,aAAA,EAAe,KAAK,SAAA,KAAc;AAAA,KACnC,CAAA;AAAA,EACH;AAAA,EAEQ,iBAAiB,EAAA,EAAqB;AAC5C,IAAA,IAAI,KAAK,YAAA,EAAc;AAIrB,MAAA,IAAA,CAAK,IAAI,2CAA2C,CAAA;AACpD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,YAAA,CAAa,KAAA,CAAM,IAAA,EAAM,4BAA4B,CAAA;AAAA,MAC5D,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,IAAA,CAAK,IAAI,8BAA8B,CAAA;AACvC,IAAA,IAAA,CAAK,YAAA,GAAe,EAAA;AACpB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,eAAA,EAAiB;AAC1C,MAAA,OAAA,EAAQ;AAAA,IACV;AAEA,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,cAAA,CAAe,EAAE,CAAA;AAE5C,IAAA,EAAA,CAAG,EAAA,CAAG,SAAA,EAAW,CAAC,IAAA,KAAS;AACzB,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,IAAI,CAAC,CAAA;AAKtC,QAAA,IAAI,UAAU,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,SAAS,cAAA,EAAgB;AAC1E,UAAA,MAAM,MAAA,GAAS,yBAAyB,MAAM,CAAA;AAC9C,UAAA,IAAI,WAAW,IAAA,EAAM;AACnB,YAAA,IAAA,CAAK,GAAA,CAAI,CAAA,4CAAA,EAA+C,MAAM,CAAA,CAAE,CAAA;AAChE,YAAA,IAAA,CAAK,OAAA,CAAQ;AAAA,cACX,IAAA,EAAM,OAAA;AAAA,cACN,OAAA,EAAS,yBAAyB,MAAM,CAAA,CAAA;AAAA,cACxC,KAAA,EAAO;AAAA,aACR,CAAA;AACD,YAAA;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,GAAA,GAAM,MAAA;AACZ,QAAA,KAAA,MAAW,OAAA,IAAW,KAAK,eAAA,EAAiB;AAC1C,UAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,QACb;AAAA,MACF,SAAS,GAAA,EAAK;AACZ,QAAA,IAAA,CAAK,OAAA,CAAQ;AAAA,UACX,IAAA,EAAM,OAAA;AAAA,UACN,OAAA,EAAS,oBAAoB,GAAA,YAAe,KAAA,GAAQ,IAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA;AAAA,UAC7E,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,IACF,CAAC,CAAA;AAED,IAAA,EAAA,CAAG,EAAA,CAAG,SAAS,MAAM;AACnB,MAAA,aAAA,EAAc;AACd,MAAA,IAAA,CAAK,IAAI,iCAAiC,CAAA;AAC1C,MAAA,IAAI,IAAA,CAAK,iBAAiB,EAAA,EAAI;AAC5B,QAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,QAAA,KAAA,MAAW,OAAA,IAAW,KAAK,kBAAA,EAAoB;AAC7C,UAAA,OAAA,EAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAAe,EAAA,EAA2B;AAChD,IAAA,IAAI,IAAA,CAAK,cAAA,IAAkB,CAAA,EAAG,OAAO,MAAM;AAAA,IAAC,CAAA;AAC5C,IAAA,IAAI,MAAA,GAAS,CAAA;AACb,IAAA,MAAM,YAAY,MAAM;AACtB,MAAA,MAAA,GAAS,CAAA;AAAA,IACX,CAAA;AACA,IAAA,EAAA,CAAG,EAAA,CAAG,QAAQ,SAAS,CAAA;AACvB,IAAA,EAAA,CAAG,EAAA,CAAG,WAAW,SAAS,CAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,MAAA,IAAI,EAAA,CAAG,UAAA,KAAe,EAAA,CAAG,IAAA,EAAM;AAC/B,MAAA,IAAI,MAAA,IAAU,KAAK,cAAA,EAAgB;AACjC,QAAA,IAAA,CAAK,GAAA;AAAA,UACH,CAAA,wBAAA,EAA2B,MAAM,CAAA,eAAA,EAAkB,IAAA,CAAK,WAAW,CAAA,sBAAA;AAAA,SACrE;AACA,QAAA,aAAA,CAAc,KAAK,CAAA;AACnB,QAAA,EAAA,CAAG,SAAA,EAAU;AACb,QAAA;AAAA,MACF;AACA,MAAA,MAAA,EAAA;AACA,MAAA,IAAI;AACF,QAAA,EAAA,CAAG,IAAA,EAAK;AAAA,MACV,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF,CAAA,EAAG,KAAK,WAAW,CAAA;AACnB,IAAA,KAAA,CAAM,KAAA,IAAQ;AACd,IAAA,OAAO,MAAM,cAAc,KAAK,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,cAAc,SAAA,EAAU;AAAA,IAC/B,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,IAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAChB,IAAA,IAAA,CAAK,GAAA,GAAM,IAAA;AACX,IAAA,MAAM,aAAa,IAAA,CAAK,UAAA;AACxB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,mBAAA,EAAoB;AAC/B,MAAA,MAAM,IAAI,QAAc,CAAC,OAAA,KAAY,WAAW,KAAA,CAAM,MAAM,OAAA,EAAS,CAAC,CAAA;AAAA,IACxE;AAAA,EACF;AAAA,EAEA,KAAK,KAAA,EAAyB;AAC5B,IAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EACpB;AAAA,EAEA,UAAU,OAAA,EAAsD;AAC9D,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,OAAO,CAAA;AAChC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,IACrC,CAAA;AAAA,EACF;AAAA,EAEA,UAAU,OAAA,EAAiC;AACzC,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,OAAO,CAAA;AAChC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,eAAA,CAAgB,OAAO,OAAO,CAAA;AAAA,IACrC,CAAA;AAAA,EACF;AAAA,EAEA,aAAa,OAAA,EAAiC;AAC5C,IAAA,IAAA,CAAK,kBAAA,CAAmB,IAAI,OAAO,CAAA;AACnC,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,kBAAA,CAAmB,OAAO,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,EACF;AAAA,EAEA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,YAAA,KAAiB,IAAA;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,SAAA,GAAgC;AAClC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,UAAA,EAAY,OAAA,EAAQ;AACtC,IAAA,OAAO,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,GAAO,KAAK,IAAA,GAAO,MAAA;AAAA,EACxD;AAAA,EAEQ,QAAQ,IAAA,EAAqB;AACnC,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,IAAA,CAAK,YAAA;AACpB,MAAA,IAAI,MAAA,IAAU,MAAA,CAAO,UAAA,KAAe,CAAA,EAAG;AACrC,QAAA,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,MAClC;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF","file":"chunk-6A6HE5LW.js","sourcesContent":["/**\n * WebSocketServerTransport — `ws`-based WebSocket server.\n *\n * Accepts one client at a time. Implements ServerTransport from @skaile/workspaces/types.\n * Built on the `ws` package + `node:http`, both of which run under Node and Bun.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport { listenWithProbe } from \"@skaile/workspaces/core\";\nimport type { AgentCommand, AgentEvent, ServerTransport } from \"@skaile/workspaces/types\";\nimport { type WebSocket, WebSocketServer } from \"ws\";\nimport { decodeBearerFromHeader, tokensEqual } from \"./auth.js\";\n\n/** @deprecated Moved to `@skaile/workspaces/core`, where `portIsHeld` replaces it. */\nexport { needsWildcardProbe } from \"@skaile/workspaces/core\";\n\n/**\n * Options for {@link WebSocketServerTransport}.\n *\n * @param port - TCP port to bind to; `0` picks a free port (read it back via `boundPort`).\n * @param onLog - Optional callback for internal log lines (client connect/disconnect events).\n * @param host - Address to bind. Default: every interface (Node's unspecified address).\n * `127.0.0.1` accepts local clients only.\n * @param authToken - When set, every upgrade must present this token via the\n * `skaile-bearer.<base64url>` subprotocol or the handshake is rejected with\n * HTTP 401. When unset, no auth is enforced (back-compatible default).\n * @param onListening - Called once the socket is bound, with the address a client\n * connects to (`url`) and whether it needs a token. For a plain-text ready line\n * (the `onLog` record carries the port only inside a log sentence).\n * @param heartbeatMs - Interval in milliseconds between pings to the active\n * client. Default: 30 000.\n * @param maxMissedPongs - Heartbeat ticks the client may go without a pong (or\n * any inbound frame) before its socket is terminated, so a client lost behind a\n * dead path (no FIN) fires `onDisconnect` instead of swallowing every event\n * sent to it. Default: 2, i.e. detection within 3 x `heartbeatMs`. `0` turns\n * the heartbeat off. Every WebSocket client answers pings on its own.\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport interface WebSocketServerOptions {\n port: number;\n host?: string;\n onLog?: (line: string) => void;\n authToken?: string;\n onListening?: (info: { url: string; port: number; tokenRequired: boolean }) => void;\n heartbeatMs?: number;\n maxMissedPongs?: number;\n}\n\nconst DEFAULT_HEARTBEAT_MS = 30_000;\nconst DEFAULT_MAX_MISSED_PONGS = 2;\n\n/**\n * Shape-validate a `session_init` frame at ingress before forwarding to the\n * runner. Protocol v3 requires the deterministic bootstrap envelope to carry\n * an identity tuple, a semver-shaped `protocolVersion`, a resolved config\n * object, and a `credentials` map keyed by `mounts` / `connectors`. Frames\n * that fail the guard are rejected on the wire so the runner never sees a\n * malformed boot envelope.\n *\n * Returns `null` when the frame is well-formed, or a human-readable reason\n * when it should be rejected.\n *\n * Spec: `_devlog/specs/2026-05-10-deterministic-session-bootstrap.md`\n * § \"SessionInitCommand schema\".\n *\n * @internal\n */\nfunction validateSessionInitShape(cmd: Record<string, unknown>): string | null {\n if (cmd.type !== \"session_init\") return \"type must be 'session_init'\";\n\n const identity = cmd.identity as Record<string, unknown> | undefined;\n if (!identity || typeof identity !== \"object\" || Array.isArray(identity)) {\n return \"identity must be an object\";\n }\n if (typeof identity.containerId !== \"string\") return \"identity.containerId must be a string\";\n if (typeof identity.sessionId !== \"string\") return \"identity.sessionId must be a string\";\n if (typeof identity.projectId !== \"string\") return \"identity.projectId must be a string\";\n\n const protocolVersion = cmd.protocolVersion as Record<string, unknown> | undefined;\n if (!protocolVersion || typeof protocolVersion !== \"object\" || Array.isArray(protocolVersion)) {\n return \"protocolVersion must be an object\";\n }\n if (typeof protocolVersion.major !== \"number\") return \"protocolVersion.major must be a number\";\n if (typeof protocolVersion.minor !== \"number\") return \"protocolVersion.minor must be a number\";\n if (typeof protocolVersion.patch !== \"number\") return \"protocolVersion.patch must be a number\";\n\n if (\n !cmd.resolvedConfig ||\n typeof cmd.resolvedConfig !== \"object\" ||\n Array.isArray(cmd.resolvedConfig)\n ) {\n return \"resolvedConfig must be an object\";\n }\n\n if (!cmd.credentials || typeof cmd.credentials !== \"object\" || Array.isArray(cmd.credentials)) {\n return \"credentials must be an object\";\n }\n\n return null;\n}\n\n/**\n * `ServerTransport` implementation backed by the `ws` package on top of a\n * `node:http` server.\n *\n * Enforces a single-client policy: if a new connection arrives while a client is\n * already connected, the stale connection is closed with code 4001 and replaced.\n * Incoming frames are parsed as JSON `AgentCommand` messages and fanned out to\n * registered `onCommand` handlers; outgoing `AgentEvent` objects are JSON-serialised\n * before sending. Call `listen()` to start accepting connections and `close()` to\n * shut down cleanly — `close()` terminates the active socket immediately rather\n * than keeping the event loop alive.\n *\n * @example\n * ```ts\n * const server = new WebSocketServerTransport({ port: 8080 });\n * await server.listen();\n * server.onCommand((cmd) => console.log(cmd));\n * server.send({ type: 'agent_end' });\n * await server.close();\n * ```\n *\n * @docLink packages/transport/dev-guide#websocket-server-transport\n */\nexport class WebSocketServerTransport implements ServerTransport {\n private httpServer: HttpServer | null = null;\n private wss: WebSocketServer | null = null;\n private activeClient: WebSocket | null = null;\n private readonly port: number;\n private readonly log: (line: string) => void;\n private readonly host?: string;\n private readonly authToken?: string;\n private readonly heartbeatMs: number;\n private readonly maxMissedPongs: number;\n\n private readonly onListening: WebSocketServerOptions[\"onListening\"];\n private readonly commandHandlers = new Set<(command: AgentCommand) => void>();\n private readonly connectHandlers = new Set<() => void>();\n private readonly disconnectHandlers = new Set<() => void>();\n\n constructor(options: WebSocketServerOptions) {\n this.port = options.port;\n this.log = options.onLog ?? console.log;\n this.host = options.host;\n this.authToken = options.authToken;\n this.onListening = options.onListening;\n this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;\n this.maxMissedPongs = options.maxMissedPongs ?? DEFAULT_MAX_MISSED_PONGS;\n }\n\n /** Start the server and accept connections. */\n async listen(): Promise<void> {\n const httpServer = createServer((_req, res) => {\n res.writeHead(200, { \"content-type\": \"text/plain\" });\n res.end(\"skaile agent server\");\n });\n // When an auth token is configured, reject the upgrade at handshake time\n // (HTTP 401) unless the client presents the matching bearer subprotocol.\n // Rejecting here — before `connection` — keeps unauthenticated sockets from\n // ever reaching the command handlers.\n const expected = this.authToken;\n const wss = new WebSocketServer({\n server: httpServer,\n verifyClient: expected\n ? (info, cb) => {\n const presented = decodeBearerFromHeader(info.req.headers[\"sec-websocket-protocol\"]);\n if (presented !== null && tokensEqual(presented, expected)) {\n cb(true);\n } else {\n this.log(\"[ws-server] rejecting unauthenticated upgrade\");\n cb(false, 401, \"Unauthorized\");\n }\n }\n : undefined,\n });\n wss.on(\"connection\", (ws) => this.handleConnection(ws));\n // `ws` re-emits the http server's errors on `wss`; unhandled during the bind,\n // that throws before the reject below runs and `listen()` hangs. Scoped to the\n // bind so a post-startup server error still surfaces loudly.\n const swallowBindError = () => {};\n wss.on(\"error\", swallowBindError);\n this.httpServer = httpServer;\n this.wss = wss;\n\n try {\n await listenWithProbe(httpServer, this.port, this.host);\n } catch (err) {\n wss.close();\n this.wss = null;\n this.httpServer = null;\n if (err instanceof Error && \"code\" in err && err.code === \"EADDRINUSE\") {\n const where = this.host ? `${this.host}:${this.port}` : `port ${this.port}`;\n throw Object.assign(\n new Error(\n `${where} is already in use: another process listens there. Pick another port.`,\n ),\n { code: \"EADDRINUSE\" },\n );\n }\n throw err;\n } finally {\n wss.off(\"error\", swallowBindError);\n }\n\n const addr = httpServer.address();\n const boundPort = typeof addr === \"object\" && addr ? addr.port : this.port;\n const bind = this.host ?? \"all interfaces\";\n this.log(\n `[ws-server] listening on port ${boundPort} (${bind}; ${this.authToken ? \"token required\" : \"no authentication\"})`,\n );\n // A wildcard bind is reachable on loopback; that is the URL worth printing.\n const urlHost =\n !this.host || this.host === \"0.0.0.0\" || this.host === \"::\"\n ? \"127.0.0.1\"\n : this.host.includes(\":\") && !this.host.startsWith(\"[\")\n ? `[${this.host}]`\n : this.host;\n this.onListening?.({\n url: `ws://${urlHost}:${boundPort}`,\n port: boundPort,\n tokenRequired: this.authToken !== undefined,\n });\n }\n\n private handleConnection(ws: WebSocket): void {\n if (this.activeClient) {\n // Close the stale connection instead of rejecting the new one.\n // This handles the race where waitForWsReady's probe hasn't\n // fully disconnected before the real gateway client connects.\n this.log(\"[ws-server] replacing previous connection\");\n try {\n this.activeClient.close(4001, \"Replaced by new connection\");\n } catch {\n // already closed\n }\n }\n this.log(\"[ws-server] client connected\");\n this.activeClient = ws;\n for (const handler of this.connectHandlers) {\n handler();\n }\n\n const stopHeartbeat = this.startHeartbeat(ws);\n\n ws.on(\"message\", (data) => {\n try {\n const parsed = JSON.parse(String(data)) as Record<string, unknown>;\n // Protocol v3: shape-validate `session_init` at ingress so the\n // runner never receives a malformed boot envelope. Other command\n // shapes pass through unchanged — invalid `AgentCommand`s surface\n // through the existing per-command handlers in serve.ts.\n if (parsed && typeof parsed === \"object\" && parsed.type === \"session_init\") {\n const reason = validateSessionInitShape(parsed);\n if (reason !== null) {\n this.log(`[ws-server] rejecting invalid session_init: ${reason}`);\n this.sendRaw({\n type: \"error\",\n message: `Invalid session_init: ${reason}`,\n fatal: false,\n });\n return;\n }\n }\n const cmd = parsed as unknown as AgentCommand;\n for (const handler of this.commandHandlers) {\n handler(cmd);\n }\n } catch (err) {\n this.sendRaw({\n type: \"error\",\n message: `Invalid message: ${err instanceof Error ? err.message : String(err)}`,\n fatal: false,\n });\n }\n });\n\n ws.on(\"close\", () => {\n stopHeartbeat();\n this.log(\"[ws-server] client disconnected\");\n if (this.activeClient === ws) {\n this.activeClient = null;\n for (const handler of this.disconnectHandlers) {\n handler();\n }\n }\n });\n }\n\n /**\n * Ping `ws` every {@link heartbeatMs}; terminate it after\n * {@link maxMissedPongs} unanswered ticks. `terminate()` makes `ws` emit\n * `close`, which runs the normal disconnect path. Returns the stop function.\n */\n private startHeartbeat(ws: WebSocket): () => void {\n if (this.maxMissedPongs <= 0) return () => {};\n let missed = 0;\n const markAlive = () => {\n missed = 0;\n };\n ws.on(\"pong\", markAlive);\n ws.on(\"message\", markAlive);\n const timer = setInterval(() => {\n if (ws.readyState !== ws.OPEN) return;\n if (missed >= this.maxMissedPongs) {\n this.log(\n `[ws-server] no pong for ${missed} heartbeats of ${this.heartbeatMs}ms; terminating client`,\n );\n clearInterval(timer);\n ws.terminate();\n return;\n }\n missed++;\n try {\n ws.ping();\n } catch {\n // socket closing underneath us; the close event cleans up\n }\n }, this.heartbeatMs);\n timer.unref?.();\n return () => clearInterval(timer);\n }\n\n /**\n * Stop the server.\n *\n * Terminates the active socket immediately — otherwise a still-connected\n * client would keep the event loop alive and block process shutdown.\n */\n async close(): Promise<void> {\n try {\n this.activeClient?.terminate();\n } catch {\n // already closed\n }\n this.activeClient = null;\n this.wss?.close();\n this.wss = null;\n const httpServer = this.httpServer;\n this.httpServer = null;\n if (httpServer) {\n httpServer.closeAllConnections();\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n send(event: AgentEvent): void {\n this.sendRaw(event);\n }\n\n onCommand(handler: (command: AgentCommand) => void): () => void {\n this.commandHandlers.add(handler);\n return () => {\n this.commandHandlers.delete(handler);\n };\n }\n\n onConnect(handler: () => void): () => void {\n this.connectHandlers.add(handler);\n return () => {\n this.connectHandlers.delete(handler);\n };\n }\n\n onDisconnect(handler: () => void): () => void {\n this.disconnectHandlers.add(handler);\n return () => {\n this.disconnectHandlers.delete(handler);\n };\n }\n\n get connected(): boolean {\n return this.activeClient !== null;\n }\n\n /** The port actually bound by `listen()` (resolves `port: 0`); `undefined` when not listening. */\n get boundPort(): number | undefined {\n const addr = this.httpServer?.address();\n return typeof addr === \"object\" && addr ? addr.port : undefined;\n }\n\n private sendRaw(data: unknown): void {\n try {\n const client = this.activeClient;\n if (client && client.readyState === 1) {\n client.send(JSON.stringify(data));\n }\n } catch {\n // Silently ignore send failures\n }\n }\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { factoryAssetRoots, resolveWorkspacesPackageRoot } from './chunk-
|
|
1
|
+
import { factoryAssetRoots, resolveWorkspacesPackageRoot } from './chunk-JIQUF3X2.js';
|
|
2
2
|
import { deployedBase } from './chunk-5KRMNUL4.js';
|
|
3
3
|
import { DEFAULT_HARNESS_ID } from './chunk-C5HFK34M.js';
|
|
4
|
-
import { ConfigParseError, valueShapeError, parseSourceEntries, resolveSkWorkspaceConfig } from './chunk-
|
|
4
|
+
import { ConfigParseError, valueShapeError, parseSourceEntries, resolveSkWorkspaceConfig } from './chunk-6SXSVKQY.js';
|
|
5
5
|
import { globalSkaileDir } from './chunk-L5VSIPQD.js';
|
|
6
6
|
import { harnessNameForHarnessId, SUPPORTED_HARNESSES } from './chunk-VTETQCR6.js';
|
|
7
|
-
import { sha256Hex, compositeSha256, orderedAsset, ASSET_KEY_ORDER } from './chunk-
|
|
8
|
-
import { yamlQuotingHint } from './chunk-
|
|
7
|
+
import { sha256Hex, compositeSha256, orderedAsset, ASSET_KEY_ORDER } from './chunk-E2EC6SAX.js';
|
|
8
|
+
import { yamlQuotingHint } from './chunk-GNUA7XT2.js';
|
|
9
9
|
import { parseAssetRefLoose } from './chunk-V5KG6CCV.js';
|
|
10
10
|
import { validateRegistry, mergeRegistries, validateMergedRegistry } from './chunk-RXGPFAMN.js';
|
|
11
11
|
import fs, { existsSync, readFileSync, writeFileSync, realpathSync, renameSync, rmSync, mkdirSync, cpSync, readdirSync } from 'fs';
|
|
@@ -1601,5 +1601,5 @@ var WorkspaceYamlEditor = class _WorkspaceYamlEditor {
|
|
|
1601
1601
|
};
|
|
1602
1602
|
|
|
1603
1603
|
export { AGENT_PROMPT_SECTION_ORDER, FACTORY_SOURCE_URL, GLOBAL_INSTALL_DIR_NAME, LOCK_FACTORY_URL, LOCK_SCHEMA_VERSION, LockParseError, ManifestYamlEditor, PROVIDERS_FILENAME, SETTINGS_DEFAULTS, WorkspaceYamlEditor, aiResourceRoots, applyPatch, assertLockHashes, buildAgentPromptSections, buildLibraryLockEntries, buildLockFile, capMemoryContent, checkAgentSkillConformance, computeHash, connectProbe, defaultLibraryDir, detectPackageManager, extractForPatch, findProjectPackageDir, generatePatch, globalInstallDir, globalSettingsPath, harnessSdkMissingError, holdsWildcard, installCommand, installCommandFor, isFactoryMemorySource, listPatches, listenWithProbe, loadHarnessSdk, loadHarnessSdkSubpath, loadProviderRegistry, loadProviderRegistryFile, loadSettings, mapLegacyFields, maskApiKey, mergeLockFile, migrateSettings, needsWildcardProbe, portIsHeld, probeHosts, projectAssetRoots, projectSettingsPath, readLock, readPatch, readPluginsLockSlice, renderMemorySection, resolveConfiguredHarnessName, resolveLibraryDir, resolveSettings, resolveSettingsWithSources, resyncLockSources, savePatch, saveProviderRegistryFile, saveSettings, sdkLookupPlaces, skaileInstallDir, userProviderRegistryPath, verifyLock, verifyLockEntry, walkForSettings, workspaceProviderRegistryPath, writeLock, writePluginsLockSlice };
|
|
1604
|
-
//# sourceMappingURL=chunk-
|
|
1605
|
-
//# sourceMappingURL=chunk-
|
|
1604
|
+
//# sourceMappingURL=chunk-6ARPZHOV.js.map
|
|
1605
|
+
//# sourceMappingURL=chunk-6ARPZHOV.js.map
|