yaml-flow 5.2.8 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/board-livecards-server-runtime.js +15 -66
- package/browser/board-livegraph-engine.js +4 -1
- package/browser/board-livegraph-engine.js.map +1 -1
- package/browser/card-compute.js +1 -1
- package/browser/live-cards.js +178 -144
- package/browser/live-cards.schema.json +1 -1
- package/dist/board-livegraph-runtime/index.cjs +4 -1
- package/dist/board-livegraph-runtime/index.cjs.map +1 -1
- package/dist/board-livegraph-runtime/index.js +4 -1
- package/dist/board-livegraph-runtime/index.js.map +1 -1
- package/dist/card-compute/index.cjs +5 -1
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.js +5 -1
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +2416 -2113
- package/dist/cli/board-live-cards-cli.cjs.map +1 -1
- package/dist/cli/board-live-cards-cli.d.cts +59 -113
- package/dist/cli/board-live-cards-cli.d.ts +59 -113
- package/dist/cli/board-live-cards-cli.js +2413 -2109
- package/dist/cli/board-live-cards-cli.js.map +1 -1
- package/dist/continuous-event-graph/index.cjs +4 -1
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.js +4 -1
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +4 -4
- package/examples/example-board/agent-instructions-cardlayout.md +28 -0
- package/examples/example-board/agent-instructions.md +4 -5
- package/examples/example-board/cards/card-rebalance-sim.json +13 -3
- package/examples/example-board/demo-server.js +77 -11
- package/examples/example-board/demo-shell-browser.html +4 -4
- package/examples/example-board/demo-shell-with-server.html +4 -4
- package/examples/example-board/demo-task-executor.js +22 -2
- package/package.json +1 -1
- package/schema/live-cards.schema.json +1 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var fs7 = require('fs');
|
|
4
|
+
var os3 = require('os');
|
|
5
|
+
var path7 = require('path');
|
|
6
6
|
var crypto = require('crypto');
|
|
7
7
|
var child_process = require('child_process');
|
|
8
8
|
var url = require('url');
|
|
9
9
|
var fg = require('fast-glob');
|
|
10
10
|
var properLockfile = require('proper-lockfile');
|
|
11
|
-
var jsonata2 = require('jsonata');
|
|
12
11
|
var addFormats = require('ajv-formats');
|
|
12
|
+
var jsonata2 = require('jsonata');
|
|
13
13
|
|
|
14
14
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -32,12 +32,12 @@ function _interopNamespace(e) {
|
|
|
32
32
|
return Object.freeze(n);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
35
|
+
var fs7__namespace = /*#__PURE__*/_interopNamespace(fs7);
|
|
36
|
+
var os3__namespace = /*#__PURE__*/_interopNamespace(os3);
|
|
37
|
+
var path7__namespace = /*#__PURE__*/_interopNamespace(path7);
|
|
38
38
|
var fg__default = /*#__PURE__*/_interopDefault(fg);
|
|
39
|
-
var jsonata2__default = /*#__PURE__*/_interopDefault(jsonata2);
|
|
40
39
|
var addFormats__default = /*#__PURE__*/_interopDefault(addFormats);
|
|
40
|
+
var jsonata2__default = /*#__PURE__*/_interopDefault(jsonata2);
|
|
41
41
|
|
|
42
42
|
var __create = Object.create;
|
|
43
43
|
var __defProp = Object.defineProperty;
|
|
@@ -2994,7 +2994,7 @@ var require_compile = __commonJS({
|
|
|
2994
2994
|
const schOrFunc = root.refs[ref];
|
|
2995
2995
|
if (schOrFunc)
|
|
2996
2996
|
return schOrFunc;
|
|
2997
|
-
let _sch =
|
|
2997
|
+
let _sch = resolve6.call(this, root, ref);
|
|
2998
2998
|
if (_sch === void 0) {
|
|
2999
2999
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
3000
3000
|
const { schemaId } = this.opts;
|
|
@@ -3021,7 +3021,7 @@ var require_compile = __commonJS({
|
|
|
3021
3021
|
function sameSchemaEnv(s1, s2) {
|
|
3022
3022
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3023
3023
|
}
|
|
3024
|
-
function
|
|
3024
|
+
function resolve6(root, ref) {
|
|
3025
3025
|
let sch;
|
|
3026
3026
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3027
3027
|
ref = sch;
|
|
@@ -3235,8 +3235,8 @@ var require_utils = __commonJS({
|
|
|
3235
3235
|
}
|
|
3236
3236
|
return ind;
|
|
3237
3237
|
}
|
|
3238
|
-
function removeDotSegments(
|
|
3239
|
-
let input =
|
|
3238
|
+
function removeDotSegments(path8) {
|
|
3239
|
+
let input = path8;
|
|
3240
3240
|
const output = [];
|
|
3241
3241
|
let nextSlash = -1;
|
|
3242
3242
|
let len = 0;
|
|
@@ -3434,8 +3434,8 @@ var require_schemes = __commonJS({
|
|
|
3434
3434
|
wsComponent.secure = void 0;
|
|
3435
3435
|
}
|
|
3436
3436
|
if (wsComponent.resourceName) {
|
|
3437
|
-
const [
|
|
3438
|
-
wsComponent.path =
|
|
3437
|
+
const [path8, query] = wsComponent.resourceName.split("?");
|
|
3438
|
+
wsComponent.path = path8 && path8 !== "/" ? path8 : void 0;
|
|
3439
3439
|
wsComponent.query = query;
|
|
3440
3440
|
wsComponent.resourceName = void 0;
|
|
3441
3441
|
}
|
|
@@ -3593,7 +3593,7 @@ var require_fast_uri = __commonJS({
|
|
|
3593
3593
|
}
|
|
3594
3594
|
return uri;
|
|
3595
3595
|
}
|
|
3596
|
-
function
|
|
3596
|
+
function resolve6(baseURI, relativeURI, options) {
|
|
3597
3597
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3598
3598
|
const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3599
3599
|
schemelessOptions.skipEscape = true;
|
|
@@ -3820,7 +3820,7 @@ var require_fast_uri = __commonJS({
|
|
|
3820
3820
|
var fastUri = {
|
|
3821
3821
|
SCHEMES,
|
|
3822
3822
|
normalize,
|
|
3823
|
-
resolve:
|
|
3823
|
+
resolve: resolve6,
|
|
3824
3824
|
resolveComponent,
|
|
3825
3825
|
equal,
|
|
3826
3826
|
serialize,
|
|
@@ -7584,6 +7584,7 @@ var live_cards_schema_default = {
|
|
|
7584
7584
|
"badge",
|
|
7585
7585
|
"text",
|
|
7586
7586
|
"markdown",
|
|
7587
|
+
"ref",
|
|
7587
7588
|
"custom",
|
|
7588
7589
|
"actions"
|
|
7589
7590
|
]
|
|
@@ -7769,25 +7770,25 @@ function parseRootPathNamespace(pathValue) {
|
|
|
7769
7770
|
const match = ROOT_PATH_NAMESPACE_RE.exec(pathValue);
|
|
7770
7771
|
return match ? match[1] : null;
|
|
7771
7772
|
}
|
|
7772
|
-
function validateJsonataExprWithNamespaces(expr,
|
|
7773
|
+
function validateJsonataExprWithNamespaces(expr, path8, allowedNamespaces, errors) {
|
|
7773
7774
|
try {
|
|
7774
7775
|
jsonata2__default.default(expr);
|
|
7775
7776
|
} catch (err) {
|
|
7776
7777
|
const message = err instanceof Error ? err.message : String(err);
|
|
7777
|
-
errors.push(`${
|
|
7778
|
+
errors.push(`${path8}: invalid JSONata expression (${message})`);
|
|
7778
7779
|
return;
|
|
7779
7780
|
}
|
|
7780
7781
|
const usedNamespaces = referencedNamespaces(expr);
|
|
7781
7782
|
for (const namespace of usedNamespaces) {
|
|
7782
7783
|
if (!allowedNamespaces.has(namespace)) {
|
|
7783
|
-
errors.push(`${
|
|
7784
|
+
errors.push(`${path8}: disallowed namespace "${namespace}" in expression`);
|
|
7784
7785
|
}
|
|
7785
7786
|
}
|
|
7786
7787
|
}
|
|
7787
|
-
function walkViewPathReferences(value,
|
|
7788
|
+
function walkViewPathReferences(value, path8, errors) {
|
|
7788
7789
|
if (Array.isArray(value)) {
|
|
7789
7790
|
value.forEach((entry, index) => {
|
|
7790
|
-
walkViewPathReferences(entry, `${
|
|
7791
|
+
walkViewPathReferences(entry, `${path8}/${index}`, errors);
|
|
7791
7792
|
});
|
|
7792
7793
|
return;
|
|
7793
7794
|
}
|
|
@@ -7795,14 +7796,14 @@ function walkViewPathReferences(value, path2, errors) {
|
|
|
7795
7796
|
const rootNamespace = parseRootPathNamespace(value);
|
|
7796
7797
|
if (!rootNamespace) return;
|
|
7797
7798
|
if (!(/* @__PURE__ */ new Set(["card_data", "requires", "fetched_sources", "computed_values"])).has(rootNamespace)) {
|
|
7798
|
-
errors.push(`${
|
|
7799
|
+
errors.push(`${path8}: disallowed namespace "${rootNamespace}" in view reference`);
|
|
7799
7800
|
}
|
|
7800
7801
|
return;
|
|
7801
7802
|
}
|
|
7802
7803
|
if (!value || typeof value !== "object") return;
|
|
7803
7804
|
const record = value;
|
|
7804
7805
|
for (const [key, next] of Object.entries(record)) {
|
|
7805
|
-
walkViewPathReferences(next, `${
|
|
7806
|
+
walkViewPathReferences(next, `${path8}/${key}`, errors);
|
|
7806
7807
|
}
|
|
7807
7808
|
}
|
|
7808
7809
|
function getValidator() {
|
|
@@ -7816,8 +7817,8 @@ function validateLiveCardSchema(node) {
|
|
|
7816
7817
|
const validate = getValidator();
|
|
7817
7818
|
const valid = validate(node);
|
|
7818
7819
|
const errors = (validate.errors ?? []).map((e) => {
|
|
7819
|
-
const
|
|
7820
|
-
return `${
|
|
7820
|
+
const path8 = e.instancePath || "/";
|
|
7821
|
+
return `${path8}: ${e.message ?? "unknown error"}`;
|
|
7821
7822
|
});
|
|
7822
7823
|
if (node && typeof node === "object" && !Array.isArray(node)) {
|
|
7823
7824
|
const source_defs = node.source_defs;
|
|
@@ -7916,1095 +7917,1696 @@ function validateLiveCardDefinition(node) {
|
|
|
7916
7917
|
if (!runtime.ok) return { ok: false, errors: runtime.errors };
|
|
7917
7918
|
return { ok: true, errors: [] };
|
|
7918
7919
|
}
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
fetched_sources: node._sourcesData,
|
|
7949
|
-
computed_values: node.computed_values
|
|
7950
|
-
};
|
|
7951
|
-
for (const step of node.compute) {
|
|
7952
|
-
try {
|
|
7953
|
-
const val = await jsonata2__default.default(step.expr).evaluate(ctx);
|
|
7954
|
-
deepSet(node.computed_values, step.bindTo, val);
|
|
7955
|
-
ctx.computed_values = node.computed_values;
|
|
7956
|
-
} catch (err) {
|
|
7957
|
-
console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
|
|
7920
|
+
function createBoardCommandHandlers(deps) {
|
|
7921
|
+
function cmdInit(args) {
|
|
7922
|
+
const dir = args[0];
|
|
7923
|
+
if (!dir) {
|
|
7924
|
+
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
7925
|
+
}
|
|
7926
|
+
const teIdx = args.indexOf("--task-executor");
|
|
7927
|
+
const taskExecutor = teIdx !== -1 ? args[teIdx + 1] : void 0;
|
|
7928
|
+
const chIdx = args.indexOf("--chat-handler");
|
|
7929
|
+
const chatHandler = chIdx !== -1 ? args[chIdx + 1] : void 0;
|
|
7930
|
+
const iaIdx = args.indexOf("--inference-adapter");
|
|
7931
|
+
const inferenceAdapter = iaIdx !== -1 ? args[iaIdx + 1] : void 0;
|
|
7932
|
+
const roIdx = args.indexOf("--runtime-out");
|
|
7933
|
+
const runtimeOut = roIdx !== -1 ? args[roIdx + 1] : void 0;
|
|
7934
|
+
if (roIdx !== -1 && !runtimeOut) {
|
|
7935
|
+
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
7936
|
+
}
|
|
7937
|
+
const result = deps.initBoard(dir);
|
|
7938
|
+
if (taskExecutor) {
|
|
7939
|
+
const teExtraIdx = args.indexOf("--task-executor-extra");
|
|
7940
|
+
let teExtra;
|
|
7941
|
+
if (teExtraIdx !== -1 && args[teExtraIdx + 1]) {
|
|
7942
|
+
try {
|
|
7943
|
+
teExtra = JSON.parse(args[teExtraIdx + 1]);
|
|
7944
|
+
} catch {
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
const teConfig = { command: taskExecutor, ...teExtra ? { extra: teExtra } : {} };
|
|
7948
|
+
fs7__namespace.writeFileSync(path7__namespace.join(dir, ".task-executor"), JSON.stringify(teConfig, null, 2), "utf-8");
|
|
7958
7949
|
}
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
}
|
|
7962
|
-
async function evalExpr(expr, node) {
|
|
7963
|
-
const ctx = {
|
|
7964
|
-
card_data: node.card_data ?? {},
|
|
7965
|
-
requires: node.requires ?? {},
|
|
7966
|
-
fetched_sources: node._sourcesData ?? {},
|
|
7967
|
-
computed_values: node.computed_values ?? {}
|
|
7968
|
-
};
|
|
7969
|
-
return jsonata2__default.default(expr).evaluate(ctx);
|
|
7970
|
-
}
|
|
7971
|
-
function resolve(node, path2) {
|
|
7972
|
-
if (path2.startsWith("fetched_sources.")) {
|
|
7973
|
-
return deepGet(node._sourcesData ?? {}, path2.slice("fetched_sources.".length));
|
|
7974
|
-
}
|
|
7975
|
-
return deepGet(node, path2);
|
|
7976
|
-
}
|
|
7977
|
-
var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
|
|
7978
|
-
"metric",
|
|
7979
|
-
"table",
|
|
7980
|
-
"chart",
|
|
7981
|
-
"form",
|
|
7982
|
-
"filter",
|
|
7983
|
-
"list",
|
|
7984
|
-
"notes",
|
|
7985
|
-
"todo",
|
|
7986
|
-
"alert",
|
|
7987
|
-
"narrative",
|
|
7988
|
-
"badge",
|
|
7989
|
-
"text",
|
|
7990
|
-
"markdown",
|
|
7991
|
-
"custom"
|
|
7992
|
-
]);
|
|
7993
|
-
var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "card_data", "compute", "source_defs"]);
|
|
7994
|
-
function validateNode(node) {
|
|
7995
|
-
const errors = [];
|
|
7996
|
-
if (!node || typeof node !== "object" || Array.isArray(node)) {
|
|
7997
|
-
return { ok: false, errors: ["Node must be a non-null object"] };
|
|
7998
|
-
}
|
|
7999
|
-
const n = node;
|
|
8000
|
-
if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
|
|
8001
|
-
for (const key of Object.keys(n)) {
|
|
8002
|
-
if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
|
|
8003
|
-
}
|
|
8004
|
-
if (n.card_data == null || typeof n.card_data !== "object" || Array.isArray(n.card_data)) {
|
|
8005
|
-
errors.push("card_data: required, must be an object");
|
|
8006
|
-
}
|
|
8007
|
-
if (n.meta != null) {
|
|
8008
|
-
if (typeof n.meta !== "object" || Array.isArray(n.meta)) {
|
|
8009
|
-
errors.push("meta: must be an object");
|
|
8010
|
-
} else {
|
|
8011
|
-
const meta = n.meta;
|
|
8012
|
-
if (meta.title != null && typeof meta.title !== "string") errors.push("meta.title: must be a string");
|
|
8013
|
-
if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
|
|
7950
|
+
if (chatHandler) {
|
|
7951
|
+
fs7__namespace.writeFileSync(path7__namespace.join(dir, ".chat-handler"), chatHandler, "utf-8");
|
|
8014
7952
|
}
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
if (n.provides != null) {
|
|
8018
|
-
if (!Array.isArray(n.provides)) {
|
|
8019
|
-
errors.push("provides: must be an array of { bindTo, ref } bindings");
|
|
8020
|
-
} else {
|
|
8021
|
-
n.provides.forEach((p, i) => {
|
|
8022
|
-
if (!p || typeof p !== "object" || Array.isArray(p)) {
|
|
8023
|
-
errors.push(`provides[${i}]: must be an object with bindTo and ref`);
|
|
8024
|
-
} else {
|
|
8025
|
-
const b = p;
|
|
8026
|
-
if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
|
|
8027
|
-
if (typeof b.ref !== "string" || !b.ref) errors.push(`provides[${i}]: missing required "ref" string`);
|
|
8028
|
-
}
|
|
8029
|
-
});
|
|
7953
|
+
if (inferenceAdapter) {
|
|
7954
|
+
fs7__namespace.writeFileSync(path7__namespace.join(dir, ".inference-adapter"), inferenceAdapter, "utf-8");
|
|
8030
7955
|
}
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
7956
|
+
const runtimeOutDir = deps.configureRuntimeOutDir(dir, runtimeOut);
|
|
7957
|
+
const live = deps.loadBoard(dir);
|
|
7958
|
+
deps.writeJsonAtomic(deps.resolveStatusSnapshotPath(dir), deps.buildBoardStatusObject(dir, live));
|
|
7959
|
+
if (result === "exists") {
|
|
7960
|
+
console.log(`Board already initialized at ${path7__namespace.resolve(dir)}${taskExecutor ? ` (task-executor updated: ${taskExecutor})` : ""} (runtime-out: ${runtimeOutDir})`);
|
|
8035
7961
|
} else {
|
|
8036
|
-
|
|
8037
|
-
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
8038
|
-
errors.push(`compute[${i}]: must be a compute step object`);
|
|
8039
|
-
} else {
|
|
8040
|
-
const s = step;
|
|
8041
|
-
if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
|
|
8042
|
-
if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
|
|
8043
|
-
}
|
|
8044
|
-
});
|
|
7962
|
+
console.log(`Board initialized at ${path7__namespace.resolve(dir)}${taskExecutor ? ` (task-executor: ${taskExecutor})` : ""} (runtime-out: ${runtimeOutDir})`);
|
|
8045
7963
|
}
|
|
8046
7964
|
}
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
7965
|
+
function cmdStatus(args) {
|
|
7966
|
+
const rgIdx = args.indexOf("--rg");
|
|
7967
|
+
const asJson = args.includes("--json");
|
|
7968
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
7969
|
+
if (!dir) {
|
|
7970
|
+
console.error("Usage: board-live-cards status --rg <dir>");
|
|
7971
|
+
process.exit(1);
|
|
7972
|
+
}
|
|
7973
|
+
const statusOutPath = deps.resolveStatusSnapshotPath(dir);
|
|
7974
|
+
let statusObject;
|
|
7975
|
+
if (fs7__namespace.existsSync(statusOutPath)) {
|
|
7976
|
+
statusObject = JSON.parse(fs7__namespace.readFileSync(statusOutPath, "utf-8"));
|
|
8050
7977
|
} else {
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
7978
|
+
statusObject = deps.buildBoardStatusObject(dir, deps.loadBoard(dir));
|
|
7979
|
+
deps.writeJsonAtomic(statusOutPath, statusObject);
|
|
7980
|
+
}
|
|
7981
|
+
if (asJson) {
|
|
7982
|
+
console.log(JSON.stringify(statusObject, null, 2));
|
|
7983
|
+
return;
|
|
7984
|
+
}
|
|
7985
|
+
console.log(`Board: ${statusObject.meta.board.path}`);
|
|
7986
|
+
console.log(`Tasks: ${statusObject.summary.card_count}`);
|
|
7987
|
+
console.log("");
|
|
7988
|
+
for (const card of statusObject.cards) {
|
|
7989
|
+
const dataKeys = card.provides_runtime.join(", ");
|
|
7990
|
+
console.log(` ${card.status.padEnd(12)} ${card.name}${dataKeys ? ` \u2014 [${dataKeys}]` : ""}`);
|
|
7991
|
+
}
|
|
7992
|
+
console.log("");
|
|
7993
|
+
console.log(`Schedule: ${statusObject.summary.eligible} eligible, ${statusObject.summary.pending} pending, ${statusObject.summary.blocked} blocked, ${statusObject.summary.unresolved} unresolved`);
|
|
7994
|
+
}
|
|
7995
|
+
function cmdValidateCard(args) {
|
|
7996
|
+
const cardIdx = args.indexOf("--card");
|
|
7997
|
+
const globIdx = args.indexOf("--card-glob");
|
|
7998
|
+
const rgIdx = args.indexOf("--rg");
|
|
7999
|
+
const cardFile = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
8000
|
+
const cardGlob = globIdx !== -1 ? args[globIdx + 1] : void 0;
|
|
8001
|
+
const boardDir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8002
|
+
if (!cardFile && !cardGlob || cardFile && cardGlob) {
|
|
8003
|
+
throw new Error("Usage: board-live-cards validate-card (--card <card.json> | --card-glob <glob>) [--rg <boardDir>]");
|
|
8004
|
+
}
|
|
8005
|
+
let teConfig;
|
|
8006
|
+
if (boardDir) {
|
|
8007
|
+
teConfig = deps.readTaskExecutorConfig(boardDir);
|
|
8008
|
+
if (!teConfig) {
|
|
8009
|
+
throw new Error(`--rg specified but no .task-executor found in ${boardDir}`);
|
|
8010
|
+
}
|
|
8011
|
+
}
|
|
8012
|
+
const files = cardFile ? [path7__namespace.resolve(cardFile)] : deps.resolveCardGlobMatches(cardGlob);
|
|
8013
|
+
if (files.length === 0) {
|
|
8014
|
+
throw new Error(`No card files matched glob: ${cardGlob}`);
|
|
8015
|
+
}
|
|
8016
|
+
let failures = 0;
|
|
8017
|
+
for (const f of files) {
|
|
8018
|
+
const label = path7__namespace.relative(process.cwd(), f) || f;
|
|
8019
|
+
if (!fs7__namespace.existsSync(f)) {
|
|
8020
|
+
console.error(`FAIL ${label}: file not found`);
|
|
8021
|
+
failures++;
|
|
8022
|
+
continue;
|
|
8023
|
+
}
|
|
8024
|
+
let card;
|
|
8025
|
+
try {
|
|
8026
|
+
card = JSON.parse(fs7__namespace.readFileSync(f, "utf-8"));
|
|
8027
|
+
} catch (err) {
|
|
8028
|
+
console.error(`FAIL ${label}: invalid JSON \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
8029
|
+
failures++;
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
const result = deps.validateLiveCardDefinition(card);
|
|
8033
|
+
const sourceErrors = [];
|
|
8034
|
+
if (teConfig && Array.isArray(card.source_defs)) {
|
|
8035
|
+
for (const src of card.source_defs) {
|
|
8036
|
+
const bindTo = typeof src.bindTo === "string" ? src.bindTo : "(unknown)";
|
|
8037
|
+
const tmpFile = path7__namespace.join(os3__namespace.tmpdir(), `validate-src-${bindTo}-${Date.now()}.json`);
|
|
8038
|
+
try {
|
|
8039
|
+
fs7__namespace.writeFileSync(tmpFile, JSON.stringify(src), "utf-8");
|
|
8040
|
+
let stdout;
|
|
8041
|
+
try {
|
|
8042
|
+
stdout = deps.execCommandSync(teConfig.command, ["validate-source-def", "--in", tmpFile], { shell: true, timeout: 1e4 });
|
|
8043
|
+
} catch (execErr) {
|
|
8044
|
+
stdout = typeof execErr?.stdout === "string" ? execErr.stdout : Buffer.isBuffer(execErr?.stdout) ? execErr.stdout.toString("utf-8") : "";
|
|
8045
|
+
if (!stdout.trim()) {
|
|
8046
|
+
sourceErrors.push(`source "${bindTo}": executor validate-source-def failed \u2014 ${execErr instanceof Error ? execErr.message : String(execErr)}`);
|
|
8047
|
+
continue;
|
|
8048
|
+
}
|
|
8063
8049
|
}
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8050
|
+
const parsed = JSON.parse(stdout.trim());
|
|
8051
|
+
if (!parsed.ok && Array.isArray(parsed.errors)) {
|
|
8052
|
+
for (const error of parsed.errors) {
|
|
8053
|
+
sourceErrors.push(`source "${bindTo}": ${error}`);
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
} catch (err) {
|
|
8057
|
+
sourceErrors.push(`source "${bindTo}": executor validate-source-def failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
8058
|
+
} finally {
|
|
8059
|
+
try {
|
|
8060
|
+
fs7__namespace.unlinkSync(tmpFile);
|
|
8061
|
+
} catch {
|
|
8071
8062
|
}
|
|
8072
|
-
outputFiles.add(s.outputFile);
|
|
8073
|
-
}
|
|
8074
|
-
if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
|
|
8075
|
-
errors.push(`source_defs[${i}]: optionalForCompletionGating must be a boolean`);
|
|
8076
8063
|
}
|
|
8077
8064
|
}
|
|
8078
|
-
}
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
if (typeof n.view !== "object" || Array.isArray(n.view)) {
|
|
8083
|
-
errors.push("view: must be an object");
|
|
8084
|
-
} else {
|
|
8085
|
-
const view = n.view;
|
|
8086
|
-
if (!Array.isArray(view.elements) || view.elements.length === 0) {
|
|
8087
|
-
errors.push("view.elements: required, must be a non-empty array");
|
|
8065
|
+
}
|
|
8066
|
+
const allErrors = [...result.errors, ...sourceErrors];
|
|
8067
|
+
if (allErrors.length === 0) {
|
|
8068
|
+
console.log(`OK ${label}`);
|
|
8088
8069
|
} else {
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
if (!elem.kind || typeof elem.kind !== "string") {
|
|
8095
|
-
errors.push(`view.elements[${i}].kind: required, must be a string`);
|
|
8096
|
-
} else if (!VALID_ELEMENT_KINDS.has(elem.kind)) {
|
|
8097
|
-
errors.push(`view.elements[${i}].kind: unknown kind "${elem.kind}". Valid: ${[...VALID_ELEMENT_KINDS].join(", ")}`);
|
|
8098
|
-
}
|
|
8099
|
-
if (elem.data != null && (typeof elem.data !== "object" || Array.isArray(elem.data))) {
|
|
8100
|
-
errors.push(`view.elements[${i}].data: must be an object`);
|
|
8101
|
-
}
|
|
8102
|
-
});
|
|
8070
|
+
console.error(`FAIL ${label}:`);
|
|
8071
|
+
for (const error of allErrors) {
|
|
8072
|
+
console.error(` ${error}`);
|
|
8073
|
+
}
|
|
8074
|
+
failures++;
|
|
8103
8075
|
}
|
|
8104
|
-
if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
|
|
8105
|
-
if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
|
|
8106
8076
|
}
|
|
8077
|
+
if (failures > 0) {
|
|
8078
|
+
throw new Error(`${failures} of ${files.length} card(s) failed validation.`);
|
|
8079
|
+
}
|
|
8080
|
+
console.log(`
|
|
8081
|
+
${files.length} card(s) passed validation.`);
|
|
8082
|
+
}
|
|
8083
|
+
function cmdRemoveCard(args) {
|
|
8084
|
+
const rgIdx = args.indexOf("--rg");
|
|
8085
|
+
const idIdx = args.indexOf("--id");
|
|
8086
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8087
|
+
const cardId = idIdx !== -1 ? args[idIdx + 1] : void 0;
|
|
8088
|
+
if (!dir || !cardId) {
|
|
8089
|
+
console.error("Usage: board-live-cards remove-card --rg <dir> --id <card-id>");
|
|
8090
|
+
process.exit(1);
|
|
8091
|
+
}
|
|
8092
|
+
deps.appendEventToJournal(dir, {
|
|
8093
|
+
type: "task-removal",
|
|
8094
|
+
taskName: cardId,
|
|
8095
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8096
|
+
});
|
|
8097
|
+
void deps.processAccumulatedEventsInfinitePass(dir);
|
|
8098
|
+
console.log(`Card "${cardId}" removed.`);
|
|
8099
|
+
}
|
|
8100
|
+
function cmdRetrigger(args) {
|
|
8101
|
+
const rgIdx = args.indexOf("--rg");
|
|
8102
|
+
const taskIdx = args.indexOf("--task");
|
|
8103
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8104
|
+
const taskName = taskIdx !== -1 ? args[taskIdx + 1] : void 0;
|
|
8105
|
+
if (!dir || !taskName) {
|
|
8106
|
+
console.error("Usage: board-live-cards retrigger --rg <dir> --task <task-name>");
|
|
8107
|
+
process.exit(1);
|
|
8108
|
+
}
|
|
8109
|
+
deps.appendEventToJournal(dir, {
|
|
8110
|
+
type: "task-restart",
|
|
8111
|
+
taskName,
|
|
8112
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8113
|
+
});
|
|
8114
|
+
void deps.processAccumulatedEventsInfinitePass(dir);
|
|
8115
|
+
console.log(`Task "${taskName}" retriggered.`);
|
|
8107
8116
|
}
|
|
8108
|
-
return {
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
requires: context.requires ?? {}
|
|
8117
|
+
return {
|
|
8118
|
+
cmdInit,
|
|
8119
|
+
cmdStatus,
|
|
8120
|
+
cmdValidateCard,
|
|
8121
|
+
cmdRemoveCard,
|
|
8122
|
+
cmdRetrigger
|
|
8115
8123
|
};
|
|
8116
|
-
return Promise.all(
|
|
8117
|
-
source_defs.map(async (src) => {
|
|
8118
|
-
const _projections = {};
|
|
8119
|
-
if (src.projections && typeof src.projections === "object" && !Array.isArray(src.projections)) {
|
|
8120
|
-
for (const [key, expr] of Object.entries(src.projections)) {
|
|
8121
|
-
if (typeof expr === "string" && expr.trim().length > 0) {
|
|
8122
|
-
try {
|
|
8123
|
-
_projections[key] = await jsonata2__default.default(expr).evaluate(evalCtx);
|
|
8124
|
-
} catch {
|
|
8125
|
-
_projections[key] = void 0;
|
|
8126
|
-
}
|
|
8127
|
-
}
|
|
8128
|
-
}
|
|
8129
|
-
}
|
|
8130
|
-
return { ...src, _projections };
|
|
8131
|
-
})
|
|
8132
|
-
);
|
|
8133
8124
|
}
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
var JOURNAL_FILE = "board-journal.jsonl";
|
|
8145
|
-
var TASK_EXECUTOR_LOG_FILE = "task-executor.jsonl";
|
|
8146
|
-
var INFERENCE_ADAPTER_LOG_FILE = "inference-adapter.jsonl";
|
|
8147
|
-
var INVENTORY_FILE = "cards-inventory.jsonl";
|
|
8148
|
-
var RUNTIME_OUT_FILE = ".runtime-out";
|
|
8149
|
-
var DEFAULT_RUNTIME_OUT_DIR = "runtime-out";
|
|
8150
|
-
var RUNTIME_STATUS_FILE = "board-livegraph-status.json";
|
|
8151
|
-
var RUNTIME_CARDS_DIR = "cards";
|
|
8152
|
-
var RUNTIME_DATA_OBJECTS_DIR = "data-objects";
|
|
8153
|
-
var INFERENCE_ADAPTER_FILE = ".inference-adapter";
|
|
8154
|
-
var TASK_EXECUTOR_FILE = ".task-executor";
|
|
8155
|
-
var DEFAULT_TASK_COMPLETION_RULE = "all_required_sources_fetched";
|
|
8156
|
-
function readTaskExecutorConfig(boardDir) {
|
|
8157
|
-
const executorFile = path__namespace.join(boardDir, TASK_EXECUTOR_FILE);
|
|
8158
|
-
if (!fs__namespace.existsSync(executorFile)) return void 0;
|
|
8159
|
-
const raw = fs__namespace.readFileSync(executorFile, "utf-8").trim();
|
|
8160
|
-
if (!raw) return void 0;
|
|
8161
|
-
try {
|
|
8162
|
-
const parsed = JSON.parse(raw);
|
|
8163
|
-
if (parsed && typeof parsed === "object" && typeof parsed.command === "string") {
|
|
8164
|
-
return parsed;
|
|
8125
|
+
function createCallbackCommandHandlers(deps) {
|
|
8126
|
+
function cmdTaskCompleted(args) {
|
|
8127
|
+
const rgIdx = args.indexOf("--rg");
|
|
8128
|
+
const tokenIdx = args.indexOf("--token");
|
|
8129
|
+
const dataIdx = args.indexOf("--data");
|
|
8130
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8131
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8132
|
+
if (!dir || !token) {
|
|
8133
|
+
console.error("Usage: board-live-cards task-completed --rg <dir> --token <token> [--data <json>]");
|
|
8134
|
+
process.exit(1);
|
|
8165
8135
|
}
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
var EMPTY_CONFIG = { settings: { completion: "manual", refreshStrategy: "data-changed" }, tasks: {} };
|
|
8171
|
-
var BoardJournal = class {
|
|
8172
|
-
journalPath;
|
|
8173
|
-
lastDrainedId;
|
|
8174
|
-
constructor(journalPath, lastDrainedJournalId) {
|
|
8175
|
-
this.journalPath = journalPath;
|
|
8176
|
-
this.lastDrainedId = lastDrainedJournalId;
|
|
8177
|
-
}
|
|
8178
|
-
append(event) {
|
|
8179
|
-
const entry = { id: crypto.randomUUID(), event };
|
|
8180
|
-
fs__namespace.appendFileSync(this.journalPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
8181
|
-
}
|
|
8182
|
-
drain() {
|
|
8183
|
-
if (!fs__namespace.existsSync(this.journalPath)) return [];
|
|
8184
|
-
const content = fs__namespace.readFileSync(this.journalPath, "utf-8").trim();
|
|
8185
|
-
if (!content) return [];
|
|
8186
|
-
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
8187
|
-
let startIdx = 0;
|
|
8188
|
-
if (this.lastDrainedId) {
|
|
8189
|
-
const drainedIdx = entries.findIndex((e) => e.id === this.lastDrainedId);
|
|
8190
|
-
if (drainedIdx !== -1) startIdx = drainedIdx + 1;
|
|
8136
|
+
const decoded = deps.decodeCallbackToken(token);
|
|
8137
|
+
if (!decoded) {
|
|
8138
|
+
console.error("Invalid callback token");
|
|
8139
|
+
process.exit(1);
|
|
8191
8140
|
}
|
|
8192
|
-
const
|
|
8193
|
-
|
|
8194
|
-
|
|
8141
|
+
const data = dataIdx !== -1 ? JSON.parse(args[dataIdx + 1]) : {};
|
|
8142
|
+
deps.writeRuntimeDataObjects(dir, data);
|
|
8143
|
+
deps.appendEventToJournal(dir, {
|
|
8144
|
+
type: "task-completed",
|
|
8145
|
+
taskName: decoded.taskName,
|
|
8146
|
+
data,
|
|
8147
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8148
|
+
});
|
|
8149
|
+
void deps.processAccumulatedEventsForced(dir);
|
|
8150
|
+
console.log("Task completed.");
|
|
8151
|
+
}
|
|
8152
|
+
function cmdTaskFailed(args) {
|
|
8153
|
+
const rgIdx = args.indexOf("--rg");
|
|
8154
|
+
const tokenIdx = args.indexOf("--token");
|
|
8155
|
+
const errorIdx = args.indexOf("--error");
|
|
8156
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8157
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8158
|
+
const errorMsg = errorIdx !== -1 ? args[errorIdx + 1] : "unknown error";
|
|
8159
|
+
if (!dir || !token) {
|
|
8160
|
+
console.error("Usage: board-live-cards task-failed --rg <dir> --token <token> [--error <message>]");
|
|
8161
|
+
process.exit(1);
|
|
8195
8162
|
}
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
const content = fs__namespace.readFileSync(this.journalPath, "utf-8").trim();
|
|
8201
|
-
if (!content) return 0;
|
|
8202
|
-
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
8203
|
-
if (!this.lastDrainedId) return entries.length;
|
|
8204
|
-
const drainedIdx = entries.findIndex((e) => e.id === this.lastDrainedId);
|
|
8205
|
-
return drainedIdx === -1 ? entries.length : entries.length - drainedIdx - 1;
|
|
8206
|
-
}
|
|
8207
|
-
get lastDrainedJournalId() {
|
|
8208
|
-
return this.lastDrainedId;
|
|
8209
|
-
}
|
|
8210
|
-
};
|
|
8211
|
-
function readCardInventory(boardDir) {
|
|
8212
|
-
const inventoryPath = path__namespace.join(boardDir, INVENTORY_FILE);
|
|
8213
|
-
if (!fs__namespace.existsSync(inventoryPath)) return [];
|
|
8214
|
-
const lines = fs__namespace.readFileSync(inventoryPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
8215
|
-
return lines.map((l) => JSON.parse(l));
|
|
8216
|
-
}
|
|
8217
|
-
function lookupCardPath(boardDir, cardId) {
|
|
8218
|
-
const entries = readCardInventory(boardDir);
|
|
8219
|
-
const entry = entries.find((e) => e.cardId === cardId);
|
|
8220
|
-
return entry?.cardFilePath ?? null;
|
|
8221
|
-
}
|
|
8222
|
-
function appendCardInventory(boardDir, entry) {
|
|
8223
|
-
const inventoryPath = path__namespace.join(boardDir, INVENTORY_FILE);
|
|
8224
|
-
const normalized = { ...entry, cardFilePath: path__namespace.resolve(entry.cardFilePath) };
|
|
8225
|
-
fs__namespace.appendFileSync(inventoryPath, JSON.stringify(normalized) + "\n");
|
|
8226
|
-
}
|
|
8227
|
-
function buildCardInventoryIndex(boardDir) {
|
|
8228
|
-
const byCardId = /* @__PURE__ */ new Map();
|
|
8229
|
-
const byCardPath = /* @__PURE__ */ new Map();
|
|
8230
|
-
for (const entry of readCardInventory(boardDir)) {
|
|
8231
|
-
const normalizedPath = path__namespace.resolve(entry.cardFilePath);
|
|
8232
|
-
const normalizedEntry = {
|
|
8233
|
-
...entry,
|
|
8234
|
-
cardFilePath: normalizedPath
|
|
8235
|
-
};
|
|
8236
|
-
const existingById = byCardId.get(entry.cardId);
|
|
8237
|
-
if (existingById && existingById.cardFilePath !== normalizedPath) {
|
|
8238
|
-
throw new Error(
|
|
8239
|
-
`Inventory invariant violation: card id "${entry.cardId}" maps to multiple files: "${existingById.cardFilePath}" and "${normalizedPath}"`
|
|
8240
|
-
);
|
|
8163
|
+
const decoded = deps.decodeCallbackToken(token);
|
|
8164
|
+
if (!decoded) {
|
|
8165
|
+
console.error("Invalid callback token");
|
|
8166
|
+
process.exit(1);
|
|
8241
8167
|
}
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
)
|
|
8168
|
+
deps.appendEventToJournal(dir, {
|
|
8169
|
+
type: "task-failed",
|
|
8170
|
+
taskName: decoded.taskName,
|
|
8171
|
+
error: errorMsg,
|
|
8172
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8173
|
+
});
|
|
8174
|
+
void deps.processAccumulatedEventsForced(dir);
|
|
8175
|
+
console.log("Task failed.");
|
|
8176
|
+
}
|
|
8177
|
+
function cmdSourceDataFetched(args) {
|
|
8178
|
+
const tmpIdx = args.indexOf("--tmp");
|
|
8179
|
+
const tokenIdx = args.indexOf("--token");
|
|
8180
|
+
const tmpFile = tmpIdx !== -1 ? args[tmpIdx + 1] : void 0;
|
|
8181
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8182
|
+
if (!tmpFile || !token) {
|
|
8183
|
+
console.error("Usage: board-live-cards source-data-fetched --tmp <tmp-file> --token <sourceToken>");
|
|
8184
|
+
process.exit(1);
|
|
8247
8185
|
}
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
}
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8186
|
+
const payload = deps.decodeSourceToken(token);
|
|
8187
|
+
if (!payload) {
|
|
8188
|
+
console.error("Invalid source token");
|
|
8189
|
+
process.exit(1);
|
|
8190
|
+
}
|
|
8191
|
+
const { cbk, rg, cid, b, d, cs } = payload;
|
|
8192
|
+
const destPath = path7__namespace.join(rg, cid, d);
|
|
8193
|
+
fs7__namespace.mkdirSync(path7__namespace.dirname(destPath), { recursive: true });
|
|
8194
|
+
fs7__namespace.renameSync(tmpFile, destPath);
|
|
8195
|
+
console.log(`[source-data-fetched] ${cid}.${b} -> ${cid}/${d}`);
|
|
8196
|
+
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8197
|
+
const cbkDecoded = deps.decodeCallbackToken(cbk);
|
|
8198
|
+
if (!cbkDecoded) {
|
|
8199
|
+
console.error("Invalid callback token embedded in source token");
|
|
8200
|
+
process.exit(1);
|
|
8201
|
+
}
|
|
8202
|
+
deps.appendEventToJournal(rg, {
|
|
8203
|
+
type: "task-progress",
|
|
8204
|
+
taskName: cbkDecoded.taskName,
|
|
8205
|
+
update: { bindTo: b, outputFile: d, fetchedAt, sourceChecksum: cs },
|
|
8206
|
+
timestamp: fetchedAt
|
|
8207
|
+
});
|
|
8208
|
+
void deps.processAccumulatedEventsInfinitePass(rg);
|
|
8209
|
+
}
|
|
8210
|
+
function cmdSourceDataFetchFailure(args) {
|
|
8211
|
+
const tokenIdx = args.indexOf("--token");
|
|
8212
|
+
const reasonIdx = args.indexOf("--reason");
|
|
8213
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8214
|
+
const reason = reasonIdx !== -1 ? args[reasonIdx + 1] : "unknown";
|
|
8215
|
+
if (!token) {
|
|
8216
|
+
console.error("Usage: board-live-cards source-data-fetch-failure --token <sourceToken> [--reason <msg>]");
|
|
8217
|
+
process.exit(1);
|
|
8264
8218
|
}
|
|
8219
|
+
const payload = deps.decodeSourceToken(token);
|
|
8220
|
+
if (!payload) {
|
|
8221
|
+
console.error("Invalid source token");
|
|
8222
|
+
process.exit(1);
|
|
8223
|
+
}
|
|
8224
|
+
const { cbk, rg, cid, b, d, cs } = payload;
|
|
8225
|
+
console.log(`[source-data-fetch-failure] ${cid}.${b}: ${reason}`);
|
|
8226
|
+
const cbkDecoded = deps.decodeCallbackToken(cbk);
|
|
8227
|
+
if (!cbkDecoded) {
|
|
8228
|
+
console.error("Invalid callback token embedded in source token");
|
|
8229
|
+
process.exit(1);
|
|
8230
|
+
}
|
|
8231
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8232
|
+
deps.appendEventToJournal(rg, {
|
|
8233
|
+
type: "task-progress",
|
|
8234
|
+
taskName: cbkDecoded.taskName,
|
|
8235
|
+
update: { bindTo: b, outputFile: d, failure: true, reason, sourceChecksum: cs },
|
|
8236
|
+
timestamp
|
|
8237
|
+
});
|
|
8238
|
+
void deps.processAccumulatedEventsInfinitePass(rg);
|
|
8239
|
+
}
|
|
8240
|
+
function cmdTaskProgress(args) {
|
|
8241
|
+
const rgIdx = args.indexOf("--rg");
|
|
8242
|
+
const tokenIdx = args.indexOf("--token");
|
|
8243
|
+
const updateIdx = args.indexOf("--update");
|
|
8244
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8245
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8246
|
+
const updateJson = updateIdx !== -1 ? args[updateIdx + 1] : "{}";
|
|
8247
|
+
if (!dir || !token) {
|
|
8248
|
+
console.error("Usage: board-live-cards task-progress --rg <dir> --token <token> [--update <json>]");
|
|
8249
|
+
process.exit(1);
|
|
8250
|
+
}
|
|
8251
|
+
const decoded = deps.decodeCallbackToken(token);
|
|
8252
|
+
if (!decoded) {
|
|
8253
|
+
console.error("Invalid callback token");
|
|
8254
|
+
process.exit(1);
|
|
8255
|
+
}
|
|
8256
|
+
const update = updateJson ? JSON.parse(updateJson) : {};
|
|
8257
|
+
deps.appendEventToJournal(dir, {
|
|
8258
|
+
type: "task-progress",
|
|
8259
|
+
taskName: decoded.taskName,
|
|
8260
|
+
update,
|
|
8261
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8262
|
+
});
|
|
8263
|
+
void deps.processAccumulatedEventsInfinitePass(dir);
|
|
8265
8264
|
}
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
}
|
|
8273
|
-
function loadBoardEnvelope(dir) {
|
|
8274
|
-
const raw = fs__namespace.readFileSync(path__namespace.join(dir, BOARD_FILE), "utf-8");
|
|
8275
|
-
return JSON.parse(raw);
|
|
8276
|
-
}
|
|
8277
|
-
function loadBoard(dir) {
|
|
8278
|
-
const envelope = loadBoardEnvelope(dir);
|
|
8279
|
-
return restore(envelope.graph);
|
|
8280
|
-
}
|
|
8281
|
-
function saveBoard(dir, rg, journal) {
|
|
8282
|
-
const snap = rg.snapshot();
|
|
8283
|
-
const envelope = {
|
|
8284
|
-
lastDrainedJournalId: journal.lastDrainedJournalId,
|
|
8285
|
-
graph: snap
|
|
8265
|
+
return {
|
|
8266
|
+
cmdTaskCompleted,
|
|
8267
|
+
cmdTaskFailed,
|
|
8268
|
+
cmdTaskProgress,
|
|
8269
|
+
cmdSourceDataFetched,
|
|
8270
|
+
cmdSourceDataFetchFailure
|
|
8286
8271
|
};
|
|
8287
|
-
writeJsonAtomic(path__namespace.join(dir, BOARD_FILE), envelope);
|
|
8288
|
-
const live = restore(snap);
|
|
8289
|
-
const statusObject = buildBoardStatusObject(dir, live);
|
|
8290
|
-
writeJsonAtomic(resolveStatusSnapshotPath(dir), statusObject);
|
|
8291
|
-
}
|
|
8292
|
-
function runtimeOutConfigPath(boardDir) {
|
|
8293
|
-
return path__namespace.join(boardDir, RUNTIME_OUT_FILE);
|
|
8294
8272
|
}
|
|
8295
|
-
function
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
const
|
|
8299
|
-
|
|
8300
|
-
|
|
8273
|
+
function createNonCoreCommandHandlers(deps) {
|
|
8274
|
+
function cmdRunSourceFetch(args) {
|
|
8275
|
+
const inIdx = args.indexOf("--in");
|
|
8276
|
+
const outIdx = args.indexOf("--out");
|
|
8277
|
+
const errIdx = args.indexOf("--err");
|
|
8278
|
+
const inFile = inIdx !== -1 ? args[inIdx + 1] : void 0;
|
|
8279
|
+
const outFile = outIdx !== -1 ? args[outIdx + 1] : void 0;
|
|
8280
|
+
const errFile = errIdx !== -1 ? args[errIdx + 1] : void 0;
|
|
8281
|
+
if (!inFile || !outFile) {
|
|
8282
|
+
console.error("Usage: board-live-cards run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]");
|
|
8283
|
+
process.exit(1);
|
|
8284
|
+
}
|
|
8285
|
+
if (!fs7__namespace.existsSync(inFile)) {
|
|
8286
|
+
const msg = `Input file not found: ${inFile}`;
|
|
8287
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8288
|
+
console.error(`[run-source-fetch] ${msg}`);
|
|
8289
|
+
process.exit(1);
|
|
8290
|
+
}
|
|
8291
|
+
let source;
|
|
8292
|
+
try {
|
|
8293
|
+
const raw = fs7__namespace.readFileSync(inFile, "utf-8");
|
|
8294
|
+
source = JSON.parse(raw);
|
|
8295
|
+
} catch (err) {
|
|
8296
|
+
const msg = `Failed to parse input file: ${err.message}`;
|
|
8297
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8298
|
+
console.error(`[run-source-fetch] ${msg}`);
|
|
8299
|
+
process.exit(1);
|
|
8300
|
+
}
|
|
8301
|
+
if (!source.cli) {
|
|
8302
|
+
const msg = "Source definition missing cli field (board-live-cards built-in executor only understands source.cli)";
|
|
8303
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8304
|
+
console.error(`[run-source-fetch] ${msg}`);
|
|
8305
|
+
process.exit(1);
|
|
8306
|
+
}
|
|
8307
|
+
console.log(`[run-source-fetch] executing: ${source.cli}`);
|
|
8308
|
+
const timeout = source.timeout ?? 12e4;
|
|
8309
|
+
const sourceCwd = typeof source.cwd === "string" ? source.cwd : process.cwd();
|
|
8310
|
+
const sourceBoardDir = typeof source.boardDir === "string" ? source.boardDir : void 0;
|
|
8311
|
+
const cmdParts = deps.splitCommandLine(source.cli);
|
|
8312
|
+
if (cmdParts.length === 0) {
|
|
8313
|
+
const msg = "Source cli command is empty";
|
|
8314
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8315
|
+
console.error(`[run-source-fetch] ${msg}`);
|
|
8316
|
+
process.exit(1);
|
|
8317
|
+
}
|
|
8318
|
+
const rawCmd = cmdParts[0];
|
|
8319
|
+
const { cmd, args: cliArgs } = deps.resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
8320
|
+
let stdout;
|
|
8321
|
+
try {
|
|
8322
|
+
stdout = deps.execCommandSync(cmd, cliArgs, {
|
|
8323
|
+
shell: false,
|
|
8324
|
+
encoding: "utf-8",
|
|
8325
|
+
timeout,
|
|
8326
|
+
cwd: sourceCwd,
|
|
8327
|
+
env: {
|
|
8328
|
+
...process.env,
|
|
8329
|
+
...sourceBoardDir ? { BOARD_DIR: sourceBoardDir } : {}
|
|
8330
|
+
}
|
|
8331
|
+
});
|
|
8332
|
+
} catch (err) {
|
|
8333
|
+
const msg = err.message ?? String(err);
|
|
8334
|
+
console.error(`[run-source-fetch] cli failed: ${msg}`);
|
|
8335
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8336
|
+
process.exit(1);
|
|
8337
|
+
}
|
|
8338
|
+
const result = stdout.trim();
|
|
8339
|
+
try {
|
|
8340
|
+
fs7__namespace.writeFileSync(outFile, result);
|
|
8341
|
+
console.log(`[run-source-fetch] result written to ${outFile}`);
|
|
8342
|
+
} catch (err) {
|
|
8343
|
+
const msg = `Failed to write output file: ${err.message}`;
|
|
8344
|
+
console.error(`[run-source-fetch] ${msg}`);
|
|
8345
|
+
if (errFile) fs7__namespace.writeFileSync(errFile, msg);
|
|
8346
|
+
process.exit(1);
|
|
8301
8347
|
}
|
|
8302
8348
|
}
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
}
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
}
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
}
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
}
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
}
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8349
|
+
async function cmdProbeSource(args) {
|
|
8350
|
+
const cardIdx = args.indexOf("--card");
|
|
8351
|
+
const sourceIdxArg = args.indexOf("--source-idx");
|
|
8352
|
+
const sourceBindArg = args.indexOf("--source-bind");
|
|
8353
|
+
const mockProjectionsIdx = args.indexOf("--mock-projections");
|
|
8354
|
+
const rgIdx = args.indexOf("--rg");
|
|
8355
|
+
const outIdx = args.indexOf("--out");
|
|
8356
|
+
const cardFilePath = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
8357
|
+
const sourceIdxVal = sourceIdxArg !== -1 ? parseInt(args[sourceIdxArg + 1], 10) : 0;
|
|
8358
|
+
const sourceBindVal = sourceBindArg !== -1 ? args[sourceBindArg + 1] : void 0;
|
|
8359
|
+
const mockProjectionsRaw = mockProjectionsIdx !== -1 ? args[mockProjectionsIdx + 1] : void 0;
|
|
8360
|
+
const boardDirArg = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8361
|
+
const outFile = outIdx !== -1 ? args[outIdx + 1] : void 0;
|
|
8362
|
+
if (!cardFilePath) {
|
|
8363
|
+
console.error("Usage: board-live-cards probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>] [--mock-projections <json>] [--rg <boardDir>] [--out <result.json>]");
|
|
8364
|
+
process.exit(1);
|
|
8365
|
+
}
|
|
8366
|
+
let card;
|
|
8367
|
+
try {
|
|
8368
|
+
card = JSON.parse(fs7__namespace.readFileSync(path7__namespace.resolve(cardFilePath), "utf-8"));
|
|
8369
|
+
} catch (e) {
|
|
8370
|
+
console.error(`[probe-source] Cannot read card: ${e.message}`);
|
|
8371
|
+
process.exit(1);
|
|
8372
|
+
}
|
|
8373
|
+
const source_defs = card.source_defs ?? [];
|
|
8374
|
+
if (source_defs.length === 0) {
|
|
8375
|
+
console.error(`[probe-source] Card "${card.id}" has no source_defs`);
|
|
8376
|
+
process.exit(1);
|
|
8377
|
+
}
|
|
8378
|
+
let sourceIdx;
|
|
8379
|
+
if (sourceBindVal) {
|
|
8380
|
+
sourceIdx = source_defs.findIndex((s) => s.bindTo === sourceBindVal);
|
|
8381
|
+
if (sourceIdx === -1) {
|
|
8382
|
+
console.error(`[probe-source] No source with bindTo="${sourceBindVal}" in card "${card.id}"`);
|
|
8383
|
+
process.exit(1);
|
|
8384
|
+
}
|
|
8385
|
+
} else {
|
|
8386
|
+
sourceIdx = sourceIdxVal;
|
|
8387
|
+
if (isNaN(sourceIdx) || sourceIdx < 0 || sourceIdx >= source_defs.length) {
|
|
8388
|
+
console.error(`[probe-source] --source-idx ${sourceIdxVal} out of range (card has ${source_defs.length} source(s))`);
|
|
8389
|
+
process.exit(1);
|
|
8390
|
+
}
|
|
8391
|
+
}
|
|
8392
|
+
const sourceDef = source_defs[sourceIdx];
|
|
8393
|
+
const cardDir = path7__namespace.resolve(path7__namespace.dirname(cardFilePath));
|
|
8394
|
+
const boardDir = boardDirArg ? path7__namespace.resolve(boardDirArg) : cardDir;
|
|
8395
|
+
let mockProjections = {};
|
|
8396
|
+
if (mockProjectionsRaw) {
|
|
8397
|
+
const raw = mockProjectionsRaw.startsWith("@") ? fs7__namespace.readFileSync(path7__namespace.resolve(mockProjectionsRaw.slice(1)), "utf-8") : mockProjectionsRaw;
|
|
8398
|
+
try {
|
|
8399
|
+
mockProjections = JSON.parse(raw);
|
|
8400
|
+
} catch (e) {
|
|
8401
|
+
console.error(`[probe-source] --mock-projections is not valid JSON: ${e.message}`);
|
|
8402
|
+
process.exit(1);
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
const teConfig = deps.readTaskExecutorConfig(boardDir);
|
|
8406
|
+
const taskExecutor = teConfig?.command;
|
|
8407
|
+
const taskExecutorExtraB64 = teConfig?.extra ? Buffer.from(JSON.stringify(teConfig.extra)).toString("base64") : void 0;
|
|
8408
|
+
const inPayload = {
|
|
8409
|
+
...sourceDef,
|
|
8410
|
+
cwd: typeof sourceDef.cwd === "string" && sourceDef.cwd ? sourceDef.cwd : cardDir,
|
|
8411
|
+
boardDir: typeof sourceDef.boardDir === "string" && sourceDef.boardDir ? sourceDef.boardDir : boardDir,
|
|
8412
|
+
_projections: mockProjections
|
|
8413
|
+
};
|
|
8414
|
+
let sourceKind = "unknown";
|
|
8415
|
+
if (taskExecutor) {
|
|
8416
|
+
try {
|
|
8417
|
+
const capRaw = deps.execCommandSync(taskExecutor, ["describe-capabilities"], {
|
|
8418
|
+
shell: true,
|
|
8419
|
+
timeout: 8e3,
|
|
8420
|
+
encoding: "utf-8"
|
|
8421
|
+
});
|
|
8422
|
+
const caps = JSON.parse(String(capRaw));
|
|
8423
|
+
const knownKinds = caps?.sourceKinds ? Object.keys(caps.sourceKinds) : [];
|
|
8424
|
+
const defKeys = new Set(Object.keys(sourceDef));
|
|
8425
|
+
sourceKind = knownKinds.find((k) => defKeys.has(k)) ?? "unknown";
|
|
8426
|
+
} catch {
|
|
8427
|
+
}
|
|
8428
|
+
}
|
|
8429
|
+
console.log(`[probe-source] card: ${card.id}`);
|
|
8430
|
+
console.log(`[probe-source] source[${sourceIdx}]: bindTo="${sourceDef.bindTo}" kind=${sourceKind}`);
|
|
8431
|
+
console.log(`[probe-source] _projections: ${JSON.stringify(mockProjections)}`);
|
|
8432
|
+
console.log(`[probe-source] executor: ${taskExecutor ?? "built-in (source.cli only)"}`);
|
|
8433
|
+
console.log("[probe-source] running fetch...");
|
|
8434
|
+
const ts = Date.now();
|
|
8435
|
+
const inFile = path7__namespace.join(os3__namespace.tmpdir(), `probe-in-${sourceDef.bindTo}-${ts}.json`);
|
|
8436
|
+
const tmpOut = path7__namespace.join(os3__namespace.tmpdir(), `probe-out-${sourceDef.bindTo}-${ts}.json`);
|
|
8437
|
+
const errFile = path7__namespace.join(os3__namespace.tmpdir(), `probe-err-${sourceDef.bindTo}-${ts}.txt`);
|
|
8438
|
+
fs7__namespace.writeFileSync(inFile, JSON.stringify(inPayload, null, 2), "utf-8");
|
|
8439
|
+
let passed = false;
|
|
8440
|
+
let errorMsg;
|
|
8441
|
+
let resultRaw;
|
|
8442
|
+
try {
|
|
8443
|
+
if (taskExecutor) {
|
|
8444
|
+
const executorArgs = ["run-source-fetch", "--in", inFile, "--out", tmpOut, "--err", errFile];
|
|
8445
|
+
if (taskExecutorExtraB64) executorArgs.push("--extra", taskExecutorExtraB64);
|
|
8446
|
+
deps.execCommandSync(taskExecutor, executorArgs, {
|
|
8447
|
+
shell: true,
|
|
8448
|
+
timeout: sourceDef.timeout ?? 3e4
|
|
8449
|
+
});
|
|
8450
|
+
} else {
|
|
8451
|
+
if (!inPayload.cli) {
|
|
8452
|
+
throw new Error("No task-executor registered and source has no cli field \u2014 cannot probe with built-in executor");
|
|
8453
|
+
}
|
|
8454
|
+
const cmdParts = deps.splitCommandLine(inPayload.cli);
|
|
8455
|
+
const rawCmd = cmdParts[0];
|
|
8456
|
+
const { cmd, args: cliArgs } = deps.resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
8457
|
+
const stdout = deps.execCommandSync(cmd, cliArgs, {
|
|
8458
|
+
shell: false,
|
|
8459
|
+
encoding: "utf-8",
|
|
8460
|
+
timeout: sourceDef.timeout ?? 3e4,
|
|
8461
|
+
cwd: inPayload.cwd
|
|
8462
|
+
});
|
|
8463
|
+
fs7__namespace.writeFileSync(tmpOut, String(stdout).trim(), "utf-8");
|
|
8464
|
+
}
|
|
8465
|
+
passed = fs7__namespace.existsSync(tmpOut);
|
|
8466
|
+
if (passed) {
|
|
8467
|
+
resultRaw = fs7__namespace.readFileSync(tmpOut, "utf-8");
|
|
8468
|
+
} else {
|
|
8469
|
+
errorMsg = fs7__namespace.existsSync(errFile) ? fs7__namespace.readFileSync(errFile, "utf-8").trim() : "executor produced no output file";
|
|
8470
|
+
}
|
|
8471
|
+
} catch (e) {
|
|
8472
|
+
errorMsg = e.message ?? String(e);
|
|
8473
|
+
if (!errorMsg && fs7__namespace.existsSync(errFile)) {
|
|
8474
|
+
errorMsg = fs7__namespace.readFileSync(errFile, "utf-8").trim();
|
|
8475
|
+
}
|
|
8476
|
+
}
|
|
8477
|
+
for (const f of [inFile, errFile]) {
|
|
8478
|
+
try {
|
|
8479
|
+
fs7__namespace.unlinkSync(f);
|
|
8480
|
+
} catch {
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
if (passed && resultRaw !== void 0) {
|
|
8484
|
+
const resultSize = resultRaw.length;
|
|
8485
|
+
const sample = resultRaw.slice(0, 300);
|
|
8486
|
+
console.log("[probe-source] STATUS: PROBE_PASS");
|
|
8487
|
+
console.log(`[probe-source] result size: ${resultSize} bytes`);
|
|
8488
|
+
console.log(`[probe-source] sample: ${sample}${resultSize > 300 ? "..." : ""}`);
|
|
8489
|
+
if (outFile) {
|
|
8490
|
+
fs7__namespace.writeFileSync(path7__namespace.resolve(outFile), resultRaw);
|
|
8491
|
+
console.log(`[probe-source] result written to: ${outFile}`);
|
|
8492
|
+
} else {
|
|
8493
|
+
try {
|
|
8494
|
+
fs7__namespace.unlinkSync(tmpOut);
|
|
8495
|
+
} catch {
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
} else {
|
|
8499
|
+
console.log("[probe-source] STATUS: PROBE_FAIL");
|
|
8500
|
+
if (errorMsg) console.log(`[probe-source] error: ${errorMsg}`);
|
|
8501
|
+
try {
|
|
8502
|
+
if (fs7__namespace.existsSync(tmpOut)) fs7__namespace.unlinkSync(tmpOut);
|
|
8503
|
+
} catch {
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
const summary = {
|
|
8507
|
+
status: passed ? "PROBE_PASS" : "PROBE_FAIL",
|
|
8508
|
+
cardId: card.id,
|
|
8509
|
+
sourceIdx,
|
|
8510
|
+
bindTo: sourceDef.bindTo,
|
|
8511
|
+
sourceKind,
|
|
8512
|
+
mockProjectionsKeys: Object.keys(mockProjections),
|
|
8513
|
+
resultSizeBytes: resultRaw !== void 0 ? resultRaw.length : 0,
|
|
8514
|
+
error: errorMsg ?? void 0
|
|
8515
|
+
};
|
|
8516
|
+
console.log(`[probe-source:result] ${JSON.stringify(summary)}`);
|
|
8517
|
+
process.exit(passed ? 0 : 1);
|
|
8518
|
+
}
|
|
8519
|
+
function cmdDescribeTaskExecutorCapabilities(args) {
|
|
8520
|
+
const rgIdx = args.indexOf("--rg");
|
|
8521
|
+
const boardDir = rgIdx !== -1 ? path7__namespace.resolve(args[rgIdx + 1]) : void 0;
|
|
8522
|
+
if (!boardDir) {
|
|
8523
|
+
console.error("Usage: board-live-cards describe-task-executor-capabilities --rg <dir>");
|
|
8524
|
+
process.exit(1);
|
|
8525
|
+
}
|
|
8526
|
+
const teConfig = deps.readTaskExecutorConfig(boardDir);
|
|
8527
|
+
if (!teConfig) {
|
|
8528
|
+
console.error(`[describe-task-executor-capabilities] No .task-executor registered in ${boardDir}`);
|
|
8529
|
+
process.exit(1);
|
|
8530
|
+
}
|
|
8531
|
+
try {
|
|
8532
|
+
const stdout = deps.execCommandSync(teConfig.command, ["describe-capabilities"], {
|
|
8533
|
+
shell: true,
|
|
8534
|
+
timeout: 1e4,
|
|
8535
|
+
encoding: "utf-8"
|
|
8536
|
+
});
|
|
8537
|
+
process.stdout.write(String(stdout));
|
|
8538
|
+
if (!String(stdout).endsWith("\n")) process.stdout.write("\n");
|
|
8539
|
+
} catch (e) {
|
|
8540
|
+
console.error(`[describe-task-executor-capabilities] Executor failed: ${e.message ?? e}`);
|
|
8541
|
+
process.exit(1);
|
|
8542
|
+
}
|
|
8352
8543
|
}
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8544
|
+
function cmdHelp() {
|
|
8545
|
+
console.log(`
|
|
8546
|
+
board-live-cards-cli \u2014 LiveCards board CLI
|
|
8547
|
+
|
|
8548
|
+
USAGE
|
|
8549
|
+
board-live-cards-cli <command> [options]
|
|
8550
|
+
|
|
8551
|
+
BOARD MANAGEMENT
|
|
8552
|
+
init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]
|
|
8553
|
+
Create a new board in <dir>.
|
|
8554
|
+
If --task-executor is given, writes <dir>/.task-executor with the script path.
|
|
8555
|
+
If --chat-handler is given, writes <dir>/.chat-handler with the script path.
|
|
8556
|
+
If --inference-adapter is given, writes <dir>/.inference-adapter with the script path.
|
|
8557
|
+
Writes <dir>/.runtime-out (default: <dir>/runtime-out).
|
|
8558
|
+
Published runtime files:
|
|
8559
|
+
<runtime-out>/board-livegraph-status.json
|
|
8560
|
+
<runtime-out>/cards/<card-id>.computed.json
|
|
8561
|
+
Re-running init on an existing board is safe; handler registrations are updated.
|
|
8562
|
+
|
|
8563
|
+
status --rg <dir> [--json]
|
|
8564
|
+
Read and print the published status snapshot from <runtime-out>/board-livegraph-status.json.
|
|
8565
|
+
--json emits the stable machine-readable status object.
|
|
8566
|
+
|
|
8567
|
+
CARD MANAGEMENT
|
|
8568
|
+
upsert-card --rg <dir> (--card <card.json> | --card-glob <glob>) [--card-id <card-id>] [--restart]
|
|
8569
|
+
Insert or update one or many cards.
|
|
8570
|
+
Enforces strict one-to-one mapping between card id and file path:
|
|
8571
|
+
- same id + same file path: update
|
|
8572
|
+
- new id + new file path: insert
|
|
8573
|
+
- id remap or file remap: rejected
|
|
8574
|
+
If --card-id is provided, it must match the id inside the file.
|
|
8575
|
+
--card-id is valid only with --card (single file), not with --card-glob.
|
|
8576
|
+
--restart clears the task so it re-triggers from scratch.
|
|
8577
|
+
|
|
8578
|
+
validate-card (--card <card.json> | --card-glob <glob>) [--rg <boardDir>]
|
|
8579
|
+
Validate one or many card JSON files without adding them to a board.
|
|
8580
|
+
Checks JSON Schema structure, runtime expression syntax, and provides.ref namespaces.
|
|
8581
|
+
When --rg is provided, also invokes the board's task executor validate-source-def
|
|
8582
|
+
subcommand to structurally validate each source definition against supported kinds.
|
|
8583
|
+
Exits with code 1 if any card fails validation.
|
|
8584
|
+
|
|
8585
|
+
remove-card --rg <dir> --id <card-id>
|
|
8586
|
+
Remove a card and its task from the board.
|
|
8587
|
+
|
|
8588
|
+
retrigger --rg <dir> --task <task-name>
|
|
8589
|
+
Mark a task not-started and drain to re-trigger it.
|
|
8590
|
+
|
|
8591
|
+
TASK CALLBACKS (called by task executor scripts)
|
|
8592
|
+
task-completed --token <callbackToken> [--data <json>]
|
|
8593
|
+
Signal successful task completion with optional JSON result data.
|
|
8594
|
+
|
|
8595
|
+
task-failed --token <callbackToken> [--error <message>]
|
|
8596
|
+
Signal task failure with an optional error message.
|
|
8597
|
+
|
|
8598
|
+
task-progress --rg <dir> --token <callbackToken> [--update <json>]
|
|
8599
|
+
Signal task progress with optional update payload (for waiting on more evidence, etc.).
|
|
8600
|
+
|
|
8601
|
+
SOURCE CALLBACKS (called internally by run-sourcedefs-internal)
|
|
8602
|
+
source-data-fetched --tmp <file> --token <sourceToken>
|
|
8603
|
+
Atomically rename <file> into the outputFile destination and record delivery
|
|
8604
|
+
via journal events. Appends a task-progress event to re-invoke the card handler.
|
|
8605
|
+
|
|
8606
|
+
source-data-fetch-failure --token <sourceToken> [--reason <message>]
|
|
8607
|
+
Record a source fetch failure via journal events and append a task-progress event.
|
|
8608
|
+
|
|
8609
|
+
INTERNAL COMMANDS
|
|
8610
|
+
process-accumulated-events --rg <dir>
|
|
8611
|
+
Executes forced drain for this board.
|
|
8612
|
+
This command is also used as the background relay worker.
|
|
8613
|
+
By default it schedules a detached worker and returns quickly.
|
|
8614
|
+
Internal workers run with --inline-loop to perform the settle loop.
|
|
8615
|
+
|
|
8616
|
+
Eventual-progress guarantee is relay-based (not per-call blocking guarantee):
|
|
8617
|
+
1) at least one runner continues processing,
|
|
8618
|
+
2) no crash/forced exit in relay window,
|
|
8619
|
+
3) lock stays healthy,
|
|
8620
|
+
4) event production eventually quiesces.
|
|
8621
|
+
|
|
8622
|
+
run-sourcedefs-internal --card <card.json> --token <callbackToken> --rg <dir>
|
|
8623
|
+
Execute all source[] entries for a card, then report delivery or failure.
|
|
8624
|
+
(Internal command \u2014 invoked by the card-handler. Not intended for direct use.)
|
|
8625
|
+
|
|
8626
|
+
If <dir>/.task-executor exists, invokes it with run-source-fetch subcommand:
|
|
8627
|
+
<executor> run-source-fetch --in <source_json> --out <outfile> --err <errfile>
|
|
8628
|
+
|
|
8629
|
+
If no .task-executor is registered, uses board-live-cards built-in run-source-fetch.
|
|
8630
|
+
|
|
8631
|
+
run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]
|
|
8632
|
+
Execute a source definition. Board-live-cards reads source.cli and executes it.
|
|
8633
|
+
Writes result to --out. Presence of --out after exit indicates success.
|
|
8634
|
+
|
|
8635
|
+
describe-task-executor-capabilities --rg <dir>
|
|
8636
|
+
Invoke the registered task-executor's describe-capabilities subcommand and
|
|
8637
|
+
print its capabilities JSON to stdout. Requires a .task-executor file in <dir>.
|
|
8638
|
+
|
|
8639
|
+
probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>]
|
|
8640
|
+
[--mock-projections <json>] [--rg <boardDir>] [--out <result.json>]
|
|
8641
|
+
Validate that a card source can be fetched successfully.
|
|
8642
|
+
Reads the card file, extracts the chosen source (default: index 0), builds the
|
|
8643
|
+
run-source-fetch --in payload with the supplied _projections data, invokes the
|
|
8644
|
+
registered task-executor (or built-in executor for source.cli), and reports pass/fail.
|
|
8645
|
+
--mock-projections: JSON string (or @file.json) providing pre-resolved _projections values
|
|
8646
|
+
the source needs. Craft the minimal payload that exercises the
|
|
8647
|
+
source \u2014 e.g. '{"holdings":[{"ticker":"AAPL","quantity":10}]}'.
|
|
8648
|
+
If omitted, _projections is passed as empty ({}).
|
|
8649
|
+
--source-idx: 0-based index into card.source_defs[]. Default: 0.
|
|
8650
|
+
--source-bind: Select source by its bindTo name instead of index.
|
|
8651
|
+
--rg: Board directory used to find .task-executor. Defaults to the
|
|
8652
|
+
directory containing the card file.
|
|
8653
|
+
--out: Optional path to write the raw fetch result JSON.
|
|
8654
|
+
Prints a structured report ending with a [probe-source:result] JSON line.
|
|
8655
|
+
Exits 0 on PROBE_PASS, 1 on PROBE_FAIL.
|
|
8656
|
+
|
|
8657
|
+
run-inference-internal --in <input.json> --token <inferenceToken>
|
|
8658
|
+
Execute inference via registered .inference-adapter and forward result to inference-done.
|
|
8659
|
+
inferenceToken encodes boardDir (rg), cardId (cid), callbackToken (cbk), checksum (cs).
|
|
8660
|
+
(Internal command \u2014 invoked by the card-handler when custom completion rule is used.)
|
|
8661
|
+
|
|
8662
|
+
inference-done --tmp <result.json> --token <inferenceToken>
|
|
8663
|
+
Persist llm_task_completion_inference on the card and append a task-progress event.
|
|
8664
|
+
Reads boardDir/callbackToken/checksum from decoded inferenceToken; deletes --tmp file after reading.
|
|
8665
|
+
(Internal command \u2014 invoked by run-inference-internal.)
|
|
8666
|
+
|
|
8667
|
+
RUN-SOURCE-FETCH PROTOCOL
|
|
8668
|
+
External task-executors implement:
|
|
8669
|
+
<executor> run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]
|
|
8670
|
+
|
|
8671
|
+
INPUT: --in file contains the full source_defs[x] definition object
|
|
8672
|
+
OUTPUT: --out file is written with the result to signal success.
|
|
8673
|
+
--err file may be written to explain failure.
|
|
8674
|
+
|
|
8675
|
+
Exit code and --out presence determine success:
|
|
8676
|
+
Exit 0 + --out file present \u2192 source delivery recorded, card re-evaluated.
|
|
8677
|
+
Exit non-zero OR --out absent \u2192 source-data-fetch-failure recorded.
|
|
8678
|
+
|
|
8679
|
+
BOARD-LIVE-CARDS BUILT-IN EXECUTOR
|
|
8680
|
+
Understands source.cli field only:
|
|
8681
|
+
"source_defs": [{ "cli": "node ../fetch-prices.js", "bindTo": "prices", "outputFile": "prices.json" }]
|
|
8682
|
+
|
|
8683
|
+
The source.cli command is executed with:
|
|
8684
|
+
- Direct command invocation (no shell; quote-aware argument parsing)
|
|
8685
|
+
- Stdout is captured and delivered to the card as-is
|
|
8686
|
+
- Timeout from source.timeout (default 120s)
|
|
8687
|
+
|
|
8688
|
+
The source.cli command must:
|
|
8689
|
+
- Execute successfully (exit 0)
|
|
8690
|
+
- Write output to stdout
|
|
8691
|
+
- Complete within the timeout
|
|
8692
|
+
|
|
8693
|
+
The output format is the concern of the card's compute function to interpret.
|
|
8694
|
+
|
|
8695
|
+
External task-executors can interpret source definitions however they want.
|
|
8696
|
+
|
|
8697
|
+
EXAMPLES
|
|
8698
|
+
board-live-cards-cli init ./my-board
|
|
8699
|
+
board-live-cards-cli init ./my-board --task-executor ./executors/my-runner.py
|
|
8700
|
+
board-live-cards-cli upsert-card --rg ./my-board --card cards/prices.json
|
|
8701
|
+
board-live-cards-cli status --rg ./my-board
|
|
8702
|
+
board-live-cards-cli retrigger --rg ./my-board --task price-fetch
|
|
8703
|
+
board-live-cards-cli probe-source --card cards/card-market-prices.json --source-idx 0 --rg ./my-board --mock-projections '{"holdings":[{"ticker":"AAPL","quantity":10}]}'
|
|
8704
|
+
`.trimStart());
|
|
8361
8705
|
}
|
|
8706
|
+
return {
|
|
8707
|
+
cmdHelp,
|
|
8708
|
+
cmdRunSourceFetch,
|
|
8709
|
+
cmdProbeSource,
|
|
8710
|
+
cmdDescribeTaskExecutorCapabilities
|
|
8711
|
+
};
|
|
8362
8712
|
}
|
|
8363
|
-
function
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
const
|
|
8369
|
-
|
|
8370
|
-
|
|
8713
|
+
function createCardCommandHandlers(deps) {
|
|
8714
|
+
function cmdUpsertCard(args) {
|
|
8715
|
+
const rgIdx = args.indexOf("--rg");
|
|
8716
|
+
const cardIdx = args.indexOf("--card");
|
|
8717
|
+
const globIdx = args.indexOf("--card-glob");
|
|
8718
|
+
const cardIdIdx = args.indexOf("--card-id");
|
|
8719
|
+
const restart = args.includes("--restart");
|
|
8720
|
+
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8721
|
+
const cardFile = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
8722
|
+
const cardGlob = globIdx !== -1 ? args[globIdx + 1] : void 0;
|
|
8723
|
+
const requestedCardId = cardIdIdx !== -1 ? args[cardIdIdx + 1] : void 0;
|
|
8724
|
+
if (!dir || !cardFile && !cardGlob || cardFile && cardGlob) {
|
|
8725
|
+
console.error("Usage: board-live-cards upsert-card --rg <dir> (--card <card.json> | --card-glob <glob>) [--card-id <card-id>] [--restart]");
|
|
8726
|
+
process.exit(1);
|
|
8727
|
+
}
|
|
8728
|
+
if (cardGlob && requestedCardId) {
|
|
8729
|
+
console.error("Usage: --card-id may be used only with --card (single file), not with --card-glob");
|
|
8730
|
+
process.exit(1);
|
|
8731
|
+
}
|
|
8732
|
+
const cardFiles = cardFile ? [path7__namespace.resolve(cardFile)] : deps.resolveCardGlobMatches(cardGlob);
|
|
8733
|
+
if (!cardFile && cardFiles.length === 0) {
|
|
8734
|
+
console.error(`No card files matched glob: ${cardGlob}`);
|
|
8735
|
+
process.exit(1);
|
|
8736
|
+
}
|
|
8737
|
+
const idx = deps.buildCardInventoryIndex(dir);
|
|
8738
|
+
const batchByCardId = /* @__PURE__ */ new Map();
|
|
8739
|
+
const batchByCardPath = /* @__PURE__ */ new Map();
|
|
8740
|
+
const plans = [];
|
|
8741
|
+
const logs = [];
|
|
8742
|
+
for (const absCardPath of cardFiles) {
|
|
8743
|
+
if (!fs7__namespace.existsSync(absCardPath)) {
|
|
8744
|
+
console.error(`Card file not found: ${absCardPath}`);
|
|
8745
|
+
process.exit(1);
|
|
8746
|
+
}
|
|
8747
|
+
const card = JSON.parse(fs7__namespace.readFileSync(absCardPath, "utf-8"));
|
|
8748
|
+
if (!card.id) {
|
|
8749
|
+
console.error(`Card JSON must have an "id" field (${absCardPath})`);
|
|
8750
|
+
process.exit(1);
|
|
8751
|
+
}
|
|
8752
|
+
if (requestedCardId && requestedCardId !== card.id) {
|
|
8753
|
+
console.error(
|
|
8754
|
+
`Card id mismatch: --card-id "${requestedCardId}" does not match file id "${card.id}" (${absCardPath})`
|
|
8755
|
+
);
|
|
8756
|
+
process.exit(1);
|
|
8757
|
+
}
|
|
8758
|
+
const seenPathCardId = batchByCardPath.get(absCardPath);
|
|
8759
|
+
if (seenPathCardId && seenPathCardId !== card.id) {
|
|
8760
|
+
console.error(
|
|
8761
|
+
`Upsert rejected: file "${absCardPath}" appears multiple times in batch with conflicting ids ("${seenPathCardId}" vs "${card.id}")`
|
|
8762
|
+
);
|
|
8763
|
+
process.exit(1);
|
|
8764
|
+
}
|
|
8765
|
+
const seenCardPath = batchByCardId.get(card.id);
|
|
8766
|
+
if (seenCardPath && seenCardPath !== absCardPath) {
|
|
8767
|
+
console.error(
|
|
8768
|
+
`Upsert rejected: card id "${card.id}" appears multiple times in batch with conflicting files ("${seenCardPath}" vs "${absCardPath}")`
|
|
8769
|
+
);
|
|
8770
|
+
process.exit(1);
|
|
8771
|
+
}
|
|
8772
|
+
const existingById = idx.byCardId.get(card.id);
|
|
8773
|
+
const existingByPath = idx.byCardPath.get(absCardPath);
|
|
8774
|
+
if (existingByPath && existingByPath.cardId !== card.id) {
|
|
8775
|
+
console.error(
|
|
8776
|
+
`Upsert rejected: file "${absCardPath}" is already mapped to card id "${existingByPath.cardId}", cannot remap to "${card.id}"`
|
|
8777
|
+
);
|
|
8778
|
+
process.exit(1);
|
|
8779
|
+
}
|
|
8780
|
+
if (existingById && existingById.cardFilePath !== absCardPath) {
|
|
8781
|
+
console.error(
|
|
8782
|
+
`Upsert rejected: card id "${card.id}" is already mapped to file "${existingById.cardFilePath}", cannot remap to "${absCardPath}"`
|
|
8783
|
+
);
|
|
8784
|
+
process.exit(1);
|
|
8785
|
+
}
|
|
8786
|
+
batchByCardPath.set(absCardPath, card.id);
|
|
8787
|
+
batchByCardId.set(card.id, absCardPath);
|
|
8788
|
+
plans.push({
|
|
8789
|
+
card,
|
|
8790
|
+
absCardPath,
|
|
8791
|
+
isInsert: !existingById
|
|
8792
|
+
});
|
|
8793
|
+
}
|
|
8794
|
+
for (const plan of plans) {
|
|
8795
|
+
const { card, absCardPath, isInsert } = plan;
|
|
8796
|
+
if (isInsert) {
|
|
8797
|
+
const newEntry = {
|
|
8798
|
+
cardId: card.id,
|
|
8799
|
+
cardFilePath: absCardPath,
|
|
8800
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8801
|
+
};
|
|
8802
|
+
deps.appendCardInventory(dir, newEntry);
|
|
8803
|
+
idx.byCardId.set(card.id, newEntry);
|
|
8804
|
+
idx.byCardPath.set(absCardPath, newEntry);
|
|
8805
|
+
}
|
|
8806
|
+
const taskConfig = deps.liveCardToTaskConfig(card);
|
|
8807
|
+
deps.appendEventToJournal(dir, {
|
|
8808
|
+
type: "task-upsert",
|
|
8809
|
+
taskName: card.id,
|
|
8810
|
+
taskConfig,
|
|
8811
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8812
|
+
});
|
|
8813
|
+
if (restart) {
|
|
8814
|
+
deps.appendEventToJournal(dir, {
|
|
8815
|
+
type: "task-restart",
|
|
8816
|
+
taskName: card.id,
|
|
8817
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8818
|
+
});
|
|
8819
|
+
}
|
|
8820
|
+
logs.push(`Card "${card.id}" ${isInsert ? "upserted (inserted)" : "upserted (updated)"}${restart ? " (restarted)" : ""}.`);
|
|
8821
|
+
}
|
|
8822
|
+
void deps.processAccumulatedEventsInfinitePass(dir);
|
|
8823
|
+
if (cardGlob) {
|
|
8824
|
+
console.log(`Upserted ${cardFiles.length} cards from glob: ${cardGlob}${restart ? " (restarted)" : ""}`);
|
|
8825
|
+
} else {
|
|
8826
|
+
console.log(logs[0]);
|
|
8371
8827
|
}
|
|
8372
|
-
return null;
|
|
8373
|
-
} catch {
|
|
8374
|
-
return null;
|
|
8375
|
-
}
|
|
8376
|
-
}
|
|
8377
|
-
function markRequested(entry, requestedAt) {
|
|
8378
|
-
entry.lastRequestedAt = requestedAt;
|
|
8379
|
-
}
|
|
8380
|
-
function markFetchFailed(entry, reason) {
|
|
8381
|
-
entry.lastError = reason;
|
|
8382
|
-
delete entry.lastFetchedAt;
|
|
8383
|
-
}
|
|
8384
|
-
function markFetchCompleted(entry, fetchedAt) {
|
|
8385
|
-
entry.lastFetchedAt = fetchedAt;
|
|
8386
|
-
delete entry.lastError;
|
|
8387
|
-
}
|
|
8388
|
-
function isSourceInFlight(entry) {
|
|
8389
|
-
if (!entry?.lastRequestedAt) return false;
|
|
8390
|
-
return !entry.lastFetchedAt || entry.lastFetchedAt < entry.lastRequestedAt;
|
|
8391
|
-
}
|
|
8392
|
-
function decideSourceAction(entry, queueRequestedAt) {
|
|
8393
|
-
if (!entry?.lastRequestedAt) return "dispatch";
|
|
8394
|
-
const inFlight = isSourceInFlight(entry);
|
|
8395
|
-
if (inFlight) return "in-flight";
|
|
8396
|
-
if (!entry.lastFetchedAt) return "dispatch";
|
|
8397
|
-
if (entry.lastFetchedAt < queueRequestedAt) return "dispatch";
|
|
8398
|
-
return "idle";
|
|
8399
|
-
}
|
|
8400
|
-
function nextEntryAfterFetchDelivery(entry, fetchedAt) {
|
|
8401
|
-
const next = { ...entry };
|
|
8402
|
-
markFetchCompleted(next, fetchedAt);
|
|
8403
|
-
return next;
|
|
8404
|
-
}
|
|
8405
|
-
function nextEntryAfterFetchFailure(entry, reason) {
|
|
8406
|
-
const next = { ...entry };
|
|
8407
|
-
markFetchFailed(next, reason);
|
|
8408
|
-
return next;
|
|
8409
|
-
}
|
|
8410
|
-
function runtimePath(boardDir, cardId) {
|
|
8411
|
-
return path__namespace.join(boardDir, cardId, "runtime.json");
|
|
8412
|
-
}
|
|
8413
|
-
function readRuntimeState(boardDir, cardId) {
|
|
8414
|
-
const p = runtimePath(boardDir, cardId);
|
|
8415
|
-
if (!fs__namespace.existsSync(p)) return { _sources: {} };
|
|
8416
|
-
try {
|
|
8417
|
-
return JSON.parse(fs__namespace.readFileSync(p, "utf-8"));
|
|
8418
|
-
} catch {
|
|
8419
|
-
return { _sources: {} };
|
|
8420
8828
|
}
|
|
8829
|
+
return { cmdUpsertCard };
|
|
8421
8830
|
}
|
|
8422
|
-
function
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
fs__namespace.appendFileSync(journalPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
8431
|
-
}
|
|
8432
|
-
function getUndrainedEntries(boardDir, lastDrainedId) {
|
|
8433
|
-
const journalPath = path__namespace.join(boardDir, JOURNAL_FILE);
|
|
8434
|
-
if (!fs__namespace.existsSync(journalPath)) return [];
|
|
8435
|
-
const content = fs__namespace.readFileSync(journalPath, "utf-8").trim();
|
|
8436
|
-
if (!content) return [];
|
|
8437
|
-
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
8438
|
-
if (!lastDrainedId) return entries;
|
|
8439
|
-
const idx = entries.findIndex((e) => e.id === lastDrainedId);
|
|
8440
|
-
return idx === -1 ? entries : entries.slice(idx + 1);
|
|
8441
|
-
}
|
|
8442
|
-
function determineLatestPendingAccumulated(boardDir) {
|
|
8443
|
-
const boardPath = path__namespace.join(boardDir, BOARD_FILE);
|
|
8444
|
-
if (!fs__namespace.existsSync(boardPath)) return 0;
|
|
8445
|
-
try {
|
|
8446
|
-
const envelope = loadBoardEnvelope(boardDir);
|
|
8447
|
-
return getUndrainedEntries(boardDir, envelope.lastDrainedJournalId).length;
|
|
8448
|
-
} catch {
|
|
8449
|
-
return 0;
|
|
8831
|
+
function createExecutionCommandHandlers(deps) {
|
|
8832
|
+
function invokeSourceDataFetched(sourceToken, tmpFile, callback) {
|
|
8833
|
+
const { cmd, args } = deps.getCliInvocation("source-data-fetched", ["--tmp", tmpFile, "--token", sourceToken]);
|
|
8834
|
+
deps.execCommandAsync(cmd, args, (err, stdout, stderr) => {
|
|
8835
|
+
if (err) console.error(`[source-data-fetched] call failed:`, err.message);
|
|
8836
|
+
if (stdout) console.log(stdout.trim());
|
|
8837
|
+
if (stderr) console.error(stderr.trim());
|
|
8838
|
+
});
|
|
8450
8839
|
}
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
const
|
|
8463
|
-
|
|
8464
|
-
|
|
8840
|
+
function invokeSourceDataFetchFailure(sourceToken, reason, callback) {
|
|
8841
|
+
const { cmd, args } = deps.getCliInvocation("source-data-fetch-failure", ["--token", sourceToken, "--reason", reason]);
|
|
8842
|
+
deps.execCommandAsync(cmd, args, (err) => callback(err));
|
|
8843
|
+
}
|
|
8844
|
+
function cmdRunSources(args) {
|
|
8845
|
+
const cardIdx = args.indexOf("--card");
|
|
8846
|
+
const tokenIdx = args.indexOf("--token");
|
|
8847
|
+
const rgIdx = args.indexOf("--rg");
|
|
8848
|
+
const sourceChecksumsIdx = args.indexOf("--source-checksums");
|
|
8849
|
+
const cardFilePath = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
8850
|
+
const callbackToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8851
|
+
const boardDir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
8852
|
+
const sourceChecksumsJson = sourceChecksumsIdx !== -1 ? args[sourceChecksumsIdx + 1] : void 0;
|
|
8853
|
+
const sourceChecksums = sourceChecksumsJson ? JSON.parse(sourceChecksumsJson) : void 0;
|
|
8854
|
+
if (!cardFilePath || !callbackToken || !boardDir) {
|
|
8855
|
+
console.error("Usage: board-live-cards run-sourcedefs-internal --card <path> --token <token> --rg <dir> [--source-checksums <json>]");
|
|
8856
|
+
process.exit(1);
|
|
8465
8857
|
}
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
const candidates = [
|
|
8469
|
-
process.env.SHELL,
|
|
8470
|
-
process.env.PROGRAMFILES && path__namespace.join(process.env.PROGRAMFILES, "Git", "usr", "bin", "bash.exe"),
|
|
8471
|
-
process.env.PROGRAMFILES && path__namespace.join(process.env.PROGRAMFILES, "Git", "bin", "bash.exe"),
|
|
8472
|
-
process.env["PROGRAMFILES(X86)"] && path__namespace.join(process.env["PROGRAMFILES(X86)"], "Git", "bin", "bash.exe"),
|
|
8473
|
-
process.env.LOCALAPPDATA && path__namespace.join(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")
|
|
8474
|
-
];
|
|
8475
|
-
for (const c of candidates) {
|
|
8476
|
-
if (c && /bash(\.exe)?$/i.test(c) && fs__namespace.existsSync(c)) {
|
|
8477
|
-
_gitBashPath = c;
|
|
8858
|
+
const card = JSON.parse(fs7__namespace.readFileSync(cardFilePath, "utf-8"));
|
|
8859
|
+
if (path7__namespace.basename(cardFilePath).startsWith("card-enriched-")) {
|
|
8478
8860
|
try {
|
|
8479
|
-
|
|
8861
|
+
fs7__namespace.unlinkSync(cardFilePath);
|
|
8480
8862
|
} catch {
|
|
8481
8863
|
}
|
|
8482
|
-
|
|
8864
|
+
}
|
|
8865
|
+
console.log(`[run-sourcedefs-internal] Processing card "${card.id}"`);
|
|
8866
|
+
const teConfig = deps.readTaskExecutorConfig(boardDir);
|
|
8867
|
+
const taskExecutor = teConfig?.command;
|
|
8868
|
+
const taskExecutorExtraB64 = teConfig?.extra ? Buffer.from(JSON.stringify(teConfig.extra)).toString("base64") : void 0;
|
|
8869
|
+
function runSource(src) {
|
|
8870
|
+
const sourceChecksumForInvoke = src.outputFile ? sourceChecksums?.[src.outputFile] : void 0;
|
|
8871
|
+
const sourceToken = deps.encodeSourceToken({
|
|
8872
|
+
cbk: callbackToken,
|
|
8873
|
+
rg: boardDir,
|
|
8874
|
+
cid: card.id,
|
|
8875
|
+
b: src.bindTo,
|
|
8876
|
+
d: src.outputFile ?? "",
|
|
8877
|
+
cs: sourceChecksumForInvoke
|
|
8878
|
+
});
|
|
8879
|
+
function reportFailure(reason) {
|
|
8880
|
+
invokeSourceDataFetchFailure(sourceToken, reason, (err) => {
|
|
8881
|
+
if (err) console.error(`[run-sourcedefs-internal] source-data-fetch-failure call failed:`, err.message);
|
|
8882
|
+
});
|
|
8883
|
+
}
|
|
8884
|
+
function reportFetched(outFile2) {
|
|
8885
|
+
invokeSourceDataFetched(sourceToken, outFile2);
|
|
8886
|
+
}
|
|
8887
|
+
if (taskExecutor) {
|
|
8888
|
+
if (!src.outputFile) {
|
|
8889
|
+
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}" has no outputFile configured \u2014 cannot deliver`);
|
|
8890
|
+
reportFailure("no outputFile configured");
|
|
8891
|
+
return;
|
|
8892
|
+
}
|
|
8893
|
+
const inFile = path7__namespace.join(os3__namespace.tmpdir(), `card-source-in-${src.bindTo}-${Date.now()}.json`);
|
|
8894
|
+
const outFile2 = path7__namespace.join(os3__namespace.tmpdir(), `card-source-out-${src.bindTo}-${Date.now()}.json`);
|
|
8895
|
+
const errFile = path7__namespace.join(os3__namespace.tmpdir(), `card-source-err-${src.bindTo}-${Date.now()}.txt`);
|
|
8896
|
+
const sourceForExecutor = {
|
|
8897
|
+
...src,
|
|
8898
|
+
cwd: typeof src.cwd === "string" && src.cwd ? src.cwd : path7__namespace.dirname(cardFilePath || ""),
|
|
8899
|
+
boardDir: typeof src.boardDir === "string" && src.boardDir ? src.boardDir : boardDir
|
|
8900
|
+
};
|
|
8901
|
+
deps.appendTaskExecutorLog(boardDir, sourceForExecutor, "external-task-executor");
|
|
8902
|
+
fs7__namespace.writeFileSync(inFile, JSON.stringify(sourceForExecutor, null, 2), "utf-8");
|
|
8903
|
+
const executorArgs = ["run-source-fetch", "--in", inFile, "--out", outFile2, "--err", errFile];
|
|
8904
|
+
if (taskExecutorExtraB64) executorArgs.push("--extra", taskExecutorExtraB64);
|
|
8905
|
+
console.log(`[run-sourcedefs-internal] task-executor: ${taskExecutor} ${executorArgs.join(" ")}`);
|
|
8906
|
+
try {
|
|
8907
|
+
deps.execCommandSync(taskExecutor, executorArgs, {
|
|
8908
|
+
shell: true,
|
|
8909
|
+
timeout: src.timeout ?? 12e4
|
|
8910
|
+
});
|
|
8911
|
+
} catch (err) {
|
|
8912
|
+
const reason = err.message ?? String(err);
|
|
8913
|
+
console.error(`[run-sourcedefs-internal] task-executor failed for source "${src.bindTo}":`, reason);
|
|
8914
|
+
reportFailure(reason);
|
|
8915
|
+
return;
|
|
8916
|
+
}
|
|
8917
|
+
if (fs7__namespace.existsSync(outFile2)) {
|
|
8918
|
+
reportFetched(outFile2);
|
|
8919
|
+
} else {
|
|
8920
|
+
const errMsg = fs7__namespace.existsSync(errFile) ? fs7__namespace.readFileSync(errFile, "utf-8").trim() : "executor produced no output file";
|
|
8921
|
+
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
8922
|
+
reportFailure(errMsg);
|
|
8923
|
+
}
|
|
8924
|
+
return;
|
|
8925
|
+
}
|
|
8926
|
+
if (!src.outputFile) {
|
|
8927
|
+
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}" has no outputFile configured \u2014 cannot deliver`);
|
|
8928
|
+
reportFailure("no outputFile configured");
|
|
8929
|
+
return;
|
|
8930
|
+
}
|
|
8931
|
+
const outFile = path7__namespace.join(os3__namespace.tmpdir(), `card-source-out-${src.bindTo}-${Date.now()}.json`);
|
|
8932
|
+
if (!src.cli) {
|
|
8933
|
+
const errMsg = "source.cli is required for built-in source execution";
|
|
8934
|
+
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
8935
|
+
reportFailure(errMsg);
|
|
8936
|
+
return;
|
|
8937
|
+
}
|
|
8938
|
+
const timeout = src.timeout ?? 12e4;
|
|
8939
|
+
const sourceCwd = typeof src.cwd === "string" ? src.cwd : path7__namespace.dirname(cardFilePath || "");
|
|
8940
|
+
const sourceBoardDir = typeof src.boardDir === "string" ? src.boardDir : boardDir;
|
|
8941
|
+
const sourceForBuiltInExecutor = {
|
|
8942
|
+
...src,
|
|
8943
|
+
cwd: sourceCwd,
|
|
8944
|
+
boardDir: sourceBoardDir
|
|
8945
|
+
};
|
|
8946
|
+
deps.appendTaskExecutorLog(boardDir, sourceForBuiltInExecutor, "built-in-run-source-fetch");
|
|
8947
|
+
const cmdParts = deps.splitCommandLine(src.cli);
|
|
8948
|
+
if (cmdParts.length === 0) {
|
|
8949
|
+
const errMsg = "source.cli command is empty";
|
|
8950
|
+
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
8951
|
+
reportFailure(errMsg);
|
|
8952
|
+
return;
|
|
8953
|
+
}
|
|
8954
|
+
const rawCmd = cmdParts[0];
|
|
8955
|
+
const { cmd, args: cliArgs } = deps.resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
8956
|
+
let stdout;
|
|
8957
|
+
try {
|
|
8958
|
+
stdout = deps.execCommandSync(cmd, cliArgs, {
|
|
8959
|
+
shell: false,
|
|
8960
|
+
encoding: "utf-8",
|
|
8961
|
+
timeout,
|
|
8962
|
+
cwd: sourceCwd,
|
|
8963
|
+
env: {
|
|
8964
|
+
...process.env,
|
|
8965
|
+
...sourceBoardDir ? { BOARD_DIR: sourceBoardDir } : {}
|
|
8966
|
+
}
|
|
8967
|
+
});
|
|
8968
|
+
} catch (err) {
|
|
8969
|
+
const reason = err.message ?? String(err);
|
|
8970
|
+
console.error(`[run-sourcedefs-internal] source fetch failed for source "${src.bindTo}":`, reason);
|
|
8971
|
+
reportFailure(reason);
|
|
8972
|
+
return;
|
|
8973
|
+
}
|
|
8974
|
+
fs7__namespace.writeFileSync(outFile, stdout.trim(), "utf-8");
|
|
8975
|
+
reportFetched(outFile);
|
|
8976
|
+
}
|
|
8977
|
+
const source_defs = card.source_defs ?? [];
|
|
8978
|
+
for (const src of source_defs) {
|
|
8979
|
+
runSource(src);
|
|
8483
8980
|
}
|
|
8484
8981
|
}
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8982
|
+
function cmdRunInference(args) {
|
|
8983
|
+
const inIdx = args.indexOf("--in");
|
|
8984
|
+
const tokenIdx = args.indexOf("--token");
|
|
8985
|
+
const inFile = inIdx !== -1 ? args[inIdx + 1] : void 0;
|
|
8986
|
+
const inferenceToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
8987
|
+
if (!inFile || !inferenceToken) {
|
|
8988
|
+
console.error("Usage: board-live-cards run-inference-internal --in <input.json> --token <inference-token>");
|
|
8989
|
+
process.exit(1);
|
|
8990
|
+
}
|
|
8991
|
+
const decodedToken = deps.decodeSourceToken(inferenceToken);
|
|
8992
|
+
if (!decodedToken) {
|
|
8993
|
+
console.error("Invalid inference token");
|
|
8994
|
+
process.exit(1);
|
|
8995
|
+
}
|
|
8996
|
+
const callbackToken = decodedToken.cbk;
|
|
8997
|
+
const boardDir = decodedToken.rg;
|
|
8998
|
+
const cbkDecoded = deps.decodeCallbackToken(callbackToken);
|
|
8999
|
+
if (!cbkDecoded) {
|
|
9000
|
+
console.error("Invalid callback token embedded in inference token");
|
|
9001
|
+
process.exit(1);
|
|
9002
|
+
}
|
|
9003
|
+
function spawnInferenceDone(tmpFile) {
|
|
9004
|
+
const { cmd, args: cliArgs } = deps.getCliInvocation("inference-done", ["--tmp", tmpFile, "--token", inferenceToken]);
|
|
9005
|
+
deps.spawnDetachedCommand(cmd, cliArgs);
|
|
9006
|
+
}
|
|
9007
|
+
function spawnInferenceDoneError(reason) {
|
|
9008
|
+
const tmpFile = path7__namespace.join(os3__namespace.tmpdir(), `card-inference-err-${Date.now()}.json`);
|
|
9009
|
+
fs7__namespace.writeFileSync(tmpFile, JSON.stringify({ isTaskCompleted: false, reason }), "utf-8");
|
|
9010
|
+
spawnInferenceDone(tmpFile);
|
|
9011
|
+
}
|
|
9012
|
+
if (!fs7__namespace.existsSync(inFile)) {
|
|
9013
|
+
spawnInferenceDoneError(`inference input not found: ${inFile}`);
|
|
8502
9014
|
return;
|
|
8503
9015
|
}
|
|
8504
|
-
const
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
const output = child_process.execFileSync(cmd, args, {
|
|
8517
|
-
shell: shouldUseShellForCommand(cmd, options?.shell),
|
|
8518
|
-
timeout: options?.timeout,
|
|
8519
|
-
encoding: options?.encoding,
|
|
8520
|
-
cwd: options?.cwd,
|
|
8521
|
-
windowsHide: true,
|
|
8522
|
-
env: options?.env
|
|
8523
|
-
});
|
|
8524
|
-
return typeof output === "string" ? output : output.toString("utf-8");
|
|
8525
|
-
}
|
|
8526
|
-
function execCommandAsync(cmd, args, callback) {
|
|
8527
|
-
child_process.execFile(
|
|
8528
|
-
cmd,
|
|
8529
|
-
args,
|
|
8530
|
-
{ shell: shouldUseShellForCommand(cmd), encoding: "utf8", windowsHide: true },
|
|
8531
|
-
(err, stdout, stderr) => callback(err ?? null, stdout, stderr)
|
|
8532
|
-
);
|
|
8533
|
-
}
|
|
8534
|
-
function splitCommandLine(command) {
|
|
8535
|
-
const tokens = [];
|
|
8536
|
-
let current = "";
|
|
8537
|
-
let quote = null;
|
|
8538
|
-
for (const ch of command.trim()) {
|
|
8539
|
-
if (quote) {
|
|
8540
|
-
if (ch === quote) {
|
|
8541
|
-
quote = null;
|
|
8542
|
-
} else {
|
|
8543
|
-
current += ch;
|
|
8544
|
-
}
|
|
8545
|
-
continue;
|
|
9016
|
+
const adapterFile = path7__namespace.join(boardDir, deps.INFERENCE_ADAPTER_FILE);
|
|
9017
|
+
const inferenceAdapter = fs7__namespace.existsSync(adapterFile) ? fs7__namespace.readFileSync(adapterFile, "utf-8").trim() : void 0;
|
|
9018
|
+
if (!inferenceAdapter) {
|
|
9019
|
+
spawnInferenceDoneError(`inference adapter is not configured (${deps.INFERENCE_ADAPTER_FILE})`);
|
|
9020
|
+
return;
|
|
9021
|
+
}
|
|
9022
|
+
const outFile = path7__namespace.join(os3__namespace.tmpdir(), `card-inference-out-${Date.now()}.json`);
|
|
9023
|
+
const errFile = path7__namespace.join(os3__namespace.tmpdir(), `card-inference-err-${Date.now()}.txt`);
|
|
9024
|
+
const adapterParts = deps.splitCommandLine(inferenceAdapter);
|
|
9025
|
+
if (adapterParts.length === 0) {
|
|
9026
|
+
spawnInferenceDoneError("inference adapter command is empty");
|
|
9027
|
+
return;
|
|
8546
9028
|
}
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
9029
|
+
const adapterRawCmd = adapterParts[0];
|
|
9030
|
+
const adapterRawArgs = adapterParts.slice(1);
|
|
9031
|
+
const { cmd: adapterCmd, args: adapterArgsPrefix } = deps.resolveCommandInvocation(adapterRawCmd, adapterRawArgs);
|
|
9032
|
+
const adapterArgs = [...adapterArgsPrefix, "run-inference", "--in", inFile, "--out", outFile, "--err", errFile];
|
|
9033
|
+
try {
|
|
9034
|
+
deps.execCommandSync(adapterCmd, adapterArgs, {
|
|
9035
|
+
shell: false,
|
|
9036
|
+
timeout: 12e4,
|
|
9037
|
+
cwd: boardDir,
|
|
9038
|
+
env: {
|
|
9039
|
+
...process.env,
|
|
9040
|
+
BOARD_DIR: boardDir
|
|
9041
|
+
}
|
|
9042
|
+
});
|
|
9043
|
+
} catch (err) {
|
|
9044
|
+
const reason = err.message ?? String(err);
|
|
9045
|
+
spawnInferenceDoneError(reason);
|
|
9046
|
+
return;
|
|
8550
9047
|
}
|
|
8551
|
-
if (
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
}
|
|
8556
|
-
continue;
|
|
9048
|
+
if (!fs7__namespace.existsSync(outFile)) {
|
|
9049
|
+
const errMsg = fs7__namespace.existsSync(errFile) ? fs7__namespace.readFileSync(errFile, "utf-8").trim() : "inference adapter produced no output file";
|
|
9050
|
+
spawnInferenceDoneError(errMsg);
|
|
9051
|
+
return;
|
|
8557
9052
|
}
|
|
8558
|
-
|
|
9053
|
+
spawnInferenceDone(outFile);
|
|
8559
9054
|
}
|
|
8560
|
-
|
|
8561
|
-
|
|
9055
|
+
function cmdInferenceDone(args) {
|
|
9056
|
+
const tmpIdx = args.indexOf("--tmp");
|
|
9057
|
+
const tokenIdx = args.indexOf("--token");
|
|
9058
|
+
const tmpFile = tmpIdx !== -1 ? args[tmpIdx + 1] : void 0;
|
|
9059
|
+
const inferenceToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
9060
|
+
if (!tmpFile || !inferenceToken) {
|
|
9061
|
+
console.error("Usage: board-live-cards inference-done --tmp <result.json> --token <inference-token>");
|
|
9062
|
+
process.exit(1);
|
|
9063
|
+
}
|
|
9064
|
+
const decodedToken = deps.decodeSourceToken(inferenceToken);
|
|
9065
|
+
if (!decodedToken) {
|
|
9066
|
+
console.error("Invalid inference token");
|
|
9067
|
+
process.exit(1);
|
|
9068
|
+
}
|
|
9069
|
+
const { cbk: callbackToken, rg: dir, cs: inputChecksum } = decodedToken;
|
|
9070
|
+
const decoded = deps.decodeCallbackToken(callbackToken);
|
|
9071
|
+
if (!decoded) {
|
|
9072
|
+
console.error("Invalid callback token embedded in inference token");
|
|
9073
|
+
process.exit(1);
|
|
9074
|
+
}
|
|
9075
|
+
const taskName = decoded.taskName;
|
|
9076
|
+
const cardPath = deps.lookupCardPath(dir, taskName);
|
|
9077
|
+
if (!cardPath) {
|
|
9078
|
+
console.error(`Card file for task "${taskName}" not found in inventory`);
|
|
9079
|
+
process.exit(1);
|
|
9080
|
+
}
|
|
9081
|
+
let result = {};
|
|
9082
|
+
if (fs7__namespace.existsSync(tmpFile)) {
|
|
9083
|
+
try {
|
|
9084
|
+
result = JSON.parse(fs7__namespace.readFileSync(tmpFile, "utf-8").trim());
|
|
9085
|
+
} catch (err) {
|
|
9086
|
+
result = { isTaskCompleted: false, reason: `failed to parse inference result: ${err instanceof Error ? err.message : String(err)}` };
|
|
9087
|
+
}
|
|
9088
|
+
try {
|
|
9089
|
+
fs7__namespace.unlinkSync(tmpFile);
|
|
9090
|
+
} catch {
|
|
9091
|
+
}
|
|
9092
|
+
} else {
|
|
9093
|
+
result = { isTaskCompleted: false, reason: `inference result file not found: ${tmpFile}` };
|
|
9094
|
+
}
|
|
9095
|
+
const isTaskCompletedFlag = result.isTaskCompleted === true;
|
|
9096
|
+
const inferenceCompletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9097
|
+
const card = JSON.parse(fs7__namespace.readFileSync(cardPath, "utf-8"));
|
|
9098
|
+
if (!card.card_data) card.card_data = {};
|
|
9099
|
+
const cardData = card.card_data;
|
|
9100
|
+
const existingInference = cardData.llm_task_completion_inference && typeof cardData.llm_task_completion_inference === "object" ? cardData.llm_task_completion_inference : {};
|
|
9101
|
+
cardData.llm_task_completion_inference = {
|
|
9102
|
+
...existingInference,
|
|
9103
|
+
isTaskCompleted: isTaskCompletedFlag,
|
|
9104
|
+
reason: typeof result.reason === "string" ? result.reason : "",
|
|
9105
|
+
evidence: typeof result.evidence === "string" ? result.evidence : "",
|
|
9106
|
+
inferenceCompletedAt
|
|
9107
|
+
};
|
|
9108
|
+
fs7__namespace.writeFileSync(cardPath, JSON.stringify(card, null, 2), "utf-8");
|
|
9109
|
+
const runtimePath = path7__namespace.join(dir, taskName, "runtime.json");
|
|
9110
|
+
let runtime = { _sources: {} };
|
|
9111
|
+
if (fs7__namespace.existsSync(runtimePath)) {
|
|
9112
|
+
try {
|
|
9113
|
+
runtime = JSON.parse(fs7__namespace.readFileSync(runtimePath, "utf-8"));
|
|
9114
|
+
} catch {
|
|
9115
|
+
}
|
|
9116
|
+
}
|
|
9117
|
+
const inferenceEntry = runtime._inferenceEntry ?? {};
|
|
9118
|
+
runtime._inferenceEntry = deps.nextEntryAfterFetchDelivery(inferenceEntry, inferenceCompletedAt);
|
|
9119
|
+
fs7__namespace.mkdirSync(path7__namespace.dirname(runtimePath), { recursive: true });
|
|
9120
|
+
fs7__namespace.writeFileSync(runtimePath, JSON.stringify(runtime, null, 2), "utf-8");
|
|
9121
|
+
deps.appendEventToJournal(dir, {
|
|
9122
|
+
type: "task-progress",
|
|
9123
|
+
taskName,
|
|
9124
|
+
update: {
|
|
9125
|
+
kind: "inference-done",
|
|
9126
|
+
isTaskCompleted: isTaskCompletedFlag,
|
|
9127
|
+
inputChecksum
|
|
9128
|
+
},
|
|
9129
|
+
timestamp: inferenceCompletedAt
|
|
9130
|
+
});
|
|
9131
|
+
void deps.processAccumulatedEventsInfinitePass(dir);
|
|
9132
|
+
}
|
|
9133
|
+
async function cmdTryDrain(args) {
|
|
9134
|
+
const rgIdx = args.indexOf("--rg");
|
|
9135
|
+
const inlineLoop = args.includes("--inline-loop");
|
|
9136
|
+
const boardDir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9137
|
+
if (!boardDir) {
|
|
9138
|
+
console.error("Usage: board-live-cards process-accumulated-events --rg <dir>");
|
|
9139
|
+
process.exit(1);
|
|
9140
|
+
}
|
|
9141
|
+
await deps.processAccumulatedEventsForced(boardDir, { inlineLoop });
|
|
8562
9142
|
}
|
|
8563
|
-
|
|
8564
|
-
return tokens;
|
|
9143
|
+
return { cmdRunSources, cmdRunInference, cmdInferenceDone, cmdTryDrain };
|
|
8565
9144
|
}
|
|
8566
|
-
function
|
|
8567
|
-
if (
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
9145
|
+
function deepGet(obj, path8) {
|
|
9146
|
+
if (!path8 || !obj) return void 0;
|
|
9147
|
+
const parts = path8.split(".");
|
|
9148
|
+
let cur = obj;
|
|
9149
|
+
for (let i = 0; i < parts.length; i++) {
|
|
9150
|
+
if (cur == null) return void 0;
|
|
9151
|
+
cur = cur[parts[i]];
|
|
8572
9152
|
}
|
|
8573
|
-
return
|
|
9153
|
+
return cur;
|
|
8574
9154
|
}
|
|
8575
|
-
function
|
|
8576
|
-
const
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
return false;
|
|
9155
|
+
function deepSet(obj, path8, value) {
|
|
9156
|
+
const parts = path8.split(".");
|
|
9157
|
+
let cur = obj;
|
|
9158
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
9159
|
+
if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") cur[parts[i]] = {};
|
|
9160
|
+
cur = cur[parts[i]];
|
|
8582
9161
|
}
|
|
9162
|
+
cur[parts[parts.length - 1]] = value;
|
|
8583
9163
|
}
|
|
8584
|
-
async function
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
9164
|
+
async function run(node, options) {
|
|
9165
|
+
if (!node?.compute?.length) return node;
|
|
9166
|
+
if (!node.card_data) node.card_data = {};
|
|
9167
|
+
node.computed_values = {};
|
|
9168
|
+
node._sourcesData = options?.sourcesData ?? {};
|
|
9169
|
+
const ctx = {
|
|
9170
|
+
card_data: node.card_data,
|
|
9171
|
+
requires: node.requires ?? {},
|
|
9172
|
+
fetched_sources: node._sourcesData,
|
|
9173
|
+
computed_values: node.computed_values
|
|
9174
|
+
};
|
|
9175
|
+
for (const step of node.compute) {
|
|
9176
|
+
try {
|
|
9177
|
+
const val = await jsonata2__default.default(step.expr).evaluate(ctx);
|
|
9178
|
+
deepSet(node.computed_values, step.bindTo, val);
|
|
9179
|
+
ctx.computed_values = node.computed_values;
|
|
9180
|
+
} catch (err) {
|
|
9181
|
+
console.error(`CardCompute.run error on "${node.id ?? "?"}.${step.bindTo}":`, err);
|
|
9182
|
+
}
|
|
8589
9183
|
}
|
|
8590
|
-
return
|
|
8591
|
-
}
|
|
8592
|
-
function shouldAvoidDetachedProcessSpawn() {
|
|
8593
|
-
return process.env.BOARD_LIVE_CARDS_NO_SPAWN === "1";
|
|
9184
|
+
return node;
|
|
8594
9185
|
}
|
|
8595
|
-
async function
|
|
8596
|
-
const
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
try {
|
|
8604
|
-
const { rg, journal } = createBoardReactiveGraph(boardDir);
|
|
8605
|
-
const undrained = journal.drain();
|
|
8606
|
-
rg.pushAll(undrained);
|
|
8607
|
-
await rg.dispose({ wait: true });
|
|
8608
|
-
saveBoard(boardDir, rg, journal);
|
|
8609
|
-
return true;
|
|
8610
|
-
} finally {
|
|
8611
|
-
release();
|
|
8612
|
-
}
|
|
9186
|
+
async function evalExpr(expr, node) {
|
|
9187
|
+
const ctx = {
|
|
9188
|
+
card_data: node.card_data ?? {},
|
|
9189
|
+
requires: node.requires ?? {},
|
|
9190
|
+
fetched_sources: node._sourcesData ?? {},
|
|
9191
|
+
computed_values: node.computed_values ?? {}
|
|
9192
|
+
};
|
|
9193
|
+
return jsonata2__default.default(expr).evaluate(ctx);
|
|
8613
9194
|
}
|
|
8614
|
-
|
|
8615
|
-
if (
|
|
8616
|
-
return
|
|
9195
|
+
function resolve4(node, path8) {
|
|
9196
|
+
if (path8.startsWith("fetched_sources.")) {
|
|
9197
|
+
return deepGet(node._sourcesData ?? {}, path8.slice("fetched_sources.".length));
|
|
8617
9198
|
}
|
|
8618
|
-
return
|
|
8619
|
-
}
|
|
8620
|
-
async function processAccumulatedEventsForced(boardDir, options) {
|
|
8621
|
-
await processAccumulatedEvents(boardDir);
|
|
8622
|
-
await processAccumulatedEventsInfinitePass(boardDir, 50, options);
|
|
8623
|
-
}
|
|
8624
|
-
function liveCardToTaskConfig(card) {
|
|
8625
|
-
const requires = card.requires;
|
|
8626
|
-
const provides = card.provides?.map((p) => p.bindTo) ?? [];
|
|
8627
|
-
return {
|
|
8628
|
-
requires: requires && requires.length > 0 ? requires : void 0,
|
|
8629
|
-
provides,
|
|
8630
|
-
taskHandlers: ["card-handler"],
|
|
8631
|
-
description: card.meta?.title ?? card.id
|
|
8632
|
-
};
|
|
9199
|
+
return deepGet(node, path8);
|
|
8633
9200
|
}
|
|
8634
|
-
var
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
9201
|
+
var VALID_ELEMENT_KINDS = /* @__PURE__ */ new Set([
|
|
9202
|
+
"metric",
|
|
9203
|
+
"table",
|
|
9204
|
+
"editable-table",
|
|
9205
|
+
"chart",
|
|
9206
|
+
"form",
|
|
9207
|
+
"filter",
|
|
9208
|
+
"list",
|
|
9209
|
+
"notes",
|
|
9210
|
+
"todo",
|
|
9211
|
+
"alert",
|
|
9212
|
+
"narrative",
|
|
9213
|
+
"badge",
|
|
9214
|
+
"text",
|
|
9215
|
+
"markdown",
|
|
9216
|
+
"ref",
|
|
9217
|
+
"custom",
|
|
9218
|
+
"actions"
|
|
9219
|
+
]);
|
|
9220
|
+
var ALLOWED_KEYS = /* @__PURE__ */ new Set(["id", "meta", "requires", "provides", "view", "card_data", "compute", "source_defs"]);
|
|
9221
|
+
function validateNode(node) {
|
|
9222
|
+
const errors = [];
|
|
9223
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) {
|
|
9224
|
+
return { ok: false, errors: ["Node must be a non-null object"] };
|
|
8641
9225
|
}
|
|
8642
|
-
const
|
|
8643
|
-
if (
|
|
8644
|
-
|
|
9226
|
+
const n = node;
|
|
9227
|
+
if (typeof n.id !== "string" || !n.id) errors.push("id: required, must be a non-empty string");
|
|
9228
|
+
for (const key of Object.keys(n)) {
|
|
9229
|
+
if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown top-level key: "${key}"`);
|
|
8645
9230
|
}
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
}
|
|
8649
|
-
function invokeRunSources(boardDir, cardPath, callbackToken, callback) {
|
|
8650
|
-
const args = ["--card", cardPath, "--token", callbackToken, "--rg", boardDir];
|
|
8651
|
-
const { cmd, args: cmdArgs } = getCliInvocation("run-sourcedefs-internal", args);
|
|
8652
|
-
try {
|
|
8653
|
-
spawnDetachedCommand(cmd, cmdArgs);
|
|
8654
|
-
callback(null);
|
|
8655
|
-
} catch (err) {
|
|
8656
|
-
callback(err instanceof Error ? err : new Error(String(err)));
|
|
9231
|
+
if (n.card_data == null || typeof n.card_data !== "object" || Array.isArray(n.card_data)) {
|
|
9232
|
+
errors.push("card_data: required, must be an object");
|
|
8657
9233
|
}
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
callback(err instanceof Error ? err : new Error(String(err)));
|
|
9234
|
+
if (n.meta != null) {
|
|
9235
|
+
if (typeof n.meta !== "object" || Array.isArray(n.meta)) {
|
|
9236
|
+
errors.push("meta: must be an object");
|
|
9237
|
+
} else {
|
|
9238
|
+
const meta = n.meta;
|
|
9239
|
+
if (meta.title != null && typeof meta.title !== "string") errors.push("meta.title: must be a string");
|
|
9240
|
+
if (meta.tags != null && !Array.isArray(meta.tags)) errors.push("meta.tags: must be an array");
|
|
9241
|
+
}
|
|
8667
9242
|
}
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
9243
|
+
if (n.requires != null && !Array.isArray(n.requires)) errors.push("requires: must be an array of strings");
|
|
9244
|
+
if (n.provides != null) {
|
|
9245
|
+
if (!Array.isArray(n.provides)) {
|
|
9246
|
+
errors.push("provides: must be an array of { bindTo, ref } bindings");
|
|
9247
|
+
} else {
|
|
9248
|
+
n.provides.forEach((p, i) => {
|
|
9249
|
+
if (!p || typeof p !== "object" || Array.isArray(p)) {
|
|
9250
|
+
errors.push(`provides[${i}]: must be an object with bindTo and ref`);
|
|
9251
|
+
} else {
|
|
9252
|
+
const b = p;
|
|
9253
|
+
if (typeof b.bindTo !== "string" || !b.bindTo) errors.push(`provides[${i}]: missing required "bindTo" string`);
|
|
9254
|
+
if (typeof b.ref !== "string" || !b.ref) errors.push(`provides[${i}]: missing required "ref" string`);
|
|
9255
|
+
}
|
|
9256
|
+
});
|
|
9257
|
+
}
|
|
8679
9258
|
}
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
9259
|
+
if (n.compute != null) {
|
|
9260
|
+
if (!Array.isArray(n.compute)) {
|
|
9261
|
+
errors.push("compute: must be an array of compute steps");
|
|
9262
|
+
} else {
|
|
9263
|
+
n.compute.forEach((step, i) => {
|
|
9264
|
+
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
9265
|
+
errors.push(`compute[${i}]: must be a compute step object`);
|
|
9266
|
+
} else {
|
|
9267
|
+
const s = step;
|
|
9268
|
+
if (typeof s.bindTo !== "string" || !s.bindTo) errors.push(`compute[${i}]: missing required "bindTo" property`);
|
|
9269
|
+
if (typeof s.expr !== "string" || !s.expr) errors.push(`compute[${i}]: missing required "expr" string (JSONata expression)`);
|
|
9270
|
+
}
|
|
9271
|
+
});
|
|
9272
|
+
}
|
|
8691
9273
|
}
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
}
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
}
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
if (!cardPath) return "task-initiate-failure";
|
|
8714
|
-
const card = JSON.parse(fs__namespace.readFileSync(cardPath, "utf-8"));
|
|
8715
|
-
const cardId = card.id;
|
|
8716
|
-
const cardState = card.card_data ?? {};
|
|
8717
|
-
const allSources = card.source_defs ?? [];
|
|
8718
|
-
const requiredSources = allSources.filter((s) => s.optionalForCompletionGating !== true);
|
|
8719
|
-
const runtime = readRuntimeState(boardDir, cardId);
|
|
8720
|
-
let runtimeDirty = false;
|
|
8721
|
-
const currentExecutionCount = input.taskState?.executionCount ?? 0;
|
|
8722
|
-
if (typeof runtime._lastExecutionCount === "number" && runtime._lastExecutionCount !== currentExecutionCount) {
|
|
8723
|
-
runtime._sources = {};
|
|
8724
|
-
runtime._inferenceEntry = void 0;
|
|
8725
|
-
}
|
|
8726
|
-
if (runtime._lastExecutionCount !== currentExecutionCount) {
|
|
8727
|
-
runtime._lastExecutionCount = currentExecutionCount;
|
|
8728
|
-
runtimeDirty = true;
|
|
8729
|
-
}
|
|
8730
|
-
if (input.update) {
|
|
8731
|
-
const u = input.update;
|
|
8732
|
-
const outputFile = u.outputFile;
|
|
8733
|
-
if (outputFile) {
|
|
8734
|
-
if (!runtime._sources[outputFile]) runtime._sources[outputFile] = {};
|
|
8735
|
-
const entry = runtime._sources[outputFile];
|
|
8736
|
-
if (u.failure) {
|
|
8737
|
-
runtime._sources[outputFile] = nextEntryAfterFetchFailure(entry, u.reason ?? "unknown");
|
|
8738
|
-
runtimeDirty = true;
|
|
9274
|
+
if (n.source_defs != null) {
|
|
9275
|
+
if (!Array.isArray(n.source_defs)) {
|
|
9276
|
+
errors.push("source_defs: must be an array");
|
|
9277
|
+
} else {
|
|
9278
|
+
const bindTos = /* @__PURE__ */ new Set();
|
|
9279
|
+
const outputFiles = /* @__PURE__ */ new Set();
|
|
9280
|
+
n.source_defs.forEach((src, i) => {
|
|
9281
|
+
if (!src || typeof src !== "object" || Array.isArray(src)) {
|
|
9282
|
+
errors.push(`source_defs[${i}]: must be an object`);
|
|
9283
|
+
} else {
|
|
9284
|
+
const s = src;
|
|
9285
|
+
if (typeof s.bindTo !== "string" || !s.bindTo) {
|
|
9286
|
+
errors.push(`source_defs[${i}]: missing required "bindTo" property`);
|
|
9287
|
+
} else {
|
|
9288
|
+
if (bindTos.has(s.bindTo)) {
|
|
9289
|
+
errors.push(`source_defs[${i}]: bindTo "${s.bindTo}" is not unique across source_defs`);
|
|
9290
|
+
}
|
|
9291
|
+
bindTos.add(s.bindTo);
|
|
9292
|
+
}
|
|
9293
|
+
if (typeof s.outputFile !== "string" || !s.outputFile) {
|
|
9294
|
+
errors.push(`source_defs[${i}]: missing required "outputFile" property`);
|
|
8739
9295
|
} else {
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
);
|
|
8744
|
-
|
|
9296
|
+
if (outputFiles.has(s.outputFile)) {
|
|
9297
|
+
errors.push(`source_defs[${i}]: outputFile "${s.outputFile}" is not unique across source_defs`);
|
|
9298
|
+
}
|
|
9299
|
+
outputFiles.add(s.outputFile);
|
|
9300
|
+
}
|
|
9301
|
+
if (s.optionalForCompletionGating != null && typeof s.optionalForCompletionGating !== "boolean") {
|
|
9302
|
+
errors.push(`source_defs[${i}]: optionalForCompletionGating must be a boolean`);
|
|
8745
9303
|
}
|
|
8746
|
-
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
8747
9304
|
}
|
|
9305
|
+
});
|
|
9306
|
+
}
|
|
9307
|
+
}
|
|
9308
|
+
if (n.view != null) {
|
|
9309
|
+
if (typeof n.view !== "object" || Array.isArray(n.view)) {
|
|
9310
|
+
errors.push("view: must be an object");
|
|
9311
|
+
} else {
|
|
9312
|
+
const view = n.view;
|
|
9313
|
+
if (!Array.isArray(view.elements) || view.elements.length === 0) {
|
|
9314
|
+
errors.push("view.elements: required, must be a non-empty array");
|
|
9315
|
+
} else {
|
|
9316
|
+
view.elements.forEach((elem, i) => {
|
|
9317
|
+
if (!elem || typeof elem !== "object") {
|
|
9318
|
+
errors.push(`view.elements[${i}]: must be an object`);
|
|
9319
|
+
return;
|
|
9320
|
+
}
|
|
9321
|
+
if (!elem.kind || typeof elem.kind !== "string") {
|
|
9322
|
+
errors.push(`view.elements[${i}].kind: required, must be a string`);
|
|
9323
|
+
} else if (!VALID_ELEMENT_KINDS.has(elem.kind)) {
|
|
9324
|
+
errors.push(`view.elements[${i}].kind: unknown kind "${elem.kind}". Valid: ${[...VALID_ELEMENT_KINDS].join(", ")}`);
|
|
9325
|
+
}
|
|
9326
|
+
if (elem.data != null && (typeof elem.data !== "object" || Array.isArray(elem.data))) {
|
|
9327
|
+
errors.push(`view.elements[${i}].data: must be an object`);
|
|
9328
|
+
}
|
|
9329
|
+
});
|
|
8748
9330
|
}
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
9331
|
+
if (view.layout != null && (typeof view.layout !== "object" || Array.isArray(view.layout))) errors.push("view.layout: must be an object");
|
|
9332
|
+
if (view.features != null && (typeof view.features !== "object" || Array.isArray(view.features))) errors.push("view.features: must be an object");
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9335
|
+
return { ok: errors.length === 0, errors };
|
|
9336
|
+
}
|
|
9337
|
+
async function enrichSources(source_defs, context) {
|
|
9338
|
+
if (!source_defs || source_defs.length === 0) return [];
|
|
9339
|
+
const evalCtx = {
|
|
9340
|
+
card_data: context.card_data ?? {},
|
|
9341
|
+
requires: context.requires ?? {}
|
|
9342
|
+
};
|
|
9343
|
+
return Promise.all(
|
|
9344
|
+
source_defs.map(async (src) => {
|
|
9345
|
+
const _projections = {};
|
|
9346
|
+
if (src.projections && typeof src.projections === "object" && !Array.isArray(src.projections)) {
|
|
9347
|
+
for (const [key, expr] of Object.entries(src.projections)) {
|
|
9348
|
+
if (typeof expr === "string" && expr.trim().length > 0) {
|
|
8755
9349
|
try {
|
|
8756
|
-
|
|
9350
|
+
_projections[key] = await jsonata2__default.default(expr).evaluate(evalCtx);
|
|
8757
9351
|
} catch {
|
|
8758
|
-
|
|
9352
|
+
_projections[key] = void 0;
|
|
8759
9353
|
}
|
|
8760
9354
|
}
|
|
8761
9355
|
}
|
|
8762
9356
|
}
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
9357
|
+
return { ...src, _projections };
|
|
9358
|
+
})
|
|
9359
|
+
);
|
|
9360
|
+
}
|
|
9361
|
+
var CardCompute = {
|
|
9362
|
+
run,
|
|
9363
|
+
eval: evalExpr,
|
|
9364
|
+
resolve: resolve4,
|
|
9365
|
+
validate: validateNode,
|
|
9366
|
+
enrichSources
|
|
9367
|
+
};
|
|
9368
|
+
|
|
9369
|
+
// src/cli/board-live-cards-lib-types.ts
|
|
9370
|
+
function isSourceInFlight(entry) {
|
|
9371
|
+
if (!entry?.lastRequestedAt) return false;
|
|
9372
|
+
return !entry.lastFetchedAt || entry.lastFetchedAt < entry.lastRequestedAt;
|
|
9373
|
+
}
|
|
9374
|
+
function decideSourceAction(entry, queueRequestedAt) {
|
|
9375
|
+
if (!entry?.lastRequestedAt) return "dispatch";
|
|
9376
|
+
const inFlight = isSourceInFlight(entry);
|
|
9377
|
+
if (inFlight) return "in-flight";
|
|
9378
|
+
if (!entry.lastFetchedAt) return "dispatch";
|
|
9379
|
+
if (entry.lastFetchedAt < queueRequestedAt) return "dispatch";
|
|
9380
|
+
return "idle";
|
|
9381
|
+
}
|
|
9382
|
+
function nextEntryAfterFetchDelivery(entry, fetchedAt) {
|
|
9383
|
+
const next = { ...entry, lastFetchedAt: fetchedAt };
|
|
9384
|
+
delete next.lastError;
|
|
9385
|
+
return next;
|
|
9386
|
+
}
|
|
9387
|
+
function nextEntryAfterFetchFailure(entry, reason) {
|
|
9388
|
+
const next = { ...entry, lastError: reason };
|
|
9389
|
+
delete next.lastFetchedAt;
|
|
9390
|
+
return next;
|
|
9391
|
+
}
|
|
9392
|
+
|
|
9393
|
+
// src/cli/board-live-cards-lib-card-handler.ts
|
|
9394
|
+
var DEFAULT_TASK_COMPLETION_RULE = "all_required_sources_fetched";
|
|
9395
|
+
function createCardHandlerFn(boardDir, adapters) {
|
|
9396
|
+
return async (input) => {
|
|
9397
|
+
const cardPath = adapters.cardStore.lookupCardPath(boardDir, input.nodeId);
|
|
9398
|
+
if (!cardPath) return "task-initiate-failure";
|
|
9399
|
+
const card = adapters.cardStore.readCard(cardPath);
|
|
9400
|
+
if (!card) return "task-initiate-failure";
|
|
9401
|
+
const cardId = card.id;
|
|
9402
|
+
const cardState = card.card_data ?? {};
|
|
9403
|
+
const allSources = card.source_defs ?? [];
|
|
9404
|
+
const requiredSources = allSources.filter((s) => s.optionalForCompletionGating !== true);
|
|
9405
|
+
const session = adapters.runtimeStore.openSession(boardDir, cardId);
|
|
9406
|
+
const currentExecutionCount = input.taskState?.executionCount ?? 0;
|
|
9407
|
+
const lastExecCount = session.getLastExecutionCount();
|
|
9408
|
+
if (typeof lastExecCount === "number" && lastExecCount !== currentExecutionCount) {
|
|
9409
|
+
session.resetSources();
|
|
9410
|
+
session.resetInferenceEntry();
|
|
9411
|
+
}
|
|
9412
|
+
if (lastExecCount !== currentExecutionCount) {
|
|
9413
|
+
session.setLastExecutionCount(currentExecutionCount);
|
|
9414
|
+
}
|
|
9415
|
+
if (input.update) {
|
|
9416
|
+
const u = input.update;
|
|
9417
|
+
const outputFile = u.outputFile;
|
|
9418
|
+
if (outputFile) {
|
|
9419
|
+
const entry = session.getSourceEntry(outputFile);
|
|
9420
|
+
if (u.failure) {
|
|
9421
|
+
session.setSourceEntry(outputFile, nextEntryAfterFetchFailure(entry, u.reason ?? "unknown"));
|
|
8768
9422
|
} else {
|
|
8769
|
-
|
|
9423
|
+
session.setSourceEntry(outputFile, nextEntryAfterFetchDelivery(
|
|
9424
|
+
entry,
|
|
9425
|
+
u.fetchedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
9426
|
+
));
|
|
8770
9427
|
}
|
|
9428
|
+
session.flush();
|
|
8771
9429
|
}
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
computeNode._sourcesData = sourcesData;
|
|
8780
|
-
if (card.compute) {
|
|
8781
|
-
await CardCompute.run(computeNode, { sourcesData });
|
|
8782
|
-
}
|
|
8783
|
-
const cvPath = resolveComputedValuesPath(boardDir, cardId);
|
|
8784
|
-
writeJsonAtomic(cvPath, {
|
|
8785
|
-
schema_version: "v1",
|
|
8786
|
-
card_id: cardId,
|
|
8787
|
-
computed_values: computeNode.computed_values ?? {}
|
|
8788
|
-
});
|
|
8789
|
-
const enrichedCard = { ...card };
|
|
8790
|
-
const enrichedSources = await CardCompute.enrichSources(
|
|
8791
|
-
Array.isArray(card.source_defs) ? card.source_defs : void 0,
|
|
8792
|
-
{
|
|
8793
|
-
card_data: card.card_data,
|
|
8794
|
-
requires,
|
|
8795
|
-
sourcesData,
|
|
8796
|
-
computed_values: computeNode.computed_values
|
|
9430
|
+
}
|
|
9431
|
+
const sourcesData = {};
|
|
9432
|
+
for (const src of allSources) {
|
|
9433
|
+
if (src.outputFile) {
|
|
9434
|
+
const content = adapters.cardStore.readSourceFileContent(boardDir, cardId, src.outputFile);
|
|
9435
|
+
if (content !== null) {
|
|
9436
|
+
sourcesData[src.bindTo] = content;
|
|
8797
9437
|
}
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
const requires = {};
|
|
9441
|
+
for (const [token, taskData] of Object.entries(input.state ?? {})) {
|
|
9442
|
+
if (taskData !== null && typeof taskData === "object" && !Array.isArray(taskData)) {
|
|
9443
|
+
const unwrapped = taskData[token];
|
|
9444
|
+
requires[token] = unwrapped !== void 0 ? unwrapped : taskData;
|
|
9445
|
+
} else {
|
|
9446
|
+
requires[token] = taskData;
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
const computeNode = {
|
|
9450
|
+
id: cardId,
|
|
9451
|
+
card_data: { ...cardState },
|
|
9452
|
+
requires,
|
|
9453
|
+
source_defs: allSources,
|
|
9454
|
+
compute: card.compute
|
|
9455
|
+
};
|
|
9456
|
+
computeNode._sourcesData = sourcesData;
|
|
9457
|
+
if (card.compute) {
|
|
9458
|
+
await CardCompute.run(computeNode, { sourcesData });
|
|
9459
|
+
}
|
|
9460
|
+
adapters.outputStore.writeComputedValues(boardDir, cardId, computeNode.computed_values ?? {});
|
|
9461
|
+
const enrichedCard = { ...card };
|
|
9462
|
+
const enrichedSources = await CardCompute.enrichSources(
|
|
9463
|
+
Array.isArray(card.source_defs) ? card.source_defs : void 0,
|
|
9464
|
+
{
|
|
9465
|
+
card_data: card.card_data,
|
|
9466
|
+
requires,
|
|
9467
|
+
sourcesData,
|
|
9468
|
+
computed_values: computeNode.computed_values
|
|
9469
|
+
}
|
|
9470
|
+
);
|
|
9471
|
+
const sourceCwd = cardPath.replace(/[\\/][^\\/]*$/, "");
|
|
9472
|
+
enrichedCard.source_defs = Array.isArray(enrichedSources) ? enrichedSources.map((src) => ({
|
|
9473
|
+
...src,
|
|
9474
|
+
cwd: typeof src.cwd === "string" && src.cwd ? src.cwd : sourceCwd,
|
|
9475
|
+
boardDir: typeof src.boardDir === "string" && src.boardDir ? src.boardDir : boardDir
|
|
9476
|
+
})) : enrichedSources;
|
|
9477
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9478
|
+
const runQueuedAt = input.update ? void 0 : now;
|
|
9479
|
+
const undeliveredRequired = requiredSources.filter((s) => {
|
|
9480
|
+
const outputFile = s.outputFile;
|
|
9481
|
+
if (typeof outputFile !== "string" || !outputFile) return true;
|
|
9482
|
+
let entry = session.getSourceEntry(outputFile);
|
|
9483
|
+
if (runQueuedAt) {
|
|
9484
|
+
entry = { ...entry, queueRequestedAt: runQueuedAt };
|
|
9485
|
+
session.setSourceEntry(outputFile, entry);
|
|
9486
|
+
}
|
|
9487
|
+
const qrt = entry.queueRequestedAt ?? entry.lastRequestedAt ?? now;
|
|
9488
|
+
const action = decideSourceAction(entry, qrt);
|
|
9489
|
+
if (action === "in-flight") return false;
|
|
9490
|
+
return action === "dispatch";
|
|
9491
|
+
});
|
|
9492
|
+
session.flush();
|
|
9493
|
+
if (undeliveredRequired.length > 0) {
|
|
9494
|
+
let stampedAny = false;
|
|
9495
|
+
for (const src of undeliveredRequired) {
|
|
8807
9496
|
const outputFile = src.outputFile;
|
|
8808
|
-
if (typeof outputFile
|
|
8809
|
-
|
|
8810
|
-
}
|
|
9497
|
+
if (typeof outputFile !== "string" || !outputFile) continue;
|
|
9498
|
+
const entry = session.getSourceEntry(outputFile);
|
|
9499
|
+
session.setSourceEntry(outputFile, { ...entry, lastRequestedAt: now });
|
|
9500
|
+
stampedAny = true;
|
|
9501
|
+
}
|
|
9502
|
+
if (stampedAny) session.flush();
|
|
9503
|
+
if (!stampedAny) return "task-initiated";
|
|
9504
|
+
const result = await adapters.invocationAdapter.requestSourceFetch(
|
|
9505
|
+
boardDir,
|
|
9506
|
+
enrichedCard,
|
|
9507
|
+
input.callbackToken
|
|
9508
|
+
);
|
|
9509
|
+
if (!result.dispatched && result.error) {
|
|
9510
|
+
console.error(`[card-handler] ${input.nodeId}: source fetch dispatch failed: ${result.error}`);
|
|
8811
9511
|
}
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
9512
|
+
return "task-initiated";
|
|
9513
|
+
}
|
|
9514
|
+
const providesBindings = card.provides ?? [];
|
|
9515
|
+
const data = {};
|
|
9516
|
+
for (const { bindTo, ref } of providesBindings) {
|
|
9517
|
+
data[bindTo] = CardCompute.resolve(computeNode, ref);
|
|
9518
|
+
}
|
|
9519
|
+
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;
|
|
9520
|
+
const cardData = card.card_data;
|
|
9521
|
+
const llmCompletion = cardData?.llm_task_completion_inference ?? {};
|
|
9522
|
+
const isLlmTaskCompleted = llmCompletion.isTaskCompleted === true;
|
|
9523
|
+
const inferenceEntry = session.getInferenceEntry();
|
|
9524
|
+
const inferenceRequestedAt = typeof inferenceEntry.lastRequestedAt === "string" ? inferenceEntry.lastRequestedAt : void 0;
|
|
9525
|
+
const inferenceCompletedAt = typeof llmCompletion.inferenceCompletedAt === "string" ? llmCompletion.inferenceCompletedAt : void 0;
|
|
9526
|
+
const inferencePending = !!inferenceRequestedAt && (!inferenceCompletedAt || inferenceCompletedAt < inferenceRequestedAt);
|
|
9527
|
+
const latestRequiredSourceFetchedAt = requiredSources.reduce((latest, src) => {
|
|
9528
|
+
const fetchedAt = session.getSourceEntry(src.outputFile).lastFetchedAt;
|
|
9529
|
+
if (typeof fetchedAt !== "string") return latest;
|
|
9530
|
+
if (!latest || fetchedAt > latest) return fetchedAt;
|
|
9531
|
+
return latest;
|
|
9532
|
+
}, void 0);
|
|
9533
|
+
const shouldRequestInference = !inferenceRequestedAt || !inferenceCompletedAt || !!latestRequiredSourceFetchedAt && latestRequiredSourceFetchedAt > inferenceCompletedAt;
|
|
9534
|
+
if (completionRule !== DEFAULT_TASK_COMPLETION_RULE) {
|
|
9535
|
+
if (isLlmTaskCompleted) ; else if (inferencePending) {
|
|
9536
|
+
return "task-initiated";
|
|
9537
|
+
} else if (!shouldRequestInference) {
|
|
9538
|
+
return "task-initiated";
|
|
9539
|
+
} else {
|
|
9540
|
+
const inferencePayload = {
|
|
9541
|
+
cardId,
|
|
9542
|
+
taskName: input.nodeId,
|
|
9543
|
+
completionRule,
|
|
9544
|
+
context: {
|
|
9545
|
+
requires,
|
|
9546
|
+
sourcesData,
|
|
9547
|
+
computed_values: computeNode.computed_values ?? {},
|
|
9548
|
+
provides: data,
|
|
9549
|
+
card_data: computeNode.card_data ?? {}
|
|
9550
|
+
}
|
|
9551
|
+
};
|
|
9552
|
+
let updatedInferenceEntry = { ...inferenceEntry };
|
|
8819
9553
|
if (runQueuedAt) {
|
|
8820
|
-
|
|
8821
|
-
|
|
9554
|
+
updatedInferenceEntry = { ...updatedInferenceEntry, queueRequestedAt: runQueuedAt };
|
|
9555
|
+
session.setInferenceEntry(updatedInferenceEntry);
|
|
8822
9556
|
}
|
|
8823
|
-
const
|
|
8824
|
-
const
|
|
8825
|
-
if (
|
|
8826
|
-
|
|
8827
|
-
});
|
|
8828
|
-
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
8829
|
-
if (undeliveredRequired.length > 0) {
|
|
8830
|
-
let stampedAny = false;
|
|
8831
|
-
for (const src of undeliveredRequired) {
|
|
8832
|
-
const outputFile = src.outputFile;
|
|
8833
|
-
if (typeof outputFile !== "string" || !outputFile) continue;
|
|
8834
|
-
const entry = runtime._sources[outputFile] ?? {};
|
|
8835
|
-
markRequested(entry, now);
|
|
8836
|
-
runtime._sources[outputFile] = entry;
|
|
8837
|
-
stampedAny = true;
|
|
8838
|
-
}
|
|
8839
|
-
if (stampedAny) writeRuntimeState(boardDir, cardId, runtime);
|
|
8840
|
-
if (!stampedAny) return "task-initiated";
|
|
8841
|
-
const enrichedCardPath = path__namespace.join(os__namespace.tmpdir(), `card-enriched-${cardId}-${Date.now()}.json`);
|
|
8842
|
-
fs__namespace.writeFileSync(enrichedCardPath, JSON.stringify(enrichedCard, null, 2), "utf-8");
|
|
8843
|
-
invokeRunSources(boardDir, enrichedCardPath, input.callbackToken, (err) => {
|
|
8844
|
-
if (err) {
|
|
8845
|
-
console.error(`[card-handler] ${input.nodeId}:`, err.message);
|
|
8846
|
-
try {
|
|
8847
|
-
fs__namespace.unlinkSync(enrichedCardPath);
|
|
8848
|
-
} catch {
|
|
8849
|
-
}
|
|
8850
|
-
}
|
|
8851
|
-
});
|
|
8852
|
-
return "task-initiated";
|
|
8853
|
-
}
|
|
8854
|
-
const providesBindings = card.provides ?? [];
|
|
8855
|
-
const data = {};
|
|
8856
|
-
for (const { bindTo, ref } of providesBindings) {
|
|
8857
|
-
data[bindTo] = CardCompute.resolve(computeNode, ref);
|
|
8858
|
-
}
|
|
8859
|
-
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;
|
|
8860
|
-
const cardData = card.card_data;
|
|
8861
|
-
const llmCompletion = cardData?.llm_task_completion_inference ?? {};
|
|
8862
|
-
const isLlmTaskCompleted = llmCompletion.isTaskCompleted === true;
|
|
8863
|
-
const inferenceEntry = runtime._inferenceEntry ?? {};
|
|
8864
|
-
const inferenceRequestedAt = typeof inferenceEntry.lastRequestedAt === "string" ? inferenceEntry.lastRequestedAt : void 0;
|
|
8865
|
-
const inferenceCompletedAt = typeof llmCompletion.inferenceCompletedAt === "string" ? llmCompletion.inferenceCompletedAt : void 0;
|
|
8866
|
-
const inferencePending = !!inferenceRequestedAt && (!inferenceCompletedAt || inferenceCompletedAt < inferenceRequestedAt);
|
|
8867
|
-
const latestRequiredSourceFetchedAt = requiredSources.reduce((latest, src) => {
|
|
8868
|
-
const fetchedAt = runtime._sources[src.outputFile]?.lastFetchedAt;
|
|
8869
|
-
if (typeof fetchedAt !== "string") return latest;
|
|
8870
|
-
if (!latest || fetchedAt > latest) return fetchedAt;
|
|
8871
|
-
return latest;
|
|
8872
|
-
}, void 0);
|
|
8873
|
-
const shouldRequestInference = !inferenceRequestedAt || !inferenceCompletedAt || !!latestRequiredSourceFetchedAt && latestRequiredSourceFetchedAt > inferenceCompletedAt;
|
|
8874
|
-
if (completionRule !== DEFAULT_TASK_COMPLETION_RULE) {
|
|
8875
|
-
if (isLlmTaskCompleted) ; else if (inferencePending) {
|
|
9557
|
+
const inferenceQrt = updatedInferenceEntry.queueRequestedAt ?? updatedInferenceEntry.lastRequestedAt ?? now;
|
|
9558
|
+
const inferenceAction = decideSourceAction(updatedInferenceEntry, inferenceQrt);
|
|
9559
|
+
if (inferenceAction === "in-flight") {
|
|
9560
|
+
session.flush();
|
|
8876
9561
|
return "task-initiated";
|
|
8877
|
-
}
|
|
9562
|
+
}
|
|
9563
|
+
if (inferenceAction === "idle") {
|
|
8878
9564
|
return "task-initiated";
|
|
8879
|
-
}
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
9565
|
+
}
|
|
9566
|
+
adapters.outputStore.appendInferenceLog(boardDir, cardId, inferencePayload);
|
|
9567
|
+
session.setInferenceEntry({ ...updatedInferenceEntry, lastRequestedAt: now });
|
|
9568
|
+
session.flush();
|
|
9569
|
+
const inferenceResult = await adapters.invocationAdapter.requestInference(
|
|
9570
|
+
boardDir,
|
|
9571
|
+
cardId,
|
|
9572
|
+
inferencePayload,
|
|
9573
|
+
input.callbackToken
|
|
9574
|
+
);
|
|
9575
|
+
if (!inferenceResult.dispatched) {
|
|
9576
|
+
const failedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9577
|
+
adapters.inputStore.appendEvent(boardDir, {
|
|
9578
|
+
type: "task-failed",
|
|
8883
9579
|
taskName: input.nodeId,
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
requires,
|
|
8887
|
-
sourcesData,
|
|
8888
|
-
computed_values: computeNode.computed_values ?? {},
|
|
8889
|
-
provides: data,
|
|
8890
|
-
card_data: computeNode.card_data ?? {}
|
|
8891
|
-
}
|
|
8892
|
-
};
|
|
8893
|
-
if (runQueuedAt) {
|
|
8894
|
-
inferenceEntry.queueRequestedAt = runQueuedAt;
|
|
8895
|
-
runtimeDirty = true;
|
|
8896
|
-
}
|
|
8897
|
-
const inferenceQrt = inferenceEntry.queueRequestedAt ?? inferenceEntry.lastRequestedAt ?? now2;
|
|
8898
|
-
const inferenceAction = decideSourceAction(inferenceEntry, inferenceQrt);
|
|
8899
|
-
if (inferenceAction === "in-flight") {
|
|
8900
|
-
runtime._inferenceEntry = inferenceEntry;
|
|
8901
|
-
if (runtimeDirty) writeRuntimeState(boardDir, cardId, runtime);
|
|
8902
|
-
return "task-initiated";
|
|
8903
|
-
}
|
|
8904
|
-
if (inferenceAction === "idle") {
|
|
8905
|
-
return "task-initiated";
|
|
8906
|
-
}
|
|
8907
|
-
const inferenceInFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-${cardId}-${Date.now()}.json`);
|
|
8908
|
-
fs__namespace.writeFileSync(inferenceInFile, JSON.stringify(inferencePayload, null, 2), "utf-8");
|
|
8909
|
-
appendInferenceAdapterLog(boardDir, cardId, inferencePayload);
|
|
8910
|
-
markRequested(inferenceEntry, now2);
|
|
8911
|
-
runtime._inferenceEntry = inferenceEntry;
|
|
8912
|
-
runtimeDirty = true;
|
|
8913
|
-
invokeRunInference(boardDir, cardId, inferenceInFile, input.callbackToken, void 0, (err) => {
|
|
8914
|
-
if (err) {
|
|
8915
|
-
console.error(`[card-handler] ${input.nodeId}:`, err.message);
|
|
8916
|
-
const failedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8917
|
-
appendEventToJournal(boardDir, {
|
|
8918
|
-
type: "task-failed",
|
|
8919
|
-
taskName: input.nodeId,
|
|
8920
|
-
error: err.message,
|
|
8921
|
-
timestamp: failedAt
|
|
8922
|
-
});
|
|
8923
|
-
}
|
|
9580
|
+
error: inferenceResult.error ?? "inference dispatch failed",
|
|
9581
|
+
timestamp: failedAt
|
|
8924
9582
|
});
|
|
8925
|
-
return "task-initiated";
|
|
8926
9583
|
}
|
|
9584
|
+
return "task-initiated";
|
|
8927
9585
|
}
|
|
8928
|
-
writeRuntimeDataObjects(boardDir, data);
|
|
8929
|
-
const undeliveredOptional = allSources.filter((s) => {
|
|
8930
|
-
if (s.optionalForCompletionGating !== true) return false;
|
|
8931
|
-
const entry = runtime._sources[s.outputFile];
|
|
8932
|
-
if (!entry?.lastRequestedAt) return true;
|
|
8933
|
-
if (!entry.lastFetchedAt) return true;
|
|
8934
|
-
return entry.lastFetchedAt <= entry.lastRequestedAt;
|
|
8935
|
-
});
|
|
8936
|
-
if (undeliveredOptional.length > 0) {
|
|
8937
|
-
invokeRunSources(boardDir, cardPath, input.callbackToken, (err) => {
|
|
8938
|
-
if (err) console.error(`[card-handler] ${input.nodeId}:`, err.message);
|
|
8939
|
-
});
|
|
8940
|
-
}
|
|
8941
|
-
appendEventToJournal(boardDir, {
|
|
8942
|
-
type: "task-completed",
|
|
8943
|
-
taskName: input.nodeId,
|
|
8944
|
-
data,
|
|
8945
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8946
|
-
});
|
|
8947
|
-
return "task-initiated";
|
|
8948
9586
|
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
dot: false
|
|
8960
|
-
});
|
|
8961
|
-
return [...matches].map((m) => path__namespace.resolve(m)).sort((a, b) => a.localeCompare(b));
|
|
8962
|
-
}
|
|
8963
|
-
function cmdInit(args) {
|
|
8964
|
-
const dir = args[0];
|
|
8965
|
-
if (!dir) {
|
|
8966
|
-
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
8967
|
-
}
|
|
8968
|
-
const teIdx = args.indexOf("--task-executor");
|
|
8969
|
-
const taskExecutor = teIdx !== -1 ? args[teIdx + 1] : void 0;
|
|
8970
|
-
const chIdx = args.indexOf("--chat-handler");
|
|
8971
|
-
const chatHandler = chIdx !== -1 ? args[chIdx + 1] : void 0;
|
|
8972
|
-
const iaIdx = args.indexOf("--inference-adapter");
|
|
8973
|
-
const inferenceAdapter = iaIdx !== -1 ? args[iaIdx + 1] : void 0;
|
|
8974
|
-
const roIdx = args.indexOf("--runtime-out");
|
|
8975
|
-
const runtimeOut = roIdx !== -1 ? args[roIdx + 1] : void 0;
|
|
8976
|
-
if (roIdx !== -1 && !runtimeOut) {
|
|
8977
|
-
throw new Error("Usage: board-live-cards init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]");
|
|
8978
|
-
}
|
|
8979
|
-
const result = initBoard(dir);
|
|
8980
|
-
if (taskExecutor) {
|
|
8981
|
-
const teExtraIdx = args.indexOf("--task-executor-extra");
|
|
8982
|
-
let teExtra;
|
|
8983
|
-
if (teExtraIdx !== -1 && args[teExtraIdx + 1]) {
|
|
8984
|
-
try {
|
|
8985
|
-
teExtra = JSON.parse(args[teExtraIdx + 1]);
|
|
8986
|
-
} catch {
|
|
8987
|
-
}
|
|
9587
|
+
adapters.outputStore.writeDataObjects(boardDir, data);
|
|
9588
|
+
const undeliveredOptional = allSources.filter((s) => {
|
|
9589
|
+
if (s.optionalForCompletionGating !== true) return false;
|
|
9590
|
+
const entry = session.getSourceEntry(s.outputFile);
|
|
9591
|
+
if (!entry.lastRequestedAt) return true;
|
|
9592
|
+
if (!entry.lastFetchedAt) return true;
|
|
9593
|
+
return entry.lastFetchedAt <= entry.lastRequestedAt;
|
|
9594
|
+
});
|
|
9595
|
+
if (undeliveredOptional.length > 0) {
|
|
9596
|
+
adapters.invocationAdapter.requestSourceFetch(boardDir, enrichedCard, input.callbackToken).catch((err) => console.error(`[card-handler] ${input.nodeId}: optional source fetch:`, err));
|
|
8988
9597
|
}
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
}
|
|
8998
|
-
const runtimeOutDir = configureRuntimeOutDir(dir, runtimeOut);
|
|
8999
|
-
const live = loadBoard(dir);
|
|
9000
|
-
writeJsonAtomic(resolveStatusSnapshotPath(dir), buildBoardStatusObject(dir, live));
|
|
9001
|
-
if (result === "exists") {
|
|
9002
|
-
console.log(`Board already initialized at ${path__namespace.resolve(dir)}${taskExecutor ? ` (task-executor updated: ${taskExecutor})` : ""} (runtime-out: ${runtimeOutDir})`);
|
|
9003
|
-
} else {
|
|
9004
|
-
console.log(`Board initialized at ${path__namespace.resolve(dir)}${taskExecutor ? ` (task-executor: ${taskExecutor})` : ""} (runtime-out: ${runtimeOutDir})`);
|
|
9005
|
-
}
|
|
9598
|
+
adapters.inputStore.appendEvent(boardDir, {
|
|
9599
|
+
type: "task-completed",
|
|
9600
|
+
taskName: input.nodeId,
|
|
9601
|
+
data,
|
|
9602
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9603
|
+
});
|
|
9604
|
+
return "task-initiated";
|
|
9605
|
+
};
|
|
9006
9606
|
}
|
|
9007
|
-
|
|
9607
|
+
|
|
9608
|
+
// src/cli/board-live-cards-lib-board-status.ts
|
|
9609
|
+
function buildBoardStatusObject(boardPath, live) {
|
|
9008
9610
|
const taskState = live.state.tasks;
|
|
9009
9611
|
const taskConfig = live.config.tasks;
|
|
9010
9612
|
const cardNames = Object.keys(taskState);
|
|
@@ -9088,15 +9690,15 @@ function buildBoardStatusObject(dir, live) {
|
|
|
9088
9690
|
let orphanCards = 0;
|
|
9089
9691
|
for (const [name, cfg] of Object.entries(taskConfig)) {
|
|
9090
9692
|
const requiresNone = (cfg.requires ?? []).length === 0;
|
|
9091
|
-
const
|
|
9092
|
-
const feedsAny =
|
|
9693
|
+
const providesList = cfg.provides ?? [];
|
|
9694
|
+
const feedsAny = providesList.some((p) => (dependentsByToken.get(p) ?? []).some((d) => d !== name));
|
|
9093
9695
|
if (requiresNone && !feedsAny) orphanCards += 1;
|
|
9094
9696
|
}
|
|
9095
9697
|
return {
|
|
9096
9698
|
schema_version: "v1",
|
|
9097
9699
|
meta: {
|
|
9098
9700
|
board: {
|
|
9099
|
-
path:
|
|
9701
|
+
path: boardPath
|
|
9100
9702
|
}
|
|
9101
9703
|
},
|
|
9102
9704
|
summary: {
|
|
@@ -9118,1193 +9720,895 @@ function buildBoardStatusObject(dir, live) {
|
|
|
9118
9720
|
cards
|
|
9119
9721
|
};
|
|
9120
9722
|
}
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
console.error("Usage: board-live-cards status --rg <dir>");
|
|
9127
|
-
process.exit(1);
|
|
9723
|
+
var NodeRuntimeStoreSession = class {
|
|
9724
|
+
constructor(boardDir, cardId) {
|
|
9725
|
+
this.boardDir = boardDir;
|
|
9726
|
+
this.cardId = cardId;
|
|
9727
|
+
this.state = NodeRuntimeInternalStore.readState(boardDir, cardId);
|
|
9128
9728
|
}
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
writeJsonAtomic(statusOutPath, statusObject);
|
|
9729
|
+
boardDir;
|
|
9730
|
+
cardId;
|
|
9731
|
+
state;
|
|
9732
|
+
dirty = false;
|
|
9733
|
+
getSourceEntry(outputFile) {
|
|
9734
|
+
return { ...this.state._sources[outputFile] ?? {} };
|
|
9136
9735
|
}
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9736
|
+
setSourceEntry(outputFile, entry) {
|
|
9737
|
+
this.state._sources[outputFile] = entry;
|
|
9738
|
+
this.dirty = true;
|
|
9739
|
+
}
|
|
9740
|
+
resetSources() {
|
|
9741
|
+
this.state._sources = {};
|
|
9742
|
+
this.dirty = true;
|
|
9743
|
+
}
|
|
9744
|
+
getInferenceEntry() {
|
|
9745
|
+
return { ...this.state._inferenceEntry ?? {} };
|
|
9746
|
+
}
|
|
9747
|
+
setInferenceEntry(entry) {
|
|
9748
|
+
this.state._inferenceEntry = entry;
|
|
9749
|
+
this.dirty = true;
|
|
9750
|
+
}
|
|
9751
|
+
resetInferenceEntry() {
|
|
9752
|
+
this.state._inferenceEntry = void 0;
|
|
9753
|
+
this.dirty = true;
|
|
9140
9754
|
}
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
console.log("");
|
|
9144
|
-
for (const card of statusObject.cards) {
|
|
9145
|
-
const dataKeys = card.provides_runtime.join(", ");
|
|
9146
|
-
console.log(` ${card.status.padEnd(12)} ${card.name}${dataKeys ? ` \u2014 [${dataKeys}]` : ""}`);
|
|
9755
|
+
getLastExecutionCount() {
|
|
9756
|
+
return this.state._lastExecutionCount;
|
|
9147
9757
|
}
|
|
9148
|
-
|
|
9149
|
-
|
|
9758
|
+
setLastExecutionCount(count) {
|
|
9759
|
+
this.state._lastExecutionCount = count;
|
|
9760
|
+
this.dirty = true;
|
|
9761
|
+
}
|
|
9762
|
+
flush() {
|
|
9763
|
+
if (!this.dirty) return;
|
|
9764
|
+
NodeRuntimeInternalStore.writeState(this.boardDir, this.cardId, this.state);
|
|
9765
|
+
this.dirty = false;
|
|
9766
|
+
}
|
|
9767
|
+
};
|
|
9768
|
+
var NodeRuntimeInternalStore = class {
|
|
9769
|
+
openSession(boardDir, cardId) {
|
|
9770
|
+
return new NodeRuntimeStoreSession(boardDir, cardId);
|
|
9771
|
+
}
|
|
9772
|
+
static readState(boardDir, cardId) {
|
|
9773
|
+
const p = path7__namespace.join(boardDir, cardId, "runtime.json");
|
|
9774
|
+
if (!fs7__namespace.existsSync(p)) return { _sources: {} };
|
|
9775
|
+
try {
|
|
9776
|
+
return JSON.parse(fs7__namespace.readFileSync(p, "utf-8"));
|
|
9777
|
+
} catch {
|
|
9778
|
+
return { _sources: {} };
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9781
|
+
static writeState(boardDir, cardId, state) {
|
|
9782
|
+
const p = path7__namespace.join(boardDir, cardId, "runtime.json");
|
|
9783
|
+
fs7__namespace.mkdirSync(path7__namespace.dirname(p), { recursive: true });
|
|
9784
|
+
fs7__namespace.writeFileSync(p, JSON.stringify(state, null, 2));
|
|
9785
|
+
}
|
|
9786
|
+
};
|
|
9787
|
+
function createNodeRuntimeStore() {
|
|
9788
|
+
return new NodeRuntimeInternalStore();
|
|
9150
9789
|
}
|
|
9151
|
-
function
|
|
9152
|
-
|
|
9153
|
-
const
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9790
|
+
function writeJsonAtomic(filePath, payload) {
|
|
9791
|
+
fs7__namespace.mkdirSync(path7__namespace.dirname(filePath), { recursive: true });
|
|
9792
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
9793
|
+
fs7__namespace.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
9794
|
+
fs7__namespace.renameSync(tmpPath, filePath);
|
|
9795
|
+
}
|
|
9796
|
+
var NodeOutputStore = class {
|
|
9797
|
+
constructor(resolveComputedValuesPath2, resolveDataObjectsDirPath2, inferenceAdapterLogFile) {
|
|
9798
|
+
this.resolveComputedValuesPath = resolveComputedValuesPath2;
|
|
9799
|
+
this.resolveDataObjectsDirPath = resolveDataObjectsDirPath2;
|
|
9800
|
+
this.inferenceAdapterLogFile = inferenceAdapterLogFile;
|
|
9801
|
+
}
|
|
9802
|
+
resolveComputedValuesPath;
|
|
9803
|
+
resolveDataObjectsDirPath;
|
|
9804
|
+
inferenceAdapterLogFile;
|
|
9805
|
+
writeComputedValues(boardDir, cardId, values) {
|
|
9806
|
+
writeJsonAtomic(this.resolveComputedValuesPath(boardDir, cardId), {
|
|
9807
|
+
schema_version: "v1",
|
|
9808
|
+
card_id: cardId,
|
|
9809
|
+
computed_values: values
|
|
9810
|
+
});
|
|
9160
9811
|
}
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9812
|
+
writeDataObjects(boardDir, data) {
|
|
9813
|
+
for (const [token, payload] of Object.entries(data)) {
|
|
9814
|
+
if (!token) continue;
|
|
9815
|
+
const fileName = token.replace(/[\\/]/g, "__");
|
|
9816
|
+
if (!fileName) continue;
|
|
9817
|
+
writeJsonAtomic(path7__namespace.join(this.resolveDataObjectsDirPath(boardDir), fileName), payload);
|
|
9818
|
+
}
|
|
9165
9819
|
}
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9820
|
+
appendInferenceLog(boardDir, cardId, payload) {
|
|
9821
|
+
try {
|
|
9822
|
+
const entry = { timestamp: (/* @__PURE__ */ new Date()).toISOString(), cardId, payload };
|
|
9823
|
+
fs7__namespace.appendFileSync(
|
|
9824
|
+
path7__namespace.join(boardDir, this.inferenceAdapterLogFile),
|
|
9825
|
+
JSON.stringify(entry) + "\n",
|
|
9826
|
+
"utf-8"
|
|
9827
|
+
);
|
|
9828
|
+
} catch (logErr) {
|
|
9829
|
+
console.error(`[inference-adapter-log] append failed: ${logErr instanceof Error ? logErr.message : String(logErr)}`);
|
|
9830
|
+
}
|
|
9831
|
+
}
|
|
9832
|
+
};
|
|
9833
|
+
function createNodeOutputStore(resolveComputedValuesPath2, resolveDataObjectsDirPath2, inferenceAdapterLogFile) {
|
|
9834
|
+
return new NodeOutputStore(resolveComputedValuesPath2, resolveDataObjectsDirPath2, inferenceAdapterLogFile);
|
|
9176
9835
|
}
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
const errorIdx = args.indexOf("--error");
|
|
9181
|
-
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9182
|
-
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
9183
|
-
const errorMsg = errorIdx !== -1 ? args[errorIdx + 1] : "unknown error";
|
|
9184
|
-
if (!dir || !token) {
|
|
9185
|
-
console.error("Usage: board-live-cards task-failed --rg <dir> --token <token> [--error <message>]");
|
|
9186
|
-
process.exit(1);
|
|
9836
|
+
var NodeInputStore = class {
|
|
9837
|
+
constructor(journalFile) {
|
|
9838
|
+
this.journalFile = journalFile;
|
|
9187
9839
|
}
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9840
|
+
journalFile;
|
|
9841
|
+
appendEvent(boardDir, event) {
|
|
9842
|
+
const journalPath = path7__namespace.join(boardDir, this.journalFile);
|
|
9843
|
+
const entry = { id: crypto.randomUUID(), event };
|
|
9844
|
+
fs7__namespace.appendFileSync(journalPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
9192
9845
|
}
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
error: errorMsg,
|
|
9197
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9198
|
-
});
|
|
9199
|
-
void processAccumulatedEventsForced(dir);
|
|
9200
|
-
console.log("Task failed.");
|
|
9846
|
+
};
|
|
9847
|
+
function createNodeInputStore(journalFile) {
|
|
9848
|
+
return new NodeInputStore(journalFile);
|
|
9201
9849
|
}
|
|
9202
|
-
function
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
const
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9850
|
+
function shouldSuppressSpawn() {
|
|
9851
|
+
return process.env.BOARD_LIVE_CARDS_NO_SPAWN === "1";
|
|
9852
|
+
}
|
|
9853
|
+
function getCliInvocationPath(cliDir) {
|
|
9854
|
+
const jsPath = path7__namespace.join(cliDir, "board-live-cards-cli.js");
|
|
9855
|
+
if (fs7__namespace.existsSync(jsPath)) {
|
|
9856
|
+
return { cmd: process.execPath, args: [jsPath] };
|
|
9857
|
+
}
|
|
9858
|
+
const tsPath = path7__namespace.join(cliDir, "board-live-cards-cli.ts");
|
|
9859
|
+
const localTsx = path7__namespace.join(cliDir, "..", "..", "node_modules", ".bin", "tsx");
|
|
9860
|
+
if (fs7__namespace.existsSync(tsPath) && fs7__namespace.existsSync(localTsx)) {
|
|
9861
|
+
return { cmd: process.execPath, args: [localTsx, tsPath] };
|
|
9862
|
+
}
|
|
9863
|
+
return null;
|
|
9864
|
+
}
|
|
9865
|
+
function buildCliInvocation(cliDir, command, args) {
|
|
9866
|
+
const found = getCliInvocationPath(cliDir);
|
|
9867
|
+
if (found) {
|
|
9868
|
+
return { cmd: found.cmd, args: [...found.args, command, ...args] };
|
|
9869
|
+
}
|
|
9870
|
+
const tsPath = path7__namespace.join(cliDir, "board-live-cards-cli.ts");
|
|
9871
|
+
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
9872
|
+
return { cmd: npxCmd, args: ["tsx", tsPath, command, ...args] };
|
|
9873
|
+
}
|
|
9874
|
+
var _gitBashCache;
|
|
9875
|
+
var GIT_BASH_CACHE_FILE = path7__namespace.join(os3__namespace.tmpdir(), ".board-live-cards-git-bash-path.json");
|
|
9876
|
+
function findGitBash() {
|
|
9877
|
+
if (_gitBashCache !== void 0) return _gitBashCache;
|
|
9878
|
+
try {
|
|
9879
|
+
const cached = JSON.parse(fs7__namespace.readFileSync(GIT_BASH_CACHE_FILE, "utf-8"));
|
|
9880
|
+
if (typeof cached?.path === "string" || cached?.path === false) {
|
|
9881
|
+
_gitBashCache = cached.path;
|
|
9882
|
+
return _gitBashCache;
|
|
9883
|
+
}
|
|
9884
|
+
} catch {
|
|
9885
|
+
}
|
|
9886
|
+
const candidates = [
|
|
9887
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
9888
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe"
|
|
9889
|
+
];
|
|
9890
|
+
for (const c of candidates) {
|
|
9891
|
+
if (fs7__namespace.existsSync(c)) {
|
|
9892
|
+
_gitBashCache = c;
|
|
9893
|
+
try {
|
|
9894
|
+
fs7__namespace.writeFileSync(GIT_BASH_CACHE_FILE, JSON.stringify({ path: c }));
|
|
9895
|
+
} catch {
|
|
9896
|
+
}
|
|
9897
|
+
return c;
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
_gitBashCache = false;
|
|
9901
|
+
try {
|
|
9902
|
+
fs7__namespace.writeFileSync(GIT_BASH_CACHE_FILE, JSON.stringify({ path: false }));
|
|
9903
|
+
} catch {
|
|
9904
|
+
}
|
|
9905
|
+
return false;
|
|
9906
|
+
}
|
|
9907
|
+
function shellQuote(s) {
|
|
9908
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
9909
|
+
}
|
|
9910
|
+
function spawnDetached(cmd, args) {
|
|
9911
|
+
if (process.platform === "win32") {
|
|
9912
|
+
const bash = findGitBash();
|
|
9913
|
+
if (bash) {
|
|
9914
|
+
const shellCmd = [cmd, ...args].map((a) => shellQuote(a.replace(/\\/g, "/"))).join(" ");
|
|
9915
|
+
const child3 = child_process.spawn(bash, ["-c", shellCmd], { detached: true, stdio: "ignore", windowsHide: true });
|
|
9916
|
+
child3.unref();
|
|
9917
|
+
return;
|
|
9918
|
+
}
|
|
9919
|
+
const child2 = child_process.spawn("cmd", ["/c", "start", "/b", "", cmd, ...args], {
|
|
9920
|
+
detached: true,
|
|
9921
|
+
stdio: "ignore",
|
|
9922
|
+
windowsHide: true
|
|
9923
|
+
});
|
|
9924
|
+
child2.unref();
|
|
9925
|
+
return;
|
|
9926
|
+
}
|
|
9927
|
+
const child = child_process.spawn(cmd, args, { detached: true, stdio: "ignore" });
|
|
9928
|
+
child.unref();
|
|
9929
|
+
}
|
|
9930
|
+
var NodeInvocationAdapter = class {
|
|
9931
|
+
constructor(cliDir, encodeSourceToken2) {
|
|
9932
|
+
this.cliDir = cliDir;
|
|
9933
|
+
this.encodeSourceToken = encodeSourceToken2;
|
|
9934
|
+
}
|
|
9935
|
+
cliDir;
|
|
9936
|
+
encodeSourceToken;
|
|
9937
|
+
async requestSourceFetch(boardDir, enrichedCard, callbackToken) {
|
|
9938
|
+
if (shouldSuppressSpawn()) {
|
|
9939
|
+
return { dispatched: false, invocationId: void 0 };
|
|
9940
|
+
}
|
|
9941
|
+
try {
|
|
9942
|
+
const cardId = enrichedCard.id ?? "unknown";
|
|
9943
|
+
const enrichedCardPath = path7__namespace.join(os3__namespace.tmpdir(), `card-enriched-${cardId}-${Date.now()}.json`);
|
|
9944
|
+
fs7__namespace.writeFileSync(enrichedCardPath, JSON.stringify(enrichedCard, null, 2), "utf-8");
|
|
9945
|
+
const args = ["--card", enrichedCardPath, "--token", callbackToken, "--rg", boardDir];
|
|
9946
|
+
const { cmd, args: cmdArgs } = buildCliInvocation(this.cliDir, "run-sourcedefs-internal", args);
|
|
9947
|
+
const invocationId = crypto.randomUUID();
|
|
9948
|
+
spawnDetached(cmd, cmdArgs);
|
|
9949
|
+
return { dispatched: true, invocationId };
|
|
9950
|
+
} catch (err) {
|
|
9951
|
+
return { dispatched: false, error: err instanceof Error ? err.message : String(err) };
|
|
9217
9952
|
}
|
|
9218
9953
|
}
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
}
|
|
9223
|
-
let failures = 0;
|
|
9224
|
-
for (const f of files) {
|
|
9225
|
-
const label = path__namespace.relative(process.cwd(), f) || f;
|
|
9226
|
-
if (!fs__namespace.existsSync(f)) {
|
|
9227
|
-
console.error(`FAIL ${label}: file not found`);
|
|
9228
|
-
failures++;
|
|
9229
|
-
continue;
|
|
9954
|
+
async requestInference(boardDir, cardId, inferencePayload, callbackToken) {
|
|
9955
|
+
if (shouldSuppressSpawn()) {
|
|
9956
|
+
return { dispatched: false, invocationId: void 0 };
|
|
9230
9957
|
}
|
|
9231
|
-
let card;
|
|
9232
9958
|
try {
|
|
9233
|
-
|
|
9959
|
+
const inferenceInFile = path7__namespace.join(os3__namespace.tmpdir(), `card-inference-${cardId}-${Date.now()}.json`);
|
|
9960
|
+
fs7__namespace.writeFileSync(inferenceInFile, JSON.stringify(inferencePayload, null, 2), "utf-8");
|
|
9961
|
+
const inferenceToken = this.encodeSourceToken({
|
|
9962
|
+
cbk: callbackToken,
|
|
9963
|
+
rg: boardDir,
|
|
9964
|
+
cid: cardId,
|
|
9965
|
+
b: "",
|
|
9966
|
+
d: "",
|
|
9967
|
+
cs: void 0
|
|
9968
|
+
});
|
|
9969
|
+
const { cmd, args } = buildCliInvocation(
|
|
9970
|
+
this.cliDir,
|
|
9971
|
+
"run-inference-internal",
|
|
9972
|
+
["--in", inferenceInFile, "--token", inferenceToken]
|
|
9973
|
+
);
|
|
9974
|
+
const invocationId = crypto.randomUUID();
|
|
9975
|
+
spawnDetached(cmd, args);
|
|
9976
|
+
return { dispatched: true, invocationId };
|
|
9234
9977
|
} catch (err) {
|
|
9235
|
-
|
|
9236
|
-
failures++;
|
|
9237
|
-
continue;
|
|
9978
|
+
return { dispatched: false, error: err instanceof Error ? err.message : String(err) };
|
|
9238
9979
|
}
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
continue;
|
|
9255
|
-
}
|
|
9256
|
-
}
|
|
9257
|
-
const parsed = JSON.parse(stdout.trim());
|
|
9258
|
-
if (!parsed.ok && Array.isArray(parsed.errors)) {
|
|
9259
|
-
for (const e of parsed.errors) {
|
|
9260
|
-
sourceErrors.push(`source "${bindTo}": ${e}`);
|
|
9261
|
-
}
|
|
9262
|
-
}
|
|
9263
|
-
} catch (err) {
|
|
9264
|
-
sourceErrors.push(`source "${bindTo}": executor validate-source-def failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
9265
|
-
} finally {
|
|
9266
|
-
try {
|
|
9267
|
-
fs__namespace.unlinkSync(tmpFile);
|
|
9268
|
-
} catch {
|
|
9269
|
-
}
|
|
9270
|
-
}
|
|
9271
|
-
}
|
|
9980
|
+
}
|
|
9981
|
+
};
|
|
9982
|
+
function createNodeInvocationAdapter(cliDir, encodeSourceToken2) {
|
|
9983
|
+
return new NodeInvocationAdapter(cliDir, encodeSourceToken2);
|
|
9984
|
+
}
|
|
9985
|
+
var NodeCardStore = class {
|
|
9986
|
+
constructor(lookupCardPathFn) {
|
|
9987
|
+
this.lookupCardPathFn = lookupCardPathFn;
|
|
9988
|
+
}
|
|
9989
|
+
lookupCardPathFn;
|
|
9990
|
+
readCard(cardPath) {
|
|
9991
|
+
try {
|
|
9992
|
+
return JSON.parse(fs7__namespace.readFileSync(cardPath, "utf-8"));
|
|
9993
|
+
} catch {
|
|
9994
|
+
return null;
|
|
9272
9995
|
}
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9996
|
+
}
|
|
9997
|
+
readSourceFileContent(boardDir, cardId, outputFile) {
|
|
9998
|
+
const filePath = path7__namespace.join(boardDir, cardId, outputFile);
|
|
9999
|
+
if (!fs7__namespace.existsSync(filePath)) return null;
|
|
10000
|
+
try {
|
|
10001
|
+
const raw = fs7__namespace.readFileSync(filePath, "utf-8").trim();
|
|
10002
|
+
try {
|
|
10003
|
+
return JSON.parse(raw);
|
|
10004
|
+
} catch {
|
|
10005
|
+
return raw;
|
|
9280
10006
|
}
|
|
9281
|
-
|
|
10007
|
+
} catch {
|
|
10008
|
+
return null;
|
|
9282
10009
|
}
|
|
9283
10010
|
}
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
} else {
|
|
9287
|
-
console.log(`
|
|
9288
|
-
${files.length} card(s) passed validation.`);
|
|
10011
|
+
lookupCardPath(boardDir, nodeId) {
|
|
10012
|
+
return this.lookupCardPathFn(boardDir, nodeId);
|
|
9289
10013
|
}
|
|
10014
|
+
};
|
|
10015
|
+
function createNodeCardStore(lookupCardPath2) {
|
|
10016
|
+
return new NodeCardStore(lookupCardPath2);
|
|
9290
10017
|
}
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
10018
|
+
|
|
10019
|
+
// src/cli/board-live-cards-cli.ts
|
|
10020
|
+
var BOARD_FILE = "board-graph.json";
|
|
10021
|
+
var JOURNAL_FILE = "board-journal.jsonl";
|
|
10022
|
+
var TASK_EXECUTOR_LOG_FILE = "task-executor.jsonl";
|
|
10023
|
+
var INFERENCE_ADAPTER_LOG_FILE = "inference-adapter.jsonl";
|
|
10024
|
+
var INVENTORY_FILE = "cards-inventory.jsonl";
|
|
10025
|
+
var RUNTIME_OUT_FILE = ".runtime-out";
|
|
10026
|
+
var DEFAULT_RUNTIME_OUT_DIR = "runtime-out";
|
|
10027
|
+
var RUNTIME_STATUS_FILE = "board-livegraph-status.json";
|
|
10028
|
+
var RUNTIME_CARDS_DIR = "cards";
|
|
10029
|
+
var RUNTIME_DATA_OBJECTS_DIR = "data-objects";
|
|
10030
|
+
var INFERENCE_ADAPTER_FILE = ".inference-adapter";
|
|
10031
|
+
var TASK_EXECUTOR_FILE = ".task-executor";
|
|
10032
|
+
function readTaskExecutorConfig(boardDir) {
|
|
10033
|
+
const executorFile = path7__namespace.join(boardDir, TASK_EXECUTOR_FILE);
|
|
10034
|
+
if (!fs7__namespace.existsSync(executorFile)) return void 0;
|
|
10035
|
+
const raw = fs7__namespace.readFileSync(executorFile, "utf-8").trim();
|
|
10036
|
+
if (!raw) return void 0;
|
|
10037
|
+
try {
|
|
10038
|
+
const parsed = JSON.parse(raw);
|
|
10039
|
+
if (parsed && typeof parsed === "object" && typeof parsed.command === "string") {
|
|
10040
|
+
return parsed;
|
|
10041
|
+
}
|
|
10042
|
+
} catch {
|
|
9299
10043
|
}
|
|
9300
|
-
|
|
9301
|
-
type: "task-removal",
|
|
9302
|
-
taskName: cardId,
|
|
9303
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9304
|
-
});
|
|
9305
|
-
void processAccumulatedEventsInfinitePass(dir);
|
|
9306
|
-
console.log(`Card "${cardId}" removed.`);
|
|
10044
|
+
return { command: raw };
|
|
9307
10045
|
}
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
process.exit(1);
|
|
10046
|
+
var EMPTY_CONFIG = { settings: { completion: "manual", refreshStrategy: "data-changed" }, tasks: {} };
|
|
10047
|
+
var BoardJournal = class {
|
|
10048
|
+
journalPath;
|
|
10049
|
+
lastDrainedId;
|
|
10050
|
+
constructor(journalPath, lastDrainedJournalId) {
|
|
10051
|
+
this.journalPath = journalPath;
|
|
10052
|
+
this.lastDrainedId = lastDrainedJournalId;
|
|
9316
10053
|
}
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
process.exit(1);
|
|
10054
|
+
append(event) {
|
|
10055
|
+
const entry = { id: crypto.randomUUID(), event };
|
|
10056
|
+
fs7__namespace.appendFileSync(this.journalPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
9321
10057
|
}
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
10058
|
+
drain() {
|
|
10059
|
+
if (!fs7__namespace.existsSync(this.journalPath)) return [];
|
|
10060
|
+
const content = fs7__namespace.readFileSync(this.journalPath, "utf-8").trim();
|
|
10061
|
+
if (!content) return [];
|
|
10062
|
+
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
10063
|
+
let startIdx = 0;
|
|
10064
|
+
if (this.lastDrainedId) {
|
|
10065
|
+
const drainedIdx = entries.findIndex((e) => e.id === this.lastDrainedId);
|
|
10066
|
+
if (drainedIdx !== -1) startIdx = drainedIdx + 1;
|
|
10067
|
+
}
|
|
10068
|
+
const undrained = entries.slice(startIdx);
|
|
10069
|
+
if (undrained.length > 0) {
|
|
10070
|
+
this.lastDrainedId = undrained[undrained.length - 1].id;
|
|
10071
|
+
}
|
|
10072
|
+
return undrained.map((e) => e.event);
|
|
9332
10073
|
}
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
function cmdSourceDataFetchFailure(args) {
|
|
9342
|
-
const tokenIdx = args.indexOf("--token");
|
|
9343
|
-
const reasonIdx = args.indexOf("--reason");
|
|
9344
|
-
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
9345
|
-
const reason = reasonIdx !== -1 ? args[reasonIdx + 1] : "unknown";
|
|
9346
|
-
if (!token) {
|
|
9347
|
-
console.error("Usage: board-live-cards source-data-fetch-failure --token <sourceToken> [--reason <msg>]");
|
|
9348
|
-
process.exit(1);
|
|
10074
|
+
get size() {
|
|
10075
|
+
if (!fs7__namespace.existsSync(this.journalPath)) return 0;
|
|
10076
|
+
const content = fs7__namespace.readFileSync(this.journalPath, "utf-8").trim();
|
|
10077
|
+
if (!content) return 0;
|
|
10078
|
+
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
10079
|
+
if (!this.lastDrainedId) return entries.length;
|
|
10080
|
+
const drainedIdx = entries.findIndex((e) => e.id === this.lastDrainedId);
|
|
10081
|
+
return drainedIdx === -1 ? entries.length : entries.length - drainedIdx - 1;
|
|
9349
10082
|
}
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
console.error("Invalid source token");
|
|
9353
|
-
process.exit(1);
|
|
10083
|
+
get lastDrainedJournalId() {
|
|
10084
|
+
return this.lastDrainedId;
|
|
9354
10085
|
}
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
const
|
|
9358
|
-
if (!
|
|
9359
|
-
|
|
9360
|
-
|
|
10086
|
+
};
|
|
10087
|
+
function readCardInventory(boardDir) {
|
|
10088
|
+
const inventoryPath = path7__namespace.join(boardDir, INVENTORY_FILE);
|
|
10089
|
+
if (!fs7__namespace.existsSync(inventoryPath)) return [];
|
|
10090
|
+
const lines = fs7__namespace.readFileSync(inventoryPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
10091
|
+
return lines.map((l) => JSON.parse(l));
|
|
10092
|
+
}
|
|
10093
|
+
function lookupCardPath(boardDir, cardId) {
|
|
10094
|
+
const entries = readCardInventory(boardDir);
|
|
10095
|
+
const entry = entries.find((e) => e.cardId === cardId);
|
|
10096
|
+
return entry?.cardFilePath ?? null;
|
|
10097
|
+
}
|
|
10098
|
+
function appendCardInventory(boardDir, entry) {
|
|
10099
|
+
const inventoryPath = path7__namespace.join(boardDir, INVENTORY_FILE);
|
|
10100
|
+
const normalized = { ...entry, cardFilePath: path7__namespace.resolve(entry.cardFilePath) };
|
|
10101
|
+
fs7__namespace.appendFileSync(inventoryPath, JSON.stringify(normalized) + "\n");
|
|
10102
|
+
}
|
|
10103
|
+
function buildCardInventoryIndex(boardDir) {
|
|
10104
|
+
const byCardId = /* @__PURE__ */ new Map();
|
|
10105
|
+
const byCardPath = /* @__PURE__ */ new Map();
|
|
10106
|
+
for (const entry of readCardInventory(boardDir)) {
|
|
10107
|
+
const normalizedPath = path7__namespace.resolve(entry.cardFilePath);
|
|
10108
|
+
const normalizedEntry = {
|
|
10109
|
+
...entry,
|
|
10110
|
+
cardFilePath: normalizedPath
|
|
10111
|
+
};
|
|
10112
|
+
const existingById = byCardId.get(entry.cardId);
|
|
10113
|
+
if (existingById && existingById.cardFilePath !== normalizedPath) {
|
|
10114
|
+
throw new Error(
|
|
10115
|
+
`Inventory invariant violation: card id "${entry.cardId}" maps to multiple files: "${existingById.cardFilePath}" and "${normalizedPath}"`
|
|
10116
|
+
);
|
|
10117
|
+
}
|
|
10118
|
+
const existingByPath = byCardPath.get(normalizedPath);
|
|
10119
|
+
if (existingByPath && existingByPath.cardId !== entry.cardId) {
|
|
10120
|
+
throw new Error(
|
|
10121
|
+
`Inventory invariant violation: file "${normalizedPath}" maps to multiple ids: "${existingByPath.cardId}" and "${entry.cardId}"`
|
|
10122
|
+
);
|
|
10123
|
+
}
|
|
10124
|
+
byCardId.set(entry.cardId, normalizedEntry);
|
|
10125
|
+
byCardPath.set(normalizedPath, normalizedEntry);
|
|
9361
10126
|
}
|
|
9362
|
-
|
|
9363
|
-
appendEventToJournal(rg, {
|
|
9364
|
-
type: "task-progress",
|
|
9365
|
-
taskName: cbkDecoded.taskName,
|
|
9366
|
-
update: { bindTo: b, outputFile: d, failure: true, reason, sourceChecksum: cs },
|
|
9367
|
-
timestamp
|
|
9368
|
-
});
|
|
9369
|
-
void processAccumulatedEventsInfinitePass(rg);
|
|
10127
|
+
return { byCardId, byCardPath };
|
|
9370
10128
|
}
|
|
9371
|
-
function
|
|
9372
|
-
const
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
const callbackToken = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
9378
|
-
const boardDir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9379
|
-
const sourceChecksumsJson = sourceChecksumsIdx !== -1 ? args[sourceChecksumsIdx + 1] : void 0;
|
|
9380
|
-
const sourceChecksums = sourceChecksumsJson ? JSON.parse(sourceChecksumsJson) : void 0;
|
|
9381
|
-
if (!cardFilePath || !callbackToken || !boardDir) {
|
|
9382
|
-
console.error("Usage: board-live-cards run-sourcedefs-internal --card <path> --token <token> --rg <dir> [--source-checksums <json>]");
|
|
9383
|
-
process.exit(1);
|
|
10129
|
+
function initBoard(dir) {
|
|
10130
|
+
const boardPath = path7__namespace.join(dir, BOARD_FILE);
|
|
10131
|
+
if (fs7__namespace.existsSync(boardPath)) {
|
|
10132
|
+
const envelope2 = JSON.parse(fs7__namespace.readFileSync(boardPath, "utf-8"));
|
|
10133
|
+
restore(envelope2.graph);
|
|
10134
|
+
return "exists";
|
|
9384
10135
|
}
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
} catch {
|
|
10136
|
+
if (fs7__namespace.existsSync(dir)) {
|
|
10137
|
+
const entries = fs7__namespace.readdirSync(dir);
|
|
10138
|
+
if (entries.length > 0) {
|
|
10139
|
+
throw new Error(`Directory "${dir}" is not empty and has no valid ${BOARD_FILE}`);
|
|
9390
10140
|
}
|
|
9391
10141
|
}
|
|
9392
|
-
|
|
9393
|
-
const
|
|
9394
|
-
const
|
|
9395
|
-
const
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
};
|
|
9428
|
-
appendTaskExecutorLog(boardDir, sourceForExecutor, "external-task-executor");
|
|
9429
|
-
fs__namespace.writeFileSync(inFile, JSON.stringify(sourceForExecutor, null, 2), "utf-8");
|
|
9430
|
-
const executorArgs = ["run-source-fetch", "--in", inFile, "--out", outFile2, "--err", errFile];
|
|
9431
|
-
if (taskExecutorExtraB64) executorArgs.push("--extra", taskExecutorExtraB64);
|
|
9432
|
-
console.log(`[run-sourcedefs-internal] task-executor: ${taskExecutor} ${executorArgs.join(" ")}`);
|
|
9433
|
-
try {
|
|
9434
|
-
execCommandSync(taskExecutor, executorArgs, {
|
|
9435
|
-
shell: true,
|
|
9436
|
-
timeout: src.timeout ?? 12e4
|
|
9437
|
-
});
|
|
9438
|
-
} catch (err) {
|
|
9439
|
-
const reason = err.message ?? String(err);
|
|
9440
|
-
console.error(`[run-sourcedefs-internal] task-executor failed for source "${src.bindTo}":`, reason);
|
|
9441
|
-
reportFailure(reason);
|
|
9442
|
-
return;
|
|
9443
|
-
}
|
|
9444
|
-
if (fs__namespace.existsSync(outFile2)) {
|
|
9445
|
-
reportFetched(outFile2);
|
|
9446
|
-
} else {
|
|
9447
|
-
const errMsg = fs__namespace.existsSync(errFile) ? fs__namespace.readFileSync(errFile, "utf-8").trim() : "executor produced no output file";
|
|
9448
|
-
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
9449
|
-
reportFailure(errMsg);
|
|
9450
|
-
}
|
|
9451
|
-
return;
|
|
9452
|
-
}
|
|
9453
|
-
if (!src.outputFile) {
|
|
9454
|
-
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}" has no outputFile configured \u2014 cannot deliver`);
|
|
9455
|
-
reportFailure("no outputFile configured");
|
|
9456
|
-
return;
|
|
9457
|
-
}
|
|
9458
|
-
const outFile = path__namespace.join(os__namespace.tmpdir(), `card-source-out-${src.bindTo}-${Date.now()}.json`);
|
|
9459
|
-
if (!src.cli) {
|
|
9460
|
-
const errMsg = "source.cli is required for built-in source execution";
|
|
9461
|
-
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
9462
|
-
reportFailure(errMsg);
|
|
9463
|
-
return;
|
|
9464
|
-
}
|
|
9465
|
-
const timeout = src.timeout ?? 12e4;
|
|
9466
|
-
const sourceCwd = typeof src.cwd === "string" ? src.cwd : path__namespace.dirname(cardFilePath || "");
|
|
9467
|
-
const sourceBoardDir = typeof src.boardDir === "string" ? src.boardDir : boardDir;
|
|
9468
|
-
const sourceForBuiltInExecutor = {
|
|
9469
|
-
...src,
|
|
9470
|
-
cwd: sourceCwd,
|
|
9471
|
-
boardDir: sourceBoardDir
|
|
9472
|
-
};
|
|
9473
|
-
appendTaskExecutorLog(boardDir, sourceForBuiltInExecutor, "built-in-run-source-fetch");
|
|
9474
|
-
const cmdParts = splitCommandLine(src.cli);
|
|
9475
|
-
if (cmdParts.length === 0) {
|
|
9476
|
-
const errMsg = "source.cli command is empty";
|
|
9477
|
-
console.warn(`[run-sourcedefs-internal] source "${src.bindTo}": ${errMsg}`);
|
|
9478
|
-
reportFailure(errMsg);
|
|
9479
|
-
return;
|
|
9480
|
-
}
|
|
9481
|
-
const rawCmd = cmdParts[0];
|
|
9482
|
-
const { cmd, args: cliArgs } = resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
9483
|
-
let stdout;
|
|
9484
|
-
try {
|
|
9485
|
-
stdout = execCommandSync(cmd, cliArgs, {
|
|
9486
|
-
shell: false,
|
|
9487
|
-
encoding: "utf-8",
|
|
9488
|
-
timeout,
|
|
9489
|
-
cwd: sourceCwd,
|
|
9490
|
-
env: {
|
|
9491
|
-
...process.env,
|
|
9492
|
-
...sourceBoardDir ? { BOARD_DIR: sourceBoardDir } : {}
|
|
9493
|
-
}
|
|
9494
|
-
});
|
|
9495
|
-
} catch (err) {
|
|
9496
|
-
const reason = err.message ?? String(err);
|
|
9497
|
-
console.error(`[run-sourcedefs-internal] source fetch failed for source "${src.bindTo}":`, reason);
|
|
9498
|
-
reportFailure(reason);
|
|
9499
|
-
return;
|
|
10142
|
+
fs7__namespace.mkdirSync(dir, { recursive: true });
|
|
10143
|
+
const live = createLiveGraph(EMPTY_CONFIG);
|
|
10144
|
+
const snap = snapshot(live);
|
|
10145
|
+
const envelope = { lastDrainedJournalId: "", graph: snap };
|
|
10146
|
+
fs7__namespace.writeFileSync(boardPath, JSON.stringify(envelope, null, 2));
|
|
10147
|
+
return "created";
|
|
10148
|
+
}
|
|
10149
|
+
function loadBoardEnvelope(dir) {
|
|
10150
|
+
const raw = fs7__namespace.readFileSync(path7__namespace.join(dir, BOARD_FILE), "utf-8");
|
|
10151
|
+
return JSON.parse(raw);
|
|
10152
|
+
}
|
|
10153
|
+
function loadBoard(dir) {
|
|
10154
|
+
const envelope = loadBoardEnvelope(dir);
|
|
10155
|
+
return restore(envelope.graph);
|
|
10156
|
+
}
|
|
10157
|
+
function saveBoard(dir, rg, journal) {
|
|
10158
|
+
const snap = rg.snapshot();
|
|
10159
|
+
const envelope = {
|
|
10160
|
+
lastDrainedJournalId: journal.lastDrainedJournalId,
|
|
10161
|
+
graph: snap
|
|
10162
|
+
};
|
|
10163
|
+
writeJsonAtomic2(path7__namespace.join(dir, BOARD_FILE), envelope);
|
|
10164
|
+
const live = restore(snap);
|
|
10165
|
+
const statusObject = buildBoardStatusObject(path7__namespace.resolve(dir), live);
|
|
10166
|
+
writeJsonAtomic2(resolveStatusSnapshotPath(dir), statusObject);
|
|
10167
|
+
}
|
|
10168
|
+
function runtimeOutConfigPath(boardDir) {
|
|
10169
|
+
return path7__namespace.join(boardDir, RUNTIME_OUT_FILE);
|
|
10170
|
+
}
|
|
10171
|
+
function resolveConfiguredRuntimeOutDir(boardDir) {
|
|
10172
|
+
const cfgPath = runtimeOutConfigPath(boardDir);
|
|
10173
|
+
if (fs7__namespace.existsSync(cfgPath)) {
|
|
10174
|
+
const configured = fs7__namespace.readFileSync(cfgPath, "utf-8").trim();
|
|
10175
|
+
if (configured) {
|
|
10176
|
+
return path7__namespace.isAbsolute(configured) ? configured : path7__namespace.resolve(boardDir, configured);
|
|
9500
10177
|
}
|
|
9501
|
-
fs__namespace.writeFileSync(outFile, stdout.trim(), "utf-8");
|
|
9502
|
-
reportFetched(outFile);
|
|
9503
10178
|
}
|
|
9504
|
-
const
|
|
9505
|
-
|
|
9506
|
-
|
|
10179
|
+
const defaultDir = path7__namespace.join(boardDir, DEFAULT_RUNTIME_OUT_DIR);
|
|
10180
|
+
fs7__namespace.writeFileSync(cfgPath, defaultDir, "utf-8");
|
|
10181
|
+
return defaultDir;
|
|
10182
|
+
}
|
|
10183
|
+
function configureRuntimeOutDir(boardDir, runtimeOut) {
|
|
10184
|
+
let resolved;
|
|
10185
|
+
if (runtimeOut) {
|
|
10186
|
+
resolved = path7__namespace.isAbsolute(runtimeOut) ? runtimeOut : path7__namespace.resolve(boardDir, runtimeOut);
|
|
10187
|
+
} else {
|
|
10188
|
+
resolved = path7__namespace.join(boardDir, DEFAULT_RUNTIME_OUT_DIR);
|
|
10189
|
+
}
|
|
10190
|
+
fs7__namespace.mkdirSync(resolved, { recursive: true });
|
|
10191
|
+
fs7__namespace.writeFileSync(runtimeOutConfigPath(boardDir), resolved, "utf-8");
|
|
10192
|
+
return resolved;
|
|
10193
|
+
}
|
|
10194
|
+
function resolveStatusSnapshotPath(boardDir) {
|
|
10195
|
+
return path7__namespace.join(resolveConfiguredRuntimeOutDir(boardDir), RUNTIME_STATUS_FILE);
|
|
10196
|
+
}
|
|
10197
|
+
function resolveComputedValuesPath(boardDir, cardId) {
|
|
10198
|
+
return path7__namespace.join(resolveConfiguredRuntimeOutDir(boardDir), RUNTIME_CARDS_DIR, `${cardId}.computed.json`);
|
|
10199
|
+
}
|
|
10200
|
+
function resolveDataObjectsDirPath(boardDir) {
|
|
10201
|
+
return path7__namespace.join(resolveConfiguredRuntimeOutDir(boardDir), RUNTIME_DATA_OBJECTS_DIR);
|
|
10202
|
+
}
|
|
10203
|
+
function toDataObjectFileName(token) {
|
|
10204
|
+
return token.replace(/[\\/]/g, "__");
|
|
10205
|
+
}
|
|
10206
|
+
function writeRuntimeDataObjects(boardDir, data) {
|
|
10207
|
+
for (const [token, payload] of Object.entries(data)) {
|
|
10208
|
+
if (!token) continue;
|
|
10209
|
+
const fileName = toDataObjectFileName(token);
|
|
10210
|
+
if (!fileName) continue;
|
|
10211
|
+
const filePath = path7__namespace.join(resolveDataObjectsDirPath(boardDir), fileName);
|
|
10212
|
+
writeJsonAtomic2(filePath, payload);
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
function writeJsonAtomic2(filePath, payload) {
|
|
10216
|
+
fs7__namespace.mkdirSync(path7__namespace.dirname(filePath), { recursive: true });
|
|
10217
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
10218
|
+
fs7__namespace.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
10219
|
+
fs7__namespace.renameSync(tmpPath, filePath);
|
|
10220
|
+
}
|
|
10221
|
+
function withBoardLock(boardDir, fn) {
|
|
10222
|
+
const boardPath = path7__namespace.join(boardDir, BOARD_FILE);
|
|
10223
|
+
const release = properLockfile.lockSync(boardPath, { retries: { retries: 5, minTimeout: 100 } });
|
|
10224
|
+
try {
|
|
10225
|
+
return fn();
|
|
10226
|
+
} finally {
|
|
10227
|
+
release();
|
|
10228
|
+
}
|
|
10229
|
+
}
|
|
10230
|
+
function decodeCallbackToken2(token) {
|
|
10231
|
+
try {
|
|
10232
|
+
const payload = JSON.parse(Buffer.from(token, "base64url").toString());
|
|
10233
|
+
if (typeof payload?.t === "string") return { taskName: payload.t };
|
|
10234
|
+
return null;
|
|
10235
|
+
} catch {
|
|
10236
|
+
return null;
|
|
9507
10237
|
}
|
|
9508
10238
|
}
|
|
9509
|
-
function
|
|
9510
|
-
|
|
9511
|
-
const tokenIdx = args.indexOf("--token");
|
|
9512
|
-
const updateIdx = args.indexOf("--update");
|
|
9513
|
-
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9514
|
-
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : void 0;
|
|
9515
|
-
const updateJson = updateIdx !== -1 ? args[updateIdx + 1] : "{}";
|
|
9516
|
-
if (!dir || !token) {
|
|
9517
|
-
console.error("Usage: board-live-cards task-progress --rg <dir> --token <token> [--update <json>]");
|
|
9518
|
-
process.exit(1);
|
|
9519
|
-
}
|
|
9520
|
-
const decoded = decodeCallbackToken2(token);
|
|
9521
|
-
if (!decoded) {
|
|
9522
|
-
console.error("Invalid callback token");
|
|
9523
|
-
process.exit(1);
|
|
9524
|
-
}
|
|
9525
|
-
const update = updateJson ? JSON.parse(updateJson) : {};
|
|
9526
|
-
appendEventToJournal(dir, {
|
|
9527
|
-
type: "task-progress",
|
|
9528
|
-
taskName: decoded.taskName,
|
|
9529
|
-
update,
|
|
9530
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9531
|
-
});
|
|
9532
|
-
void processAccumulatedEventsInfinitePass(dir);
|
|
10239
|
+
function encodeSourceToken(payload) {
|
|
10240
|
+
return Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
9533
10241
|
}
|
|
9534
|
-
function
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
const decodedToken = decodeSourceToken(inferenceToken);
|
|
9544
|
-
if (!decodedToken) {
|
|
9545
|
-
console.error("Invalid inference token");
|
|
9546
|
-
process.exit(1);
|
|
9547
|
-
}
|
|
9548
|
-
const callbackToken = decodedToken.cbk;
|
|
9549
|
-
const boardDir = decodedToken.rg;
|
|
9550
|
-
const cbkDecoded = decodeCallbackToken2(callbackToken);
|
|
9551
|
-
if (!cbkDecoded) {
|
|
9552
|
-
console.error("Invalid callback token embedded in inference token");
|
|
9553
|
-
process.exit(1);
|
|
9554
|
-
}
|
|
9555
|
-
function spawnInferenceDone(tmpFile) {
|
|
9556
|
-
const { cmd, args: cliArgs } = getCliInvocation("inference-done", ["--tmp", tmpFile, "--token", inferenceToken]);
|
|
9557
|
-
spawnDetachedCommand(cmd, cliArgs);
|
|
9558
|
-
}
|
|
9559
|
-
function spawnInferenceDoneError(reason) {
|
|
9560
|
-
const tmpFile = path__namespace.join(os__namespace.tmpdir(), `card-inference-err-${Date.now()}.json`);
|
|
9561
|
-
fs__namespace.writeFileSync(tmpFile, JSON.stringify({ isTaskCompleted: false, reason }), "utf-8");
|
|
9562
|
-
spawnInferenceDone(tmpFile);
|
|
10242
|
+
function decodeSourceToken(token) {
|
|
10243
|
+
try {
|
|
10244
|
+
const p = JSON.parse(Buffer.from(token, "base64url").toString());
|
|
10245
|
+
if (typeof p?.cbk === "string" && typeof p?.cid === "string" && typeof p?.b === "string" && typeof p?.d === "string") {
|
|
10246
|
+
return p;
|
|
10247
|
+
}
|
|
10248
|
+
return null;
|
|
10249
|
+
} catch {
|
|
10250
|
+
return null;
|
|
9563
10251
|
}
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
10252
|
+
}
|
|
10253
|
+
function appendEventToJournal(boardDir, event) {
|
|
10254
|
+
const journalPath = path7__namespace.join(boardDir, JOURNAL_FILE);
|
|
10255
|
+
const entry = { id: crypto.randomUUID(), event };
|
|
10256
|
+
fs7__namespace.appendFileSync(journalPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
10257
|
+
}
|
|
10258
|
+
function getUndrainedEntries(boardDir, lastDrainedId) {
|
|
10259
|
+
const journalPath = path7__namespace.join(boardDir, JOURNAL_FILE);
|
|
10260
|
+
if (!fs7__namespace.existsSync(journalPath)) return [];
|
|
10261
|
+
const content = fs7__namespace.readFileSync(journalPath, "utf-8").trim();
|
|
10262
|
+
if (!content) return [];
|
|
10263
|
+
const entries = content.split("\n").map((l) => JSON.parse(l));
|
|
10264
|
+
if (!lastDrainedId) return entries;
|
|
10265
|
+
const idx = entries.findIndex((e) => e.id === lastDrainedId);
|
|
10266
|
+
return idx === -1 ? entries : entries.slice(idx + 1);
|
|
10267
|
+
}
|
|
10268
|
+
function determineLatestPendingAccumulated(boardDir) {
|
|
10269
|
+
const boardPath = path7__namespace.join(boardDir, BOARD_FILE);
|
|
10270
|
+
if (!fs7__namespace.existsSync(boardPath)) return 0;
|
|
10271
|
+
try {
|
|
10272
|
+
const envelope = loadBoardEnvelope(boardDir);
|
|
10273
|
+
return getUndrainedEntries(boardDir, envelope.lastDrainedJournalId).length;
|
|
10274
|
+
} catch {
|
|
10275
|
+
return 0;
|
|
9567
10276
|
}
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
if (
|
|
9571
|
-
|
|
9572
|
-
|
|
10277
|
+
}
|
|
10278
|
+
function shouldUseShellForCommand(cmd, forceShell) {
|
|
10279
|
+
if (typeof forceShell === "boolean") return forceShell;
|
|
10280
|
+
return process.platform === "win32" && /\.(cmd|bat)$/i.test(cmd);
|
|
10281
|
+
}
|
|
10282
|
+
var _gitBashPath;
|
|
10283
|
+
var GIT_BASH_CACHE_FILE2 = path7__namespace.join(os3__namespace.tmpdir(), ".board-live-cards-git-bash-cache.json");
|
|
10284
|
+
function findGitBash2() {
|
|
10285
|
+
if (_gitBashPath !== void 0) return _gitBashPath;
|
|
10286
|
+
if (process.platform !== "win32") return _gitBashPath = false;
|
|
10287
|
+
try {
|
|
10288
|
+
const cached = JSON.parse(fs7__namespace.readFileSync(GIT_BASH_CACHE_FILE2, "utf8"));
|
|
10289
|
+
if (cached.path === false || typeof cached.path === "string" && fs7__namespace.existsSync(cached.path)) {
|
|
10290
|
+
return _gitBashPath = cached.path;
|
|
10291
|
+
}
|
|
10292
|
+
} catch {
|
|
9573
10293
|
}
|
|
9574
|
-
const
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
10294
|
+
const candidates = [
|
|
10295
|
+
process.env.SHELL,
|
|
10296
|
+
process.env.PROGRAMFILES && path7__namespace.join(process.env.PROGRAMFILES, "Git", "usr", "bin", "bash.exe"),
|
|
10297
|
+
process.env.PROGRAMFILES && path7__namespace.join(process.env.PROGRAMFILES, "Git", "bin", "bash.exe"),
|
|
10298
|
+
process.env["PROGRAMFILES(X86)"] && path7__namespace.join(process.env["PROGRAMFILES(X86)"], "Git", "bin", "bash.exe"),
|
|
10299
|
+
process.env.LOCALAPPDATA && path7__namespace.join(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")
|
|
10300
|
+
];
|
|
10301
|
+
for (const c of candidates) {
|
|
10302
|
+
if (c && /bash(\.exe)?$/i.test(c) && fs7__namespace.existsSync(c)) {
|
|
10303
|
+
_gitBashPath = c;
|
|
10304
|
+
try {
|
|
10305
|
+
fs7__namespace.writeFileSync(GIT_BASH_CACHE_FILE2, JSON.stringify({ path: c }));
|
|
10306
|
+
} catch {
|
|
10307
|
+
}
|
|
10308
|
+
return _gitBashPath;
|
|
10309
|
+
}
|
|
9580
10310
|
}
|
|
9581
|
-
|
|
9582
|
-
const adapterRawArgs = adapterParts.slice(1);
|
|
9583
|
-
const { cmd: adapterCmd, args: adapterArgsPrefix } = resolveCommandInvocation(adapterRawCmd, adapterRawArgs);
|
|
9584
|
-
const adapterArgs = [...adapterArgsPrefix, "run-inference", "--in", inFile, "--out", outFile, "--err", errFile];
|
|
10311
|
+
_gitBashPath = false;
|
|
9585
10312
|
try {
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
timeout: 12e4,
|
|
9589
|
-
cwd: boardDir,
|
|
9590
|
-
env: {
|
|
9591
|
-
...process.env,
|
|
9592
|
-
BOARD_DIR: boardDir
|
|
9593
|
-
}
|
|
9594
|
-
});
|
|
9595
|
-
} catch (err) {
|
|
9596
|
-
const reason = err.message ?? String(err);
|
|
9597
|
-
spawnInferenceDoneError(reason);
|
|
9598
|
-
return;
|
|
10313
|
+
fs7__namespace.writeFileSync(GIT_BASH_CACHE_FILE2, JSON.stringify({ path: false }));
|
|
10314
|
+
} catch {
|
|
9599
10315
|
}
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
10316
|
+
return _gitBashPath;
|
|
10317
|
+
}
|
|
10318
|
+
function shellQuote2(s) {
|
|
10319
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
10320
|
+
}
|
|
10321
|
+
function spawnDetachedCommand(cmd, args) {
|
|
10322
|
+
if (process.platform === "win32") {
|
|
10323
|
+
const bash = findGitBash2();
|
|
10324
|
+
if (bash) {
|
|
10325
|
+
const shellCmd = [cmd, ...args].map((a) => shellQuote2(a.replace(/\\/g, "/"))).join(" ");
|
|
10326
|
+
const child3 = child_process.spawn(bash, ["-c", shellCmd], { detached: true, stdio: "ignore", windowsHide: true });
|
|
10327
|
+
child3.unref();
|
|
10328
|
+
return;
|
|
10329
|
+
}
|
|
10330
|
+
const child2 = child_process.spawn("cmd", ["/c", "start", "/b", "", cmd, ...args], {
|
|
10331
|
+
detached: true,
|
|
10332
|
+
stdio: "ignore",
|
|
10333
|
+
windowsHide: true
|
|
10334
|
+
});
|
|
10335
|
+
child2.unref();
|
|
9603
10336
|
return;
|
|
9604
10337
|
}
|
|
9605
|
-
|
|
10338
|
+
const child = child_process.spawn(cmd, args, { detached: true, stdio: "ignore" });
|
|
10339
|
+
child.unref();
|
|
9606
10340
|
}
|
|
9607
|
-
function
|
|
9608
|
-
const
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
}
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
const
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
result = { isTaskCompleted: false, reason: `failed to parse inference result: ${err instanceof Error ? err.message : String(err)}` };
|
|
10341
|
+
function execCommandSync(cmd, args, options) {
|
|
10342
|
+
const output = child_process.execFileSync(cmd, args, {
|
|
10343
|
+
shell: shouldUseShellForCommand(cmd, options?.shell),
|
|
10344
|
+
timeout: options?.timeout,
|
|
10345
|
+
encoding: options?.encoding,
|
|
10346
|
+
cwd: options?.cwd,
|
|
10347
|
+
windowsHide: true,
|
|
10348
|
+
env: options?.env
|
|
10349
|
+
});
|
|
10350
|
+
return typeof output === "string" ? output : output.toString("utf-8");
|
|
10351
|
+
}
|
|
10352
|
+
function execCommandAsync(cmd, args, callback) {
|
|
10353
|
+
child_process.execFile(
|
|
10354
|
+
cmd,
|
|
10355
|
+
args,
|
|
10356
|
+
{ shell: shouldUseShellForCommand(cmd), encoding: "utf8", windowsHide: true },
|
|
10357
|
+
(err, stdout, stderr) => callback(err ?? null, stdout, stderr)
|
|
10358
|
+
);
|
|
10359
|
+
}
|
|
10360
|
+
function splitCommandLine(command) {
|
|
10361
|
+
const tokens = [];
|
|
10362
|
+
let current = "";
|
|
10363
|
+
let quote = null;
|
|
10364
|
+
for (const ch of command.trim()) {
|
|
10365
|
+
if (quote) {
|
|
10366
|
+
if (ch === quote) {
|
|
10367
|
+
quote = null;
|
|
10368
|
+
} else {
|
|
10369
|
+
current += ch;
|
|
10370
|
+
}
|
|
10371
|
+
continue;
|
|
9639
10372
|
}
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
10373
|
+
if (ch === '"' || ch === "'") {
|
|
10374
|
+
quote = ch;
|
|
10375
|
+
continue;
|
|
9643
10376
|
}
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
if (!card.card_data) card.card_data = {};
|
|
9651
|
-
const cardData = card.card_data;
|
|
9652
|
-
const existingInference = cardData.llm_task_completion_inference && typeof cardData.llm_task_completion_inference === "object" ? cardData.llm_task_completion_inference : {};
|
|
9653
|
-
cardData.llm_task_completion_inference = {
|
|
9654
|
-
...existingInference,
|
|
9655
|
-
isTaskCompleted: isTaskCompletedFlag,
|
|
9656
|
-
reason: typeof result.reason === "string" ? result.reason : "",
|
|
9657
|
-
evidence: typeof result.evidence === "string" ? result.evidence : "",
|
|
9658
|
-
inferenceCompletedAt
|
|
9659
|
-
};
|
|
9660
|
-
fs__namespace.writeFileSync(cardPath, JSON.stringify(card, null, 2), "utf-8");
|
|
9661
|
-
const runtimePath2 = path__namespace.join(dir, taskName, "runtime.json");
|
|
9662
|
-
let runtime = { _sources: {} };
|
|
9663
|
-
if (fs__namespace.existsSync(runtimePath2)) {
|
|
9664
|
-
try {
|
|
9665
|
-
runtime = JSON.parse(fs__namespace.readFileSync(runtimePath2, "utf-8"));
|
|
9666
|
-
} catch {
|
|
10377
|
+
if (/\s/.test(ch)) {
|
|
10378
|
+
if (current) {
|
|
10379
|
+
tokens.push(current);
|
|
10380
|
+
current = "";
|
|
10381
|
+
}
|
|
10382
|
+
continue;
|
|
9667
10383
|
}
|
|
10384
|
+
current += ch;
|
|
9668
10385
|
}
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
type: "task-progress",
|
|
9675
|
-
taskName,
|
|
9676
|
-
update: {
|
|
9677
|
-
kind: "inference-done",
|
|
9678
|
-
isTaskCompleted: isTaskCompletedFlag,
|
|
9679
|
-
inputChecksum
|
|
9680
|
-
},
|
|
9681
|
-
timestamp: inferenceCompletedAt
|
|
9682
|
-
});
|
|
9683
|
-
void processAccumulatedEventsInfinitePass(dir);
|
|
10386
|
+
if (quote) {
|
|
10387
|
+
throw new Error(`Unterminated quote in command: ${command}`);
|
|
10388
|
+
}
|
|
10389
|
+
if (current) tokens.push(current);
|
|
10390
|
+
return tokens;
|
|
9684
10391
|
}
|
|
9685
|
-
function
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
const errIdx = args.indexOf("--err");
|
|
9689
|
-
const inFile = inIdx !== -1 ? args[inIdx + 1] : void 0;
|
|
9690
|
-
const outFile = outIdx !== -1 ? args[outIdx + 1] : void 0;
|
|
9691
|
-
const errFile = errIdx !== -1 ? args[errIdx + 1] : void 0;
|
|
9692
|
-
if (!inFile || !outFile) {
|
|
9693
|
-
console.error("Usage: board-live-cards run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]");
|
|
9694
|
-
process.exit(1);
|
|
10392
|
+
function resolveCommandInvocation(rawCmd, rawArgs) {
|
|
10393
|
+
if (/^(node|node\.exe)$/i.test(rawCmd)) {
|
|
10394
|
+
return { cmd: process.execPath, args: rawArgs };
|
|
9695
10395
|
}
|
|
9696
|
-
if (
|
|
9697
|
-
|
|
9698
|
-
if (errFile) fs__namespace.writeFileSync(errFile, msg);
|
|
9699
|
-
console.error(`[run-source-fetch] ${msg}`);
|
|
9700
|
-
process.exit(1);
|
|
10396
|
+
if (/\.m?js$/i.test(rawCmd)) {
|
|
10397
|
+
return { cmd: process.execPath, args: [rawCmd, ...rawArgs] };
|
|
9701
10398
|
}
|
|
9702
|
-
|
|
10399
|
+
return { cmd: rawCmd, args: rawArgs };
|
|
10400
|
+
}
|
|
10401
|
+
function spawnDetachedProcessAccumulatedWorker(boardDir) {
|
|
10402
|
+
const { cmd, args: cliArgs } = getCliInvocation("process-accumulated-events", ["--rg", boardDir, "--inline-loop"]);
|
|
9703
10403
|
try {
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
} catch
|
|
9707
|
-
|
|
9708
|
-
if (errFile) fs__namespace.writeFileSync(errFile, msg);
|
|
9709
|
-
console.error(`[run-source-fetch] ${msg}`);
|
|
9710
|
-
process.exit(1);
|
|
9711
|
-
}
|
|
9712
|
-
if (!source.cli) {
|
|
9713
|
-
const msg = "Source definition missing cli field (board-live-cards built-in executor only understands source.cli)";
|
|
9714
|
-
if (errFile) fs__namespace.writeFileSync(errFile, msg);
|
|
9715
|
-
console.error(`[run-source-fetch] ${msg}`);
|
|
9716
|
-
process.exit(1);
|
|
10404
|
+
spawnDetachedCommand(cmd, cliArgs);
|
|
10405
|
+
return true;
|
|
10406
|
+
} catch {
|
|
10407
|
+
return false;
|
|
9717
10408
|
}
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
const msg = "Source cli command is empty";
|
|
9725
|
-
if (errFile) fs__namespace.writeFileSync(errFile, msg);
|
|
9726
|
-
console.error(`[run-source-fetch] ${msg}`);
|
|
9727
|
-
process.exit(1);
|
|
10409
|
+
}
|
|
10410
|
+
async function processAccumulatedEventsInlineLoop(boardDir, settleDelayMs = 50) {
|
|
10411
|
+
while (determineLatestPendingAccumulated(boardDir) > 0) {
|
|
10412
|
+
const ran = await processAccumulatedEvents(boardDir);
|
|
10413
|
+
if (!ran) return false;
|
|
10414
|
+
await new Promise((resolve6) => setTimeout(resolve6, settleDelayMs));
|
|
9728
10415
|
}
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
10416
|
+
return true;
|
|
10417
|
+
}
|
|
10418
|
+
function shouldAvoidDetachedProcessSpawn() {
|
|
10419
|
+
return process.env.BOARD_LIVE_CARDS_NO_SPAWN === "1";
|
|
10420
|
+
}
|
|
10421
|
+
async function processAccumulatedEvents(boardDir) {
|
|
10422
|
+
const boardPath = path7__namespace.join(boardDir, BOARD_FILE);
|
|
10423
|
+
const cliDir = path7__namespace.resolve(__dirname$1, "..", "..");
|
|
10424
|
+
let release;
|
|
9732
10425
|
try {
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
timeout,
|
|
9737
|
-
cwd: sourceCwd,
|
|
9738
|
-
env: {
|
|
9739
|
-
...process.env,
|
|
9740
|
-
...sourceBoardDir ? { BOARD_DIR: sourceBoardDir } : {}
|
|
9741
|
-
}
|
|
9742
|
-
});
|
|
9743
|
-
} catch (err) {
|
|
9744
|
-
const msg = err.message ?? String(err);
|
|
9745
|
-
console.error(`[run-source-fetch] cli failed: ${msg}`);
|
|
9746
|
-
if (errFile) fs__namespace.writeFileSync(errFile, msg);
|
|
9747
|
-
process.exit(1);
|
|
10426
|
+
release = properLockfile.lockSync(boardPath, { retries: 0 });
|
|
10427
|
+
} catch {
|
|
10428
|
+
return false;
|
|
9748
10429
|
}
|
|
9749
|
-
const result = stdout.trim();
|
|
9750
10430
|
try {
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
10431
|
+
const cardHandlerAdapters = {
|
|
10432
|
+
cardStore: createNodeCardStore(lookupCardPath),
|
|
10433
|
+
runtimeStore: createNodeRuntimeStore(),
|
|
10434
|
+
outputStore: createNodeOutputStore(resolveComputedValuesPath, resolveDataObjectsDirPath, INFERENCE_ADAPTER_LOG_FILE),
|
|
10435
|
+
inputStore: createNodeInputStore(JOURNAL_FILE),
|
|
10436
|
+
invocationAdapter: createNodeInvocationAdapter(cliDir, encodeSourceToken)
|
|
10437
|
+
};
|
|
10438
|
+
const envelope = loadBoardEnvelope(boardDir);
|
|
10439
|
+
const live = restore(envelope.graph);
|
|
10440
|
+
const journal = new BoardJournal(path7__namespace.join(boardDir, JOURNAL_FILE), envelope.lastDrainedJournalId);
|
|
10441
|
+
const rg = createReactiveGraph(live, { handlers: { "card-handler": createCardHandlerFn(boardDir, cardHandlerAdapters) } });
|
|
10442
|
+
const undrained = journal.drain();
|
|
10443
|
+
rg.pushAll(undrained);
|
|
10444
|
+
await rg.dispose({ wait: true });
|
|
10445
|
+
saveBoard(boardDir, rg, journal);
|
|
10446
|
+
return true;
|
|
10447
|
+
} finally {
|
|
10448
|
+
release();
|
|
9758
10449
|
}
|
|
9759
10450
|
}
|
|
9760
|
-
function
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
const globIdx = args.indexOf("--card-glob");
|
|
9764
|
-
const cardIdIdx = args.indexOf("--card-id");
|
|
9765
|
-
const restart = args.includes("--restart");
|
|
9766
|
-
const dir = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9767
|
-
const cardFile = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
9768
|
-
const cardGlob = globIdx !== -1 ? args[globIdx + 1] : void 0;
|
|
9769
|
-
const requestedCardId = cardIdIdx !== -1 ? args[cardIdIdx + 1] : void 0;
|
|
9770
|
-
if (!dir || !cardFile && !cardGlob || cardFile && cardGlob) {
|
|
9771
|
-
console.error("Usage: board-live-cards upsert-card --rg <dir> (--card <card.json> | --card-glob <glob>) [--card-id <card-id>] [--restart]");
|
|
9772
|
-
process.exit(1);
|
|
9773
|
-
}
|
|
9774
|
-
if (cardGlob && requestedCardId) {
|
|
9775
|
-
console.error("Usage: --card-id may be used only with --card (single file), not with --card-glob");
|
|
9776
|
-
process.exit(1);
|
|
9777
|
-
}
|
|
9778
|
-
const cardFiles = cardFile ? [path__namespace.resolve(cardFile)] : resolveCardGlobMatches(cardGlob);
|
|
9779
|
-
if (!cardFile && cardFiles.length === 0) {
|
|
9780
|
-
console.error(`No card files matched glob: ${cardGlob}`);
|
|
9781
|
-
process.exit(1);
|
|
9782
|
-
}
|
|
9783
|
-
const idx = buildCardInventoryIndex(dir);
|
|
9784
|
-
const batchByCardId = /* @__PURE__ */ new Map();
|
|
9785
|
-
const batchByCardPath = /* @__PURE__ */ new Map();
|
|
9786
|
-
const plans = [];
|
|
9787
|
-
const logs = [];
|
|
9788
|
-
for (const absCardPath of cardFiles) {
|
|
9789
|
-
if (!fs__namespace.existsSync(absCardPath)) {
|
|
9790
|
-
console.error(`Card file not found: ${absCardPath}`);
|
|
9791
|
-
process.exit(1);
|
|
9792
|
-
}
|
|
9793
|
-
const card = JSON.parse(fs__namespace.readFileSync(absCardPath, "utf-8"));
|
|
9794
|
-
if (!card.id) {
|
|
9795
|
-
console.error(`Card JSON must have an "id" field (${absCardPath})`);
|
|
9796
|
-
process.exit(1);
|
|
9797
|
-
}
|
|
9798
|
-
if (requestedCardId && requestedCardId !== card.id) {
|
|
9799
|
-
console.error(
|
|
9800
|
-
`Card id mismatch: --card-id "${requestedCardId}" does not match file id "${card.id}" (${absCardPath})`
|
|
9801
|
-
);
|
|
9802
|
-
process.exit(1);
|
|
9803
|
-
}
|
|
9804
|
-
const seenPathCardId = batchByCardPath.get(absCardPath);
|
|
9805
|
-
if (seenPathCardId && seenPathCardId !== card.id) {
|
|
9806
|
-
console.error(
|
|
9807
|
-
`Upsert rejected: file "${absCardPath}" appears multiple times in batch with conflicting ids ("${seenPathCardId}" vs "${card.id}")`
|
|
9808
|
-
);
|
|
9809
|
-
process.exit(1);
|
|
9810
|
-
}
|
|
9811
|
-
const seenCardPath = batchByCardId.get(card.id);
|
|
9812
|
-
if (seenCardPath && seenCardPath !== absCardPath) {
|
|
9813
|
-
console.error(
|
|
9814
|
-
`Upsert rejected: card id "${card.id}" appears multiple times in batch with conflicting files ("${seenCardPath}" vs "${absCardPath}")`
|
|
9815
|
-
);
|
|
9816
|
-
process.exit(1);
|
|
9817
|
-
}
|
|
9818
|
-
const existingById = idx.byCardId.get(card.id);
|
|
9819
|
-
const existingByPath = idx.byCardPath.get(absCardPath);
|
|
9820
|
-
if (existingByPath && existingByPath.cardId !== card.id) {
|
|
9821
|
-
console.error(
|
|
9822
|
-
`Upsert rejected: file "${absCardPath}" is already mapped to card id "${existingByPath.cardId}", cannot remap to "${card.id}"`
|
|
9823
|
-
);
|
|
9824
|
-
process.exit(1);
|
|
9825
|
-
}
|
|
9826
|
-
if (existingById && existingById.cardFilePath !== absCardPath) {
|
|
9827
|
-
console.error(
|
|
9828
|
-
`Upsert rejected: card id "${card.id}" is already mapped to file "${existingById.cardFilePath}", cannot remap to "${absCardPath}"`
|
|
9829
|
-
);
|
|
9830
|
-
process.exit(1);
|
|
9831
|
-
}
|
|
9832
|
-
batchByCardPath.set(absCardPath, card.id);
|
|
9833
|
-
batchByCardId.set(card.id, absCardPath);
|
|
9834
|
-
plans.push({
|
|
9835
|
-
card,
|
|
9836
|
-
absCardPath,
|
|
9837
|
-
isInsert: !existingById
|
|
9838
|
-
});
|
|
10451
|
+
async function processAccumulatedEventsInfinitePass(boardDir, settleDelayMs = 50, options) {
|
|
10452
|
+
if (options?.inlineLoop || shouldAvoidDetachedProcessSpawn()) {
|
|
10453
|
+
return processAccumulatedEventsInlineLoop(boardDir, settleDelayMs);
|
|
9839
10454
|
}
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9864
|
-
});
|
|
9865
|
-
}
|
|
9866
|
-
logs.push(`Card "${card.id}" ${isInsert ? "upserted (inserted)" : "upserted (updated)"}${restart ? " (restarted)" : ""}.`);
|
|
10455
|
+
return spawnDetachedProcessAccumulatedWorker(boardDir);
|
|
10456
|
+
}
|
|
10457
|
+
async function processAccumulatedEventsForced(boardDir, options) {
|
|
10458
|
+
await processAccumulatedEvents(boardDir);
|
|
10459
|
+
await processAccumulatedEventsInfinitePass(boardDir, 50, options);
|
|
10460
|
+
}
|
|
10461
|
+
function liveCardToTaskConfig(card) {
|
|
10462
|
+
const requires = card.requires;
|
|
10463
|
+
const provides = card.provides?.map((p) => p.bindTo) ?? [];
|
|
10464
|
+
return {
|
|
10465
|
+
requires: requires && requires.length > 0 ? requires : void 0,
|
|
10466
|
+
provides,
|
|
10467
|
+
taskHandlers: ["card-handler"],
|
|
10468
|
+
description: card.meta?.title ?? card.id
|
|
10469
|
+
};
|
|
10470
|
+
}
|
|
10471
|
+
var __dirname$1 = path7__namespace.dirname(url.fileURLToPath((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))));
|
|
10472
|
+
var REPO_ROOT = path7__namespace.resolve(__dirname$1, "..", "..");
|
|
10473
|
+
var LOCAL_TSX_CLI = path7__namespace.join(REPO_ROOT, "node_modules", "tsx", "dist", "cli.mjs");
|
|
10474
|
+
function getCliInvocation(command, args) {
|
|
10475
|
+
const jsPath = path7__namespace.join(__dirname$1, "board-live-cards-cli.js");
|
|
10476
|
+
if (fs7__namespace.existsSync(jsPath)) {
|
|
10477
|
+
return { cmd: process.execPath, args: [jsPath, command, ...args] };
|
|
9867
10478
|
}
|
|
9868
|
-
|
|
9869
|
-
if (
|
|
9870
|
-
|
|
9871
|
-
} else {
|
|
9872
|
-
console.log(logs[0]);
|
|
10479
|
+
const tsPath = path7__namespace.join(__dirname$1, "board-live-cards-cli.ts");
|
|
10480
|
+
if (fs7__namespace.existsSync(tsPath) && fs7__namespace.existsSync(LOCAL_TSX_CLI)) {
|
|
10481
|
+
return { cmd: process.execPath, args: [LOCAL_TSX_CLI, tsPath, command, ...args] };
|
|
9873
10482
|
}
|
|
10483
|
+
const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
10484
|
+
return { cmd: npxCmd, args: ["tsx", tsPath, command, ...args] };
|
|
9874
10485
|
}
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
10486
|
+
function appendTaskExecutorLog(boardDir, hydratedSource, mode) {
|
|
10487
|
+
try {
|
|
10488
|
+
const entry = {
|
|
10489
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10490
|
+
mode,
|
|
10491
|
+
hydratedSource
|
|
10492
|
+
};
|
|
10493
|
+
fs7__namespace.appendFileSync(path7__namespace.join(boardDir, TASK_EXECUTOR_LOG_FILE), JSON.stringify(entry) + "\n", "utf-8");
|
|
10494
|
+
} catch (logErr) {
|
|
10495
|
+
console.error(`[task-executor-log] append failed: ${logErr instanceof Error ? logErr.message : String(logErr)}`);
|
|
9882
10496
|
}
|
|
9883
|
-
await processAccumulatedEventsForced(boardDir, { inlineLoop });
|
|
9884
10497
|
}
|
|
9885
|
-
function
|
|
9886
|
-
const
|
|
9887
|
-
const
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
process.exit(1);
|
|
9893
|
-
}
|
|
9894
|
-
appendEventToJournal(dir, {
|
|
9895
|
-
type: "task-restart",
|
|
9896
|
-
taskName,
|
|
9897
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10498
|
+
function resolveCardGlobMatches(cardGlob) {
|
|
10499
|
+
const patterns = cardGlob.split(",").map((s) => s.trim()).filter(Boolean).map((p) => p.replace(/\\/g, "/"));
|
|
10500
|
+
const matches = fg__default.default.sync(patterns, {
|
|
10501
|
+
absolute: true,
|
|
10502
|
+
onlyFiles: true,
|
|
10503
|
+
unique: true,
|
|
10504
|
+
dot: false
|
|
9898
10505
|
});
|
|
9899
|
-
|
|
9900
|
-
console.log(`Task "${taskName}" retriggered.`);
|
|
10506
|
+
return [...matches].map((m) => path7__namespace.resolve(m)).sort((a, b) => a.localeCompare(b));
|
|
9901
10507
|
}
|
|
9902
10508
|
async function cli(argv) {
|
|
10509
|
+
const boardCommandHandlers = createBoardCommandHandlers({
|
|
10510
|
+
initBoard,
|
|
10511
|
+
configureRuntimeOutDir,
|
|
10512
|
+
loadBoard,
|
|
10513
|
+
writeJsonAtomic: writeJsonAtomic2,
|
|
10514
|
+
resolveStatusSnapshotPath,
|
|
10515
|
+
buildBoardStatusObject: (dir, live) => buildBoardStatusObject(path7__namespace.resolve(dir), live),
|
|
10516
|
+
readTaskExecutorConfig,
|
|
10517
|
+
resolveCardGlobMatches,
|
|
10518
|
+
validateLiveCardDefinition,
|
|
10519
|
+
execCommandSync,
|
|
10520
|
+
appendEventToJournal,
|
|
10521
|
+
processAccumulatedEventsInfinitePass
|
|
10522
|
+
});
|
|
10523
|
+
const callbackCommandHandlers = createCallbackCommandHandlers({
|
|
10524
|
+
decodeCallbackToken: decodeCallbackToken2,
|
|
10525
|
+
decodeSourceToken,
|
|
10526
|
+
writeRuntimeDataObjects,
|
|
10527
|
+
appendEventToJournal,
|
|
10528
|
+
processAccumulatedEventsForced,
|
|
10529
|
+
processAccumulatedEventsInfinitePass
|
|
10530
|
+
});
|
|
10531
|
+
const nonCoreCommandHandlers = createNonCoreCommandHandlers({
|
|
10532
|
+
readTaskExecutorConfig,
|
|
10533
|
+
execCommandSync,
|
|
10534
|
+
splitCommandLine,
|
|
10535
|
+
resolveCommandInvocation
|
|
10536
|
+
});
|
|
10537
|
+
const cardCommandHandlers = createCardCommandHandlers({
|
|
10538
|
+
resolveCardGlobMatches,
|
|
10539
|
+
buildCardInventoryIndex,
|
|
10540
|
+
appendCardInventory,
|
|
10541
|
+
liveCardToTaskConfig,
|
|
10542
|
+
appendEventToJournal,
|
|
10543
|
+
processAccumulatedEventsInfinitePass
|
|
10544
|
+
});
|
|
10545
|
+
const executionCommandHandlers = createExecutionCommandHandlers({
|
|
10546
|
+
INFERENCE_ADAPTER_FILE,
|
|
10547
|
+
readTaskExecutorConfig,
|
|
10548
|
+
execCommandSync,
|
|
10549
|
+
execCommandAsync,
|
|
10550
|
+
splitCommandLine,
|
|
10551
|
+
resolveCommandInvocation,
|
|
10552
|
+
encodeSourceToken,
|
|
10553
|
+
decodeSourceToken,
|
|
10554
|
+
decodeCallbackToken: decodeCallbackToken2,
|
|
10555
|
+
spawnDetachedCommand,
|
|
10556
|
+
getCliInvocation,
|
|
10557
|
+
appendTaskExecutorLog,
|
|
10558
|
+
appendEventToJournal,
|
|
10559
|
+
processAccumulatedEventsInfinitePass,
|
|
10560
|
+
processAccumulatedEventsForced,
|
|
10561
|
+
lookupCardPath,
|
|
10562
|
+
nextEntryAfterFetchDelivery
|
|
10563
|
+
});
|
|
9903
10564
|
const cmd = argv[0];
|
|
9904
10565
|
const rest = argv.slice(1);
|
|
9905
10566
|
switch (cmd) {
|
|
9906
10567
|
case "help":
|
|
9907
10568
|
case "--help":
|
|
9908
10569
|
case "-h":
|
|
9909
|
-
return cmdHelp();
|
|
10570
|
+
return nonCoreCommandHandlers.cmdHelp();
|
|
9910
10571
|
case "init":
|
|
9911
|
-
return cmdInit(rest);
|
|
10572
|
+
return boardCommandHandlers.cmdInit(rest);
|
|
9912
10573
|
case "status":
|
|
9913
|
-
return cmdStatus(rest);
|
|
10574
|
+
return boardCommandHandlers.cmdStatus(rest);
|
|
9914
10575
|
case "upsert-card":
|
|
9915
|
-
return cmdUpsertCard(rest);
|
|
10576
|
+
return cardCommandHandlers.cmdUpsertCard(rest);
|
|
9916
10577
|
case "validate-card":
|
|
9917
|
-
return cmdValidateCard(rest);
|
|
10578
|
+
return boardCommandHandlers.cmdValidateCard(rest);
|
|
9918
10579
|
case "remove-card":
|
|
9919
|
-
return cmdRemoveCard(rest);
|
|
10580
|
+
return boardCommandHandlers.cmdRemoveCard(rest);
|
|
9920
10581
|
case "retrigger":
|
|
9921
|
-
return cmdRetrigger(rest);
|
|
10582
|
+
return boardCommandHandlers.cmdRetrigger(rest);
|
|
9922
10583
|
case "task-completed":
|
|
9923
|
-
return cmdTaskCompleted(rest);
|
|
10584
|
+
return callbackCommandHandlers.cmdTaskCompleted(rest);
|
|
9924
10585
|
case "task-failed":
|
|
9925
|
-
return cmdTaskFailed(rest);
|
|
10586
|
+
return callbackCommandHandlers.cmdTaskFailed(rest);
|
|
9926
10587
|
case "task-progress":
|
|
9927
|
-
return cmdTaskProgress(rest);
|
|
10588
|
+
return callbackCommandHandlers.cmdTaskProgress(rest);
|
|
9928
10589
|
case "source-data-fetched":
|
|
9929
|
-
return cmdSourceDataFetched(rest);
|
|
10590
|
+
return callbackCommandHandlers.cmdSourceDataFetched(rest);
|
|
9930
10591
|
case "source-data-fetch-failure":
|
|
9931
|
-
return cmdSourceDataFetchFailure(rest);
|
|
10592
|
+
return callbackCommandHandlers.cmdSourceDataFetchFailure(rest);
|
|
9932
10593
|
case "run-sourcedefs-internal":
|
|
9933
|
-
return cmdRunSources(rest);
|
|
10594
|
+
return executionCommandHandlers.cmdRunSources(rest);
|
|
9934
10595
|
case "run-inference-internal":
|
|
9935
|
-
return cmdRunInference(rest);
|
|
10596
|
+
return executionCommandHandlers.cmdRunInference(rest);
|
|
9936
10597
|
case "inference-done":
|
|
9937
|
-
return cmdInferenceDone(rest);
|
|
10598
|
+
return executionCommandHandlers.cmdInferenceDone(rest);
|
|
9938
10599
|
case "run-source-fetch":
|
|
9939
|
-
return cmdRunSourceFetch(rest);
|
|
10600
|
+
return nonCoreCommandHandlers.cmdRunSourceFetch(rest);
|
|
9940
10601
|
case "probe-source":
|
|
9941
|
-
return await cmdProbeSource(rest);
|
|
10602
|
+
return await nonCoreCommandHandlers.cmdProbeSource(rest);
|
|
9942
10603
|
case "describe-task-executor-capabilities":
|
|
9943
|
-
return cmdDescribeTaskExecutorCapabilities(rest);
|
|
10604
|
+
return nonCoreCommandHandlers.cmdDescribeTaskExecutorCapabilities(rest);
|
|
9944
10605
|
case "process-accumulated-events":
|
|
9945
|
-
return await cmdTryDrain(rest);
|
|
10606
|
+
return await executionCommandHandlers.cmdTryDrain(rest);
|
|
9946
10607
|
default:
|
|
9947
10608
|
throw new Error(`Unknown command: ${cmd ?? "(none)"}`);
|
|
9948
10609
|
}
|
|
9949
10610
|
}
|
|
9950
|
-
|
|
9951
|
-
const cardIdx = args.indexOf("--card");
|
|
9952
|
-
const sourceIdxArg = args.indexOf("--source-idx");
|
|
9953
|
-
const sourceBindArg = args.indexOf("--source-bind");
|
|
9954
|
-
const mockProjectionsIdx = args.indexOf("--mock-projections");
|
|
9955
|
-
const rgIdx = args.indexOf("--rg");
|
|
9956
|
-
const outIdx = args.indexOf("--out");
|
|
9957
|
-
const cardFilePath = cardIdx !== -1 ? args[cardIdx + 1] : void 0;
|
|
9958
|
-
const sourceIdxVal = sourceIdxArg !== -1 ? parseInt(args[sourceIdxArg + 1], 10) : 0;
|
|
9959
|
-
const sourceBindVal = sourceBindArg !== -1 ? args[sourceBindArg + 1] : void 0;
|
|
9960
|
-
const mockProjectionsRaw = mockProjectionsIdx !== -1 ? args[mockProjectionsIdx + 1] : void 0;
|
|
9961
|
-
const boardDirArg = rgIdx !== -1 ? args[rgIdx + 1] : void 0;
|
|
9962
|
-
const outFile = outIdx !== -1 ? args[outIdx + 1] : void 0;
|
|
9963
|
-
if (!cardFilePath) {
|
|
9964
|
-
console.error("Usage: board-live-cards probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>] [--mock-projections <json>] [--rg <boardDir>] [--out <result.json>]");
|
|
9965
|
-
process.exit(1);
|
|
9966
|
-
}
|
|
9967
|
-
let card;
|
|
9968
|
-
try {
|
|
9969
|
-
card = JSON.parse(fs__namespace.readFileSync(path__namespace.resolve(cardFilePath), "utf-8"));
|
|
9970
|
-
} catch (e) {
|
|
9971
|
-
console.error(`[probe-source] Cannot read card: ${e.message}`);
|
|
9972
|
-
process.exit(1);
|
|
9973
|
-
}
|
|
9974
|
-
const source_defs = card.source_defs ?? [];
|
|
9975
|
-
if (source_defs.length === 0) {
|
|
9976
|
-
console.error(`[probe-source] Card "${card.id}" has no source_defs`);
|
|
9977
|
-
process.exit(1);
|
|
9978
|
-
}
|
|
9979
|
-
let sourceIdx;
|
|
9980
|
-
if (sourceBindVal) {
|
|
9981
|
-
sourceIdx = source_defs.findIndex((s) => s.bindTo === sourceBindVal);
|
|
9982
|
-
if (sourceIdx === -1) {
|
|
9983
|
-
console.error(`[probe-source] No source with bindTo="${sourceBindVal}" in card "${card.id}"`);
|
|
9984
|
-
process.exit(1);
|
|
9985
|
-
}
|
|
9986
|
-
} else {
|
|
9987
|
-
sourceIdx = sourceIdxVal;
|
|
9988
|
-
if (isNaN(sourceIdx) || sourceIdx < 0 || sourceIdx >= source_defs.length) {
|
|
9989
|
-
console.error(`[probe-source] --source-idx ${sourceIdxVal} out of range (card has ${source_defs.length} source(s))`);
|
|
9990
|
-
process.exit(1);
|
|
9991
|
-
}
|
|
9992
|
-
}
|
|
9993
|
-
const sourceDef = source_defs[sourceIdx];
|
|
9994
|
-
const cardDir = path__namespace.resolve(path__namespace.dirname(cardFilePath));
|
|
9995
|
-
const boardDir = boardDirArg ? path__namespace.resolve(boardDirArg) : cardDir;
|
|
9996
|
-
let mockProjections = {};
|
|
9997
|
-
if (mockProjectionsRaw) {
|
|
9998
|
-
const raw = mockProjectionsRaw.startsWith("@") ? fs__namespace.readFileSync(path__namespace.resolve(mockProjectionsRaw.slice(1)), "utf-8") : mockProjectionsRaw;
|
|
9999
|
-
try {
|
|
10000
|
-
mockProjections = JSON.parse(raw);
|
|
10001
|
-
} catch (e) {
|
|
10002
|
-
console.error(`[probe-source] --mock-projections is not valid JSON: ${e.message}`);
|
|
10003
|
-
process.exit(1);
|
|
10004
|
-
}
|
|
10005
|
-
}
|
|
10006
|
-
const teConfig = readTaskExecutorConfig(boardDir);
|
|
10007
|
-
const taskExecutor = teConfig?.command;
|
|
10008
|
-
const taskExecutorExtraB64 = teConfig?.extra ? Buffer.from(JSON.stringify(teConfig.extra)).toString("base64") : void 0;
|
|
10009
|
-
const inPayload = {
|
|
10010
|
-
...sourceDef,
|
|
10011
|
-
cwd: typeof sourceDef.cwd === "string" && sourceDef.cwd ? sourceDef.cwd : cardDir,
|
|
10012
|
-
boardDir: typeof sourceDef.boardDir === "string" && sourceDef.boardDir ? sourceDef.boardDir : boardDir,
|
|
10013
|
-
_projections: mockProjections
|
|
10014
|
-
};
|
|
10015
|
-
let sourceKind = "unknown";
|
|
10016
|
-
if (taskExecutor) {
|
|
10017
|
-
try {
|
|
10018
|
-
const capRaw = execCommandSync(taskExecutor, ["describe-capabilities"], {
|
|
10019
|
-
shell: true,
|
|
10020
|
-
timeout: 8e3,
|
|
10021
|
-
encoding: "utf-8"
|
|
10022
|
-
});
|
|
10023
|
-
const caps = JSON.parse(String(capRaw));
|
|
10024
|
-
const knownKinds = caps?.sourceKinds ? Object.keys(caps.sourceKinds) : [];
|
|
10025
|
-
const defKeys = new Set(Object.keys(sourceDef));
|
|
10026
|
-
sourceKind = knownKinds.find((k) => defKeys.has(k)) ?? "unknown";
|
|
10027
|
-
} catch {
|
|
10028
|
-
}
|
|
10029
|
-
}
|
|
10030
|
-
console.log(`[probe-source] card: ${card.id}`);
|
|
10031
|
-
console.log(`[probe-source] source[${sourceIdx}]: bindTo="${sourceDef.bindTo}" kind=${sourceKind}`);
|
|
10032
|
-
console.log(`[probe-source] _projections: ${JSON.stringify(mockProjections)}`);
|
|
10033
|
-
console.log(`[probe-source] executor: ${taskExecutor ?? "built-in (source.cli only)"}`);
|
|
10034
|
-
console.log(`[probe-source] running fetch...`);
|
|
10035
|
-
const ts = Date.now();
|
|
10036
|
-
const inFile = path__namespace.join(os__namespace.tmpdir(), `probe-in-${sourceDef.bindTo}-${ts}.json`);
|
|
10037
|
-
const tmpOut = path__namespace.join(os__namespace.tmpdir(), `probe-out-${sourceDef.bindTo}-${ts}.json`);
|
|
10038
|
-
const errFile = path__namespace.join(os__namespace.tmpdir(), `probe-err-${sourceDef.bindTo}-${ts}.txt`);
|
|
10039
|
-
fs__namespace.writeFileSync(inFile, JSON.stringify(inPayload, null, 2), "utf-8");
|
|
10040
|
-
let passed = false;
|
|
10041
|
-
let errorMsg;
|
|
10042
|
-
let resultRaw;
|
|
10043
|
-
try {
|
|
10044
|
-
if (taskExecutor) {
|
|
10045
|
-
const executorArgs = ["run-source-fetch", "--in", inFile, "--out", tmpOut, "--err", errFile];
|
|
10046
|
-
if (taskExecutorExtraB64) executorArgs.push("--extra", taskExecutorExtraB64);
|
|
10047
|
-
execCommandSync(taskExecutor, executorArgs, {
|
|
10048
|
-
shell: true,
|
|
10049
|
-
timeout: sourceDef.timeout ?? 3e4
|
|
10050
|
-
});
|
|
10051
|
-
} else {
|
|
10052
|
-
if (!inPayload.cli) {
|
|
10053
|
-
throw new Error("No task-executor registered and source has no cli field \u2014 cannot probe with built-in executor");
|
|
10054
|
-
}
|
|
10055
|
-
const cmdParts = splitCommandLine(inPayload.cli);
|
|
10056
|
-
const rawCmd = cmdParts[0];
|
|
10057
|
-
const { cmd, args: cliArgs } = resolveCommandInvocation(rawCmd, cmdParts.slice(1));
|
|
10058
|
-
const stdout = execCommandSync(cmd, cliArgs, {
|
|
10059
|
-
shell: false,
|
|
10060
|
-
encoding: "utf-8",
|
|
10061
|
-
timeout: sourceDef.timeout ?? 3e4,
|
|
10062
|
-
cwd: inPayload.cwd
|
|
10063
|
-
});
|
|
10064
|
-
fs__namespace.writeFileSync(tmpOut, stdout.trim(), "utf-8");
|
|
10065
|
-
}
|
|
10066
|
-
passed = fs__namespace.existsSync(tmpOut);
|
|
10067
|
-
if (passed) {
|
|
10068
|
-
resultRaw = fs__namespace.readFileSync(tmpOut, "utf-8");
|
|
10069
|
-
} else {
|
|
10070
|
-
errorMsg = fs__namespace.existsSync(errFile) ? fs__namespace.readFileSync(errFile, "utf-8").trim() : "executor produced no output file";
|
|
10071
|
-
}
|
|
10072
|
-
} catch (e) {
|
|
10073
|
-
errorMsg = e.message ?? String(e);
|
|
10074
|
-
if (!errorMsg && fs__namespace.existsSync(errFile)) {
|
|
10075
|
-
errorMsg = fs__namespace.readFileSync(errFile, "utf-8").trim();
|
|
10076
|
-
}
|
|
10077
|
-
}
|
|
10078
|
-
for (const f of [inFile, errFile]) {
|
|
10079
|
-
try {
|
|
10080
|
-
fs__namespace.unlinkSync(f);
|
|
10081
|
-
} catch {
|
|
10082
|
-
}
|
|
10083
|
-
}
|
|
10084
|
-
if (passed && resultRaw !== void 0) {
|
|
10085
|
-
const resultSize = resultRaw.length;
|
|
10086
|
-
const sample = resultRaw.slice(0, 300);
|
|
10087
|
-
console.log(`[probe-source] STATUS: PROBE_PASS`);
|
|
10088
|
-
console.log(`[probe-source] result size: ${resultSize} bytes`);
|
|
10089
|
-
console.log(`[probe-source] sample: ${sample}${resultSize > 300 ? "..." : ""}`);
|
|
10090
|
-
if (outFile) {
|
|
10091
|
-
fs__namespace.writeFileSync(path__namespace.resolve(outFile), resultRaw);
|
|
10092
|
-
console.log(`[probe-source] result written to: ${outFile}`);
|
|
10093
|
-
} else {
|
|
10094
|
-
try {
|
|
10095
|
-
fs__namespace.unlinkSync(tmpOut);
|
|
10096
|
-
} catch {
|
|
10097
|
-
}
|
|
10098
|
-
}
|
|
10099
|
-
} else {
|
|
10100
|
-
console.log(`[probe-source] STATUS: PROBE_FAIL`);
|
|
10101
|
-
if (errorMsg) console.log(`[probe-source] error: ${errorMsg}`);
|
|
10102
|
-
try {
|
|
10103
|
-
if (fs__namespace.existsSync(tmpOut)) fs__namespace.unlinkSync(tmpOut);
|
|
10104
|
-
} catch {
|
|
10105
|
-
}
|
|
10106
|
-
}
|
|
10107
|
-
const summary = {
|
|
10108
|
-
status: passed ? "PROBE_PASS" : "PROBE_FAIL",
|
|
10109
|
-
cardId: card.id,
|
|
10110
|
-
sourceIdx,
|
|
10111
|
-
bindTo: sourceDef.bindTo,
|
|
10112
|
-
sourceKind,
|
|
10113
|
-
mockProjectionsKeys: Object.keys(mockProjections),
|
|
10114
|
-
resultSizeBytes: resultRaw !== void 0 ? resultRaw.length : 0,
|
|
10115
|
-
error: errorMsg ?? void 0
|
|
10116
|
-
};
|
|
10117
|
-
console.log(`[probe-source:result] ${JSON.stringify(summary)}`);
|
|
10118
|
-
process.exit(passed ? 0 : 1);
|
|
10119
|
-
}
|
|
10120
|
-
function cmdDescribeTaskExecutorCapabilities(args) {
|
|
10121
|
-
const rgIdx = args.indexOf("--rg");
|
|
10122
|
-
const boardDir = rgIdx !== -1 ? path__namespace.resolve(args[rgIdx + 1]) : void 0;
|
|
10123
|
-
if (!boardDir) {
|
|
10124
|
-
console.error("Usage: board-live-cards describe-task-executor-capabilities --rg <dir>");
|
|
10125
|
-
process.exit(1);
|
|
10126
|
-
}
|
|
10127
|
-
const teConfig = readTaskExecutorConfig(boardDir);
|
|
10128
|
-
if (!teConfig) {
|
|
10129
|
-
console.error(`[describe-task-executor-capabilities] No .task-executor registered in ${boardDir}`);
|
|
10130
|
-
process.exit(1);
|
|
10131
|
-
}
|
|
10132
|
-
try {
|
|
10133
|
-
const stdout = execCommandSync(teConfig.command, ["describe-capabilities"], {
|
|
10134
|
-
shell: true,
|
|
10135
|
-
timeout: 1e4,
|
|
10136
|
-
encoding: "utf-8"
|
|
10137
|
-
});
|
|
10138
|
-
process.stdout.write(String(stdout));
|
|
10139
|
-
if (!String(stdout).endsWith("\n")) process.stdout.write("\n");
|
|
10140
|
-
} catch (e) {
|
|
10141
|
-
console.error(`[describe-task-executor-capabilities] Executor failed: ${e.message ?? e}`);
|
|
10142
|
-
process.exit(1);
|
|
10143
|
-
}
|
|
10144
|
-
}
|
|
10145
|
-
function cmdHelp() {
|
|
10146
|
-
console.log(`
|
|
10147
|
-
board-live-cards-cli \u2014 LiveCards board CLI
|
|
10148
|
-
|
|
10149
|
-
USAGE
|
|
10150
|
-
board-live-cards-cli <command> [options]
|
|
10151
|
-
|
|
10152
|
-
BOARD MANAGEMENT
|
|
10153
|
-
init <dir> [--task-executor <script>] [--chat-handler <script>] [--inference-adapter <script>] [--runtime-out <dir>]
|
|
10154
|
-
Create a new board in <dir>.
|
|
10155
|
-
If --task-executor is given, writes <dir>/.task-executor with the script path.
|
|
10156
|
-
If --chat-handler is given, writes <dir>/.chat-handler with the script path.
|
|
10157
|
-
If --inference-adapter is given, writes <dir>/.inference-adapter with the script path.
|
|
10158
|
-
Writes <dir>/.runtime-out (default: <dir>/runtime-out).
|
|
10159
|
-
Published runtime files:
|
|
10160
|
-
<runtime-out>/board-livegraph-status.json
|
|
10161
|
-
<runtime-out>/cards/<card-id>.computed.json
|
|
10162
|
-
Re-running init on an existing board is safe; handler registrations are updated.
|
|
10163
|
-
|
|
10164
|
-
status --rg <dir> [--json]
|
|
10165
|
-
Read and print the published status snapshot from <runtime-out>/board-livegraph-status.json.
|
|
10166
|
-
--json emits the stable machine-readable status object.
|
|
10167
|
-
|
|
10168
|
-
CARD MANAGEMENT
|
|
10169
|
-
upsert-card --rg <dir> (--card <card.json> | --card-glob <glob>) [--card-id <card-id>] [--restart]
|
|
10170
|
-
Insert or update one or many cards.
|
|
10171
|
-
Enforces strict one-to-one mapping between card id and file path:
|
|
10172
|
-
- same id + same file path: update
|
|
10173
|
-
- new id + new file path: insert
|
|
10174
|
-
- id remap or file remap: rejected
|
|
10175
|
-
If --card-id is provided, it must match the id inside the file.
|
|
10176
|
-
--card-id is valid only with --card (single file), not with --card-glob.
|
|
10177
|
-
--restart clears the task so it re-triggers from scratch.
|
|
10178
|
-
|
|
10179
|
-
validate-card (--card <card.json> | --card-glob <glob>) [--rg <boardDir>]
|
|
10180
|
-
Validate one or many card JSON files without adding them to a board.
|
|
10181
|
-
Checks JSON Schema structure, runtime expression syntax, and provides.ref namespaces.
|
|
10182
|
-
When --rg is provided, also invokes the board's task executor validate-source-def
|
|
10183
|
-
subcommand to structurally validate each source definition against supported kinds.
|
|
10184
|
-
Exits with code 1 if any card fails validation.
|
|
10185
|
-
|
|
10186
|
-
remove-card --rg <dir> --id <card-id>
|
|
10187
|
-
Remove a card and its task from the board.
|
|
10188
|
-
|
|
10189
|
-
retrigger --rg <dir> --task <task-name>
|
|
10190
|
-
Mark a task not-started and drain to re-trigger it.
|
|
10191
|
-
|
|
10192
|
-
TASK CALLBACKS (called by task executor scripts)
|
|
10193
|
-
task-completed --token <callbackToken> [--data <json>]
|
|
10194
|
-
Signal successful task completion with optional JSON result data.
|
|
10195
|
-
|
|
10196
|
-
task-failed --token <callbackToken> [--error <message>]
|
|
10197
|
-
Signal task failure with an optional error message.
|
|
10198
|
-
|
|
10199
|
-
task-progress --rg <dir> --token <callbackToken> [--update <json>]
|
|
10200
|
-
Signal task progress with optional update payload (for waiting on more evidence, etc.).
|
|
10201
|
-
|
|
10202
|
-
SOURCE CALLBACKS (called internally by run-sourcedefs-internal)
|
|
10203
|
-
source-data-fetched --tmp <file> --token <sourceToken>
|
|
10204
|
-
Atomically rename <file> into the outputFile destination and record delivery
|
|
10205
|
-
via journal events. Appends a task-progress event to re-invoke the card handler.
|
|
10206
|
-
|
|
10207
|
-
source-data-fetch-failure --token <sourceToken> [--reason <message>]
|
|
10208
|
-
Record a source fetch failure via journal events and append a task-progress event.
|
|
10209
|
-
|
|
10210
|
-
INTERNAL COMMANDS
|
|
10211
|
-
process-accumulated-events --rg <dir>
|
|
10212
|
-
Executes forced drain for this board.
|
|
10213
|
-
This command is also used as the background relay worker.
|
|
10214
|
-
By default it schedules a detached worker and returns quickly.
|
|
10215
|
-
Internal workers run with --inline-loop to perform the settle loop.
|
|
10216
|
-
|
|
10217
|
-
Eventual-progress guarantee is relay-based (not per-call blocking guarantee):
|
|
10218
|
-
1) at least one runner continues processing,
|
|
10219
|
-
2) no crash/forced exit in relay window,
|
|
10220
|
-
3) lock stays healthy,
|
|
10221
|
-
4) event production eventually quiesces.
|
|
10222
|
-
|
|
10223
|
-
run-sourcedefs-internal --card <card.json> --token <callbackToken> --rg <dir>
|
|
10224
|
-
Execute all source[] entries for a card, then report delivery or failure.
|
|
10225
|
-
(Internal command \u2014 invoked by the card-handler. Not intended for direct use.)
|
|
10226
|
-
|
|
10227
|
-
If <dir>/.task-executor exists, invokes it with run-source-fetch subcommand:
|
|
10228
|
-
<executor> run-source-fetch --in <source_json> --out <outfile> --err <errfile>
|
|
10229
|
-
|
|
10230
|
-
If no .task-executor is registered, uses board-live-cards built-in run-source-fetch.
|
|
10231
|
-
|
|
10232
|
-
run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]
|
|
10233
|
-
Execute a source definition. Board-live-cards reads source.cli and executes it.
|
|
10234
|
-
Writes result to --out. Presence of --out after exit indicates success.
|
|
10235
|
-
|
|
10236
|
-
describe-task-executor-capabilities --rg <dir>
|
|
10237
|
-
Invoke the registered task-executor's describe-capabilities subcommand and
|
|
10238
|
-
print its capabilities JSON to stdout. Requires a .task-executor file in <dir>.
|
|
10239
|
-
|
|
10240
|
-
probe-source --card <card.json> [--source-idx <n>] [--source-bind <name>]
|
|
10241
|
-
[--mock-projections <json>] [--rg <boardDir>] [--out <result.json>]
|
|
10242
|
-
Validate that a card source can be fetched successfully.
|
|
10243
|
-
Reads the card file, extracts the chosen source (default: index 0), builds the
|
|
10244
|
-
run-source-fetch --in payload with the supplied _projections data, invokes the
|
|
10245
|
-
registered task-executor (or built-in executor for source.cli), and reports pass/fail.
|
|
10246
|
-
--mock-projections: JSON string (or @file.json) providing pre-resolved _projections values
|
|
10247
|
-
the source needs. Craft the minimal payload that exercises the
|
|
10248
|
-
source \u2014 e.g. '{"holdings":[{"ticker":"AAPL","quantity":10}]}'.
|
|
10249
|
-
If omitted, _projections is passed as empty ({}).
|
|
10250
|
-
--source-idx: 0-based index into card.source_defs[]. Default: 0.
|
|
10251
|
-
--source-bind: Select source by its bindTo name instead of index.
|
|
10252
|
-
--rg: Board directory used to find .task-executor. Defaults to the
|
|
10253
|
-
directory containing the card file.
|
|
10254
|
-
--out: Optional path to write the raw fetch result JSON.
|
|
10255
|
-
Prints a structured report ending with a [probe-source:result] JSON line.
|
|
10256
|
-
Exits 0 on PROBE_PASS, 1 on PROBE_FAIL.
|
|
10257
|
-
|
|
10258
|
-
run-inference-internal --in <input.json> --token <inferenceToken>
|
|
10259
|
-
Execute inference via registered .inference-adapter and forward result to inference-done.
|
|
10260
|
-
inferenceToken encodes boardDir (rg), cardId (cid), callbackToken (cbk), checksum (cs).
|
|
10261
|
-
(Internal command \u2014 invoked by the card-handler when custom completion rule is used.)
|
|
10262
|
-
|
|
10263
|
-
inference-done --tmp <result.json> --token <inferenceToken>
|
|
10264
|
-
Persist llm_task_completion_inference on the card and append a task-progress event.
|
|
10265
|
-
Reads boardDir/callbackToken/checksum from decoded inferenceToken; deletes --tmp file after reading.
|
|
10266
|
-
(Internal command \u2014 invoked by run-inference-internal.)
|
|
10267
|
-
|
|
10268
|
-
RUN-SOURCE-FETCH PROTOCOL
|
|
10269
|
-
External task-executors implement:
|
|
10270
|
-
<executor> run-source-fetch --in <source.json> --out <result.json> [--err <error.txt>]
|
|
10271
|
-
|
|
10272
|
-
INPUT: --in file contains the full source_defs[x] definition object
|
|
10273
|
-
OUTPUT: --out file is written with the result to signal success.
|
|
10274
|
-
--err file may be written to explain failure.
|
|
10275
|
-
|
|
10276
|
-
Exit code and --out presence determine success:
|
|
10277
|
-
Exit 0 + --out file present \u2192 source delivery recorded, card re-evaluated.
|
|
10278
|
-
Exit non-zero OR --out absent \u2192 source-data-fetch-failure recorded.
|
|
10279
|
-
|
|
10280
|
-
BOARD-LIVE-CARDS BUILT-IN EXECUTOR
|
|
10281
|
-
Understands source.cli field only:
|
|
10282
|
-
"source_defs": [{ "cli": "node ../fetch-prices.js", "bindTo": "prices", "outputFile": "prices.json" }]
|
|
10283
|
-
|
|
10284
|
-
The source.cli command is executed with:
|
|
10285
|
-
- Direct command invocation (no shell; quote-aware argument parsing)
|
|
10286
|
-
- Stdout is captured and delivered to the card as-is
|
|
10287
|
-
- Timeout from source.timeout (default 120s)
|
|
10288
|
-
|
|
10289
|
-
The source.cli command must:
|
|
10290
|
-
- Execute successfully (exit 0)
|
|
10291
|
-
- Write output to stdout
|
|
10292
|
-
- Complete within the timeout
|
|
10293
|
-
|
|
10294
|
-
The output format is the concern of the card's compute function to interpret.
|
|
10295
|
-
|
|
10296
|
-
External task-executors can interpret source definitions however they want.
|
|
10297
|
-
|
|
10298
|
-
EXAMPLES
|
|
10299
|
-
board-live-cards-cli init ./my-board
|
|
10300
|
-
board-live-cards-cli init ./my-board --task-executor ./executors/my-runner.py
|
|
10301
|
-
board-live-cards-cli upsert-card --rg ./my-board --card cards/prices.json
|
|
10302
|
-
board-live-cards-cli status --rg ./my-board
|
|
10303
|
-
board-live-cards-cli retrigger --rg ./my-board --task price-fetch
|
|
10304
|
-
board-live-cards-cli probe-source --card cards/card-market-prices.json --source-idx 0 --rg ./my-board --mock-projections '{"holdings":[{"ticker":"AAPL","quantity":10}]}'
|
|
10305
|
-
`.trimStart());
|
|
10306
|
-
}
|
|
10307
|
-
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"));
|
|
10611
|
+
var isMain = process.argv[1] && path7__namespace.resolve(process.argv[1]) === path7__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"));
|
|
10308
10612
|
if (isMain) {
|
|
10309
10613
|
cli(process.argv.slice(2)).catch((err) => {
|
|
10310
10614
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -10318,7 +10622,6 @@ exports.appendCardInventory = appendCardInventory;
|
|
|
10318
10622
|
exports.appendEventToJournal = appendEventToJournal;
|
|
10319
10623
|
exports.buildCardInventoryIndex = buildCardInventoryIndex;
|
|
10320
10624
|
exports.cli = cli;
|
|
10321
|
-
exports.createBoardReactiveGraph = createBoardReactiveGraph;
|
|
10322
10625
|
exports.decideSourceAction = decideSourceAction;
|
|
10323
10626
|
exports.decodeSourceToken = decodeSourceToken;
|
|
10324
10627
|
exports.encodeSourceToken = encodeSourceToken;
|