@manyoars/oars 0.1.1 → 0.3.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/README.md +134 -26
- package/dist/api.js +1 -1
- package/dist/bin.js +6 -29
- package/dist/cli.d.ts +6 -3
- package/dist/cli.js +370 -72
- package/dist/commands/keys.d.ts +31 -0
- package/dist/commands/keys.js +65 -0
- package/dist/commands/login.d.ts +1 -1
- package/dist/commands/login.js +3 -5
- package/dist/errors.d.ts +13 -13
- package/dist/errors.js +28 -11
- package/dist/gate/checks.d.ts +68 -0
- package/dist/gate/checks.js +584 -0
- package/dist/gate/command.d.ts +14 -0
- package/dist/gate/command.js +61 -0
- package/dist/gate/hook-fill-main.d.ts +1 -0
- package/dist/gate/hook-fill-main.js +16 -0
- package/dist/gate/hook-fill.d.ts +38 -0
- package/dist/gate/hook-fill.js +94 -0
- package/dist/gate/hook-main.d.ts +1 -0
- package/dist/gate/hook-main.js +35 -0
- package/dist/gate/hook-process.d.ts +14 -0
- package/dist/gate/hook-process.js +64 -0
- package/dist/gate/hook.d.ts +46 -0
- package/dist/gate/hook.js +256 -0
- package/dist/gate/install.d.ts +25 -0
- package/dist/gate/install.js +126 -0
- package/dist/gate/local-bin.d.ts +8 -0
- package/dist/gate/local-bin.js +36 -0
- package/dist/gate/notes.d.ts +19 -0
- package/dist/gate/notes.js +27 -0
- package/dist/gate/novelty.d.ts +35 -0
- package/dist/gate/novelty.js +99 -0
- package/dist/gate/policy.d.ts +25 -0
- package/dist/gate/policy.js +73 -0
- package/dist/gate/record.d.ts +120 -0
- package/dist/gate/record.js +125 -0
- package/dist/gate/registry-state.d.ts +10 -0
- package/dist/gate/registry-state.js +30 -0
- package/dist/gate/registry.d.ts +56 -0
- package/dist/gate/registry.js +94 -0
- package/dist/gate/replay.d.ts +20 -0
- package/dist/gate/replay.js +257 -0
- package/dist/gate/report.d.ts +50 -0
- package/dist/gate/report.js +89 -0
- package/dist/gate/scope.d.ts +7 -0
- package/dist/gate/scope.js +27 -0
- package/dist/gate/transcripts.d.ts +68 -0
- package/dist/gate/transcripts.js +232 -0
- package/dist/hook-bin.d.ts +1 -0
- package/dist/hook-bin.js +25 -0
- package/dist/issuer.d.ts +16 -0
- package/dist/issuer.js +55 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +64 -0
- package/dist/oidc.js +1 -1
- package/dist/sessions/actions.d.ts +35 -0
- package/dist/sessions/actions.js +351 -0
- package/dist/sessions/allow.d.ts +14 -0
- package/dist/sessions/allow.js +51 -0
- package/dist/sessions/archive.d.ts +11 -0
- package/dist/sessions/archive.js +38 -0
- package/dist/sessions/audit.d.ts +42 -0
- package/dist/sessions/audit.js +104 -0
- package/dist/sessions/baseline.d.ts +62 -0
- package/dist/sessions/baseline.js +188 -0
- package/dist/sessions/capture-runtime.d.ts +10 -0
- package/dist/sessions/capture-runtime.js +12 -0
- package/dist/sessions/cell-increments.d.ts +43 -0
- package/dist/sessions/cell-increments.js +209 -0
- package/dist/sessions/cells.d.ts +79 -0
- package/dist/sessions/cells.js +181 -0
- package/dist/sessions/codex.d.ts +53 -0
- package/dist/sessions/codex.js +679 -0
- package/dist/sessions/command.d.ts +117 -0
- package/dist/sessions/command.js +373 -0
- package/dist/sessions/copilot.d.ts +40 -0
- package/dist/sessions/copilot.js +432 -0
- package/dist/sessions/cursor.d.ts +24 -0
- package/dist/sessions/cursor.js +324 -0
- package/dist/sessions/derive.d.ts +57 -0
- package/dist/sessions/derive.js +320 -0
- package/dist/sessions/exclusion.d.ts +81 -0
- package/dist/sessions/exclusion.js +353 -0
- package/dist/sessions/extract.d.ts +179 -0
- package/dist/sessions/extract.js +1774 -0
- package/dist/sessions/fold.d.ts +17 -0
- package/dist/sessions/fold.js +71 -0
- package/dist/sessions/frame.d.ts +24 -0
- package/dist/sessions/frame.js +95 -0
- package/dist/sessions/gemini.d.ts +37 -0
- package/dist/sessions/gemini.js +613 -0
- package/dist/sessions/gold-arms.d.ts +67 -0
- package/dist/sessions/gold-arms.js +172 -0
- package/dist/sessions/gold.d.ts +197 -0
- package/dist/sessions/gold.js +463 -0
- package/dist/sessions/graph.d.ts +12 -0
- package/dist/sessions/graph.js +34 -0
- package/dist/sessions/grok.d.ts +36 -0
- package/dist/sessions/grok.js +421 -0
- package/dist/sessions/hook-calls.d.ts +3 -0
- package/dist/sessions/hook-calls.js +47 -0
- package/dist/sessions/hook-index.d.ts +53 -0
- package/dist/sessions/hook-index.js +157 -0
- package/dist/sessions/hooks.d.ts +13 -0
- package/dist/sessions/hooks.js +170 -0
- package/dist/sessions/identity.d.ts +22 -0
- package/dist/sessions/identity.js +101 -0
- package/dist/sessions/install.d.ts +54 -0
- package/dist/sessions/install.js +201 -0
- package/dist/sessions/lineage.d.ts +19 -0
- package/dist/sessions/lineage.js +118 -0
- package/dist/sessions/mcp.d.ts +6 -0
- package/dist/sessions/mcp.js +153 -0
- package/dist/sessions/merkle.d.ts +7 -0
- package/dist/sessions/merkle.js +28 -0
- package/dist/sessions/motif.d.ts +43 -0
- package/dist/sessions/motif.js +59 -0
- package/dist/sessions/package-resolve.d.ts +30 -0
- package/dist/sessions/package-resolve.js +183 -0
- package/dist/sessions/pending.d.ts +34 -0
- package/dist/sessions/pending.js +142 -0
- package/dist/sessions/pool-command.d.ts +44 -0
- package/dist/sessions/pool-command.js +542 -0
- package/dist/sessions/pool-delta.d.ts +54 -0
- package/dist/sessions/pool-delta.js +190 -0
- package/dist/sessions/pool-outbox.d.ts +63 -0
- package/dist/sessions/pool-outbox.js +260 -0
- package/dist/sessions/pool-print.d.ts +26 -0
- package/dist/sessions/pool-print.js +68 -0
- package/dist/sessions/pool-wire.d.ts +225 -0
- package/dist/sessions/pool-wire.js +620 -0
- package/dist/sessions/pool.d.ts +32 -0
- package/dist/sessions/pool.js +164 -0
- package/dist/sessions/projection.d.ts +75 -0
- package/dist/sessions/projection.js +165 -0
- package/dist/sessions/public-vocabulary.d.ts +73 -0
- package/dist/sessions/public-vocabulary.js +302 -0
- package/dist/sessions/query.d.ts +111 -0
- package/dist/sessions/query.js +246 -0
- package/dist/sessions/read-adoption.d.ts +2 -0
- package/dist/sessions/read-adoption.js +27 -0
- package/dist/sessions/read-call-shape.d.ts +2 -0
- package/dist/sessions/read-call-shape.js +25 -0
- package/dist/sessions/read-change.d.ts +2 -0
- package/dist/sessions/read-change.js +30 -0
- package/dist/sessions/read-cost.d.ts +4 -0
- package/dist/sessions/read-cost.js +37 -0
- package/dist/sessions/read-day-move.d.ts +3 -0
- package/dist/sessions/read-day-move.js +65 -0
- package/dist/sessions/read-fit.d.ts +7 -0
- package/dist/sessions/read-fit.js +41 -0
- package/dist/sessions/read-interaction.d.ts +5 -0
- package/dist/sessions/read-interaction.js +62 -0
- package/dist/sessions/read-lead-lag.d.ts +15 -0
- package/dist/sessions/read-lead-lag.js +81 -0
- package/dist/sessions/read-live.d.ts +6 -0
- package/dist/sessions/read-live.js +38 -0
- package/dist/sessions/read-method.d.ts +2 -0
- package/dist/sessions/read-method.js +29 -0
- package/dist/sessions/read-next-step.d.ts +2 -0
- package/dist/sessions/read-next-step.js +35 -0
- package/dist/sessions/read-order.d.ts +54 -0
- package/dist/sessions/read-order.js +62 -0
- package/dist/sessions/read-readable.d.ts +8 -0
- package/dist/sessions/read-readable.js +24 -0
- package/dist/sessions/read-recovery.d.ts +20 -0
- package/dist/sessions/read-recovery.js +97 -0
- package/dist/sessions/read-rows.d.ts +13 -0
- package/dist/sessions/read-rows.js +47 -0
- package/dist/sessions/read-substitution.d.ts +5 -0
- package/dist/sessions/read-substitution.js +51 -0
- package/dist/sessions/read-support.d.ts +48 -0
- package/dist/sessions/read-support.js +73 -0
- package/dist/sessions/read-tool-day.d.ts +2 -0
- package/dist/sessions/read-tool-day.js +26 -0
- package/dist/sessions/read-version-boundary.d.ts +31 -0
- package/dist/sessions/read-version-boundary.js +95 -0
- package/dist/sessions/read.d.ts +79 -0
- package/dist/sessions/read.js +84 -0
- package/dist/sessions/recall-consequence.d.ts +45 -0
- package/dist/sessions/recall-consequence.js +179 -0
- package/dist/sessions/recall-object.d.ts +137 -0
- package/dist/sessions/recall-object.js +77 -0
- package/dist/sessions/recall-pool.d.ts +19 -0
- package/dist/sessions/recall-pool.js +96 -0
- package/dist/sessions/recall-push.d.ts +26 -0
- package/dist/sessions/recall-push.js +95 -0
- package/dist/sessions/recall-reads.d.ts +117 -0
- package/dist/sessions/recall-reads.js +410 -0
- package/dist/sessions/recall-render.d.ts +19 -0
- package/dist/sessions/recall-render.js +168 -0
- package/dist/sessions/redact.d.ts +6 -0
- package/dist/sessions/redact.js +54 -0
- package/dist/sessions/repo-root.d.ts +10 -0
- package/dist/sessions/repo-root.js +101 -0
- package/dist/sessions/schema.d.ts +644 -0
- package/dist/sessions/schema.js +148 -0
- package/dist/sessions/session-shape.d.ts +73 -0
- package/dist/sessions/session-shape.js +342 -0
- package/dist/sessions/sources.d.ts +50 -0
- package/dist/sessions/sources.js +159 -0
- package/dist/sessions/steps.d.ts +105 -0
- package/dist/sessions/steps.js +250 -0
- package/dist/sessions/store.d.ts +297 -0
- package/dist/sessions/store.js +1478 -0
- package/dist/sessions/strata.d.ts +26 -0
- package/dist/sessions/strata.js +77 -0
- package/dist/sessions/survival.d.ts +25 -0
- package/dist/sessions/survival.js +230 -0
- package/dist/sessions/verify.d.ts +53 -0
- package/dist/sessions/verify.js +286 -0
- package/dist/sessions/vocab-load.d.ts +26 -0
- package/dist/sessions/vocab-load.js +61 -0
- package/dist/sessions/vscode-copilot.d.ts +38 -0
- package/dist/sessions/vscode-copilot.js +477 -0
- package/package.json +4 -5
- package/vocab/crates.json +1 -0
- package/vocab/go.json +1 -0
- package/vocab/harness.json +1 -0
- package/vocab/homebrew.json +1 -0
- package/vocab/manifest.json +1 -0
- package/vocab/maven.json +1 -0
- package/vocab/models.json +1 -0
- package/vocab/npm.json +1 -0
- package/vocab/programs.json +1 -0
- package/vocab/pypi.json +1 -0
- package/vocab/rubygems.json +1 -0
- package/dist/commands/deploy-input.d.ts +0 -35
- package/dist/commands/deploy-input.js +0 -107
- package/dist/commands/deploy.d.ts +0 -22
- package/dist/commands/deploy.js +0 -120
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/status.js +0 -18
- package/dist/commands/test-charge.d.ts +0 -11
- package/dist/commands/test-charge.js +0 -34
- package/dist/journal.d.ts +0 -26
- package/dist/journal.js +0 -58
- package/parity/deploy-plans/paid-api.json +0 -34
- package/parity/deploy-plans/per-task-agent.json +0 -34
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { components } from '@manyoars/oars-client';
|
|
2
|
-
type RegisterWorkloadRequest = components['schemas']['RegisterWorkloadRequest'];
|
|
3
|
-
type CreatePriceInput = components['schemas']['CreatePriceInput'];
|
|
4
|
-
/** What `oars deploy` is told to deploy, as the flags give it. A flag the developer did not pass is undefined. */
|
|
5
|
-
export interface DeployFlags {
|
|
6
|
-
template?: string;
|
|
7
|
-
image?: string;
|
|
8
|
-
manifestFile?: string;
|
|
9
|
-
name?: string;
|
|
10
|
-
port?: number;
|
|
11
|
-
public?: boolean;
|
|
12
|
-
cpuMillicores?: number;
|
|
13
|
-
memoryMib?: number;
|
|
14
|
-
alwaysOn?: boolean;
|
|
15
|
-
/** `--price`: starter prices to pick from a template. */
|
|
16
|
-
prices: string[];
|
|
17
|
-
/** `--no-prices`. */
|
|
18
|
-
noPrices: boolean;
|
|
19
|
-
priceFile?: string;
|
|
20
|
-
/** `NAME=value` entries. */
|
|
21
|
-
secrets: string[];
|
|
22
|
-
/** Names of environment variables to read. */
|
|
23
|
-
secretsFromEnv: string[];
|
|
24
|
-
}
|
|
25
|
-
/** The register body and the prices to create, validated and ready to send. */
|
|
26
|
-
export interface DeployInput {
|
|
27
|
-
request: RegisterWorkloadRequest;
|
|
28
|
-
prices: CreatePriceInput[];
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Turns the flags into the calls' bodies without touching the network, so a mistake costs nothing. Exactly one of
|
|
32
|
-
* `--template`, `--image`, `--manifest`; `--template` waits for the public template catalog (the `oars templates` brief).
|
|
33
|
-
*/
|
|
34
|
-
export declare const loadDeployInput: (flags: DeployFlags, env: NodeJS.ProcessEnv) => Promise<DeployInput>;
|
|
35
|
-
export {};
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { CliError, ExitCode } from '../errors.js';
|
|
3
|
-
const usage = (message) => new CliError(message, ExitCode.USAGE, 'usage');
|
|
4
|
-
const DIGEST = /@sha256:[0-9a-f]{64}$/;
|
|
5
|
-
const PRICE_KINDS = ['ONE_TIME', 'RECURRING_MONTHLY', 'CREDIT_PACK'];
|
|
6
|
-
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
7
|
-
const has = (value, key, type) => typeof Reflect.get(value, key) === type;
|
|
8
|
-
const isService = (value) => isObject(value) && has(value, 'name', 'string') && has(value, 'image', 'string') && has(value, 'cpuMillicores', 'number') && has(value, 'memoryMib', 'number');
|
|
9
|
-
const isRegisterRequest = (value) => {
|
|
10
|
-
if (!isObject(value) || !has(value, 'name', 'string'))
|
|
11
|
-
return false;
|
|
12
|
-
const services = Reflect.get(value, 'services');
|
|
13
|
-
return Array.isArray(services) && services.length > 0 && services.every(isService);
|
|
14
|
-
};
|
|
15
|
-
const isPriceKind = (value) => PRICE_KINDS.some((kind) => kind === value);
|
|
16
|
-
const isPrice = (value) => isObject(value) && has(value, 'priceRef', 'string') && has(value, 'displayName', 'string') && has(value, 'amountUsd', 'string') && isPriceKind(Reflect.get(value, 'kind'));
|
|
17
|
-
const isPrices = (value) => Array.isArray(value) && value.every(isPrice);
|
|
18
|
-
/** A JSON file's parsed content; a file that cannot be read or parsed is exit 2 naming it. */
|
|
19
|
-
const readJson = async (path) => {
|
|
20
|
-
let text;
|
|
21
|
-
try {
|
|
22
|
-
text = await readFile(path, 'utf8');
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
throw usage(`Cannot read ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
return JSON.parse(text);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
throw usage(`${path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
/** The platform deploys what a digest names; a tag can move, so a service without one is refused before any request. */
|
|
35
|
-
const requireDigest = (image) => {
|
|
36
|
-
if (!DIGEST.test(image)) {
|
|
37
|
-
throw usage(`${image} is not pinned by digest. Print one with: docker buildx imagetools inspect ${image}\nthen deploy <name>@sha256:<64 hex digits>.`);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const secretsFrom = (flags, env) => [
|
|
41
|
-
...flags.secrets.map((entry) => {
|
|
42
|
-
const at = entry.indexOf('=');
|
|
43
|
-
// The entry may be the secret itself, so the message does not echo it.
|
|
44
|
-
if (at < 1)
|
|
45
|
-
throw usage('--secret takes NAME=value; use --secret-from-env NAME to read the value from the environment.');
|
|
46
|
-
return { name: entry.slice(0, at), value: entry.slice(at + 1) };
|
|
47
|
-
}),
|
|
48
|
-
...flags.secretsFromEnv.map((name) => {
|
|
49
|
-
const value = env[name];
|
|
50
|
-
if (value === undefined || value === '')
|
|
51
|
-
throw usage(`--secret-from-env ${name}: the environment variable ${name} is not set.`);
|
|
52
|
-
return { name, value };
|
|
53
|
-
}),
|
|
54
|
-
];
|
|
55
|
-
const requestFromFlags = (flags, image) => {
|
|
56
|
-
if (flags.name === undefined)
|
|
57
|
-
throw usage('--image needs --name <name>.');
|
|
58
|
-
requireDigest(image);
|
|
59
|
-
return {
|
|
60
|
-
name: flags.name,
|
|
61
|
-
...(flags.alwaysOn === true ? { alwaysOn: true } : {}),
|
|
62
|
-
services: [{ name: 'app', image, port: flags.port ?? 8080, public: flags.public ?? false, cpuMillicores: flags.cpuMillicores ?? 250, memoryMib: flags.memoryMib ?? 256 }],
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
const requestFromFile = async (flags, path) => {
|
|
66
|
-
const given = ['name', 'port', 'public', 'cpuMillicores', 'memoryMib'].filter((key) => flags[key] !== undefined);
|
|
67
|
-
if (given.length > 0)
|
|
68
|
-
throw usage('--name, --port, --public, --cpu-millicores and --memory-mib describe an --image deploy; a --manifest file holds its own.');
|
|
69
|
-
const parsed = await readJson(path);
|
|
70
|
-
if (!isRegisterRequest(parsed)) {
|
|
71
|
-
throw usage(`${path} is not a workload manifest: it needs a name and services, each with name, image, cpuMillicores and memoryMib.`);
|
|
72
|
-
}
|
|
73
|
-
for (const service of parsed.services)
|
|
74
|
-
requireDigest(service.image);
|
|
75
|
-
return flags.alwaysOn === true ? { ...parsed, alwaysOn: true } : parsed;
|
|
76
|
-
};
|
|
77
|
-
const pricesFromFile = async (path) => {
|
|
78
|
-
const parsed = await readJson(path);
|
|
79
|
-
if (!isPrices(parsed)) {
|
|
80
|
-
throw usage(`${path} must be a JSON array of prices, each with priceRef, displayName, amountUsd (a decimal string) and kind (ONE_TIME, RECURRING_MONTHLY or CREDIT_PACK).`);
|
|
81
|
-
}
|
|
82
|
-
return parsed;
|
|
83
|
-
};
|
|
84
|
-
const baseRequest = async (flags) => {
|
|
85
|
-
if (flags.image !== undefined)
|
|
86
|
-
return requestFromFlags(flags, flags.image);
|
|
87
|
-
if (flags.manifestFile !== undefined)
|
|
88
|
-
return requestFromFile(flags, flags.manifestFile);
|
|
89
|
-
throw usage('--template is not available yet: the public API does not publish the template catalog. Deploy with --image <name@sha256:...> --name <name>, or with --manifest <file>.');
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Turns the flags into the calls' bodies without touching the network, so a mistake costs nothing. Exactly one of
|
|
93
|
-
* `--template`, `--image`, `--manifest`; `--template` waits for the public template catalog (the `oars templates` brief).
|
|
94
|
-
*/
|
|
95
|
-
export const loadDeployInput = async (flags, env) => {
|
|
96
|
-
if ([flags.template, flags.image, flags.manifestFile].filter((source) => source !== undefined).length !== 1) {
|
|
97
|
-
throw usage('Pass exactly one of --template, --image or --manifest.');
|
|
98
|
-
}
|
|
99
|
-
const base = await baseRequest(flags);
|
|
100
|
-
if (flags.prices.length > 0 || flags.noPrices)
|
|
101
|
-
throw usage("--price and --no-prices choose among a template's starter prices; use --price-file with --image or --manifest.");
|
|
102
|
-
const secrets = [...(base.secrets ?? []), ...secretsFrom(flags, env)];
|
|
103
|
-
return {
|
|
104
|
-
request: secrets.length === 0 ? base : { ...base, secrets },
|
|
105
|
-
prices: flags.priceFile === undefined ? [] : await pricesFromFile(flags.priceFile),
|
|
106
|
-
};
|
|
107
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type DeployFlags } from './deploy-input.js';
|
|
2
|
-
import type { Outcome, RunDeps } from '../cli.js';
|
|
3
|
-
export interface DeployOptions extends DeployFlags {
|
|
4
|
-
apiUrl: string;
|
|
5
|
-
/** The command line as typed (without the node and script entries), for the rerun line. */
|
|
6
|
-
argv: string[];
|
|
7
|
-
/** False for `--no-start`: register and price, but make no deploy call. */
|
|
8
|
-
start: boolean;
|
|
9
|
-
/** `--idempotency-key`: the base of every step's key. A fresh UUID when absent. */
|
|
10
|
-
idempotencyKey?: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* The command that resumes a failed deploy: the same arguments plus `--idempotency-key <base>`. Secret values are never
|
|
14
|
-
* printed, so `--secret NAME=value` becomes `--secret-from-env NAME` (the developer exports NAME before the rerun).
|
|
15
|
-
*/
|
|
16
|
-
export declare const rerunCommand: (argv: string[], base: string) => string;
|
|
17
|
-
/**
|
|
18
|
-
* The web deploy page's order: register, each price, deploy (design §3.1). Each call carries its own idempotency key
|
|
19
|
-
* (`stepKey(base, step)`), so a rerun with the same base replays what finished and carries on. The first failed step stops
|
|
20
|
-
* the sequence (exit 9) with the workload id, the rerun line, and the SDK key when this run minted it.
|
|
21
|
-
*/
|
|
22
|
-
export declare const deploy: (deps: RunDeps, options: DeployOptions) => Promise<Outcome>;
|
package/dist/commands/deploy.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { apiClient, unwrap } from '../api.js';
|
|
3
|
-
import { CliError, ExitCode } from '../errors.js';
|
|
4
|
-
import { stepKey } from '../journal.js';
|
|
5
|
-
import { resolveAuthToken } from '../session.js';
|
|
6
|
-
import { loadDeployInput } from './deploy-input.js';
|
|
7
|
-
const SHELL_SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
8
|
-
const quote = (token) => (SHELL_SAFE.test(token) ? token : `'${token.replaceAll("'", "'\\''")}'`);
|
|
9
|
-
/**
|
|
10
|
-
* The command that resumes a failed deploy: the same arguments plus `--idempotency-key <base>`. Secret values are never
|
|
11
|
-
* printed, so `--secret NAME=value` becomes `--secret-from-env NAME` (the developer exports NAME before the rerun).
|
|
12
|
-
*/
|
|
13
|
-
export const rerunCommand = (argv, base) => {
|
|
14
|
-
const secretName = (entry) => entry.split('=')[0] ?? '';
|
|
15
|
-
const tokens = [];
|
|
16
|
-
for (let at = 0; at < argv.length; at += 1) {
|
|
17
|
-
const arg = argv[at] ?? '';
|
|
18
|
-
if (arg === '--idempotency-key') {
|
|
19
|
-
at += 1;
|
|
20
|
-
}
|
|
21
|
-
else if (arg === '--secret') {
|
|
22
|
-
at += 1;
|
|
23
|
-
tokens.push('--secret-from-env', secretName(argv[at] ?? ''));
|
|
24
|
-
}
|
|
25
|
-
else if (arg.startsWith('--secret=')) {
|
|
26
|
-
tokens.push('--secret-from-env', secretName(arg.slice('--secret='.length)));
|
|
27
|
-
}
|
|
28
|
-
else if (!arg.startsWith('--idempotency-key=')) {
|
|
29
|
-
tokens.push(arg);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return ['oars', ...tokens, '--idempotency-key', base].map(quote).join(' ');
|
|
33
|
-
};
|
|
34
|
-
/** A failure after register is exit 9 whatever its own code: the workload exists, so the developer's next move is the rerun. */
|
|
35
|
-
const partialFailure = (error, step, partial) => {
|
|
36
|
-
const cause = error instanceof CliError ? error : new CliError(`Unexpected error: ${error instanceof Error ? error.message : String(error)}`, ExitCode.UNEXPECTED, step);
|
|
37
|
-
return new CliError(cause.message, ExitCode.PARTIAL, step, cause.problem, partial);
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* The web deploy page's order: register, each price, deploy (design §3.1). Each call carries its own idempotency key
|
|
41
|
-
* (`stepKey(base, step)`), so a rerun with the same base replays what finished and carries on. The first failed step stops
|
|
42
|
-
* the sequence (exit 9) with the workload id, the rerun line, and the SDK key when this run minted it.
|
|
43
|
-
*/
|
|
44
|
-
export const deploy = async (deps, options) => {
|
|
45
|
-
const input = await loadDeployInput(options, deps.env);
|
|
46
|
-
const token = await resolveAuthToken(deps, options.apiUrl);
|
|
47
|
-
const base = options.idempotencyKey ?? randomUUID();
|
|
48
|
-
const clientFor = (step) => apiClient(deps, { apiUrl: options.apiUrl, step, token });
|
|
49
|
-
const prior = await deps.journal.read(base).catch(() => undefined);
|
|
50
|
-
const progress = { base, steps: prior?.steps ?? [] };
|
|
51
|
-
if (prior !== undefined) {
|
|
52
|
-
deps.io.stderr(`Resuming deploy ${base}: ${prior.steps.map((entry) => entry.step).join(', ') || 'nothing finished yet'} already done.\n`);
|
|
53
|
-
}
|
|
54
|
-
// The journal only tells the developer where the last run got to; the platform's replay is what makes the rerun correct, so
|
|
55
|
-
// a journal that cannot be written must not lose the one-time SDK key.
|
|
56
|
-
const note = async (step, status) => {
|
|
57
|
-
progress.steps = [...progress.steps.filter((entry) => entry.step !== step), { step, status }];
|
|
58
|
-
await deps.journal.write(progress).catch((error) => {
|
|
59
|
-
deps.io.stderr(`Could not record progress in the journal: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
60
|
-
});
|
|
61
|
-
};
|
|
62
|
-
const registerResult = await clientFor('register').POST('/agents/workloads', {
|
|
63
|
-
params: { header: { 'Idempotency-Key': stepKey(base, 'register') } },
|
|
64
|
-
body: input.request,
|
|
65
|
-
});
|
|
66
|
-
const workload = unwrap('register', registerResult);
|
|
67
|
-
await note('register', registerResult.response.status);
|
|
68
|
-
// A replayed registration carries no key: only the run that created the workload can show it.
|
|
69
|
-
const partial = { workloadId: workload.id, ...(workload.sdkKey === undefined ? {} : { sdkKey: workload.sdkKey }), rerun: rerunCommand(options.argv, base) };
|
|
70
|
-
const path = { workloadId: workload.id };
|
|
71
|
-
const stages = [
|
|
72
|
-
...input.prices.map((price) => {
|
|
73
|
-
const step = `price:${price.priceRef}`;
|
|
74
|
-
return {
|
|
75
|
-
step,
|
|
76
|
-
send: async () => {
|
|
77
|
-
const result = await clientFor(step).POST('/billing/agents/{workloadId}/prices', { params: { path }, headers: { 'Idempotency-Key': stepKey(base, step) }, body: price });
|
|
78
|
-
unwrap(step, result);
|
|
79
|
-
return result.response.status;
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}),
|
|
83
|
-
...(options.start
|
|
84
|
-
? [
|
|
85
|
-
{
|
|
86
|
-
step: 'deploy',
|
|
87
|
-
send: async () => {
|
|
88
|
-
const result = await clientFor('deploy').POST('/agents/workloads/{workloadId}/deploy', { params: { path }, headers: { 'Idempotency-Key': stepKey(base, 'deploy') } });
|
|
89
|
-
unwrap('deploy', result);
|
|
90
|
-
return result.response.status;
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
]
|
|
94
|
-
: []),
|
|
95
|
-
];
|
|
96
|
-
for (const stage of stages) {
|
|
97
|
-
let status;
|
|
98
|
-
try {
|
|
99
|
-
status = await stage.send();
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
throw partialFailure(error, stage.step, partial);
|
|
103
|
-
}
|
|
104
|
-
await note(stage.step, status);
|
|
105
|
-
}
|
|
106
|
-
await deps.journal.delete(base).catch(() => undefined);
|
|
107
|
-
const prices = input.prices.map((price) => price.priceRef);
|
|
108
|
-
const nextSteps = [`oars status ${workload.id}`, `oars test-charge ${workload.id}${prices[0] === undefined ? '' : ` --price ${prices[0]}`}`];
|
|
109
|
-
const lines = [
|
|
110
|
-
`${options.start ? 'Deployed' : 'Registered'} ${workload.name} (workload ${workload.id})`,
|
|
111
|
-
...(workload.sdkKey === undefined ? [] : [`SDK key (shown once; save it now): ${workload.sdkKey}`]),
|
|
112
|
-
...(prices.length === 0 ? [] : [`Prices: ${prices.join(', ')}`]),
|
|
113
|
-
'Next:',
|
|
114
|
-
...nextSteps.map((step) => ` ${step}`),
|
|
115
|
-
];
|
|
116
|
-
return {
|
|
117
|
-
document: { ok: true, workloadId: workload.id, ...(workload.sdkKey === undefined ? {} : { sdkKey: workload.sdkKey }), prices, started: options.start, nextSteps },
|
|
118
|
-
text: lines.join('\n'),
|
|
119
|
-
};
|
|
120
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Outcome, RunDeps } from '../cli.js';
|
|
2
|
-
export interface StatusOptions {
|
|
3
|
-
apiUrl: string;
|
|
4
|
-
workloadId: string;
|
|
5
|
-
}
|
|
6
|
-
/** The workload (`GET /agents/workloads/{id}`) and its billing mode (`GET /billing/agents/{id}/mode`, TEST until the creator goes live). */
|
|
7
|
-
export declare const status: (deps: RunDeps, options: StatusOptions) => Promise<Outcome>;
|
package/dist/commands/status.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { apiClient, unwrap } from '../api.js';
|
|
2
|
-
import { resolveAuthToken } from '../session.js';
|
|
3
|
-
const STEP = 'status';
|
|
4
|
-
/** The workload (`GET /agents/workloads/{id}`) and its billing mode (`GET /billing/agents/{id}/mode`, TEST until the creator goes live). */
|
|
5
|
-
export const status = async (deps, options) => {
|
|
6
|
-
const client = apiClient(deps, { apiUrl: options.apiUrl, step: STEP, token: await resolveAuthToken(deps, options.apiUrl) });
|
|
7
|
-
const params = { path: { workloadId: options.workloadId } };
|
|
8
|
-
const workload = unwrap(STEP, await client.GET('/agents/workloads/{workloadId}', { params }));
|
|
9
|
-
const { billingMode } = unwrap(STEP, await client.GET('/billing/agents/{workloadId}/mode', { params }));
|
|
10
|
-
const { deployment } = workload;
|
|
11
|
-
const lines = [
|
|
12
|
-
`${workload.name} (${workload.id})`,
|
|
13
|
-
`Workload: ${workload.status}${workload.suspended ? ' (suspended)' : ''}`,
|
|
14
|
-
`Deployment: ${deployment.status} (${deployment.health}), desired ${deployment.desiredState}`,
|
|
15
|
-
`Billing mode: ${billingMode}`,
|
|
16
|
-
];
|
|
17
|
-
return { document: { ok: true, workload, billingMode }, text: lines.join('\n') };
|
|
18
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Outcome, RunDeps } from '../cli.js';
|
|
2
|
-
export interface TestChargeOptions {
|
|
3
|
-
apiUrl: string;
|
|
4
|
-
workloadId: string;
|
|
5
|
-
/** `--price`; absent means the workload's one active price. */
|
|
6
|
-
priceRef?: string;
|
|
7
|
-
/** The base of the step key, so a retry of this command replays the charge instead of making a second one. */
|
|
8
|
-
idempotencyKey: string;
|
|
9
|
-
}
|
|
10
|
-
/** `POST /billing/agents/{id}/test-charges`: a TEST-mode charge against one of the workload's prices (no money moves). */
|
|
11
|
-
export declare const testCharge: (deps: RunDeps, options: TestChargeOptions) => Promise<Outcome>;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { apiClient, unwrap } from '../api.js';
|
|
2
|
-
import { CliError, ExitCode } from '../errors.js';
|
|
3
|
-
import { stepKey } from '../journal.js';
|
|
4
|
-
import { resolveAuthToken } from '../session.js';
|
|
5
|
-
const STEP = 'test-charge';
|
|
6
|
-
/** The price a charge without `--price` uses: the workload's only active one, else exit 2 saying which to pick from. */
|
|
7
|
-
const onlyActivePrice = async (client, workloadId) => {
|
|
8
|
-
const page = unwrap(STEP, await client.GET('/billing/agents/{workloadId}/prices', { params: { path: { workloadId }, query: { size: 100 } } }));
|
|
9
|
-
const active = page.content.filter((price) => price.active).map((price) => price.priceRef);
|
|
10
|
-
if (!page.last) {
|
|
11
|
-
throw new CliError(`${workloadId} has more prices than one request lists; pass --price <priceRef>.`, ExitCode.USAGE, 'usage');
|
|
12
|
-
}
|
|
13
|
-
const [only, ...others] = active;
|
|
14
|
-
if (only === undefined) {
|
|
15
|
-
throw new CliError(`${workloadId} has no active price; create one, or pass --price <priceRef>.`, ExitCode.USAGE, 'usage');
|
|
16
|
-
}
|
|
17
|
-
if (others.length > 0) {
|
|
18
|
-
throw new CliError(`${workloadId} has several active prices (${active.join(', ')}); pass --price <priceRef>.`, ExitCode.USAGE, 'usage');
|
|
19
|
-
}
|
|
20
|
-
return only;
|
|
21
|
-
};
|
|
22
|
-
/** `POST /billing/agents/{id}/test-charges`: a TEST-mode charge against one of the workload's prices (no money moves). */
|
|
23
|
-
export const testCharge = async (deps, options) => {
|
|
24
|
-
const client = apiClient(deps, { apiUrl: options.apiUrl, step: STEP, token: await resolveAuthToken(deps, options.apiUrl) });
|
|
25
|
-
const priceRef = options.priceRef ?? (await onlyActivePrice(client, options.workloadId));
|
|
26
|
-
const charge = unwrap(STEP, await client.POST('/billing/agents/{workloadId}/test-charges', {
|
|
27
|
-
params: { path: { workloadId: options.workloadId }, header: { 'Idempotency-Key': stepKey(options.idempotencyKey, STEP) } },
|
|
28
|
-
body: { priceRef },
|
|
29
|
-
}));
|
|
30
|
-
return {
|
|
31
|
-
document: { ok: true, chargeId: charge.chargeId, status: charge.status },
|
|
32
|
-
text: `Test charge ${charge.chargeId} for ${priceRef}: ${charge.status}`,
|
|
33
|
-
};
|
|
34
|
-
};
|
package/dist/journal.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where a command got to, for the rerun that follows a failure (design §3.2). It holds step names and the HTTP status each
|
|
3
|
-
* answered with, never a secret: the SDK key is shown once and the platform replays every finished step by its idempotency key.
|
|
4
|
-
*/
|
|
5
|
-
export interface JournalRecord {
|
|
6
|
-
/** The human base key (`--idempotency-key` or the UUID the first run made). */
|
|
7
|
-
base: string;
|
|
8
|
-
steps: Array<{
|
|
9
|
-
step: string;
|
|
10
|
-
status: number;
|
|
11
|
-
}>;
|
|
12
|
-
}
|
|
13
|
-
export interface JournalStore {
|
|
14
|
-
/** Undefined when no run with this base key has left a record, including when the file is unreadable as one. */
|
|
15
|
-
read: (base: string) => Promise<JournalRecord | undefined>;
|
|
16
|
-
write: (record: JournalRecord) => Promise<void>;
|
|
17
|
-
delete: (base: string) => Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
/** One step's `Idempotency-Key`: the lowercase SHA-256 hex of `<base>:<step>`, the 64-hex form every aor-common interceptor accepts. */
|
|
20
|
-
export declare const stepKey: (base: string, step: string) => string;
|
|
21
|
-
/**
|
|
22
|
-
* Keeps one file per base key in `<dir>/oars/journal/`, named by the key's hash because the key is whatever the developer typed.
|
|
23
|
-
* A write goes to a sibling file and is renamed over the old one; a torn file reads as no record, and the platform's replay
|
|
24
|
-
* makes the rerun correct either way.
|
|
25
|
-
*/
|
|
26
|
-
export declare const fileJournalStore: (dir: string) => JournalStore;
|
package/dist/journal.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
/** One step's `Idempotency-Key`: the lowercase SHA-256 hex of `<base>:<step>`, the 64-hex form every aor-common interceptor accepts. */
|
|
5
|
-
export const stepKey = (base, step) => createHash('sha256').update(`${base}:${step}`).digest('hex');
|
|
6
|
-
const isRecord = (value) => {
|
|
7
|
-
if (typeof value !== 'object' || value === null)
|
|
8
|
-
return false;
|
|
9
|
-
const base = Reflect.get(value, 'base');
|
|
10
|
-
const steps = Reflect.get(value, 'steps');
|
|
11
|
-
return (typeof base === 'string' &&
|
|
12
|
-
Array.isArray(steps) &&
|
|
13
|
-
steps.every((entry) => typeof entry === 'object' && entry !== null && typeof Reflect.get(entry, 'step') === 'string' && typeof Reflect.get(entry, 'status') === 'number'));
|
|
14
|
-
};
|
|
15
|
-
const isMissing = (error) => error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
16
|
-
/**
|
|
17
|
-
* Keeps one file per base key in `<dir>/oars/journal/`, named by the key's hash because the key is whatever the developer typed.
|
|
18
|
-
* A write goes to a sibling file and is renamed over the old one; a torn file reads as no record, and the platform's replay
|
|
19
|
-
* makes the rerun correct either way.
|
|
20
|
-
*/
|
|
21
|
-
export const fileJournalStore = (dir) => {
|
|
22
|
-
const folder = join(dir, 'oars', 'journal');
|
|
23
|
-
const fileFor = (base) => join(folder, `${createHash('sha256').update(base).digest('hex')}.json`);
|
|
24
|
-
return {
|
|
25
|
-
read: async (base) => {
|
|
26
|
-
let text;
|
|
27
|
-
try {
|
|
28
|
-
text = await readFile(fileFor(base), 'utf8');
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
if (isMissing(error))
|
|
32
|
-
return undefined;
|
|
33
|
-
throw error;
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
const parsed = JSON.parse(text);
|
|
37
|
-
return isRecord(parsed) && parsed.base === base ? parsed : undefined;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
write: async (record) => {
|
|
44
|
-
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
45
|
-
const file = fileFor(record.base);
|
|
46
|
-
const temp = `${file}.tmp`;
|
|
47
|
-
try {
|
|
48
|
-
await writeFile(temp, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
|
|
49
|
-
await rename(temp, file);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
await rm(temp, { force: true });
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
delete: (base) => rm(fileFor(base), { force: true }),
|
|
57
|
-
};
|
|
58
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"method": "POST",
|
|
4
|
-
"path": "/agents/workloads",
|
|
5
|
-
"body": {
|
|
6
|
-
"name": "Paid API",
|
|
7
|
-
"services": [
|
|
8
|
-
{
|
|
9
|
-
"name": "api",
|
|
10
|
-
"image": "ghcr.io/manyoars/paid-api@sha256:bc18de1da5b0a0fbdc18d6d68aa9da4f89a0ab118a2c18921a1247b09d9d4146",
|
|
11
|
-
"port": 8080,
|
|
12
|
-
"public": true,
|
|
13
|
-
"cpuMillicores": 250,
|
|
14
|
-
"memoryMib": 256
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"method": "POST",
|
|
21
|
-
"path": "/billing/agents/{workloadId}/prices",
|
|
22
|
-
"body": { "priceRef": "calls-100", "displayName": "100 calls", "amountUsd": "5.00", "kind": "CREDIT_PACK", "credits": 100 }
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"method": "POST",
|
|
26
|
-
"path": "/billing/agents/{workloadId}/prices",
|
|
27
|
-
"body": { "priceRef": "calls-1000", "displayName": "1,000 calls", "amountUsd": "40.00", "kind": "CREDIT_PACK", "credits": 1000 }
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"method": "POST",
|
|
31
|
-
"path": "/agents/workloads/{workloadId}/deploy",
|
|
32
|
-
"body": null
|
|
33
|
-
}
|
|
34
|
-
]
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"method": "POST",
|
|
4
|
-
"path": "/agents/workloads",
|
|
5
|
-
"body": {
|
|
6
|
-
"name": "Per-task agent",
|
|
7
|
-
"services": [
|
|
8
|
-
{
|
|
9
|
-
"name": "agent",
|
|
10
|
-
"image": "ghcr.io/manyoars/per-task-agent@sha256:b88bb11504236ad97349f6a4ad820a98c16eef670e9aa2f93c157019406aecd6",
|
|
11
|
-
"port": 8080,
|
|
12
|
-
"public": true,
|
|
13
|
-
"cpuMillicores": 500,
|
|
14
|
-
"memoryMib": 512
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"method": "POST",
|
|
21
|
-
"path": "/billing/agents/{workloadId}/prices",
|
|
22
|
-
"body": { "priceRef": "task-1", "displayName": "1 task", "amountUsd": "9.00", "kind": "CREDIT_PACK", "credits": 1 }
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"method": "POST",
|
|
26
|
-
"path": "/billing/agents/{workloadId}/prices",
|
|
27
|
-
"body": { "priceRef": "tasks-5", "displayName": "5 tasks", "amountUsd": "40.00", "kind": "CREDIT_PACK", "credits": 5 }
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"method": "POST",
|
|
31
|
-
"path": "/agents/workloads/{workloadId}/deploy",
|
|
32
|
-
"body": null
|
|
33
|
-
}
|
|
34
|
-
]
|