@f-o-h/cli 0.1.71 → 0.1.73

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 (2) hide show
  1. package/dist/foh.js +105 -11
  2. package/package.json +41 -41
package/dist/foh.js CHANGED
@@ -32790,7 +32790,7 @@ var StdioServerTransport = class {
32790
32790
  };
32791
32791
 
32792
32792
  // src/lib/cli-version.ts
32793
- var CLI_VERSION = "0.1.71";
32793
+ var CLI_VERSION = "0.1.73";
32794
32794
 
32795
32795
  // src/commands/mcp-serve.ts
32796
32796
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -39593,9 +39593,11 @@ function ranked(map3) {
39593
39593
  function collectDocUrls(text) {
39594
39594
  return Array.from(new Set((String(text || "").match(DOC_URL_RE) || []).map((url2) => url2.replace(/[.?!:]+$/g, "")).filter((url2) => url2.startsWith("https://frontofhouse.okii.uk/")))).sort();
39595
39595
  }
39596
- function findRunFiles(root) {
39596
+ function findRunCandidates(root) {
39597
39597
  if (!(0, import_fs16.existsSync)(root)) return [];
39598
- const files = [];
39598
+ const candidates = [];
39599
+ const seenRunDirs = /* @__PURE__ */ new Set();
39600
+ const captureDirs = [];
39599
39601
  const stack = [root];
39600
39602
  while (stack.length > 0) {
39601
39603
  const current = stack.pop();
@@ -39605,11 +39607,18 @@ function findRunFiles(root) {
39605
39607
  if (entry.isDirectory()) {
39606
39608
  stack.push(absolute);
39607
39609
  } else if (entry.isFile() && entry.name === "run.json") {
39608
- files.push(absolute);
39610
+ candidates.push({ path: absolute, synthetic: false });
39611
+ seenRunDirs.add((0, import_path15.dirname)(absolute));
39612
+ } else if (entry.isFile() && entry.name === "commands.ndjson") {
39613
+ captureDirs.push(current);
39609
39614
  }
39610
39615
  }
39611
39616
  }
39612
- return files.sort();
39617
+ for (const captureDir of captureDirs) {
39618
+ if (seenRunDirs.has(captureDir)) continue;
39619
+ candidates.push({ path: (0, import_path15.join)(captureDir, "run.json"), synthetic: true });
39620
+ }
39621
+ return candidates.sort((a, b) => a.path.localeCompare(b.path));
39613
39622
  }
39614
39623
  function validateExternalAgentRun(value) {
39615
39624
  const findings = [];
@@ -39642,6 +39651,90 @@ function runSortTime(run) {
39642
39651
  const time3 = Date.parse(raw);
39643
39652
  return Number.isFinite(time3) ? time3 : 0;
39644
39653
  }
39654
+ function latestCommandTime(commands) {
39655
+ const times = commands.map((command) => String(command.completed_at || command.started_at || command.recorded_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => b.time - a.time);
39656
+ return times[0]?.raw ?? null;
39657
+ }
39658
+ function firstCommandTime(commands) {
39659
+ const times = commands.map((command) => String(command.started_at || command.recorded_at || command.completed_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => a.time - b.time);
39660
+ return times[0]?.raw ?? null;
39661
+ }
39662
+ function commandReasonCodes(commands) {
39663
+ const codes = /* @__PURE__ */ new Set();
39664
+ for (const command of commands) {
39665
+ if (command.reason_code) codes.add(String(command.reason_code));
39666
+ for (const reasonCode of toArray2(command.check_reason_codes)) {
39667
+ if (reasonCode) codes.add(String(reasonCode));
39668
+ }
39669
+ }
39670
+ return Array.from(codes);
39671
+ }
39672
+ function syntheticStatusFromCommands(commands) {
39673
+ const commandReasons = commandReasonCodes(commands);
39674
+ const failed = commands.find((command) => {
39675
+ const status = String(command.status || "").toLowerCase();
39676
+ return status === "fail" || typeof command.exit_code === "number" && command.exit_code !== 0 && status !== "hold";
39677
+ });
39678
+ if (failed) {
39679
+ return {
39680
+ status: "fail",
39681
+ reasonCode: String(failed.reason_code || commandReasons[0] || "external_agent_command_failed")
39682
+ };
39683
+ }
39684
+ const held = commands.find((command) => String(command.status || "").toLowerCase() === "hold");
39685
+ if (held) {
39686
+ return {
39687
+ status: "hold",
39688
+ reasonCode: String(held.reason_code || commandReasons[0] || "external_agent_command_held")
39689
+ };
39690
+ }
39691
+ if (commands.length === 0) {
39692
+ return { status: "hold", reasonCode: "external_agent_capture_empty" };
39693
+ }
39694
+ return { status: "pass", reasonCode: null };
39695
+ }
39696
+ function synthesizeRunFromCapture(runPath) {
39697
+ const runDir = (0, import_path15.dirname)(runPath);
39698
+ const commands = collapseCommandRecords(readNdjson((0, import_path15.join)(runDir, "commands.ndjson")));
39699
+ const metadata = asObject((0, import_fs16.existsSync)((0, import_path15.join)(runDir, "external-agent-metadata.json")) ? readJson((0, import_path15.join)(runDir, "external-agent-metadata.json")) : {});
39700
+ const blockerCodes = toArray2(metadata?.blocker_reason_codes).map(String).filter(Boolean);
39701
+ const commandClassification = syntheticStatusFromCommands(commands);
39702
+ const status = commandClassification.status === "fail" ? "fail" : blockerCodes.length > 0 ? "hold" : commandClassification.status;
39703
+ const reasonCode = commandClassification.status === "fail" ? commandClassification.reasonCode : blockerCodes[0] || commandClassification.reasonCode;
39704
+ const firstCommand = commands[0] || {};
39705
+ const startedAt = firstCommandTime(commands) || (/* @__PURE__ */ new Date(0)).toISOString();
39706
+ const endedAt = latestCommandTime(commands) || startedAt;
39707
+ const docs = toArray2(metadata?.docs_pages_used).map(String).filter(Boolean);
39708
+ const runId = (0, import_path15.dirname)(runPath).split(/[\\/]/).filter(Boolean).slice(-3).join("-") || "capture-only-run";
39709
+ return {
39710
+ schema_version: "external_agent_run.v1",
39711
+ run_id: runId,
39712
+ status,
39713
+ failure_reason_code: status === "pass" ? null : reasonCode || "external_agent_capture_unfinalized",
39714
+ model_provider: "unknown",
39715
+ model_name: "unknown",
39716
+ prompt_version: String(firstCommand.prompt_version || "unknown"),
39717
+ started_at: startedAt,
39718
+ ended_at: endedAt,
39719
+ manual_intervention_count: 0,
39720
+ environment: {
39721
+ foh_cli_version: firstCommand.cli_version || null,
39722
+ capture_only: true
39723
+ },
39724
+ public_entrypoints: [
39725
+ "https://frontofhouse.okii.uk",
39726
+ "npx --yes @f-o-h/cli@latest"
39727
+ ],
39728
+ commands_run: commands.map((command) => String(command.command || "")).filter(Boolean),
39729
+ docs_pages_used: docs,
39730
+ artifacts: {
39731
+ command_log: "commands.ndjson",
39732
+ agent_metadata: (0, import_fs16.existsSync)((0, import_path15.join)(runDir, "external-agent-metadata.json")) ? "external-agent-metadata.json" : null,
39733
+ capture_only: true
39734
+ },
39735
+ summary: "Synthetic run record created from external-agent capture artifacts because run.json was missing."
39736
+ };
39737
+ }
39645
39738
  function cohortIdForRunPath(root, runPath) {
39646
39739
  const normalized = (0, import_path15.relative)(root, (0, import_path15.dirname)(runPath)).replaceAll("\\", "/");
39647
39740
  const parts = normalized.split("/").filter(Boolean);
@@ -39652,9 +39745,10 @@ function cohortIdForRunPath(root, runPath) {
39652
39745
  function readRunRecords(root, cwd) {
39653
39746
  const records = [];
39654
39747
  const invalid_runs = [];
39655
- for (const file2 of findRunFiles(root)) {
39748
+ for (const candidate of findRunCandidates(root)) {
39749
+ const file2 = candidate.path;
39656
39750
  try {
39657
- const parsed = readJson(file2);
39751
+ const parsed = candidate.synthetic ? synthesizeRunFromCapture(file2) : readJson(file2);
39658
39752
  const findings = validateExternalAgentRun(parsed);
39659
39753
  if (findings.length > 0) {
39660
39754
  invalid_runs.push({ path: (0, import_path15.relative)(cwd, file2).replaceAll("\\", "/"), findings });
@@ -39813,7 +39907,7 @@ function summarizeExternalAgentRuns(options) {
39813
39907
  for (const page of toArray2(artifactSummary.docs_pages_observed)) increment(docsCounts, page);
39814
39908
  }
39815
39909
  const topFailures = ranked(failureCounts);
39816
- const commandReasonCodes = ranked(commandReasonCounts);
39910
+ const commandReasonCodes2 = ranked(commandReasonCounts);
39817
39911
  const recommendedFixes = topFailures.map((failure) => ({
39818
39912
  reason_code: failure.key,
39819
39913
  count: failure.count,
@@ -39841,7 +39935,7 @@ function summarizeExternalAgentRuns(options) {
39841
39935
  missing_completion_count: missingCompletionCount,
39842
39936
  commands_with_duration_count: commandsWithDurationCount,
39843
39937
  total_command_duration_ms: totalCommandDurationMs,
39844
- command_reason_codes: commandReasonCodes,
39938
+ command_reason_codes: commandReasonCodes2,
39845
39939
  slow_steps: slowSteps.sort((a, b) => Number(b.duration_ms || 0) - Number(a.duration_ms || 0) || String(a.command || "").localeCompare(String(b.command || ""))).slice(0, 20)
39846
39940
  },
39847
39941
  codex_telemetry: {
@@ -39915,11 +40009,11 @@ function classifyExternalAgentRun(input) {
39915
40009
  if (observedVersions.some((version2) => version2 !== CLI_VERSION)) {
39916
40010
  return { status: "hold", reasonCode: "external_agent_cli_version_drift" };
39917
40011
  }
39918
- const commandReasonCodes = completedCommands.flatMap((record2) => [
40012
+ const commandReasonCodes2 = completedCommands.flatMap((record2) => [
39919
40013
  String(record2.reason_code || ""),
39920
40014
  ...Array.isArray(record2.check_reason_codes) ? record2.check_reason_codes.map((code) => String(code || "")) : []
39921
40015
  ]).filter(Boolean);
39922
- const hasCommandReason = (pattern) => commandReasonCodes.some((reason) => pattern.test(reason));
40016
+ const hasCommandReason = (pattern) => commandReasonCodes2.some((reason) => pattern.test(reason));
39923
40017
  if (hasCommandReason(new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i"))) {
39924
40018
  return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
39925
40019
  }
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
- {
2
- "name": "@f-o-h/cli",
3
- "version": "0.1.71",
4
- "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
- "license": "UNLICENSED",
6
- "bin": {
7
- "foh": "dist/foh.js"
8
- },
9
- "main": "dist/foh.js",
10
- "files": [
11
- "dist/",
12
- "examples/",
13
- "schemas/",
14
- "README.md",
15
- "package.json"
16
- ],
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "engines": {
21
- "node": ">=18"
22
- },
23
- "scripts": {
24
- "build": "node build.mjs",
25
- "test": "vitest run",
26
- "typecheck": "tsc --noEmit"
27
- },
28
- "dependencies": {
29
- "@modelcontextprotocol/sdk": "^1.29.0",
30
- "commander": "^12.1.0",
31
- "js-yaml": "^4.1.1",
32
- "picocolors": "^1.1.1",
33
- "zod": "^4.3.6"
34
- },
35
- "devDependencies": {
36
- "@types/js-yaml": "^4.0.9",
37
- "@types/node": "^22.0.0",
38
- "esbuild": "^0.24.0",
39
- "vitest": "^2.0.0"
40
- }
41
- }
1
+ {
2
+ "name": "@f-o-h/cli",
3
+ "version": "0.1.73",
4
+ "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
+ "license": "UNLICENSED",
6
+ "bin": {
7
+ "foh": "dist/foh.js"
8
+ },
9
+ "main": "dist/foh.js",
10
+ "files": [
11
+ "dist/",
12
+ "examples/",
13
+ "schemas/",
14
+ "README.md",
15
+ "package.json"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "scripts": {
24
+ "build": "node build.mjs",
25
+ "test": "vitest run",
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.29.0",
30
+ "commander": "^12.1.0",
31
+ "js-yaml": "^4.1.1",
32
+ "picocolors": "^1.1.1",
33
+ "zod": "^4.3.6"
34
+ },
35
+ "devDependencies": {
36
+ "@types/js-yaml": "^4.0.9",
37
+ "@types/node": "^22.0.0",
38
+ "esbuild": "^0.24.0",
39
+ "vitest": "^2.0.0"
40
+ }
41
+ }