humanish 0.48.0 → 0.50.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 +65 -6
- package/dist/actor-contract.d.ts +14 -0
- package/dist/actor-contract.js.map +1 -1
- package/dist/computer-use-actor.d.ts +3 -7
- package/dist/computer-use.d.ts +4 -7
- package/dist/computer-use.js +39 -3
- package/dist/computer-use.js.map +1 -1
- package/dist/concurrent-shared-world-lab.d.ts +5 -0
- package/dist/concurrent-shared-world-lab.js +30 -1
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +7 -0
- package/dist/cua-actor-lab.js +50 -9
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/e2b-terminal-lab.d.ts +7 -0
- package/dist/e2b-terminal-lab.js +54 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/init-templates.js +7 -2
- package/dist/init-templates.js.map +1 -1
- package/dist/key-resolution.d.ts +67 -0
- package/dist/key-resolution.js +373 -0
- package/dist/key-resolution.js.map +1 -0
- package/dist/lab-engine.d.ts +11 -0
- package/dist/lab-engine.js +18 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/openai-responses-cu.d.ts +1 -1
- package/dist/openai-responses-cu.js +8 -2
- package/dist/openai-responses-cu.js.map +1 -1
- package/dist/oss-lab.d.ts +3 -0
- package/dist/oss-lab.js.map +1 -1
- package/dist/oss-meta-lab.d.ts +3 -0
- package/dist/oss-meta-lab.js +13 -0
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/pricing.d.ts +20 -0
- package/dist/pricing.js +109 -17
- package/dist/pricing.js.map +1 -1
- package/dist/program.d.ts +18 -1
- package/dist/program.js +291 -17
- package/dist/program.js.map +1 -1
- package/dist/run-detail.d.ts +59 -0
- package/dist/run-detail.js +108 -0
- package/dist/run-detail.js.map +1 -0
- package/dist/run-index.d.ts +71 -0
- package/dist/run-index.js +206 -0
- package/dist/run-index.js.map +1 -0
- package/dist/run-paths.js +16 -1
- package/dist/run-paths.js.map +1 -1
- package/dist/run-projection.d.ts +182 -0
- package/dist/run-projection.js +349 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/run-status.d.ts +139 -0
- package/dist/run-status.js +218 -0
- package/dist/run-status.js.map +1 -0
- package/dist/run.d.ts +15 -0
- package/dist/run.js +115 -6
- package/dist/run.js.map +1 -1
- package/dist/scripted-browser-lab.d.ts +5 -0
- package/dist/scripted-browser-lab.js +29 -1
- package/dist/scripted-browser-lab.js.map +1 -1
- package/dist/shared-world-lab.d.ts +5 -0
- package/dist/shared-world-lab.js +29 -1
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/tui-app.js +402 -0
- package/dist/tui-contract.d.ts +73 -0
- package/dist/tui-contract.js +32 -0
- package/dist/tui-contract.js.map +1 -0
- package/dist/tui-launch.d.ts +48 -0
- package/dist/tui-launch.js +159 -0
- package/dist/tui-launch.js.map +1 -0
- package/docs/contracts/run-bundle.md +8 -0
- package/docs/contracts/schemas.md +85 -7
- package/docs/goals/current.md +16 -7
- package/docs/ramp/README.md +1 -1
- package/package.json +8 -5
- package/skills/humanish/SKILL.md +19 -0
package/dist/program.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { formatOrientationHuman, readOrientation } from "./orientation.js";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { Command, Option } from "commander";
|
|
7
7
|
import { startCodexAppServerUi } from "./codex-app-server-ui.js";
|
|
8
8
|
import { loadEnvFile } from "./env-file.js";
|
|
9
|
+
import { discoverProviderKeys, listUserKeys, resolveKeyName, setUserKey, unsetUserKey, userKeyStorePath } from "./key-resolution.js";
|
|
9
10
|
import { redactText } from "./redaction.js";
|
|
10
11
|
import { draftFeedback, listFeedback, renderIssueMarkdown, renderIssueUrl, verifyFeedback } from "./feedback.js";
|
|
11
12
|
import { runInit } from "./init.js";
|
|
@@ -23,6 +24,10 @@ import { DEFAULT_OSS_REPOS, runOssLab } from "./oss-lab.js";
|
|
|
23
24
|
import { cleanupOssMetaLabSandboxes, cleanupStaleOssMetaLabSandboxes, runOssMetaLab, startOssMetaLabLiveRefresh } from "./oss-meta-lab.js";
|
|
24
25
|
import { cleanupRun, doctor, listRuns, readReview, runDryRun, verifyRun } from "./run.js";
|
|
25
26
|
import { reclaimRunSandboxes } from "./reclaim.js";
|
|
27
|
+
import { RunIndexCache, readRunIndex } from "./run-index.js";
|
|
28
|
+
import { readRunDetail } from "./run-detail.js";
|
|
29
|
+
import { launchRun, readLaunchLogTail } from "./tui-launch.js";
|
|
30
|
+
import { TUI_MIN_NODE_MAJOR, nodeSupportsTui, tuiBundleUrl } from "./tui-contract.js";
|
|
26
31
|
import { runCommsCatchHost } from "./comms-catch-host.js";
|
|
27
32
|
import { DEFAULT_SANDBOX_CATCH_PORT } from "./comms-sandbox-catch.js";
|
|
28
33
|
export const CLI_RESPONSE_SCHEMA = "humanish.cli-response.v1";
|
|
@@ -136,6 +141,8 @@ function reportUnexpectedActionError(command, io, error) {
|
|
|
136
141
|
}
|
|
137
142
|
export function createProgram(io = {}) {
|
|
138
143
|
const cliIo = { ...defaultIo, ...io };
|
|
144
|
+
keyDiscoveryFn = io.keyDiscovery ?? discoverProviderKeys;
|
|
145
|
+
tuiRuntime = { ...defaultTuiRuntime, ...io.tuiRuntime };
|
|
139
146
|
const program = new HumanishCommand(undefined, cliIo);
|
|
140
147
|
// Bare `humanish` orients instead of printing sixteen subcommands (#367). --help is untouched;
|
|
141
148
|
// this is only what happens when no command was chosen at all.
|
|
@@ -177,6 +184,8 @@ export function createProgram(io = {}) {
|
|
|
177
184
|
].join("\n"));
|
|
178
185
|
registerInitCommand(program, cliIo);
|
|
179
186
|
registerDoctorCommand(program, cliIo);
|
|
187
|
+
registerTuiCommand(program, cliIo);
|
|
188
|
+
registerKeysCommand(program, cliIo);
|
|
180
189
|
registerRunCommand(program, cliIo);
|
|
181
190
|
registerVerifyCommand(program, cliIo);
|
|
182
191
|
registerCleanupCommand(program, cliIo);
|
|
@@ -234,6 +243,244 @@ function registerDoctorCommand(parent, io) {
|
|
|
234
243
|
io.setExitCode(result.ok ? 0 : 2);
|
|
235
244
|
});
|
|
236
245
|
}
|
|
246
|
+
const defaultTuiRuntime = {
|
|
247
|
+
stdin: process.stdin,
|
|
248
|
+
stdout: process.stdout,
|
|
249
|
+
nodeVersion: process.version,
|
|
250
|
+
loadTui: async (bundle) => {
|
|
251
|
+
if (!existsSync(bundle))
|
|
252
|
+
return null;
|
|
253
|
+
return (await import(bundle.href));
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
let tuiRuntime = defaultTuiRuntime;
|
|
257
|
+
const TUI_RESULT_SCHEMA = "humanish.tui-result.v1";
|
|
258
|
+
function refuseTui(command, io, refusal) {
|
|
259
|
+
if (wantsJson(command)) {
|
|
260
|
+
io.writeOut(`${JSON.stringify(refusal, null, 2)}\n`);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
io.writeErr(`${refusal.error.message}\n`);
|
|
264
|
+
}
|
|
265
|
+
markInvocationEnvelopeWritten(command);
|
|
266
|
+
io.setExitCode(2);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The stakeholder surface (#455). Every other command is written so an agent can drive it; this one
|
|
270
|
+
* is the opposite — it takes the screen and waits for a person.
|
|
271
|
+
*
|
|
272
|
+
* That inversion is why it refuses rather than degrades. An agent that runs `humanish tui` with a
|
|
273
|
+
* piped stdout has asked for something that cannot exist, and the useful answer is a structured
|
|
274
|
+
* error naming the command that WOULD have answered the question. A TUI that quietly rendered
|
|
275
|
+
* frames into a pipe would poison a transcript with escape codes and look like a hang.
|
|
276
|
+
*/
|
|
277
|
+
function registerTuiCommand(parent, io) {
|
|
278
|
+
parent
|
|
279
|
+
.command("tui")
|
|
280
|
+
.description("Open the interactive terminal surface for browsing labs and runs (humans only).")
|
|
281
|
+
.summary("Open the interactive terminal surface.")
|
|
282
|
+
.option("--cwd <path>", "Target project directory.", ".")
|
|
283
|
+
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
284
|
+
.action(async (options, command) => {
|
|
285
|
+
const { stdin, stdout } = tuiRuntime;
|
|
286
|
+
if (stdin.isTTY !== true || stdout.isTTY !== true) {
|
|
287
|
+
refuseTui(command, io, {
|
|
288
|
+
schema: TUI_RESULT_SCHEMA,
|
|
289
|
+
ok: false,
|
|
290
|
+
error: {
|
|
291
|
+
code: "HUMANISH_TUI_REQUIRES_TTY",
|
|
292
|
+
message: "humanish tui needs an interactive terminal. For scripted or agent use, `humanish runs --json` lists the same runs and `humanish lab run --json` starts one."
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (!nodeSupportsTui(tuiRuntime.nodeVersion)) {
|
|
298
|
+
refuseTui(command, io, {
|
|
299
|
+
schema: TUI_RESULT_SCHEMA,
|
|
300
|
+
ok: false,
|
|
301
|
+
error: {
|
|
302
|
+
code: "HUMANISH_TUI_UNSUPPORTED_NODE",
|
|
303
|
+
message: `humanish tui needs Node ${TUI_MIN_NODE_MAJOR} or newer (this is ${tuiRuntime.nodeVersion}). Every other humanish command still works on this runtime.`
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// The Ink app ships as a pre-built bundle beside the compiled CLI and is loaded ONLY here, so
|
|
309
|
+
// no agent-facing command pays its parse cost.
|
|
310
|
+
const bundle = tuiBundleUrl(import.meta.url);
|
|
311
|
+
const runIndexCache = new RunIndexCache();
|
|
312
|
+
const loaded = await tuiRuntime.loadTui(bundle);
|
|
313
|
+
if (loaded === null) {
|
|
314
|
+
refuseTui(command, io, {
|
|
315
|
+
schema: TUI_RESULT_SCHEMA,
|
|
316
|
+
ok: false,
|
|
317
|
+
error: {
|
|
318
|
+
code: "HUMANISH_TUI_BUNDLE_MISSING",
|
|
319
|
+
message: `The terminal surface bundle is missing at ${bundle.pathname}. In a checkout, run \`pnpm build\`; in an install, this package is incomplete — please report it.`
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const exitCode = await loaded.startTui({
|
|
325
|
+
cwd: resolve(options.cwd),
|
|
326
|
+
version: { cli: CLI_VERSION },
|
|
327
|
+
capabilities: {
|
|
328
|
+
// One cache for the life of the surface: it refreshes on a cadence, and re-walking every
|
|
329
|
+
// run tree each tick is the cost this index exists to avoid.
|
|
330
|
+
readRunIndex: (target, readOptions) => readRunIndex(target, { ...readOptions, cache: runIndexCache }),
|
|
331
|
+
listLabs: listLabManifests,
|
|
332
|
+
startRun: launchRun,
|
|
333
|
+
readLaunchLog: readLaunchLogTail,
|
|
334
|
+
readRunDetail
|
|
335
|
+
},
|
|
336
|
+
stdin,
|
|
337
|
+
stdout
|
|
338
|
+
});
|
|
339
|
+
// The surface owned the screen; it has already told the operator whatever there was to say.
|
|
340
|
+
markInvocationEnvelopeWritten(command);
|
|
341
|
+
io.setExitCode(exitCode);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
// The discovery fn the env seam calls; injectable via createProgram for hermetic CLI tests.
|
|
345
|
+
let keyDiscoveryFn = discoverProviderKeys;
|
|
346
|
+
const KEYS_RESULT_SCHEMA = "humanish.keys-result.v1";
|
|
347
|
+
function formatKeysHuman(result) {
|
|
348
|
+
const lines = [`humanish keys ${result.ok ? "ok" : "failed"}`, `store: ${result.store}`, result.message];
|
|
349
|
+
if (result.action === "list" && result.names.length > 0) {
|
|
350
|
+
for (const name of result.names)
|
|
351
|
+
lines.push(`- ${name}`);
|
|
352
|
+
}
|
|
353
|
+
return `${lines.join("\n")}\n`;
|
|
354
|
+
}
|
|
355
|
+
/** Read one secret line: from a piped stdin when --stdin, else a hidden TTY prompt. */
|
|
356
|
+
async function readSecretValue(useStdin, promptLabel, io) {
|
|
357
|
+
if (useStdin || !process.stdin.isTTY) {
|
|
358
|
+
const chunks = [];
|
|
359
|
+
for await (const chunk of process.stdin)
|
|
360
|
+
chunks.push(chunk);
|
|
361
|
+
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
362
|
+
return text.length > 0 ? text : null;
|
|
363
|
+
}
|
|
364
|
+
io.writeErr(`${promptLabel} (input hidden): `);
|
|
365
|
+
return await new Promise((resolve) => {
|
|
366
|
+
const stdin = process.stdin;
|
|
367
|
+
let value = "";
|
|
368
|
+
stdin.setRawMode?.(true);
|
|
369
|
+
stdin.resume();
|
|
370
|
+
stdin.setEncoding("utf8");
|
|
371
|
+
const onData = (key) => {
|
|
372
|
+
if (key === "\u0003") {
|
|
373
|
+
cleanup();
|
|
374
|
+
resolve(null);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (key === "\r" || key === "\n") {
|
|
378
|
+
cleanup();
|
|
379
|
+
io.writeErr("\n");
|
|
380
|
+
resolve(value.trim().length > 0 ? value.trim() : null);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (key === "\u007f" || key === "\b") {
|
|
384
|
+
value = value.slice(0, -1);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
value += key;
|
|
388
|
+
};
|
|
389
|
+
const cleanup = () => {
|
|
390
|
+
stdin.off("data", onData);
|
|
391
|
+
stdin.setRawMode?.(false);
|
|
392
|
+
stdin.pause();
|
|
393
|
+
};
|
|
394
|
+
stdin.on("data", onData);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function registerKeysCommand(parent, io) {
|
|
398
|
+
const keys = parent
|
|
399
|
+
.command("keys")
|
|
400
|
+
.description("Manage the humanish user-level key store used by provider-key discovery (#436).")
|
|
401
|
+
.summary("Manage the user-level provider key store.");
|
|
402
|
+
keys
|
|
403
|
+
.command("set")
|
|
404
|
+
.argument("<vendor-or-name>", "A vendor alias (openai, e2b, anthropic, github) or a raw ENV_NAME.")
|
|
405
|
+
.description("Store one provider key in the user store (0600), prompted with hidden input.")
|
|
406
|
+
.option("--stdin", "Read the value from stdin instead of prompting (for agents/pipes).")
|
|
407
|
+
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
408
|
+
.action(async (vendorOrName, options, command) => {
|
|
409
|
+
const name = resolveKeyName(vendorOrName);
|
|
410
|
+
const storePath = userKeyStorePath(process.env);
|
|
411
|
+
if (name === null) {
|
|
412
|
+
const result = {
|
|
413
|
+
schema: KEYS_RESULT_SCHEMA, ok: false, action: "set", store: storePath, names: [],
|
|
414
|
+
message: `Not a vendor alias or valid env name: ${vendorOrName}. Vendors: openai, e2b, anthropic, github.`
|
|
415
|
+
};
|
|
416
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
417
|
+
io.setExitCode(2);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const value = await readSecretValue(options.stdin === true, `Value for ${name}`, io);
|
|
421
|
+
if (value === null) {
|
|
422
|
+
const result = {
|
|
423
|
+
schema: KEYS_RESULT_SCHEMA, ok: false, action: "set", store: storePath, names: [name],
|
|
424
|
+
message: "No value provided; nothing written."
|
|
425
|
+
};
|
|
426
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
427
|
+
io.setExitCode(2);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
try {
|
|
431
|
+
const written = setUserKey(name, value, process.env);
|
|
432
|
+
const result = {
|
|
433
|
+
schema: KEYS_RESULT_SCHEMA, ok: true, action: "set", store: written.path, names: [name],
|
|
434
|
+
message: `${name} stored (0600). Live commands resolve it automatically; remove with "humanish keys unset ${name}".`
|
|
435
|
+
};
|
|
436
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
437
|
+
io.setExitCode(0);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const result = {
|
|
441
|
+
schema: KEYS_RESULT_SCHEMA, ok: false, action: "set", store: storePath, names: [name],
|
|
442
|
+
message: error instanceof Error ? error.message : "Failed to write the key store."
|
|
443
|
+
};
|
|
444
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
445
|
+
io.setExitCode(2);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
keys
|
|
449
|
+
.command("unset")
|
|
450
|
+
.argument("<vendor-or-name>", "A vendor alias or raw ENV_NAME to remove from the store.")
|
|
451
|
+
.description("Remove one key from the user store.")
|
|
452
|
+
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
453
|
+
.action(async (vendorOrName, _options, command) => {
|
|
454
|
+
const name = resolveKeyName(vendorOrName);
|
|
455
|
+
const storePath = userKeyStorePath(process.env);
|
|
456
|
+
const had = name !== null && unsetUserKey(name, process.env);
|
|
457
|
+
const result = {
|
|
458
|
+
schema: KEYS_RESULT_SCHEMA, ok: name !== null, action: "unset", store: storePath,
|
|
459
|
+
names: name === null ? [] : [name],
|
|
460
|
+
message: name === null
|
|
461
|
+
? `Not a vendor alias or valid env name: ${vendorOrName}.`
|
|
462
|
+
: had ? `${name} removed from the store.` : `${name} was not in the store; nothing changed.`
|
|
463
|
+
};
|
|
464
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
465
|
+
io.setExitCode(name === null ? 2 : 0);
|
|
466
|
+
});
|
|
467
|
+
keys
|
|
468
|
+
.command("list")
|
|
469
|
+
.description("List the NAMES stored in the user store. Values are never printed.")
|
|
470
|
+
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
471
|
+
.action(async (_options, command) => {
|
|
472
|
+
const storePath = userKeyStorePath(process.env);
|
|
473
|
+
const names = listUserKeys(process.env);
|
|
474
|
+
const result = {
|
|
475
|
+
schema: KEYS_RESULT_SCHEMA, ok: true, action: "list", store: storePath, names,
|
|
476
|
+
message: names.length === 0
|
|
477
|
+
? "The store is empty. Add a key with `humanish keys set <vendor>`."
|
|
478
|
+
: `${names.length} key name(s) stored. Values are never printed.`
|
|
479
|
+
};
|
|
480
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
481
|
+
io.setExitCode(0);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
237
484
|
function registerRunCommand(parent, io) {
|
|
238
485
|
parent
|
|
239
486
|
.command("run")
|
|
@@ -1612,6 +1859,11 @@ async function runLabCommand(args) {
|
|
|
1612
1859
|
args.io.writeErr(`warning: ${warning}\n`);
|
|
1613
1860
|
}
|
|
1614
1861
|
const config = resolved.config;
|
|
1862
|
+
// The run's identity (#455): which manifest, where it lives, and whether it is committed, a
|
|
1863
|
+
// local overlay, or an explicit path. Resolved once here — the only place that knows all three —
|
|
1864
|
+
// and carried into the run's status record and bundle so the filesystem can answer "which lab
|
|
1865
|
+
// produced this run" without the old `persona.source = "lab:<id>"` string convention.
|
|
1866
|
+
const lab = { id: config.id, path: resolved.path, origin: resolved.origin };
|
|
1615
1867
|
const backend = selectLabBackend(config);
|
|
1616
1868
|
if (backend !== "cua" && labRerunFlagsRequested(args.options)) {
|
|
1617
1869
|
writeUnsupportedRerunFlagsResult(args, backend);
|
|
@@ -1663,28 +1915,28 @@ async function runLabCommand(args) {
|
|
|
1663
1915
|
}
|
|
1664
1916
|
switch (backend) {
|
|
1665
1917
|
case "synthetic":
|
|
1666
|
-
await runSyntheticBackend({ ...args, config });
|
|
1918
|
+
await runSyntheticBackend({ ...args, config, labProvenance: lab });
|
|
1667
1919
|
return;
|
|
1668
1920
|
case "meta":
|
|
1669
|
-
await runMetaBackend({ ...args, config });
|
|
1921
|
+
await runMetaBackend({ ...args, config, labProvenance: lab });
|
|
1670
1922
|
return;
|
|
1671
1923
|
case "smoke":
|
|
1672
|
-
await runSmokeBackend({ ...args, config });
|
|
1924
|
+
await runSmokeBackend({ ...args, config, labProvenance: lab });
|
|
1673
1925
|
return;
|
|
1674
1926
|
case "cua":
|
|
1675
|
-
await runCuaBackend({ ...args, config, ...(scorer ? { scorer } : {}) });
|
|
1927
|
+
await runCuaBackend({ ...args, config, labProvenance: lab, ...(scorer ? { scorer } : {}) });
|
|
1676
1928
|
return;
|
|
1677
1929
|
case "scripted":
|
|
1678
|
-
await runScriptedBackend({ ...args, config });
|
|
1930
|
+
await runScriptedBackend({ ...args, config, labProvenance: lab });
|
|
1679
1931
|
return;
|
|
1680
1932
|
case "terminal":
|
|
1681
|
-
await runTerminalBackend({ ...args, config, ...(scorer ? { scorer } : {}) });
|
|
1933
|
+
await runTerminalBackend({ ...args, config, labProvenance: lab, ...(scorer ? { scorer } : {}) });
|
|
1682
1934
|
return;
|
|
1683
1935
|
case "shared-world":
|
|
1684
|
-
await runSharedWorldBackend({ ...args, config, ...(scorer ? { scorer } : {}) });
|
|
1936
|
+
await runSharedWorldBackend({ ...args, config, labProvenance: lab, ...(scorer ? { scorer } : {}) });
|
|
1685
1937
|
return;
|
|
1686
1938
|
case "concurrent-shared-world":
|
|
1687
|
-
await runConcurrentSharedWorldBackend({ ...args, config, ...(scorer ? { scorer } : {}) });
|
|
1939
|
+
await runConcurrentSharedWorldBackend({ ...args, config, labProvenance: lab, ...(scorer ? { scorer } : {}) });
|
|
1688
1940
|
return;
|
|
1689
1941
|
default:
|
|
1690
1942
|
// Compile-time exhaustiveness: a future backend must be handled here, not silently no-op.
|
|
@@ -1727,6 +1979,7 @@ async function runSyntheticBackend(args) {
|
|
|
1727
1979
|
}
|
|
1728
1980
|
const outcome = await runLab(args.config, {
|
|
1729
1981
|
cwd: args.options.cwd,
|
|
1982
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
1730
1983
|
count: simCount,
|
|
1731
1984
|
...(args.options.dryRun === undefined ? {} : { dryRun: args.options.dryRun }),
|
|
1732
1985
|
...(args.options.runId === undefined ? {} : { runId: args.options.runId })
|
|
@@ -1834,6 +2087,7 @@ async function runCuaBackend(args) {
|
|
|
1834
2087
|
try {
|
|
1835
2088
|
outcome = await runLab(args.config, {
|
|
1836
2089
|
cwd: args.options.cwd,
|
|
2090
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
1837
2091
|
// Watch mode opens the served Observer below (or prints the phone target under --expose)
|
|
1838
2092
|
// instead of the static render — preserved byte-for-byte from the pre-0.18 open policy so a
|
|
1839
2093
|
// non-follow watch (dry-run/--json) does not double-open. Run mode keeps the static open.
|
|
@@ -1969,6 +2223,7 @@ async function runScriptedBackend(args) {
|
|
|
1969
2223
|
});
|
|
1970
2224
|
const outcome = await runLab(args.config, {
|
|
1971
2225
|
cwd: args.options.cwd,
|
|
2226
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
1972
2227
|
// Watch mode opens the served Observer below instead of the static render.
|
|
1973
2228
|
open: args.mode === "watch" ? false : shouldOpen,
|
|
1974
2229
|
...(args.options.dryRun === undefined ? {} : { dryRun: args.options.dryRun }),
|
|
@@ -2005,6 +2260,7 @@ async function runTerminalBackend(args) {
|
|
|
2005
2260
|
});
|
|
2006
2261
|
const outcome = await runLab(args.config, {
|
|
2007
2262
|
cwd: args.options.cwd,
|
|
2263
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2008
2264
|
open: args.mode === "watch" ? false : shouldOpen,
|
|
2009
2265
|
...(args.options.dryRun === undefined ? {} : { dryRun: args.options.dryRun }),
|
|
2010
2266
|
...(args.options.runId === undefined ? {} : { runId: args.options.runId }),
|
|
@@ -2038,6 +2294,7 @@ async function runSharedWorldBackend(args) {
|
|
|
2038
2294
|
});
|
|
2039
2295
|
const outcome = await runLab(args.config, {
|
|
2040
2296
|
cwd: args.options.cwd,
|
|
2297
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2041
2298
|
open: args.mode === "watch" ? false : shouldOpen,
|
|
2042
2299
|
...(args.options.dryRun === undefined ? {} : { dryRun: args.options.dryRun }),
|
|
2043
2300
|
...(args.options.runId === undefined ? {} : { runId: args.options.runId }),
|
|
@@ -2119,6 +2376,7 @@ async function runConcurrentSharedWorldBackend(args) {
|
|
|
2119
2376
|
try {
|
|
2120
2377
|
outcome = await runLab(args.config, {
|
|
2121
2378
|
cwd: args.options.cwd,
|
|
2379
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2122
2380
|
open: wantsFollow ? false : shouldOpen,
|
|
2123
2381
|
dryRun,
|
|
2124
2382
|
...(wantsFollow
|
|
@@ -2280,6 +2538,7 @@ async function runSmokeBackend(args) {
|
|
|
2280
2538
|
const repos = labReposOverride(args.options);
|
|
2281
2539
|
const outcome = await runLab(args.config, {
|
|
2282
2540
|
cwd: args.options.cwd,
|
|
2541
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2283
2542
|
count: limit,
|
|
2284
2543
|
...(repos === undefined ? {} : { repos }),
|
|
2285
2544
|
...(args.options.keep === undefined ? {} : { keep: args.options.keep }),
|
|
@@ -2336,6 +2595,8 @@ async function runMetaBackend(args) {
|
|
|
2336
2595
|
try {
|
|
2337
2596
|
const outcome = await runLab(args.config, {
|
|
2338
2597
|
cwd: args.options.cwd,
|
|
2598
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2599
|
+
...(args.labProvenance === undefined ? {} : { lab: args.labProvenance }),
|
|
2339
2600
|
...(wantsFollow ? { completionTimeoutMs: 0 } : {}),
|
|
2340
2601
|
...(codexAppServer === undefined ? {} : { codexAppServer }),
|
|
2341
2602
|
...(wantsFollow
|
|
@@ -2440,16 +2701,29 @@ async function renderAndMaybeFollowObserver(args) {
|
|
|
2440
2701
|
}
|
|
2441
2702
|
}
|
|
2442
2703
|
async function applyEnvFileOption(args) {
|
|
2443
|
-
if (
|
|
2444
|
-
|
|
2704
|
+
if (args.envFile) {
|
|
2705
|
+
const result = await loadEnvFile(args.cwd, args.envFile);
|
|
2706
|
+
if (!result.ok) {
|
|
2707
|
+
writeResult(args.command, args.io, result, formatEnvFileHuman);
|
|
2708
|
+
args.io.setExitCode(2);
|
|
2709
|
+
return false;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
// Provider-key discovery (#436): fill still-missing keys from the documented project
|
|
2713
|
+
// overlay, the owning vendors' native stores, and the humanish user store — fill-only
|
|
2714
|
+
// (an explicit --env-file or process env always wins), each fill announced by name and
|
|
2715
|
+
// source on stderr, never by value. HUMANISH_STRICT_KEYS=1 restores env-only behavior.
|
|
2716
|
+
try {
|
|
2717
|
+
await keyDiscoveryFn({
|
|
2718
|
+
cwd: args.cwd,
|
|
2719
|
+
env: process.env,
|
|
2720
|
+
announce: (line) => args.io.writeErr(`${line}\n`)
|
|
2721
|
+
});
|
|
2445
2722
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
return true;
|
|
2723
|
+
catch {
|
|
2724
|
+
// Discovery must never break a command; a rung that fails to read is a miss, not an error.
|
|
2449
2725
|
}
|
|
2450
|
-
|
|
2451
|
-
args.io.setExitCode(2);
|
|
2452
|
-
return false;
|
|
2726
|
+
return true;
|
|
2453
2727
|
}
|
|
2454
2728
|
function labReposOverride(options) {
|
|
2455
2729
|
const override = [
|