@skaile/workspaces 3.16.2 → 3.18.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 +69 -0
- package/dist/app-sdk/index.js +1 -1
- package/dist/{asset-feeds-FQT6GH24.js → asset-feeds-LT4GI576.js} +7 -7
- package/dist/{asset-feeds-FQT6GH24.js.map → asset-feeds-LT4GI576.js.map} +1 -1
- package/dist/asset-manager/catalog-deployer.js +3 -3
- package/dist/asset-manager/index.js +6 -6
- package/dist/asset-manager/installer.js +4 -4
- package/dist/bridge/codex-app-server.d.ts +2 -0
- package/dist/bridge/codex-app-server.js +5 -0
- package/dist/bridge/codex-app-server.js.map +1 -0
- package/dist/bridge/drivers/claude-sdk.js +5 -5
- package/dist/bridge/drivers/codex.js +1137 -4
- package/dist/bridge/drivers/codex.js.map +1 -1
- package/dist/bridge/drivers/continue.js +2 -2
- package/dist/bridge/drivers/echo.js +2 -2
- package/dist/bridge/drivers/gemini.js +3 -3
- package/dist/bridge/drivers/goose.js +2 -2
- package/dist/bridge/drivers/omp.js +1 -1
- package/dist/bridge/drivers/opencode.js +2 -2
- package/dist/bridge/drivers/qwen.js +3 -3
- package/dist/bridge/index.js +7 -6
- package/dist/bridge/src/capability-dispatch.d.ts +1 -1
- package/dist/bridge/src/capability-dispatch.d.ts.map +1 -1
- package/dist/bridge/src/codex/credentials.d.ts +12 -0
- package/dist/bridge/src/codex/credentials.d.ts.map +1 -0
- package/dist/bridge/src/codex/inputs.d.ts +8 -0
- package/dist/bridge/src/codex/inputs.d.ts.map +1 -0
- package/dist/bridge/src/codex/instance-state.d.ts +16 -0
- package/dist/bridge/src/codex/instance-state.d.ts.map +1 -0
- package/dist/bridge/src/codex/managed-driver.d.ts +71 -0
- package/dist/bridge/src/codex/managed-driver.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-approvals.d.ts +22 -0
- package/dist/bridge/src/codex/native-approvals.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-policy.d.ts +38 -0
- package/dist/bridge/src/codex/native-policy.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-protocol.generated.d.ts +1411 -0
- package/dist/bridge/src/codex/native-protocol.generated.d.ts.map +1 -0
- package/dist/bridge/src/codex/native-state.d.ts +7 -0
- package/dist/bridge/src/codex/native-state.d.ts.map +1 -0
- package/dist/bridge/src/codex/questions.d.ts +19 -0
- package/dist/bridge/src/codex/questions.d.ts.map +1 -0
- package/dist/bridge/src/codex-app-server.d.ts +60 -0
- package/dist/bridge/src/codex-app-server.d.ts.map +1 -0
- package/dist/bridge/src/drivers/codex.d.ts +9 -1
- package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
- package/dist/bridge/src/index.d.ts +1 -0
- package/dist/bridge/src/index.d.ts.map +1 -1
- package/dist/bridge/src/normalizer.d.ts.map +1 -1
- package/dist/bridge/src/runtime.d.ts +3 -2
- package/dist/bridge/src/runtime.d.ts.map +1 -1
- package/dist/bridge/src/types.d.ts +77 -4
- package/dist/bridge/src/types.d.ts.map +1 -1
- package/dist/{chunk-2KBA5OO2.js → chunk-23L3P7IB.js} +4 -4
- package/dist/{chunk-2KBA5OO2.js.map → chunk-23L3P7IB.js.map} +1 -1
- package/dist/{chunk-NC5YXVZF.js → chunk-2FDKUKDE.js} +3 -3
- package/dist/{chunk-NC5YXVZF.js.map → chunk-2FDKUKDE.js.map} +1 -1
- package/dist/{chunk-GRVSSAAQ.js → chunk-2IROGAS7.js} +2 -2
- package/dist/{chunk-GRVSSAAQ.js.map → chunk-2IROGAS7.js.map} +1 -1
- package/dist/{chunk-BUKPS4YL.js → chunk-4JDCKPTM.js} +2 -2
- package/dist/{chunk-BUKPS4YL.js.map → chunk-4JDCKPTM.js.map} +1 -1
- package/dist/{chunk-5KSIVLOX.js → chunk-5T7Y3AZU.js} +3 -3
- package/dist/{chunk-5KSIVLOX.js.map → chunk-5T7Y3AZU.js.map} +1 -1
- package/dist/{chunk-SWUVULBU.js → chunk-67KB2A77.js} +7 -7
- package/dist/{chunk-SWUVULBU.js.map → chunk-67KB2A77.js.map} +1 -1
- package/dist/{chunk-RRVQAE5D.js → chunk-7ZO3MUP3.js} +6 -5
- package/dist/chunk-7ZO3MUP3.js.map +1 -0
- package/dist/{chunk-EO7IJY3Q.js → chunk-AI2PZX4H.js} +3 -3
- package/dist/{chunk-EO7IJY3Q.js.map → chunk-AI2PZX4H.js.map} +1 -1
- package/dist/{chunk-CMGYUF5P.js → chunk-BJI7IGZF.js} +7 -7
- package/dist/{chunk-CMGYUF5P.js.map → chunk-BJI7IGZF.js.map} +1 -1
- package/dist/{chunk-HY6CIKFH.js → chunk-CP62G5PK.js} +8 -7
- package/dist/chunk-CP62G5PK.js.map +1 -0
- package/dist/{chunk-PBRMY64S.js → chunk-CQY7XKRV.js} +30 -9
- package/dist/chunk-CQY7XKRV.js.map +1 -0
- package/dist/{chunk-OIDFK7SG.js → chunk-DCG4UWJM.js} +3 -3
- package/dist/{chunk-OIDFK7SG.js.map → chunk-DCG4UWJM.js.map} +1 -1
- package/dist/{chunk-VT4MPCRK.js → chunk-FBD5SOIH.js} +3 -3
- package/dist/{chunk-VT4MPCRK.js.map → chunk-FBD5SOIH.js.map} +1 -1
- package/dist/{chunk-DUMD4AA2.js → chunk-FYVDBIZV.js} +6 -6
- package/dist/{chunk-DUMD4AA2.js.map → chunk-FYVDBIZV.js.map} +1 -1
- package/dist/{chunk-RTM4SFLA.js → chunk-GMKNGIIB.js} +7 -4
- package/dist/chunk-GMKNGIIB.js.map +1 -0
- package/dist/{chunk-NIQPSQFN.js → chunk-GMMACS5W.js} +4 -4
- package/dist/{chunk-NIQPSQFN.js.map → chunk-GMMACS5W.js.map} +1 -1
- package/dist/{chunk-QFYEDM63.js → chunk-GRA4BWCV.js} +5 -5
- package/dist/{chunk-QFYEDM63.js.map → chunk-GRA4BWCV.js.map} +1 -1
- package/dist/{chunk-CIPK5JMJ.js → chunk-H662SXKS.js} +4 -4
- package/dist/{chunk-CIPK5JMJ.js.map → chunk-H662SXKS.js.map} +1 -1
- package/dist/{chunk-4W2OOVN5.js → chunk-IST2V7RU.js} +3 -3
- package/dist/{chunk-4W2OOVN5.js.map → chunk-IST2V7RU.js.map} +1 -1
- package/dist/{chunk-L2UE4YJO.js → chunk-IT42ZT3T.js} +9 -9
- package/dist/{chunk-L2UE4YJO.js.map → chunk-IT42ZT3T.js.map} +1 -1
- package/dist/{chunk-FRER2PEN.js → chunk-J7VSUGGH.js} +16 -3
- package/dist/chunk-J7VSUGGH.js.map +1 -0
- package/dist/{chunk-ZNCZ7RUO.js → chunk-JVFZ5R7L.js} +3 -3
- package/dist/{chunk-ZNCZ7RUO.js.map → chunk-JVFZ5R7L.js.map} +1 -1
- package/dist/chunk-LOO5CB5M.js +12 -0
- package/dist/chunk-LOO5CB5M.js.map +1 -0
- package/dist/{chunk-AL6BU3SP.js → chunk-OJ4N4T4H.js} +12 -4
- package/dist/chunk-OJ4N4T4H.js.map +1 -0
- package/dist/chunk-QEZPFJX3.js +254 -0
- package/dist/chunk-QEZPFJX3.js.map +1 -0
- package/dist/{chunk-C4AP3XZB.js → chunk-QG2ZXU5W.js} +4 -4
- package/dist/{chunk-C4AP3XZB.js.map → chunk-QG2ZXU5W.js.map} +1 -1
- package/dist/{chunk-I3COVVFB.js → chunk-RJOGAEIY.js} +1024 -204
- package/dist/chunk-RJOGAEIY.js.map +1 -0
- package/dist/{chunk-OZ7UNMTK.js → chunk-RPVTPVGX.js} +7 -5
- package/dist/chunk-RPVTPVGX.js.map +1 -0
- package/dist/{chunk-S4UA244H.js → chunk-S635WMXH.js} +4 -4
- package/dist/{chunk-S4UA244H.js.map → chunk-S635WMXH.js.map} +1 -1
- package/dist/{chunk-BQMRUH7J.js → chunk-SFCVAEJ5.js} +4 -4
- package/dist/{chunk-BQMRUH7J.js.map → chunk-SFCVAEJ5.js.map} +1 -1
- package/dist/{chunk-EXT2LNST.js → chunk-SH7GXS7V.js} +125 -37
- package/dist/chunk-SH7GXS7V.js.map +1 -0
- package/dist/chunk-T3YLZQS4.js +336 -0
- package/dist/chunk-T3YLZQS4.js.map +1 -0
- package/dist/{chunk-X7FJ3ES3.js → chunk-X3LUB275.js} +4 -4
- package/dist/{chunk-X7FJ3ES3.js.map → chunk-X3LUB275.js.map} +1 -1
- package/dist/chunk-XKSGEYZC.js +244 -0
- package/dist/chunk-XKSGEYZC.js.map +1 -0
- package/dist/{chunk-SUPIVTLX.js → chunk-XL45U3C5.js} +3 -3
- package/dist/{chunk-SUPIVTLX.js.map → chunk-XL45U3C5.js.map} +1 -1
- package/dist/{chunk-AXEOG4B5.js → chunk-YCMHWNZA.js} +3 -3
- package/dist/{chunk-AXEOG4B5.js.map → chunk-YCMHWNZA.js.map} +1 -1
- package/dist/cli/index.js +75 -53
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/serve.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/src/client.d.ts +3 -2
- package/dist/client/src/client.d.ts.map +1 -1
- package/dist/connectors/config.js +4 -4
- package/dist/connectors/index.js +12 -11
- package/dist/connectors/src/connector-prompt.d.ts +11 -0
- package/dist/connectors/src/connector-prompt.d.ts.map +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/runtime-assets.js +2 -2
- package/dist/core/src/workspace-config.d.ts.map +1 -1
- package/dist/core/workspace-config.js +1 -1
- package/dist/deploy/index.js +3 -3
- package/dist/{ensure-sources-4GBXK6UR.js → ensure-sources-4BQ6GMYG.js} +9 -9
- package/dist/{ensure-sources-4GBXK6UR.js.map → ensure-sources-4BQ6GMYG.js.map} +1 -1
- package/dist/factory-assets/connectors/box.js +12 -11
- package/dist/factory-assets/connectors/deploy.js +12 -11
- package/dist/factory-assets/connectors/devserver.js +12 -11
- package/dist/factory-assets/connectors/flow/CONNECTOR.md +4 -5
- package/dist/factory-assets/connectors/flow/adapter.d.ts +8 -0
- package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/adapter.js +12 -11
- package/dist/factory-assets/connectors/flow/contract/index.d.ts +1 -1
- package/dist/factory-assets/connectors/flow/contract/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts +50 -17
- package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/contract.js +3 -3
- package/dist/factory-assets/connectors/flow/engine/index.d.ts +1 -0
- package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow/engine.js +5 -5
- package/dist/factory-assets/connectors/flow/run-flow.js +13 -12
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts +10 -1
- package/dist/factory-assets/connectors/flow/subprompt-runner.d.ts.map +1 -1
- package/dist/factory-assets/connectors/flow.js +12 -11
- package/dist/factory-assets/connectors/git.js +12 -11
- package/dist/factory-assets/connectors/gmail.js +12 -11
- package/dist/factory-assets/connectors/googledrive.js +12 -11
- package/dist/factory-assets/connectors/local.js +12 -11
- package/dist/factory-assets/connectors/mattermost.js +12 -11
- package/dist/factory-assets/connectors/memory.js +12 -11
- package/dist/factory-assets/connectors/minio.js +12 -11
- package/dist/factory-assets/connectors/postgres.js +12 -11
- package/dist/factory-assets/connectors/s3.js +12 -11
- package/dist/factory-assets/connectors/sharepoint.js +12 -11
- package/dist/factory-assets/connectors/sqlite.js +12 -11
- package/dist/factory-assets/connectors/static-server.js +12 -11
- package/dist/factory-assets/connectors/tunnel.js +12 -11
- package/dist/factory-assets/connectors/webdav.js +12 -11
- package/dist/factory-assets/connectors/xstate-store.js +12 -11
- package/dist/factory-assets/connectors/xstate.js +12 -11
- package/dist/{flows-2SUFOVVO.js → flows-COBS26MK.js} +6 -6
- package/dist/{flows-2SUFOVVO.js.map → flows-COBS26MK.js.map} +1 -1
- package/dist/{helpers-26LW4IXY.js → helpers-4UJMSN6T.js} +9 -9
- package/dist/{helpers-26LW4IXY.js.map → helpers-4UJMSN6T.js.map} +1 -1
- package/dist/library/index.js +4 -4
- package/dist/library/install.js +3 -3
- package/dist/{library-publish-IYGHOPEK.js → library-publish-QHQDN4IC.js} +14 -14
- package/dist/{library-publish-IYGHOPEK.js.map → library-publish-QHQDN4IC.js.map} +1 -1
- package/dist/{open-library-HH5LST7Z.js → open-library-ZDM6DUKJ.js} +12 -12
- package/dist/{open-library-HH5LST7Z.js.map → open-library-ZDM6DUKJ.js.map} +1 -1
- package/dist/{paths-4APA2PKT.js → paths-CZPCCBYX.js} +6 -6
- package/dist/{paths-4APA2PKT.js.map → paths-CZPCCBYX.js.map} +1 -1
- package/dist/{plugin-store-JQAIKR3R.js → plugin-store-ZEIBSNTO.js} +5 -5
- package/dist/{plugin-store-JQAIKR3R.js.map → plugin-store-ZEIBSNTO.js.map} +1 -1
- package/dist/{project-U5QRUCBN.js → project-JJUAAI7N.js} +10 -10
- package/dist/{project-U5QRUCBN.js.map → project-JJUAAI7N.js.map} +1 -1
- package/dist/resolver/index.js +3 -3
- package/dist/runner/index.js +25 -16
- package/dist/runner/src/capability-registry.d.ts +4 -0
- package/dist/runner/src/capability-registry.d.ts.map +1 -1
- package/dist/runner/src/codex-init.d.ts +5 -0
- package/dist/runner/src/codex-init.d.ts.map +1 -0
- package/dist/runner/src/codex-resource-tools.d.ts +16 -0
- package/dist/runner/src/codex-resource-tools.d.ts.map +1 -0
- package/dist/runner/src/codex-tools.d.ts +12 -0
- package/dist/runner/src/codex-tools.d.ts.map +1 -0
- package/dist/runner/src/define-capability.d.ts +2 -0
- package/dist/runner/src/define-capability.d.ts.map +1 -1
- package/dist/runner/src/external-mcp.d.ts +8 -3
- package/dist/runner/src/external-mcp.d.ts.map +1 -1
- package/dist/runner/src/index.d.ts +2 -0
- package/dist/runner/src/index.d.ts.map +1 -1
- package/dist/runner/src/managed-codex-bootstrap.d.ts +54 -0
- package/dist/runner/src/managed-codex-bootstrap.d.ts.map +1 -0
- package/dist/runner/src/resources.d.ts +1 -0
- package/dist/runner/src/resources.d.ts.map +1 -1
- package/dist/runner/src/serve.d.ts +5 -0
- package/dist/runner/src/serve.d.ts.map +1 -1
- package/dist/runner/src/session-builder.d.ts +18 -1
- package/dist/runner/src/session-builder.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +6 -6
- package/dist/sdk/bridge.js +7 -6
- package/dist/sdk/client.js +1 -1
- package/dist/sdk/core.js +3 -3
- package/dist/sdk/flow.js +5 -5
- package/dist/sdk/index.js +26 -17
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/resolver.js +3 -3
- package/dist/sdk/runner.js +25 -16
- package/dist/sdk/session.js +2 -2
- package/dist/sdk/store.js +1 -1
- package/dist/sdk/types.js +1 -1
- package/dist/session/index.js +2 -2
- package/dist/{setup-XKTBHRWM.js → setup-PS6ZERZ5.js} +9 -8
- package/dist/setup-PS6ZERZ5.js.map +1 -0
- package/dist/{skills-Q7PJVB3W.js → skills-LG6IQSSN.js} +5 -5
- package/dist/{skills-Q7PJVB3W.js.map → skills-LG6IQSSN.js.map} +1 -1
- package/dist/{source-6PBVTACZ.js → source-QLDIZCI3.js} +13 -13
- package/dist/{source-6PBVTACZ.js.map → source-QLDIZCI3.js.map} +1 -1
- package/dist/{source-manifest-publish-VBVNW7KL.js → source-manifest-publish-J3AVCTLX.js} +15 -15
- package/dist/{source-manifest-publish-VBVNW7KL.js.map → source-manifest-publish-J3AVCTLX.js.map} +1 -1
- package/dist/store/index.js +1 -1
- package/dist/store/react.js +1 -1
- package/dist/store/react.js.map +1 -1
- package/dist/store/src/react.d.ts +1 -1
- package/dist/store/src/react.d.ts.map +1 -1
- package/dist/store/src/store.d.ts +2 -1
- package/dist/store/src/store.d.ts.map +1 -1
- package/dist/tui/index.js +25 -16
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/src/events.d.ts +14 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/lifecycle.d.ts +43 -0
- package/dist/types/src/lifecycle.d.ts.map +1 -1
- package/dist/types/src/protocol.d.ts +18 -0
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/runtime.d.ts +3 -1
- package/dist/types/src/runtime.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +7 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/dist/workspace-plugin/adapters/mcp.js +9 -255
- package/dist/workspace-plugin/adapters/mcp.js.map +1 -1
- package/dist/workspace-plugin/adapters/omp.js +7 -7
- package/dist/workspace-plugin/index.js +1 -1
- package/package.json +10 -1
- package/dist/chunk-AL6BU3SP.js.map +0 -1
- package/dist/chunk-EXT2LNST.js.map +0 -1
- package/dist/chunk-FRER2PEN.js.map +0 -1
- package/dist/chunk-HY6CIKFH.js.map +0 -1
- package/dist/chunk-I3COVVFB.js.map +0 -1
- package/dist/chunk-OZ7UNMTK.js.map +0 -1
- package/dist/chunk-PBRMY64S.js.map +0 -1
- package/dist/chunk-RRVQAE5D.js.map +0 -1
- package/dist/chunk-RTM4SFLA.js.map +0 -1
- package/dist/setup-XKTBHRWM.js.map +0 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { CodexNativeStateUnavailable } from './chunk-LOO5CB5M.js';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
|
|
4
|
+
var CodexAppServerError = class extends Error {
|
|
5
|
+
constructor(code) {
|
|
6
|
+
super(`Codex App Server ${code}`);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "CodexAppServerError";
|
|
9
|
+
}
|
|
10
|
+
code;
|
|
11
|
+
};
|
|
12
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
var validId = (value) => typeof value === "string" || typeof value === "number" && Number.isSafeInteger(value);
|
|
14
|
+
function bound(value, fallback, maximum) {
|
|
15
|
+
const result = value ?? fallback;
|
|
16
|
+
if (!Number.isSafeInteger(result) || result <= 0 || result > maximum)
|
|
17
|
+
throw new CodexAppServerError("protocol");
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
var CodexAppServerClient = class _CodexAppServerClient {
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.timeout = bound(options.requestTimeoutMs, 15e3, 3e5);
|
|
24
|
+
this.callbackTimeout = bound(options.serverRequestTimeoutMs, 3e4, 3e5);
|
|
25
|
+
this.capacity = bound(options.maxPendingRequests, 64, 1024);
|
|
26
|
+
this.frameLimit = bound(options.maxFrameBytes, 1024 * 1024, 16 * 1024 * 1024);
|
|
27
|
+
this.child = spawn(options.command, options.args, {
|
|
28
|
+
cwd: options.cwd,
|
|
29
|
+
env: options.env,
|
|
30
|
+
stdio: "pipe",
|
|
31
|
+
detached: options.processGroup === true && process.platform !== "win32"
|
|
32
|
+
});
|
|
33
|
+
this.closed = new Promise(
|
|
34
|
+
(resolve) => this.child.once("close", () => {
|
|
35
|
+
this.childClosed = true;
|
|
36
|
+
resolve();
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
this.child.stdout.setEncoding("utf8");
|
|
40
|
+
this.child.stdout.on("data", (chunk) => this.receive(chunk));
|
|
41
|
+
this.child.stderr.resume();
|
|
42
|
+
this.child.on("error", () => this.fail("process"));
|
|
43
|
+
this.child.stdin.on("error", () => this.fail("process"));
|
|
44
|
+
this.child.stdout.on("error", () => this.fail("process"));
|
|
45
|
+
this.child.stderr.on("error", () => this.fail("process"));
|
|
46
|
+
this.child.once("exit", () => this.fail("process"));
|
|
47
|
+
}
|
|
48
|
+
options;
|
|
49
|
+
pending = /* @__PURE__ */ new Map();
|
|
50
|
+
callbacks = /* @__PURE__ */ new Map();
|
|
51
|
+
timeout;
|
|
52
|
+
callbackTimeout;
|
|
53
|
+
capacity;
|
|
54
|
+
frameLimit;
|
|
55
|
+
child;
|
|
56
|
+
closed;
|
|
57
|
+
terminal = false;
|
|
58
|
+
childClosed = false;
|
|
59
|
+
nextId = 1;
|
|
60
|
+
buffer = "";
|
|
61
|
+
/** Spawn and finish the native initialize/initialized handshake before returning. */
|
|
62
|
+
static async connect(options, signal) {
|
|
63
|
+
let client;
|
|
64
|
+
const abort = () => client?.fail("closed");
|
|
65
|
+
try {
|
|
66
|
+
if (signal?.aborted) throw new CodexAppServerError("closed");
|
|
67
|
+
client = new _CodexAppServerClient(options);
|
|
68
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
69
|
+
await client.request("initialize", {
|
|
70
|
+
clientInfo: options.clientInfo,
|
|
71
|
+
capabilities: { experimentalApi: true }
|
|
72
|
+
});
|
|
73
|
+
client.write({ method: "initialized" });
|
|
74
|
+
return client;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
await client?.dispose();
|
|
77
|
+
throw error instanceof CodexAppServerError ? error : new CodexAppServerError("process");
|
|
78
|
+
} finally {
|
|
79
|
+
signal?.removeEventListener("abort", abort);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Timeout closes this generation, so late replies can never satisfy a later request. */
|
|
83
|
+
request(method, params) {
|
|
84
|
+
if (this.terminal) return Promise.reject(new CodexAppServerError("closed"));
|
|
85
|
+
if (this.pending.size >= this.capacity)
|
|
86
|
+
return Promise.reject(new CodexAppServerError("capacity"));
|
|
87
|
+
const id = this.nextId++;
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const timer = setTimeout(() => this.fail("timeout"), this.timeout);
|
|
90
|
+
this.pending.set(id, {
|
|
91
|
+
resolve,
|
|
92
|
+
reject,
|
|
93
|
+
timer,
|
|
94
|
+
...method === "thread/resume" && params !== null && "threadId" in params && typeof params.threadId === "string" ? { resumeThreadId: params.threadId } : {}
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
this.write({ id, method, params });
|
|
98
|
+
} catch {
|
|
99
|
+
this.fail("protocol");
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Reject pending work immediately and wait a bounded interval for process shutdown. */
|
|
104
|
+
async dispose() {
|
|
105
|
+
this.fail("closed");
|
|
106
|
+
let timer;
|
|
107
|
+
await Promise.race([
|
|
108
|
+
this.closed,
|
|
109
|
+
new Promise((resolve) => {
|
|
110
|
+
timer = setTimeout(resolve, 1e3);
|
|
111
|
+
})
|
|
112
|
+
]);
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
}
|
|
115
|
+
/** Positive exit proof for callers that may replace a failed native generation. */
|
|
116
|
+
get terminationConfirmed() {
|
|
117
|
+
if (!this.childClosed) return false;
|
|
118
|
+
if (!this.options.processGroup || process.platform === "win32" || !this.child.pid) return true;
|
|
119
|
+
try {
|
|
120
|
+
process.kill(-this.child.pid, 0);
|
|
121
|
+
return false;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return error.code === "ESRCH";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
fail(code) {
|
|
127
|
+
if (this.terminal) return;
|
|
128
|
+
this.terminal = true;
|
|
129
|
+
this.buffer = "";
|
|
130
|
+
for (const request of this.pending.values()) {
|
|
131
|
+
clearTimeout(request.timer);
|
|
132
|
+
request.reject(new CodexAppServerError(code));
|
|
133
|
+
}
|
|
134
|
+
this.pending.clear();
|
|
135
|
+
for (const controller of this.callbacks.values()) controller.abort();
|
|
136
|
+
this.callbacks.clear();
|
|
137
|
+
if (this.options.processGroup && process.platform !== "win32" && this.child.pid) {
|
|
138
|
+
try {
|
|
139
|
+
process.kill(-this.child.pid, "SIGKILL");
|
|
140
|
+
} catch {
|
|
141
|
+
this.child.kill("SIGKILL");
|
|
142
|
+
}
|
|
143
|
+
} else this.child.kill("SIGKILL");
|
|
144
|
+
try {
|
|
145
|
+
this.options.onClose?.(new CodexAppServerError(code));
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
write(frame) {
|
|
150
|
+
if (this.terminal) throw new CodexAppServerError("closed");
|
|
151
|
+
const line = `${JSON.stringify(frame)}
|
|
152
|
+
`;
|
|
153
|
+
if (Buffer.byteLength(line) > this.frameLimit) throw new CodexAppServerError("protocol");
|
|
154
|
+
this.child.stdin.write(line);
|
|
155
|
+
}
|
|
156
|
+
receive(chunk) {
|
|
157
|
+
if (this.terminal) return;
|
|
158
|
+
this.buffer += chunk;
|
|
159
|
+
try {
|
|
160
|
+
for (; ; ) {
|
|
161
|
+
const end = this.buffer.indexOf("\n");
|
|
162
|
+
if (end < 0) break;
|
|
163
|
+
const line = this.buffer.slice(0, end);
|
|
164
|
+
this.buffer = this.buffer.slice(end + 1);
|
|
165
|
+
if (Buffer.byteLength(line) > this.frameLimit) throw new CodexAppServerError("protocol");
|
|
166
|
+
if (!line.trim()) continue;
|
|
167
|
+
const frame = JSON.parse(line);
|
|
168
|
+
if (!record(frame)) throw new CodexAppServerError("protocol");
|
|
169
|
+
if (typeof frame.method === "string") {
|
|
170
|
+
if ("id" in frame) {
|
|
171
|
+
if (!validId(frame.id)) throw new CodexAppServerError("protocol");
|
|
172
|
+
void this.handleServerRequest(frame.id, frame.method, frame.params);
|
|
173
|
+
} else this.options.onNotification?.(frame.method, frame.params);
|
|
174
|
+
} else {
|
|
175
|
+
if (typeof frame.id !== "number" || !Number.isSafeInteger(frame.id) || "error" in frame === "result" in frame)
|
|
176
|
+
throw new CodexAppServerError("protocol");
|
|
177
|
+
const request = this.pending.get(frame.id);
|
|
178
|
+
if (!request) throw new CodexAppServerError("protocol");
|
|
179
|
+
clearTimeout(request.timer);
|
|
180
|
+
this.pending.delete(frame.id);
|
|
181
|
+
if ("error" in frame) {
|
|
182
|
+
const missing = request.resumeThreadId && record(frame.error) && frame.error.code === -32600 && frame.error.message === `no rollout found for thread id ${request.resumeThreadId}`;
|
|
183
|
+
request.reject(
|
|
184
|
+
missing ? new CodexNativeStateUnavailable() : new CodexAppServerError("rejected")
|
|
185
|
+
);
|
|
186
|
+
} else request.resolve(frame.result);
|
|
187
|
+
}
|
|
188
|
+
if (this.terminal) return;
|
|
189
|
+
}
|
|
190
|
+
if (Buffer.byteLength(this.buffer) > this.frameLimit)
|
|
191
|
+
throw new CodexAppServerError("protocol");
|
|
192
|
+
} catch {
|
|
193
|
+
this.fail("protocol");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async handleServerRequest(id, method, params) {
|
|
197
|
+
if (this.callbacks.has(id) || this.callbacks.size >= this.capacity)
|
|
198
|
+
return this.fail("protocol");
|
|
199
|
+
const controller = new AbortController();
|
|
200
|
+
this.callbacks.set(id, controller);
|
|
201
|
+
let timer;
|
|
202
|
+
try {
|
|
203
|
+
const handler = Object.hasOwn(this.options.onServerRequest ?? {}, method) ? this.options.onServerRequest?.[method] : void 0;
|
|
204
|
+
if (!handler) throw new CodexAppServerError("rejected");
|
|
205
|
+
const result = await Promise.race([
|
|
206
|
+
Promise.resolve().then(() => {
|
|
207
|
+
if (this.terminal || controller.signal.aborted) throw new CodexAppServerError("closed");
|
|
208
|
+
return handler(
|
|
209
|
+
params,
|
|
210
|
+
controller.signal
|
|
211
|
+
);
|
|
212
|
+
}),
|
|
213
|
+
new Promise((_, reject) => {
|
|
214
|
+
controller.signal.addEventListener(
|
|
215
|
+
"abort",
|
|
216
|
+
() => reject(new CodexAppServerError("closed")),
|
|
217
|
+
{ once: true }
|
|
218
|
+
);
|
|
219
|
+
timer = setTimeout(() => {
|
|
220
|
+
controller.abort();
|
|
221
|
+
reject(new CodexAppServerError("timeout"));
|
|
222
|
+
}, this.callbackTimeout);
|
|
223
|
+
})
|
|
224
|
+
]);
|
|
225
|
+
this.write({ id, result });
|
|
226
|
+
} catch {
|
|
227
|
+
if (!this.terminal) {
|
|
228
|
+
try {
|
|
229
|
+
this.write({ id, error: { code: -32603, message: "Codex client request failed" } });
|
|
230
|
+
} catch {
|
|
231
|
+
this.fail("protocol");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} finally {
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
controller.abort();
|
|
237
|
+
this.callbacks.delete(id);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export { CodexAppServerClient, CodexAppServerError };
|
|
243
|
+
//# sourceMappingURL=chunk-XKSGEYZC.js.map
|
|
244
|
+
//# sourceMappingURL=chunk-XKSGEYZC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../bridge/src/codex-app-server.ts"],"names":[],"mappings":";;;AAgCO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,YACW,IAAA,EACT;AACA,IAAA,KAAA,CAAM,CAAA,iBAAA,EAAoB,IAAI,CAAA,CAAE,CAAA;AAFvB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAGT,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AAAA,EAJW,IAAA;AAKb;AAOA,IAAM,MAAA,GAAS,CAAC,KAAA,KACd,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AACrE,IAAM,OAAA,GAAU,CAAC,KAAA,KACf,OAAO,KAAA,KAAU,QAAA,IAAa,OAAO,KAAA,KAAU,QAAA,IAAY,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AACvF,SAAS,KAAA,CAAM,KAAA,EAA2B,QAAA,EAAkB,OAAA,EAAyB;AACnF,EAAA,MAAM,SAAS,KAAA,IAAS,QAAA;AACxB,EAAA,IAAI,CAAC,MAAA,CAAO,aAAA,CAAc,MAAM,CAAA,IAAK,MAAA,IAAU,KAAK,MAAA,GAAS,OAAA;AAC3D,IAAA,MAAM,IAAI,oBAAoB,UAAU,CAAA;AAC1C,EAAA,OAAO,MAAA;AACT;AAGO,IAAM,oBAAA,GAAN,MAAM,qBAAA,CAAqB;AAAA,EAcxB,YAA6B,OAAA,EAAgC;AAAhC,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACnC,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA,CAAM,OAAA,CAAQ,gBAAA,EAAkB,MAAQ,GAAO,CAAA;AAC9D,IAAA,IAAA,CAAK,eAAA,GAAkB,KAAA,CAAM,OAAA,CAAQ,sBAAA,EAAwB,KAAQ,GAAO,CAAA;AAC5E,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,kBAAA,EAAoB,IAAI,IAAI,CAAA;AAC1D,IAAA,IAAA,CAAK,UAAA,GAAa,MAAM,OAAA,CAAQ,aAAA,EAAe,OAAO,IAAA,EAAM,EAAA,GAAK,OAAO,IAAI,CAAA;AAC5E,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,OAAA,EAAS,QAAQ,IAAA,EAAM;AAAA,MAChD,KAAK,OAAA,CAAQ,GAAA;AAAA,MACb,KAAK,OAAA,CAAQ,GAAA;AAAA,MACb,KAAA,EAAO,MAAA;AAAA,MACP,QAAA,EAAU,OAAA,CAAQ,YAAA,KAAiB,IAAA,IAAQ,QAAQ,QAAA,KAAa;AAAA,KACjE,CAAA;AACD,IAAA,IAAA,CAAK,SAAS,IAAI,OAAA;AAAA,MAAQ,CAAC,OAAA,KACzB,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAS,MAAM;AAC7B,QAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,QAAA,OAAA,EAAQ;AAAA,MACV,CAAC;AAAA,KACH;AACA,IAAA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,WAAA,CAAY,MAAM,CAAA;AACpC,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAC,CAAA;AAEnE,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,MAAA,EAAO;AACzB,IAAA,IAAA,CAAK,MAAM,EAAA,CAAG,OAAA,EAAS,MAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAC,CAAA;AACjD,IAAA,IAAA,CAAK,KAAA,CAAM,MAAM,EAAA,CAAG,OAAA,EAAS,MAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAC,CAAA;AACvD,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,EAAA,CAAG,OAAA,EAAS,MAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAC,CAAA;AACxD,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,EAAA,CAAG,OAAA,EAAS,MAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAC,CAAA;AACxD,IAAA,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,EAAQ,MAAM,IAAA,CAAK,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,EACpD;AAAA,EA1BqC,OAAA;AAAA,EAbpB,OAAA,uBAAc,GAAA,EAAqB;AAAA,EACnC,SAAA,uBAAgB,GAAA,EAAsC;AAAA,EACtD,OAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACT,QAAA,GAAW,KAAA;AAAA,EACX,WAAA,GAAc,KAAA;AAAA,EACd,MAAA,GAAS,CAAA;AAAA,EACT,MAAA,GAAS,EAAA;AAAA;AAAA,EA+BjB,aAAa,OAAA,CACX,OAAA,EACA,MAAA,EAC+B;AAC/B,IAAA,IAAI,MAAA;AACJ,IAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AACzC,IAAA,IAAI;AACF,MAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAM,IAAI,oBAAoB,QAAQ,CAAA;AAC3D,MAAA,MAAA,GAAS,IAAI,sBAAqB,OAAO,CAAA;AACzC,MAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,KAAA,EAAO,EAAE,IAAA,EAAM,MAAM,CAAA;AACvD,MAAA,MAAM,MAAA,CAAO,QAAQ,YAAA,EAAc;AAAA,QACjC,YAAY,OAAA,CAAQ,UAAA;AAAA,QACpB,YAAA,EAAc,EAAE,eAAA,EAAiB,IAAA;AAAK,OACvC,CAAA;AACD,MAAA,MAAA,CAAO,KAAA,CAAM,EAAE,MAAA,EAAQ,aAAA,EAAe,CAAA;AACtC,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,QAAQ,OAAA,EAAQ;AACtB,MAAA,MAAM,KAAA,YAAiB,mBAAA,GAAsB,KAAA,GAAQ,IAAI,oBAAoB,SAAS,CAAA;AAAA,IACxF,CAAA,SAAE;AACA,MAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,KAAK,CAAA;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA,EAGA,OAAA,CACE,QACA,MAAA,EACsC;AACtC,IAAA,IAAI,IAAA,CAAK,UAAU,OAAO,OAAA,CAAQ,OAAO,IAAI,mBAAA,CAAoB,QAAQ,CAAC,CAAA;AAC1E,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,IAAA,IAAQ,IAAA,CAAK,QAAA;AAC5B,MAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,IAAI,mBAAA,CAAoB,UAAU,CAAC,CAAA;AAC3D,IAAA,MAAM,KAAK,IAAA,CAAK,MAAA,EAAA;AAChB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM,IAAA,CAAK,KAAK,SAAS,CAAA,EAAG,KAAK,OAAO,CAAA;AACjE,MAAA,IAAA,CAAK,OAAA,CAAQ,IAAI,EAAA,EAAI;AAAA,QACnB,OAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA,GAAI,MAAA,KAAW,eAAA,IACf,MAAA,KAAW,IAAA,IACX,cAAc,MAAA,IACd,OAAO,MAAA,CAAO,QAAA,KAAa,WACvB,EAAE,cAAA,EAAgB,MAAA,CAAO,QAAA,KACzB;AAAC,OACN,CAAA;AACD,MAAA,IAAI;AACF,QAAA,IAAA,CAAK,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,QAAQ,CAAA;AAAA,MACnC,CAAA,CAAA,MAAQ;AACN,QAAA,IAAA,CAAK,KAAK,UAAU,CAAA;AAAA,MACtB;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,OAAA,GAAyB;AAC7B,IAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAClB,IAAA,IAAI,KAAA;AACJ,IAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,MACjB,IAAA,CAAK,MAAA;AAAA,MACL,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AAC7B,QAAA,KAAA,GAAQ,UAAA,CAAW,SAAS,GAAI,CAAA;AAAA,MAClC,CAAC;AAAA,KACF,CAAA;AACD,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB;AAAA;AAAA,EAGA,IAAI,oBAAA,GAAgC;AAClC,IAAA,IAAI,CAAC,IAAA,CAAK,WAAA,EAAa,OAAO,KAAA;AAC9B,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,CAAQ,YAAA,IAAgB,OAAA,CAAQ,QAAA,KAAa,OAAA,IAAW,CAAC,IAAA,CAAK,KAAA,CAAM,GAAA,EAAK,OAAO,IAAA;AAC1F,IAAA,IAAI;AACF,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,IAAA,CAAK,KAAA,CAAM,KAAK,CAAC,CAAA;AAC/B,MAAA,OAAO,KAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAQ,MAAgC,IAAA,KAAS,OAAA;AAAA,IACnD;AAAA,EACF;AAAA,EAEQ,KAAK,IAAA,EAAyC;AACpD,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IAAA,CAAK,MAAA,GAAS,EAAA;AACd,IAAA,KAAA,MAAW,OAAA,IAAW,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAO,EAAG;AAC3C,MAAA,YAAA,CAAa,QAAQ,KAAK,CAAA;AAC1B,MAAA,OAAA,CAAQ,MAAA,CAAO,IAAI,mBAAA,CAAoB,IAAI,CAAC,CAAA;AAAA,IAC9C;AACA,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AACnB,IAAA,KAAA,MAAW,cAAc,IAAA,CAAK,SAAA,CAAU,MAAA,EAAO,aAAc,KAAA,EAAM;AACnE,IAAA,IAAA,CAAK,UAAU,KAAA,EAAM;AACrB,IAAA,IAAI,IAAA,CAAK,QAAQ,YAAA,IAAgB,OAAA,CAAQ,aAAa,OAAA,IAAW,IAAA,CAAK,MAAM,GAAA,EAAK;AAC/E,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,IAAA,CAAK,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,MACzC,CAAA,CAAA,MAAQ;AACN,QAAA,IAAA,CAAK,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,MAC3B;AAAA,IACF,CAAA,MAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA;AAChC,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAA,GAAU,IAAI,mBAAA,CAAoB,IAAI,CAAC,CAAA;AAAA,IACtD,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,MAAM,KAAA,EAAsB;AAClC,IAAA,IAAI,IAAA,CAAK,QAAA,EAAU,MAAM,IAAI,oBAAoB,QAAQ,CAAA;AACzD,IAAA,MAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC;AAAA,CAAA;AACrC,IAAA,IAAI,MAAA,CAAO,WAAW,IAAI,CAAA,GAAI,KAAK,UAAA,EAAY,MAAM,IAAI,mBAAA,CAAoB,UAAU,CAAA;AACvF,IAAA,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AAAA,EAC7B;AAAA,EAEQ,QAAQ,KAAA,EAAqB;AACnC,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,IAAA,CAAK,MAAA,IAAU,KAAA;AACf,IAAA,IAAI;AACF,MAAA,WAAS;AACP,QAAA,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AACpC,QAAA,IAAI,MAAM,CAAA,EAAG;AACb,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,GAAG,GAAG,CAAA;AACrC,QAAA,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,MAAM,CAAC,CAAA;AACvC,QAAA,IAAI,MAAA,CAAO,WAAW,IAAI,CAAA,GAAI,KAAK,UAAA,EAAY,MAAM,IAAI,mBAAA,CAAoB,UAAU,CAAA;AACvF,QAAA,IAAI,CAAC,IAAA,CAAK,IAAA,EAAK,EAAG;AAClB,QAAA,MAAM,KAAA,GAAiB,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AACtC,QAAA,IAAI,CAAC,MAAA,CAAO,KAAK,GAAG,MAAM,IAAI,oBAAoB,UAAU,CAAA;AAC5D,QAAA,IAAI,OAAO,KAAA,CAAM,MAAA,KAAW,QAAA,EAAU;AACpC,UAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,YAAA,IAAI,CAAC,QAAQ,KAAA,CAAM,EAAE,GAAG,MAAM,IAAI,oBAAoB,UAAU,CAAA;AAChE,YAAA,KAAK,KAAK,mBAAA,CAAoB,KAAA,CAAM,IAAI,KAAA,CAAM,MAAA,EAAQ,MAAM,MAAM,CAAA;AAAA,UACpE,OAAO,IAAA,CAAK,OAAA,CAAQ,iBAAiB,KAAA,CAAM,MAAA,EAAQ,MAAM,MAAM,CAAA;AAAA,QACjE,CAAA,MAAO;AACL,UAAA,IACE,OAAO,KAAA,CAAM,EAAA,KAAO,QAAA,IACpB,CAAC,MAAA,CAAO,aAAA,CAAc,KAAA,CAAM,EAAE,CAAA,IAC9B,OAAA,IAAW,KAAA,KAAU,QAAA,IAAY,KAAA;AAEjC,YAAA,MAAM,IAAI,oBAAoB,UAAU,CAAA;AAC1C,UAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,MAAM,EAAE,CAAA;AACzC,UAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,oBAAoB,UAAU,CAAA;AACtD,UAAA,YAAA,CAAa,QAAQ,KAAK,CAAA;AAC1B,UAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA;AAC5B,UAAA,IAAI,WAAW,KAAA,EAAO;AAGpB,YAAA,MAAM,UACJ,OAAA,CAAQ,cAAA,IACR,MAAA,CAAO,KAAA,CAAM,KAAK,CAAA,IAClB,KAAA,CAAM,KAAA,CAAM,IAAA,KAAS,UACrB,KAAA,CAAM,KAAA,CAAM,OAAA,KAAY,CAAA,+BAAA,EAAkC,QAAQ,cAAc,CAAA,CAAA;AAClF,YAAA,OAAA,CAAQ,MAAA;AAAA,cACN,UAAU,IAAI,2BAAA,EAA4B,GAAI,IAAI,oBAAoB,UAAU;AAAA,aAClF;AAAA,UACF,CAAA,MAAO,OAAA,CAAQ,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAAA,QACrC;AACA,QAAA,IAAI,KAAK,QAAA,EAAU;AAAA,MACrB;AACA,MAAA,IAAI,MAAA,CAAO,UAAA,CAAW,IAAA,CAAK,MAAM,IAAI,IAAA,CAAK,UAAA;AACxC,QAAA,MAAM,IAAI,oBAAoB,UAAU,CAAA;AAAA,IAC5C,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,CAAK,KAAK,UAAU,CAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,mBAAA,CACZ,EAAA,EACA,MAAA,EACA,MAAA,EACe;AACf,IAAA,IAAI,IAAA,CAAK,UAAU,GAAA,CAAI,EAAE,KAAK,IAAA,CAAK,SAAA,CAAU,QAAQ,IAAA,CAAK,QAAA;AACxD,MAAA,OAAO,IAAA,CAAK,KAAK,UAAU,CAAA;AAC7B,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,EAAA,EAAI,UAAU,CAAA;AACjC,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,eAAA,IAAmB,EAAC,EAAG,MAAM,CAAA,GACpE,IAAA,CAAK,OAAA,CAAQ,eAAA,GAAkB,MAAoC,CAAA,GACnE,KAAA,CAAA;AACJ,MAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,oBAAoB,UAAU,CAAA;AACtD,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,QAChC,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,MAAM;AAC3B,UAAA,IAAI,IAAA,CAAK,YAAY,UAAA,CAAW,MAAA,CAAO,SAAS,MAAM,IAAI,oBAAoB,QAAQ,CAAA;AACtF,UAAA,OAAQ,OAAA;AAAA,YACN,MAAA;AAAA,YACA,UAAA,CAAW;AAAA,WACb;AAAA,QACF,CAAC,CAAA;AAAA,QACD,IAAI,OAAA,CAAe,CAAC,CAAA,EAAG,MAAA,KAAW;AAChC,UAAA,UAAA,CAAW,MAAA,CAAO,gBAAA;AAAA,YAChB,OAAA;AAAA,YACA,MAAM,MAAA,CAAO,IAAI,mBAAA,CAAoB,QAAQ,CAAC,CAAA;AAAA,YAC9C,EAAE,MAAM,IAAA;AAAK,WACf;AACA,UAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,YAAA,UAAA,CAAW,KAAA,EAAM;AACjB,YAAA,MAAA,CAAO,IAAI,mBAAA,CAAoB,SAAS,CAAC,CAAA;AAAA,UAC3C,CAAA,EAAG,KAAK,eAAe,CAAA;AAAA,QACzB,CAAC;AAAA,OACF,CAAA;AACD,MAAA,IAAA,CAAK,KAAA,CAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,CAAA;AAAA,IAC3B,CAAA,CAAA,MAAQ;AACN,MAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AAClB,QAAA,IAAI;AACF,UAAA,IAAA,CAAK,KAAA,CAAM,EAAE,EAAA,EAAI,KAAA,EAAO,EAAE,MAAM,CAAA,KAAA,EAAQ,OAAA,EAAS,6BAAA,EAA8B,EAAG,CAAA;AAAA,QACpF,CAAA,CAAA,MAAQ;AACN,UAAA,IAAA,CAAK,KAAK,UAAU,CAAA;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAA,SAAE;AACA,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,UAAA,CAAW,KAAA,EAAM;AACjB,MAAA,IAAA,CAAK,SAAA,CAAU,OAAO,EAAE,CAAA;AAAA,IAC1B;AAAA,EACF;AACF","file":"chunk-XKSGEYZC.js","sourcesContent":["import { CodexNativeStateUnavailable } from \"./codex/native-state.js\";\nimport { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport type { NativeRequests, NativeServerRequests } from \"./codex/native-protocol.generated.js\";\n\n/** The retained native request types, exposed only through this client surface. */\nexport type CodexRequestMethod = keyof NativeRequests;\n/** Native callback handlers receive cancellation when their process is disposed. */\nexport type CodexServerRequestHandlers = {\n [M in keyof NativeServerRequests]?: (\n params: NativeServerRequests[M][\"params\"],\n signal: AbortSignal,\n ) => Promise<NativeServerRequests[M][\"result\"]>;\n};\n/** Explicit process inputs keep managed callers from inheriting ambient credentials. */\nexport interface CodexAppServerOptions {\n command: string;\n args: string[];\n cwd: string;\n env: Record<string, string>;\n clientInfo: NativeRequests[\"initialize\"][\"params\"][\"clientInfo\"];\n requestTimeoutMs?: number;\n serverRequestTimeoutMs?: number;\n maxPendingRequests?: number;\n maxFrameBytes?: number;\n /** Isolate and reap the native process group on POSIX. */\n processGroup?: boolean;\n onServerRequest?: CodexServerRequestHandlers;\n onNotification?: (method: string, params: unknown) => void;\n /** Exactly once on terminal failure or disposal, even with no RPC pending. */\n onClose?: (error: CodexAppServerError) => void;\n}\n/** Safe error codes never contain stderr, native errors, RPC payloads or credentials. */\nexport class CodexAppServerError extends Error {\n constructor(\n readonly code: \"closed\" | \"process\" | \"protocol\" | \"timeout\" | \"rejected\" | \"capacity\",\n ) {\n super(`Codex App Server ${code}`);\n this.name = \"CodexAppServerError\";\n }\n}\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (error: CodexAppServerError | CodexNativeStateUnavailable) => void;\n timer: ReturnType<typeof setTimeout>;\n resumeThreadId?: string;\n};\nconst record = (value: unknown): value is Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value);\nconst validId = (value: unknown): value is string | number =>\n typeof value === \"string\" || (typeof value === \"number\" && Number.isSafeInteger(value));\nfunction bound(value: number | undefined, fallback: number, maximum: number): number {\n const result = value ?? fallback;\n if (!Number.isSafeInteger(result) || result <= 0 || result > maximum)\n throw new CodexAppServerError(\"protocol\");\n return result;\n}\n\n/** One initialized stdio process with correlated, bounded requests and terminal disposal. */\nexport class CodexAppServerClient {\n private readonly pending = new Map<number, Pending>();\n private readonly callbacks = new Map<string | number, AbortController>();\n private readonly timeout: number;\n private readonly callbackTimeout: number;\n private readonly capacity: number;\n private readonly frameLimit: number;\n private readonly child: ChildProcessWithoutNullStreams;\n private readonly closed: Promise<void>;\n private terminal = false;\n private childClosed = false;\n private nextId = 1;\n private buffer = \"\";\n\n private constructor(private readonly options: CodexAppServerOptions) {\n this.timeout = bound(options.requestTimeoutMs, 15_000, 300_000);\n this.callbackTimeout = bound(options.serverRequestTimeoutMs, 30_000, 300_000);\n this.capacity = bound(options.maxPendingRequests, 64, 1024);\n this.frameLimit = bound(options.maxFrameBytes, 1024 * 1024, 16 * 1024 * 1024);\n this.child = spawn(options.command, options.args, {\n cwd: options.cwd,\n env: options.env,\n stdio: \"pipe\",\n detached: options.processGroup === true && process.platform !== \"win32\",\n });\n this.closed = new Promise((resolve) =>\n this.child.once(\"close\", () => {\n this.childClosed = true;\n resolve();\n }),\n );\n this.child.stdout.setEncoding(\"utf8\");\n this.child.stdout.on(\"data\", (chunk: string) => this.receive(chunk));\n // Drain without retaining raw diagnostic output: native stderr can contain secrets.\n this.child.stderr.resume();\n this.child.on(\"error\", () => this.fail(\"process\"));\n this.child.stdin.on(\"error\", () => this.fail(\"process\"));\n this.child.stdout.on(\"error\", () => this.fail(\"process\"));\n this.child.stderr.on(\"error\", () => this.fail(\"process\"));\n this.child.once(\"exit\", () => this.fail(\"process\"));\n }\n\n /** Spawn and finish the native initialize/initialized handshake before returning. */\n static async connect(\n options: CodexAppServerOptions,\n signal?: AbortSignal,\n ): Promise<CodexAppServerClient> {\n let client: CodexAppServerClient | undefined;\n const abort = () => client?.fail(\"closed\");\n try {\n if (signal?.aborted) throw new CodexAppServerError(\"closed\");\n client = new CodexAppServerClient(options);\n signal?.addEventListener(\"abort\", abort, { once: true });\n await client.request(\"initialize\", {\n clientInfo: options.clientInfo,\n capabilities: { experimentalApi: true },\n });\n client.write({ method: \"initialized\" });\n return client;\n } catch (error) {\n await client?.dispose();\n throw error instanceof CodexAppServerError ? error : new CodexAppServerError(\"process\");\n } finally {\n signal?.removeEventListener(\"abort\", abort);\n }\n }\n\n /** Timeout closes this generation, so late replies can never satisfy a later request. */\n request<M extends CodexRequestMethod>(\n method: M,\n params: NativeRequests[M][\"params\"],\n ): Promise<NativeRequests[M][\"result\"]> {\n if (this.terminal) return Promise.reject(new CodexAppServerError(\"closed\"));\n if (this.pending.size >= this.capacity)\n return Promise.reject(new CodexAppServerError(\"capacity\"));\n const id = this.nextId++;\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => this.fail(\"timeout\"), this.timeout);\n this.pending.set(id, {\n resolve: resolve as (value: unknown) => void,\n reject,\n timer,\n ...(method === \"thread/resume\" &&\n params !== null &&\n \"threadId\" in params &&\n typeof params.threadId === \"string\"\n ? { resumeThreadId: params.threadId }\n : {}),\n });\n try {\n this.write({ id, method, params });\n } catch {\n this.fail(\"protocol\");\n }\n });\n }\n\n /** Reject pending work immediately and wait a bounded interval for process shutdown. */\n async dispose(): Promise<void> {\n this.fail(\"closed\");\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.closed,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, 1000);\n }),\n ]);\n clearTimeout(timer);\n }\n\n /** Positive exit proof for callers that may replace a failed native generation. */\n get terminationConfirmed(): boolean {\n if (!this.childClosed) return false;\n if (!this.options.processGroup || process.platform === \"win32\" || !this.child.pid) return true;\n try {\n process.kill(-this.child.pid, 0);\n return false;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"ESRCH\";\n }\n }\n\n private fail(code: CodexAppServerError[\"code\"]): void {\n if (this.terminal) return;\n this.terminal = true;\n this.buffer = \"\";\n for (const request of this.pending.values()) {\n clearTimeout(request.timer);\n request.reject(new CodexAppServerError(code));\n }\n this.pending.clear();\n for (const controller of this.callbacks.values()) controller.abort();\n this.callbacks.clear();\n if (this.options.processGroup && process.platform !== \"win32\" && this.child.pid) {\n try {\n process.kill(-this.child.pid, \"SIGKILL\");\n } catch {\n this.child.kill(\"SIGKILL\");\n }\n } else this.child.kill(\"SIGKILL\");\n try {\n this.options.onClose?.(new CodexAppServerError(code));\n } catch {\n // Observer failures cannot interrupt transport cleanup.\n }\n }\n\n private write(frame: unknown): void {\n if (this.terminal) throw new CodexAppServerError(\"closed\");\n const line = `${JSON.stringify(frame)}\\n`;\n if (Buffer.byteLength(line) > this.frameLimit) throw new CodexAppServerError(\"protocol\");\n this.child.stdin.write(line);\n }\n\n private receive(chunk: string): void {\n if (this.terminal) return;\n this.buffer += chunk;\n try {\n for (;;) {\n const end = this.buffer.indexOf(\"\\n\");\n if (end < 0) break;\n const line = this.buffer.slice(0, end);\n this.buffer = this.buffer.slice(end + 1);\n if (Buffer.byteLength(line) > this.frameLimit) throw new CodexAppServerError(\"protocol\");\n if (!line.trim()) continue;\n const frame: unknown = JSON.parse(line);\n if (!record(frame)) throw new CodexAppServerError(\"protocol\");\n if (typeof frame.method === \"string\") {\n if (\"id\" in frame) {\n if (!validId(frame.id)) throw new CodexAppServerError(\"protocol\");\n void this.handleServerRequest(frame.id, frame.method, frame.params);\n } else this.options.onNotification?.(frame.method, frame.params);\n } else {\n if (\n typeof frame.id !== \"number\" ||\n !Number.isSafeInteger(frame.id) ||\n \"error\" in frame === \"result\" in frame\n )\n throw new CodexAppServerError(\"protocol\");\n const request = this.pending.get(frame.id);\n if (!request) throw new CodexAppServerError(\"protocol\");\n clearTimeout(request.timer);\n this.pending.delete(frame.id);\n if (\"error\" in frame) {\n // Pinned 0.153.4 thread_store_resume_read_error maps only missing rollout\n // to this exact invalid-request message. Never retain raw native errors.\n const missing =\n request.resumeThreadId &&\n record(frame.error) &&\n frame.error.code === -32600 &&\n frame.error.message === `no rollout found for thread id ${request.resumeThreadId}`;\n request.reject(\n missing ? new CodexNativeStateUnavailable() : new CodexAppServerError(\"rejected\"),\n );\n } else request.resolve(frame.result);\n }\n if (this.terminal) return;\n }\n if (Buffer.byteLength(this.buffer) > this.frameLimit)\n throw new CodexAppServerError(\"protocol\");\n } catch {\n this.fail(\"protocol\");\n }\n }\n\n private async handleServerRequest(\n id: string | number,\n method: string,\n params: unknown,\n ): Promise<void> {\n if (this.callbacks.has(id) || this.callbacks.size >= this.capacity)\n return this.fail(\"protocol\");\n const controller = new AbortController();\n this.callbacks.set(id, controller);\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const handler = Object.hasOwn(this.options.onServerRequest ?? {}, method)\n ? this.options.onServerRequest?.[method as keyof NativeServerRequests]\n : undefined;\n if (!handler) throw new CodexAppServerError(\"rejected\");\n const result = await Promise.race([\n Promise.resolve().then(() => {\n if (this.terminal || controller.signal.aborted) throw new CodexAppServerError(\"closed\");\n return (handler as (params: unknown, signal: AbortSignal) => Promise<unknown>)(\n params,\n controller.signal,\n );\n }),\n new Promise<never>((_, reject) => {\n controller.signal.addEventListener(\n \"abort\",\n () => reject(new CodexAppServerError(\"closed\")),\n { once: true },\n );\n timer = setTimeout(() => {\n controller.abort();\n reject(new CodexAppServerError(\"timeout\"));\n }, this.callbackTimeout);\n }),\n ]);\n this.write({ id, result });\n } catch {\n if (!this.terminal) {\n try {\n this.write({ id, error: { code: -32603, message: \"Codex client request failed\" } });\n } catch {\n this.fail(\"protocol\");\n }\n }\n } finally {\n clearTimeout(timer);\n controller.abort();\n this.callbacks.delete(id);\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { globalSettingsPath, mapLegacyFields } from './chunk-
|
|
1
|
+
import { globalSettingsPath, mapLegacyFields } from './chunk-H662SXKS.js';
|
|
2
2
|
import { rmSync, existsSync, readFileSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
3
3
|
import { randomBytes, createHash } from 'crypto';
|
|
4
4
|
import { homedir } from 'os';
|
|
@@ -228,5 +228,5 @@ function isStoreAuthenticated() {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
export { deviceLogin, getStoreConfig, getStoreIdentity, getStoreToken, hasStoredAuth, isStoreAuthenticated, loadStoreAuthConfig, logout, storeFetch };
|
|
231
|
-
//# sourceMappingURL=chunk-
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
231
|
+
//# sourceMappingURL=chunk-XL45U3C5.js.map
|
|
232
|
+
//# sourceMappingURL=chunk-XL45U3C5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../cli/src/store-auth.ts","../cli/src/store-client.ts"],"names":["readFileSync"],"mappings":"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","file":"chunk-SUPIVTLX.js","sourcesContent":["/**\n * Keycloak Device Authorization Grant (RFC 8628) for the store CLI: a public\n * device-flow client (`skaile-store-cli`) issues a plain realm JWT the store's\n * `AuthGuard` accepts — a CLI can't use the storefront's confidential browser\n * client. Tokens cache at `~/.skaile/auth.json` (0600, shared with `store/cli`);\n * `getAccessToken` refreshes transparently. All targets are env-overridable.\n *\n * @docLink cli/dev-guide#store-auth\n */\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * A PKCE (RFC 7636) `code_verifier` + its S256 `code_challenge`. Keycloak\n * enforces PKCE on the `skaile-store-cli` device flow (the device-authorization\n * request 400s with \"Missing parameter: code_challenge_method\" otherwise), so\n * every `deviceLogin` mints a fresh pair: the challenge goes on the device-auth\n * request, the verifier on the matching token poll. `base64url` (no padding)\n * per the spec.\n */\nfunction makePkcePair(): { verifier: string; challenge: string } {\n const verifier = randomBytes(32).toString(\"base64url\");\n const challenge = createHash(\"sha256\").update(verifier).digest(\"base64url\");\n return { verifier, challenge };\n}\n\n/** Resolved auth targets — every field has a prod default, all env-overridable. */\nexport interface StoreAuthConfig {\n keycloakUrl: string;\n realm: string;\n clientId: string;\n authFile: string;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Load the auth config. Defaults target the production store\n * (`auth.skaile.ai`, realm `skaile-store`, public client `skaile-store-cli`);\n * override with `SKAILE_KEYCLOAK_URL` / `SKAILE_STORE_REALM` /\n * `SKAILE_STORE_CLI_CLIENT` / `SKAILE_AUTH_FILE` for dev or a preview deploy.\n */\nexport function loadStoreAuthConfig(): StoreAuthConfig {\n const home = process.env.SKAILE_HOME ?? join(homedir(), \".skaile\");\n return {\n keycloakUrl: trimTrailingSlash(process.env.SKAILE_KEYCLOAK_URL ?? \"https://auth.skaile.ai\"),\n realm: process.env.SKAILE_STORE_REALM ?? \"skaile-store\",\n clientId: process.env.SKAILE_STORE_CLI_CLIENT ?? \"skaile-store-cli\",\n authFile: process.env.SKAILE_AUTH_FILE ?? join(home, \"auth.json\"),\n };\n}\n\n/** Keycloak OIDC endpoints derived from the realm base. */\nfunction oidc(config: StoreAuthConfig): { deviceAuth: string; token: string } {\n const base = `${config.keycloakUrl}/realms/${config.realm}/protocol/openid-connect`;\n return { deviceAuth: `${base}/auth/device`, token: `${base}/token` };\n}\n\nexport interface StoredAuth {\n access_token: string;\n refresh_token?: string;\n /** Epoch ms when the access token expires (with a safety margin already applied). */\n expires_at: number;\n}\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n error?: string;\n error_description?: string;\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n interval?: number;\n expires_in: number;\n}\n\n/** Refresh this many ms before real expiry so an in-flight call never 401s. */\nconst EXPIRY_MARGIN_MS = 30_000;\n\n/** Injectable seams so the poll loop is unit-testable without real time or network. */\nexport interface AuthDeps {\n fetchImpl?: typeof fetch;\n sleep?: (ms: number) => Promise<void>;\n /** Called once with the verification URL + user code (default: write to stderr). */\n onPrompt?: (url: string, userCode: string) => void;\n}\n\nfunction readAuth(config: StoreAuthConfig): StoredAuth | null {\n try {\n const parsed = JSON.parse(readFileSync(config.authFile, \"utf-8\"));\n // Reject a corrupt / hand-edited file rather than treat it as a session.\n if (parsed && typeof parsed.access_token === \"string\") return parsed as StoredAuth;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * The caller's store identity, decoded LOCALLY from the cached token's JWT\n * payload (no network, no signature check — this is an author-side hint, not an\n * authorization decision). Returns `null` when not signed in or the token is\n * unreadable. `githubLogin` is the verified GitHub handle (the preferred\n * publisher namespace); `username` is the Keycloak `preferred_username`.\n */\nexport function getStoreIdentity(\n config: StoreAuthConfig = loadStoreAuthConfig(),\n): { username?: string; githubLogin?: string } | null {\n const auth = readAuth(config);\n if (!auth) return null;\n try {\n const payload = auth.access_token.split(\".\")[1];\n const json = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf-8\"));\n const username = typeof json.preferred_username === \"string\" ? json.preferred_username : undefined;\n const githubLogin = typeof json.github_login === \"string\" ? json.github_login : undefined;\n return username || githubLogin ? { username, githubLogin } : null;\n } catch {\n return null;\n }\n}\n\nfunction writeAuth(config: StoreAuthConfig, auth: StoredAuth): void {\n mkdirSync(dirname(config.authFile), { recursive: true });\n writeFileSync(config.authFile, JSON.stringify(auth, null, 2), { mode: 0o600 });\n chmodSync(config.authFile, 0o600);\n}\n\n/** Remove the cached session (sign out). */\nexport function logout(config: StoreAuthConfig): void {\n try {\n rmSync(config.authFile);\n } catch {\n // already absent — nothing to do\n }\n}\n\n/** True when a cached, well-formed session exists (validity/refresh resolved on use). */\nexport function hasStoredAuth(config: StoreAuthConfig): boolean {\n return readAuth(config) !== null;\n}\n\nfunction tokenToStored(token: TokenResponse): StoredAuth {\n return {\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n expires_at: Date.now() + (token.expires_in ?? 300) * 1000 - EXPIRY_MARGIN_MS,\n };\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\nfunction defaultPrompt(url: string, userCode: string): void {\n process.stderr.write(\n `\\nTo sign in, open:\\n\\n ${url}\\n\\nand enter the code: ${userCode}\\n\\nWaiting for approval…\\n`,\n );\n}\n\n/**\n * Run the device flow to completion, persisting tokens on success. Prints the\n * verification URL + user code for the operator to approve in a browser.\n */\nexport async function deviceLogin(\n config: StoreAuthConfig,\n deps: AuthDeps = {},\n): Promise<StoredAuth> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? defaultSleep;\n const onPrompt = deps.onPrompt ?? defaultPrompt;\n const endpoints = oidc(config);\n const pkce = makePkcePair();\n\n const deviceRes = await fetchImpl(endpoints.deviceAuth, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n client_id: config.clientId,\n scope: \"openid profile\",\n code_challenge: pkce.challenge,\n code_challenge_method: \"S256\",\n }),\n });\n if (!deviceRes.ok) {\n throw new Error(\n `Device authorization request failed (${deviceRes.status}): ${await deviceRes.text()}`,\n );\n }\n const device = (await deviceRes.json()) as DeviceAuthResponse;\n\n onPrompt(device.verification_uri_complete ?? device.verification_uri, device.user_code);\n\n const intervalMs = (device.interval ?? 5) * 1000;\n const deadline = Date.now() + device.expires_in * 1000;\n let waitMs = intervalMs;\n\n while (Date.now() < deadline) {\n await sleep(waitMs);\n const tokenRes = await fetchImpl(endpoints.token, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code: device.device_code,\n client_id: config.clientId,\n code_verifier: pkce.verifier,\n }),\n });\n const token = (await tokenRes.json()) as TokenResponse;\n if (tokenRes.ok) {\n const stored = tokenToStored(token);\n writeAuth(config, stored);\n return stored;\n }\n // RFC 8628 §3.5 pending / slow-down handling.\n if (token.error === \"authorization_pending\") continue;\n if (token.error === \"slow_down\") {\n waitMs += 5_000;\n continue;\n }\n throw new Error(`Sign-in failed: ${token.error_description ?? token.error ?? \"unknown error\"}`);\n }\n throw new Error(\"Sign-in timed out before approval.\");\n}\n\n/** Exchange a refresh token for a fresh access token; persist on success. */\nasync function refresh(\n config: StoreAuthConfig,\n refreshToken: string,\n fetchImpl: typeof fetch,\n): Promise<StoredAuth> {\n const res = await fetchImpl(oidc(config).token, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: config.clientId,\n }),\n });\n const token = (await res.json()) as TokenResponse;\n if (!res.ok) {\n throw new Error(\n `Token refresh failed: ${token.error_description ?? token.error ?? res.status}`,\n );\n }\n const stored = tokenToStored(token);\n writeAuth(config, stored);\n return stored;\n}\n\n/**\n * Return a valid access token, refreshing if it is expired (or about to). Throws\n * a clear \"run `skaile store login`\" error when there is no usable session.\n */\nexport async function getAccessToken(\n config: StoreAuthConfig,\n deps: AuthDeps = {},\n): Promise<string> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const auth = readAuth(config);\n if (!auth) throw new Error(\"Not signed in. Run `skaile store login` first.\");\n if (Date.now() < auth.expires_at) return auth.access_token;\n if (!auth.refresh_token) throw new Error(\"Session expired. Run `skaile store login` again.\");\n try {\n return (await refresh(config, auth.refresh_token, fetchImpl)).access_token;\n } catch {\n throw new Error(\"Session expired and refresh failed. Run `skaile store login` again.\");\n }\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { globalSettingsPath, mapLegacyFields } from \"@skaile/workspaces/core\";\nimport { type AuthDeps, getAccessToken, hasStoredAuth, loadStoreAuthConfig } from \"./store-auth.ts\";\n\n/**\n * Connection configuration for the AI Asset Store API. `jwt` is normally left\n * null here and filled per-call by the caller from {@link getStoreToken} (the\n * device-flow session); it stays on the type for the `{ ...config, jwt }` spread.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport interface StoreConfig {\n url: string;\n jwt: string | null;\n}\n\n/**\n * Read the store API base URL (settings `storeUrl` → `SKAILE_STORE_URL` →\n * default). Auth is not read here — it lives in the device-flow session; see\n * {@link getStoreToken}.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport function getStoreConfig(): StoreConfig {\n const settingsPath = globalSettingsPath();\n let settings: Record<string, any> = {};\n if (existsSync(settingsPath)) {\n try {\n settings = mapLegacyFields(JSON.parse(readFileSync(settingsPath, \"utf-8\")));\n } catch {\n settings = {};\n }\n }\n return {\n url: settings.storeUrl ?? process.env.SKAILE_STORE_URL ?? \"https://api.skaile.store\",\n jwt: null,\n };\n}\n\n/**\n * Return a valid store access token (Keycloak device-flow session, refreshed if\n * needed). Throws a \"run `skaile store login`\" error when there is no session.\n * This is the auth source for every `/api/v1/*` publisher call.\n */\nexport function getStoreToken(deps?: AuthDeps): Promise<string> {\n return getAccessToken(loadStoreAuthConfig(), deps);\n}\n\n/**\n * Make an authenticated HTTP request to the store REST API.\n *\n * @param config - Store connection config (URL + auth token).\n * @param path - API path (e.g. `/api/assets`).\n * @param opts - Optional method, body, query params, extra headers, and\n * `timeoutMs` (default 1500). Publisher calls pass a longer timeout because\n * they trigger server-side clone/sync work.\n * @returns Parsed JSON response body.\n * @docLink cli/dev-guide#store-client\n */\nexport async function storeFetch<T = any>(\n config: StoreConfig,\n path: string,\n opts?: {\n method?: string;\n body?: unknown;\n params?: Record<string, string>;\n headers?: Record<string, string>;\n timeoutMs?: number;\n },\n): Promise<T> {\n let url = `${config.url}${path}`;\n if (opts?.params) {\n const sp = new URLSearchParams(opts.params);\n url += `?${sp}`;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(opts?.headers ?? {}),\n };\n if (config.jwt) {\n headers.Authorization = `Bearer ${config.jwt}`;\n }\n\n const fetchOpts: RequestInit = {\n method: opts?.method ?? \"GET\",\n headers,\n };\n if (opts?.body) {\n fetchOpts.body = JSON.stringify(opts.body);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1_500);\n try {\n const res = await fetch(url, { ...fetchOpts, signal: controller.signal });\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));\n // Prefer the store's human-readable `message` (e.g. \"Namespace '@x' is\n // reserved … Sign in with GitHub to claim it\") over the bare `error` code\n // (`UNPROCESSABLE_CONTENT`) — the message is the actionable part.\n const b = body as { message?: string; error?: string };\n throw new Error(b.message ?? b.error ?? `Store API error: ${res.status}`);\n }\n return res.json() as Promise<T>;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Return `true` when a store session exists (a Keycloak device-flow token cached\n * at `~/.skaile/auth.json`). Validity/refresh is resolved on use via\n * {@link getStoreToken}.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport function isStoreAuthenticated(): boolean {\n return hasStoredAuth(loadStoreAuthConfig());\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../cli/src/store-auth.ts","../cli/src/store-client.ts"],"names":["readFileSync"],"mappings":";;;;;;AAsBA,SAAS,YAAA,GAAwD;AAC/D,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,EAAE,CAAA,CAAE,SAAS,WAAW,CAAA;AACrD,EAAA,MAAM,SAAA,GAAY,WAAW,QAAQ,CAAA,CAAE,OAAO,QAAQ,CAAA,CAAE,OAAO,WAAW,CAAA;AAC1E,EAAA,OAAO,EAAE,UAAU,SAAA,EAAU;AAC/B;AAUA,SAAS,kBAAkB,GAAA,EAAqB;AAC9C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AAC/B;AAQO,SAAS,mBAAA,GAAuC;AACrD,EAAA,MAAM,OAAO,OAAA,CAAQ,GAAA,CAAI,eAAe,IAAA,CAAK,OAAA,IAAW,SAAS,CAAA;AACjE,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,iBAAA,CAAkB,OAAA,CAAQ,GAAA,CAAI,uBAAuB,wBAAwB,CAAA;AAAA,IAC1F,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,kBAAA,IAAsB,cAAA;AAAA,IACzC,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,uBAAA,IAA2B,kBAAA;AAAA,IACjD,UAAU,OAAA,CAAQ,GAAA,CAAI,gBAAA,IAAoB,IAAA,CAAK,MAAM,WAAW;AAAA,GAClE;AACF;AAGA,SAAS,KAAK,MAAA,EAAgE;AAC5E,EAAA,MAAM,OAAO,CAAA,EAAG,MAAA,CAAO,WAAW,CAAA,QAAA,EAAW,OAAO,KAAK,CAAA,wBAAA,CAAA;AACzD,EAAA,OAAO,EAAE,YAAY,CAAA,EAAG,IAAI,gBAAgB,KAAA,EAAO,CAAA,EAAG,IAAI,CAAA,MAAA,CAAA,EAAS;AACrE;AA2BA,IAAM,gBAAA,GAAmB,GAAA;AAUzB,SAAS,SAAS,MAAA,EAA4C;AAC5D,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,aAAa,MAAA,CAAO,QAAA,EAAU,OAAO,CAAC,CAAA;AAEhE,IAAA,IAAI,MAAA,IAAU,OAAO,MAAA,CAAO,YAAA,KAAiB,UAAU,OAAO,MAAA;AAC9D,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AASO,SAAS,gBAAA,CACd,MAAA,GAA0B,mBAAA,EAAoB,EACM;AACpD,EAAA,MAAM,IAAA,GAAO,SAAS,MAAM,CAAA;AAC5B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,UAAU,IAAA,CAAK,YAAA,CAAa,KAAA,CAAM,GAAG,EAAE,CAAC,CAAA;AAC9C,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,SAAS,WAAW,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,CAAA;AAC3E,IAAA,MAAM,WAAW,OAAO,IAAA,CAAK,kBAAA,KAAuB,QAAA,GAAW,KAAK,kBAAA,GAAqB,KAAA,CAAA;AACzF,IAAA,MAAM,cAAc,OAAO,IAAA,CAAK,YAAA,KAAiB,QAAA,GAAW,KAAK,YAAA,GAAe,KAAA,CAAA;AAChF,IAAA,OAAO,QAAA,IAAY,WAAA,GAAc,EAAE,QAAA,EAAU,aAAY,GAAI,IAAA;AAAA,EAC/D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,SAAS,SAAA,CAAU,QAAyB,IAAA,EAAwB;AAClE,EAAA,SAAA,CAAU,QAAQ,MAAA,CAAO,QAAQ,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACvD,EAAA,aAAA,CAAc,MAAA,CAAO,QAAA,EAAU,IAAA,CAAK,SAAA,CAAU,IAAA,EAAM,IAAA,EAAM,CAAC,CAAA,EAAG,EAAE,IAAA,EAAM,GAAA,EAAO,CAAA;AAC7E,EAAA,SAAA,CAAU,MAAA,CAAO,UAAU,GAAK,CAAA;AAClC;AAGO,SAAS,OAAO,MAAA,EAA+B;AACpD,EAAA,IAAI;AACF,IAAA,MAAA,CAAO,OAAO,QAAQ,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAGO,SAAS,cAAc,MAAA,EAAkC;AAC9D,EAAA,OAAO,QAAA,CAAS,MAAM,CAAA,KAAM,IAAA;AAC9B;AAEA,SAAS,cAAc,KAAA,EAAkC;AACvD,EAAA,OAAO;AAAA,IACL,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,eAAe,KAAA,CAAM,aAAA;AAAA,IACrB,YAAY,IAAA,CAAK,GAAA,MAAS,KAAA,CAAM,UAAA,IAAc,OAAO,GAAA,GAAO;AAAA,GAC9D;AACF;AAEA,IAAM,YAAA,GAAe,CAAC,EAAA,KAA8B,IAAI,OAAA,CAAQ,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAExF,SAAS,aAAA,CAAc,KAAa,QAAA,EAAwB;AAC1D,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,IACb;AAAA;;AAAA,IAAA,EAA8B,GAAG;;AAAA,oBAAA,EAA2B,QAAQ;;AAAA;AAAA;AAAA,GACtE;AACF;AAMA,eAAsB,WAAA,CACpB,MAAA,EACA,IAAA,GAAiB,EAAC,EACG;AACrB,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,KAAA;AACpC,EAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,IAAS,YAAA;AAC5B,EAAA,MAAM,QAAA,GAAW,KAAK,QAAA,IAAY,aAAA;AAClC,EAAA,MAAM,SAAA,GAAY,KAAK,MAAM,CAAA;AAC7B,EAAA,MAAM,OAAO,YAAA,EAAa;AAE1B,EAAA,MAAM,SAAA,GAAY,MAAM,SAAA,CAAU,SAAA,CAAU,UAAA,EAAY;AAAA,IACtD,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,mCAAA,EAAoC;AAAA,IAC/D,IAAA,EAAM,IAAI,eAAA,CAAgB;AAAA,MACxB,WAAW,MAAA,CAAO,QAAA;AAAA,MAClB,KAAA,EAAO,gBAAA;AAAA,MACP,gBAAgB,IAAA,CAAK,SAAA;AAAA,MACrB,qBAAA,EAAuB;AAAA,KACxB;AAAA,GACF,CAAA;AACD,EAAA,IAAI,CAAC,UAAU,EAAA,EAAI;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,wCAAwC,SAAA,CAAU,MAAM,MAAM,MAAM,SAAA,CAAU,MAAM,CAAA;AAAA,KACtF;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAU,MAAM,SAAA,CAAU,IAAA,EAAK;AAErC,EAAA,QAAA,CAAS,MAAA,CAAO,yBAAA,IAA6B,MAAA,CAAO,gBAAA,EAAkB,OAAO,SAAS,CAAA;AAEtF,EAAA,MAAM,UAAA,GAAA,CAAc,MAAA,CAAO,QAAA,IAAY,CAAA,IAAK,GAAA;AAC5C,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,OAAO,UAAA,GAAa,GAAA;AAClD,EAAA,IAAI,MAAA,GAAS,UAAA;AAEb,EAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,QAAA,EAAU;AAC5B,IAAA,MAAM,MAAM,MAAM,CAAA;AAClB,IAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,SAAA,CAAU,KAAA,EAAO;AAAA,MAChD,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,mCAAA,EAAoC;AAAA,MAC/D,IAAA,EAAM,IAAI,eAAA,CAAgB;AAAA,QACxB,UAAA,EAAY,8CAAA;AAAA,QACZ,aAAa,MAAA,CAAO,WAAA;AAAA,QACpB,WAAW,MAAA,CAAO,QAAA;AAAA,QAClB,eAAe,IAAA,CAAK;AAAA,OACrB;AAAA,KACF,CAAA;AACD,IAAA,MAAM,KAAA,GAAS,MAAM,QAAA,CAAS,IAAA,EAAK;AACnC,IAAA,IAAI,SAAS,EAAA,EAAI;AACf,MAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,MAAA,SAAA,CAAU,QAAQ,MAAM,CAAA;AACxB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,KAAA,CAAM,UAAU,uBAAA,EAAyB;AAC7C,IAAA,IAAI,KAAA,CAAM,UAAU,WAAA,EAAa;AAC/B,MAAA,MAAA,IAAU,GAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAI,MAAM,CAAA,gBAAA,EAAmB,KAAA,CAAM,qBAAqB,KAAA,CAAM,KAAA,IAAS,eAAe,CAAA,CAAE,CAAA;AAAA,EAChG;AACA,EAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AACtD;AAGA,eAAe,OAAA,CACb,MAAA,EACA,YAAA,EACA,SAAA,EACqB;AACrB,EAAA,MAAM,MAAM,MAAM,SAAA,CAAU,IAAA,CAAK,MAAM,EAAE,KAAA,EAAO;AAAA,IAC9C,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,mCAAA,EAAoC;AAAA,IAC/D,IAAA,EAAM,IAAI,eAAA,CAAgB;AAAA,MACxB,UAAA,EAAY,eAAA;AAAA,MACZ,aAAA,EAAe,YAAA;AAAA,MACf,WAAW,MAAA,CAAO;AAAA,KACnB;AAAA,GACF,CAAA;AACD,EAAA,MAAM,KAAA,GAAS,MAAM,GAAA,CAAI,IAAA,EAAK;AAC9B,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,yBAAyB,KAAA,CAAM,iBAAA,IAAqB,KAAA,CAAM,KAAA,IAAS,IAAI,MAAM,CAAA;AAAA,KAC/E;AAAA,EACF;AACA,EAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,EAAA,SAAA,CAAU,QAAQ,MAAM,CAAA;AACxB,EAAA,OAAO,MAAA;AACT;AAMA,eAAsB,cAAA,CACpB,MAAA,EACA,IAAA,GAAiB,EAAC,EACD;AACjB,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,KAAA;AACpC,EAAA,MAAM,IAAA,GAAO,SAAS,MAAM,CAAA;AAC5B,EAAA,IAAI,CAAC,IAAA,EAAM,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAC3E,EAAA,IAAI,KAAK,GAAA,EAAI,GAAI,IAAA,CAAK,UAAA,SAAmB,IAAA,CAAK,YAAA;AAC9C,EAAA,IAAI,CAAC,IAAA,CAAK,aAAA,EAAe,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAC3F,EAAA,IAAI;AACF,IAAA,OAAA,CAAQ,MAAM,OAAA,CAAQ,MAAA,EAAQ,IAAA,CAAK,aAAA,EAAe,SAAS,CAAA,EAAG,YAAA;AAAA,EAChE,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,MAAM,qEAAqE,CAAA;AAAA,EACvF;AACF;;;AC9PO,SAAS,cAAA,GAA8B;AAC5C,EAAA,MAAM,eAAe,kBAAA,EAAmB;AACxC,EAAA,IAAI,WAAgC,EAAC;AACrC,EAAA,IAAI,UAAA,CAAW,YAAY,CAAA,EAAG;AAC5B,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,gBAAgB,IAAA,CAAK,KAAA,CAAMA,aAAa,YAAA,EAAc,OAAO,CAAC,CAAC,CAAA;AAAA,IAC5E,CAAA,CAAA,MAAQ;AACN,MAAA,QAAA,GAAW,EAAC;AAAA,IACd;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,QAAA,CAAS,QAAA,IAAY,OAAA,CAAQ,IAAI,gBAAA,IAAoB,0BAAA;AAAA,IAC1D,GAAA,EAAK;AAAA,GACP;AACF;AAOO,SAAS,cAAc,IAAA,EAAkC;AAC9D,EAAA,OAAO,cAAA,CAAe,mBAAA,EAAoB,EAAG,IAAI,CAAA;AACnD;AAaA,eAAsB,UAAA,CACpB,MAAA,EACA,IAAA,EACA,IAAA,EAOY;AACZ,EAAA,IAAI,GAAA,GAAM,CAAA,EAAG,MAAA,CAAO,GAAG,GAAG,IAAI,CAAA,CAAA;AAC9B,EAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,IAAA,MAAM,EAAA,GAAK,IAAI,eAAA,CAAgB,IAAA,CAAK,MAAM,CAAA;AAC1C,IAAA,GAAA,IAAO,IAAI,EAAE,CAAA,CAAA;AAAA,EACf;AAEA,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,cAAA,EAAgB,kBAAA;AAAA,IAChB,MAAA,EAAQ,kBAAA;AAAA,IACR,GAAI,IAAA,EAAM,OAAA,IAAW;AAAC,GACxB;AACA,EAAA,IAAI,OAAO,GAAA,EAAK;AACd,IAAA,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,MAAA,CAAO,GAAG,CAAA,CAAA;AAAA,EAC9C;AAEA,EAAA,MAAM,SAAA,GAAyB;AAAA,IAC7B,MAAA,EAAQ,MAAM,MAAA,IAAU,KAAA;AAAA,IACxB;AAAA,GACF;AACA,EAAA,IAAI,MAAM,IAAA,EAAM;AACd,IAAA,SAAA,CAAU,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AAAA,EAC3C;AAEA,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,OAAA,GAAU,WAAW,MAAM,UAAA,CAAW,OAAM,EAAG,IAAA,EAAM,aAAa,IAAK,CAAA;AAC7E,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,GAAG,SAAA,EAAW,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,CAAA;AACxE,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,OAAO,EAAE,KAAA,EAAO,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,IAAG,CAAE,CAAA;AAI3E,MAAA,MAAM,CAAA,GAAI,IAAA;AACV,MAAA,MAAM,IAAI,MAAM,CAAA,CAAE,OAAA,IAAW,EAAE,KAAA,IAAS,CAAA,iBAAA,EAAoB,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,IAC1E;AACA,IAAA,OAAO,IAAI,IAAA,EAAK;AAAA,EAClB,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,OAAO,CAAA;AAAA,EACtB;AACF;AASO,SAAS,oBAAA,GAAgC;AAC9C,EAAA,OAAO,aAAA,CAAc,qBAAqB,CAAA;AAC5C","file":"chunk-XL45U3C5.js","sourcesContent":["/**\n * Keycloak Device Authorization Grant (RFC 8628) for the store CLI: a public\n * device-flow client (`skaile-store-cli`) issues a plain realm JWT the store's\n * `AuthGuard` accepts — a CLI can't use the storefront's confidential browser\n * client. Tokens cache at `~/.skaile/auth.json` (0600, shared with `store/cli`);\n * `getAccessToken` refreshes transparently. All targets are env-overridable.\n *\n * @docLink cli/dev-guide#store-auth\n */\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * A PKCE (RFC 7636) `code_verifier` + its S256 `code_challenge`. Keycloak\n * enforces PKCE on the `skaile-store-cli` device flow (the device-authorization\n * request 400s with \"Missing parameter: code_challenge_method\" otherwise), so\n * every `deviceLogin` mints a fresh pair: the challenge goes on the device-auth\n * request, the verifier on the matching token poll. `base64url` (no padding)\n * per the spec.\n */\nfunction makePkcePair(): { verifier: string; challenge: string } {\n const verifier = randomBytes(32).toString(\"base64url\");\n const challenge = createHash(\"sha256\").update(verifier).digest(\"base64url\");\n return { verifier, challenge };\n}\n\n/** Resolved auth targets — every field has a prod default, all env-overridable. */\nexport interface StoreAuthConfig {\n keycloakUrl: string;\n realm: string;\n clientId: string;\n authFile: string;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Load the auth config. Defaults target the production store\n * (`auth.skaile.ai`, realm `skaile-store`, public client `skaile-store-cli`);\n * override with `SKAILE_KEYCLOAK_URL` / `SKAILE_STORE_REALM` /\n * `SKAILE_STORE_CLI_CLIENT` / `SKAILE_AUTH_FILE` for dev or a preview deploy.\n */\nexport function loadStoreAuthConfig(): StoreAuthConfig {\n const home = process.env.SKAILE_HOME ?? join(homedir(), \".skaile\");\n return {\n keycloakUrl: trimTrailingSlash(process.env.SKAILE_KEYCLOAK_URL ?? \"https://auth.skaile.ai\"),\n realm: process.env.SKAILE_STORE_REALM ?? \"skaile-store\",\n clientId: process.env.SKAILE_STORE_CLI_CLIENT ?? \"skaile-store-cli\",\n authFile: process.env.SKAILE_AUTH_FILE ?? join(home, \"auth.json\"),\n };\n}\n\n/** Keycloak OIDC endpoints derived from the realm base. */\nfunction oidc(config: StoreAuthConfig): { deviceAuth: string; token: string } {\n const base = `${config.keycloakUrl}/realms/${config.realm}/protocol/openid-connect`;\n return { deviceAuth: `${base}/auth/device`, token: `${base}/token` };\n}\n\nexport interface StoredAuth {\n access_token: string;\n refresh_token?: string;\n /** Epoch ms when the access token expires (with a safety margin already applied). */\n expires_at: number;\n}\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n error?: string;\n error_description?: string;\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete?: string;\n interval?: number;\n expires_in: number;\n}\n\n/** Refresh this many ms before real expiry so an in-flight call never 401s. */\nconst EXPIRY_MARGIN_MS = 30_000;\n\n/** Injectable seams so the poll loop is unit-testable without real time or network. */\nexport interface AuthDeps {\n fetchImpl?: typeof fetch;\n sleep?: (ms: number) => Promise<void>;\n /** Called once with the verification URL + user code (default: write to stderr). */\n onPrompt?: (url: string, userCode: string) => void;\n}\n\nfunction readAuth(config: StoreAuthConfig): StoredAuth | null {\n try {\n const parsed = JSON.parse(readFileSync(config.authFile, \"utf-8\"));\n // Reject a corrupt / hand-edited file rather than treat it as a session.\n if (parsed && typeof parsed.access_token === \"string\") return parsed as StoredAuth;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * The caller's store identity, decoded LOCALLY from the cached token's JWT\n * payload (no network, no signature check — this is an author-side hint, not an\n * authorization decision). Returns `null` when not signed in or the token is\n * unreadable. `githubLogin` is the verified GitHub handle (the preferred\n * publisher namespace); `username` is the Keycloak `preferred_username`.\n */\nexport function getStoreIdentity(\n config: StoreAuthConfig = loadStoreAuthConfig(),\n): { username?: string; githubLogin?: string } | null {\n const auth = readAuth(config);\n if (!auth) return null;\n try {\n const payload = auth.access_token.split(\".\")[1];\n const json = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf-8\"));\n const username = typeof json.preferred_username === \"string\" ? json.preferred_username : undefined;\n const githubLogin = typeof json.github_login === \"string\" ? json.github_login : undefined;\n return username || githubLogin ? { username, githubLogin } : null;\n } catch {\n return null;\n }\n}\n\nfunction writeAuth(config: StoreAuthConfig, auth: StoredAuth): void {\n mkdirSync(dirname(config.authFile), { recursive: true });\n writeFileSync(config.authFile, JSON.stringify(auth, null, 2), { mode: 0o600 });\n chmodSync(config.authFile, 0o600);\n}\n\n/** Remove the cached session (sign out). */\nexport function logout(config: StoreAuthConfig): void {\n try {\n rmSync(config.authFile);\n } catch {\n // already absent — nothing to do\n }\n}\n\n/** True when a cached, well-formed session exists (validity/refresh resolved on use). */\nexport function hasStoredAuth(config: StoreAuthConfig): boolean {\n return readAuth(config) !== null;\n}\n\nfunction tokenToStored(token: TokenResponse): StoredAuth {\n return {\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n expires_at: Date.now() + (token.expires_in ?? 300) * 1000 - EXPIRY_MARGIN_MS,\n };\n}\n\nconst defaultSleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\nfunction defaultPrompt(url: string, userCode: string): void {\n process.stderr.write(\n `\\nTo sign in, open:\\n\\n ${url}\\n\\nand enter the code: ${userCode}\\n\\nWaiting for approval…\\n`,\n );\n}\n\n/**\n * Run the device flow to completion, persisting tokens on success. Prints the\n * verification URL + user code for the operator to approve in a browser.\n */\nexport async function deviceLogin(\n config: StoreAuthConfig,\n deps: AuthDeps = {},\n): Promise<StoredAuth> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? defaultSleep;\n const onPrompt = deps.onPrompt ?? defaultPrompt;\n const endpoints = oidc(config);\n const pkce = makePkcePair();\n\n const deviceRes = await fetchImpl(endpoints.deviceAuth, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n client_id: config.clientId,\n scope: \"openid profile\",\n code_challenge: pkce.challenge,\n code_challenge_method: \"S256\",\n }),\n });\n if (!deviceRes.ok) {\n throw new Error(\n `Device authorization request failed (${deviceRes.status}): ${await deviceRes.text()}`,\n );\n }\n const device = (await deviceRes.json()) as DeviceAuthResponse;\n\n onPrompt(device.verification_uri_complete ?? device.verification_uri, device.user_code);\n\n const intervalMs = (device.interval ?? 5) * 1000;\n const deadline = Date.now() + device.expires_in * 1000;\n let waitMs = intervalMs;\n\n while (Date.now() < deadline) {\n await sleep(waitMs);\n const tokenRes = await fetchImpl(endpoints.token, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code: device.device_code,\n client_id: config.clientId,\n code_verifier: pkce.verifier,\n }),\n });\n const token = (await tokenRes.json()) as TokenResponse;\n if (tokenRes.ok) {\n const stored = tokenToStored(token);\n writeAuth(config, stored);\n return stored;\n }\n // RFC 8628 §3.5 pending / slow-down handling.\n if (token.error === \"authorization_pending\") continue;\n if (token.error === \"slow_down\") {\n waitMs += 5_000;\n continue;\n }\n throw new Error(`Sign-in failed: ${token.error_description ?? token.error ?? \"unknown error\"}`);\n }\n throw new Error(\"Sign-in timed out before approval.\");\n}\n\n/** Exchange a refresh token for a fresh access token; persist on success. */\nasync function refresh(\n config: StoreAuthConfig,\n refreshToken: string,\n fetchImpl: typeof fetch,\n): Promise<StoredAuth> {\n const res = await fetchImpl(oidc(config).token, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n client_id: config.clientId,\n }),\n });\n const token = (await res.json()) as TokenResponse;\n if (!res.ok) {\n throw new Error(\n `Token refresh failed: ${token.error_description ?? token.error ?? res.status}`,\n );\n }\n const stored = tokenToStored(token);\n writeAuth(config, stored);\n return stored;\n}\n\n/**\n * Return a valid access token, refreshing if it is expired (or about to). Throws\n * a clear \"run `skaile store login`\" error when there is no usable session.\n */\nexport async function getAccessToken(\n config: StoreAuthConfig,\n deps: AuthDeps = {},\n): Promise<string> {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const auth = readAuth(config);\n if (!auth) throw new Error(\"Not signed in. Run `skaile store login` first.\");\n if (Date.now() < auth.expires_at) return auth.access_token;\n if (!auth.refresh_token) throw new Error(\"Session expired. Run `skaile store login` again.\");\n try {\n return (await refresh(config, auth.refresh_token, fetchImpl)).access_token;\n } catch {\n throw new Error(\"Session expired and refresh failed. Run `skaile store login` again.\");\n }\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { globalSettingsPath, mapLegacyFields } from \"@skaile/workspaces/core\";\nimport { type AuthDeps, getAccessToken, hasStoredAuth, loadStoreAuthConfig } from \"./store-auth.ts\";\n\n/**\n * Connection configuration for the AI Asset Store API. `jwt` is normally left\n * null here and filled per-call by the caller from {@link getStoreToken} (the\n * device-flow session); it stays on the type for the `{ ...config, jwt }` spread.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport interface StoreConfig {\n url: string;\n jwt: string | null;\n}\n\n/**\n * Read the store API base URL (settings `storeUrl` → `SKAILE_STORE_URL` →\n * default). Auth is not read here — it lives in the device-flow session; see\n * {@link getStoreToken}.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport function getStoreConfig(): StoreConfig {\n const settingsPath = globalSettingsPath();\n let settings: Record<string, any> = {};\n if (existsSync(settingsPath)) {\n try {\n settings = mapLegacyFields(JSON.parse(readFileSync(settingsPath, \"utf-8\")));\n } catch {\n settings = {};\n }\n }\n return {\n url: settings.storeUrl ?? process.env.SKAILE_STORE_URL ?? \"https://api.skaile.store\",\n jwt: null,\n };\n}\n\n/**\n * Return a valid store access token (Keycloak device-flow session, refreshed if\n * needed). Throws a \"run `skaile store login`\" error when there is no session.\n * This is the auth source for every `/api/v1/*` publisher call.\n */\nexport function getStoreToken(deps?: AuthDeps): Promise<string> {\n return getAccessToken(loadStoreAuthConfig(), deps);\n}\n\n/**\n * Make an authenticated HTTP request to the store REST API.\n *\n * @param config - Store connection config (URL + auth token).\n * @param path - API path (e.g. `/api/assets`).\n * @param opts - Optional method, body, query params, extra headers, and\n * `timeoutMs` (default 1500). Publisher calls pass a longer timeout because\n * they trigger server-side clone/sync work.\n * @returns Parsed JSON response body.\n * @docLink cli/dev-guide#store-client\n */\nexport async function storeFetch<T = any>(\n config: StoreConfig,\n path: string,\n opts?: {\n method?: string;\n body?: unknown;\n params?: Record<string, string>;\n headers?: Record<string, string>;\n timeoutMs?: number;\n },\n): Promise<T> {\n let url = `${config.url}${path}`;\n if (opts?.params) {\n const sp = new URLSearchParams(opts.params);\n url += `?${sp}`;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(opts?.headers ?? {}),\n };\n if (config.jwt) {\n headers.Authorization = `Bearer ${config.jwt}`;\n }\n\n const fetchOpts: RequestInit = {\n method: opts?.method ?? \"GET\",\n headers,\n };\n if (opts?.body) {\n fetchOpts.body = JSON.stringify(opts.body);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1_500);\n try {\n const res = await fetch(url, { ...fetchOpts, signal: controller.signal });\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));\n // Prefer the store's human-readable `message` (e.g. \"Namespace '@x' is\n // reserved … Sign in with GitHub to claim it\") over the bare `error` code\n // (`UNPROCESSABLE_CONTENT`) — the message is the actionable part.\n const b = body as { message?: string; error?: string };\n throw new Error(b.message ?? b.error ?? `Store API error: ${res.status}`);\n }\n return res.json() as Promise<T>;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/**\n * Return `true` when a store session exists (a Keycloak device-flow token cached\n * at `~/.skaile/auth.json`). Validity/refresh is resolved on use via\n * {@link getStoreToken}.\n *\n * @docLink cli/dev-guide#store-client\n */\nexport function isStoreAuthenticated(): boolean {\n return hasStoredAuth(loadStoreAuthConfig());\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InputSchema } from './chunk-
|
|
1
|
+
import { InputSchema } from './chunk-CP62G5PK.js';
|
|
2
2
|
|
|
3
3
|
// factory-assets/connectors/flow/contract/input-schema.ts
|
|
4
4
|
var INPUT_SCHEMA_SHAPES_HINT = "{ kind: 'text', multiline? }, { kind: 'choice', options: [{ id, label, description? }], multiple? }, { kind: 'form', fields: [{ id, label, type: text|textarea|number|choice|file|date|boolean, required?, options?, accept? }] }, or { kind: 'file', accept?, maxSize?, multiple? }";
|
|
@@ -189,5 +189,5 @@ function describe(value) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
export { INPUT_SCHEMA_SHAPES_HINT, coerceInputSchema };
|
|
192
|
-
//# sourceMappingURL=chunk-
|
|
193
|
-
//# sourceMappingURL=chunk-
|
|
192
|
+
//# sourceMappingURL=chunk-YCMHWNZA.js.map
|
|
193
|
+
//# sourceMappingURL=chunk-YCMHWNZA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../factory-assets/connectors/flow/contract/input-schema.ts"],"names":[],"mappings":"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","file":"chunk-AXEOG4B5.js","sourcesContent":["/**\n * Coercion of an agent-supplied `request_input` schema onto the flow contract.\n *\n * `request_input` used to cast whatever the agent passed straight into\n * `InputSchema`, so a choice gate could be persisted with option ids no host\n * could render — a run parked forever on a question no human could answer.\n * This module tolerates the shapes agents actually reach for (a bare string per\n * option, an option with a label and no id) and refuses the rest, because a\n * refusal the agent can read and retry beats a stored gate nobody can answer.\n *\n * Exported because hosts normalize the same payloads when they read a stored\n * gate; one shared rule is what keeps the write and read sides in agreement.\n */\n\nimport type { InputSchema } from \"@skaile/workspaces/types\";\nimport type * as z from \"zod\";\nimport { InputSchema as InputSchemaContract } from \"./schema.js\";\n\n/**\n * The shapes `request_input` accepts, phrased for an agent retrying a refused\n * call. Every optional key the contract admits is listed, because validation is\n * strict: an agent refused for one unknown key would otherwise read a key it\n * omitted here as illegal too.\n */\nexport const INPUT_SCHEMA_SHAPES_HINT =\n \"{ kind: 'text', multiline? }, { kind: 'choice', options: [{ id, label, description? }], multiple? }, { kind: 'form', fields: [{ id, label, type: text|textarea|number|choice|file|date|boolean, required?, options?, accept? }] }, or { kind: 'file', accept?, maxSize?, multiple? }\";\n\n/**\n * Outcome of {@link coerceInputSchema}: either a schema in the contract shape,\n * or the reasons it was refused — one entry per problem, each naming its path,\n * closed by a final entry naming the accepted shapes. The hint travels with the\n * issues so every caller's refusal is actionable without re-appending it.\n */\nexport type InputSchemaCoercion =\n | { ok: true; schema: InputSchema }\n | { ok: false; issues: string[] };\n\nconst KNOWN_KINDS = [\"text\", \"choice\", \"form\", \"file\"] as const;\n\n/** A coerced option or form field, and whether its id was derived from its label. */\ntype Prepared = { entry: Record<string, unknown>; derivedId: boolean };\n\n/**\n * Brings an untrusted `request_input` schema into the `InputSchema` contract.\n *\n * Tolerated: a JSON string wrapper, a choice option given as a bare string, and\n * an option or form field whose id is missing but whose label can supply one.\n * Refused: an unknown `kind`, an option or field that carries no usable label, a\n * duplicate explicit id, an id or label of the wrong type, and a choice or form\n * with nothing to answer — anything a host could not turn into an answerable\n * control. Options are never dropped: every unusable one produces an issue.\n *\n * @param value Raw `schema` argument as the agent supplied it.\n * @returns The contract-shaped schema, or the issues that make it unanswerable.\n */\nexport function coerceInputSchema(value: unknown): InputSchemaCoercion {\n let candidate: unknown = value;\n if (typeof candidate === \"string\") {\n try {\n candidate = JSON.parse(candidate);\n } catch {\n // A free-text box is still answerable, so an unparseable string keeps the\n // fallback it has always had rather than failing the agent's turn.\n return { ok: true, schema: { kind: \"text\" } };\n }\n }\n if (candidate === null || typeof candidate !== \"object\") {\n return { ok: true, schema: { kind: \"text\" } };\n }\n if (Array.isArray(candidate)) {\n return refuse([\n \"schema: expected an object with a `kind`, got an array — a bare option list needs { kind: 'choice', options: [...] }\",\n ]);\n }\n\n const raw = candidate as Record<string, unknown>;\n const kind = raw.kind;\n // An unknown kind is refused rather than degraded to text: text would discard\n // the agent's intent and hand the user a box the flow cannot consume.\n if (typeof kind !== \"string\" || !KNOWN_KINDS.some((known) => known === kind)) {\n return refuse([\n `schema.kind: expected one of ${KNOWN_KINDS.join(\", \")}, got ${describe(kind)}`,\n ]);\n }\n\n const issues: string[] = [];\n const prepared =\n kind === \"choice\"\n ? prepareChoice(raw, issues)\n : kind === \"form\"\n ? prepareForm(raw, issues)\n : raw;\n if (issues.length > 0) return refuse(issues);\n\n const parsed = InputSchemaContract.safeParse(prepared);\n if (!parsed.success) return refuse(formatIssues(parsed.error.issues));\n return { ok: true, schema: parsed.data };\n}\n\n/** Closes a refusal with the accepted shapes, so no caller has to know to add them. */\nfunction refuse(issues: string[]): InputSchemaCoercion {\n return { ok: false, issues: [...issues, `valid shapes: ${INPUT_SCHEMA_SHAPES_HINT}`] };\n}\n\nfunction prepareChoice(raw: Record<string, unknown>, issues: string[]): Record<string, unknown> {\n const options = raw.options;\n if (!Array.isArray(options)) {\n issues.push(`options: expected an array of choices, got ${describe(options)}`);\n return raw;\n }\n if (options.length === 0) {\n issues.push(\n \"options: expected at least one choice — a choice gate with none cannot be answered\",\n );\n return raw;\n }\n return { ...raw, options: coerceChoices(options, \"options\", issues) };\n}\n\nfunction prepareForm(raw: Record<string, unknown>, issues: string[]): Record<string, unknown> {\n const fields = raw.fields;\n if (!Array.isArray(fields)) {\n issues.push(`fields: expected an array of form fields, got ${describe(fields)}`);\n return raw;\n }\n // A form with no fields is as unanswerable as a choice with no options.\n if (fields.length === 0) {\n issues.push(\"fields: expected at least one field — a form gate with none cannot be answered\");\n return raw;\n }\n const prepared = fields.map((field, index) => coerceField(field, `fields[${index}]`, issues));\n return { ...raw, fields: settleIds(prepared, \"fields\", issues) };\n}\n\nfunction coerceChoices(\n options: unknown[],\n path: string,\n issues: string[],\n): Record<string, unknown>[] {\n const prepared = options.map((option, index) =>\n coerceChoice(option, `${path}[${index}]`, issues),\n );\n return settleIds(prepared, path, issues);\n}\n\nfunction coerceChoice(option: unknown, at: string, issues: string[]): Prepared | undefined {\n if (typeof option === \"string\") {\n const label = option.trim();\n if (label === \"\") {\n issues.push(`${at}: expected a non-empty string`);\n return undefined;\n }\n return { entry: { id: label, label }, derivedId: true };\n }\n if (!isPlainObject(option)) {\n issues.push(\n `${at}: expected a string or an object with { id, label }, got ${describe(option)}`,\n );\n return undefined;\n }\n const identity = coerceIdentity(option, at, issues);\n if (!identity) return undefined;\n const entry: Record<string, unknown> = { ...option, id: identity.id, label: identity.label };\n const { description } = entry;\n // An absent, null, or blank description carries nothing; the contract admits\n // only a non-empty string, and anything else stays for it to reject.\n if (\n description === null ||\n description === undefined ||\n (typeof description === \"string\" && description.trim() === \"\")\n ) {\n delete entry.description;\n }\n return { entry, derivedId: identity.derivedId };\n}\n\nfunction coerceField(field: unknown, at: string, issues: string[]): Prepared | undefined {\n if (!isPlainObject(field)) {\n issues.push(`${at}: expected an object with { id, label, type }, got ${describe(field)}`);\n return undefined;\n }\n const identity = coerceIdentity(field, at, issues);\n if (!identity) return undefined;\n const entry: Record<string, unknown> = { ...field, id: identity.id, label: identity.label };\n const options = field.options;\n // A select with nothing to select is the same unrenderable control a choice\n // gate with no options is, one level down.\n if (field.type === \"choice\" && (!Array.isArray(options) || options.length === 0)) {\n issues.push(\n `${at}.options: expected at least one choice — a choice field with none cannot be answered`,\n );\n return undefined;\n }\n if (Array.isArray(options)) {\n entry.options = coerceChoices(options, `${at}.options`, issues);\n }\n return { entry, derivedId: identity.derivedId };\n}\n\n/**\n * Resolves the `id`/`label` pair of an option or field, letting either supply\n * the other. An id or label of the wrong type is refused rather than replaced:\n * silently swapping in a different id is the failure this module exists to stop\n * — an agent that branches on the id it wrote would never see the substitution.\n */\nfunction coerceIdentity(\n raw: Record<string, unknown>,\n at: string,\n issues: string[],\n): { id: string; label: string; derivedId: boolean } | undefined {\n const id = explicitText(raw.id, at, \"id\", issues);\n const label = explicitText(raw.label, at, \"label\", issues);\n if (!id.ok || !label.ok) return undefined;\n const text = label.value ?? id.value;\n if (text === undefined) {\n issues.push(`${at}: expected a non-empty \\`label\\`, got ${describe(raw.label)}`);\n return undefined;\n }\n return { id: id.value ?? text, label: text, derivedId: id.value === undefined };\n}\n\n/**\n * An explicit `id`/`label`, or `undefined` when the agent supplied nothing to\n * carry (absent, null, or blank). Any other type is intent in the wrong shape,\n * so it is reported instead of dropped.\n */\nfunction explicitText(\n value: unknown,\n at: string,\n key: \"id\" | \"label\",\n issues: string[],\n): { ok: true; value: string | undefined } | { ok: false } {\n if (value === undefined || value === null) return { ok: true, value: undefined };\n if (typeof value !== \"string\") {\n issues.push(`${at}.${key}: expected a non-empty string, got ${describe(value)}`);\n return { ok: false };\n }\n const trimmed = value.trim();\n return { ok: true, value: trimmed === \"\" ? undefined : trimmed };\n}\n\n/**\n * Settles ids across a list of options or fields. Every explicit id is reserved\n * before any derived one is filled in, so a collision's verdict cannot depend on\n * which side of it came first — and a refusal never names an id the agent did\n * not write.\n */\nfunction settleIds(\n prepared: (Prepared | undefined)[],\n path: string,\n issues: string[],\n): Record<string, unknown>[] {\n const used = new Set<string>();\n const reserved = prepared.map((item, index) => {\n if (!item || item.derivedId) return item;\n const id = String(item.entry.id);\n if (used.has(id)) {\n issues.push(\n `${path}[${index}]: duplicate id \"${id}\" — ids must be unique or a host cannot tell the answers apart`,\n );\n return undefined;\n }\n used.add(id);\n return item;\n });\n\n const settled: Record<string, unknown>[] = [];\n reserved.forEach((item, index) => {\n if (!item) return;\n if (item.derivedId) item.entry.id = uniqueId(String(item.entry.id), index, used);\n used.add(String(item.entry.id));\n settled.push(item.entry);\n });\n return settled;\n}\n\nfunction uniqueId(base: string, index: number, used: Set<string>): string {\n if (!used.has(base)) return base;\n // Two options can legitimately share a label; the index disambiguates the id\n // derived from it so the answer stays unambiguous.\n let attempt = index;\n let candidate = `${base}-${attempt}`;\n while (used.has(candidate)) {\n attempt += 1;\n candidate = `${base}-${attempt}`;\n }\n return candidate;\n}\n\nfunction formatIssues(issues: readonly z.core.$ZodIssue[]): string[] {\n return issues.map((issue) => {\n const path = issue.path.reduce<string>((acc, segment) => {\n if (typeof segment === \"number\") return `${acc}[${segment}]`;\n return acc === \"\" ? String(segment) : `${acc}.${String(segment)}`;\n }, \"\");\n return `${path === \"\" ? \"schema\" : path}: ${issue.message}`;\n });\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction describe(value: unknown): string {\n if (value === undefined) return \"nothing\";\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n if (typeof value === \"string\") return `the string ${JSON.stringify(value)}`;\n if (typeof value === \"object\") return \"an object\";\n return `${typeof value} ${String(value)}`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../factory-assets/connectors/flow/contract/input-schema.ts"],"names":[],"mappings":"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","file":"chunk-YCMHWNZA.js","sourcesContent":["/**\n * Coercion of an agent-supplied `request_input` schema onto the flow contract.\n *\n * `request_input` used to cast whatever the agent passed straight into\n * `InputSchema`, so a choice gate could be persisted with option ids no host\n * could render — a run parked forever on a question no human could answer.\n * This module tolerates the shapes agents actually reach for (a bare string per\n * option, an option with a label and no id) and refuses the rest, because a\n * refusal the agent can read and retry beats a stored gate nobody can answer.\n *\n * Exported because hosts normalize the same payloads when they read a stored\n * gate; one shared rule is what keeps the write and read sides in agreement.\n */\n\nimport type { InputSchema } from \"@skaile/workspaces/types\";\nimport type * as z from \"zod\";\nimport { InputSchema as InputSchemaContract } from \"./schema.js\";\n\n/**\n * The shapes `request_input` accepts, phrased for an agent retrying a refused\n * call. Every optional key the contract admits is listed, because validation is\n * strict: an agent refused for one unknown key would otherwise read a key it\n * omitted here as illegal too.\n */\nexport const INPUT_SCHEMA_SHAPES_HINT =\n \"{ kind: 'text', multiline? }, { kind: 'choice', options: [{ id, label, description? }], multiple? }, { kind: 'form', fields: [{ id, label, type: text|textarea|number|choice|file|date|boolean, required?, options?, accept? }] }, or { kind: 'file', accept?, maxSize?, multiple? }\";\n\n/**\n * Outcome of {@link coerceInputSchema}: either a schema in the contract shape,\n * or the reasons it was refused — one entry per problem, each naming its path,\n * closed by a final entry naming the accepted shapes. The hint travels with the\n * issues so every caller's refusal is actionable without re-appending it.\n */\nexport type InputSchemaCoercion =\n | { ok: true; schema: InputSchema }\n | { ok: false; issues: string[] };\n\nconst KNOWN_KINDS = [\"text\", \"choice\", \"form\", \"file\"] as const;\n\n/** A coerced option or form field, and whether its id was derived from its label. */\ntype Prepared = { entry: Record<string, unknown>; derivedId: boolean };\n\n/**\n * Brings an untrusted `request_input` schema into the `InputSchema` contract.\n *\n * Tolerated: a JSON string wrapper, a choice option given as a bare string, and\n * an option or form field whose id is missing but whose label can supply one.\n * Refused: an unknown `kind`, an option or field that carries no usable label, a\n * duplicate explicit id, an id or label of the wrong type, and a choice or form\n * with nothing to answer — anything a host could not turn into an answerable\n * control. Options are never dropped: every unusable one produces an issue.\n *\n * @param value Raw `schema` argument as the agent supplied it.\n * @returns The contract-shaped schema, or the issues that make it unanswerable.\n */\nexport function coerceInputSchema(value: unknown): InputSchemaCoercion {\n let candidate: unknown = value;\n if (typeof candidate === \"string\") {\n try {\n candidate = JSON.parse(candidate);\n } catch {\n // A free-text box is still answerable, so an unparseable string keeps the\n // fallback it has always had rather than failing the agent's turn.\n return { ok: true, schema: { kind: \"text\" } };\n }\n }\n if (candidate === null || typeof candidate !== \"object\") {\n return { ok: true, schema: { kind: \"text\" } };\n }\n if (Array.isArray(candidate)) {\n return refuse([\n \"schema: expected an object with a `kind`, got an array — a bare option list needs { kind: 'choice', options: [...] }\",\n ]);\n }\n\n const raw = candidate as Record<string, unknown>;\n const kind = raw.kind;\n // An unknown kind is refused rather than degraded to text: text would discard\n // the agent's intent and hand the user a box the flow cannot consume.\n if (typeof kind !== \"string\" || !KNOWN_KINDS.some((known) => known === kind)) {\n return refuse([\n `schema.kind: expected one of ${KNOWN_KINDS.join(\", \")}, got ${describe(kind)}`,\n ]);\n }\n\n const issues: string[] = [];\n const prepared =\n kind === \"choice\"\n ? prepareChoice(raw, issues)\n : kind === \"form\"\n ? prepareForm(raw, issues)\n : raw;\n if (issues.length > 0) return refuse(issues);\n\n const parsed = InputSchemaContract.safeParse(prepared);\n if (!parsed.success) return refuse(formatIssues(parsed.error.issues));\n return { ok: true, schema: parsed.data };\n}\n\n/** Closes a refusal with the accepted shapes, so no caller has to know to add them. */\nfunction refuse(issues: string[]): InputSchemaCoercion {\n return { ok: false, issues: [...issues, `valid shapes: ${INPUT_SCHEMA_SHAPES_HINT}`] };\n}\n\nfunction prepareChoice(raw: Record<string, unknown>, issues: string[]): Record<string, unknown> {\n const options = raw.options;\n if (!Array.isArray(options)) {\n issues.push(`options: expected an array of choices, got ${describe(options)}`);\n return raw;\n }\n if (options.length === 0) {\n issues.push(\n \"options: expected at least one choice — a choice gate with none cannot be answered\",\n );\n return raw;\n }\n return { ...raw, options: coerceChoices(options, \"options\", issues) };\n}\n\nfunction prepareForm(raw: Record<string, unknown>, issues: string[]): Record<string, unknown> {\n const fields = raw.fields;\n if (!Array.isArray(fields)) {\n issues.push(`fields: expected an array of form fields, got ${describe(fields)}`);\n return raw;\n }\n // A form with no fields is as unanswerable as a choice with no options.\n if (fields.length === 0) {\n issues.push(\"fields: expected at least one field — a form gate with none cannot be answered\");\n return raw;\n }\n const prepared = fields.map((field, index) => coerceField(field, `fields[${index}]`, issues));\n return { ...raw, fields: settleIds(prepared, \"fields\", issues) };\n}\n\nfunction coerceChoices(\n options: unknown[],\n path: string,\n issues: string[],\n): Record<string, unknown>[] {\n const prepared = options.map((option, index) =>\n coerceChoice(option, `${path}[${index}]`, issues),\n );\n return settleIds(prepared, path, issues);\n}\n\nfunction coerceChoice(option: unknown, at: string, issues: string[]): Prepared | undefined {\n if (typeof option === \"string\") {\n const label = option.trim();\n if (label === \"\") {\n issues.push(`${at}: expected a non-empty string`);\n return undefined;\n }\n return { entry: { id: label, label }, derivedId: true };\n }\n if (!isPlainObject(option)) {\n issues.push(\n `${at}: expected a string or an object with { id, label }, got ${describe(option)}`,\n );\n return undefined;\n }\n const identity = coerceIdentity(option, at, issues);\n if (!identity) return undefined;\n const entry: Record<string, unknown> = { ...option, id: identity.id, label: identity.label };\n const { description } = entry;\n // An absent, null, or blank description carries nothing; the contract admits\n // only a non-empty string, and anything else stays for it to reject.\n if (\n description === null ||\n description === undefined ||\n (typeof description === \"string\" && description.trim() === \"\")\n ) {\n delete entry.description;\n }\n return { entry, derivedId: identity.derivedId };\n}\n\nfunction coerceField(field: unknown, at: string, issues: string[]): Prepared | undefined {\n if (!isPlainObject(field)) {\n issues.push(`${at}: expected an object with { id, label, type }, got ${describe(field)}`);\n return undefined;\n }\n const identity = coerceIdentity(field, at, issues);\n if (!identity) return undefined;\n const entry: Record<string, unknown> = { ...field, id: identity.id, label: identity.label };\n const options = field.options;\n // A select with nothing to select is the same unrenderable control a choice\n // gate with no options is, one level down.\n if (field.type === \"choice\" && (!Array.isArray(options) || options.length === 0)) {\n issues.push(\n `${at}.options: expected at least one choice — a choice field with none cannot be answered`,\n );\n return undefined;\n }\n if (Array.isArray(options)) {\n entry.options = coerceChoices(options, `${at}.options`, issues);\n }\n return { entry, derivedId: identity.derivedId };\n}\n\n/**\n * Resolves the `id`/`label` pair of an option or field, letting either supply\n * the other. An id or label of the wrong type is refused rather than replaced:\n * silently swapping in a different id is the failure this module exists to stop\n * — an agent that branches on the id it wrote would never see the substitution.\n */\nfunction coerceIdentity(\n raw: Record<string, unknown>,\n at: string,\n issues: string[],\n): { id: string; label: string; derivedId: boolean } | undefined {\n const id = explicitText(raw.id, at, \"id\", issues);\n const label = explicitText(raw.label, at, \"label\", issues);\n if (!id.ok || !label.ok) return undefined;\n const text = label.value ?? id.value;\n if (text === undefined) {\n issues.push(`${at}: expected a non-empty \\`label\\`, got ${describe(raw.label)}`);\n return undefined;\n }\n return { id: id.value ?? text, label: text, derivedId: id.value === undefined };\n}\n\n/**\n * An explicit `id`/`label`, or `undefined` when the agent supplied nothing to\n * carry (absent, null, or blank). Any other type is intent in the wrong shape,\n * so it is reported instead of dropped.\n */\nfunction explicitText(\n value: unknown,\n at: string,\n key: \"id\" | \"label\",\n issues: string[],\n): { ok: true; value: string | undefined } | { ok: false } {\n if (value === undefined || value === null) return { ok: true, value: undefined };\n if (typeof value !== \"string\") {\n issues.push(`${at}.${key}: expected a non-empty string, got ${describe(value)}`);\n return { ok: false };\n }\n const trimmed = value.trim();\n return { ok: true, value: trimmed === \"\" ? undefined : trimmed };\n}\n\n/**\n * Settles ids across a list of options or fields. Every explicit id is reserved\n * before any derived one is filled in, so a collision's verdict cannot depend on\n * which side of it came first — and a refusal never names an id the agent did\n * not write.\n */\nfunction settleIds(\n prepared: (Prepared | undefined)[],\n path: string,\n issues: string[],\n): Record<string, unknown>[] {\n const used = new Set<string>();\n const reserved = prepared.map((item, index) => {\n if (!item || item.derivedId) return item;\n const id = String(item.entry.id);\n if (used.has(id)) {\n issues.push(\n `${path}[${index}]: duplicate id \"${id}\" — ids must be unique or a host cannot tell the answers apart`,\n );\n return undefined;\n }\n used.add(id);\n return item;\n });\n\n const settled: Record<string, unknown>[] = [];\n reserved.forEach((item, index) => {\n if (!item) return;\n if (item.derivedId) item.entry.id = uniqueId(String(item.entry.id), index, used);\n used.add(String(item.entry.id));\n settled.push(item.entry);\n });\n return settled;\n}\n\nfunction uniqueId(base: string, index: number, used: Set<string>): string {\n if (!used.has(base)) return base;\n // Two options can legitimately share a label; the index disambiguates the id\n // derived from it so the answer stays unambiguous.\n let attempt = index;\n let candidate = `${base}-${attempt}`;\n while (used.has(candidate)) {\n attempt += 1;\n candidate = `${base}-${attempt}`;\n }\n return candidate;\n}\n\nfunction formatIssues(issues: readonly z.core.$ZodIssue[]): string[] {\n return issues.map((issue) => {\n const path = issue.path.reduce<string>((acc, segment) => {\n if (typeof segment === \"number\") return `${acc}[${segment}]`;\n return acc === \"\" ? String(segment) : `${acc}.${String(segment)}`;\n }, \"\");\n return `${path === \"\" ? \"schema\" : path}: ${issue.message}`;\n });\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction describe(value: unknown): string {\n if (value === undefined) return \"nothing\";\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n if (typeof value === \"string\") return `the string ${JSON.stringify(value)}`;\n if (typeof value === \"object\") return \"an object\";\n return `${typeof value} ${String(value)}`;\n}\n"]}
|