@f-o-h/cli 0.1.16 → 0.1.18

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.
Files changed (3) hide show
  1. package/README.md +25 -25
  2. package/dist/foh.js +144 -19
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@ AI-operator provisioning CLI for Front Of House.
4
4
 
5
5
  Public mirror: https://github.com/iiko38/front-of-house-cli
6
6
 
7
- Current published baseline: `@f-o-h/cli@0.1.16`
7
+ Current published baseline: `@f-o-h/cli@0.1.18`
8
8
 
9
9
  This mirror is a generated release artifact. The private product monorepo is not
10
10
  published here, and no open-source license is granted unless stated separately.
@@ -144,30 +144,30 @@ After dry-run review, one controlled Codex run can be launched explicitly with
144
144
  `run.json` even on timeout or non-zero exit:
145
145
 
146
146
  ```bash
147
- foh eval external-agent execute \
148
- --runner codex \
149
- --batch test-results/external-agent-runs/<one-model-batch>/batch.json \
150
- --timeout-minutes 30 \
151
- --live \
152
- --json
153
- ```
154
-
155
- For Linux eval hosts where Codex's default Bubblewrap sandbox cannot configure
156
- loopback networking, keep sandboxing enabled and select the legacy Landlock
157
- backend explicitly:
158
-
159
- ```bash
160
- foh eval external-agent execute \
161
- --runner codex \
162
- --batch test-results/external-agent-runs/<one-model-batch>/batch.json \
163
- --codex-sandbox-backend legacy-landlock \
164
- --codex-network-access \
165
- --timeout-minutes 30 \
166
- --live \
167
- --json
168
- ```
169
-
170
- ## Local Scenario Suites
147
+ foh eval external-agent execute \
148
+ --runner codex \
149
+ --batch test-results/external-agent-runs/<one-model-batch>/batch.json \
150
+ --timeout-minutes 30 \
151
+ --live \
152
+ --json
153
+ ```
154
+
155
+ For Linux eval hosts where Codex's default Bubblewrap sandbox cannot configure
156
+ loopback networking, keep sandboxing enabled and select the legacy Landlock
157
+ backend explicitly:
158
+
159
+ ```bash
160
+ foh eval external-agent execute \
161
+ --runner codex \
162
+ --batch test-results/external-agent-runs/<one-model-batch>/batch.json \
163
+ --codex-sandbox-backend legacy-landlock \
164
+ --codex-network-access \
165
+ --timeout-minutes 30 \
166
+ --live \
167
+ --json
168
+ ```
169
+
170
+ ## Local Scenario Suites
171
171
 
172
172
  `foh test run --suite <file>` runs deterministic widget-runtime checks for a
173
173
  specific agent. The suite format supports reply text checks plus structured
package/dist/foh.js CHANGED
@@ -14637,9 +14637,9 @@ function registerAgent(program3) {
14637
14637
  process.stdout.write(yaml);
14638
14638
  return;
14639
14639
  }
14640
- const { writeFileSync: writeFileSync9 } = await import("fs");
14640
+ const { writeFileSync: writeFileSync10 } = await import("fs");
14641
14641
  const outputPath = opts.output ?? "tenant.yaml";
14642
- writeFileSync9(
14642
+ writeFileSync10(
14643
14643
  outputPath,
14644
14644
  `# tenant.yaml - Front Of House agent manifest
14645
14645
  # Edit this file and run: foh plan tenant.yaml
@@ -16074,11 +16074,11 @@ function registerVoice(program3) {
16074
16074
  }
16075
16075
  const outputPath = String(opts.out || `foh-voice-preview-${provider}-${voiceId}.mp3`).trim();
16076
16076
  const audio = Buffer.from(await res.arrayBuffer());
16077
- const { mkdirSync: mkdirSync7, writeFileSync: writeFileSync9 } = await import("fs");
16077
+ const { mkdirSync: mkdirSync7, writeFileSync: writeFileSync10 } = await import("fs");
16078
16078
  const { dirname: dirname6, resolve: resolve12 } = await import("path");
16079
16079
  const absolutePath = resolve12(outputPath);
16080
16080
  mkdirSync7(dirname6(absolutePath), { recursive: true });
