@aexhq/sdk 0.13.7 → 0.13.9
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/README.md +14 -14
- package/dist/_contracts/connection-ticket.d.ts +8 -7
- package/dist/_contracts/connection-ticket.js +20 -14
- package/dist/_contracts/event-envelope.d.ts +17 -18
- package/dist/_contracts/event-envelope.js +10 -11
- package/dist/_contracts/managed-key.d.ts +27 -1
- package/dist/_contracts/managed-key.js +75 -4
- package/dist/_contracts/operations.d.ts +9 -20
- package/dist/_contracts/operations.js +33 -82
- package/dist/_contracts/proxy-protocol.d.ts +35 -2
- package/dist/_contracts/proxy-protocol.js +34 -1
- package/dist/_contracts/run-artifacts.d.ts +12 -10
- package/dist/_contracts/run-artifacts.js +13 -11
- package/dist/_contracts/run-config.d.ts +7 -0
- package/dist/_contracts/run-config.js +93 -24
- package/dist/_contracts/run-custody.d.ts +3 -3
- package/dist/_contracts/run-custody.js +5 -5
- package/dist/_contracts/run-record.d.ts +5 -17
- package/dist/_contracts/run-record.js +4 -15
- package/dist/_contracts/run-retention.d.ts +2 -2
- package/dist/_contracts/run-retention.js +3 -3
- package/dist/_contracts/run-unit.d.ts +4 -5
- package/dist/_contracts/runner-event.d.ts +7 -8
- package/dist/_contracts/runner-event.js +7 -8
- package/dist/_contracts/side-effect-audit.d.ts +2 -2
- package/dist/_contracts/side-effect-audit.js +3 -3
- package/dist/_contracts/stable.d.ts +1 -1
- package/dist/_contracts/stable.js +1 -1
- package/dist/_contracts/submission.d.ts +5 -6
- package/dist/_contracts/submission.js +1 -1
- package/dist/cli.mjs +127 -127
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +7 -57
- package/dist/client.js +624 -167
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/cleanup.md +4 -4
- package/docs/credentials.md +5 -5
- package/docs/events.md +5 -5
- package/docs/outputs.md +23 -25
- package/docs/product-boundaries.md +5 -5
- package/docs/provider-runtime-capabilities.md +1 -1
- package/docs/quickstart.md +12 -12
- package/docs/run-config.md +1 -1
- package/docs/run-record.md +6 -9
- package/docs/skills.md +23 -25
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ aex is a TypeScript-first SDK + CLI for running autonomous agent sessions across
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
AgentExecutor, // the only client class — submits durable runs to aex
|
|
12
12
|
Skill, // workspace / provider / inline skill bundles
|
|
13
13
|
McpServer, // MCP server declarations (headers split into secrets server-side)
|
|
14
14
|
ProxyEndpoint, // per-run managed HTTP proxy endpoint
|
|
@@ -25,7 +25,7 @@ aex status <run-id> --api-token …
|
|
|
25
25
|
aex wait <run-id> [--timeout 8m] [--interval 2s] --api-token …
|
|
26
26
|
aex events <run-id> [--follow] [--timeout 8m] --api-token …
|
|
27
27
|
aex outputs <run-id> --api-token …
|
|
28
|
-
aex download <run-id> [--only outputs|
|
|
28
|
+
aex download <run-id> [--only outputs|events|metadata] [--out path] --api-token …
|
|
29
29
|
aex cancel <run-id> --api-token …
|
|
30
30
|
aex delete <run-id> --api-token …
|
|
31
31
|
aex whoami --api-token …
|
|
@@ -34,11 +34,11 @@ aex skills <upload|list|get|delete> [flags] --api-token …
|
|
|
34
34
|
|
|
35
35
|
The SDK class and the CLI are backed by the same public `@aexhq/contracts` operations module — any read or write you can do through one, you can do through the other, against the same durable run records. The same npm package also ships the in-container `aex` CLI as its `bin` entry; managed runs mount that CLI inside the runner so skills can call `aex proxy …` against the per-run manifest. See [product capabilities and boundaries](docs/product-boundaries.md).
|
|
36
36
|
|
|
37
|
-
The aex URL defaults to `https://api.aex.dev`. Set `--aex-url` on the CLI or `baseUrl` on `
|
|
37
|
+
The aex URL defaults to `https://api.aex.dev`. Set `--aex-url` on the CLI or `baseUrl` on `AgentExecutor` for local, staging, or hosted aex API planes. This is not a supported self-host deployment claim. The workspace is derived server-side from your API token (1:1 binding), so there is no `--workspace` flag and no `workspaceId` option.
|
|
38
38
|
|
|
39
39
|
## Product boundaries
|
|
40
40
|
|
|
41
|
-
- Multi-provider via
|
|
41
|
+
- Multi-provider via the managed runtime. The published surface is the same
|
|
42
42
|
regardless of provider:
|
|
43
43
|
- omit `runtime` or pass `runtime: "managed"`; every provider uses the
|
|
44
44
|
managed runtime and BYOK provider-proxy.
|
|
@@ -54,31 +54,31 @@ The aex URL defaults to `https://api.aex.dev`. Set `--aex-url` on the CLI or `ba
|
|
|
54
54
|
## Quickstart (SDK)
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import {
|
|
57
|
+
import { AgentExecutor } from "@aexhq/sdk";
|
|
58
58
|
|
|
59
|
-
const
|
|
59
|
+
const aex = new AgentExecutor({
|
|
60
60
|
apiToken: process.env.AEX_API_TOKEN!
|
|
61
61
|
// baseUrl defaults to https://api.aex.dev - set it for local or staging planes.
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
const runId = await
|
|
64
|
+
const runId = await aex.submitRun({
|
|
65
65
|
model: "claude-haiku-4-5",
|
|
66
66
|
system: "You are a concise automation agent.",
|
|
67
67
|
prompt: "Write a short answer about agent-first SDK design.",
|
|
68
68
|
secrets: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! } }
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
const run = await
|
|
71
|
+
const run = await aex.wait(runId);
|
|
72
72
|
console.log(run.status);
|
|
73
73
|
|
|
74
|
-
for (const output of await
|
|
74
|
+
for (const output of await aex.outputs(runId)) {
|
|
75
75
|
console.log(output.id, output.filename);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
const report = await
|
|
78
|
+
const report = await aex.downloadOutput(runId, { path: "report.txt", match: "suffix" });
|
|
79
79
|
console.log(new TextDecoder().decode(report));
|
|
80
80
|
|
|
81
|
-
await
|
|
81
|
+
await aex.downloadOutputs(runId, { to: "./outputs.zip" });
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Reusable, credential-free configs can be ordinary functions:
|
|
@@ -92,16 +92,16 @@ function summarise(topic: string) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
const runId = await
|
|
95
|
+
const runId = await aex.submitRun({
|
|
96
96
|
...summarise("agent-first SDK design"),
|
|
97
97
|
secrets: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! } }
|
|
98
98
|
});
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
Stream events live with `
|
|
101
|
+
Stream events live with `aex.stream(runId)`:
|
|
102
102
|
|
|
103
103
|
```ts
|
|
104
|
-
for await (const event of
|
|
104
|
+
for await (const event of aex.stream(runId)) {
|
|
105
105
|
if (event.type === "agent.message") {
|
|
106
106
|
// typed event helpers live under `aex`'s event guard exports.
|
|
107
107
|
}
|
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
* A browser/Node WebSocket handshake cannot carry an Authorization header,
|
|
5
5
|
* so a subscriber first obtains a short-lived ticket from an authenticated
|
|
6
6
|
* HTTP endpoint, then presents it as a `?ticket=` query parameter on the WS
|
|
7
|
-
* upgrade. The ticket is an HMAC over `${runId}.${exp}` keyed by
|
|
8
|
-
* coordinator secret — it binds the grant to one run
|
|
9
|
-
* verified without any per-ticket storage.
|
|
7
|
+
* upgrade. The ticket is an HMAC over `${runId}.${channel}.${exp}` keyed by
|
|
8
|
+
* the coordinator secret — it binds the grant to one run, one stream channel,
|
|
9
|
+
* and one expiry and is verified without any per-ticket storage.
|
|
10
10
|
*
|
|
11
11
|
* This lives in shared so the coordinator (which verifies) and the API
|
|
12
12
|
* hosted API's ticket broker (which mints, on behalf of a workspace token) use
|
|
13
13
|
* ONE implementation. Pure Web Crypto — identical under Node and workerd.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
export type ConnectionTicketChannel = "event" | "log" | "all";
|
|
16
|
+
/** Mint a `${channel}.${exp}.${mac}` ticket valid for `ttlMs` from `nowMs`. */
|
|
17
|
+
export declare function mintConnectionTicket(runId: string, secret: string, nowMs: number, ttlMs?: number, channel?: ConnectionTicketChannel): Promise<{
|
|
17
18
|
ticket: string;
|
|
18
19
|
expiresAtMs: number;
|
|
19
20
|
}>;
|
|
20
|
-
/** Verify a ticket against `runId
|
|
21
|
-
export declare function verifyConnectionTicket(ticket: string, runId: string, secret: string, nowMs: number): Promise<boolean>;
|
|
21
|
+
/** Verify a ticket against `runId`, channel, and current time. Constant-time MAC compare. */
|
|
22
|
+
export declare function verifyConnectionTicket(ticket: string, runId: string, secret: string, nowMs: number, channel?: ConnectionTicketChannel): Promise<boolean>;
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* A browser/Node WebSocket handshake cannot carry an Authorization header,
|
|
5
5
|
* so a subscriber first obtains a short-lived ticket from an authenticated
|
|
6
6
|
* HTTP endpoint, then presents it as a `?ticket=` query parameter on the WS
|
|
7
|
-
* upgrade. The ticket is an HMAC over `${runId}.${exp}` keyed by
|
|
8
|
-
* coordinator secret — it binds the grant to one run
|
|
9
|
-
* verified without any per-ticket storage.
|
|
7
|
+
* upgrade. The ticket is an HMAC over `${runId}.${channel}.${exp}` keyed by
|
|
8
|
+
* the coordinator secret — it binds the grant to one run, one stream channel,
|
|
9
|
+
* and one expiry and is verified without any per-ticket storage.
|
|
10
10
|
*
|
|
11
11
|
* This lives in shared so the coordinator (which verifies) and the API
|
|
12
12
|
* hosted API's ticket broker (which mints, on behalf of a workspace token) use
|
|
@@ -14,27 +14,33 @@
|
|
|
14
14
|
*/
|
|
15
15
|
const DEFAULT_TICKET_TTL_MS = 60_000;
|
|
16
16
|
const encoder = new TextEncoder();
|
|
17
|
+
function normalizeTicketChannel(channel) {
|
|
18
|
+
return channel === "log" || channel === "all" ? channel : "event";
|
|
19
|
+
}
|
|
17
20
|
async function hmacHex(secret, message) {
|
|
18
21
|
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
19
22
|
const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
20
23
|
return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
21
24
|
}
|
|
22
|
-
/** Mint a `${exp}.${mac}` ticket valid for `ttlMs` from `nowMs`. */
|
|
23
|
-
export async function mintConnectionTicket(runId, secret, nowMs, ttlMs = DEFAULT_TICKET_TTL_MS) {
|
|
25
|
+
/** Mint a `${channel}.${exp}.${mac}` ticket valid for `ttlMs` from `nowMs`. */
|
|
26
|
+
export async function mintConnectionTicket(runId, secret, nowMs, ttlMs = DEFAULT_TICKET_TTL_MS, channel = "event") {
|
|
27
|
+
const boundChannel = normalizeTicketChannel(channel);
|
|
24
28
|
const exp = nowMs + ttlMs;
|
|
25
|
-
const mac = await hmacHex(secret, `${runId}.${exp}`);
|
|
26
|
-
return { ticket: `${exp}.${mac}`, expiresAtMs: exp };
|
|
29
|
+
const mac = await hmacHex(secret, `${runId}.${boundChannel}.${exp}`);
|
|
30
|
+
return { ticket: `${boundChannel}.${exp}.${mac}`, expiresAtMs: exp };
|
|
27
31
|
}
|
|
28
|
-
/** Verify a ticket against `runId
|
|
29
|
-
export async function verifyConnectionTicket(ticket, runId, secret, nowMs) {
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
+
/** Verify a ticket against `runId`, channel, and current time. Constant-time MAC compare. */
|
|
33
|
+
export async function verifyConnectionTicket(ticket, runId, secret, nowMs, channel = "event") {
|
|
34
|
+
const [ticketChannel, expRaw, mac, ...rest] = ticket.split(".");
|
|
35
|
+
if (rest.length > 0 || !ticketChannel || !expRaw || !mac)
|
|
36
|
+
return false;
|
|
37
|
+
const boundChannel = normalizeTicketChannel(channel);
|
|
38
|
+
if (ticketChannel !== boundChannel)
|
|
32
39
|
return false;
|
|
33
|
-
const exp = Number(
|
|
34
|
-
const mac = ticket.slice(dot + 1);
|
|
40
|
+
const exp = Number(expRaw);
|
|
35
41
|
if (!Number.isFinite(exp) || exp < nowMs)
|
|
36
42
|
return false;
|
|
37
|
-
const expected = await hmacHex(secret, `${runId}.${exp}`);
|
|
43
|
+
const expected = await hmacHex(secret, `${runId}.${boundChannel}.${exp}`);
|
|
38
44
|
return timingSafeEqual(mac, expected);
|
|
39
45
|
}
|
|
40
46
|
/** Length-then-constant-time comparison of two hex strings. */
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Unified observability spine:
|
|
19
19
|
* the envelope additionally carries four ordering attributes so BOTH the typed
|
|
20
|
-
* event stream and the high-volume hosted
|
|
20
|
+
* event stream and the high-volume hosted-platform log stream ride one per-run
|
|
21
21
|
* coordinator:
|
|
22
22
|
* - `channel` — "event" (the typed AG-UI stream) or "log" (a verbose log
|
|
23
23
|
* line). The single axis a consumer splits the unified stream
|
|
@@ -52,24 +52,23 @@ export declare const AEX_EVENT_MAP_VERSION: 1;
|
|
|
52
52
|
* Coarse origin classifier — the first axis a consumer filters on.
|
|
53
53
|
* - `agent` — the model: text, reasoning, builtin tool calls/results.
|
|
54
54
|
* - `worker` — the hosted aex edge itself.
|
|
55
|
-
* - `runtime` — the execution runtime
|
|
56
|
-
*
|
|
55
|
+
* - `runtime` — the execution runtime: lifecycle, diagnostics, non-fatal
|
|
56
|
+
* stream errors.
|
|
57
57
|
* - `mcp` — an MCP server (a tool call/result routed through MCP).
|
|
58
58
|
* - `aex` — the platform: skills, files, and other aex-native events.
|
|
59
|
-
* - `workflow`— the orchestration layer
|
|
60
|
-
* - `
|
|
61
|
-
*
|
|
62
|
-
* Goose-container / Anthropic-session source, distinct from the
|
|
63
|
-
* host machine that carries it.
|
|
59
|
+
* - `workflow`— the orchestration layer.
|
|
60
|
+
* - `host` — the managed host the runtime executes on; distinct from the
|
|
61
|
+
* runtime process itself.
|
|
64
62
|
*/
|
|
65
|
-
export declare const AEX_EVENT_SOURCES: readonly ["agent", "worker", "runtime", "mcp", "aex", "workflow", "
|
|
63
|
+
export declare const AEX_EVENT_SOURCES: readonly ["agent", "worker", "runtime", "mcp", "aex", "workflow", "host"];
|
|
66
64
|
export type AexEventSource = (typeof AEX_EVENT_SOURCES)[number];
|
|
67
65
|
/**
|
|
68
66
|
* The channel a record rides on the unified per-run stream:
|
|
69
67
|
* - `event` — the typed, low-volume, fully-replayed AG-UI event stream.
|
|
70
68
|
* - `log` — a high-volume verbose log line (level + message + fields). The
|
|
71
|
-
* coordinator prunes flushed log rows after
|
|
72
|
-
* append-only, events are kept). Absent on the wire ⇒
|
|
69
|
+
* coordinator prunes flushed log rows after evidence archival
|
|
70
|
+
* (logs are append-only, events are kept). Absent on the wire ⇒
|
|
71
|
+
* `event`.
|
|
73
72
|
*/
|
|
74
73
|
export declare const AEX_EVENT_CHANNELS: readonly ["event", "log"];
|
|
75
74
|
export type AexEventChannel = (typeof AEX_EVENT_CHANNELS)[number];
|
|
@@ -130,13 +129,13 @@ export interface AexEvent {
|
|
|
130
129
|
* companion to `sequence` — distinct from `emittedAt` (the SOURCE's clock) and
|
|
131
130
|
* `time` (the LOGICAL time = run base + relative tMs).
|
|
132
131
|
*
|
|
133
|
-
* NOTE:
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
132
|
+
* NOTE: some edge clocks are coarsened/frozen-at-I/O, so `receivedAt` is
|
|
133
|
+
* precisely "the coordinator's last-I/O wall-clock at ingest" — that is fine
|
|
134
|
+
* as the authoritative receive marker. It does NOT need to exceed `emittedAt`:
|
|
135
|
+
* the source runs on a different host with an independent clock, so cross-host
|
|
136
|
+
* drift can leave `receivedAt < emittedAt` and that is expected, not an error.
|
|
137
|
+
* Absent on records produced before this field existed (back-compat with
|
|
138
|
+
* archived records).
|
|
140
139
|
*/
|
|
141
140
|
readonly receivedAt?: number;
|
|
142
141
|
/**
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Unified observability spine:
|
|
19
19
|
* the envelope additionally carries four ordering attributes so BOTH the typed
|
|
20
|
-
* event stream and the high-volume hosted
|
|
20
|
+
* event stream and the high-volume hosted-platform log stream ride one per-run
|
|
21
21
|
* coordinator:
|
|
22
22
|
* - `channel` — "event" (the typed AG-UI stream) or "log" (a verbose log
|
|
23
23
|
* line). The single axis a consumer splits the unified stream
|
|
@@ -50,23 +50,22 @@ export const AEX_EVENT_MAP_VERSION = 1;
|
|
|
50
50
|
* Coarse origin classifier — the first axis a consumer filters on.
|
|
51
51
|
* - `agent` — the model: text, reasoning, builtin tool calls/results.
|
|
52
52
|
* - `worker` — the hosted aex edge itself.
|
|
53
|
-
* - `runtime` — the execution runtime
|
|
54
|
-
*
|
|
53
|
+
* - `runtime` — the execution runtime: lifecycle, diagnostics, non-fatal
|
|
54
|
+
* stream errors.
|
|
55
55
|
* - `mcp` — an MCP server (a tool call/result routed through MCP).
|
|
56
56
|
* - `aex` — the platform: skills, files, and other aex-native events.
|
|
57
|
-
* - `workflow`— the orchestration layer
|
|
58
|
-
* - `
|
|
59
|
-
*
|
|
60
|
-
* Goose-container / Anthropic-session source, distinct from the
|
|
61
|
-
* host machine that carries it.
|
|
57
|
+
* - `workflow`— the orchestration layer.
|
|
58
|
+
* - `host` — the managed host the runtime executes on; distinct from the
|
|
59
|
+
* runtime process itself.
|
|
62
60
|
*/
|
|
63
|
-
export const AEX_EVENT_SOURCES = ["agent", "worker", "runtime", "mcp", "aex", "workflow", "
|
|
61
|
+
export const AEX_EVENT_SOURCES = ["agent", "worker", "runtime", "mcp", "aex", "workflow", "host"];
|
|
64
62
|
/**
|
|
65
63
|
* The channel a record rides on the unified per-run stream:
|
|
66
64
|
* - `event` — the typed, low-volume, fully-replayed AG-UI event stream.
|
|
67
65
|
* - `log` — a high-volume verbose log line (level + message + fields). The
|
|
68
|
-
* coordinator prunes flushed log rows after
|
|
69
|
-
* append-only, events are kept). Absent on the wire ⇒
|
|
66
|
+
* coordinator prunes flushed log rows after evidence archival
|
|
67
|
+
* (logs are append-only, events are kept). Absent on the wire ⇒
|
|
68
|
+
* `event`.
|
|
70
69
|
*/
|
|
71
70
|
export const AEX_EVENT_CHANNELS = ["event", "log"];
|
|
72
71
|
/**
|
|
@@ -3,10 +3,13 @@ export declare const CREDENTIAL_MODES: readonly ["byok", "managed"];
|
|
|
3
3
|
export type CredentialMode = (typeof CREDENTIAL_MODES)[number];
|
|
4
4
|
export declare const DEFAULT_CREDENTIAL_MODE: CredentialMode;
|
|
5
5
|
export declare const MANAGED_KEY_POLICY_SCHEMA_VERSION = 1;
|
|
6
|
+
export declare const MANAGED_KEY_RESERVATION_SCHEMA_VERSION = 1;
|
|
6
7
|
export declare const MANAGED_KEY_LAUNCH_STAGES: readonly ["blocked", "pilot", "ga"];
|
|
7
8
|
export type ManagedKeyLaunchStage = (typeof MANAGED_KEY_LAUNCH_STAGES)[number];
|
|
8
9
|
export declare const MANAGED_KEY_FEATURE_DECISIONS: readonly ["disabled", "allowed"];
|
|
9
10
|
export type ManagedKeyFeatureDecision = (typeof MANAGED_KEY_FEATURE_DECISIONS)[number];
|
|
11
|
+
export declare const MANAGED_KEY_RESERVATION_STATUSES: readonly ["open", "settled", "released"];
|
|
12
|
+
export type ManagedKeyReservationStatus = (typeof MANAGED_KEY_RESERVATION_STATUSES)[number];
|
|
10
13
|
export interface ManagedKeyFeaturePolicyV1 {
|
|
11
14
|
readonly files: ManagedKeyFeatureDecision;
|
|
12
15
|
readonly packages: ManagedKeyFeatureDecision;
|
|
@@ -24,13 +27,35 @@ export interface ManagedKeyPolicyV1 {
|
|
|
24
27
|
readonly schemaVersion: typeof MANAGED_KEY_POLICY_SCHEMA_VERSION;
|
|
25
28
|
readonly credentialMode: "managed";
|
|
26
29
|
readonly launchStage: ManagedKeyLaunchStage;
|
|
27
|
-
readonly
|
|
30
|
+
readonly privateImplementationAvailable: boolean;
|
|
28
31
|
readonly billingRequired: true;
|
|
29
32
|
readonly providers: readonly RunProvider[];
|
|
30
33
|
readonly runtimes: readonly RuntimeKind[];
|
|
31
34
|
readonly models?: readonly string[];
|
|
32
35
|
readonly features: ManagedKeyFeaturePolicyV1;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Public-safe reservation lifecycle summary. It intentionally carries only
|
|
39
|
+
* credit-unit invariants and public row identifiers; private key handles,
|
|
40
|
+
* account selection, rate cards, margins, and payment-provider references
|
|
41
|
+
* remain outside this contract.
|
|
42
|
+
*/
|
|
43
|
+
export interface ManagedKeyReservationLifecycleV1 {
|
|
44
|
+
readonly schemaVersion: typeof MANAGED_KEY_RESERVATION_SCHEMA_VERSION;
|
|
45
|
+
readonly reservationId: string;
|
|
46
|
+
readonly workspaceId: string;
|
|
47
|
+
readonly runId: string;
|
|
48
|
+
readonly credentialMode: "managed";
|
|
49
|
+
readonly status: ManagedKeyReservationStatus;
|
|
50
|
+
readonly reservedCreditUnits: number;
|
|
51
|
+
readonly chargedCreditUnits: number;
|
|
52
|
+
readonly releasedCreditUnits: number;
|
|
53
|
+
readonly createdAt?: string;
|
|
54
|
+
readonly closedAt?: string;
|
|
55
|
+
}
|
|
56
|
+
export type ManagedKeyReservationLifecycleInput = Omit<ManagedKeyReservationLifecycleV1, "schemaVersion" | "credentialMode"> & {
|
|
57
|
+
readonly credentialMode?: "managed";
|
|
58
|
+
};
|
|
34
59
|
export declare const BLOCKED_MANAGED_KEY_FEATURE_POLICY_V1: ManagedKeyFeaturePolicyV1;
|
|
35
60
|
export declare const BLOCKED_MANAGED_KEY_POLICY_V1: ManagedKeyPolicyV1;
|
|
36
61
|
export declare class ManagedKeyUnavailableError extends Error {
|
|
@@ -40,6 +65,7 @@ export declare class ManagedKeyUnavailableError extends Error {
|
|
|
40
65
|
export declare function parseCredentialMode(input: unknown): CredentialMode;
|
|
41
66
|
export declare function credentialModeOrDefault(input: CredentialMode | undefined): CredentialMode;
|
|
42
67
|
export declare function isCredentialMode(input: unknown): input is CredentialMode;
|
|
68
|
+
export declare function buildManagedKeyReservationLifecycle(input: ManagedKeyReservationLifecycleInput): ManagedKeyReservationLifecycleV1;
|
|
43
69
|
export declare function isManagedKeyGenerallyAvailable(policy: ManagedKeyPolicyV1): boolean;
|
|
44
70
|
export declare function isManagedKeyAdmissionAllowed(policy: ManagedKeyPolicyV1): boolean;
|
|
45
71
|
export declare function assertManagedKeyModeAvailable(policy?: ManagedKeyPolicyV1): void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export const CREDENTIAL_MODES = ["byok", "managed"];
|
|
2
2
|
export const DEFAULT_CREDENTIAL_MODE = "byok";
|
|
3
3
|
export const MANAGED_KEY_POLICY_SCHEMA_VERSION = 1;
|
|
4
|
+
export const MANAGED_KEY_RESERVATION_SCHEMA_VERSION = 1;
|
|
4
5
|
export const MANAGED_KEY_LAUNCH_STAGES = ["blocked", "pilot", "ga"];
|
|
5
6
|
export const MANAGED_KEY_FEATURE_DECISIONS = ["disabled", "allowed"];
|
|
7
|
+
export const MANAGED_KEY_RESERVATION_STATUSES = ["open", "settled", "released"];
|
|
6
8
|
export const BLOCKED_MANAGED_KEY_FEATURE_POLICY_V1 = Object.freeze({
|
|
7
9
|
files: "disabled",
|
|
8
10
|
packages: "disabled",
|
|
@@ -15,7 +17,7 @@ export const BLOCKED_MANAGED_KEY_POLICY_V1 = Object.freeze({
|
|
|
15
17
|
schemaVersion: MANAGED_KEY_POLICY_SCHEMA_VERSION,
|
|
16
18
|
credentialMode: "managed",
|
|
17
19
|
launchStage: "blocked",
|
|
18
|
-
|
|
20
|
+
privateImplementationAvailable: false,
|
|
19
21
|
billingRequired: true,
|
|
20
22
|
providers: Object.freeze([]),
|
|
21
23
|
runtimes: Object.freeze([]),
|
|
@@ -23,7 +25,7 @@ export const BLOCKED_MANAGED_KEY_POLICY_V1 = Object.freeze({
|
|
|
23
25
|
});
|
|
24
26
|
export class ManagedKeyUnavailableError extends Error {
|
|
25
27
|
code = "managed_key_unavailable";
|
|
26
|
-
constructor(message = "credentialMode: \"managed\" is not available") {
|
|
28
|
+
constructor(message = "credentialMode: \"managed\" is not available without a private managed-key implementation") {
|
|
27
29
|
super(message);
|
|
28
30
|
this.name = "ManagedKeyUnavailableError";
|
|
29
31
|
}
|
|
@@ -43,11 +45,58 @@ export function credentialModeOrDefault(input) {
|
|
|
43
45
|
export function isCredentialMode(input) {
|
|
44
46
|
return typeof input === "string" && CREDENTIAL_MODES.includes(input);
|
|
45
47
|
}
|
|
48
|
+
export function buildManagedKeyReservationLifecycle(input) {
|
|
49
|
+
const status = normalizeManagedKeyReservationStatus(input.status);
|
|
50
|
+
const reservedCreditUnits = nonNegativeFinite(input.reservedCreditUnits, "reservedCreditUnits");
|
|
51
|
+
const chargedCreditUnits = nonNegativeFinite(input.chargedCreditUnits, "chargedCreditUnits");
|
|
52
|
+
const releasedCreditUnits = nonNegativeFinite(input.releasedCreditUnits, "releasedCreditUnits");
|
|
53
|
+
if (input.credentialMode !== undefined && input.credentialMode !== "managed") {
|
|
54
|
+
throw new Error("managed-key reservation credentialMode must be managed");
|
|
55
|
+
}
|
|
56
|
+
if (status === "open") {
|
|
57
|
+
if (input.closedAt !== undefined) {
|
|
58
|
+
throw new Error("managed-key open reservation must not have closedAt");
|
|
59
|
+
}
|
|
60
|
+
if (chargedCreditUnits !== 0 || releasedCreditUnits !== 0) {
|
|
61
|
+
throw new Error("managed-key open reservation cannot have charged or released credit units");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (status === "settled") {
|
|
65
|
+
if (!input.closedAt) {
|
|
66
|
+
throw new Error("managed-key settled reservation requires closedAt");
|
|
67
|
+
}
|
|
68
|
+
const expectedReleased = Math.max(0, reservedCreditUnits - chargedCreditUnits);
|
|
69
|
+
if (!nearlyEqual(releasedCreditUnits, expectedReleased)) {
|
|
70
|
+
throw new Error("managed-key settlement releasedCreditUnits must equal max(reserved - charged, 0)");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (status === "released") {
|
|
74
|
+
if (!input.closedAt) {
|
|
75
|
+
throw new Error("managed-key released reservation requires closedAt");
|
|
76
|
+
}
|
|
77
|
+
if (chargedCreditUnits !== 0 || !nearlyEqual(releasedCreditUnits, reservedCreditUnits)) {
|
|
78
|
+
throw new Error("managed-key released reservation must release all reserved credit units without charge");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
schemaVersion: MANAGED_KEY_RESERVATION_SCHEMA_VERSION,
|
|
83
|
+
reservationId: nonEmptyString(input.reservationId, "reservationId"),
|
|
84
|
+
workspaceId: nonEmptyString(input.workspaceId, "workspaceId"),
|
|
85
|
+
runId: nonEmptyString(input.runId, "runId"),
|
|
86
|
+
credentialMode: "managed",
|
|
87
|
+
status,
|
|
88
|
+
reservedCreditUnits,
|
|
89
|
+
chargedCreditUnits,
|
|
90
|
+
releasedCreditUnits,
|
|
91
|
+
...(input.createdAt ? { createdAt: input.createdAt } : {}),
|
|
92
|
+
...(input.closedAt ? { closedAt: input.closedAt } : {})
|
|
93
|
+
});
|
|
94
|
+
}
|
|
46
95
|
export function isManagedKeyGenerallyAvailable(policy) {
|
|
47
|
-
return policy.launchStage === "ga" && policy.
|
|
96
|
+
return policy.launchStage === "ga" && policy.privateImplementationAvailable;
|
|
48
97
|
}
|
|
49
98
|
export function isManagedKeyAdmissionAllowed(policy) {
|
|
50
|
-
return policy.launchStage !== "blocked" && policy.
|
|
99
|
+
return policy.launchStage !== "blocked" && policy.privateImplementationAvailable;
|
|
51
100
|
}
|
|
52
101
|
export function assertManagedKeyModeAvailable(policy = BLOCKED_MANAGED_KEY_POLICY_V1) {
|
|
53
102
|
if (!isManagedKeyGenerallyAvailable(policy)) {
|
|
@@ -107,4 +156,26 @@ function resolvePolicyDenial(input) {
|
|
|
107
156
|
}
|
|
108
157
|
return null;
|
|
109
158
|
}
|
|
159
|
+
function normalizeManagedKeyReservationStatus(input) {
|
|
160
|
+
if (typeof input !== "string" ||
|
|
161
|
+
!MANAGED_KEY_RESERVATION_STATUSES.includes(input)) {
|
|
162
|
+
throw new Error(`managed-key reservation status ${String(input)} is not supported`);
|
|
163
|
+
}
|
|
164
|
+
return input;
|
|
165
|
+
}
|
|
166
|
+
function nonEmptyString(value, field) {
|
|
167
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
168
|
+
throw new Error(`managed-key reservation ${field} must be a non-empty string`);
|
|
169
|
+
}
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function nonNegativeFinite(value, field) {
|
|
173
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
174
|
+
throw new Error(`managed-key reservation ${field} must be a non-negative finite number`);
|
|
175
|
+
}
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
function nearlyEqual(left, right) {
|
|
179
|
+
return Math.abs(left - right) <= 1e-9;
|
|
180
|
+
}
|
|
110
181
|
//# sourceMappingURL=managed-key.js.map
|
|
@@ -27,9 +27,13 @@ export declare function getRun(http: HttpClient, runId: string): Promise<Run>;
|
|
|
27
27
|
* stays for callers that only need the loose record.
|
|
28
28
|
*/
|
|
29
29
|
export declare function getRunUnit(http: HttpClient, runId: string): Promise<RunUnit>;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* List a run's events. The read endpoint is PAGED (bounded per response so a
|
|
32
|
+
* long run can't return an unbounded body); this follows `nextCursor` across
|
|
33
|
+
* pages and returns the FULL accumulated list, preserving the prior single-call
|
|
34
|
+
* contract for callers (download/*, CLI, streamEvents polling).
|
|
35
|
+
*/
|
|
36
|
+
export declare function listRunEvents(http: HttpClient, runId: string): Promise<readonly RunEvent[]>;
|
|
33
37
|
/** A coordinator WS connection grant minted by the hosted API's ticket broker. */
|
|
34
38
|
export interface CoordinatorTicket {
|
|
35
39
|
readonly wsUrl: string;
|
|
@@ -44,11 +48,6 @@ export interface CoordinatorTicket {
|
|
|
44
48
|
*/
|
|
45
49
|
export declare function getCoordinatorTicket(http: HttpClient, runId: string): Promise<CoordinatorTicket>;
|
|
46
50
|
export declare function listOutputs(http: HttpClient, runId: string): Promise<readonly Output[]>;
|
|
47
|
-
/**
|
|
48
|
-
* List the run's platform diagnostics (the `logs` namespace). Legacy stored
|
|
49
|
-
* filenames are normalized to canonical public namespaces.
|
|
50
|
-
*/
|
|
51
|
-
export declare function listLogs(http: HttpClient, runId: string): Promise<readonly Output[]>;
|
|
52
51
|
export declare function createOutputLink(http: HttpClient, runId: string, outputId: string): Promise<SignedOutputLink>;
|
|
53
52
|
export declare function resolveOutputFileSelector(outputs: readonly Output[], selector: OutputFileSelector, runId?: string): Output;
|
|
54
53
|
export declare function downloadOutput(http: HttpClient, runId: string, selector: OutputFileSelector): Promise<OutputFileDownload>;
|
|
@@ -63,15 +62,12 @@ export declare function deleteRun(http: HttpClient, runId: string): Promise<void
|
|
|
63
62
|
export declare function deleteWorkspaceAsset(http: HttpClient, hash: string): Promise<void>;
|
|
64
63
|
export declare function whoami(http: HttpClient): Promise<WhoAmI>;
|
|
65
64
|
/**
|
|
66
|
-
* Download EVERYTHING about a run as one zip, organised into the
|
|
65
|
+
* Download EVERYTHING public about a run as one zip, organised into the three
|
|
67
66
|
* namespace folders:
|
|
68
67
|
*
|
|
69
68
|
* metadata/run.json — the run record.
|
|
70
69
|
* events/events.jsonl — typed event-channel records.
|
|
71
|
-
* events/logs.jsonl — log-channel records, when the API serves them.
|
|
72
|
-
* events/all.jsonl — full unified stream, when the API serves it.
|
|
73
70
|
* outputs/<rel> — the run's deliverables.
|
|
74
|
-
* logs/<rel> — platform diagnostics.
|
|
75
71
|
* manifest.json — `RunRecordManifestV1`.
|
|
76
72
|
*/
|
|
77
73
|
export declare function download(http: HttpClient, runId: string): Promise<Uint8Array>;
|
|
@@ -81,16 +77,9 @@ export declare function download(http: HttpClient, runId: string): Promise<Uint8
|
|
|
81
77
|
* (`{ runId, namespace: "outputs", outputs[], errors[] }`).
|
|
82
78
|
*/
|
|
83
79
|
export declare function downloadOutputs(http: HttpClient, runId: string): Promise<Uint8Array>;
|
|
84
|
-
/**
|
|
85
|
-
* Download only the platform diagnostics (the `logs` namespace). Zip
|
|
86
|
-
* layout: `<rel>` per file plus a `manifest.json`
|
|
87
|
-
* (`{ runId, namespace: "logs", logs[], errors[] }`).
|
|
88
|
-
*/
|
|
89
|
-
export declare function downloadLogs(http: HttpClient, runId: string): Promise<Uint8Array>;
|
|
90
80
|
/**
|
|
91
81
|
* Download only the event archive (the `events` namespace). Always includes
|
|
92
|
-
* typed `events.jsonl
|
|
93
|
-
* event API proves those channel exports are available.
|
|
82
|
+
* typed `events.jsonl`.
|
|
94
83
|
*/
|
|
95
84
|
export declare function downloadEvents(http: HttpClient, runId: string): Promise<Uint8Array>;
|
|
96
85
|
/**
|