@whiteroom-ai/cli 1.1.0 → 2.0.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 +54 -0
- package/contract/catalog.json +197 -0
- package/dist/index.js +12 -15
- package/dist/index.js.map +1 -1
- package/dist/legacy-args.d.ts +4 -0
- package/dist/legacy-args.js +17 -0
- package/dist/legacy-args.js.map +1 -0
- package/dist/practice/adapter.d.ts +59 -0
- package/dist/practice/adapter.js +169 -0
- package/dist/practice/adapter.js.map +1 -0
- package/dist/practice/catalog.d.ts +16 -0
- package/dist/practice/catalog.js +6 -0
- package/dist/practice/catalog.js.map +1 -0
- package/dist/practice/engine.d.ts +50 -0
- package/dist/practice/engine.js +147 -0
- package/dist/practice/engine.js.map +1 -0
- package/dist/practice/index.d.ts +2 -0
- package/dist/practice/index.js +72 -0
- package/dist/practice/index.js.map +1 -0
- package/dist/practice/preflight.d.ts +58 -0
- package/dist/practice/preflight.js +185 -0
- package/dist/practice/preflight.js.map +1 -0
- package/dist/practice/run.d.ts +11 -0
- package/dist/practice/run.js +374 -0
- package/dist/practice/run.js.map +1 -0
- package/dist/practice/text.d.ts +91 -0
- package/dist/practice/text.js +140 -0
- package/dist/practice/text.js.map +1 -0
- package/package.json +10 -3
- package/starters/wr-adapter.mjs +104 -0
- package/starters/wr_adapter.py +136 -0
- package/src/index.ts +0 -144
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** How long the CLI keeps retrying while WhiteRoom can't be reached or asks it to wait. */
|
|
2
|
+
export declare const OFFLINE_MS: number;
|
|
3
|
+
export declare const sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
4
|
+
/** Waits until one more request keeps the last minute at or under PER_MINUTE. */
|
|
5
|
+
export declare function pace(): Promise<void>;
|
|
6
|
+
/** A reply from WhiteRoom; `body` is null when it wasn't JSON. */
|
|
7
|
+
export interface Reply {
|
|
8
|
+
status: number;
|
|
9
|
+
/** The route's JSON, read field by field by the caller. */
|
|
10
|
+
body: any;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A reply that tried to send the CLI somewhere else. Requests that carry the
|
|
14
|
+
* key or the session never follow a redirect (`redirect: "error"`): nothing is
|
|
15
|
+
* resent, and the caller stops with plain copy.
|
|
16
|
+
*/
|
|
17
|
+
export declare const REDIRECTED = 0;
|
|
18
|
+
export declare const isRedirectError: (err: unknown) => boolean;
|
|
19
|
+
/** WhiteRoom couldn't be reached (or kept asking the CLI to wait) for OFFLINE_MS. */
|
|
20
|
+
export declare class Unreachable extends Error {
|
|
21
|
+
}
|
|
22
|
+
/** The session's stop signal fired (Ctrl+C, or WhiteRoom ended the test). */
|
|
23
|
+
export declare class Stopped extends Error {
|
|
24
|
+
}
|
|
25
|
+
export declare class Engine {
|
|
26
|
+
readonly origin: string;
|
|
27
|
+
private readonly offlineMs;
|
|
28
|
+
private readonly timeoutMs;
|
|
29
|
+
session: string | null;
|
|
30
|
+
/** Aborts any request in flight when the session stops. */
|
|
31
|
+
signal: AbortSignal | null;
|
|
32
|
+
/** `offlineMs`: how long to keep retrying; `timeoutMs`: one request, body included (tests shorten both). */
|
|
33
|
+
constructor(origin: string, offlineMs?: number, timeoutMs?: number);
|
|
34
|
+
/** One request, its body read inside the same timeout: a server that stalls mid-body fails like one that never answers. */
|
|
35
|
+
private send;
|
|
36
|
+
/**
|
|
37
|
+
* Ends the run (best effort, one try, not paced): after Ctrl+C, or a crashed adapter
|
|
38
|
+
* (`keepResults`: with a result over the tasks done). Null if WhiteRoom didn't answer.
|
|
39
|
+
*/
|
|
40
|
+
cancel(keepResults?: boolean): Promise<Reply | null>;
|
|
41
|
+
/**
|
|
42
|
+
* POSTs to a route. WhiteRoom's waits (503, a 429 that isn't an answer) are
|
|
43
|
+
* retried as Retry-After says. `retryNetwork`: also retry a network error or
|
|
44
|
+
* another 5xx, for requests that are safe to repeat; otherwise those throw
|
|
45
|
+
* Unreachable. A redirect is returned as status REDIRECTED, never followed.
|
|
46
|
+
*/
|
|
47
|
+
post(path: string, body: unknown, o?: {
|
|
48
|
+
retryNetwork?: boolean;
|
|
49
|
+
}): Promise<Reply>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// The CLI's requests to WhiteRoom's /api/readiness routes (spec §4, §7):
|
|
2
|
+
// plain fetch, paced to 60 a minute (§4 Run), with WhiteRoom's own waits
|
|
3
|
+
// (an IP-limit 429, a 503 while it switches servers) retried.
|
|
4
|
+
import { copy, warn } from "./text.js";
|
|
5
|
+
/** The CLI's own requests stay at or under this many a minute; WhiteRoom allows 120 per IP. */
|
|
6
|
+
const PER_MINUTE = 60;
|
|
7
|
+
/** How long the CLI keeps retrying while WhiteRoom can't be reached or asks it to wait. */
|
|
8
|
+
export const OFFLINE_MS = 5 * 60_000;
|
|
9
|
+
const REQUEST_TIMEOUT_MS = 90_000;
|
|
10
|
+
const sent = [];
|
|
11
|
+
export const sleep = (ms, signal) => new Promise((resolve) => {
|
|
12
|
+
const done = () => {
|
|
13
|
+
clearTimeout(t);
|
|
14
|
+
signal?.removeEventListener("abort", done);
|
|
15
|
+
resolve();
|
|
16
|
+
};
|
|
17
|
+
const t = setTimeout(done, ms);
|
|
18
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
19
|
+
});
|
|
20
|
+
/** Waits until one more request keeps the last minute at or under PER_MINUTE. */
|
|
21
|
+
export async function pace() {
|
|
22
|
+
for (;;) {
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
while (sent.length && sent[0] <= now - 60_000)
|
|
25
|
+
sent.shift();
|
|
26
|
+
if (sent.length < PER_MINUTE) {
|
|
27
|
+
sent.push(now);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await sleep(sent[0] + 60_000 - now);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A reply that tried to send the CLI somewhere else. Requests that carry the
|
|
35
|
+
* key or the session never follow a redirect (`redirect: "error"`): nothing is
|
|
36
|
+
* resent, and the caller stops with plain copy.
|
|
37
|
+
*/
|
|
38
|
+
export const REDIRECTED = 0;
|
|
39
|
+
export const isRedirectError = (err) => /redirect/i.test(String(err?.cause?.message ?? ""));
|
|
40
|
+
/** WhiteRoom couldn't be reached (or kept asking the CLI to wait) for OFFLINE_MS. */
|
|
41
|
+
export class Unreachable extends Error {
|
|
42
|
+
}
|
|
43
|
+
/** The session's stop signal fired (Ctrl+C, or WhiteRoom ended the test). */
|
|
44
|
+
export class Stopped extends Error {
|
|
45
|
+
}
|
|
46
|
+
/** 429s that are answers, not a request to wait. */
|
|
47
|
+
const ANSWERS_429 = new Set(["busy", "tool_limit", "starts_today", "owner_busy"]);
|
|
48
|
+
export class Engine {
|
|
49
|
+
origin;
|
|
50
|
+
offlineMs;
|
|
51
|
+
timeoutMs;
|
|
52
|
+
session = null;
|
|
53
|
+
/** Aborts any request in flight when the session stops. */
|
|
54
|
+
signal = null;
|
|
55
|
+
/** `offlineMs`: how long to keep retrying; `timeoutMs`: one request, body included (tests shorten both). */
|
|
56
|
+
constructor(origin, offlineMs = OFFLINE_MS, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
57
|
+
this.origin = origin;
|
|
58
|
+
this.offlineMs = offlineMs;
|
|
59
|
+
this.timeoutMs = timeoutMs;
|
|
60
|
+
}
|
|
61
|
+
/** One request, its body read inside the same timeout: a server that stalls mid-body fails like one that never answers. */
|
|
62
|
+
async send(path, body, timeoutMs, signal) {
|
|
63
|
+
const ac = new AbortController();
|
|
64
|
+
const abort = () => ac.abort();
|
|
65
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
66
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
67
|
+
try {
|
|
68
|
+
const res = await fetch(this.origin + path, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: { "content-type": "application/json", ...(this.session && { authorization: `Bearer ${this.session}` }) },
|
|
71
|
+
body: JSON.stringify(body),
|
|
72
|
+
redirect: "error",
|
|
73
|
+
signal: ac.signal,
|
|
74
|
+
});
|
|
75
|
+
const text = await res.text();
|
|
76
|
+
let json = null;
|
|
77
|
+
try {
|
|
78
|
+
json = JSON.parse(text);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Not JSON: the caller reads a null body.
|
|
82
|
+
}
|
|
83
|
+
return { status: res.status, retryAfter: res.headers.get("retry-after"), json };
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
signal?.removeEventListener("abort", abort);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Ends the run (best effort, one try, not paced): after Ctrl+C, or a crashed adapter
|
|
92
|
+
* (`keepResults`: with a result over the tasks done). Null if WhiteRoom didn't answer.
|
|
93
|
+
*/
|
|
94
|
+
async cancel(keepResults = false) {
|
|
95
|
+
return this.send("/api/readiness/cancel", keepResults ? { keepResults } : {}, 5_000, null).then((r) => ({ status: r.status, body: r.json }), () => null);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* POSTs to a route. WhiteRoom's waits (503, a 429 that isn't an answer) are
|
|
99
|
+
* retried as Retry-After says. `retryNetwork`: also retry a network error or
|
|
100
|
+
* another 5xx, for requests that are safe to repeat; otherwise those throw
|
|
101
|
+
* Unreachable. A redirect is returned as status REDIRECTED, never followed.
|
|
102
|
+
*/
|
|
103
|
+
async post(path, body, o = {}) {
|
|
104
|
+
const started = Date.now();
|
|
105
|
+
let warned = false;
|
|
106
|
+
for (;;) {
|
|
107
|
+
await pace();
|
|
108
|
+
if (this.signal?.aborted)
|
|
109
|
+
throw new Stopped();
|
|
110
|
+
let res;
|
|
111
|
+
try {
|
|
112
|
+
res = await this.send(path, body, this.timeoutMs, this.signal);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (this.signal?.aborted)
|
|
116
|
+
throw new Stopped();
|
|
117
|
+
if (isRedirectError(err))
|
|
118
|
+
return { status: REDIRECTED, body: { reason: "redirected" } };
|
|
119
|
+
if (!o.retryNetwork || Date.now() - started > this.offlineMs)
|
|
120
|
+
throw new Unreachable();
|
|
121
|
+
if (!warned)
|
|
122
|
+
warn(copy.offline);
|
|
123
|
+
warned = true;
|
|
124
|
+
await sleep(Math.min(5_000, this.offlineMs), this.signal ?? undefined);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const { json } = res;
|
|
128
|
+
if (res.status >= 500 && res.status !== 503) {
|
|
129
|
+
// It may have been handled: only a request that is safe to repeat is sent again.
|
|
130
|
+
if (!o.retryNetwork || Date.now() - started > this.offlineMs)
|
|
131
|
+
throw new Unreachable();
|
|
132
|
+
await sleep(Math.min(5_000, this.offlineMs), this.signal ?? undefined);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const wait = res.status === 503 || (res.status === 429 && !ANSWERS_429.has(json?.reason));
|
|
136
|
+
if (wait) {
|
|
137
|
+
if (Date.now() - started > this.offlineMs)
|
|
138
|
+
throw new Unreachable();
|
|
139
|
+
const after = res.retryAfter;
|
|
140
|
+
await sleep(after === null ? 5_000 : Math.min(Math.max(Number(after) || 0, 1), 60) * 1000, this.signal ?? undefined);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
return { status: res.status, body: json };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/practice/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const PRACTICE_USAGE = "whiteroom practice \u2014 run a WhiteRoom practice test on your agent\n\nUsage:\n whiteroom practice run [--adapter \"<command>\"] [--key-env <VAR>] [--code-stdin] [--url <https url>]\n whiteroom practice adapter --lang js|python\n\n run Starts the practice test from the run code shown in the dashboard.\n Without --adapter, WhiteRoom's sample agent runs the practice tasks.\n adapter Prints a starter adapter to connect your own agent, for example:\n whiteroom practice adapter --lang js > wr-adapter.mjs\n\nOptions:\n --adapter \"<command>\" The command that starts your agent's adapter, e.g. \"node wr-adapter.mjs\"\n --key-env <VAR> The variable your agent's key is in, e.g. ANTHROPIC_API_KEY, when several are set\n --code-stdin Read the run code from stdin instead of asking for it\n --url <https url> Only for testing WhiteRoom itself (a whiteroom.tech address or localhost)\n";
|
|
2
|
+
export declare function practice(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// `whiteroom practice` (hidden alias `readiness`), spec §10 CLI. Usage
|
|
2
|
+
// problems exit 2, never 1 (§8); so does anything unexpected.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
import { practiceRun, STARTERS_DIR } from "./run.js";
|
|
6
|
+
import { warn } from "./text.js";
|
|
7
|
+
export const PRACTICE_USAGE = `whiteroom practice — run a WhiteRoom practice test on your agent
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
whiteroom practice run [--adapter "<command>"] [--key-env <VAR>] [--code-stdin] [--url <https url>]
|
|
11
|
+
whiteroom practice adapter --lang js|python
|
|
12
|
+
|
|
13
|
+
run Starts the practice test from the run code shown in the dashboard.
|
|
14
|
+
Without --adapter, WhiteRoom's sample agent runs the practice tasks.
|
|
15
|
+
adapter Prints a starter adapter to connect your own agent, for example:
|
|
16
|
+
whiteroom practice adapter --lang js > wr-adapter.mjs
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--adapter "<command>" The command that starts your agent's adapter, e.g. "node wr-adapter.mjs"
|
|
20
|
+
--key-env <VAR> The variable your agent's key is in, e.g. ANTHROPIC_API_KEY, when several are set
|
|
21
|
+
--code-stdin Read the run code from stdin instead of asking for it
|
|
22
|
+
--url <https url> Only for testing WhiteRoom itself (a whiteroom.tech address or localhost)
|
|
23
|
+
`;
|
|
24
|
+
const STARTERS = { js: "wr-adapter.mjs", python: "wr_adapter.py" };
|
|
25
|
+
function usage(problem) {
|
|
26
|
+
if (!problem)
|
|
27
|
+
return process.stdout.write(PRACTICE_USAGE), 0;
|
|
28
|
+
warn(`${problem}\nRun whiteroom practice --help to see the commands.`);
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
31
|
+
export async function practice(argv) {
|
|
32
|
+
const [sub, ...rest] = argv;
|
|
33
|
+
if (!sub || sub === "--help" || sub === "-h" || sub === "help")
|
|
34
|
+
return usage();
|
|
35
|
+
let values;
|
|
36
|
+
try {
|
|
37
|
+
({ values } = parseArgs({
|
|
38
|
+
args: rest,
|
|
39
|
+
strict: true,
|
|
40
|
+
allowPositionals: false,
|
|
41
|
+
options: sub === "run"
|
|
42
|
+
? { adapter: { type: "string" }, "key-env": { type: "string" }, "code-stdin": { type: "boolean" }, url: { type: "string" }, help: { type: "boolean", short: "h" } }
|
|
43
|
+
: { lang: { type: "string" }, help: { type: "boolean", short: "h" } },
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return usage(err.message);
|
|
48
|
+
}
|
|
49
|
+
if (values.help)
|
|
50
|
+
return usage();
|
|
51
|
+
try {
|
|
52
|
+
if (sub === "run") {
|
|
53
|
+
if (values.adapter !== undefined && !values.adapter.trim())
|
|
54
|
+
return usage("--adapter needs the command that starts your adapter.");
|
|
55
|
+
return await practiceRun({ adapter: values.adapter, codeStdin: !!values["code-stdin"], url: values.url, keyEnv: values["key-env"] });
|
|
56
|
+
}
|
|
57
|
+
if (sub === "adapter") {
|
|
58
|
+
const file = STARTERS[values.lang];
|
|
59
|
+
if (!file)
|
|
60
|
+
return usage("Choose a starter with --lang js or --lang python.");
|
|
61
|
+
process.stdout.write(readFileSync(new URL(file, STARTERS_DIR), "utf8"));
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
return usage(`Unknown practice command: ${sub}`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// Any adapter still running is killed as the process exits (adapter.ts).
|
|
68
|
+
warn(`Something went wrong in the CLI: ${err?.message ?? String(err)}`);
|
|
69
|
+
return 2;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/practice/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const PRODUCTION = "https://proxy.whiteroom.tech";
|
|
2
|
+
export declare const RUN_CODE: RegExp;
|
|
3
|
+
/**
|
|
4
|
+
* The origin practice commands talk to: production, or `--url` if it is
|
|
5
|
+
* WhiteRoom's own (https on whiteroom.tech) or loopback, since the provider
|
|
6
|
+
* key goes there. Any other https host only with WHITEROOM_PRACTICE_DEV=1
|
|
7
|
+
* (testing WhiteRoom itself). Null if `--url` isn't allowed.
|
|
8
|
+
*/
|
|
9
|
+
export declare function targetOrigin(url: string | undefined, env?: NodeJS.ProcessEnv): string | null;
|
|
10
|
+
/** Splits a command line into words as a POSIX shell would for plain words and quotes (enough to find files). */
|
|
11
|
+
export declare function words(cmd: string): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Why `--adapter` can't start, or null: its program is on PATH (or a file), and
|
|
14
|
+
* script files it names (by extension) exist. Commands with shell syntax or a
|
|
15
|
+
* builtin first word are left to the shell.
|
|
16
|
+
*/
|
|
17
|
+
export declare function adapterProblem(cmd: string, cwd?: string, path?: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* The adapter as reported to WhiteRoom (§7 "What was tested"): only the
|
|
20
|
+
* program's name and its script file's name, e.g. "python3 wr_adapter.py".
|
|
21
|
+
* Never environment assignments, flags, flag values or directories.
|
|
22
|
+
*/
|
|
23
|
+
export declare function adapterLabel(cmd: string): string;
|
|
24
|
+
/** Where a practice test's model calls go, as the claim names it: one of the catalog's providers. */
|
|
25
|
+
export type Provider = string;
|
|
26
|
+
export declare const isProvider: (v: unknown) => v is Provider;
|
|
27
|
+
/** The variable a provider's key is read from, and the adapter gets it in. */
|
|
28
|
+
export declare const keyVarOf: (p: Provider) => string;
|
|
29
|
+
export declare const isKeyVar: (v: string) => boolean;
|
|
30
|
+
/** OpenAI and Azure OpenAI take chat completions (their SDKs want a base URL ending in /v1); the others Anthropic's Messages API. */
|
|
31
|
+
export declare const chatCompletions: (p: Provider) => boolean;
|
|
32
|
+
/** The header a provider's key travels in, as the catalog says: Azure's api-key, a Bearer token, or x-api-key. */
|
|
33
|
+
export declare function keyHeader(p: Provider, key: string): Record<string, string>;
|
|
34
|
+
/** A direct key's provider, by its shape: what an older WhiteRoom that names no provider means. */
|
|
35
|
+
export declare const providerByShape: (key: string) => Provider;
|
|
36
|
+
/** Short-term Bedrock keys can expire during a test; WhiteRoom refuses them without using a try. */
|
|
37
|
+
export declare const isShortTermBedrockKey: (key: string) => boolean;
|
|
38
|
+
export interface KeyChoice {
|
|
39
|
+
/** The variable the key came from; null when it was pasted. */
|
|
40
|
+
from: string | null;
|
|
41
|
+
key: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The key from `keyEnv` (`--key-env`, the variable the dashboard's command
|
|
45
|
+
* names), else from the one provider variable that is set; null to prompt.
|
|
46
|
+
* When more than one is set, the variables: before the claim the CLI can't
|
|
47
|
+
* tell which provider the run uses, and a guess could use a try.
|
|
48
|
+
*/
|
|
49
|
+
export declare function keyFromEnv(env: NodeJS.ProcessEnv, keyEnv?: string): KeyChoice | string[] | null;
|
|
50
|
+
export declare class Interrupted extends Error {
|
|
51
|
+
}
|
|
52
|
+
type Terminal = Pick<NodeJS.ReadStream, "setRawMode" | "setEncoding" | "resume" | "pause" | "on" | "off">;
|
|
53
|
+
/** Reads a line from the terminal without echoing it. Ctrl+C rejects with Interrupted. */
|
|
54
|
+
export declare function hiddenPrompt(question: string, stdin?: Terminal): Promise<string>;
|
|
55
|
+
/** The first line of stdin (`--code-stdin`). */
|
|
56
|
+
export declare function firstLineOfStdin(): Promise<string>;
|
|
57
|
+
export declare const nodeTooOld: (version?: string) => string | null;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Checks that run before anything is sent (spec §10 CLI, §9.3): the target,
|
|
2
|
+
// the adapter command, the provider key and the run code's shape.
|
|
3
|
+
import { accessSync, constants, statSync } from "node:fs";
|
|
4
|
+
import { basename, delimiter, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { CATALOG } from "./catalog.js";
|
|
6
|
+
import { copy } from "./text.js";
|
|
7
|
+
export const PRODUCTION = "https://proxy.whiteroom.tech";
|
|
8
|
+
const LOOPBACK = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
9
|
+
export const RUN_CODE = /^wrrt_[A-Za-z0-9_-]{43}$/;
|
|
10
|
+
/**
|
|
11
|
+
* The origin practice commands talk to: production, or `--url` if it is
|
|
12
|
+
* WhiteRoom's own (https on whiteroom.tech) or loopback, since the provider
|
|
13
|
+
* key goes there. Any other https host only with WHITEROOM_PRACTICE_DEV=1
|
|
14
|
+
* (testing WhiteRoom itself). Null if `--url` isn't allowed.
|
|
15
|
+
*/
|
|
16
|
+
export function targetOrigin(url, env = process.env) {
|
|
17
|
+
if (url === undefined)
|
|
18
|
+
return PRODUCTION;
|
|
19
|
+
let u;
|
|
20
|
+
try {
|
|
21
|
+
u = new URL(url);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
if (u.username || u.password)
|
|
27
|
+
return null;
|
|
28
|
+
if (u.protocol === "http:")
|
|
29
|
+
return LOOPBACK.has(u.hostname) ? u.origin : null;
|
|
30
|
+
if (u.protocol !== "https:")
|
|
31
|
+
return null;
|
|
32
|
+
const ours = u.hostname === "whiteroom.tech" || u.hostname.endsWith(".whiteroom.tech") || LOOPBACK.has(u.hostname);
|
|
33
|
+
return ours || env.WHITEROOM_PRACTICE_DEV === "1" ? u.origin : null;
|
|
34
|
+
}
|
|
35
|
+
/** Splits a command line into words as a POSIX shell would for plain words and quotes (enough to find files). */
|
|
36
|
+
export function words(cmd) {
|
|
37
|
+
const out = [];
|
|
38
|
+
const re = /"((?:[^"\\]|\\.)*)"|'([^']*)'|((?:[^\s"'\\]|\\.)+)|(\s+)/g;
|
|
39
|
+
let cur = null;
|
|
40
|
+
for (const m of cmd.matchAll(re)) {
|
|
41
|
+
if (m[4] !== undefined) {
|
|
42
|
+
if (cur !== null)
|
|
43
|
+
out.push(cur);
|
|
44
|
+
cur = null;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const part = m[1] !== undefined ? m[1].replace(/\\(.)/g, "$1") : m[2] ?? m[3].replace(/\\(.)/g, "$1");
|
|
48
|
+
cur = (cur ?? "") + part;
|
|
49
|
+
}
|
|
50
|
+
if (cur !== null)
|
|
51
|
+
out.push(cur);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
const isFile = (p) => {
|
|
55
|
+
try {
|
|
56
|
+
return statSync(p).isFile();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const isExecutable = (p) => {
|
|
63
|
+
try {
|
|
64
|
+
accessSync(p, constants.X_OK);
|
|
65
|
+
return isFile(p);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const SCRIPT = /\.(m?js|cjs|ts|mts|py|rb|php|pl|sh|jar|exe)$/i;
|
|
72
|
+
/** Shell syntax and builtins this check can't follow: the user's shell runs the command, so it isn't second-guessed. */
|
|
73
|
+
const SHELL_SYNTAX = /[&|;<>$`(){}*?~\n]/;
|
|
74
|
+
const BUILTINS = new Set(["cd", "source", ".", "export", "exec", "eval", "set", "alias", "command", "builtin", "time", "nohup", "env"]);
|
|
75
|
+
/**
|
|
76
|
+
* Why `--adapter` can't start, or null: its program is on PATH (or a file), and
|
|
77
|
+
* script files it names (by extension) exist. Commands with shell syntax or a
|
|
78
|
+
* builtin first word are left to the shell.
|
|
79
|
+
*/
|
|
80
|
+
export function adapterProblem(cmd, cwd = process.cwd(), path = process.env.PATH ?? "") {
|
|
81
|
+
if (!cmd.trim())
|
|
82
|
+
return "the command is empty";
|
|
83
|
+
if (SHELL_SYNTAX.test(cmd))
|
|
84
|
+
return null;
|
|
85
|
+
const w = words(cmd);
|
|
86
|
+
while (w.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(w[0]))
|
|
87
|
+
w.shift(); // FOO=1 node agent.js
|
|
88
|
+
const [program, ...args] = w;
|
|
89
|
+
if (!program || BUILTINS.has(program))
|
|
90
|
+
return null;
|
|
91
|
+
if (program.includes("/")) {
|
|
92
|
+
if (!isExecutable(resolve(cwd, program)))
|
|
93
|
+
return `${program} isn't a file you can run`;
|
|
94
|
+
}
|
|
95
|
+
else if (!path.split(delimiter).some((dir) => dir && isExecutable(join(isAbsolute(dir) ? dir : resolve(cwd, dir), program)))) {
|
|
96
|
+
return `${program} isn't on your PATH`;
|
|
97
|
+
}
|
|
98
|
+
const missing = args.find((a) => !a.startsWith("-") && SCRIPT.test(a) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(a) && !isFile(resolve(cwd, a)));
|
|
99
|
+
return missing ? `${missing} doesn't exist` : null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The adapter as reported to WhiteRoom (§7 "What was tested"): only the
|
|
103
|
+
* program's name and its script file's name, e.g. "python3 wr_adapter.py".
|
|
104
|
+
* Never environment assignments, flags, flag values or directories.
|
|
105
|
+
*/
|
|
106
|
+
export function adapterLabel(cmd) {
|
|
107
|
+
const w = words(cmd).filter((x) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(x));
|
|
108
|
+
const safe = (p) => basename(p).replace(/[^\w.+-]/g, "").slice(0, 64);
|
|
109
|
+
const script = w.slice(1).find((a) => !a.startsWith("-") && SCRIPT.test(a));
|
|
110
|
+
return [w[0] && safe(w[0]), script && safe(script)].filter(Boolean).join(" ") || "adapter";
|
|
111
|
+
}
|
|
112
|
+
export const isProvider = (v) => typeof v === "string" && Object.hasOwn(CATALOG.providers, v);
|
|
113
|
+
/** The variable a provider's key is read from, and the adapter gets it in. */
|
|
114
|
+
export const keyVarOf = (p) => CATALOG.providers[p].keyVar;
|
|
115
|
+
const ALL_KEY_VARS = Object.values(CATALOG.providers).map((p) => p.keyVar);
|
|
116
|
+
export const isKeyVar = (v) => ALL_KEY_VARS.includes(v);
|
|
117
|
+
/** OpenAI and Azure OpenAI take chat completions (their SDKs want a base URL ending in /v1); the others Anthropic's Messages API. */
|
|
118
|
+
export const chatCompletions = (p) => CATALOG.providers[p].api === "chat_completions";
|
|
119
|
+
/** The header a provider's key travels in, as the catalog says: Azure's api-key, a Bearer token, or x-api-key. */
|
|
120
|
+
export function keyHeader(p, key) {
|
|
121
|
+
const h = CATALOG.providers[p].header;
|
|
122
|
+
return h === "bearer" ? { authorization: `Bearer ${key}` } : { [h]: key };
|
|
123
|
+
}
|
|
124
|
+
/** A direct key's provider, by its shape: what an older WhiteRoom that names no provider means. */
|
|
125
|
+
export const providerByShape = (key) => (key.startsWith("sk-ant-") ? "anthropic" : "openai");
|
|
126
|
+
/** Short-term Bedrock keys can expire during a test; WhiteRoom refuses them without using a try. */
|
|
127
|
+
export const isShortTermBedrockKey = (key) => key.startsWith("bedrock-api-key-");
|
|
128
|
+
/**
|
|
129
|
+
* The key from `keyEnv` (`--key-env`, the variable the dashboard's command
|
|
130
|
+
* names), else from the one provider variable that is set; null to prompt.
|
|
131
|
+
* When more than one is set, the variables: before the claim the CLI can't
|
|
132
|
+
* tell which provider the run uses, and a guess could use a try.
|
|
133
|
+
*/
|
|
134
|
+
export function keyFromEnv(env, keyEnv) {
|
|
135
|
+
const set = keyEnv ? [keyEnv].filter((v) => env[v]?.trim()) : ALL_KEY_VARS.filter((v) => env[v]?.trim());
|
|
136
|
+
if (set.length > 1)
|
|
137
|
+
return set;
|
|
138
|
+
return set.length ? { from: set[0], key: env[set[0]].trim() } : null;
|
|
139
|
+
}
|
|
140
|
+
export class Interrupted extends Error {
|
|
141
|
+
}
|
|
142
|
+
/** Reads a line from the terminal without echoing it. Ctrl+C rejects with Interrupted. */
|
|
143
|
+
export function hiddenPrompt(question, stdin = process.stdin) {
|
|
144
|
+
const { stderr } = process;
|
|
145
|
+
stderr.write(question);
|
|
146
|
+
stdin.setRawMode(true);
|
|
147
|
+
stdin.setEncoding("utf8");
|
|
148
|
+
stdin.resume();
|
|
149
|
+
return new Promise((resolvePrompt, reject) => {
|
|
150
|
+
let s = "";
|
|
151
|
+
const done = () => {
|
|
152
|
+
stdin.off("data", onData);
|
|
153
|
+
stdin.setRawMode(false);
|
|
154
|
+
stdin.pause();
|
|
155
|
+
stderr.write("\n");
|
|
156
|
+
};
|
|
157
|
+
const onData = (chunk) => {
|
|
158
|
+
// A terminal with bracketed paste wraps a paste in ESC[200~ … ESC[201~.
|
|
159
|
+
for (const ch of chunk.replace(/\x1b\[20[01]~/g, "")) {
|
|
160
|
+
if (ch === "\r" || ch === "\n")
|
|
161
|
+
return done(), resolvePrompt(s.trim());
|
|
162
|
+
if (ch === "\u0003")
|
|
163
|
+
return done(), reject(new Interrupted());
|
|
164
|
+
if (ch === "\u007f" || ch === "\b")
|
|
165
|
+
s = s.slice(0, -1);
|
|
166
|
+
else if (ch >= " ")
|
|
167
|
+
s += ch;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
stdin.on("data", onData);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** The first line of stdin (`--code-stdin`). */
|
|
174
|
+
export async function firstLineOfStdin() {
|
|
175
|
+
let data = "";
|
|
176
|
+
process.stdin.setEncoding("utf8");
|
|
177
|
+
for await (const chunk of process.stdin) {
|
|
178
|
+
data += chunk;
|
|
179
|
+
if (data.includes("\n") || data.length > 4096)
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
return data.split("\n")[0].trim();
|
|
183
|
+
}
|
|
184
|
+
export const nodeTooOld = (version = process.versions.node) => (Number(version.split(".")[0]) < 20 ? copy.node(version) : null);
|
|
185
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/practice/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The final answer WhiteRoom takes: 32 Ki characters, inside its 64 KiB body limit. */
|
|
2
|
+
export declare function finalText(s: string): string;
|
|
3
|
+
export declare const STARTERS_DIR: URL;
|
|
4
|
+
export interface RunArgs {
|
|
5
|
+
adapter?: string;
|
|
6
|
+
codeStdin: boolean;
|
|
7
|
+
url?: string;
|
|
8
|
+
/** --key-env: the variable the run's key is read from, when several are set. */
|
|
9
|
+
keyEnv?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function practiceRun(a: RunArgs): Promise<number>;
|