16081
- writeFileSync9(absolutePath, audio);
16081
+ writeFileSync10(absolutePath, audio);
16082
16082
  format({
16083
16083
  status: "ok",
16084
16084
  provider,
@@ -32681,7 +32681,7 @@ var StdioServerTransport = class {
32681
32681
  };
32682
32682
 
32683
32683
  // src/lib/cli-version.ts
32684
- var CLI_VERSION = "0.1.16";
32684
+ var CLI_VERSION = "0.1.18";
32685
32685
 
32686
32686
  // src/commands/mcp-serve.ts
32687
32687
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -34816,8 +34816,8 @@ function registerSetup(program3) {
34816
34816
  }
34817
34817
  try {
34818
34818
  const manifest = await agentExport(resolvedAgentId, { apiUrlOverride: opts.apiUrl });
34819
- const { writeFileSync: writeFileSync9 } = await import("fs");
34820
- writeFileSync9(
34819
+ const { writeFileSync: writeFileSync10 } = await import("fs");
34820
+ writeFileSync10(
34821
34821
  "tenant.yaml",
34822
34822
  `# tenant.yaml - Front Of House agent manifest
34823
34823
  # Edit this file and run: foh plan tenant.yaml
@@ -34985,8 +34985,8 @@ function registerSim(program3) {
34985
34985
  }
34986
34986
  const cert = response.certificate;
34987
34987
  if (opts.out) {
34988
- const { writeFileSync: writeFileSync9 } = await import("fs");
34989
- writeFileSync9(opts.out, JSON.stringify(cert, null, 2) + "\n", "utf-8");
34988
+ const { writeFileSync: writeFileSync10 } = await import("fs");
34989
+ writeFileSync10(opts.out, JSON.stringify(cert, null, 2) + "\n", "utf-8");
34990
34990
  process.stderr.write(` Certificate written to ${opts.out}
34991
34991
  `);
34992
34992
  }
@@ -35036,8 +35036,8 @@ function registerSim(program3) {
35036
35036
  });
35037
35037
  }
35038
35038
  if (opts.out) {
35039
- const { writeFileSync: writeFileSync9 } = await import("fs");
35040
- writeFileSync9(opts.out, JSON.stringify(response.certificate, null, 2) + "\n", "utf-8");
35039
+ const { writeFileSync: writeFileSync10 } = await import("fs");
35040
+ writeFileSync10(opts.out, JSON.stringify(response.certificate, null, 2) + "\n", "utf-8");
35041
35041
  process.stderr.write(` Final certificate written to ${opts.out}
35042
35042
  `);
35043
35043
  }
@@ -38376,7 +38376,8 @@ function artifactFiles(runDir) {
38376
38376
  if (!(0, import_fs12.existsSync)(runDir)) return [];
38377
38377
  return (0, import_fs12.readdirSync)(runDir).map((name) => (0, import_path10.join)(runDir, name)).filter((path2) => {
38378
38378
  const stat = (0, import_fs12.statSync)(path2);
38379
- return stat.isFile() && TEXT_ARTIFACT_NAMES.has(path2.split(/[\\/]/).pop() || "");
38379
+ const name = path2.split(/[\\/]/).pop() || "";
38380
+ return stat.isFile() && (TEXT_ARTIFACT_NAMES.has(name) || name.startsWith("command-output-cmd_"));
38380
38381
  }).sort();
38381
38382
  }
38382
38383
  function finding(kind, file2, count) {
@@ -38465,30 +38466,154 @@ function getExternalAgentRunDir() {
38465
38466
  if (!raw || !raw.trim()) return null;
38466
38467
  return (0, import_path11.resolve)(raw);
38467
38468
  }
38469
+ function commandIdFor(input) {
38470
+ const seed = `${input.startedAt}:${input.argv.join("\0")}:${process.pid}`;
38471
+ let hash2 = 2166136261;
38472
+ for (let i = 0; i < seed.length; i += 1) {
38473
+ hash2 ^= seed.charCodeAt(i);
38474
+ hash2 = Math.imul(hash2, 16777619);
38475
+ }
38476
+ return `cmd_${(hash2 >>> 0).toString(16).padStart(8, "0")}`;
38477
+ }
38478
+ function outputArtifactName(commandId) {
38479
+ return `command-output-${commandId}.txt`;
38480
+ }
38481
+ function parseEnvelope(text) {
38482
+ const candidates = [
38483
+ ...text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith("{") && line.endsWith("}")).reverse()
38484
+ ];
38485
+ const firstObject = text.indexOf("{");
38486
+ const lastObject = text.lastIndexOf("}");
38487
+ if (firstObject >= 0 && lastObject > firstObject) candidates.push(text.slice(firstObject, lastObject + 1));
38488
+ for (const candidate of candidates) {
38489
+ try {
38490
+ const parsed = JSON.parse(candidate);
38491
+ const status = typeof parsed.status === "string" ? parsed.status : parsed.ok === true ? "pass" : parsed.ok === false ? "fail" : null;
38492
+ const reasonCode = typeof parsed.reason_code === "string" ? parsed.reason_code : typeof parsed.code === "string" ? parsed.code : null;
38493
+ if (status || reasonCode) return { status, reasonCode };
38494
+ } catch {
38495
+ }
38496
+ }
38497
+ return { status: null, reasonCode: null };
38498
+ }
38468
38499
  function recordExternalAgentCliInvocation(input) {
38469
38500
  const runDir = getExternalAgentRunDir();
38470
- if (!runDir) return;
38501
+ if (!runDir) return null;
38471
38502
  try {
38472
38503
  (0, import_fs13.mkdirSync)(runDir, { recursive: true });
38504
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
38473
38505
  const args = input.argv.slice(2).map((arg) => redactText(String(arg)));
38506
+ const commandId = commandIdFor({ startedAt, argv: args });
38507
+ const promptVersion = process.env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] || null;
38508
+ const cwd = redactPath(process.cwd());
38474
38509
  const record2 = {
38475
- schema_version: "external_agent_cli_command.v1",
38476
- recorded_at: (/* @__PURE__ */ new Date()).toISOString(),
38510
+ schema_version: "external_agent_cli_command.v2",
38511
+ command_id: commandId,
38512
+ phase: "started",
38513
+ recorded_at: startedAt,
38477
38514
  cli_version: input.cliVersion,
38478
- cwd: redactPath(process.cwd()),
38515
+ cwd,
38479
38516
  argv: args,
38480
38517
  command: args.join(" "),
38481
38518
  json_requested: args.includes("--json"),
38482
- prompt_version: process.env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] || null
38519
+ prompt_version: promptVersion,
38520
+ started_at: startedAt
38483
38521
  };
38484
38522
  (0, import_fs13.appendFileSync)((0, import_path11.join)(runDir, "commands.ndjson"), safeJsonLine(record2), "utf8");
38523
+ return {
38524
+ commandId,
38525
+ runDir,
38526
+ startedAt,
38527
+ cliVersion: input.cliVersion,
38528
+ cwd,
38529
+ argv: args,
38530
+ command: args.join(" "),
38531
+ jsonRequested: args.includes("--json"),
38532
+ promptVersion
38533
+ };
38485
38534
  } catch {
38535
+ return null;
38536
+ }
38537
+ }
38538
+ function installExternalAgentCliCompletionRecorder(capture) {
38539
+ if (!capture) return;
38540
+ const chunks = [];
38541
+ const maxChars = 256 * 1024;
38542
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
38543
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
38544
+ function captureChunk(chunk) {
38545
+ if (typeof chunk === "string") {
38546
+ if (chunks.join("").length < maxChars) chunks.push(chunk.slice(0, maxChars));
38547
+ return;
38548
+ }
38549
+ if (Buffer.isBuffer(chunk)) {
38550
+ if (chunks.join("").length < maxChars) chunks.push(chunk.toString("utf8").slice(0, maxChars));
38551
+ }
38486
38552
  }
38553
+ process.stdout.write = ((chunk, ...args) => {
38554
+ captureChunk(chunk);
38555
+ return originalStdoutWrite(chunk, ...args);
38556
+ });
38557
+ process.stderr.write = ((chunk, ...args) => {
38558
+ captureChunk(chunk);
38559
+ return originalStderrWrite(chunk, ...args);
38560
+ });
38561
+ process.once("exit", (code) => {
38562
+ try {
38563
+ process.stdout.write = originalStdoutWrite;
38564
+ process.stderr.write = originalStderrWrite;
38565
+ const output = redactPath(chunks.join("").slice(0, maxChars));
38566
+ completeExternalAgentCliInvocation(capture, {
38567
+ output,
38568
+ exitCode: Number.isInteger(code) ? code : process.exitCode && Number.isInteger(process.exitCode) ? Number(process.exitCode) : null
38569
+ });
38570
+ } catch {
38571
+ }
38572
+ });
38573
+ }
38574
+ function completeExternalAgentCliInvocation(capture, input) {
38575
+ const completedAt = input.completedAt || (/* @__PURE__ */ new Date()).toISOString();
38576
+ const output = redactPath(String(input.output || "").slice(0, 256 * 1024));
38577
+ const parsed = parseEnvelope(output);
38578
+ let artifact = null;
38579
+ if (output.trim()) {
38580
+ artifact = outputArtifactName(capture.commandId);
38581
+ (0, import_fs13.writeFileSync)((0, import_path11.join)(capture.runDir, artifact), output, "utf8");
38582
+ }
38583
+ const record2 = {
38584
+ schema_version: "external_agent_cli_command.v2",
38585
+ command_id: capture.commandId,
38586
+ phase: "completed",
38587
+ recorded_at: completedAt,
38588
+ cli_version: capture.cliVersion,
38589
+ cwd: capture.cwd,
38590
+ argv: capture.argv,
38591
+ command: capture.command,
38592
+ json_requested: capture.jsonRequested,
38593
+ prompt_version: capture.promptVersion,
38594
+ started_at: capture.startedAt,
38595
+ completed_at: completedAt,
38596
+ duration_ms: Math.max(0, Date.parse(completedAt) - Date.parse(capture.startedAt)),
38597
+ exit_code: Number.isInteger(input.exitCode) ? Number(input.exitCode) : null,
38598
+ status: parsed.status,
38599
+ reason_code: parsed.reasonCode,
38600
+ output_artifact: artifact,
38601
+ output_bytes: Buffer.byteLength(output, "utf8")
38602
+ };
38603
+ (0, import_fs13.appendFileSync)((0, import_path11.join)(capture.runDir, "commands.ndjson"), safeJsonLine(record2), "utf8");
38487
38604
  }
38488
38605
  function readCommandRecords(runDir) {
38489
38606
  const commandLogPath = (0, import_path11.join)(runDir, "commands.ndjson");
38490
38607
  if (!(0, import_fs13.existsSync)(commandLogPath)) return [];
38491
- return (0, import_fs13.readFileSync)(commandLogPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
38608
+ const records = (0, import_fs13.readFileSync)(commandLogPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
38609
+ const byId = /* @__PURE__ */ new Map();
38610
+ const ordered = [];
38611
+ for (const record2 of records) {
38612
+ const id = record2.command_id || `${record2.recorded_at}:${record2.command}`;
38613
+ if (!byId.has(id)) ordered.push(record2);
38614
+ byId.set(id, record2.phase === "completed" ? record2 : byId.get(id) ?? record2);
38615
+ }
38616
+ return ordered.map((record2) => byId.get(record2.command_id || `${record2.recorded_at}:${record2.command}`) || record2);
38492
38617
  }
38493
38618
 
38494
38619
  // src/lib/external-agent-executor.ts
@@ -39501,7 +39626,7 @@ function installSoftExitTrap() {
39501
39626
 
39502
39627
  // src/index.ts
39503
39628
  installSoftExitTrap();
39504
- recordExternalAgentCliInvocation({ argv: process.argv, cliVersion: CLI_VERSION });
39629
+ installExternalAgentCliCompletionRecorder(recordExternalAgentCliInvocation({ argv: process.argv, cliVersion: CLI_VERSION }));
39505
39630
  var program2 = new Command();
39506
39631
  var BUG_REPORT_URL = process.env.FOH_BUG_REPORT_URL ?? "https://github.com/iiko38/front-of-house/issues";
39507
39632
  function shouldRenderBanner2(argv) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f-o-h/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {