@the-open-engine/zeroshot 6.29.0 → 6.30.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 +4 -0
- package/cli/index.js +2 -4
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +0 -1
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/agent-cli-provider/contract-actions.d.ts +1 -1
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +5 -5
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +2 -2
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-support.js +2 -2
- package/lib/agent-cli-provider/contract-support.js.map +1 -1
- package/lib/agent-cli-provider/contract.d.ts +1 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract.js +1 -1
- package/lib/agent-cli-provider/contract.js.map +1 -1
- package/lib/agent-cli-provider/index.d.ts +1 -1
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +2 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +1 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +4 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +3 -3
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +22 -48
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/cluster/client.cjs +30 -7
- package/lib/cluster/client.d.cts +52 -0
- package/lib/cluster/client.d.mts +52 -0
- package/lib/cluster/client.d.ts +4 -6
- package/lib/cluster/client.mjs +32 -9
- package/lib/cluster/connection-state.cjs +25 -0
- package/lib/cluster/connection-state.d.cts +13 -0
- package/lib/cluster/connection-state.d.mts +13 -0
- package/lib/cluster/connection-state.d.ts +13 -0
- package/lib/cluster/connection-state.mjs +21 -0
- package/lib/{target/hosted-run/contracts.js → cluster/connection-types.cjs} +0 -1
- package/lib/cluster/connection-types.d.cts +33 -0
- package/lib/cluster/connection-types.d.mts +33 -0
- package/lib/cluster/connection-types.d.ts +33 -0
- package/lib/cluster/connection-types.mjs +1 -0
- package/lib/cluster/connection.cjs +18 -46
- package/lib/cluster/connection.d.cts +26 -0
- package/lib/cluster/connection.d.mts +26 -0
- package/lib/cluster/connection.d.ts +7 -25
- package/lib/cluster/connection.mjs +12 -42
- package/lib/cluster/errors.cjs +9 -1
- package/lib/cluster/errors.d.cts +29 -0
- package/lib/cluster/errors.d.mts +29 -0
- package/lib/cluster/errors.d.ts +4 -0
- package/lib/cluster/errors.mjs +7 -0
- package/lib/cluster/frames.d.cts +12 -0
- package/lib/cluster/frames.d.mts +12 -0
- package/lib/cluster/generated/protocol-schema.d.cts +1 -0
- package/lib/cluster/generated/protocol-schema.d.mts +1 -0
- package/lib/cluster/generated/protocol.d.cts +781 -0
- package/lib/cluster/generated/protocol.d.mts +781 -0
- package/lib/cluster/index.cjs +2 -2
- package/lib/cluster/index.d.cts +13 -0
- package/lib/cluster/index.d.mts +13 -0
- package/lib/cluster/index.d.ts +3 -3
- package/lib/cluster/index.mjs +2 -2
- package/lib/cluster/json-source.cjs +1 -0
- package/lib/cluster/json-source.d.cts +5 -0
- package/lib/cluster/json-source.d.mts +5 -0
- package/lib/cluster/json-source.d.ts +1 -0
- package/lib/cluster/json-source.mjs +1 -0
- package/lib/cluster/payload-value.cjs +1 -0
- package/lib/cluster/payload-value.d.cts +8 -0
- package/lib/cluster/payload-value.d.mts +8 -0
- package/lib/cluster/payload-value.d.ts +1 -0
- package/lib/cluster/payload-value.mjs +1 -0
- package/lib/cluster/queue.d.cts +15 -0
- package/lib/cluster/queue.d.mts +15 -0
- package/lib/cluster/socket.cjs +8 -0
- package/lib/cluster/socket.d.cts +13 -0
- package/lib/cluster/socket.d.mts +13 -0
- package/lib/cluster/socket.d.ts +2 -0
- package/lib/cluster/socket.mjs +7 -0
- package/lib/cluster/subscriptions.d.cts +73 -0
- package/lib/cluster/subscriptions.d.mts +73 -0
- package/lib/cluster/subscriptions.d.ts +5 -1
- package/lib/cluster/validators.cjs +1 -0
- package/lib/cluster/validators.d.cts +7 -0
- package/lib/cluster/validators.d.mts +7 -0
- package/lib/cluster/validators.d.ts +1 -0
- package/lib/cluster/validators.mjs +1 -0
- package/lib/cluster-worker/engine-adapter-common.js +37 -0
- package/lib/cluster-worker/engine-adapter.js +24 -30
- package/lib/cluster-worker/engine-start.js +59 -0
- package/lib/cluster-worker/index.js +49 -67
- package/lib/cluster-worker/profiles.js +1 -42
- package/lib/cluster-worker/runtime-dependencies.js +49 -0
- package/lib/cluster-worker/runtime-support.js +2 -34
- package/lib/cluster-worker/terminal-normalizer.js +9 -10
- package/lib/hosted-session/authority.cjs +61 -0
- package/lib/hosted-session/authority.d.cts +3 -0
- package/lib/hosted-session/authority.d.mts +3 -0
- package/lib/hosted-session/authority.d.ts +3 -0
- package/lib/hosted-session/authority.mjs +57 -0
- package/lib/hosted-session/coordinator.cjs +78 -51
- package/lib/hosted-session/coordinator.d.cts +11 -0
- package/lib/hosted-session/coordinator.d.mts +11 -0
- package/lib/hosted-session/coordinator.d.ts +4 -2
- package/lib/hosted-session/coordinator.mjs +75 -51
- package/lib/hosted-session/errors.cjs +25 -0
- package/lib/hosted-session/errors.d.cts +10 -0
- package/lib/hosted-session/errors.d.mts +10 -0
- package/lib/hosted-session/errors.d.ts +10 -0
- package/lib/hosted-session/errors.mjs +19 -0
- package/lib/hosted-session/index.cjs +4 -1
- package/lib/hosted-session/index.d.cts +2 -0
- package/lib/hosted-session/index.d.mts +2 -0
- package/lib/hosted-session/index.d.ts +2 -2
- package/lib/hosted-session/index.mjs +1 -1
- package/lib/hosted-session/reconnecting-watch.cjs +131 -0
- package/lib/hosted-session/reconnecting-watch.d.cts +13 -0
- package/lib/hosted-session/reconnecting-watch.d.mts +13 -0
- package/lib/hosted-session/reconnecting-watch.d.ts +13 -0
- package/lib/hosted-session/reconnecting-watch.mjs +127 -0
- package/lib/hosted-session/types.d.cts +32 -0
- package/lib/hosted-session/types.d.mts +32 -0
- package/lib/hosted-session/types.d.ts +19 -7
- package/lib/hosted-target/access-url.cjs +44 -0
- package/lib/hosted-target/access-url.d.cts +2 -0
- package/lib/hosted-target/access-url.d.mts +2 -0
- package/lib/hosted-target/access-url.d.ts +2 -0
- package/lib/hosted-target/access-url.mjs +41 -0
- package/lib/hosted-target/adapter-request.cjs +12 -0
- package/lib/hosted-target/adapter-request.d.cts +17 -0
- package/lib/hosted-target/adapter-request.d.mts +17 -0
- package/lib/hosted-target/adapter-request.d.ts +17 -0
- package/lib/hosted-target/adapter-request.mjs +9 -0
- package/lib/hosted-target/adapter-types.cjs +2 -0
- package/lib/hosted-target/adapter-types.d.cts +27 -0
- package/lib/hosted-target/adapter-types.d.mts +27 -0
- package/lib/hosted-target/adapter-types.d.ts +27 -0
- package/lib/hosted-target/adapter-types.mjs +1 -0
- package/lib/hosted-target/bounds.cjs +10 -0
- package/lib/hosted-target/bounds.d.cts +7 -0
- package/lib/hosted-target/bounds.d.mts +7 -0
- package/lib/hosted-target/bounds.d.ts +7 -0
- package/lib/hosted-target/bounds.mjs +7 -0
- package/lib/hosted-target/capsule-error-response.cjs +76 -0
- package/lib/hosted-target/capsule-error-response.d.cts +2 -0
- package/lib/hosted-target/capsule-error-response.d.mts +2 -0
- package/lib/hosted-target/capsule-error-response.d.ts +2 -0
- package/lib/hosted-target/capsule-error-response.mjs +73 -0
- package/lib/hosted-target/errors.cjs +88 -0
- package/lib/hosted-target/errors.d.cts +36 -0
- package/lib/hosted-target/errors.d.mts +36 -0
- package/lib/hosted-target/errors.d.ts +36 -0
- package/lib/hosted-target/errors.mjs +75 -0
- package/lib/hosted-target/index.cjs +16 -0
- package/lib/hosted-target/index.d.cts +3 -0
- package/lib/hosted-target/index.d.mts +3 -0
- package/lib/hosted-target/index.d.ts +3 -0
- package/lib/hosted-target/index.mjs +2 -0
- package/lib/hosted-target/response-validation.cjs +169 -0
- package/lib/hosted-target/response-validation.d.cts +5 -0
- package/lib/hosted-target/response-validation.d.mts +5 -0
- package/lib/hosted-target/response-validation.d.ts +5 -0
- package/lib/hosted-target/response-validation.mjs +163 -0
- package/lib/hosted-target/retry-executor.cjs +51 -0
- package/lib/hosted-target/retry-executor.d.cts +8 -0
- package/lib/hosted-target/retry-executor.d.mts +8 -0
- package/lib/hosted-target/retry-executor.d.ts +8 -0
- package/lib/hosted-target/retry-executor.mjs +48 -0
- package/lib/hosted-target/retry.cjs +78 -0
- package/lib/hosted-target/retry.d.cts +9 -0
- package/lib/hosted-target/retry.d.mts +9 -0
- package/lib/hosted-target/retry.d.ts +9 -0
- package/lib/hosted-target/retry.mjs +73 -0
- package/lib/hosted-target/target-adapter.cjs +10 -0
- package/lib/hosted-target/target-adapter.d.cts +3 -0
- package/lib/hosted-target/target-adapter.d.mts +3 -0
- package/lib/hosted-target/target-adapter.d.ts +3 -0
- package/lib/hosted-target/target-adapter.mjs +7 -0
- package/lib/hosted-target/types.cjs +4 -0
- package/lib/hosted-target/types.d.cts +50 -0
- package/lib/hosted-target/types.d.mts +50 -0
- package/lib/hosted-target/types.d.ts +50 -0
- package/lib/hosted-target/types.mjs +1 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.cjs +185 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +13 -0
- package/lib/hosted-target/zero-cloud-v1-adapter.mjs +181 -0
- package/lib/start-cluster.js +3 -3
- package/lib/target/bounded-response.cjs +51 -0
- package/lib/target/bounded-response.d.cts +1 -0
- package/lib/target/bounded-response.d.mts +1 -0
- package/lib/target/bounded-response.d.ts +1 -0
- package/lib/target/bounded-response.js +51 -0
- package/lib/target/bounded-response.mjs +48 -0
- package/lib/target/credential-lock.js +1 -3
- package/lib/target/credential-store.js +9 -4
- package/lib/target/device-flow.cjs +213 -0
- package/lib/target/device-flow.d.cts +54 -0
- package/lib/target/device-flow.d.mts +54 -0
- package/lib/target/device-flow.d.ts +26 -14
- package/lib/target/device-flow.js +162 -58
- package/lib/target/device-flow.mjs +203 -0
- package/lib/target/discovery-errors.cjs +10 -0
- package/lib/target/discovery-errors.d.cts +3 -0
- package/lib/target/discovery-errors.d.mts +3 -0
- package/lib/target/discovery-errors.d.ts +3 -0
- package/lib/target/discovery-errors.js +10 -0
- package/lib/target/discovery-errors.mjs +6 -0
- package/lib/target/discovery-sections.cjs +191 -0
- package/lib/target/discovery-sections.d.cts +52 -0
- package/lib/target/discovery-sections.d.mts +52 -0
- package/lib/target/discovery-sections.d.ts +52 -0
- package/lib/target/discovery-sections.js +191 -0
- package/lib/target/discovery-sections.mjs +179 -0
- package/lib/target/discovery-validation.cjs +109 -0
- package/lib/target/discovery-validation.d.cts +28 -0
- package/lib/target/discovery-validation.d.mts +28 -0
- package/lib/target/discovery-validation.d.ts +28 -0
- package/lib/target/discovery-validation.js +109 -0
- package/lib/target/discovery-validation.mjs +99 -0
- package/lib/target/discovery.cjs +112 -0
- package/lib/target/discovery.d.cts +74 -0
- package/lib/target/discovery.d.mts +74 -0
- package/lib/target/discovery.d.ts +67 -5
- package/lib/target/discovery.js +94 -79
- package/lib/target/discovery.mjs +105 -0
- package/lib/target/index.d.ts +6 -6
- package/lib/target/index.js +36 -36
- package/lib/target/oauth-http.d.ts +2 -0
- package/lib/target/oauth-http.js +25 -0
- package/lib/target/refresh-exchange.d.ts +11 -0
- package/lib/target/refresh-exchange.js +24 -0
- package/lib/target/refresh-revocation.d.ts +8 -0
- package/lib/target/refresh-revocation.js +19 -0
- package/lib/target/route-template.cjs +85 -0
- package/lib/target/route-template.d.cts +7 -0
- package/lib/target/route-template.d.mts +7 -0
- package/lib/target/route-template.d.ts +7 -0
- package/lib/target/route-template.js +85 -0
- package/lib/target/route-template.mjs +81 -0
- package/lib/target/session-errors.d.ts +4 -0
- package/lib/target/session-errors.js +12 -0
- package/lib/target/session-verification.d.ts +10 -0
- package/lib/target/session-verification.js +42 -0
- package/lib/target/target-registry.d.ts +7 -3
- package/lib/target/target-registry.js +41 -10
- package/lib/target/target-session-manager.d.ts +48 -0
- package/lib/target/target-session-manager.js +226 -0
- package/lib/target/target-session.d.ts +39 -32
- package/lib/target/target-session.js +63 -151
- package/lib/target/token-response.cjs +51 -0
- package/lib/target/token-response.d.cts +7 -0
- package/lib/target/token-response.d.mts +7 -0
- package/lib/target/token-response.d.ts +7 -0
- package/lib/target/token-response.js +51 -0
- package/lib/target/token-response.mjs +48 -0
- package/package.json +14 -7
- package/protocol/openengine-cluster/v1/worker.schema.json +171 -9
- package/scripts/build-cluster.js +23 -3
- package/scripts/opcore-introduced-check.js +0 -2
- package/src/agent-cli-provider/adapters/codex.ts +0 -1
- package/src/agent-cli-provider/contract-actions.ts +9 -5
- package/src/agent-cli-provider/contract-invoke.ts +3 -2
- package/src/agent-cli-provider/contract-support.ts +12 -6
- package/src/agent-cli-provider/contract.ts +6 -1
- package/src/agent-cli-provider/index.ts +1 -0
- package/src/agent-cli-provider/provider-registry.ts +4 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +32 -45
- package/src/cluster/client.ts +42 -15
- package/src/cluster/connection-state.ts +33 -0
- package/src/cluster/connection-types.ts +28 -0
- package/src/cluster/connection.ts +37 -68
- package/src/cluster/errors.ts +5 -0
- package/src/cluster/index.ts +3 -1
- package/src/cluster/json-source.ts +1 -0
- package/src/cluster/payload-value.ts +1 -0
- package/src/cluster/socket.ts +13 -0
- package/src/cluster/subscriptions.ts +7 -2
- package/src/cluster/validators.ts +1 -0
- package/src/cluster/ws.d.ts +1 -1
- package/src/hosted-session/authority.ts +77 -0
- package/src/hosted-session/coordinator.ts +113 -56
- package/src/hosted-session/errors.ts +21 -0
- package/src/hosted-session/index.ts +14 -2
- package/src/hosted-session/reconnecting-watch.ts +148 -0
- package/src/hosted-session/types.ts +22 -8
- package/src/hosted-target/access-url.ts +46 -0
- package/src/hosted-target/adapter-request.ts +28 -0
- package/src/hosted-target/adapter-types.ts +39 -0
- package/src/hosted-target/bounds.ts +1 -0
- package/src/hosted-target/capsule-error-response.ts +107 -0
- package/src/hosted-target/errors.ts +36 -35
- package/src/hosted-target/index.ts +10 -23
- package/src/hosted-target/response-validation.ts +170 -62
- package/src/hosted-target/retry-executor.ts +62 -0
- package/src/hosted-target/retry.ts +47 -11
- package/src/hosted-target/target-adapter.ts +12 -8
- package/src/hosted-target/types.ts +19 -18
- package/src/hosted-target/zero-cloud-v1-adapter.ts +229 -316
- package/src/isolation-manager.js +1 -16
- package/src/target/bounded-response.ts +68 -0
- package/src/target/credential-lock.ts +1 -3
- package/src/target/credential-store.ts +14 -10
- package/src/target/device-flow.ts +209 -85
- package/src/target/discovery-errors.ts +6 -0
- package/src/target/discovery-sections.ts +251 -0
- package/src/target/discovery-validation.ts +144 -0
- package/src/target/discovery.ts +163 -97
- package/src/target/index.ts +15 -11
- package/src/target/oauth-http.ts +27 -0
- package/src/target/refresh-exchange.ts +36 -0
- package/src/target/refresh-revocation.ts +29 -0
- package/src/target/route-template.ts +105 -0
- package/src/target/session-errors.ts +9 -0
- package/src/target/session-verification.ts +51 -0
- package/src/target/target-registry.ts +60 -27
- package/src/target/target-session-manager.ts +285 -0
- package/src/target/target-session.ts +105 -226
- package/src/target/token-response.ts +62 -0
- package/docker/zeroshot-oecp/Cargo.toml +0 -12
- package/docker/zeroshot-oecp/Dockerfile +0 -65
- package/docker/zeroshot-oecp/src/main.rs +0 -32
- package/lib/target/bounded-json.d.ts +0 -6
- package/lib/target/bounded-json.js +0 -43
- package/lib/target/hosted-run/client.d.ts +0 -26
- package/lib/target/hosted-run/client.js +0 -158
- package/lib/target/hosted-run/commands.d.ts +0 -4
- package/lib/target/hosted-run/commands.js +0 -113
- package/lib/target/hosted-run/contracts.d.ts +0 -49
- package/lib/target/hosted-run/input.d.ts +0 -5
- package/lib/target/hosted-run/input.js +0 -200
- package/lib/target/hosted-run.d.ts +0 -4
- package/lib/target/hosted-run.js +0 -12
- package/src/target/bounded-json.ts +0 -48
- package/src/target/hosted-run/client.ts +0 -198
- package/src/target/hosted-run/commands.ts +0 -140
- package/src/target/hosted-run/contracts.ts +0 -53
- package/src/target/hosted-run/input.ts +0 -199
- package/src/target/hosted-run.ts +0 -8
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
async function declaredResponseBytes(
|
|
2
|
+
response: Response,
|
|
3
|
+
maxBytes: number,
|
|
4
|
+
error: (kind: 'size' | 'json') => Error,
|
|
5
|
+
): Promise<number> {
|
|
6
|
+
const declared = response.headers.get('content-length');
|
|
7
|
+
if (declared === null) return 0;
|
|
8
|
+
if (!/^\d+$/.test(declared) || Number(declared) > maxBytes) {
|
|
9
|
+
await response.body?.cancel().catch(() => undefined);
|
|
10
|
+
throw error('size');
|
|
11
|
+
}
|
|
12
|
+
return Number(declared);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function growBuffer(
|
|
16
|
+
bytes: Uint8Array,
|
|
17
|
+
required: number,
|
|
18
|
+
used: number,
|
|
19
|
+
maxBytes: number,
|
|
20
|
+
): Uint8Array {
|
|
21
|
+
const capacity = Math.min(
|
|
22
|
+
maxBytes,
|
|
23
|
+
Math.max(required, Math.max(bytes.byteLength * 2, 8 * 1024)),
|
|
24
|
+
);
|
|
25
|
+
const grown = new Uint8Array(capacity);
|
|
26
|
+
grown.set(bytes.subarray(0, used));
|
|
27
|
+
return grown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function decodeJson(
|
|
31
|
+
bytes: Uint8Array,
|
|
32
|
+
error: (kind: 'size' | 'json') => Error,
|
|
33
|
+
): unknown {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
36
|
+
} catch {
|
|
37
|
+
throw error('json');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function readBoundedResponseJson(
|
|
42
|
+
response: Response,
|
|
43
|
+
maxBytes: number,
|
|
44
|
+
error: (kind: 'size' | 'json') => Error,
|
|
45
|
+
): Promise<unknown> {
|
|
46
|
+
const declaredBytes = await declaredResponseBytes(response, maxBytes, error);
|
|
47
|
+
const reader = response.body?.getReader();
|
|
48
|
+
if (reader === undefined) return decodeJson(new Uint8Array(), error);
|
|
49
|
+
let bytes: Uint8Array = new Uint8Array(
|
|
50
|
+
Math.min(maxBytes, Math.max(declaredBytes, 8 * 1024)),
|
|
51
|
+
);
|
|
52
|
+
let total = 0;
|
|
53
|
+
while (true) {
|
|
54
|
+
const { done, value } = await reader.read();
|
|
55
|
+
if (done) break;
|
|
56
|
+
const nextTotal = total + value.byteLength;
|
|
57
|
+
if (nextTotal > maxBytes) {
|
|
58
|
+
await reader.cancel().catch(() => undefined);
|
|
59
|
+
throw error('size');
|
|
60
|
+
}
|
|
61
|
+
if (nextTotal > bytes.byteLength) {
|
|
62
|
+
bytes = growBuffer(bytes, nextTotal, total, maxBytes);
|
|
63
|
+
}
|
|
64
|
+
bytes.set(value, total);
|
|
65
|
+
total = nextTotal;
|
|
66
|
+
}
|
|
67
|
+
return decodeJson(bytes.subarray(0, total), error);
|
|
68
|
+
}
|
|
@@ -22,16 +22,16 @@ export const TARGET_ACCOUNT = 'refresh-token';
|
|
|
22
22
|
|
|
23
23
|
export class KeyringCredentialStore implements TargetCredentialStore {
|
|
24
24
|
private readonly Entry: new (service: string, account: string) => {
|
|
25
|
-
getPassword(): string;
|
|
25
|
+
getPassword(): string | null;
|
|
26
26
|
setPassword(password: string): void;
|
|
27
|
-
deletePassword():
|
|
27
|
+
deletePassword(): boolean;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
private constructor(
|
|
31
31
|
Entry: new (service: string, account: string) => {
|
|
32
|
-
getPassword(): string;
|
|
32
|
+
getPassword(): string | null;
|
|
33
33
|
setPassword(password: string): void;
|
|
34
|
-
deletePassword():
|
|
34
|
+
deletePassword(): boolean;
|
|
35
35
|
},
|
|
36
36
|
) {
|
|
37
37
|
this.Entry = Entry;
|
|
@@ -39,9 +39,9 @@ export class KeyringCredentialStore implements TargetCredentialStore {
|
|
|
39
39
|
|
|
40
40
|
static async create(): Promise<KeyringCredentialStore> {
|
|
41
41
|
let keyringModule: { Entry: new (service: string, account: string) => {
|
|
42
|
-
getPassword(): string;
|
|
42
|
+
getPassword(): string | null;
|
|
43
43
|
setPassword(password: string): void;
|
|
44
|
-
deletePassword():
|
|
44
|
+
deletePassword(): boolean;
|
|
45
45
|
} };
|
|
46
46
|
try {
|
|
47
47
|
keyringModule = await import('@napi-rs/keyring') as typeof keyringModule;
|
|
@@ -59,13 +59,17 @@ export class KeyringCredentialStore implements TargetCredentialStore {
|
|
|
59
59
|
const entry = new this.Entry(service, account);
|
|
60
60
|
return entry.getPassword();
|
|
61
61
|
} catch {
|
|
62
|
-
|
|
62
|
+
throw new CredentialStoreUnavailableError('The secure credential store could not be read.');
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async set(service: string, account: string, token: string): Promise<void> {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
try {
|
|
68
|
+
const entry = new this.Entry(service, account);
|
|
69
|
+
entry.setPassword(token);
|
|
70
|
+
} catch {
|
|
71
|
+
throw new CredentialStoreUnavailableError('The secure credential store could not be updated.');
|
|
72
|
+
}
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
async delete(service: string, account: string): Promise<void> {
|
|
@@ -73,7 +77,7 @@ export class KeyringCredentialStore implements TargetCredentialStore {
|
|
|
73
77
|
const entry = new this.Entry(service, account);
|
|
74
78
|
entry.deletePassword();
|
|
75
79
|
} catch {
|
|
76
|
-
|
|
80
|
+
throw new CredentialStoreUnavailableError('The secure credential store could not be cleared.');
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { readBoundedResponseJson } from './bounded-response.js';
|
|
2
|
+
import { parseTokenResponse, type TokenResponse } from './token-response.js';
|
|
3
|
+
export { parseTokenResponse, type TokenResponse } from './token-response.js';
|
|
1
4
|
export interface DeviceCodeResponse {
|
|
2
5
|
readonly device_code: string;
|
|
3
6
|
readonly user_code: string;
|
|
@@ -7,12 +10,36 @@ export interface DeviceCodeResponse {
|
|
|
7
10
|
readonly interval: number;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
13
|
+
|
|
14
|
+
export interface DeviceExchangeContext {
|
|
15
|
+
readonly grantType: string;
|
|
16
|
+
readonly deviceToken: string;
|
|
17
|
+
readonly deviceLabel: string;
|
|
18
|
+
readonly audience: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DeviceIdentity {
|
|
22
|
+
readonly token: string;
|
|
23
|
+
readonly label: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function exchangeFields(
|
|
27
|
+
exchange: DeviceExchangeContext | DeviceIdentity | undefined,
|
|
28
|
+
): Readonly<Record<string, string>> {
|
|
29
|
+
if (exchange === undefined) return {};
|
|
30
|
+
if ('grantType' in exchange) {
|
|
31
|
+
return {
|
|
32
|
+
grant_type: exchange.grantType,
|
|
33
|
+
device_token: exchange.deviceToken,
|
|
34
|
+
device_label: exchange.deviceLabel,
|
|
35
|
+
audience: exchange.audience,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
device_token: exchange.token,
|
|
40
|
+
device_label: exchange.label,
|
|
41
|
+
audience: 'admin',
|
|
42
|
+
};
|
|
16
43
|
}
|
|
17
44
|
|
|
18
45
|
export interface HttpTransport {
|
|
@@ -23,11 +50,6 @@ export interface Clock {
|
|
|
23
50
|
now(): number;
|
|
24
51
|
}
|
|
25
52
|
|
|
26
|
-
export interface DeviceIdentity {
|
|
27
|
-
readonly token: string;
|
|
28
|
-
readonly label: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
53
|
export class DeviceFlowDeniedError extends Error {
|
|
32
54
|
constructor() {
|
|
33
55
|
super('Device authorization denied by user');
|
|
@@ -46,7 +68,7 @@ export class UnboundSessionError extends Error {
|
|
|
46
68
|
readonly verificationUri: string;
|
|
47
69
|
constructor(verificationUri: string) {
|
|
48
70
|
super(
|
|
49
|
-
`Session not bound to an organization. Re-approve at ${verificationUri} and select an organization
|
|
71
|
+
`Session not bound to an organization. Re-approve at ${verificationUri} and select an organization.`,
|
|
50
72
|
);
|
|
51
73
|
this.name = 'UnboundSessionError';
|
|
52
74
|
this.verificationUri = verificationUri;
|
|
@@ -54,6 +76,124 @@ export class UnboundSessionError extends Error {
|
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
const DEFAULT_CLOCK: Clock = { now: () => Date.now() };
|
|
79
|
+
const MAX_OAUTH_RESPONSE_BYTES = 64 * 1024;
|
|
80
|
+
const MAX_DEVICE_CODE_BYTES = 16 * 1024;
|
|
81
|
+
const LOOPBACK_HOSTS: Readonly<Record<string, true>> = Object.freeze({
|
|
82
|
+
'127.0.0.1': true,
|
|
83
|
+
'::1': true,
|
|
84
|
+
'[::1]': true,
|
|
85
|
+
});
|
|
86
|
+
const MAX_URI_BYTES = 4 * 1024;
|
|
87
|
+
export function oauthFormRequest(
|
|
88
|
+
body: URLSearchParams,
|
|
89
|
+
signal?: AbortSignal,
|
|
90
|
+
): RequestInit & { redirect: 'error' } {
|
|
91
|
+
const init: RequestInit & { redirect: 'error' } = {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
94
|
+
body: body.toString(),
|
|
95
|
+
redirect: 'error',
|
|
96
|
+
};
|
|
97
|
+
if (signal !== undefined) init.signal = signal;
|
|
98
|
+
return init;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function requireResponseRoute(response: Response, expectedUrl: string): Promise<void> {
|
|
102
|
+
if (!response.url || new URL(response.url).href === expectedUrl) return;
|
|
103
|
+
await response.body?.cancel().catch(() => undefined);
|
|
104
|
+
throw new Error('OAuth response changed target route or authority');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async function readBoundedJson(response: Response): Promise<unknown> {
|
|
109
|
+
return readBoundedResponseJson(response, MAX_OAUTH_RESPONSE_BYTES, (kind) =>
|
|
110
|
+
new Error(kind === 'size' ? 'OAuth response exceeds the size limit' : 'OAuth response is malformed'),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function boundedString(value: unknown, maxBytes: number): value is string {
|
|
115
|
+
return (
|
|
116
|
+
typeof value === 'string' &&
|
|
117
|
+
value.length > 0 &&
|
|
118
|
+
new TextEncoder().encode(value).byteLength <= maxBytes
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function safeVerificationAuthority(url: URL, allowQuery: boolean): boolean {
|
|
123
|
+
const literalLoopback = LOOPBACK_HOSTS[url.hostname] === true;
|
|
124
|
+
return !url.username && !url.password && !url.hash &&
|
|
125
|
+
(allowQuery || !url.search) &&
|
|
126
|
+
(url.protocol === 'https:' || (url.protocol === 'http:' && literalLoopback));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function verificationUrl(value: unknown, allowQuery: boolean): value is string {
|
|
130
|
+
if (
|
|
131
|
+
!boundedString(value, MAX_URI_BYTES) ||
|
|
132
|
+
/[\u0000-\u0020\u007f]|\s/u.test(value)
|
|
133
|
+
) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(value);
|
|
138
|
+
return url.href === value && safeVerificationAuthority(url, allowQuery);
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function deviceFieldsValid(device: Record<string, unknown>): boolean {
|
|
144
|
+
const fields = Object.keys(device);
|
|
145
|
+
const required = ['device_code', 'user_code', 'verification_uri', 'expires_in', 'interval'];
|
|
146
|
+
return fields.every((field) =>
|
|
147
|
+
required.includes(field) || field === 'verification_uri_complete',
|
|
148
|
+
) && required.every((field) => field in device);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function deviceStringsValid(device: Record<string, unknown>): boolean {
|
|
152
|
+
return boundedString(device.device_code, MAX_DEVICE_CODE_BYTES) &&
|
|
153
|
+
boundedString(device.user_code, 256) &&
|
|
154
|
+
verificationUrl(device.verification_uri, false) &&
|
|
155
|
+
(device.verification_uri_complete === undefined ||
|
|
156
|
+
verificationUrl(device.verification_uri_complete, true));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function deviceTimingValid(device: Record<string, unknown>): boolean {
|
|
160
|
+
return Number.isSafeInteger(device.expires_in) &&
|
|
161
|
+
(device.expires_in as number) >= 1 &&
|
|
162
|
+
(device.expires_in as number) <= 86_400 &&
|
|
163
|
+
Number.isSafeInteger(device.interval) &&
|
|
164
|
+
(device.interval as number) >= 0 &&
|
|
165
|
+
(device.interval as number) <= 300;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function browserAuthorityMatches(device: Record<string, unknown>): boolean {
|
|
169
|
+
return device.verification_uri_complete === undefined ||
|
|
170
|
+
new URL(device.verification_uri_complete as string).origin ===
|
|
171
|
+
new URL(device.verification_uri as string).origin;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
function parseDeviceCodeResponse(value: unknown): DeviceCodeResponse {
|
|
176
|
+
const device = object(value, 'Device code');
|
|
177
|
+
if (!deviceFieldsValid(device) || !deviceStringsValid(device) ||
|
|
178
|
+
!deviceTimingValid(device) || !browserAuthorityMatches(device)) {
|
|
179
|
+
throw new Error('Device code response is malformed');
|
|
180
|
+
}
|
|
181
|
+
return Object.freeze(device) as unknown as DeviceCodeResponse;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function parseOAuthError(value: unknown): string {
|
|
185
|
+
const error = object(value, 'OAuth error');
|
|
186
|
+
if (Object.keys(error).length !== 1 || typeof error.error !== 'string') {
|
|
187
|
+
throw new Error('OAuth error response is malformed');
|
|
188
|
+
}
|
|
189
|
+
return error.error;
|
|
190
|
+
}
|
|
191
|
+
function object(value: unknown, field: string): Record<string, unknown> {
|
|
192
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
193
|
+
throw new Error(`${field} response is malformed`);
|
|
194
|
+
}
|
|
195
|
+
return value as Record<string, unknown>;
|
|
196
|
+
}
|
|
57
197
|
|
|
58
198
|
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
59
199
|
return new Promise((resolve, reject) => {
|
|
@@ -68,7 +208,7 @@ function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
|
68
208
|
clearTimeout(timer);
|
|
69
209
|
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
70
210
|
},
|
|
71
|
-
{ once: true }
|
|
211
|
+
{ once: true },
|
|
72
212
|
);
|
|
73
213
|
});
|
|
74
214
|
}
|
|
@@ -77,92 +217,76 @@ export async function requestDeviceCode(
|
|
|
77
217
|
deviceAuthorizationEndpoint: string,
|
|
78
218
|
clientId: string,
|
|
79
219
|
http: HttpTransport,
|
|
80
|
-
signal?: AbortSignal
|
|
220
|
+
signal?: AbortSignal,
|
|
81
221
|
): Promise<DeviceCodeResponse> {
|
|
82
|
-
const body = new URLSearchParams({
|
|
83
|
-
client_id: clientId,
|
|
84
|
-
scope: 'openid',
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const init: RequestInit & { redirect: 'error' } = {
|
|
88
|
-
method: 'POST',
|
|
89
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
90
|
-
body: body.toString(),
|
|
91
|
-
redirect: 'error',
|
|
92
|
-
};
|
|
93
|
-
if (signal) init.signal = signal;
|
|
222
|
+
const body = new URLSearchParams({ client_id: clientId });
|
|
94
223
|
|
|
95
|
-
const response = await http.fetch(
|
|
224
|
+
const response = await http.fetch(
|
|
225
|
+
deviceAuthorizationEndpoint,
|
|
226
|
+
oauthFormRequest(body, signal),
|
|
227
|
+
);
|
|
228
|
+
await requireResponseRoute(response, deviceAuthorizationEndpoint);
|
|
96
229
|
|
|
97
230
|
if (!response.ok) {
|
|
98
|
-
|
|
99
|
-
throw new Error(`Device code request failed (${response.status})
|
|
231
|
+
parseOAuthError(await readBoundedJson(response));
|
|
232
|
+
throw new Error(`Device code request failed (${response.status})`);
|
|
100
233
|
}
|
|
101
234
|
|
|
102
|
-
return (await response
|
|
235
|
+
return parseDeviceCodeResponse(await readBoundedJson(response));
|
|
103
236
|
}
|
|
104
237
|
|
|
105
|
-
export
|
|
106
|
-
tokenEndpoint: string
|
|
107
|
-
clientId: string
|
|
108
|
-
deviceCode: string
|
|
109
|
-
interval: number
|
|
110
|
-
expiresIn: number
|
|
111
|
-
http: HttpTransport
|
|
112
|
-
clock
|
|
113
|
-
signal?: AbortSignal
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let currentInterval = interval;
|
|
118
|
-
|
|
119
|
-
while (clock.now() < deadline) {
|
|
120
|
-
if (signal?.aborted) {
|
|
121
|
-
throw signal.reason ?? new DOMException('Aborted', 'AbortError');
|
|
122
|
-
}
|
|
238
|
+
export interface PollForTokenRequest {
|
|
239
|
+
readonly tokenEndpoint: string;
|
|
240
|
+
readonly clientId: string;
|
|
241
|
+
readonly deviceCode: string;
|
|
242
|
+
readonly interval: number;
|
|
243
|
+
readonly expiresIn: number;
|
|
244
|
+
readonly http: HttpTransport;
|
|
245
|
+
readonly clock?: Clock;
|
|
246
|
+
readonly signal?: AbortSignal;
|
|
247
|
+
readonly exchange?: DeviceExchangeContext | DeviceIdentity;
|
|
248
|
+
}
|
|
249
|
+
type PollResult = TokenResponse | 'authorization_pending' | 'slow_down';
|
|
123
250
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
body.set('audience', 'admin');
|
|
133
|
-
body.set('device_token', deviceIdentity.token);
|
|
134
|
-
body.set('device_label', deviceIdentity.label);
|
|
135
|
-
}
|
|
251
|
+
async function parsePollResponse(response: Response): Promise<PollResult> {
|
|
252
|
+
if (response.ok) return parseTokenResponse(await readBoundedJson(response));
|
|
253
|
+
const error = parseOAuthError(await readBoundedJson(response));
|
|
254
|
+
if (error === 'authorization_pending' || error === 'slow_down') return error;
|
|
255
|
+
if (error === 'access_denied') throw new DeviceFlowDeniedError();
|
|
256
|
+
if (error === 'expired_token') throw new DeviceFlowExpiredError();
|
|
257
|
+
throw new Error('Token endpoint returned an unsupported OAuth error');
|
|
258
|
+
}
|
|
136
259
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
260
|
+
function pollBody(request: PollForTokenRequest): URLSearchParams {
|
|
261
|
+
return new URLSearchParams({
|
|
262
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
263
|
+
device_code: request.deviceCode,
|
|
264
|
+
client_id: request.clientId,
|
|
265
|
+
...exchangeFields(request.exchange),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
144
268
|
|
|
145
|
-
|
|
269
|
+
export async function pollForToken(request: PollForTokenRequest): Promise<TokenResponse> {
|
|
270
|
+
const clock = request.clock ?? DEFAULT_CLOCK;
|
|
271
|
+
const deadline = clock.now() + request.expiresIn * 1000;
|
|
272
|
+
let currentInterval = request.interval;
|
|
146
273
|
|
|
147
|
-
|
|
148
|
-
|
|
274
|
+
while (clock.now() < deadline) {
|
|
275
|
+
if (request.signal?.aborted) {
|
|
276
|
+
throw request.signal.reason ?? new DOMException('Aborted', 'AbortError');
|
|
149
277
|
}
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
case 'expired_token':
|
|
161
|
-
throw new DeviceFlowExpiredError();
|
|
162
|
-
default:
|
|
163
|
-
throw new Error(`Token endpoint error: ${errorBody.error}`);
|
|
278
|
+
await sleep(currentInterval * 1000, request.signal);
|
|
279
|
+
const response = await request.http.fetch(
|
|
280
|
+
request.tokenEndpoint,
|
|
281
|
+
oauthFormRequest(pollBody(request), request.signal),
|
|
282
|
+
);
|
|
283
|
+
const result = await parsePollResponse(response);
|
|
284
|
+
if (result === 'authorization_pending') continue;
|
|
285
|
+
if (result === 'slow_down') {
|
|
286
|
+
currentInterval += 5;
|
|
287
|
+
continue;
|
|
164
288
|
}
|
|
289
|
+
return result;
|
|
165
290
|
}
|
|
166
|
-
|
|
167
291
|
throw new DeviceFlowExpiredError();
|
|
168
292
|
}
|