yaml-flow 5.1.0 → 5.2.1
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/{examples/example-board/reusable-server-runtime.js → board-livecards-server-runtime.js} +42 -20
- package/{examples/example-board/reusable-board-runtime-client.js → browser/board-livecards-runtime-client.js} +6 -2
- package/browser/{board-livegraph-runtime.js → board-livegraph-engine.js} +212 -16
- package/browser/board-livegraph-engine.js.map +1 -0
- package/browser/live-cards.js +362 -38
- package/browser/live-cards.schema.json +20 -4
- package/dist/board-livegraph-runtime/index.cjs +210 -14
- package/dist/board-livegraph-runtime/index.cjs.map +1 -1
- package/dist/board-livegraph-runtime/index.d.cts +49 -5
- package/dist/board-livegraph-runtime/index.d.ts +49 -5
- package/dist/board-livegraph-runtime/index.js +209 -15
- package/dist/board-livegraph-runtime/index.js.map +1 -1
- package/dist/card-compute/index.cjs +63 -7
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +2 -2
- package/dist/card-compute/index.d.ts +2 -2
- package/dist/card-compute/index.js +63 -7
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +664 -75
- package/dist/cli/board-live-cards-cli.cjs.map +1 -1
- package/dist/cli/board-live-cards-cli.d.cts +33 -5
- package/dist/cli/board-live-cards-cli.d.ts +33 -5
- package/dist/cli/board-live-cards-cli.js +661 -76
- package/dist/cli/board-live-cards-cli.js.map +1 -1
- package/dist/{constants-ozjf1Ejw.d.cts → constants-BzZUyYlp.d.cts} +1 -1
- package/dist/{constants-DuzE5n03.d.ts → constants-oCEbNpul.d.ts} +1 -1
- package/dist/continuous-event-graph/index.cjs +47 -14
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +9 -9
- package/dist/continuous-event-graph/index.d.ts +9 -9
- package/dist/continuous-event-graph/index.js +47 -14
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +29 -12
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +5 -5
- package/dist/event-graph/index.d.ts +5 -5
- package/dist/event-graph/index.js +29 -12
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +93 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +93 -20
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +29 -12
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +29 -12
- package/dist/inference/index.js.map +1 -1
- package/dist/{journal-NLYuqege.d.ts → journal-9HEgs7dU.d.ts} +1 -1
- package/dist/{journal-DRfJiheM.d.cts → journal-B-JCfQnh.d.cts} +1 -1
- package/dist/{live-cards-bridge-Or7fdEJV.d.ts → live-cards-bridge-CeNxiVcm.d.ts} +6 -2
- package/dist/{live-cards-bridge-vGJ6tMzN.d.cts → live-cards-bridge-z_rJCSbi.d.cts} +6 -2
- package/dist/{schedule-CMcZe5Ny.d.ts → schedule-Cszq9LYY.d.ts} +1 -1
- package/dist/{schedule-CiucyCan.d.cts → schedule-qWNL0RQh.d.cts} +1 -1
- package/dist/{types-CMFSIjpc.d.cts → types-BBhqYGhE.d.cts} +4 -0
- package/dist/{types-CMFSIjpc.d.ts → types-BBhqYGhE.d.ts} +4 -0
- package/dist/{types-BzLD8bjb.d.cts → types-CHSdoAAA.d.cts} +1 -1
- package/dist/{types-C2eJ7DAV.d.ts → types-CoW0gQl3.d.ts} +1 -1
- package/dist/{validate-DJQTQ6bP.d.ts → validate-BAVzUJWa.d.ts} +1 -1
- package/dist/{validate-ke92Cleg.d.cts → validate-Dbu7ygys.d.cts} +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-risk-assessment.json +28 -0
- package/examples/browser/boards/portfolio-tracker/cards/rebalancing-strategy.json +28 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-inference-adapter.js +187 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +139 -5
- package/examples/example-board/agent-instructions-cardlayout.md +28 -0
- package/examples/example-board/agent-instructions.md +603 -0
- package/examples/example-board/cards/card-concentration.json +42 -0
- package/examples/example-board/cards/card-market-prices.json +51 -0
- package/examples/example-board/cards/card-portfolio-action.json +19 -0
- package/examples/example-board/cards/card-portfolio-risks.json +19 -0
- package/examples/example-board/cards/card-portfolio-value.json +62 -0
- package/examples/example-board/cards/card-portfolio.json +44 -0
- package/examples/example-board/demo-chat-handler.js +373 -33
- package/examples/example-board/demo-server-config.json +0 -2
- package/examples/example-board/demo-server.js +46 -7
- package/examples/example-board/demo-shell-browser.html +75 -207
- package/examples/example-board/demo-shell-with-server.html +15 -9
- package/examples/example-board/demo-shell.html +1 -1
- package/examples/example-board/demo-task-executor.js +259 -41
- package/package.json +6 -2
- package/schema/live-cards.schema.json +20 -4
- package/browser/board-livegraph-runtime.js.map +0 -1
- package/examples/example-board/board.yaml +0 -23
- package/examples/example-board/bootstrap_payload.json +0 -1
- package/examples/example-board/cards/card-chain-region-alert.json +0 -39
- package/examples/example-board/cards/card-chain-region-totals.json +0 -26
- package/examples/example-board/cards/card-chain-top-region.json +0 -24
- package/examples/example-board/cards/card-ex-actions.json +0 -32
- package/examples/example-board/cards/card-ex-chart.json +0 -30
- package/examples/example-board/cards/card-ex-filter.json +0 -36
- package/examples/example-board/cards/card-ex-filtered-by-preference.json +0 -59
- package/examples/example-board/cards/card-ex-form.json +0 -91
- package/examples/example-board/cards/card-ex-list.json +0 -22
- package/examples/example-board/cards/card-ex-markdown.json +0 -17
- package/examples/example-board/cards/card-ex-metric.json +0 -19
- package/examples/example-board/cards/card-ex-narrative.json +0 -36
- package/examples/example-board/cards/card-ex-source-http.json +0 -28
- package/examples/example-board/cards/card-ex-source.json +0 -21
- package/examples/example-board/cards/card-ex-status.json +0 -35
- package/examples/example-board/cards/card-ex-table.json +0 -30
- package/examples/example-board/cards/card-ex-todo.json +0 -29
- package/examples/example-board/mock.db +0 -15
- package/examples/example-board/reusable-runtime-artifacts-adapter.js +0 -233
|
@@ -102,15 +102,30 @@ function groupTasksByProvides(candidateTaskNames, tasks) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// src/event-graph/task-transitions.ts
|
|
105
|
-
function applyTaskStart(state, taskName) {
|
|
105
|
+
function applyTaskStart(state, taskName, graph) {
|
|
106
106
|
const existingTask = state.tasks[taskName] ?? createDefaultGraphEngineStore();
|
|
107
|
+
const startConsumedHashes = {};
|
|
108
|
+
if (graph) {
|
|
109
|
+
const taskConfig = graph.tasks[taskName];
|
|
110
|
+
const requires = getRequires(taskConfig);
|
|
111
|
+
for (const token of requires) {
|
|
112
|
+
for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
|
|
113
|
+
if (getProvides(otherConfig).includes(token)) {
|
|
114
|
+
const otherState = state.tasks[otherName];
|
|
115
|
+
if (otherState?.lastDataHash) startConsumedHashes[token] = otherState.lastDataHash;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
107
121
|
const updatedTask = {
|
|
108
122
|
...existingTask,
|
|
109
123
|
status: "running",
|
|
110
124
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
125
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
112
126
|
progress: 0,
|
|
113
|
-
error: void 0
|
|
127
|
+
error: void 0,
|
|
128
|
+
startConsumedHashes
|
|
114
129
|
};
|
|
115
130
|
return {
|
|
116
131
|
...state,
|
|
@@ -130,16 +145,18 @@ function applyTaskCompletion(state, graph, taskName, result, dataHash, data) {
|
|
|
130
145
|
} else {
|
|
131
146
|
outputTokens = getProvides(taskConfig);
|
|
132
147
|
}
|
|
133
|
-
const lastConsumedHashes = { ...existingTask.lastConsumedHashes };
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
for (const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
const lastConsumedHashes = existingTask.startConsumedHashes ? { ...existingTask.startConsumedHashes } : { ...existingTask.lastConsumedHashes };
|
|
149
|
+
if (!existingTask.startConsumedHashes) {
|
|
150
|
+
const requires = taskConfig.requires ?? [];
|
|
151
|
+
for (const token of requires) {
|
|
152
|
+
for (const [otherName, otherConfig] of Object.entries(graph.tasks)) {
|
|
153
|
+
if (getProvides(otherConfig).includes(token)) {
|
|
154
|
+
const otherState = state.tasks[otherName];
|
|
155
|
+
if (otherState?.lastDataHash) {
|
|
156
|
+
lastConsumedHashes[token] = otherState.lastDataHash;
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
141
159
|
}
|
|
142
|
-
break;
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
}
|
|
@@ -284,7 +301,7 @@ function applyEvent(live, event) {
|
|
|
284
301
|
switch (event.type) {
|
|
285
302
|
// --- Execution state transitions ---
|
|
286
303
|
case "task-started":
|
|
287
|
-
return { config, state: applyTaskStart(state, event.taskName) };
|
|
304
|
+
return { config, state: applyTaskStart(state, event.taskName, config) };
|
|
288
305
|
case "task-completed":
|
|
289
306
|
return { config, state: applyTaskCompletion(state, config, event.taskName, event.result, event.dataHash, event.data) };
|
|
290
307
|
case "task-failed":
|
|
@@ -1112,13 +1129,29 @@ function validateNode(node) {
|
|
|
1112
1129
|
if (!Array.isArray(n.sources)) {
|
|
1113
1130
|
errors.push("sources: must be an array");
|
|
1114
1131
|
} else {
|
|
1132
|
+
const bindTos = /* @__PURE__ */ new Set();
|
|
1133
|
+
const outputFiles = /* @__PURE__ */ new Set();
|
|
1115
1134
|
n.sources.forEach((src, i) => {
|
|
1116
1135
|
if (!src || typeof src !== "object" || Array.isArray(src)) {
|
|
1117
1136
|
errors.push(`sources[${i}]: must be an object`);
|
|
1118
1137
|
} else {
|
|
1119
1138
|
const s = src;
|
|
1120
|
-
if (typeof s.bindTo !== "string" || !s.bindTo)
|
|
1121
|
-
|
|
1139
|
+
if (typeof s.bindTo !== "string" || !s.bindTo) {
|
|
1140
|
+
errors.push(`sources[${i}]: missing required "bindTo" property`);
|
|
1141
|
+
} else {
|
|
1142
|
+
if (bindTos.has(s.bindTo)) {
|
|
1143
|
+
errors.push(`sources[${i}]: bindTo "${s.bindTo}" is not unique across sources`);
|
|
1144
|
+
}
|
|
1145
|
+
bindTos.add(s.bindTo);
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof s.outputFile !== "string" || !s.outputFile) {
|
|
1148
|
+
errors.push(`sources[${i}]: missing required "outputFile" property`);
|
|
1149
|
+
} else {
|
|
1150
|
+
if (outputFiles.has(s.outputFile)) {
|
|
1151
|
+
errors.push(`sources[${i}]: outputFile "${s.outputFile}" is not unique across sources`);
|
|
1152
|
+
}
|
|
1153
|
+
outputFiles.add(s.outputFile);
|
|
1154
|
+
}
|
|
1122
1155
|
if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
|
|
1123
1156
|
errors.push(`sources[${i}]: optionalForCompletionGating must be a boolean`);
|
|
1124
1157
|
}
|
|
@@ -1176,12 +1209,15 @@ var CardCompute = {
|
|
|
1176
1209
|
var BOARD_FILE = "board-graph.json";
|
|
1177
1210
|
var JOURNAL_FILE = "board-journal.jsonl";
|
|
1178
1211
|
var TASK_EXECUTOR_LOG_FILE = "task-executor.jsonl";
|
|
1212
|
+
var INFERENCE_ADAPTER_LOG_FILE = "inference-adapter.jsonl";
|
|
1179
1213
|
var INVENTORY_FILE = "cards-inventory.jsonl";
|
|
1180
1214
|
var RUNTIME_OUT_FILE = ".runtime-out";
|
|
1181
1215
|
var DEFAULT_RUNTIME_OUT_DIR = "runtime-out";
|
|
1182
1216
|
var RUNTIME_STATUS_FILE = "board-livegraph-status.json";
|
|
1183
1217
|
var RUNTIME_CARDS_DIR = "cards";
|
|
1184
1218
|
var RUNTIME_DATA_OBJECTS_DIR = "data-objects";
|
|
1219
|
+
var INFERENCE_ADAPTER_FILE = ".inference-adapter";
|
|
1220
|
+
var DEFAULT_TASK_COMPLETION_RULE = "all_required_sources_fetched";
|
|
1185
1221
|
var EMPTY_CONFIG = { settings: { completion: "manual", refreshStrategy: "data-changed" }, tasks: {} };
|
|
1186
1222
|
var BoardJournal = class {
|
|
1187
1223
|
journalPath;
|
|
@@ -1389,6 +1425,39 @@ function decodeSourceToken(token) {
|
|
|
1389
1425
|
return null;
|
|
1390
1426
|
}
|
|
1391
1427
|
}
|
|
1428
|
+
function markRequested(entry, requestedAt) {
|
|
1429
|
+
entry.lastRequestedAt = requestedAt;
|
|
1430
|
+
}
|
|
1431
|
+
function markFetchFailed(entry, reason) {
|
|
1432
|
+
entry.lastError = reason;
|
|
1433
|
+
delete entry.lastFetchedAt;
|
|
1434
|
+
}
|
|
1435
|
+
function markFetchCompleted(entry, fetchedAt) {
|
|
1436
|
+
entry.lastFetchedAt = fetchedAt;
|
|
1437
|
+
delete entry.lastError;
|
|
1438
|
+
}
|
|
1439
|
+
function isSourceInFlight(entry) {
|
|
1440
|
+
if (!entry?.lastRequestedAt) return false;
|
|
1441
|
+
return !entry.lastFetchedAt || entry.lastFetchedAt < entry.lastRequestedAt;
|
|
1442
|
+
}
|
|
1443
|
+
function decideSourceAction(entry, queueRequestedAt) {
|
|
1444
|
+
if (!entry?.lastRequestedAt) return "dispatch";
|
|
1445
|
+
const inFlight = isSourceInFlight(entry);
|
|
1446
|
+
if (inFlight) return "in-flight";
|
|
1447
|
+
if (!entry.lastFetchedAt) return "dispatch";
|
|
1448
|
+
if (entry.lastFetchedAt < queueRequestedAt) return "dispatch";
|
|
1449
|
+
return "idle";
|
|
1450
|
+
}
|
|
1451
|
+
function nextEntryAfterFetchDelivery(entry, fetchedAt) {
|
|
1452
|
+
const next = { ...entry };
|
|
1453
|
+
markFetchCompleted(next, fetchedAt);
|
|
1454
|
+
return next;
|
|
1455
|
+
}
|
|
1456
|
+
function nextEntryAfterFetchFailure(entry, reason) {
|
|
1457
|
+
const next = { ...entry };
|
|
1458
|
+
markFetchFailed(next, reason);
|
|
1459
|
+
return next;
|
|
1460
|
+
}
|
|
1392
1461
|
function runtimePath(boardDir, cardId) {
|
|
1393
1462
|
return path__namespace.join(boardDir, `${cardId}.runtime.json`);
|
|
1394
1463
|
}
|
|
@@ -1543,6 +1612,15 @@ function splitCommandLine(command) {
|
|
|
1543
1612
|
if (current) tokens.push(current);
|
|
1544
1613
|
return tokens;
|
|
1545
1614
|
}
|
|
1615
|
+
function resolveCommandInvocation(rawCmd, rawArgs) {
|
|
1616
|
+
if (/^(node|node\.exe)$/i.test(rawCmd)) {
|
|
1617
|
+
return { cmd: process.execPath, args: rawArgs };
|
|
1618
|
+
}
|
|
1619
|
+
if (/\.m?js$/i.test(rawCmd)) {
|
|
1620
|
+
return { cmd: process.execPath, args: [rawCmd, ...rawArgs] };
|
|
1621
|
+
}
|
|
1622
|
+
return { cmd: rawCmd, args: rawArgs };
|
|
1623
|
+
}
|
|
1546
1624
|
function spawnDetachedProcessAccumulatedWorker(boardDir) {
|
|
1547
1625
|
const { cmd, args: cliArgs } = getCliInvocation("process-accumulated-events", ["--rg", boardDir, "--inline-loop"]);
|
|
1548
1626
|
try {
|
|
@@ -1618,7 +1696,18 @@ function getCliInvocation(command, args) {
|
|
|
1618
1696
|
return { cmd: npxCmd, args: ["tsx", tsPath, command, ...args] };
|
|
1619
1697
|
}
|
|
1620
1698
|
function invokeRunSources(boardDir, cardPath, callbackToken, callback) {
|
|
1621
|
-
const
|
|
1699
|
+
const args = ["--card", cardPath, "--token", callbackToken, "--rg", boardDir];
|
|
1700
|
+
const { cmd, args: cmdArgs } = getCliInvocation("run-sources-internal", args);
|
|
1701
|
+
try {
|
|
1702
|
+
spawnDetachedCommand(cmd, cmdArgs);
|
|
1703
|
+
callback(null);
|
|
1704
|
+
} catch (err) {
|
|
1705
|
+
callback(err instanceof Error ? err : new Error(String(err)));
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
function invokeRunInference(boardDir, cardId, inputFile, callbackToken, checksum, callback) {
|
|
1709
|
+
const inferenceToken = encodeSourceToken({ cbk: callbackToken, rg: boardDir, cid: cardId, b: "", d: "", cs: checksum });
|
|
1710
|
+
const { cmd, args } = getCliInvocation("run-inference-internal", ["--in", inputFile, "--token", inferenceToken]);
|
|
1622
1711
|
try {
|
|
1623
1712
|
spawnDetachedCommand(cmd, args);
|
|
1624
1713
|
callback(null);
|
|
@@ -1626,10 +1715,11 @@ function invokeRunSources(boardDir, cardPath, callbackToken, callback) {
|
|
|
1626
1715
|
callback(err instanceof Error ? err : new Error(String(err)));
|
|
1627
1716
|
}
|
|
1628
1717
|
}
|
|
1629
|
-
function appendTaskExecutorLog(boardDir, hydratedSource) {
|
|
1718
|
+
function appendTaskExecutorLog(boardDir, hydratedSource, mode) {
|
|
1630
1719
|
try {
|
|
1631
1720
|
const entry = {
|
|
1632
1721
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1722
|
+
mode,
|
|
1633
1723
|
hydratedSource
|
|
1634
1724
|
};
|
|
1635
1725
|
fs__namespace.appendFileSync(path__namespace.join(boardDir, TASK_EXECUTOR_LOG_FILE), JSON.stringify(entry) + "\n", "utf-8");
|
|
@@ -1637,6 +1727,18 @@ function appendTaskExecutorLog(boardDir, hydratedSource) {
|
|
|
1637
1727
|
console.error(`[task-executor-log] append failed: ${logErr instanceof Error ? logErr.message : String(logErr)}`);
|
|
1638
1728
|
}
|
|
1639
1729
|
}
|
|
1730
|
+
function appendInferenceAdapterLog(boardDir, cardId, payload) {
|
|
1731
|
+
try {
|
|
1732
|
+
const entry = {
|
|
1733
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1734
|
+
cardId,
|
|
1735
|
+
payload
|
|
1736
|
+
};
|
|
1737
|
+
fs__namespace.appendFileSync(path__namespace.join(boardDir, INFERENCE_ADAPTER_LOG_FILE), JSON.stringify(entry) + "\n", "utf-8");
|
|
1738
|
+
} catch (logErr) {
|
|
1739
|
+
console.error(`[inference-adapter-log] append failed: ${logErr instanceof Error ? logErr.message : String(logErr)}`);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1640
1742
|
function invokeSourceDataFetched(sourceToken, tmpFile, callback) {
|
|
1641
1743
|
const { cmd, args } = getCliInvocation("source-data-fetched", ["--tmp", tmpFile, "--token", sourceToken]);
|
|
1642
1744
|
execCommandAsync(cmd, args, (err, stdout, stderr) => {
|
|
@@ -1665,22 +1767,33 @@ function createBoardReactiveGraph(boardDir) {
|
|
|
1665
1767
|
const requiredSources = allSources.filter((s) => s.optionalForCompletionGating !== true);
|
|
1666
1768
|
const runtime = readRuntimeState(boardDir, cardId);
|
|
1667
1769
|
let runtimeDirty = false;
|
|
1770
|
+
const currentExecutionCount = input.taskState?.executionCount ?? 0;
|
|
1771
|
+
if (typeof runtime._lastExecutionCount === "number" && runtime._lastExecutionCount !== currentExecutionCount) {
|
|
1772
|
+
runtime._sources = {};
|
|
1773
|
+
runtime._inferenceEntry = void 0;
|
|
1774
|
+
}
|
|
1775
|
+
if (runtime._lastExecutionCount !== currentExecutionCount) {
|
|
1776
|
+
runtime._lastExecutionCount = currentExecutionCount;
|
|
1777
|
+
runtimeDirty = true;
|
|
1778
|
+
}
|
|
1668
1779
|
if (input.update) {
|
|
1669
1780
|
const u = input.update;
|
|
1670
|
-
const
|
|
1671
|
-
if (
|
|
1672
|
-
|
|
1673
|
-
runtime._sources[
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1781
|
+
const outputFile = u.outputFile;
|
|
1782
|
+
if (outputFile) {
|
|
1783
|
+
if (!runtime._sources[outputFile]) runtime._sources[outputFile] = {};
|
|
1784
|
+
const entry = runtime._sources[outputFile];
|
|
1785
|
+
if (u.failure) {
|
|
1786
|
+
runtime._sources[outputFile] = nextEntryAfterFetchFailure(entry, u.reason ?? "unknown");
|
|
1787
|
+
runtimeDirty = true;
|
|
1788
|
+
} else {
|
|
1789
|
+
runtime._sources[outputFile] = nextEntryAfterFetchDelivery(
|
|
1790
|
+
entry,
|
|
1791
|
+
u.fetchedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1792
|
+
);
|
|
1793
|
+
runtimeDirty = true;
|
|
1794
|
+
}
|
|
1795
|
+
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
1682
1796
|
}
|
|
1683
|
-
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
1684
1797
|
}
|
|
1685
1798
|
const sourcesData = {};
|
|
1686
1799
|
for (const src of allSources) {
|
|
@@ -1722,40 +1835,57 @@ function createBoardReactiveGraph(boardDir) {
|
|
|
1722
1835
|
card_id: cardId,
|
|
1723
1836
|
computed_values: computeNode.computed_values ?? {}
|
|
1724
1837
|
});
|
|
1838
|
+
const enrichedCard = { ...card };
|
|
1839
|
+
const enrichedSources = CardCompute.enrichSources(
|
|
1840
|
+
Array.isArray(card.sources) ? card.sources : void 0,
|
|
1841
|
+
{
|
|
1842
|
+
requires,
|
|
1843
|
+
sourcesData,
|
|
1844
|
+
computed_values: computeNode.computed_values
|
|
1845
|
+
}
|
|
1846
|
+
);
|
|
1847
|
+
const sourceCwd = path__namespace.dirname(cardPath);
|
|
1848
|
+
enrichedCard.sources = Array.isArray(enrichedSources) ? enrichedSources.map((src) => ({
|
|
1849
|
+
...src,
|
|
1850
|
+
cwd: typeof src.cwd === "string" && src.cwd ? src.cwd : sourceCwd,
|
|
1851
|
+
boardDir: typeof src.boardDir === "string" && src.boardDir ? src.boardDir : boardDir
|
|
1852
|
+
})) : enrichedSources;
|
|
1853
|
+
const enrichedByOutput = /* @__PURE__ */ new Map();
|
|
1854
|
+
for (const src of Array.isArray(enrichedCard.sources) ? enrichedCard.sources : []) {
|
|
1855
|
+
const outputFile = src.outputFile;
|
|
1856
|
+
if (typeof outputFile === "string" && outputFile) {
|
|
1857
|
+
enrichedByOutput.set(outputFile, src);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1725
1860
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1861
|
+
const runQueuedAt = input.update ? void 0 : now;
|
|
1726
1862
|
const undeliveredRequired = requiredSources.filter((s) => {
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
if (!
|
|
1730
|
-
|
|
1731
|
-
|
|
1863
|
+
const outputFile = s.outputFile;
|
|
1864
|
+
if (typeof outputFile !== "string" || !outputFile) return true;
|
|
1865
|
+
if (!runtime._sources[outputFile]) runtime._sources[outputFile] = {};
|
|
1866
|
+
const entry = runtime._sources[outputFile];
|
|
1867
|
+
if (runQueuedAt) {
|
|
1868
|
+
entry.queueRequestedAt = runQueuedAt;
|
|
1869
|
+
runtimeDirty = true;
|
|
1870
|
+
}
|
|
1871
|
+
const qrt = entry.queueRequestedAt ?? entry.lastRequestedAt ?? now;
|
|
1872
|
+
const action = decideSourceAction(entry, qrt);
|
|
1873
|
+
if (action === "in-flight") return false;
|
|
1874
|
+
return action === "dispatch";
|
|
1732
1875
|
});
|
|
1876
|
+
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
1733
1877
|
if (undeliveredRequired.length > 0) {
|
|
1734
1878
|
let stampedAny = false;
|
|
1735
1879
|
for (const src of undeliveredRequired) {
|
|
1736
|
-
const
|
|
1737
|
-
if (
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1880
|
+
const outputFile = src.outputFile;
|
|
1881
|
+
if (typeof outputFile !== "string" || !outputFile) continue;
|
|
1882
|
+
const entry = runtime._sources[outputFile] ?? {};
|
|
1883
|
+
markRequested(entry, now);
|
|
1884
|
+
runtime._sources[outputFile] = entry;
|
|
1885
|
+
stampedAny = true;
|
|
1742
1886
|
}
|
|
1743
1887
|
if (stampedAny) writeRuntimeState(boardDir, cardId, runtime);
|
|
1744
|
-
|
|
1745
|
-
const enrichedSources = CardCompute.enrichSources(
|
|
1746
|
-
Array.isArray(card.sources) ? card.sources : void 0,
|
|
1747
|
-
{
|
|
1748
|
-
requires,
|
|
1749
|
-
sourcesData,
|
|
1750
|
-
computed_values: computeNode.computed_values
|
|
1751
|
-
}
|
|
1752
|
-
);
|
|
1753
|
-
const sourceCwd = path__namespace.dirname(cardPath);
|
|
1754
|
-
enrichedCard.sources = Array.isArray(enrichedSources) ? enrichedSources.map((src) => ({
|
|
1755
|
-
...src,
|
|
1756
|
-
cwd: typeof src.cwd === "string" && src.cwd ? src.cwd : sourceCwd,
|
|
1757
|
-
boardDir: typeof src.boardDir === "string" && src.boardDir ? src.boardDir : boardDir
|
|
1758
|
-
})) : enrichedSources;
|
|
1888
|
+
if (!stampedAny) return "task-initiated";
|
|
1759
1889
|
const enrichedCardPath = path__namespace.join(os__namespace.tmpdir(), `card-enriched-${cardId}-${Date.now()}.json`);
|
|
1760
1890
|
fs__namespace.writeFileSync(enrichedCardPath, JSON.stringify(enrichedCard, null, 2), "utf-8");
|
|
1761
1891
|
invokeRunSources(boardDir, enrichedCardPath, input.callbackToken, (err) => {
|
|
@@ -1774,10 +1904,79 @@ function createBoardReactiveGraph(boardDir) {
|
|
|
1774
1904
|
for (const { bindTo, src } of providesBindings) {
|
|
1775
1905
|
data[bindTo] = CardCompute.resolve(computeNode, src);
|
|
1776
1906
|
}
|
|
1907
|
+
const completionRule = typeof card.when_is_task_completed === "string" && card.when_is_task_completed.trim() ? card.when_is_task_completed.trim() : DEFAULT_TASK_COMPLETION_RULE;
|
|
1908
|
+
const cardData = card.card_data;
|
|
1909
|
+
const llmCompletion = cardData?.llm_task_completion_inference ?? {};
|
|
1910
|
+
const isLlmTaskCompleted = llmCompletion.isTaskCompleted === true;
|
|
1911
|
+
const inferenceEntry = runtime._inferenceEntry ?? {};
|
|
1912
|
+
const inferenceRequestedAt = typeof inferenceEntry.lastRequestedAt === "string" ? inferenceEntry.lastRequestedAt : void 0;
|
|
1913
|
+
const inferenceCompletedAt = typeof llmCompletion.inferenceCompletedAt === "string" ? llmCompletion.inferenceCompletedAt : void 0;
|
|
1914
|
+
const inferencePending = !!inferenceRequestedAt && (!inferenceCompletedAt || inferenceCompletedAt < inferenceRequestedAt);
|
|
1915
|
+
const latestRequiredSourceFetchedAt = requiredSources.reduce((latest, src) => {
|
|
1916
|
+
const fetchedAt = runtime._sources[src.outputFile]?.lastFetchedAt;
|
|
1917
|
+
if (typeof fetchedAt !== "string") return latest;
|
|
1918
|
+
if (!latest || fetchedAt > latest) return fetchedAt;
|
|
1919
|
+
return latest;
|
|
1920
|
+
}, void 0);
|
|
1921
|
+
const shouldRequestInference = !inferenceRequestedAt || !inferenceCompletedAt || !!latestRequiredSourceFetchedAt && latestRequiredSourceFetchedAt > inferenceCompletedAt;
|
|
1922
|
+
if (completionRule !== DEFAULT_TASK_COMPLETION_RULE) {
|
|
1923
|
+
if (isLlmTaskCompleted) ; else if (inferencePending) {
|
|
1924
|
+
return "task-initiated";
|
|
1925
|
+
} else if (!shouldRequestInference) {
|
|
1926
|
+
return "task-initiated";
|
|
1927
|
+
} else {
|
|
1928
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
1929
|
+
const inferencePayload = {
|
|
1930
|
+
cardId,
|
|
1931
|
+
taskName: input.nodeId,
|
|
1932
|
+
completionRule,
|
|
1933
|
+
context: {
|
|
1934
|
+
requires,
|
|
1935
|
+
sourcesData,
|
|
1936
|
+
computed_values: computeNode.computed_values ?? {},
|
|
1937
|
+
provides: data,
|
|
1938
|
+
card_data: computeNode.card_data ?? {}
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
if (runQueuedAt) {
|
|
1942
|
+
inferenceEntry.queueRequestedAt = runQueuedAt;
|
|
1943
|
+
runtimeDirty = true;
|
|
1944
|
+
}
|
|
1945
|
+
const inferenceQrt = inferenceEntry.queueRequestedAt ?? inferenceEntry.lastRequestedAt ?? now2;
|
|
1946
|
+
const inferenceAction = decideSourceAction(inferenceEntry, inferenceQrt);
|
|
1947
|
+
if (inferenceAction === "in-flight") {
|
|
1948
|
+
runtime._inferenceEntry = inferenceEntry;
|
|
1949
|
+
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
1950
|
+
return "task-initiated";
|
|
1951
|
+
}
|
|
1952
|
+
if (inferenceAction === "idle") {
|
|
1953
|
+
return "task-initiated";
|
|
1954
|
+
}
|
|
1955
|
+
const inferenceInFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-${cardId}-${Date.now()}.json`);
|
|
1956
|
+
fs__namespace.writeFileSync(inferenceInFile, JSON.stringify(inferencePayload, null, 2), "utf-8");
|
|
1957
|
+
appendInferenceAdapterLog(boardDir, cardId, inferencePayload);
|
|
1958
|
+
markRequested(inferenceEntry, now2);
|
|
1959
|
+
runtime._inferenceEntry = inferenceEntry;
|
|
1960
|
+
runtimeDirty = true;
|
|
1961
|
+
invokeRunInference(boardDir, cardId, inferenceInFile, input.callbackToken, void 0, (err) => {
|
|
1962
|
+
if (err) {
|
|
1963
|
+
console.error(`[card-handler] ${input.nodeId}:`, err.message);
|
|
1964
|
+
const failedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1965
|
+
appendEventToJournal(boardDir, {
|
|
1966
|
+
type: "task-failed",
|
|
1967
|
+
taskName: input.nodeId,
|
|
1968
|
+
error: err.message,
|
|
1969
|
+
timestamp: failedAt
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
return "task-initiated";
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1777
1976
|
writeRuntimeDataObjects(boardDir, data);
|
|
1778
1977
|
const undeliveredOptional = allSources.filter((s) => {
|
|
1779
|
-
if (s.optionalForCompletionGating !== true
|
|
1780
|
-
const entry = runtime._sources[s.
|
|
1978
|
+
if (s.optionalForCompletionGating !== true) return false;
|
|
1979
|
+
const entry = runtime._sources[s.outputFile];
|
|
1781
1980
|
if (!entry?.lastRequestedAt) return true;
|
|
1782
1981
|
if (!entry.lastFetchedAt) return true;
|
|
1783
1982
|
return entry.lastFetchedAt <= entry.lastRequestedAt;
|
|
@@ -1871,16 +2070,18 @@ function cmdAddCards(args) {
|
|
|
1871
2070
|
function cmdInit(args) {
|
|
1872
2071
|
const dir = args[0];
|
|
1873
2072
|
if (!dir) {
|
|
1874
|
-
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--runtime-out <dir>]");
|
|
2073
|
+
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
1875
2074
|
}
|
|
1876
2075
|
const teIdx = args.indexOf("--task-executor");
|
|
1877
2076
|
const taskExecutor = teIdx !== -1 ? args[teIdx + 1] : void 0;
|
|
1878
2077
|
const chIdx = args.indexOf("--chat-handler");
|
|
1879
2078
|
const chatHandler = chIdx !== -1 ? args[chIdx + 1] : void 0;
|
|
2079
|
+
const iaIdx = args.indexOf("--inference-adapter");
|
|
2080
|
+
const inferenceAdapter = iaIdx !== -1 ? args[iaIdx + 1] : void 0;
|
|
1880
2081
|
const roIdx = args.indexOf("--runtime-out");
|
|
1881
2082
|
const runtimeOut = roIdx !== -1 ? args[roIdx + 1] : void 0;
|
|
1882
2083
|
if (roIdx !== -1 && !runtimeOut) {
|
|
1883
|
-
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--runtime-out <dir>]");
|
|
2084
|
+
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
1884
2085
|
}
|
|
1885
2086
|
const result = initBoard(dir);
|
|
1886
2087
|
if (taskExecutor) {
|
|
@@ -1889,6 +2090,9 @@ function cmdInit(args) {
|
|
|
1889
2090
|
if (chatHandler) {
|
|
1890
2091
|
fs__namespace.writeFileSync(path__namespace.join(dir, ".chat-handler"), chatHandler, "utf-8");
|
|
1891
2092
|
}
|
|
2093
|
+
if (inferenceAdapter) {
|
|
2094
|
+
fs__namespace.writeFileSync(path__namespace.join(dir, INFERENCE_ADAPTER_FILE), inferenceAdapter, "utf-8");
|
|
2095
|
+
}
|
|
1892
2096
|
const runtimeOutDir = configureRuntimeOutDir(dir, runtimeOut);
|
|
1893
2097
|
const live = loadBoard(dir);
|
|
1894
2098
|
writeJsonAtomic(resolveStatusSnapshotPath(dir), buildBoardStatusObject(dir, live));
|
|
@@ -2124,7 +2328,7 @@ function cmdSourceDataFetched(args) {
|
|
|
2124
2328
|
console.error("Invalid source token");
|
|
2125
2329
|
process.exit(1);
|
|
2126
2330
|
}
|
|
2127
|
-
const { cbk, rg, cid, b, d } = payload;
|
|
2331
|
+
const { cbk, rg, cid, b, d, cs } = payload;
|
|
2128
2332
|
const destPath = path__namespace.join(rg, d);
|
|
2129
2333
|
fs__namespace.mkdirSync(path__namespace.dirname(destPath), { recursive: true });
|
|
2130
2334
|
fs__namespace.renameSync(tmpFile, destPath);
|
|
@@ -2138,7 +2342,7 @@ function cmdSourceDataFetched(args) {
|
|
|
2138
2342
|
appendEventToJournal(rg, {
|
|
2139
2343
|
type: "task-progress",
|
|
2140
2344
|
taskName: cbkDecoded.taskName,
|
|
2141
|
-
update: { bindTo: b, fetchedAt,
|
|
2345
|
+
update: { bindTo: b, outputFile: d, fetchedAt, sourceChecksum: cs },
|
|
2142
2346
|
timestamp: fetchedAt
|
|
2143
2347
|
});
|
|
2144
2348
|
void processAccumulatedEventsInfinitePass(rg);
|
|
@@ -2157,7 +2361,7 @@ function cmdSourceDataFetchFailure(args) {
|
|
|
2157
2361
|
console.error("Invalid source token");
|
|
2158
2362
|
process.exit(1);
|
|
2159
2363
|
}
|
|
2160
|
-
const { cbk, rg, cid, b } = payload;
|
|
2364
|
+
const { cbk, rg, cid, b, d, cs } = payload;
|
|
2161
2365
|
console.log(`[source-data-fetch-failure] ${cid}.${b}: ${reason}`);
|
|
2162
2366
|
const cbkDecoded = decodeCallbackToken2(cbk);
|
|
2163
2367
|
if (!cbkDecoded) {
|
|
@@ -2168,7 +2372,7 @@ function cmdSourceDataFetchFailure(args) {
|
|
|
2168
2372
|
appendEventToJournal(rg, {
|
|
2169
2373
|
type: "task-progress",
|
|
2170
2374
|
taskName: cbkDecoded.taskName,
|
|
2171
|
-
update: { bindTo: b, failure: true, reason },
|
|
2375
|
+
update: { bindTo: b, outputFile: d, failure: true, reason, sourceChecksum: cs },
|
|
2172
2376
|
timestamp
|
|
2173
2377
|
});
|
|
2174
2378
|
void processAccumulatedEventsInfinitePass(rg);
|
|
@@ -2177,11 +2381,14 @@ function cmdRunSources(args) {
|
|
|
2177
2381
|
const cardIdx = args.indexOf("--card");
|
|
2178
2382
|
const tokenIdx = args.indexOf("--token");
|
|
2179
2383
|
const rgIdx = args.indexOf("--rg");
|
|
2384
|
+
const sourceChecksumsIdx = args.indexOf("--source-checksums");
|
|
2180
2385
|
const cardFilePath = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
2181
2386
|
const callbackToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
2182
2387
|
const boardDir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
2388
|
+
const sourceChecksumsJson = sourceChecksumsIdx !== -1 ? args[sourceChecksumsIdx + 1] : void 0;
|
|
2389
|
+
const sourceChecksums = sourceChecksumsJson ? JSON.parse(sourceChecksumsJson) : void 0;
|
|
2183
2390
|
if (!cardFilePath || !callbackToken || !boardDir) {
|
|
2184
|
-
console.error("Usage: board-live-cards run-sources-internal --card <path> --token <token> --rg <dir>");
|
|
2391
|
+
console.error("Usage: board-live-cards run-sources-internal --card <path> --token <token> --rg <dir> [--source-checksums <json>]");
|
|
2185
2392
|
process.exit(1);
|
|
2186
2393
|
}
|
|
2187
2394
|
const card = JSON.parse(fs__namespace.readFileSync(cardFilePath, "utf-8"));
|
|
@@ -2195,12 +2402,14 @@ function cmdRunSources(args) {
|
|
|
2195
2402
|
const executorFile = path__namespace.join(boardDir, ".task-executor");
|
|
2196
2403
|
const taskExecutor = fs__namespace.existsSync(executorFile) ? fs__namespace.readFileSync(executorFile, "utf-8").trim() : void 0;
|
|
2197
2404
|
function runSource(src) {
|
|
2405
|
+
const sourceChecksumForInvoke = src.outputFile ? sourceChecksums?.[src.outputFile] : void 0;
|
|
2198
2406
|
const sourceToken = encodeSourceToken({
|
|
2199
2407
|
cbk: callbackToken,
|
|
2200
2408
|
rg: boardDir,
|
|
2201
2409
|
cid: card.id,
|
|
2202
2410
|
b: src.bindTo,
|
|
2203
|
-
d: src.outputFile ?? ""
|
|
2411
|
+
d: src.outputFile ?? "",
|
|
2412
|
+
cs: sourceChecksumForInvoke
|
|
2204
2413
|
});
|
|
2205
2414
|
function reportFailure(reason) {
|
|
2206
2415
|
invokeSourceDataFetchFailure(sourceToken, reason, (err) => {
|
|
@@ -2224,7 +2433,7 @@ function cmdRunSources(args) {
|
|
|
2224
2433
|
cwd: typeof src.cwd === "string" && src.cwd ? src.cwd : path__namespace.dirname(cardFilePath || ""),
|
|
2225
2434
|
boardDir: typeof src.boardDir === "string" && src.boardDir ? src.boardDir : boardDir
|
|
2226
2435
|
};
|
|
2227
|
-
appendTaskExecutorLog(boardDir, sourceForExecutor);
|
|
2436
|
+
appendTaskExecutorLog(boardDir, sourceForExecutor, "external-task-executor");
|
|
2228
2437
|
fs__namespace.writeFileSync(inFile, JSON.stringify(sourceForExecutor, null, 2), "utf-8");
|
|
2229
2438
|
console.log(`[run-sources-internal] task-executor: ${taskExecutor} run-source-fetch --in ${inFile} --out ${outFile2} --err ${errFile}`);
|
|
2230
2439
|
try {
|
|
@@ -2262,6 +2471,12 @@ function cmdRunSources(args) {
|
|
|
2262
2471
|
const timeout = src.timeout ?? 12e4;
|
|
2263
2472
|
const sourceCwd = typeof src.cwd === "string" ? src.cwd : path__namespace.dirname(cardFilePath || "");
|
|
2264
2473
|
const sourceBoardDir = typeof src.boardDir === "string" ? src.boardDir : boardDir;
|
|
2474
|
+
const sourceForBuiltInExecutor = {
|
|
2475
|
+
...src,
|
|
2476
|
+
cwd: sourceCwd,
|
|
2477
|
+
boardDir: sourceBoardDir
|
|
2478
|
+
};
|
|
2479
|
+
appendTaskExecutorLog(boardDir, sourceForBuiltInExecutor, "built-in-run-source-fetch");
|
|
2265
2480
|
const cmdParts = splitCommandLine(src.cli);
|
|
2266
2481
|
if (cmdParts.length === 0) {
|
|
2267
2482
|
const errMsg = "source.cli command is empty";
|
|
@@ -2270,8 +2485,7 @@ function cmdRunSources(args) {
|
|
|
2270
2485
|
return;
|
|
2271
2486
|
}
|
|
2272
2487
|
const rawCmd = cmdParts[0];
|
|
2273
|
-
const cmd =
|
|
2274
|
-
const cliArgs = cmdParts.slice(1);
|
|
2488
|
+
const { cmd, args: cliArgs } = resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
2275
2489
|
let stdout;
|
|
2276
2490
|
try {
|
|
2277
2491
|
stdout = execCommandSync(cmd, cliArgs, {
|
|
@@ -2298,6 +2512,181 @@ function cmdRunSources(args) {
|
|
|
2298
2512
|
runSource(src);
|
|
2299
2513
|
}
|
|
2300
2514
|
}
|
|
2515
|
+
function cmdTaskProgress(args) {
|
|
2516
|
+
const rgIdx = args.indexOf("--rg");
|
|
2517
|
+
const tokenIdx = args.indexOf("--token");
|
|
2518
|
+
const updateIdx = args.indexOf("--update");
|
|
2519
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
2520
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
2521
|
+
const updateJson = updateIdx !== -1 ? args[updateIdx + 1] : "{}";
|
|
2522
|
+
if (!dir || !token) {
|
|
2523
|
+
console.error("Usage: board-live-cards task-progress --rg <dir> --token <token> [--update <json>]");
|
|
2524
|
+
process.exit(1);
|
|
2525
|
+
}
|
|
2526
|
+
const decoded = decodeCallbackToken2(token);
|
|
2527
|
+
if (!decoded) {
|
|
2528
|
+
console.error("Invalid callback token");
|
|
2529
|
+
process.exit(1);
|
|
2530
|
+
}
|
|
2531
|
+
const update = updateJson ? JSON.parse(updateJson) : {};
|
|
2532
|
+
appendEventToJournal(dir, {
|
|
2533
|
+
type: "task-progress",
|
|
2534
|
+
taskName: decoded.taskName,
|
|
2535
|
+
update,
|
|
2536
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2537
|
+
});
|
|
2538
|
+
void processAccumulatedEventsInfinitePass(dir);
|
|
2539
|
+
}
|
|
2540
|
+
function cmdRunInference(args) {
|
|
2541
|
+
const inIdx = args.indexOf("--in");
|
|
2542
|
+
const tokenIdx = args.indexOf("--token");
|
|
2543
|
+
const inFile = inIdx !== -1 ? args[inIdx + 1] : void 0;
|
|
2544
|
+
const inferenceToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
2545
|
+
if (!inFile || !inferenceToken) {
|
|
2546
|
+
console.error("Usage: board-live-cards run-inference-internal --in <input.json> --token <inference-token>");
|
|
2547
|
+
process.exit(1);
|
|
2548
|
+
}
|
|
2549
|
+
const decodedToken = decodeSourceToken(inferenceToken);
|
|
2550
|
+
if (!decodedToken) {
|
|
2551
|
+
console.error("Invalid inference token");
|
|
2552
|
+
process.exit(1);
|
|
2553
|
+
}
|
|
2554
|
+
const callbackToken = decodedToken.cbk;
|
|
2555
|
+
const boardDir = decodedToken.rg;
|
|
2556
|
+
const cbkDecoded = decodeCallbackToken2(callbackToken);
|
|
2557
|
+
if (!cbkDecoded) {
|
|
2558
|
+
console.error("Invalid callback token embedded in inference token");
|
|
2559
|
+
process.exit(1);
|
|
2560
|
+
}
|
|
2561
|
+
function spawnInferenceDone(tmpFile) {
|
|
2562
|
+
const { cmd, args: cliArgs } = getCliInvocation("inference-done", ["--tmp", tmpFile, "--token", inferenceToken]);
|
|
2563
|
+
spawnDetachedCommand(cmd, cliArgs);
|
|
2564
|
+
}
|
|
2565
|
+
function spawnInferenceDoneError(reason) {
|
|
2566
|
+
const tmpFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-err-${Date.now()}.json`);
|
|
2567
|
+
fs__namespace.writeFileSync(tmpFile, JSON.stringify({ isTaskCompleted: false, reason }), "utf-8");
|
|
2568
|
+
spawnInferenceDone(tmpFile);
|
|
2569
|
+
}
|
|
2570
|
+
if (!fs__namespace.existsSync(inFile)) {
|
|
2571
|
+
spawnInferenceDoneError(`inference input not found: ${inFile}`);
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
const adapterFile = path__namespace.join(boardDir, INFERENCE_ADAPTER_FILE);
|
|
2575
|
+
const inferenceAdapter = fs__namespace.existsSync(adapterFile) ? fs__namespace.readFileSync(adapterFile, "utf-8").trim() : void 0;
|
|
2576
|
+
if (!inferenceAdapter) {
|
|
2577
|
+
spawnInferenceDoneError(`inference adapter is not configured (${INFERENCE_ADAPTER_FILE})`);
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
const outFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-out-${Date.now()}.json`);
|
|
2581
|
+
const errFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-err-${Date.now()}.txt`);
|
|
2582
|
+
const adapterParts = splitCommandLine(inferenceAdapter);
|
|
2583
|
+
if (adapterParts.length === 0) {
|
|
2584
|
+
spawnInferenceDoneError("inference adapter command is empty");
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
const adapterRawCmd = adapterParts[0];
|
|
2588
|
+
const adapterRawArgs = adapterParts.slice(1);
|
|
2589
|
+
const { cmd: adapterCmd, args: adapterArgsPrefix } = resolveCommandInvocation(adapterRawCmd, adapterRawArgs);
|
|
2590
|
+
const adapterArgs = [...adapterArgsPrefix, "run-inference", "--in", inFile, "--out", outFile, "--err", errFile];
|
|
2591
|
+
try {
|
|
2592
|
+
execCommandSync(adapterCmd, adapterArgs, {
|
|
2593
|
+
shell: false,
|
|
2594
|
+
timeout: 12e4,
|
|
2595
|
+
cwd: boardDir,
|
|
2596
|
+
env: {
|
|
2597
|
+
...process.env,
|
|
2598
|
+
BOARD_DIR: boardDir
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
} catch (err) {
|
|
2602
|
+
const reason = err.message ?? String(err);
|
|
2603
|
+
spawnInferenceDoneError(reason);
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
if (!fs__namespace.existsSync(outFile)) {
|
|
2607
|
+
const errMsg = fs__namespace.existsSync(errFile) ? fs__namespace.readFileSync(errFile, "utf-8").trim() : "inference adapter produced no output file";
|
|
2608
|
+
spawnInferenceDoneError(errMsg);
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
spawnInferenceDone(outFile);
|
|
2612
|
+
}
|
|
2613
|
+
function cmdInferenceDone(args) {
|
|
2614
|
+
const tmpIdx = args.indexOf("--tmp");
|
|
2615
|
+
const tokenIdx = args.indexOf("--token");
|
|
2616
|
+
const tmpFile = tmpIdx !== -1 ? args[tmpIdx + 1] : void 0;
|
|
2617
|
+
const inferenceToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
2618
|
+
if (!tmpFile || !inferenceToken) {
|
|
2619
|
+
console.error("Usage: board-live-cards inference-done --tmp <result.json> --token <inference-token>");
|
|
2620
|
+
process.exit(1);
|
|
2621
|
+
}
|
|
2622
|
+
const decodedToken = decodeSourceToken(inferenceToken);
|
|
2623
|
+
if (!decodedToken) {
|
|
2624
|
+
console.error("Invalid inference token");
|
|
2625
|
+
process.exit(1);
|
|
2626
|
+
}
|
|
2627
|
+
const { cbk: callbackToken, rg: dir, cs: inputChecksum } = decodedToken;
|
|
2628
|
+
const decoded = decodeCallbackToken2(callbackToken);
|
|
2629
|
+
if (!decoded) {
|
|
2630
|
+
console.error("Invalid callback token embedded in inference token");
|
|
2631
|
+
process.exit(1);
|
|
2632
|
+
}
|
|
2633
|
+
const taskName = decoded.taskName;
|
|
2634
|
+
const cardPath = lookupCardPath(dir, taskName);
|
|
2635
|
+
if (!cardPath) {
|
|
2636
|
+
console.error(`Card file for task "${taskName}" not found in inventory`);
|
|
2637
|
+
process.exit(1);
|
|
2638
|
+
}
|
|
2639
|
+
let result = {};
|
|
2640
|
+
if (fs__namespace.existsSync(tmpFile)) {
|
|
2641
|
+
try {
|
|
2642
|
+
result = JSON.parse(fs__namespace.readFileSync(tmpFile, "utf-8").trim());
|
|
2643
|
+
} catch (err) {
|
|
2644
|
+
result = { isTaskCompleted: false, reason: `failed to parse inference result: ${err instanceof Error ? err.message : String(err)}` };
|
|
2645
|
+
}
|
|
2646
|
+
try {
|
|
2647
|
+
fs__namespace.unlinkSync(tmpFile);
|
|
2648
|
+
} catch {
|
|
2649
|
+
}
|
|
2650
|
+
} else {
|
|
2651
|
+
result = { isTaskCompleted: false, reason: `inference result file not found: ${tmpFile}` };
|
|
2652
|
+
}
|
|
2653
|
+
const isTaskCompletedFlag = result.isTaskCompleted === true;
|
|
2654
|
+
const inferenceCompletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2655
|
+
const card = JSON.parse(fs__namespace.readFileSync(cardPath, "utf-8"));
|
|
2656
|
+
if (!card.card_data) card.card_data = {};
|
|
2657
|
+
const cardData = card.card_data;
|
|
2658
|
+
const existingInference = cardData.llm_task_completion_inference && typeof cardData.llm_task_completion_inference === "object" ? cardData.llm_task_completion_inference : {};
|
|
2659
|
+
cardData.llm_task_completion_inference = {
|
|
2660
|
+
...existingInference,
|
|
2661
|
+
isTaskCompleted: isTaskCompletedFlag,
|
|
2662
|
+
reason: typeof result.reason === "string" ? result.reason : "",
|
|
2663
|
+
evidence: typeof result.evidence === "string" ? result.evidence : "",
|
|
2664
|
+
inferenceCompletedAt
|
|
2665
|
+
};
|
|
2666
|
+
fs__namespace.writeFileSync(cardPath, JSON.stringify(card, null, 2), "utf-8");
|
|
2667
|
+
const runtimePath2 = path__namespace.join(dir, `${taskName}.runtime.json`);
|
|
2668
|
+
let runtime = { _sources: {} };
|
|
2669
|
+
if (fs__namespace.existsSync(runtimePath2)) {
|
|
2670
|
+
try {
|
|
2671
|
+
runtime = JSON.parse(fs__namespace.readFileSync(runtimePath2, "utf-8"));
|
|
2672
|
+
} catch {
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
const inferenceEntry = runtime._inferenceEntry ?? {};
|
|
2676
|
+
runtime._inferenceEntry = nextEntryAfterFetchDelivery(inferenceEntry, inferenceCompletedAt);
|
|
2677
|
+
fs__namespace.writeFileSync(runtimePath2, JSON.stringify(runtime, null, 2), "utf-8");
|
|
2678
|
+
appendEventToJournal(dir, {
|
|
2679
|
+
type: "task-progress",
|
|
2680
|
+
taskName,
|
|
2681
|
+
update: {
|
|
2682
|
+
kind: "inference-done",
|
|
2683
|
+
isTaskCompleted: isTaskCompletedFlag,
|
|
2684
|
+
inputChecksum
|
|
2685
|
+
},
|
|
2686
|
+
timestamp: inferenceCompletedAt
|
|
2687
|
+
});
|
|
2688
|
+
void processAccumulatedEventsInfinitePass(dir);
|
|
2689
|
+
}
|
|
2301
2690
|
function cmdRunSourceFetch(args) {
|
|
2302
2691
|
const inIdx = args.indexOf("--in");
|
|
2303
2692
|
const outIdx = args.indexOf("--out");
|
|
@@ -2343,8 +2732,7 @@ function cmdRunSourceFetch(args) {
|
|
|
2343
2732
|
process.exit(1);
|
|
2344
2733
|
}
|
|
2345
2734
|
const rawCmd = cmdParts[0];
|
|
2346
|
-
const cmd =
|
|
2347
|
-
const cliArgs = cmdParts.slice(1);
|
|
2735
|
+
const { cmd, args: cliArgs } = resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
2348
2736
|
let stdout;
|
|
2349
2737
|
try {
|
|
2350
2738
|
stdout = execCommandSync(cmd, cliArgs, {
|
|
@@ -2576,20 +2964,183 @@ async function cli(argv) {
|
|
|
2576
2964
|
return cmdTaskCompleted(rest);
|
|
2577
2965
|
case "task-failed":
|
|
2578
2966
|
return cmdTaskFailed(rest);
|
|
2967
|
+
case "task-progress":
|
|
2968
|
+
return cmdTaskProgress(rest);
|
|
2579
2969
|
case "source-data-fetched":
|
|
2580
2970
|
return cmdSourceDataFetched(rest);
|
|
2581
2971
|
case "source-data-fetch-failure":
|
|
2582
2972
|
return cmdSourceDataFetchFailure(rest);
|
|
2583
2973
|
case "run-sources-internal":
|
|
2584
2974
|
return cmdRunSources(rest);
|
|
2975
|
+
case "run-inference-internal":
|
|
2976
|
+
return cmdRunInference(rest);
|
|
2977
|
+
case "inference-done":
|
|
2978
|
+
return cmdInferenceDone(rest);
|
|
2585
2979
|
case "run-source-fetch":
|
|
2586
2980
|
return cmdRunSourceFetch(rest);
|
|
2981
|
+
case "probe-source":
|
|
2982
|
+
return await cmdProbeSource(rest);
|
|
2587
2983
|
case "process-accumulated-events":
|
|
2588
2984
|
return await cmdTryDrain(rest);
|
|
2589
2985
|
default:
|
|
2590
2986
|
throw new Error(`Unknown command: ${cmd ?? "(none)"}`);
|
|
2591
2987
|
}
|
|
2592
2988
|
}
|
|
2989
|
+
async function cmdProbeSource(args) {
|
|
2990
|
+
const cardIdx = args.indexOf("--card");
|
|
2991
|
+
const sourceIdxArg = args.indexOf("--source-idx");
|
|
2992
|
+
const sourceBindArg = args.indexOf("--source-bind");
|
|
2993
|
+
const mockReqIdx = args.indexOf("--mock-requires");
|
|
2994
|
+
const rgIdx = args.indexOf("--rg");
|
|
2995
|
+
const outIdx = args.indexOf("--out");
|
|
2996
|
+
const cardFilePath = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
2997
|
+
const sourceIdxVal = sourceIdxArg !== -1 ? parseInt(args[sourceIdxArg + 1], 10) : 0;
|
|
2998
|
+
const sourceBindVal = sourceBindArg !== -1 ? args[sourceBindArg + 1] : void 0;
|
|
2999
|
+
const mockReqRaw = mockReqIdx !== -1 ? args[mockReqIdx + 1] : void 0;
|
|
3000
|
+
const boardDirArg = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
3001
|
+
const outFile = outIdx !== -1 ? args[outIdx + 1] : void 0;
|
|
3002
|
+
if (!cardFilePath) {
|
|
3003
|
+
console.error("Usage: board-live-cards probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>] [--mock-requires <json>] [--rg <boardDir>] [--out <result.json>]");
|
|
3004
|
+
process.exit(1);
|
|
3005
|
+
}
|
|
3006
|
+
let card;
|
|
3007
|
+
try {
|
|
3008
|
+
card = JSON.parse(fs__namespace.readFileSync(path__namespace.resolve(cardFilePath), "utf-8"));
|
|
3009
|
+
} catch (e) {
|
|
3010
|
+
console.error(`[probe-source] Cannot read card: ${e.message}`);
|
|
3011
|
+
process.exit(1);
|
|
3012
|
+
}
|
|
3013
|
+
const sources = card.sources ?? [];
|
|
3014
|
+
if (sources.length === 0) {
|
|
3015
|
+
console.error(`[probe-source] Card "${card.id}" has no sources`);
|
|
3016
|
+
process.exit(1);
|
|
3017
|
+
}
|
|
3018
|
+
let sourceIdx;
|
|
3019
|
+
if (sourceBindVal) {
|
|
3020
|
+
sourceIdx = sources.findIndex((s) => s.bindTo === sourceBindVal);
|
|
3021
|
+
if (sourceIdx === -1) {
|
|
3022
|
+
console.error(`[probe-source] No source with bindTo="${sourceBindVal}" in card "${card.id}"`);
|
|
3023
|
+
process.exit(1);
|
|
3024
|
+
}
|
|
3025
|
+
} else {
|
|
3026
|
+
sourceIdx = sourceIdxVal;
|
|
3027
|
+
if (isNaN(sourceIdx) || sourceIdx < 0 || sourceIdx >= sources.length) {
|
|
3028
|
+
console.error(`[probe-source] --source-idx ${sourceIdxVal} out of range (card has ${sources.length} source(s))`);
|
|
3029
|
+
process.exit(1);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
const sourceDef = sources[sourceIdx];
|
|
3033
|
+
const cardDir = path__namespace.resolve(path__namespace.dirname(cardFilePath));
|
|
3034
|
+
const boardDir = boardDirArg ? path__namespace.resolve(boardDirArg) : cardDir;
|
|
3035
|
+
let mockRequires = {};
|
|
3036
|
+
if (mockReqRaw) {
|
|
3037
|
+
const raw = mockReqRaw.startsWith("@") ? fs__namespace.readFileSync(path__namespace.resolve(mockReqRaw.slice(1)), "utf-8") : mockReqRaw;
|
|
3038
|
+
try {
|
|
3039
|
+
mockRequires = JSON.parse(raw);
|
|
3040
|
+
} catch (e) {
|
|
3041
|
+
console.error(`[probe-source] --mock-requires is not valid JSON: ${e.message}`);
|
|
3042
|
+
process.exit(1);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
const executorFile = path__namespace.join(boardDir, ".task-executor");
|
|
3046
|
+
const taskExecutor = fs__namespace.existsSync(executorFile) ? fs__namespace.readFileSync(executorFile, "utf-8").trim() : void 0;
|
|
3047
|
+
const inPayload = {
|
|
3048
|
+
...sourceDef,
|
|
3049
|
+
cwd: typeof sourceDef.cwd === "string" && sourceDef.cwd ? sourceDef.cwd : cardDir,
|
|
3050
|
+
boardDir: typeof sourceDef.boardDir === "string" && sourceDef.boardDir ? sourceDef.boardDir : boardDir,
|
|
3051
|
+
_requires: mockRequires,
|
|
3052
|
+
_sourcesData: {},
|
|
3053
|
+
_computed_values: {}
|
|
3054
|
+
};
|
|
3055
|
+
const sourceKind = sourceDef.chartApi ? "chartApi" : sourceDef.http ? "http" : sourceDef.copilot || sourceDef.prompt_template ? "copilot" : sourceDef.cli ? "cli" : "mock";
|
|
3056
|
+
console.log(`[probe-source] card: ${card.id}`);
|
|
3057
|
+
console.log(`[probe-source] source[${sourceIdx}]: bindTo="${sourceDef.bindTo}" kind=${sourceKind}`);
|
|
3058
|
+
console.log(`[probe-source] _requires: ${JSON.stringify(mockRequires)}`);
|
|
3059
|
+
console.log(`[probe-source] executor: ${taskExecutor ?? "built-in (source.cli only)"}`);
|
|
3060
|
+
console.log(`[probe-source] running fetch...`);
|
|
3061
|
+
const ts = Date.now();
|
|
3062
|
+
const inFile = path__namespace.join(os__namespace.tmpdir(), `probe-in-${sourceDef.bindTo}-${ts}.json`);
|
|
3063
|
+
const tmpOut = path__namespace.join(os__namespace.tmpdir(), `probe-out-${sourceDef.bindTo}-${ts}.json`);
|
|
3064
|
+
const errFile = path__namespace.join(os__namespace.tmpdir(), `probe-err-${sourceDef.bindTo}-${ts}.txt`);
|
|
3065
|
+
fs__namespace.writeFileSync(inFile, JSON.stringify(inPayload, null, 2), "utf-8");
|
|
3066
|
+
let passed = false;
|
|
3067
|
+
let errorMsg;
|
|
3068
|
+
let resultRaw;
|
|
3069
|
+
try {
|
|
3070
|
+
if (taskExecutor) {
|
|
3071
|
+
execCommandSync(taskExecutor, ["run-source-fetch", "--in", inFile, "--out", tmpOut, "--err", errFile], {
|
|
3072
|
+
shell: true,
|
|
3073
|
+
timeout: sourceDef.timeout ?? 3e4
|
|
3074
|
+
});
|
|
3075
|
+
} else {
|
|
3076
|
+
if (!inPayload.cli) {
|
|
3077
|
+
throw new Error("No task-executor registered and source has no cli field \u2014 cannot probe with built-in executor");
|
|
3078
|
+
}
|
|
3079
|
+
const cmdParts = splitCommandLine(inPayload.cli);
|
|
3080
|
+
const rawCmd = cmdParts[0];
|
|
3081
|
+
const { cmd, args: cliArgs } = resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
3082
|
+
const stdout = execCommandSync(cmd, cliArgs, {
|
|
3083
|
+
shell: false,
|
|
3084
|
+
encoding: "utf-8",
|
|
3085
|
+
timeout: sourceDef.timeout ?? 3e4,
|
|
3086
|
+
cwd: inPayload.cwd
|
|
3087
|
+
});
|
|
3088
|
+
fs__namespace.writeFileSync(tmpOut, stdout.trim(), "utf-8");
|
|
3089
|
+
}
|
|
3090
|
+
passed = fs__namespace.existsSync(tmpOut);
|
|
3091
|
+
if (passed) {
|
|
3092
|
+
resultRaw = fs__namespace.readFileSync(tmpOut, "utf-8");
|
|
3093
|
+
} else {
|
|
3094
|
+
errorMsg = fs__namespace.existsSync(errFile) ? fs__namespace.readFileSync(errFile, "utf-8").trim() : "executor produced no output file";
|
|
3095
|
+
}
|
|
3096
|
+
} catch (e) {
|
|
3097
|
+
errorMsg = e.message ?? String(e);
|
|
3098
|
+
if (!errorMsg && fs__namespace.existsSync(errFile)) {
|
|
3099
|
+
errorMsg = fs__namespace.readFileSync(errFile, "utf-8").trim();
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
for (const f of [inFile, errFile]) {
|
|
3103
|
+
try {
|
|
3104
|
+
fs__namespace.unlinkSync(f);
|
|
3105
|
+
} catch {
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
if (passed && resultRaw !== void 0) {
|
|
3109
|
+
const resultSize = resultRaw.length;
|
|
3110
|
+
const sample = resultRaw.slice(0, 300);
|
|
3111
|
+
console.log(`[probe-source] STATUS: PROBE_PASS`);
|
|
3112
|
+
console.log(`[probe-source] result size: ${resultSize} bytes`);
|
|
3113
|
+
console.log(`[probe-source] sample: ${sample}${resultSize > 300 ? "..." : ""}`);
|
|
3114
|
+
if (outFile) {
|
|
3115
|
+
fs__namespace.writeFileSync(path__namespace.resolve(outFile), resultRaw);
|
|
3116
|
+
console.log(`[probe-source] result written to: ${outFile}`);
|
|
3117
|
+
} else {
|
|
3118
|
+
try {
|
|
3119
|
+
fs__namespace.unlinkSync(tmpOut);
|
|
3120
|
+
} catch {
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
} else {
|
|
3124
|
+
console.log(`[probe-source] STATUS: PROBE_FAIL`);
|
|
3125
|
+
if (errorMsg) console.log(`[probe-source] error: ${errorMsg}`);
|
|
3126
|
+
try {
|
|
3127
|
+
if (fs__namespace.existsSync(tmpOut)) fs__namespace.unlinkSync(tmpOut);
|
|
3128
|
+
} catch {
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
const summary = {
|
|
3132
|
+
status: passed ? "PROBE_PASS" : "PROBE_FAIL",
|
|
3133
|
+
cardId: card.id,
|
|
3134
|
+
sourceIdx,
|
|
3135
|
+
bindTo: sourceDef.bindTo,
|
|
3136
|
+
sourceKind,
|
|
3137
|
+
mockRequiresKeys: Object.keys(mockRequires),
|
|
3138
|
+
resultSizeBytes: resultRaw !== void 0 ? resultRaw.length : 0,
|
|
3139
|
+
error: errorMsg ?? void 0
|
|
3140
|
+
};
|
|
3141
|
+
console.log(`[probe-source:result] ${JSON.stringify(summary)}`);
|
|
3142
|
+
process.exit(passed ? 0 : 1);
|
|
3143
|
+
}
|
|
2593
3144
|
function cmdHelp() {
|
|
2594
3145
|
console.log(`
|
|
2595
3146
|
board-live-cards-cli \u2014 LiveCards board CLI
|
|
@@ -2598,14 +3149,16 @@ USAGE
|
|
|
2598
3149
|
board-live-cards-cli <command> [options]
|
|
2599
3150
|
|
|
2600
3151
|
BOARD MANAGEMENT
|
|
2601
|
-
init <dir> [--task-executor <script>] [--runtime-out <dir>]
|
|
3152
|
+
init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]
|
|
2602
3153
|
Create a new board in <dir>.
|
|
2603
3154
|
If --task-executor is given, writes <dir>/.task-executor with the script path.
|
|
3155
|
+
If --chat-handler is given, writes <dir>/.chat-handler with the script path.
|
|
3156
|
+
If --inference-adapter is given, writes <dir>/.inference-adapter with the script path.
|
|
2604
3157
|
Writes <dir>/.runtime-out (default: <dir>/runtime-out).
|
|
2605
3158
|
Published runtime files:
|
|
2606
3159
|
<runtime-out>/board-livegraph-status.json
|
|
2607
3160
|
<runtime-out>/cards/<card-id>.computed.json
|
|
2608
|
-
Re-running init on an existing board is safe;
|
|
3161
|
+
Re-running init on an existing board is safe; handler registrations are updated.
|
|
2609
3162
|
|
|
2610
3163
|
status --rg <dir> [--json]
|
|
2611
3164
|
Read and print the published status snapshot from <runtime-out>/board-livegraph-status.json.
|
|
@@ -2645,13 +3198,16 @@ TASK CALLBACKS (called by task executor scripts)
|
|
|
2645
3198
|
task-failed --token <callbackToken> [--error <message>]
|
|
2646
3199
|
Signal task failure with an optional error message.
|
|
2647
3200
|
|
|
3201
|
+
task-progress --rg <dir> --token <callbackToken> [--update <json>]
|
|
3202
|
+
Signal task progress with optional update payload (for waiting on more evidence, etc.).
|
|
3203
|
+
|
|
2648
3204
|
SOURCE CALLBACKS (called internally by run-sources-internal)
|
|
2649
3205
|
source-data-fetched --tmp <file> --token <sourceToken>
|
|
2650
3206
|
Atomically rename <file> into the outputFile destination and record delivery
|
|
2651
|
-
|
|
3207
|
+
via journal events. Appends a task-progress event to re-invoke the card handler.
|
|
2652
3208
|
|
|
2653
3209
|
source-data-fetch-failure --token <sourceToken> [--reason <message>]
|
|
2654
|
-
Record a source fetch failure
|
|
3210
|
+
Record a source fetch failure via journal events and append a task-progress event.
|
|
2655
3211
|
|
|
2656
3212
|
INTERNAL COMMANDS
|
|
2657
3213
|
process-accumulated-events --rg <dir>
|
|
@@ -2666,7 +3222,7 @@ INTERNAL COMMANDS
|
|
|
2666
3222
|
3) lock stays healthy,
|
|
2667
3223
|
4) event production eventually quiesces.
|
|
2668
3224
|
|
|
2669
|
-
run-sources-internal
|
|
3225
|
+
run-sources-internal --card <card.json> --token <callbackToken> --rg <dir>
|
|
2670
3226
|
Execute all source[] entries for a card, then report delivery or failure.
|
|
2671
3227
|
(Internal command \u2014 invoked by the card-handler. Not intended for direct use.)
|
|
2672
3228
|
|
|
@@ -2679,6 +3235,34 @@ INTERNAL COMMANDS
|
|
|
2679
3235
|
Execute a source definition. Board-live-cards reads source.cli and executes it.
|
|
2680
3236
|
Writes result to --out. Presence of --out after exit indicates success.
|
|
2681
3237
|
|
|
3238
|
+
probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>]
|
|
3239
|
+
[--mock-requires <json>] [--rg <boardDir>] [--out <result.json>]
|
|
3240
|
+
Validate that a card source can be fetched successfully.
|
|
3241
|
+
Reads the card file, extracts the chosen source (default: index 0), builds the
|
|
3242
|
+
run-source-fetch --in payload with the supplied _requires data, invokes the
|
|
3243
|
+
registered task-executor (or built-in executor for source.cli), and reports pass/fail.
|
|
3244
|
+
--mock-requires: JSON string (or @file.json) providing the _requires token values
|
|
3245
|
+
the source needs. Craft the minimal payload that exercises the
|
|
3246
|
+
source \u2014 e.g. '{"holdings":[{"ticker":"AAPL","quantity":10}]}'.
|
|
3247
|
+
If omitted, _requires is passed as empty ({}).
|
|
3248
|
+
--source-idx: 0-based index into card.sources[]. Default: 0.
|
|
3249
|
+
--source-bind: Select source by its bindTo name instead of index.
|
|
3250
|
+
--rg: Board directory used to find .task-executor. Defaults to the
|
|
3251
|
+
directory containing the card file.
|
|
3252
|
+
--out: Optional path to write the raw fetch result JSON.
|
|
3253
|
+
Prints a structured report ending with a [probe-source:result] JSON line.
|
|
3254
|
+
Exits 0 on PROBE_PASS, 1 on PROBE_FAIL.
|
|
3255
|
+
|
|
3256
|
+
run-inference-internal --in <input.json> --token <inferenceToken>
|
|
3257
|
+
Execute inference via registered .inference-adapter and forward result to inference-done.
|
|
3258
|
+
inferenceToken encodes boardDir (rg), cardId (cid), callbackToken (cbk), checksum (cs).
|
|
3259
|
+
(Internal command \u2014 invoked by the card-handler when custom completion rule is used.)
|
|
3260
|
+
|
|
3261
|
+
inference-done --tmp <result.json> --token <inferenceToken>
|
|
3262
|
+
Persist llm_task_completion_inference on the card and append a task-progress event.
|
|
3263
|
+
Reads boardDir/callbackToken/checksum from decoded inferenceToken; deletes --tmp file after reading.
|
|
3264
|
+
(Internal command \u2014 invoked by run-inference-internal.)
|
|
3265
|
+
|
|
2682
3266
|
RUN-SOURCE-FETCH PROTOCOL
|
|
2683
3267
|
External task-executors implement:
|
|
2684
3268
|
<executor> run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]
|
|
@@ -2715,6 +3299,7 @@ EXAMPLES
|
|
|
2715
3299
|
board-live-cards-cli add-cards --rg ./my-board --card cards/prices.json
|
|
2716
3300
|
board-live-cards-cli status --rg ./my-board
|
|
2717
3301
|
board-live-cards-cli retrigger --rg ./my-board --task price-fetch
|
|
3302
|
+
board-live-cards-cli probe-source --card cards/card-market-prices.json --source-idx 0 --rg ./my-board --mock-requires '{"holdings":[{"ticker":"AAPL","quantity":10}]}'
|
|
2718
3303
|
`.trimStart());
|
|
2719
3304
|
}
|
|
2720
3305
|
var isMain = process.argv[1] && path__namespace.resolve(process.argv[1]) === path__namespace.resolve(new URL((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('board-live-cards-cli.cjs', document.baseURI).href))).pathname.replace(/^\/([A-Z]:)/, "$1"));
|
|
@@ -2732,14 +3317,18 @@ exports.appendEventToJournal = appendEventToJournal;
|
|
|
2732
3317
|
exports.buildCardInventoryIndex = buildCardInventoryIndex;
|
|
2733
3318
|
exports.cli = cli;
|
|
2734
3319
|
exports.createBoardReactiveGraph = createBoardReactiveGraph;
|
|
3320
|
+
exports.decideSourceAction = decideSourceAction;
|
|
2735
3321
|
exports.decodeSourceToken = decodeSourceToken;
|
|
2736
3322
|
exports.encodeSourceToken = encodeSourceToken;
|
|
2737
3323
|
exports.getUndrainedEntries = getUndrainedEntries;
|
|
2738
3324
|
exports.initBoard = initBoard;
|
|
3325
|
+
exports.isSourceInFlight = isSourceInFlight;
|
|
2739
3326
|
exports.liveCardToTaskConfig = liveCardToTaskConfig;
|
|
2740
3327
|
exports.loadBoard = loadBoard;
|
|
2741
3328
|
exports.loadBoardEnvelope = loadBoardEnvelope;
|
|
2742
3329
|
exports.lookupCardPath = lookupCardPath;
|
|
3330
|
+
exports.nextEntryAfterFetchDelivery = nextEntryAfterFetchDelivery;
|
|
3331
|
+
exports.nextEntryAfterFetchFailure = nextEntryAfterFetchFailure;
|
|
2743
3332
|
exports.processAccumulatedEvents = processAccumulatedEvents;
|
|
2744
3333
|
exports.processAccumulatedEventsForced = processAccumulatedEventsForced;
|
|
2745
3334
|
exports.processAccumulatedEventsInfinitePass = processAccumulatedEventsInfinitePass;
|