yaml-flow 6.0.0 → 7.1.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.
Files changed (166) hide show
  1. package/board-live-cards-cli.js +4 -4
  2. package/browser/asset-integrity.json +3 -3
  3. package/browser/board-livecards-client.js +2 -0
  4. package/browser/board-livecards-client.js.map +1 -0
  5. package/browser/board-livecards-localstorage.js +10 -0
  6. package/browser/board-livecards-localstorage.js.map +1 -0
  7. package/browser/board-livegraph-engine.js +2 -2
  8. package/browser/board-livegraph-engine.js.map +1 -1
  9. package/browser/card-compute.js +28 -28
  10. package/browser/compute-jsonata.js +5 -0
  11. package/browser/compute-jsonata.js.map +1 -0
  12. package/browser/live-cards.js +264 -151
  13. package/card-store.js +4 -4
  14. package/dist/{board-live-cards-public-CltXYgaY.d.cts → board-live-cards-public-5n1-syA3.d.cts} +8 -5
  15. package/dist/{board-live-cards-public-f-E-FAyp.d.ts → board-live-cards-public-CK_J8uv0.d.ts} +8 -5
  16. package/dist/board-livegraph-runtime/index.cjs +2 -2
  17. package/dist/board-livegraph-runtime/index.cjs.map +1 -1
  18. package/dist/board-livegraph-runtime/index.d.cts +11 -9
  19. package/dist/board-livegraph-runtime/index.d.ts +11 -9
  20. package/dist/board-livegraph-runtime/index.js +2 -2
  21. package/dist/board-livegraph-runtime/index.js.map +1 -1
  22. package/dist/board-livegraph-runtime/jsonata-sync.cjs +37 -1
  23. package/dist/card-compute/index.cjs +4 -4
  24. package/dist/card-compute/index.cjs.map +1 -1
  25. package/dist/card-compute/index.d.cts +5 -1
  26. package/dist/card-compute/index.d.ts +5 -1
  27. package/dist/card-compute/index.js +4 -4
  28. package/dist/card-compute/index.js.map +1 -1
  29. package/dist/card-compute/jsonata-sync.cjs +37 -1
  30. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs +2 -1
  31. package/dist/cli/browser-api/board-live-cards-browser-adapter.cjs.map +1 -1
  32. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.cts +27 -14
  33. package/dist/cli/browser-api/board-live-cards-browser-adapter.d.ts +27 -14
  34. package/dist/cli/browser-api/board-live-cards-browser-adapter.js +2 -1
  35. package/dist/cli/browser-api/board-live-cards-browser-adapter.js.map +1 -1
  36. package/dist/cli/browser-api/card-store-browser-api.cjs +1 -1
  37. package/dist/cli/browser-api/card-store-browser-api.cjs.map +1 -1
  38. package/dist/cli/browser-api/card-store-browser-api.js +1 -1
  39. package/dist/cli/browser-api/card-store-browser-api.js.map +1 -1
  40. package/dist/cli/browser-api/jsonata-sync.cjs +37 -1
  41. package/dist/cli/node/artifacts-store-cli.cjs +8 -8
  42. package/dist/cli/node/artifacts-store-cli.cjs.map +1 -1
  43. package/dist/cli/node/artifacts-store-cli.js +8 -8
  44. package/dist/cli/node/artifacts-store-cli.js.map +1 -1
  45. package/dist/cli/node/board-live-cards-cli.cjs +7 -7
  46. package/dist/cli/node/board-live-cards-cli.cjs.map +1 -1
  47. package/dist/cli/node/board-live-cards-cli.js +7 -7
  48. package/dist/cli/node/board-live-cards-cli.js.map +1 -1
  49. package/dist/cli/node/card-store-cli.cjs +5 -5
  50. package/dist/cli/node/card-store-cli.cjs.map +1 -1
  51. package/dist/cli/node/card-store-cli.js +5 -5
  52. package/dist/cli/node/card-store-cli.js.map +1 -1
  53. package/dist/cli/node/execution-adapter.cjs +3 -0
  54. package/dist/cli/node/execution-adapter.cjs.map +1 -0
  55. package/dist/cli/node/execution-adapter.d.cts +174 -0
  56. package/dist/cli/node/execution-adapter.d.ts +174 -0
  57. package/dist/cli/node/execution-adapter.js +3 -0
  58. package/dist/cli/node/execution-adapter.js.map +1 -0
  59. package/dist/cli/node/fs-board-adapter.cjs +7 -7
  60. package/dist/cli/node/fs-board-adapter.cjs.map +1 -1
  61. package/dist/cli/node/fs-board-adapter.d.cts +2 -2
  62. package/dist/cli/node/fs-board-adapter.d.ts +2 -2
  63. package/dist/cli/node/fs-board-adapter.js +7 -7
  64. package/dist/cli/node/fs-board-adapter.js.map +1 -1
  65. package/dist/cli/node/jsonata-sync.cjs +37 -1
  66. package/dist/cli/node/source-cli-task-executor.cjs +4 -4
  67. package/dist/cli/node/source-cli-task-executor.cjs.map +1 -1
  68. package/dist/cli/node/source-cli-task-executor.js +4 -4
  69. package/dist/cli/node/source-cli-task-executor.js.map +1 -1
  70. package/dist/continuous-event-graph/index.cjs +2 -2
  71. package/dist/continuous-event-graph/index.cjs.map +1 -1
  72. package/dist/continuous-event-graph/index.js +2 -2
  73. package/dist/continuous-event-graph/index.js.map +1 -1
  74. package/dist/continuous-event-graph/jsonata-sync.cjs +37 -1
  75. package/dist/execution-refs.cjs +2 -1
  76. package/dist/execution-refs.cjs.map +1 -1
  77. package/dist/execution-refs.d.cts +55 -12
  78. package/dist/execution-refs.d.ts +55 -12
  79. package/dist/execution-refs.js +2 -1
  80. package/dist/execution-refs.js.map +1 -1
  81. package/dist/index.cjs +10 -10
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.js +10 -10
  84. package/dist/index.js.map +1 -1
  85. package/dist/jsonata-sync.cjs +37 -1
  86. package/dist/server-runtime/index.cjs +9 -0
  87. package/dist/server-runtime/index.cjs.map +1 -0
  88. package/dist/server-runtime/index.d.cts +31 -0
  89. package/dist/server-runtime/index.d.ts +31 -0
  90. package/dist/server-runtime/index.js +9 -0
  91. package/dist/server-runtime/index.js.map +1 -0
  92. package/dist/server-runtime/jsonata-sync.cjs +7623 -0
  93. package/dist/step-machine-public/index.cjs +3 -0
  94. package/dist/step-machine-public/index.cjs.map +1 -0
  95. package/dist/step-machine-public/index.d.cts +166 -0
  96. package/dist/step-machine-public/index.d.ts +166 -0
  97. package/dist/step-machine-public/index.js +3 -0
  98. package/dist/step-machine-public/index.js.map +1 -0
  99. package/dist/step-machine-public/jsonata-sync.cjs +7623 -0
  100. package/dist/storage-refs.cjs +2 -2
  101. package/dist/storage-refs.cjs.map +1 -1
  102. package/dist/storage-refs.d.cts +6 -6
  103. package/dist/storage-refs.d.ts +6 -6
  104. package/dist/storage-refs.js +2 -2
  105. package/dist/storage-refs.js.map +1 -1
  106. package/dist/types-CU3DjTKL.d.cts +147 -0
  107. package/dist/types-HGDTWIun.d.ts +147 -0
  108. package/examples/browser/boards/portfolio-tracker/portfolio-t4.js +9 -10
  109. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.js +370 -0
  110. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-http-test.py +398 -0
  111. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-public.js +9 -10
  112. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.js +300 -0
  113. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-server.py +617 -0
  114. package/examples/browser/boards/portfolio-tracker/portfolio-tracker-sse-worker.js +48 -0
  115. package/examples/browser/boards/portfolio-tracker/portfolio-tracker.py +11 -10
  116. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +19 -4
  117. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +4 -8
  118. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +6 -10
  119. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/poll-status-cli.js +8 -16
  120. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +2 -6
  121. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +4 -8
  122. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +3 -7
  123. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +4 -8
  124. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +7 -16
  125. package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +2 -6
  126. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/_board_pycli.py +13 -3
  127. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/add-cards.py +2 -1
  128. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/init-board.py +2 -1
  129. package/examples/cli/step-machine-cli/portfolio-tracker/handlers-py/poll-status.py +2 -1
  130. package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +20 -24
  131. package/examples/cli/step-machine-cli/portfolio-tracker/run-inline-python-demo-pycli.py +0 -3
  132. package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +8 -13
  133. package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +33 -9
  134. package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +3 -1
  135. package/examples/cli/step-machine-demo/step2-double-cli.js +6 -12
  136. package/examples/cli/step-machine-demo/two-step-math.flow.yaml +66 -4
  137. package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +13 -5
  138. package/examples/example-board/agent-instructions.md +1 -1
  139. package/examples/example-board/cards/card-my-identity.json +30 -6
  140. package/examples/example-board/cards/card-portfolio-action.json +24 -6
  141. package/examples/example-board/cards/card-portfolio-intelligence.json +97 -0
  142. package/examples/example-board/cards/card-portfolio-risks.json +24 -6
  143. package/examples/example-board/cards/card-rebalance-impact.json +22 -6
  144. package/examples/example-board/cards/card-rebalance-sim.json +66 -15
  145. package/examples/example-board/cards/cardT-market-prices.json +80 -0
  146. package/examples/example-board/cards/{card-portfolio-value.json → cardT-portfolio-value.json} +38 -10
  147. package/examples/example-board/cards/cardT-portfolio.json +78 -0
  148. package/examples/example-board/demo-server-config.json +1 -1
  149. package/examples/example-board/demo-server.js +383 -69
  150. package/examples/example-board/demo-shell-localstorage.html +774 -0
  151. package/examples/example-board/demo-shell-with-server.html +18 -36
  152. package/examples/example-board/demo-shell.html +5 -4
  153. package/examples/example-board/demo-task-executor.js +213 -265
  154. package/package.json +15 -13
  155. package/step-machine-cli.js +43 -310
  156. package/board-livecards-server-runtime.js +0 -1513
  157. package/browser/board-livecards-runtime-client.js +0 -263
  158. package/dist/pycli/quickjs-board-runtime.global.js +0 -9
  159. package/dist/pycli/quickjs-board-runtime.global.js.map +0 -1
  160. package/dist/pycli/quickjs-step-machine-runtime.global.js +0 -5
  161. package/dist/pycli/quickjs-step-machine-runtime.global.js.map +0 -1
  162. package/examples/cli/step-machine-demo/two-step-math-handlers.js +0 -32
  163. package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +0 -24
  164. package/examples/example-board/cards/card-market-prices.json +0 -56
  165. package/examples/example-board/cards/card-portfolio.json +0 -44
  166. package/examples/example-board/demo-shell-browser.html +0 -675
