yaml-flow 3.1.0 → 4.0.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/README.md +81 -20
- package/board-live-cards-cli.js +37 -0
- package/browser/card-compute.js +132 -431
- package/browser/live-cards.js +41 -27
- package/browser/live-cards.schema.json +59 -77
- package/dist/card-compute/index.cjs +135 -415
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +52 -49
- package/dist/card-compute/index.d.ts +52 -49
- package/dist/card-compute/index.js +134 -415
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +2379 -0
- package/dist/cli/board-live-cards-cli.cjs.map +1 -0
- package/dist/cli/board-live-cards-cli.d.cts +213 -0
- package/dist/cli/board-live-cards-cli.d.ts +213 -0
- package/dist/cli/board-live-cards-cli.js +2332 -0
- package/dist/cli/board-live-cards-cli.js.map +1 -0
- package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
- package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
- package/dist/continuous-event-graph/index.cjs +201 -448
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +16 -340
- package/dist/continuous-event-graph/index.d.ts +16 -340
- package/dist/continuous-event-graph/index.js +198 -448
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +4 -4
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +5 -5
- package/dist/event-graph/index.d.ts +5 -5
- package/dist/event-graph/index.js +4 -4
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +278 -533
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +278 -533
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +138 -19
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +138 -19
- package/dist/inference/index.js.map +1 -1
- package/dist/journal-BJDjWb5Q.d.cts +343 -0
- package/dist/journal-B_2JnBMF.d.ts +343 -0
- package/dist/step-machine/index.cjs +18 -1
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.d.cts +2 -2
- package/dist/step-machine/index.d.ts +2 -2
- package/dist/step-machine/index.js +18 -1
- package/dist/step-machine/index.js.map +1 -1
- package/dist/stores/file.d.cts +1 -1
- package/dist/stores/file.d.ts +1 -1
- package/dist/stores/index.d.cts +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/localStorage.d.cts +1 -1
- package/dist/stores/localStorage.d.ts +1 -1
- package/dist/stores/memory.d.cts +1 -1
- package/dist/stores/memory.d.ts +1 -1
- package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
- package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
- package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
- package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
- package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
- package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
- package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
- package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +189 -0
- package/examples/browser/livecards-browser/index.html +688 -0
- package/examples/browser/step-machine-browser/index.html +367 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
- package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
- package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
- package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
- package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
- package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
- package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
- package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
- package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
- package/examples/index.html +792 -0
- package/examples/ingest.js +733 -0
- package/examples/npm-libs/batch/batch-step-machine.ts +121 -0
- package/examples/npm-libs/continuous-event-graph/live-cards-board.ts +215 -0
- package/examples/npm-libs/continuous-event-graph/live-portfolio-dashboard.ts +555 -0
- package/examples/npm-libs/continuous-event-graph/portfolio-tracker.ts +287 -0
- package/examples/npm-libs/continuous-event-graph/reactive-monitoring.ts +265 -0
- package/examples/npm-libs/continuous-event-graph/reactive-pipeline.ts +168 -0
- package/examples/npm-libs/continuous-event-graph/soc-incident-board.ts +287 -0
- package/examples/npm-libs/continuous-event-graph/stock-dashboard.ts +229 -0
- package/examples/npm-libs/event-graph/ci-cd-pipeline.ts +243 -0
- package/examples/npm-libs/event-graph/executor-diamond.ts +165 -0
- package/examples/npm-libs/event-graph/executor-pipeline.ts +161 -0
- package/examples/npm-libs/event-graph/research-pipeline.ts +137 -0
- package/examples/npm-libs/flows/ai-conversation.yaml +116 -0
- package/examples/npm-libs/flows/order-processing.yaml +143 -0
- package/examples/npm-libs/flows/simple-greeting.yaml +54 -0
- package/examples/npm-libs/graph-of-graphs/multi-stage-etl.ts +307 -0
- package/examples/npm-libs/graph-of-graphs/url-processing-pipeline.ts +254 -0
- package/examples/npm-libs/inference/azure-deployment.ts +149 -0
- package/examples/npm-libs/inference/copilot-cli.ts +138 -0
- package/examples/npm-libs/inference/data-pipeline.ts +145 -0
- package/examples/npm-libs/inference/pluggable-adapters.ts +254 -0
- package/examples/npm-libs/node/ai-conversation.ts +195 -0
- package/examples/npm-libs/node/simple-greeting.ts +101 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/package.json +14 -2
- package/schema/board-status.schema.json +118 -0
- package/schema/flow.schema.json +5 -0
- package/schema/live-cards.schema.json +59 -77
- package/step-machine-cli.js +674 -0
package/dist/stores/file.d.cts
CHANGED
package/dist/stores/file.d.ts
CHANGED
package/dist/stores/index.d.cts
CHANGED
package/dist/stores/index.d.ts
CHANGED
package/dist/stores/memory.d.cts
CHANGED
package/dist/stores/memory.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GraphConfig, c as ExecutionState, T as TaskConfig, e as GraphEngineStore } from './types-
|
|
1
|
+
import { G as GraphConfig, c as ExecutionState, T as TaskConfig, e as GraphEngineStore } from './types-CMFSIjpc.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Continuous Event Graph — Types
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as GraphConfig, c as ExecutionState, T as TaskConfig, e as GraphEngineStore } from './types-
|
|
1
|
+
import { G as GraphConfig, c as ExecutionState, T as TaskConfig, e as GraphEngineStore } from './types-CMFSIjpc.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Continuous Event Graph — Types
|
|
@@ -134,7 +134,7 @@ interface StuckDetection {
|
|
|
134
134
|
outputs_unresolvable: string[];
|
|
135
135
|
tasks_blocked: string[];
|
|
136
136
|
}
|
|
137
|
-
type GraphEvent = TaskStartedEvent | TaskCompletedEvent | TaskFailedEvent | TaskProgressEvent | TaskRestartEvent | InjectTokensEvent | AgentActionEvent |
|
|
137
|
+
type GraphEvent = TaskStartedEvent | TaskCompletedEvent | TaskFailedEvent | TaskProgressEvent | TaskRestartEvent | InjectTokensEvent | AgentActionEvent | TaskUpsertEvent | TaskRemovalEvent | NodeRequiresAddEvent | NodeRequiresRemoveEvent | NodeProvidesAddEvent | NodeProvidesRemoveEvent;
|
|
138
138
|
interface TaskStartedEvent {
|
|
139
139
|
type: 'task-started';
|
|
140
140
|
taskName: string;
|
|
@@ -165,6 +165,12 @@ interface TaskProgressEvent {
|
|
|
165
165
|
taskName: string;
|
|
166
166
|
message?: string;
|
|
167
167
|
progress?: number;
|
|
168
|
+
/**
|
|
169
|
+
* Arbitrary update payload — used by source delivery to carry
|
|
170
|
+
* { bindTo, fetchedAt, dest } or { bindTo, failure, reason }.
|
|
171
|
+
* card-handler receives this via TaskHandlerInput.update.
|
|
172
|
+
*/
|
|
173
|
+
update?: Record<string, unknown>;
|
|
168
174
|
timestamp: string;
|
|
169
175
|
executionId?: string;
|
|
170
176
|
}
|
|
@@ -185,12 +191,41 @@ interface AgentActionEvent {
|
|
|
185
191
|
timestamp: string;
|
|
186
192
|
config?: Partial<ExecutionConfig>;
|
|
187
193
|
}
|
|
188
|
-
interface
|
|
189
|
-
type: 'task-
|
|
194
|
+
interface TaskUpsertEvent {
|
|
195
|
+
type: 'task-upsert';
|
|
190
196
|
taskName: string;
|
|
191
197
|
taskConfig: TaskConfig;
|
|
192
198
|
timestamp: string;
|
|
193
199
|
}
|
|
200
|
+
interface TaskRemovalEvent {
|
|
201
|
+
type: 'task-removal';
|
|
202
|
+
taskName: string;
|
|
203
|
+
timestamp: string;
|
|
204
|
+
}
|
|
205
|
+
interface NodeRequiresAddEvent {
|
|
206
|
+
type: 'node-requires-add';
|
|
207
|
+
nodeName: string;
|
|
208
|
+
tokens: string[];
|
|
209
|
+
timestamp: string;
|
|
210
|
+
}
|
|
211
|
+
interface NodeRequiresRemoveEvent {
|
|
212
|
+
type: 'node-requires-remove';
|
|
213
|
+
nodeName: string;
|
|
214
|
+
tokens: string[];
|
|
215
|
+
timestamp: string;
|
|
216
|
+
}
|
|
217
|
+
interface NodeProvidesAddEvent {
|
|
218
|
+
type: 'node-provides-add';
|
|
219
|
+
nodeName: string;
|
|
220
|
+
tokens: string[];
|
|
221
|
+
timestamp: string;
|
|
222
|
+
}
|
|
223
|
+
interface NodeProvidesRemoveEvent {
|
|
224
|
+
type: 'node-provides-remove';
|
|
225
|
+
nodeName: string;
|
|
226
|
+
tokens: string[];
|
|
227
|
+
timestamp: string;
|
|
228
|
+
}
|
|
194
229
|
interface SchedulerResult {
|
|
195
230
|
/** Tasks eligible for execution */
|
|
196
231
|
eligibleTasks: string[];
|
|
@@ -214,4 +249,4 @@ type ExecutionMode = 'dependency-mode' | 'eligibility-mode';
|
|
|
214
249
|
type ConflictStrategy = 'alphabetical' | 'priority-first' | 'duration-first' | 'cost-optimized' | 'resource-aware' | 'random-select' | 'user-choice' | 'parallel-all' | 'skip-conflicts' | 'round-robin';
|
|
215
250
|
type RefreshStrategy = 'data-changed' | 'epoch-changed' | 'time-based' | 'manual' | 'once';
|
|
216
251
|
|
|
217
|
-
export type { AgentActionEvent as A, CompletionStrategy as C, ExecutionConfig as E, GraphConfig as G, InjectTokensEvent as I, RefreshStrategy as R, SchedulerResult as S, TaskConfig as T, ConflictStrategy as a, ExecutionMode as b, ExecutionState as c, ExecutionStatus as d, GraphEngineStore as e, GraphEvent as f, GraphSettings as g, StuckDetection as h, TaskCompletedEvent as i,
|
|
252
|
+
export type { AgentActionEvent as A, CompletionStrategy as C, ExecutionConfig as E, GraphConfig as G, InjectTokensEvent as I, RefreshStrategy as R, SchedulerResult as S, TaskConfig as T, ConflictStrategy as a, ExecutionMode as b, ExecutionState as c, ExecutionStatus as d, GraphEngineStore as e, GraphEvent as f, GraphSettings as g, StuckDetection as h, TaskCompletedEvent as i, TaskFailedEvent as j, TaskStartedEvent as k, TaskStatus as l, TaskCircuitBreakerConfig as m, TaskMessage as n, TaskProgressEvent as o, TaskRestartEvent as p, TaskRetryConfig as q };
|
|
@@ -134,7 +134,7 @@ interface StuckDetection {
|
|
|
134
134
|
outputs_unresolvable: string[];
|
|
135
135
|
tasks_blocked: string[];
|
|
136
136
|
}
|
|
137
|
-
type GraphEvent = TaskStartedEvent | TaskCompletedEvent | TaskFailedEvent | TaskProgressEvent | TaskRestartEvent | InjectTokensEvent | AgentActionEvent |
|
|
137
|
+
type GraphEvent = TaskStartedEvent | TaskCompletedEvent | TaskFailedEvent | TaskProgressEvent | TaskRestartEvent | InjectTokensEvent | AgentActionEvent | TaskUpsertEvent | TaskRemovalEvent | NodeRequiresAddEvent | NodeRequiresRemoveEvent | NodeProvidesAddEvent | NodeProvidesRemoveEvent;
|
|
138
138
|
interface TaskStartedEvent {
|
|
139
139
|
type: 'task-started';
|
|
140
140
|
taskName: string;
|
|
@@ -165,6 +165,12 @@ interface TaskProgressEvent {
|
|
|
165
165
|
taskName: string;
|
|
166
166
|
message?: string;
|
|
167
167
|
progress?: number;
|
|
168
|
+
/**
|
|
169
|
+
* Arbitrary update payload — used by source delivery to carry
|
|
170
|
+
* { bindTo, fetchedAt, dest } or { bindTo, failure, reason }.
|
|
171
|
+
* card-handler receives this via TaskHandlerInput.update.
|
|
172
|
+
*/
|
|
173
|
+
update?: Record<string, unknown>;
|
|
168
174
|
timestamp: string;
|
|
169
175
|
executionId?: string;
|
|
170
176
|
}
|
|
@@ -185,12 +191,41 @@ interface AgentActionEvent {
|
|
|
185
191
|
timestamp: string;
|
|
186
192
|
config?: Partial<ExecutionConfig>;
|
|
187
193
|
}
|
|
188
|
-
interface
|
|
189
|
-
type: 'task-
|
|
194
|
+
interface TaskUpsertEvent {
|
|
195
|
+
type: 'task-upsert';
|
|
190
196
|
taskName: string;
|
|
191
197
|
taskConfig: TaskConfig;
|
|
192
198
|
timestamp: string;
|
|
193
199
|
}
|
|
200
|
+
interface TaskRemovalEvent {
|
|
201
|
+
type: 'task-removal';
|
|
202
|
+
taskName: string;
|
|
203
|
+
timestamp: string;
|
|
204
|
+
}
|
|
205
|
+
interface NodeRequiresAddEvent {
|
|
206
|
+
type: 'node-requires-add';
|
|
207
|
+
nodeName: string;
|
|
208
|
+
tokens: string[];
|
|
209
|
+
timestamp: string;
|
|
210
|
+
}
|
|
211
|
+
interface NodeRequiresRemoveEvent {
|
|
212
|
+
type: 'node-requires-remove';
|
|
213
|
+
nodeName: string;
|
|
214
|
+
tokens: string[];
|
|
215
|
+
timestamp: string;
|
|
216
|
+
}
|
|
217
|
+
interface NodeProvidesAddEvent {
|
|
218
|
+
type: 'node-provides-add';
|
|
219
|
+
nodeName: string;
|
|
220
|
+
tokens: string[];
|
|
221
|
+
timestamp: string;
|
|
222
|
+
}
|
|
223
|
+
interface NodeProvidesRemoveEvent {
|
|
224
|
+
type: 'node-provides-remove';
|
|
225
|
+
nodeName: string;
|
|
226
|
+
tokens: string[];
|
|
227
|
+
timestamp: string;
|
|
228
|
+
}
|
|
194
229
|
interface SchedulerResult {
|
|
195
230
|
/** Tasks eligible for execution */
|
|
196
231
|
eligibleTasks: string[];
|
|
@@ -214,4 +249,4 @@ type ExecutionMode = 'dependency-mode' | 'eligibility-mode';
|
|
|
214
249
|
type ConflictStrategy = 'alphabetical' | 'priority-first' | 'duration-first' | 'cost-optimized' | 'resource-aware' | 'random-select' | 'user-choice' | 'parallel-all' | 'skip-conflicts' | 'round-robin';
|
|
215
250
|
type RefreshStrategy = 'data-changed' | 'epoch-changed' | 'time-based' | 'manual' | 'once';
|
|
216
251
|
|
|
217
|
-
export type { AgentActionEvent as A, CompletionStrategy as C, ExecutionConfig as E, GraphConfig as G, InjectTokensEvent as I, RefreshStrategy as R, SchedulerResult as S, TaskConfig as T, ConflictStrategy as a, ExecutionMode as b, ExecutionState as c, ExecutionStatus as d, GraphEngineStore as e, GraphEvent as f, GraphSettings as g, StuckDetection as h, TaskCompletedEvent as i,
|
|
252
|
+
export type { AgentActionEvent as A, CompletionStrategy as C, ExecutionConfig as E, GraphConfig as G, InjectTokensEvent as I, RefreshStrategy as R, SchedulerResult as S, TaskConfig as T, ConflictStrategy as a, ExecutionMode as b, ExecutionState as c, ExecutionStatus as d, GraphEngineStore as e, GraphEvent as f, GraphSettings as g, StuckDetection as h, TaskCompletedEvent as i, TaskFailedEvent as j, TaskStartedEvent as k, TaskStatus as l, TaskCircuitBreakerConfig as m, TaskMessage as n, TaskProgressEvent as o, TaskRestartEvent as p, TaskRetryConfig as q };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "holdings-table",
|
|
3
|
+
"meta": { "title": "Holdings Table" },
|
|
4
|
+
"requires": ["holdings", "prices"],
|
|
5
|
+
"provides": [{ "bindTo": "table", "src": "computed_values.table" }],
|
|
6
|
+
"state": {},
|
|
7
|
+
"compute": [
|
|
8
|
+
{
|
|
9
|
+
"bindTo": "table",
|
|
10
|
+
"expr": "{ \"rows\": $map(requires.holdings, function($h) { { \"symbol\": $h.symbol, \"qty\": $h.qty, \"price\": $lookup(requires.prices, $h.symbol), \"value\": $h.qty * $lookup(requires.prices, $h.symbol) } }) }"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"view": {
|
|
14
|
+
"elements": [
|
|
15
|
+
{
|
|
16
|
+
"kind": "table",
|
|
17
|
+
"label": "Portfolio Positions",
|
|
18
|
+
"data": { "bind": "computed_values.table.rows", "columns": ["symbol", "qty", "price", "value"] }
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "portfolio-form",
|
|
3
|
+
"meta": { "title": "Portfolio Holdings Form" },
|
|
4
|
+
"provides": [{ "bindTo": "holdings", "src": "state.holdings" }],
|
|
5
|
+
"state": {
|
|
6
|
+
"holdings": [
|
|
7
|
+
{ "symbol": "AAPL", "qty": 50 },
|
|
8
|
+
{ "symbol": "MSFT", "qty": 30 }
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"view": {
|
|
12
|
+
"elements": [
|
|
13
|
+
{ "kind": "table", "label": "Holdings", "data": { "bind": "state.holdings", "columns": ["symbol", "qty"] } }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "portfolio-value",
|
|
3
|
+
"meta": { "title": "Portfolio Total Value" },
|
|
4
|
+
"requires": ["table"],
|
|
5
|
+
"provides": [{ "bindTo": "totalValue", "src": "computed_values.totalValue" }],
|
|
6
|
+
"state": {},
|
|
7
|
+
"compute": [
|
|
8
|
+
{ "bindTo": "totalValue", "expr": "$sum(requires.table.rows.value)" }
|
|
9
|
+
],
|
|
10
|
+
"view": {
|
|
11
|
+
"elements": [
|
|
12
|
+
{ "kind": "metric", "label": "Total Portfolio Value", "data": { "bind": "computed_values.totalValue" } }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "price-fetch",
|
|
3
|
+
"meta": { "title": "Fetch Market Prices" },
|
|
4
|
+
"requires": ["holdings"],
|
|
5
|
+
"provides": [{ "bindTo": "prices", "src": "sources.prices" }],
|
|
6
|
+
"state": {},
|
|
7
|
+
"sources": [
|
|
8
|
+
{ "cli": "node ../fetch-prices.js", "bindTo": "prices", "outputFile": "prices.json" }
|
|
9
|
+
],
|
|
10
|
+
"view": {
|
|
11
|
+
"elements": [
|
|
12
|
+
{ "kind": "table", "label": "Market Prices", "data": { "bind": "sources.prices" } }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fetch-prices.js
|
|
3
|
+
* Polls for tmp_file1 payload and outputs JSON to stdout once available.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
|
|
8
|
+
const envBoardDir = (process.env.BOARD_DIR ?? '').trim();
|
|
9
|
+
const tmpFileCandidates = [
|
|
10
|
+
envBoardDir ? path.join(envBoardDir, 'tmp_file1') : '',
|
|
11
|
+
path.join(process.cwd(), 'tmp_file1'),
|
|
12
|
+
path.join(process.cwd(), 'board-runtime', 'tmp_file1'),
|
|
13
|
+
path.join(process.cwd(), '..', 'board-runtime', 'tmp_file1'),
|
|
14
|
+
].filter(Boolean);
|
|
15
|
+
|
|
16
|
+
function getReadableTmpFile() {
|
|
17
|
+
for (const tmpFile of tmpFileCandidates) {
|
|
18
|
+
if (!fs.existsSync(tmpFile)) continue;
|
|
19
|
+
const content = fs.readFileSync(tmpFile, 'utf-8').trim();
|
|
20
|
+
if (content) return { tmpFile, content };
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function waitForFile(timeoutMs = 120000) {
|
|
26
|
+
const started = Date.now();
|
|
27
|
+
const interval = setInterval(() => {
|
|
28
|
+
if (Date.now() - started > timeoutMs) {
|
|
29
|
+
clearInterval(interval);
|
|
30
|
+
console.error('Timed out waiting for tmp_file1 market prices input.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ready = getReadableTmpFile();
|
|
35
|
+
if (!ready) return;
|
|
36
|
+
|
|
37
|
+
clearInterval(interval);
|
|
38
|
+
fs.writeFileSync(ready.tmpFile, '', 'utf-8');
|
|
39
|
+
process.stdout.write(`${ready.content}\n`);
|
|
40
|
+
}, 250);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
waitForFile();
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* portfolio-tracker.js
|
|
3
|
+
*
|
|
4
|
+
* Runs the full T0-T4 lifecycle of the portfolio board demo.
|
|
5
|
+
*
|
|
6
|
+
* This is a BLACK-BOX client of board-live-cards CLI.
|
|
7
|
+
* It only calls CLI commands and does NOT inspect board internals.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node portfolio-tracker.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { spawnSync } from 'node:child_process';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
const CARDS_TEMPLATE = path.join(__dirname, 'cards');
|
|
21
|
+
const REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
|
|
22
|
+
const CLI_WRAPPER = path.join(REPO_ROOT, 'board-live-cards-cli.js');
|
|
23
|
+
const CLI_TS = path.join(REPO_ROOT, 'src', 'cli', 'board-live-cards-cli.ts');
|
|
24
|
+
const CLI_JS = path.join(REPO_ROOT, 'dist', 'cli', 'board-live-cards-cli.js');
|
|
25
|
+
const TSX_CLI = path.join(REPO_ROOT, 'node_modules', 'tsx', 'dist', 'cli.mjs');
|
|
26
|
+
|
|
27
|
+
// Keep runtime artifacts out of the repository.
|
|
28
|
+
const RUNTIME_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'portfolio-tracker-'));
|
|
29
|
+
const BOARD = path.join(RUNTIME_ROOT, 'board-runtime');
|
|
30
|
+
const CARDS = path.join(RUNTIME_ROOT, 'cards');
|
|
31
|
+
const TMP_FILE = path.join(BOARD, 'tmp_file1');
|
|
32
|
+
|
|
33
|
+
console.log(`Runtime root: ${RUNTIME_ROOT}`);
|
|
34
|
+
|
|
35
|
+
function sleep(ms) {
|
|
36
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function cliCommand() {
|
|
40
|
+
// Prefer node+tsx CLI on Windows to avoid flashing transient cmd windows.
|
|
41
|
+
if (fs.existsSync(CLI_TS) && fs.existsSync(TSX_CLI)) {
|
|
42
|
+
return { cmd: process.execPath, prefixArgs: [TSX_CLI, CLI_TS] };
|
|
43
|
+
}
|
|
44
|
+
if (fs.existsSync(CLI_WRAPPER)) {
|
|
45
|
+
return { cmd: process.execPath, prefixArgs: [CLI_WRAPPER] };
|
|
46
|
+
}
|
|
47
|
+
if (fs.existsSync(CLI_JS)) {
|
|
48
|
+
return { cmd: process.execPath, prefixArgs: [CLI_JS] };
|
|
49
|
+
}
|
|
50
|
+
return { cmd: process.execPath, prefixArgs: [CLI_WRAPPER] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function runCli(args, capture = false) {
|
|
54
|
+
const { cmd, prefixArgs } = cliCommand();
|
|
55
|
+
const result = spawnSync(cmd, [...prefixArgs, ...args], {
|
|
56
|
+
stdio: capture ? 'pipe' : 'inherit',
|
|
57
|
+
shell: false,
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
env: {
|
|
60
|
+
...process.env,
|
|
61
|
+
BOARD_LIVE_CARDS_NO_SPAWN: '1',
|
|
62
|
+
},
|
|
63
|
+
encoding: capture ? 'utf-8' : undefined,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (result.error) {
|
|
67
|
+
console.error(`[ERROR] Failed to run CLI ${args[0]}: ${result.error.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (result.status !== 0) {
|
|
72
|
+
if (capture && result.stdout) process.stdout.write(result.stdout);
|
|
73
|
+
if (capture && result.stderr) process.stderr.write(result.stderr);
|
|
74
|
+
console.error(`\n[ERROR] board-live-cards-cli ${args[0]} exited with status ${result.status}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return capture ? result.stdout : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function cli(...args) {
|
|
82
|
+
runCli(args, false);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function statusText() {
|
|
86
|
+
return runCli(['status', '--rg', BOARD], true) ?? '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function waitForAllCompleted(label, timeoutMs = 30000, pollMs = 500) {
|
|
90
|
+
const start = Date.now();
|
|
91
|
+
while (Date.now() - start < timeoutMs) {
|
|
92
|
+
const out = statusText();
|
|
93
|
+
const completed = [
|
|
94
|
+
/\bcompleted\s+portfolio-form\b/.test(out),
|
|
95
|
+
/\bcompleted\s+price-fetch\b/.test(out),
|
|
96
|
+
/\bcompleted\s+holdings-table\b/.test(out),
|
|
97
|
+
/\bcompleted\s+portfolio-value\b/.test(out),
|
|
98
|
+
].every(Boolean);
|
|
99
|
+
|
|
100
|
+
if (completed) {
|
|
101
|
+
console.log(`${label}: all cards completed.`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await sleep(pollMs);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.error(`[ERROR] ${label}: timed out waiting for all cards to complete.`);
|
|
109
|
+
console.error(statusText());
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function writePrices(prices) {
|
|
114
|
+
if (!fs.existsSync(BOARD)) {
|
|
115
|
+
fs.mkdirSync(BOARD, { recursive: true });
|
|
116
|
+
}
|
|
117
|
+
fs.writeFileSync(TMP_FILE, JSON.stringify(prices), 'utf-8');
|
|
118
|
+
console.log(`Wrote prices: ${JSON.stringify(prices)}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function setupRuntimeCards() {
|
|
122
|
+
fs.rmSync(CARDS, { recursive: true, force: true });
|
|
123
|
+
fs.mkdirSync(RUNTIME_ROOT, { recursive: true });
|
|
124
|
+
fs.cpSync(CARDS_TEMPLATE, CARDS, { recursive: true });
|
|
125
|
+
fs.copyFileSync(path.join(__dirname, 'fetch-prices.js'), path.join(RUNTIME_ROOT, 'fetch-prices.js'));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
(async () => {
|
|
129
|
+
setupRuntimeCards();
|
|
130
|
+
|
|
131
|
+
console.log('\n=== T0: Init board ===');
|
|
132
|
+
fs.rmSync(BOARD, { recursive: true, force: true });
|
|
133
|
+
|
|
134
|
+
cli('init', BOARD);
|
|
135
|
+
cli('add-cards', '--rg', BOARD, '--card-glob', path.join(CARDS, '*.json'));
|
|
136
|
+
|
|
137
|
+
console.log('\n--- T0 Status (after add-cards) ---');
|
|
138
|
+
process.stdout.write(statusText());
|
|
139
|
+
|
|
140
|
+
console.log('\n=== T1: Writing market prices ===');
|
|
141
|
+
writePrices({ AAPL: 198.50, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
|
|
142
|
+
await waitForAllCompleted('T1');
|
|
143
|
+
|
|
144
|
+
console.log('\n--- T1 Status ---');
|
|
145
|
+
process.stdout.write(statusText());
|
|
146
|
+
|
|
147
|
+
console.log('\n=== T2: Adding GOOG (100 shares) ===');
|
|
148
|
+
const portfolioFormPath = path.join(CARDS, 'portfolio-form.json');
|
|
149
|
+
const portfolioFormV2 = {
|
|
150
|
+
id: 'portfolio-form',
|
|
151
|
+
meta: { title: 'Portfolio Holdings Form' },
|
|
152
|
+
provides: [{ bindTo: 'holdings', src: 'state.holdings' }],
|
|
153
|
+
state: {
|
|
154
|
+
holdings: [
|
|
155
|
+
{ symbol: 'AAPL', qty: 50 },
|
|
156
|
+
{ symbol: 'MSFT', qty: 30 },
|
|
157
|
+
{ symbol: 'GOOG', qty: 100 },
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
view: {
|
|
161
|
+
elements: [
|
|
162
|
+
{ kind: 'table', label: 'Holdings', data: { bind: 'state.holdings', columns: ['symbol', 'qty'] } },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
fs.writeFileSync(portfolioFormPath, JSON.stringify(portfolioFormV2, null, 2));
|
|
167
|
+
|
|
168
|
+
cli('update-card', '--rg', BOARD, '--card-id', 'portfolio-form', '--restart');
|
|
169
|
+
await sleep(500);
|
|
170
|
+
writePrices({ AAPL: 198.50, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
|
|
171
|
+
await waitForAllCompleted('T2');
|
|
172
|
+
|
|
173
|
+
console.log('\n--- T2 Status ---');
|
|
174
|
+
process.stdout.write(statusText());
|
|
175
|
+
|
|
176
|
+
console.log('\n=== T3: Force price refresh — AAPL now 205.00 ===');
|
|
177
|
+
cli('retrigger', '--rg', BOARD, '--task', 'price-fetch');
|
|
178
|
+
await sleep(500);
|
|
179
|
+
writePrices({ AAPL: 205.00, MSFT: 425.30, GOOG: 178.90, AMZN: 192.40, TSLA: 168.75 });
|
|
180
|
+
await waitForAllCompleted('T3');
|
|
181
|
+
|
|
182
|
+
console.log('\n--- T3 Status ---');
|
|
183
|
+
process.stdout.write(statusText());
|
|
184
|
+
|
|
185
|
+
console.log('\n=== T4: Final board status ===');
|
|
186
|
+
process.stdout.write(statusText());
|
|
187
|
+
|
|
188
|
+
console.log('\nPortfolio tracker completed successfully');
|
|
189
|
+
})();
|