@the-open-engine/zeroshot 6.29.0 → 6.29.1
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/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/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.js +4 -10
- package/lib/cluster-worker/profiles.js +1 -42
- 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 +4 -7
- 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/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
|
@@ -1,86 +1,194 @@
|
|
|
1
|
-
import { TargetProtocolError } from './errors.
|
|
2
|
-
import { KNOWN_CAPSULE_STATES } from './types.
|
|
3
|
-
import type { Capsule, CapsuleAccess, CapsuleLimits, CapsuleListPage } from './types.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
body
|
|
7
|
-
|
|
8
|
-
context: string,
|
|
9
|
-
): asserts body is Record<string, unknown> {
|
|
10
|
-
if (body === null || typeof body !== 'object') {
|
|
11
|
-
throw new TargetProtocolError(`${context}: expected object, got ${typeof body}`);
|
|
1
|
+
import { TargetProtocolError } from './errors.js';
|
|
2
|
+
import { KNOWN_CAPSULE_STATES } from './types.js';
|
|
3
|
+
import type { Capsule, CapsuleAccess, CapsuleLimits, CapsuleListPage } from './types.js';
|
|
4
|
+
|
|
5
|
+
function closedObject(body: unknown, fields: readonly string[], context: string): Record<string, unknown> {
|
|
6
|
+
if (body === null || typeof body !== 'object' || Array.isArray(body)) {
|
|
7
|
+
throw new TargetProtocolError(`${context} response is malformed`);
|
|
12
8
|
}
|
|
13
9
|
const record = body as Record<string, unknown>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new TargetProtocolError(`${context}: missing required field "${field}"`);
|
|
17
|
-
}
|
|
10
|
+
if (Object.keys(record).length !== fields.length || fields.some((field) => !(field in record))) {
|
|
11
|
+
throw new TargetProtocolError(`${context} response is malformed`);
|
|
18
12
|
}
|
|
13
|
+
return record;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function nonempty(value: unknown): value is string {
|
|
17
|
+
return typeof value === 'string' && value.length > 0;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
type TimestampParts = {
|
|
21
|
+
readonly year: number;
|
|
22
|
+
readonly month: number;
|
|
23
|
+
readonly day: number;
|
|
24
|
+
readonly hour: number;
|
|
25
|
+
readonly minute: number;
|
|
26
|
+
readonly second: number;
|
|
27
|
+
readonly offsetHour: number;
|
|
28
|
+
readonly offsetMinute: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function decimal(value: string, length: number): number | null {
|
|
32
|
+
if (value.length !== length) return null;
|
|
33
|
+
for (const character of value) {
|
|
34
|
+
if (character < '0' || character > '9') return null;
|
|
25
35
|
}
|
|
36
|
+
return Number(value);
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
function parseDate(value: string): Pick<TimestampParts, 'year' | 'month' | 'day'> | null {
|
|
40
|
+
const fields = value.split('-');
|
|
41
|
+
if (fields.length !== 3) return null;
|
|
42
|
+
const year = decimal(fields[0] ?? '', 4);
|
|
43
|
+
const month = decimal(fields[1] ?? '', 2);
|
|
44
|
+
const day = decimal(fields[2] ?? '', 2);
|
|
45
|
+
if (year === null || month === null || day === null) return null;
|
|
46
|
+
return { year, month, day };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseZone(value: string): {
|
|
50
|
+
readonly clock: string;
|
|
51
|
+
readonly offsetHour: number;
|
|
52
|
+
readonly offsetMinute: number;
|
|
53
|
+
} | null {
|
|
54
|
+
if (value.endsWith('Z')) {
|
|
55
|
+
return { clock: value.slice(0, -1), offsetHour: 0, offsetMinute: 0 };
|
|
33
56
|
}
|
|
34
|
-
|
|
35
|
-
|
|
57
|
+
const marker = value.at(-6);
|
|
58
|
+
if ((marker !== '+' && marker !== '-') || value.at(-3) !== ':') return null;
|
|
59
|
+
const offsetHour = decimal(value.slice(-5, -3), 2);
|
|
60
|
+
const offsetMinute = decimal(value.slice(-2), 2);
|
|
61
|
+
if (offsetHour === null || offsetMinute === null) return null;
|
|
62
|
+
return { clock: value.slice(0, -6), offsetHour, offsetMinute };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseFractionalClock(value: string): string | null {
|
|
66
|
+
const fields = value.split('.');
|
|
67
|
+
if (fields.length > 2) return null;
|
|
68
|
+
const fraction = fields[1];
|
|
69
|
+
if (fraction !== undefined &&
|
|
70
|
+
(fraction.length === 0 || decimal(fraction, fraction.length) === null)) {
|
|
71
|
+
return null;
|
|
36
72
|
}
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
return fields[0] ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseHms(value: string): {
|
|
77
|
+
readonly hour: number;
|
|
78
|
+
readonly minute: number;
|
|
79
|
+
readonly second: number;
|
|
80
|
+
} | null {
|
|
81
|
+
const fields = value.split(':');
|
|
82
|
+
if (fields.length !== 3) return null;
|
|
83
|
+
const hour = decimal(fields[0] ?? '', 2);
|
|
84
|
+
const minute = decimal(fields[1] ?? '', 2);
|
|
85
|
+
const second = decimal(fields[2] ?? '', 2);
|
|
86
|
+
return hour === null || minute === null || second === null ? null : { hour, minute, second };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function parseClock(value: string): Omit<TimestampParts, 'year' | 'month' | 'day'> | null {
|
|
90
|
+
const zone = parseZone(value);
|
|
91
|
+
if (zone === null) return null;
|
|
92
|
+
const clock = parseFractionalClock(zone.clock);
|
|
93
|
+
if (clock === null) return null;
|
|
94
|
+
const parts = parseHms(clock);
|
|
95
|
+
if (parts === null) return null;
|
|
96
|
+
return { ...parts, offsetHour: zone.offsetHour, offsetMinute: zone.offsetMinute };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function parseTimestamp(value: string): TimestampParts | null {
|
|
100
|
+
if (value.at(10) !== 'T') return null;
|
|
101
|
+
const date = parseDate(value.slice(0, 10));
|
|
102
|
+
const clock = parseClock(value.slice(11));
|
|
103
|
+
return date === null || clock === null ? null : { ...date, ...clock };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function validClock(parts: TimestampParts): boolean {
|
|
107
|
+
return parts.hour <= 23 && parts.minute <= 59 && parts.second <= 59 &&
|
|
108
|
+
parts.offsetHour <= 23 && parts.offsetMinute <= 59;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function validCalendar(parts: TimestampParts): boolean {
|
|
112
|
+
if (parts.month < 1 || parts.month > 12) return false;
|
|
113
|
+
const leap = parts.year % 4 === 0 && (parts.year % 100 !== 0 || parts.year % 400 === 0);
|
|
114
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
115
|
+
return parts.day >= 1 && parts.day <= (days[parts.month - 1] ?? 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function timestamp(value: unknown): value is string {
|
|
119
|
+
if (!nonempty(value)) return false;
|
|
120
|
+
const parts = parseTimestamp(value);
|
|
121
|
+
return parts !== null &&
|
|
122
|
+
validClock(parts) &&
|
|
123
|
+
validCalendar(parts) &&
|
|
124
|
+
Number.isFinite(Date.parse(value));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function uint(value: unknown): value is number {
|
|
128
|
+
return Number.isSafeInteger(value) && (value as number) >= 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function assertCapsule(body: unknown): Capsule {
|
|
132
|
+
const value = closedObject(body, ['capsule_id', 'state', 'label', 'created_at'], 'Capsule');
|
|
133
|
+
if (
|
|
134
|
+
!nonempty(value.capsule_id) ||
|
|
135
|
+
typeof value.state !== 'string' ||
|
|
136
|
+
!KNOWN_CAPSULE_STATES.includes(value.state as Capsule['state']) ||
|
|
137
|
+
(value.label !== null && typeof value.label !== 'string') ||
|
|
138
|
+
!timestamp(value.created_at)
|
|
139
|
+
) {
|
|
140
|
+
throw new TargetProtocolError('Capsule response is malformed');
|
|
39
141
|
}
|
|
40
|
-
|
|
41
|
-
|
|
142
|
+
return Object.freeze({
|
|
143
|
+
id: value.capsule_id,
|
|
144
|
+
state: value.state as Capsule['state'],
|
|
145
|
+
label: value.label as string | null,
|
|
146
|
+
createdAt: value.created_at,
|
|
147
|
+
});
|
|
42
148
|
}
|
|
43
149
|
|
|
44
150
|
export function assertCapsuleAccess(body: unknown): CapsuleAccess {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
151
|
+
const value = closedObject(
|
|
152
|
+
body,
|
|
153
|
+
['protocol', 'websocket_url', 'access_token', 'token_type', 'expires_at'],
|
|
154
|
+
'CapsuleAccess',
|
|
155
|
+
);
|
|
156
|
+
if (
|
|
157
|
+
value.protocol !== 'openengine.cluster/v1' ||
|
|
158
|
+
!nonempty(value.websocket_url) ||
|
|
159
|
+
!nonempty(value.access_token) ||
|
|
160
|
+
value.token_type !== 'Bearer' ||
|
|
161
|
+
!timestamp(value.expires_at)
|
|
162
|
+
) {
|
|
163
|
+
throw new TargetProtocolError('CapsuleAccess response is malformed');
|
|
52
164
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
165
|
+
return Object.freeze({
|
|
166
|
+
protocol: 'openengine.cluster/v1',
|
|
167
|
+
websocketUrl: value.websocket_url,
|
|
168
|
+
accessToken: value.access_token,
|
|
169
|
+
tokenType: 'Bearer',
|
|
170
|
+
expiresAt: value.expires_at,
|
|
171
|
+
});
|
|
57
172
|
}
|
|
58
173
|
|
|
59
174
|
export function assertCapsuleLimits(body: unknown): CapsuleLimits {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
throw new TargetProtocolError('CapsuleLimits: "maxConcurrent" must be a number');
|
|
175
|
+
const value = closedObject(body, ['active_capsules', 'max_active_capsules'], 'CapsuleLimits');
|
|
176
|
+
if (!uint(value.active_capsules) || (value.max_active_capsules !== null && !uint(value.max_active_capsules))) {
|
|
177
|
+
throw new TargetProtocolError('CapsuleLimits response is malformed');
|
|
64
178
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
179
|
+
return Object.freeze({
|
|
180
|
+
activeCapsules: value.active_capsules,
|
|
181
|
+
maxActiveCapsules: value.max_active_capsules as number | null,
|
|
182
|
+
});
|
|
69
183
|
}
|
|
70
184
|
|
|
71
185
|
export function assertCapsuleListPage(body: unknown): CapsuleListPage {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
throw new TargetProtocolError('CapsuleListPage: "items" must be an array');
|
|
76
|
-
}
|
|
77
|
-
const items = (record['items'] as unknown[]).map((item) => assertCapsule(item));
|
|
78
|
-
const cursor = record['cursor'];
|
|
79
|
-
if (cursor !== undefined && cursor !== null && typeof cursor !== 'string') {
|
|
80
|
-
throw new TargetProtocolError('CapsuleListPage: "cursor" must be a string if present');
|
|
81
|
-
}
|
|
82
|
-
if (typeof cursor === 'string') {
|
|
83
|
-
return { items, cursor };
|
|
186
|
+
const value = closedObject(body, ['capsules', 'next_cursor'], 'CapsulePage');
|
|
187
|
+
if (!Array.isArray(value.capsules) || (value.next_cursor !== null && typeof value.next_cursor !== 'string')) {
|
|
188
|
+
throw new TargetProtocolError('CapsulePage response is malformed');
|
|
84
189
|
}
|
|
85
|
-
return {
|
|
190
|
+
return Object.freeze({
|
|
191
|
+
capsules: Object.freeze(value.capsules.map((item) => assertCapsule(item))),
|
|
192
|
+
nextCursor: value.next_cursor as string | null,
|
|
193
|
+
});
|
|
86
194
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MAX_RETRY_ELAPSED_MS } from './bounds.js';
|
|
2
|
+
import { TargetAdapterError } from './errors.js';
|
|
3
|
+
import type { Clock, RetryPolicy } from './types.js';
|
|
4
|
+
|
|
5
|
+
export type TargetOperation = 'allocate' | 'list' | 'inspect' | 'terminate' | 'limits' | 'access';
|
|
6
|
+
|
|
7
|
+
function throwIfAborted(signal?: AbortSignal): void {
|
|
8
|
+
if (signal?.aborted) {
|
|
9
|
+
throw signal.reason ?? new globalThis.DOMException('The operation was aborted', 'AbortError');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function wait(delayMs: number, signal?: AbortSignal): Promise<void> {
|
|
14
|
+
throwIfAborted(signal);
|
|
15
|
+
if (delayMs <= 0) return;
|
|
16
|
+
await new Promise<void>((resolve, reject) => {
|
|
17
|
+
const timer = setTimeout(resolve, delayMs);
|
|
18
|
+
signal?.addEventListener('abort', () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(signal.reason ?? new globalThis.DOMException('The operation was aborted', 'AbortError'));
|
|
21
|
+
}, { once: true });
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type RetryContext = {
|
|
26
|
+
readonly clock: Clock;
|
|
27
|
+
readonly policy: RetryPolicy;
|
|
28
|
+
};
|
|
29
|
+
function retryableError(value: unknown): value is TargetAdapterError {
|
|
30
|
+
return value instanceof TargetAdapterError && value.retryable;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validRetryDelay(retry: boolean, delayMs: number, remaining: number): boolean {
|
|
34
|
+
return retry && Number.isFinite(delayMs) && delayMs >= 0 && delayMs < remaining;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
export async function withTargetRetry<T>(
|
|
39
|
+
operation: TargetOperation,
|
|
40
|
+
effect: () => Promise<T>,
|
|
41
|
+
signal: AbortSignal | undefined,
|
|
42
|
+
context: RetryContext,
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
const retrySafe = operation !== 'access';
|
|
45
|
+
const started = context.clock.now();
|
|
46
|
+
let attempt = 0;
|
|
47
|
+
while (true) {
|
|
48
|
+
throwIfAborted(signal);
|
|
49
|
+
try {
|
|
50
|
+
return await effect();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throwIfAborted(signal);
|
|
53
|
+
if (!retrySafe || !retryableError(error)) throw error;
|
|
54
|
+
attempt += 1;
|
|
55
|
+
const elapsed = context.clock.now() - started;
|
|
56
|
+
const decision = context.policy.shouldRetry(attempt, elapsed, error);
|
|
57
|
+
const remaining = MAX_RETRY_ELAPSED_MS - elapsed;
|
|
58
|
+
if (!validRetryDelay(decision.retry, decision.delayMs, remaining)) throw error;
|
|
59
|
+
await wait(decision.delayMs, signal);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,13 +1,45 @@
|
|
|
1
|
-
import { MAX_RETRY_ATTEMPTS, MAX_RETRY_ELAPSED_MS } from './bounds.
|
|
2
|
-
import type { TargetAdapterError } from './errors.
|
|
3
|
-
import { TargetAuthError, TargetRateLimitError } from './errors.
|
|
4
|
-
import type { Clock, RetryPolicy } from './types.
|
|
1
|
+
import { MAX_RETRY_ATTEMPTS, MAX_RETRY_ELAPSED_MS } from './bounds.js';
|
|
2
|
+
import type { TargetAdapterError } from './errors.js';
|
|
3
|
+
import { TargetAuthError, TargetRateLimitError, TargetServerError } from './errors.js';
|
|
4
|
+
import type { Clock, RetryPolicy } from './types.js';
|
|
5
|
+
const IMF_WEEKDAYS = new Set(['Mon,', 'Tue,', 'Wed,', 'Thu,', 'Fri,', 'Sat,', 'Sun,']);
|
|
6
|
+
const IMF_MONTHS = new Set([
|
|
7
|
+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
8
|
+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function fixedDigits(value: string, length: number): boolean {
|
|
12
|
+
if (value.length !== length) return false;
|
|
13
|
+
for (const character of value) {
|
|
14
|
+
if (character < '0' || character > '9') return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function validImfDate(weekday: string, day: string, month: string, year: string): boolean {
|
|
20
|
+
return IMF_WEEKDAYS.has(weekday) &&
|
|
21
|
+
fixedDigits(day, 2) &&
|
|
22
|
+
IMF_MONTHS.has(month) &&
|
|
23
|
+
fixedDigits(year, 4);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function validImfTime(value: string): boolean {
|
|
27
|
+
const parts = value.split(':');
|
|
28
|
+
return parts.length === 3 && parts.every((part) => fixedDigits(part, 2));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isImfFixdate(value: string): boolean {
|
|
32
|
+
const parts = value.split(' ');
|
|
33
|
+
if (parts.length !== 6) return false;
|
|
34
|
+
const [weekday = '', day = '', month = '', year = '', time = '', zone = ''] = parts;
|
|
35
|
+
return validImfDate(weekday, day, month, year) && validImfTime(time) && zone === 'GMT';
|
|
36
|
+
}
|
|
5
37
|
|
|
6
38
|
export class DefaultRetryPolicy implements RetryPolicy {
|
|
7
39
|
shouldRetry(
|
|
8
40
|
attempt: number,
|
|
9
41
|
elapsed: number,
|
|
10
|
-
error: TargetAdapterError
|
|
42
|
+
error: TargetAdapterError
|
|
11
43
|
): { retry: boolean; delayMs: number } {
|
|
12
44
|
if (error instanceof TargetAuthError) return { retry: false, delayMs: 0 };
|
|
13
45
|
if (!error.retryable) return { retry: false, delayMs: 0 };
|
|
@@ -15,7 +47,8 @@ export class DefaultRetryPolicy implements RetryPolicy {
|
|
|
15
47
|
if (elapsed >= MAX_RETRY_ELAPSED_MS) return { retry: false, delayMs: 0 };
|
|
16
48
|
|
|
17
49
|
const requestedDelay =
|
|
18
|
-
error instanceof TargetRateLimitError
|
|
50
|
+
(error instanceof TargetRateLimitError || error instanceof TargetServerError) &&
|
|
51
|
+
error.retryAfterMs !== undefined
|
|
19
52
|
? Math.max(0, error.retryAfterMs)
|
|
20
53
|
: Math.min(1000 * Math.pow(2, attempt), 10_000);
|
|
21
54
|
const remaining = MAX_RETRY_ELAPSED_MS - elapsed;
|
|
@@ -33,14 +66,17 @@ export function parseRetryAfter(header: string | null, clock: Clock): number | n
|
|
|
33
66
|
const trimmed = header.trim();
|
|
34
67
|
if (trimmed.length === 0) return null;
|
|
35
68
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
69
|
+
if (/^\d+$/.test(trimmed)) {
|
|
70
|
+
const delayMs = Number(trimmed) * 1000;
|
|
71
|
+
return Number.isSafeInteger(delayMs) ? delayMs : null;
|
|
39
72
|
}
|
|
40
73
|
|
|
74
|
+
if (!isImfFixdate(trimmed)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
41
77
|
const date = Date.parse(trimmed);
|
|
42
|
-
if (Number.
|
|
78
|
+
if (!Number.isFinite(date) || new Date(date).toUTCString() !== trimmed) return null;
|
|
43
79
|
|
|
44
80
|
const delayMs = date - clock.now();
|
|
45
|
-
return delayMs > 0 ?
|
|
81
|
+
return delayMs > 0 ? delayMs : 0;
|
|
46
82
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ZeroCloudV1TargetAdapter } from './zero-cloud-v1-adapter.js';
|
|
2
|
+
import type { CreateTargetAdapterOptions, TargetAdapter } from './adapter-types.js';
|
|
3
|
+
export type {
|
|
4
|
+
CreateTargetAdapterOptions,
|
|
5
|
+
CredentialInstallCapability,
|
|
6
|
+
TargetAdapter,
|
|
7
|
+
} from './adapter-types.js';
|
|
2
8
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
limits(signal?: AbortSignal): Promise<CapsuleLimits>;
|
|
9
|
-
access(capsuleId: string, signal?: AbortSignal): Promise<CapsuleAccess>;
|
|
9
|
+
export function createTargetAdapter(options: CreateTargetAdapterOptions): TargetAdapter {
|
|
10
|
+
if (options.descriptor.adapter.majorVersion !== 1) {
|
|
11
|
+
throw new Error('Unsupported hosted target adapter version');
|
|
12
|
+
}
|
|
13
|
+
return new ZeroCloudV1TargetAdapter(options);
|
|
10
14
|
}
|
|
@@ -1,44 +1,49 @@
|
|
|
1
|
-
import type { TargetAdapterError } from './errors.
|
|
1
|
+
import type { TargetAdapterError } from './errors.js';
|
|
2
2
|
|
|
3
3
|
export interface TargetAccessTokenProvider {
|
|
4
4
|
getAccessToken(signal?: AbortSignal): Promise<string>;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export type CapsuleState = 'provisioning' | '
|
|
8
|
-
|
|
9
|
-
export const KNOWN_CAPSULE_STATES = ['provisioning', 'running', 'stopping', 'terminated', 'failed'] as const;
|
|
7
|
+
export type CapsuleState = 'provisioning' | 'ready' | 'terminating' | 'terminated' | 'failed';
|
|
8
|
+
export const KNOWN_CAPSULE_STATES = ['provisioning', 'ready', 'terminating', 'terminated', 'failed'] as const;
|
|
10
9
|
|
|
11
10
|
export interface Capsule {
|
|
12
11
|
readonly id: string;
|
|
13
12
|
readonly state: CapsuleState;
|
|
13
|
+
readonly label: string | null;
|
|
14
14
|
readonly createdAt: string;
|
|
15
|
-
readonly [key: string]: unknown;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
export interface CapsuleAccess {
|
|
19
|
-
readonly
|
|
20
|
-
readonly
|
|
18
|
+
readonly protocol: 'openengine.cluster/v1';
|
|
19
|
+
readonly websocketUrl: string;
|
|
20
|
+
readonly accessToken: string;
|
|
21
|
+
readonly tokenType: 'Bearer';
|
|
21
22
|
readonly expiresAt: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export interface CapsuleListPage {
|
|
25
|
-
readonly
|
|
26
|
-
readonly
|
|
26
|
+
readonly capsules: readonly Capsule[];
|
|
27
|
+
readonly nextCursor: string | null;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface CapsuleLimits {
|
|
30
|
-
readonly
|
|
31
|
-
readonly
|
|
32
|
-
readonly [key: string]: unknown;
|
|
31
|
+
readonly activeCapsules: number;
|
|
32
|
+
readonly maxActiveCapsules: number | null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export interface AllocateRequest {
|
|
36
36
|
readonly idempotencyKey: string;
|
|
37
|
-
readonly
|
|
37
|
+
readonly label?: string;
|
|
38
|
+
readonly size?: 'tiny' | 'small' | 'standard' | 'large';
|
|
39
|
+
}
|
|
40
|
+
export interface ListRequest {
|
|
41
|
+
readonly cursor?: string;
|
|
42
|
+
readonly limit?: number;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
export interface HttpTransport {
|
|
41
|
-
fetch(url: string, init: RequestInit & { redirect: '
|
|
46
|
+
fetch(url: string, init: RequestInit & { redirect: 'manual' }): Promise<Response>;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export interface Clock {
|
|
@@ -52,7 +57,3 @@ export interface RetryPolicy {
|
|
|
52
57
|
error: TargetAdapterError,
|
|
53
58
|
): { retry: boolean; delayMs: number };
|
|
54
59
|
}
|
|
55
|
-
|
|
56
|
-
export interface TargetDiscovery {
|
|
57
|
-
readonly capsuleV1: string;
|
|
58
|
-
}
|