@@ -7,6 +7,7 @@ fallbacks or defaults.
7
7
  """
8
8
 
9
9
  import copy
10
+ import base64
10
11
  import json
11
12
  import os
12
13
  import shutil
@@ -17,6 +18,11 @@ import time
17
18
  import argparse
18
19
 
19
20
 
21
+ def serialize_ref(ref: dict[str, str]) -> str:
22
+ payload = json.dumps({"kind": ref.get("kind", ""), "value": ref.get("value", "")}, separators=(",", ":")).encode("utf-8")
23
+ return "b64:" + base64.urlsafe_b64encode(payload).decode("ascii").rstrip("=")
24
+
25
+
20
26
  _CLI_PARSER = argparse.ArgumentParser()
21
27
  _MODE_GROUP = _CLI_PARSER.add_mutually_exclusive_group()
22
28
  _MODE_GROUP.add_argument('--run-pycli', action='store_true', help='Use pycli for board/card operations (default)')
@@ -46,7 +52,6 @@ PYTHON_RUNNER = sys.executable or PYTHON
46
52
 
47
53
  BOARD_PYCLI = os.path.join(_REPO_ROOT, 'pycli', 'main', 'board_live_cards_pycli.py')
48
54
  CARD_STORE_PYCLI = os.path.join(_REPO_ROOT, 'pycli', 'main', 'card_store_pycli.py')
49
- QUICKJS_BUNDLE = os.path.join(_REPO_ROOT, 'dist', 'pycli', 'quickjs-board-runtime.global.js')
50
55
 
51
56
  if RUN_PYCLI:
52
57
  if not os.path.exists(BOARD_PYCLI):
@@ -55,15 +60,11 @@ if RUN_PYCLI:
55
60
  if not os.path.exists(CARD_STORE_PYCLI):
56
61
  print(f'[ERROR] pycli entry not found: {CARD_STORE_PYCLI}', file=sys.stderr)
57
62
  sys.exit(1)
58
- if not os.path.exists(QUICKJS_BUNDLE):
59
- print(f'[ERROR] quickjs bundle not found: {QUICKJS_BUNDLE}', file=sys.stderr)
60
- print('Run from yaml-flow root: npm run build:quickjs', file=sys.stderr)
61
- sys.exit(1)
62
63
 
63
64
  if RUN_PYCLI:
64
65
  ACTIVE_BOARD_BIN = [PYTHON_RUNNER, BOARD_PYCLI]
65
66
  ACTIVE_CARD_STORE_BIN = [PYTHON_RUNNER, CARD_STORE_PYCLI]
66
- ACTIVE_BOARD_SUFFIX = ['--bundle', QUICKJS_BUNDLE]
67
+ ACTIVE_BOARD_SUFFIX = []
67
68
  else:
68
69
  ACTIVE_BOARD_BIN = [NODE, BOARD_CLI]
69
70
  ACTIVE_CARD_STORE_BIN = [NODE, CARD_STORE_CLI]
@@ -75,9 +76,9 @@ CARDSTORE_DIR = os.path.join(_TMP_BASE, 'cardstore')
75
76
  BOARDRUNTIME_DIR = os.path.join(_TMP_BASE, 'boardruntime')
76
77
  OUTPUTS_DIR = os.path.join(_TMP_BASE, 'outputs')
77
78
 
78
- CARDSTORE_REF = f'::fs-path::{CARDSTORE_DIR}'
79
- BOARDRUNTIME_REF = f'::fs-path::{BOARDRUNTIME_DIR}'
80
- OUTPUTS_REF = f'::fs-path::{OUTPUTS_DIR}'
79
+ CARDSTORE_REF = serialize_ref({'kind': 'fs-path', 'value': CARDSTORE_DIR})
80
+ BOARDRUNTIME_REF = serialize_ref({'kind': 'fs-path', 'value': BOARDRUNTIME_DIR})
81
+ OUTPUTS_REF = serialize_ref({'kind': 'fs-path', 'value': OUTPUTS_DIR})
81
82
 
82
83
  # ── Inline card definitions ────────────────────────────────────────────────────
83
84
  CARD_PORTFOLIO_FORM = {
@@ -229,7 +230,7 @@ _task_executor_body = json.dumps({
229
230
  'task-executor-ref': {
230
231
  'meta': 'task-executor',
231
232
  'howToRun': 'local-python',
232
- 'whatToRun': f'::fs-path::{FETCH_PRICES_PY}',
233
+ 'whatToRun': serialize_ref({'kind': 'fs-path', 'value': FETCH_PRICES_PY}),
233
234
  }
234
235
  })
235
236
  run_board_with_input(
@@ -8,9 +8,23 @@ const repoRoot = path.resolve(__dirname, '..', '..', '..', '..', '..');
8
8
  const boardCliPath = path.join(repoRoot, 'board-live-cards-cli.js');
9
9
  const cardStoreCliPath = path.join(repoRoot, 'card-store.js');
10
10
 
11
+ export function toFsRef(value) {
12
+ const payload = Buffer.from(JSON.stringify({ kind: 'fs-path', value }), 'utf-8').toString('base64url');
13
+ return `b64:${payload}`;
14
+ }
15
+
16
+ function normalizeRefArg(v) {
17
+ if (typeof v !== 'string') return v;
18
+ return v;
19
+ }
20
+
21
+ function normalizeArgs(args) {
22
+ return args.map((v) => normalizeRefArg(v));
23
+ }
24
+
11
25
  export function runBoardCli(args, options = {}) {
12
26
  const { capture = false, cwd = process.cwd() } = options;
13
- const result = spawnSync(process.execPath, [boardCliPath, ...args], {
27
+ const result = spawnSync(process.execPath, [boardCliPath, ...normalizeArgs(args)], {
14
28
  cwd,
15
29
  encoding: 'utf-8',
16
30
  windowsHide: true,
@@ -37,7 +51,7 @@ export function runBoardCli(args, options = {}) {
37
51
  /** Spawn CLI with JSON piped to stdin. */
38
52
  export function runBoardCliWithInput(args, inputJson, options = {}) {
39
53
  const { cwd = process.cwd() } = options;
40
- const result = spawnSync(process.execPath, [boardCliPath, ...args], {
54
+ const result = spawnSync(process.execPath, [boardCliPath, ...normalizeArgs(args)], {
41
55
  input: inputJson,
42
56
  cwd,
43
57
  encoding: 'utf-8',
@@ -65,7 +79,7 @@ export function runBoardCliWithInput(args, inputJson, options = {}) {
65
79
  /** Spawn card-store-cli with JSON piped to stdin. */
66
80
  export function runCardStoreCliWithInput(args, inputJson, options = {}) {
67
81
  const { cwd = process.cwd() } = options;
68
- const result = spawnSync(process.execPath, [cardStoreCliPath, ...args], {
82
+ const result = spawnSync(process.execPath, [cardStoreCliPath, ...normalizeArgs(args)], {
69
83
  input: inputJson,
70
84
  cwd,
71
85
  encoding: 'utf-8',
@@ -106,5 +120,6 @@ export function writeResult(payload) {
106
120
  }
107
121
 
108
122
  export function writeFailure(message) {
109
- writeResult({ result: 'failure', error: message });
123
+ process.stderr.write(message);
124
+ process.exit(1);
110
125
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, runCardStoreCliWithInput, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, runCardStoreCliWithInput, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  try {
6
6
  const input = await readStdinJson();
@@ -9,10 +9,9 @@ try {
9
9
 
10
10
  if (!boardDir || cards.length === 0) {
11
11
  writeFailure('BOARD_DIR and CARDS (array) are required');
12
- process.exit(0);
13
12
  }
14
13
 
15
- const baseRef = `::fs-path::${boardDir}`;
14
+ const baseRef = toFsRef(boardDir);
16
15
 
17
16
  // Write all cards to the card store in one call
18
17
  runCardStoreCliWithInput(
@@ -24,11 +23,8 @@ try {
24
23
  runBoardCli(['upsert-card', '--base-ref', baseRef, '--all']);
25
24
 
26
25
  writeResult({
27
- result: 'success',
28
- data: {
29
- board_dir: boardDir,
30
- count: cards.length,
31
- },
26
+ board_dir: boardDir,
27
+ cards_added: cards.length,
32
28
  });
33
29
  } catch (error) {
34
30
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  try {
6
6
  const input = await readStdinJson();
@@ -8,21 +8,17 @@ try {
8
8
 
9
9
  if (!boardDir) {
10
10
  writeFailure('BOARD_DIR is required');
11
- process.exit(0);
12
11
  }
13
12
 
14
13
  runBoardCli([
15
14
  'init',
16
- '--base-ref', `::fs-path::${boardDir}`,
17
- '--card-store-ref', `::fs-path::${boardDir}`,
18
- '--outputs-store-ref', `::fs-path::${boardDir}`,
15
+ '--base-ref', toFsRef(boardDir),
16
+ '--card-store-ref', toFsRef(boardDir),
17
+ '--outputs-store-ref', toFsRef(boardDir),
19
18
  ]);
20
19
  writeResult({
21
- result: 'success',
22
- data: {
23
- board_dir: boardDir,
24
- message: `initialized ${boardDir}`,
25
- },
20
+ board_dir: boardDir,
21
+ message: `initialized ${boardDir}`,
26
22
  });
27
23
  } catch (error) {
28
24
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  function sleep(ms) {
6
6
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -15,13 +15,12 @@ try {
15
15
 
16
16
  if (!boardDir || expectedCardCount <= 0) {
17
17
  writeFailure('BOARD_DIR and EXPECTED_CARD_COUNT are required');
18
- process.exit(0);
19
18
  }
20
19
 
21
20
  const started = Date.now();
22
21
 
23
22
  while (Date.now() - started < timeoutMs) {
24
- const statusJson = runBoardCli(['status', '--base-ref', `::fs-path::${boardDir}`], { capture: true });
23
+ const statusJson = runBoardCli(['status', '--base-ref', toFsRef(boardDir)], { capture: true });
25
24
  let cards = [];
26
25
  try {
27
26
  cards = JSON.parse(statusJson)?.data?.cards ?? [];
@@ -31,12 +30,9 @@ try {
31
30
 
32
31
  if (cards.length >= expectedCardCount && completedCount >= expectedCardCount) {
33
32
  writeResult({
34
- result: 'success',
35
- data: {
36
- completed: true,
37
- card_count: cards.length,
38
- completed_count: completedCount,
39
- },
33
+ all_completed: true,
34
+ card_count: cards.length,
35
+ completed_count: completedCount,
40
36
  });
41
37
  process.exit(0);
42
38
  }
@@ -44,13 +40,9 @@ try {
44
40
  await sleep(pollMs);
45
41
  }
46
42
 
47
- writeResult({
48
- result: 'timeout',
49
- data: {
50
- completed: false,
51
- error: `timed out waiting for ${expectedCardCount} cards to complete`,
52
- },
53
- });
43
+ // Timeout — exit non-zero
44
+ process.stderr.write(`timed out waiting for ${expectedCardCount} cards to complete`);
45
+ process.exit(1);
54
46
  } catch (error) {
55
47
  const message = error instanceof Error ? error.message : String(error);
56
48
  writeFailure(message);
@@ -10,18 +10,14 @@ try {
10
10
 
11
11
  if (!boardDirInput) {
12
12
  writeFailure('BOARD_DIR is required');
13
- process.exit(0);
14
13
  }
15
14
 
16
15
  const boardDir = path.resolve(boardDirInput);
17
16
  fs.rmSync(boardDir, { recursive: true, force: true });
18
17
 
19
18
  writeResult({
20
- result: 'success',
21
- data: {
22
- board_dir: boardDir,
23
- reset: true,
24
- },
19
+ board_dir: boardDir,
20
+ reset: true,
25
21
  });
26
22
  } catch (error) {
27
23
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  try {
6
6
  const input = await readStdinJson();
@@ -9,17 +9,13 @@ try {
9
9
 
10
10
  if (!boardDir || !task) {
11
11
  writeFailure('BOARD_DIR and TASK are required');
12
- process.exit(0);
13
12
  }
14
13
 
15
- runBoardCli(['retrigger', '--base-ref', `::fs-path::${boardDir}`, '--id', task]);
14
+ runBoardCli(['retrigger', '--base-ref', toFsRef(boardDir), '--id', task]);
16
15
 
17
16
  writeResult({
18
- result: 'success',
19
- data: {
20
- task,
21
- retriggered: true,
22
- },
17
+ task,
18
+ retriggered: true,
23
19
  });
24
20
  } catch (error) {
25
21
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  try {
6
6
  const input = await readStdinJson();
@@ -8,16 +8,12 @@ try {
8
8
 
9
9
  if (!boardDir) {
10
10
  writeFailure('BOARD_DIR is required');
11
- process.exit(0);
12
11
  }
13
12
 
14
- const status = runBoardCli(['status', '--base-ref', `::fs-path::${boardDir}`], { capture: true });
13
+ const status = runBoardCli(['status', '--base-ref', toFsRef(boardDir)], { capture: true });
15
14
 
16
15
  writeResult({
17
- result: 'success',
18
- data: {
19
- status,
20
- },
16
+ status,
21
17
  });
22
18
  } catch (error) {
23
19
  const message = error instanceof Error ? error.message : String(error);
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as fs from 'node:fs';
4
4
  import * as path from 'node:path';
5
- import { readStdinJson, runBoardCli, runCardStoreCliWithInput, writeFailure, writeResult } from './_board-cli.js';
5
+ import { readStdinJson, runBoardCli, runCardStoreCliWithInput, toFsRef, writeFailure, writeResult } from './_board-cli.js';
6
6
 
7
7
  try {
8
8
  const input = await readStdinJson();
@@ -12,7 +12,6 @@ try {
12
12
 
13
13
  if (!boardDir || !cardsDir || !Array.isArray(holdings)) {
14
14
  writeFailure('BOARD_DIR, CARDS_DIR and HOLDINGS array are required');
15
- process.exit(0);
16
15
  }
17
16
 
18
17
  const cardPath = path.join(cardsDir, 'portfolio-form.json');
@@ -22,7 +21,7 @@ try {
22
21
  card.card_data.holdings = holdings;
23
22
  fs.writeFileSync(cardPath, `${JSON.stringify(card, null, 2)}\n`, 'utf-8');
24
23
 
25
- const baseRef = `::fs-path::${boardDir}`;
24
+ const baseRef = toFsRef(boardDir);
26
25
  runCardStoreCliWithInput(
27
26
  ['set', '--store-ref', baseRef],
28
27
  JSON.stringify(card),
@@ -30,11 +29,8 @@ try {
30
29
  runBoardCli(['upsert-card', '--base-ref', baseRef, '--card-id', card.id, '--restart']);
31
30
 
32
31
  writeResult({
33
- result: 'success',
34
- data: {
35
- saved: true,
36
- holdings_count: holdings.length,
37
- },
32
+ saved: true,
33
+ holdings_count: holdings.length,
38
34
  });
39
35
  } catch (error) {
40
36
  const message = error instanceof Error ? error.message : String(error);
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readStdinJson, runBoardCli, writeFailure, writeResult } from './_board-cli.js';
3
+ import { readStdinJson, runBoardCli, toFsRef, writeFailure, writeResult } from './_board-cli.js';
4
4
 
5
5
  function sleep(ms) {
6
6
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -16,13 +16,12 @@ try {
16
16
 
17
17
  if (!boardDir || tasks.length === 0) {
18
18
  writeFailure('BOARD_DIR and COMPLETION_TASKS are required');
19
- process.exit(0);
20
19
  }
21
20
 
22
21
  const started = Date.now();
23
22
 
24
23
  while (Date.now() - started < timeoutMs) {
25
- const statusJson = runBoardCli(['status', '--base-ref', `::fs-path::${boardDir}`], { capture: true });
24
+ const statusJson = runBoardCli(['status', '--base-ref', toFsRef(boardDir)], { capture: true });
26
25
  let cards = [];
27
26
  try {
28
27
  cards = JSON.parse(statusJson)?.data?.cards ?? [];
@@ -31,11 +30,8 @@ try {
31
30
 
32
31
  if (complete) {
33
32
  writeResult({
34
- result: 'success',
35
- data: {
36
- label,
37
- completed: true,
38
- },
33
+ label,
34
+ completed: true,
39
35
  });
40
36
  process.exit(0);
41
37
  }
@@ -43,14 +39,9 @@ try {
43
39
  await sleep(pollMs);
44
40
  }
45
41
 
46
- writeResult({
47
- result: 'timeout',
48
- data: {
49
- label,
50
- completed: false,
51
- error: `${label}: timed out waiting for completion`,
52
- },
53
- });
42
+ // Timeout — exit non-zero
43
+ process.stderr.write(`${label}: timed out waiting for completion`);
44
+ process.exit(1);
54
45
  } catch (error) {
55
46
  const message = error instanceof Error ? error.message : String(error);
56
47
  writeFailure(message);
@@ -12,7 +12,6 @@ try {
12
12
 
13
13
  if (!boardDirInput || !prices || typeof prices !== 'object' || Array.isArray(prices)) {
14
14
  writeFailure('BOARD_DIR and PRICES object are required');
15
- process.exit(0);
16
15
  }
17
16
 
18
17
  const boardDir = path.resolve(boardDirInput);
@@ -23,11 +22,8 @@ try {
23
22
  fs.writeFileSync(tmpFile, payload, 'utf-8');
24
23
 
25
24
  writeResult({
26
- result: 'success',
27
- data: {
28
- wrote: true,
29
- tmp_file: tmpFile,
30
- },
25
+ wrote: true,
26
+ tmp_file: tmpFile,
31
27
  });
32
28
  } catch (error) {
33
29
  const message = error instanceof Error ? error.message : String(error);
@@ -6,6 +6,7 @@ Result schema: {"result": "success" | "failure" | "timeout", "data"?: {...}, "er
6
6
  from __future__ import annotations
7
7
 
8
8
  import json
9
+ import base64
9
10
  import os
10
11
  import subprocess
11
12
  import sys
@@ -50,8 +51,17 @@ def _hidden_kwargs() -> dict[str, Any]:
50
51
  return kwargs
51
52
 
52
53
 
54
+ def to_fs_ref(value: str) -> str:
55
+ payload = json.dumps({"kind": "fs-path", "value": value}, separators=(",", ":")).encode("utf-8")
56
+ return "b64:" + base64.urlsafe_b64encode(payload).decode("ascii").rstrip("=")
57
+
58
+
59
+ def _normalize_args(args: list[str]) -> list[str]:
60
+ return list(args)
61
+
62
+
53
63
  def run_board_pycli(args: list[str], *, capture: bool = False) -> str:
54
- cmd = [sys.executable, str(BOARD_PYCLI), *args, "--bundle", str(QUICKJS_BUNDLE)]
64
+ cmd = [sys.executable, str(BOARD_PYCLI), *_normalize_args(args), "--bundle", str(QUICKJS_BUNDLE)]
55
65
  proc = subprocess.run(
56
66
  cmd,
57
67
  capture_output=True,
@@ -66,7 +76,7 @@ def run_board_pycli(args: list[str], *, capture: bool = False) -> str:
66
76
 
67
77
 
68
78
  def run_board_pycli_with_input(args: list[str], input_json: str) -> str:
69
- cmd = [sys.executable, str(BOARD_PYCLI), *args, "--bundle", str(QUICKJS_BUNDLE)]
79
+ cmd = [sys.executable, str(BOARD_PYCLI), *_normalize_args(args), "--bundle", str(QUICKJS_BUNDLE)]
70
80
  proc = subprocess.run(
71
81
  cmd,
72
82
  input=input_json,
@@ -82,7 +92,7 @@ def run_board_pycli_with_input(args: list[str], input_json: str) -> str:
82
92
 
83
93
 
84
94
  def run_card_store_pycli_with_input(args: list[str], input_json: str) -> str:
85
- cmd = [sys.executable, str(CARD_STORE_PYCLI), *args]
95
+ cmd = [sys.executable, str(CARD_STORE_PYCLI), *_normalize_args(args)]
86
96
  proc = subprocess.run(
87
97
  cmd,
88
98
  input=input_json,
@@ -11,6 +11,7 @@ from _board_pycli import ( # noqa: E402
11
11
  read_stdin_json,
12
12
  run_board_pycli,
13
13
  run_card_store_pycli_with_input,
14
+ to_fs_ref,
14
15
  write_failure,
15
16
  write_result,
16
17
  )
@@ -28,7 +29,7 @@ def main() -> int:
28
29
  write_failure("BOARD_DIR and CARDS (array) are required")
29
30
  return 0
30
31
 
31
- base_ref = f"::fs-path::{board_dir}"
32
+ base_ref = to_fs_ref(board_dir)
32
33
 
33
34
  run_card_store_pycli_with_input(
34
35
  ["set", "--store-ref", base_ref],
@@ -9,6 +9,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
9
9
  from _board_pycli import ( # noqa: E402
10
10
  read_stdin_json,
11
11
  run_board_pycli,
12
+ to_fs_ref,
12
13
  write_failure,
13
14
  write_result,
14
15
  )
@@ -22,7 +23,7 @@ def main() -> int:
22
23
  write_failure("BOARD_DIR is required")
23
24
  return 0
24
25
 
25
- base_ref = f"::fs-path::{board_dir}"
26
+ base_ref = to_fs_ref(board_dir)
26
27
  run_board_pycli([
27
28
  "init",
28
29
  "--base-ref", base_ref,
@@ -11,6 +11,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
11
11
  from _board_pycli import ( # noqa: E402
12
12
  read_stdin_json,
13
13
  run_board_pycli,
14
+ to_fs_ref,
14
15
  write_failure,
15
16
  write_result,
16
17
  )
@@ -28,7 +29,7 @@ def main() -> int:
28
29
  write_failure("BOARD_DIR and EXPECTED_CARD_COUNT are required")
29
30
  return 0
30
31
 
31
- base_ref = f"::fs-path::{board_dir}"
32
+ base_ref = to_fs_ref(board_dir)
32
33
  deadline = time.monotonic() + (timeout_ms / 1000)
33
34
 
34
35
  while time.monotonic() < deadline:
@@ -10,11 +10,11 @@ steps:
10
10
  expects_data: [runtime_root, board_name]
11
11
  produces_data: [board_dir]
12
12
  handler:
13
- cli: node ./handlers/reset-board-dir-cli.js
14
- input-transforms:
15
- BOARD_DIR: runtime_root & "/" & board_name
16
- output-transforms:
17
- board_dir: data.board_dir
13
+ type: ref
14
+ howToRun: local-node
15
+ whatToRun: {kind: fs-path, value: ./handlers/reset-board-dir-cli.js}
16
+ argsMassaging:
17
+ bodyTemplate: "{ 'BOARD_DIR': runtime_root & '/' & board_name }"
18
18
  transitions:
19
19
  success: t0_init_board
20
20
  failure_transitions:
@@ -25,11 +25,11 @@ steps:
25
25
  expects_data: [board_dir]
26
26
  produces_data: [board_dir]
27
27
  handler:
28
- cli: node ./handlers/init-board-cli.js
29
- input-transforms:
30
- BOARD_DIR: board_dir
31
- output-transforms:
32
- board_dir: data.board_dir
28
+ type: ref
29
+ howToRun: local-node
30
+ whatToRun: {kind: fs-path, value: ./handlers/init-board-cli.js}
31
+ argsMassaging:
32
+ bodyTemplate: "{ 'BOARD_DIR': board_dir }"
33
33
  transitions:
34
34
  success: t1_add_cards
35
35
  failure_transitions:
@@ -40,12 +40,11 @@ steps:
40
40
  expects_data: [board_dir, cards]
41
41
  produces_data: [cards_added]
42
42
  handler:
43
- cli: node ./handlers/add-cards-cli.js
44
- input-transforms:
45
- BOARD_DIR: board_dir
46
- CARDS: cards
47
- output-transforms:
48
- cards_added: data.count
43
+ type: ref
44
+ howToRun: local-node
45
+ whatToRun: {kind: fs-path, value: ./handlers/add-cards-cli.js}
46
+ argsMassaging:
47
+ bodyTemplate: "{ 'BOARD_DIR': board_dir, 'CARDS': cards }"
49
48
  transitions:
50
49
  success: t2_poll_status
51
50
  failure_transitions:
@@ -56,14 +55,11 @@ steps:
56
55
  expects_data: [board_dir, expected_card_count]
57
56
  produces_data: [all_completed]
58
57
  handler:
59
- cli: node ./handlers/poll-status-cli.js
60
- input-transforms:
61
- BOARD_DIR: board_dir
62
- EXPECTED_CARD_COUNT: expected_card_count
63
- TIMEOUT_MS: "30000"
64
- POLL_MS: "500"
65
- output-transforms:
66
- all_completed: data.completed
58
+ type: ref
59
+ howToRun: local-node
60
+ whatToRun: {kind: fs-path, value: ./handlers/poll-status-cli.js}
61
+ argsMassaging:
62
+ bodyTemplate: "{ 'BOARD_DIR': board_dir, 'EXPECTED_CARD_COUNT': expected_card_count, 'TIMEOUT_MS': '30000', 'POLL_MS': '500' }"
67
63
  transitions:
68
64
  success: success_state
69
65
  failure_transitions:
@@ -13,7 +13,6 @@ def main() -> int:
13
13
 
14
14
  pycli = repo_root / "pycli" / "main" / "step_machine_pycli.py"
15
15
  flow = here / "inline-python-demo.flow.yaml"
16
- handlers = here / "inline-python-handlers.py"
17
16
 
18
17
  initial_data = {
19
18
  "name": "Ada",
@@ -25,8 +24,6 @@ def main() -> int:
25
24
  sys.executable,
26
25
  str(pycli),
27
26
  str(flow),
28
- "--handlers",
29
- str(handlers),
30
27
  "--initial-data",
31
28
  json.dumps(initial_data, ensure_ascii=True),
32
29
  "--store",
@@ -9,27 +9,22 @@ process.stdin.on('data', (chunk) => {
9
9
  process.stdin.on('end', () => {
10
10
  try {
11
11
  const input = JSON.parse(raw || '{}');
12
- const boardDirFromArg = process.argv[2] ?? '';
13
- const boardDirFromInput = input.BOARD_DIR ?? '';
12
+ const boardDir = input.BOARD_DIR ?? '';
14
13
 
15
- if (!boardDirFromArg || !boardDirFromInput || boardDirFromArg !== boardDirFromInput) {
16
- process.stdout.write(JSON.stringify({
17
- result: 'failure',
18
- error: 'BOARD_DIR missing or mismatch between cli arg and stdin payload',
19
- }));
14
+ if (!boardDir) {
15
+ process.stderr.write('BOARD_DIR missing from input');
16
+ process.exit(1);
20
17
  return;
21
18
  }
22
19
 
23
20
  process.stdout.write(JSON.stringify({
24
- result: 'success',
25
- data: {
26
- message: `initialized ${boardDirFromArg}`,
27
- ignored: 'will be filtered by produces_data',
28
- },
21
+ message: `initialized ${boardDir}`,
22
+ ignored: 'will be filtered by produces_data',
29
23
  }));
30
24
  } catch (error) {
31
25
  const message = error instanceof Error ? error.message : String(error);
32
- process.stdout.write(JSON.stringify({ result: 'failure', error: message }));
26
+ process.stderr.write(message);
27
+ process.exit(1);
33
28
  }
34
29
  });
35
30