@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.
- package/dist/foh.js +105 -11
- 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.
|
|
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
|
|
39596
|
+
function findRunCandidates(root) {
|
|
39597
39597
|
if (!(0, import_fs16.existsSync)(root)) return [];
|
|
39598
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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) =>
|
|
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.
|
|
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
|
+
}
|