yaml-flow 8.1.1 → 8.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/asset-integrity.json +3 -3
- package/browser/board-livecards-client.js +1 -1
- package/browser/board-livecards-localstorage.js +4 -6
- package/cli/{board-live-cards-lib-tjYsPt5U.d.ts → board-live-cards-lib-Iq_XAC09.d.ts} +1 -1
- package/cli/browser-api/board-live-cards-browser-adapter.d.ts +4 -3
- package/cli/browser-api/board-live-cards-browser-adapter.js +2 -2
- package/cli/browser-api/card-store-browser-api.d.ts +1 -1
- package/cli/node/artifacts-store-cli.js +8 -8
- package/cli/node/board-live-cards-cli.js +8 -8
- package/cli/node/card-store-cli.js +4 -4
- package/cli/node/fs-board-adapter.d.ts +6 -33
- package/cli/node/fs-board-adapter.js +10 -8
- package/cli/node/step-machine-cli.js +3 -3
- package/cli/{types-D2XnLbBj.d.ts → types--rXGWbSR.d.ts} +77 -5
- package/examples/board/.board-ws/cards/store/_index.json +17 -0
- package/examples/board/.board-ws/cards/store/card-market-prices.json +80 -0
- package/examples/board/.board-ws/cards/store/card-portfolio-value.json +90 -0
- package/examples/board/.board-ws/cards/store/card-portfolio.json +78 -0
- package/examples/board/cards/cardT-market-prices.json +6 -4
- package/examples/board/cards/cardT-portfolio-value.json +10 -38
- package/examples/board/cards/cardT-portfolio.json +9 -4
- package/examples/board/demo-shell-with-server.html +3 -3
- package/examples/board/server/board-server.js +593 -0
- package/examples/board/server/board-worker/source-def-flows/mock-handler/mock-db.js +13 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/.retain/compliance.db +0 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/.retain/optimus.db +0 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/query.cjs +51 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/seed-cpm.cjs +197 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/seed-cpmV2.cjs +128 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/seed-optimus.cjs +352 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/sqlite-config.json +3 -0
- package/examples/board/server/board-worker/source-def-flows/sqlite-handler/sqlite-handler.js +84 -0
- package/examples/board/{source-def-flows/url.flow.json → server/board-worker/source-def-flows/sqlite.flow.json} +7 -7
- package/examples/board/{source-def-handlers → server/board-worker/source-def-flows/url-handler}/http-source-handler.js +29 -21
- package/examples/board/server/board-worker/source-def-flows/url.flow.json +73 -0
- package/examples/board/{source_def_flows.json → server/board-worker/source_def_flows.json} +61 -115
- package/examples/board/server/board-worker/task-executor.js +475 -0
- package/examples/board/server/chat-flow/chat-clear-processing.js +41 -0
- package/examples/board/server/chat-flow/chat-open-turn.js +144 -0
- package/examples/board/server/chat-flow/chat-write-assistant.js +44 -0
- package/examples/board/server/chat-flow/copilot-chat/assistant.js +253 -0
- package/examples/board/server/chat-flow/echo-probe/assistant.js +28 -0
- package/examples/board/server/chat-flow/flow-steps.json +167 -0
- package/examples/board/server-config.json +22 -0
- package/examples/board/test/server-http-test.js +707 -0
- package/examples/board/test/{portfolio-tracker-sse-worker.js → sse-worker.js} +9 -8
- package/examples/board-local/demo-shell-localstorage.html +3 -3
- package/lib/{artifacts-store-lib-public-DBICnGL6.d.cts → artifacts-store-lib-public-C5UL5tyG.d.cts} +3 -31
- package/lib/{artifacts-store-lib-public-BWC3YuLa.d.ts → artifacts-store-lib-public-GD4H-fFp.d.ts} +3 -31
- package/lib/artifacts-store-public.d.cts +3 -3
- package/lib/artifacts-store-public.d.ts +3 -3
- package/lib/board-live-cards-node.cjs +10 -8
- package/lib/board-live-cards-node.d.cts +9 -8
- package/lib/board-live-cards-node.d.ts +9 -8
- package/lib/board-live-cards-node.js +10 -8
- package/lib/{board-live-cards-public-BF9FP0mL.d.cts → board-live-cards-public-BLXbcBNk.d.cts} +2 -2
- package/lib/{board-live-cards-public-dJAl5IL-.d.ts → board-live-cards-public-BZaNb2mi.d.ts} +2 -2
- package/lib/board-live-cards-public.cjs +2 -2
- package/lib/board-live-cards-public.d.cts +2 -2
- package/lib/board-live-cards-public.d.ts +2 -2
- package/lib/board-live-cards-public.js +2 -2
- package/lib/board-live-cards-server-runtime.cjs +4 -6
- package/lib/board-live-cards-server-runtime.d.cts +3 -3
- package/lib/board-live-cards-server-runtime.d.ts +3 -3
- package/lib/board-live-cards-server-runtime.js +4 -6
- package/lib/board-livegraph-runtime/index.cjs +2 -2
- package/lib/board-livegraph-runtime/index.js +2 -2
- package/lib/card-store-public.d.cts +2 -2
- package/lib/card-store-public.d.ts +2 -2
- package/lib/execution-refs.cjs +1 -1
- package/lib/execution-refs.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.cts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/server-runtime/index.cjs +4 -6
- package/lib/server-runtime/index.d.cts +4 -4
- package/lib/server-runtime/index.d.ts +4 -4
- package/lib/server-runtime/index.js +4 -6
- package/lib/step-machine-public/index.cjs +3 -3
- package/lib/step-machine-public/index.d.cts +27 -10
- package/lib/step-machine-public/index.d.ts +27 -10
- package/lib/step-machine-public/index.js +3 -3
- package/lib/{storage-interface-BhAON-gW.d.ts → storage-interface-B6ecOulj.d.cts} +25 -3
- package/lib/{storage-interface-BhAON-gW.d.cts → storage-interface-B6ecOulj.d.ts} +25 -3
- package/lib/stores/index.d.cts +1 -1
- package/lib/stores/index.d.ts +1 -1
- package/lib/stores/kv.d.cts +1 -1
- package/lib/stores/kv.d.ts +1 -1
- package/lib/{types-CXBzvC0s.d.cts → types-Bztd1KoK.d.cts} +75 -3
- package/lib/{types-D48hpnTR.d.ts → types-D-xVWPdY.d.ts} +75 -3
- package/package.json +1 -1
- package/examples/board/demo-chat-handler.js +0 -169
- package/examples/board/demo-server-config.json +0 -10
- package/examples/board/demo-server.js +0 -580
- package/examples/board/demo-task-executor.js +0 -721
- package/examples/board/gandalf-cards/card-source-kinds.json +0 -36
- package/examples/board/gandalf-cards/cards/_index.json +0 -7
- package/examples/board/gandalf-cards/cards/card-source-kinds.json +0 -64
- package/examples/board/scripts/copilot_wrapper.bat +0 -157
- package/examples/board/scripts/copilot_wrapper_helper.ps1 +0 -190
- package/examples/board/scripts/workiq_wrapper.mjs +0 -66
- package/examples/board/source-def-flows/copilot.flow.json +0 -33
- package/examples/board/source-def-flows/url-list.flow.json +0 -33
- package/examples/board/source-def-flows/workiq.flow.json +0 -34
- package/examples/board/source-def-handlers/copilot-source-handler.js +0 -141
- package/examples/board/test/demo-http-test.js +0 -317
- /package/examples/board/{source-def-flows → server/board-worker/source-def-flows}/mock.flow.json +0 -0
|
@@ -1,580 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import http from 'node:http';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import os from 'node:os';
|
|
7
|
-
import net from 'node:net';
|
|
8
|
-
import { spawnSync, spawn } from 'node:child_process';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
createMultiBoardServerRuntime,
|
|
13
|
-
createSingleBoardServerRuntime,
|
|
14
|
-
} from 'yaml-flow/board-live-cards-server-runtime';
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
createFsBoardPlatformAdapter,
|
|
18
|
-
createArtifactsStore,
|
|
19
|
-
parseRef,
|
|
20
|
-
serializeRef,
|
|
21
|
-
} from 'yaml-flow/board-live-cards-node';
|
|
22
|
-
|
|
23
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
-
const __dirname = path.dirname(__filename);
|
|
25
|
-
const cliArgs = process.argv.slice(2);
|
|
26
|
-
|
|
27
|
-
function loadServerConfig() {
|
|
28
|
-
const configPath = path.join(__dirname, 'demo-server-config.json');
|
|
29
|
-
if (!fs.existsSync(configPath)) return {};
|
|
30
|
-
try {
|
|
31
|
-
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
32
|
-
const parsed = JSON.parse(raw);
|
|
33
|
-
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
34
|
-
} catch {
|
|
35
|
-
return {};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function resolveFromConfig(configValue) {
|
|
40
|
-
if (typeof configValue !== 'string' || !configValue.trim()) return null;
|
|
41
|
-
return path.resolve(__dirname, configValue);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function resolveKindRefFromConfig(configValue) {
|
|
45
|
-
if (typeof configValue !== 'string' || !configValue.trim()) return null;
|
|
46
|
-
const trimmed = configValue.trim();
|
|
47
|
-
if (!trimmed.startsWith('b64:')) return trimmed;
|
|
48
|
-
try {
|
|
49
|
-
const parsed = parseRef(trimmed);
|
|
50
|
-
if (parsed.kind !== 'fs-path') return trimmed;
|
|
51
|
-
const rawPath = parsed.value.trim();
|
|
52
|
-
if (!rawPath) return null;
|
|
53
|
-
const resolved = path.isAbsolute(rawPath) ? rawPath : path.resolve(__dirname, rawPath);
|
|
54
|
-
return serializeRef({ kind: 'fs-path', value: resolved });
|
|
55
|
-
} catch {
|
|
56
|
-
return trimmed;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const serverConfig = loadServerConfig();
|
|
61
|
-
const configuredCardsDir = resolveFromConfig(serverConfig.cardsDir);
|
|
62
|
-
const configuredTaskExecutorPath = resolveFromConfig(serverConfig.taskExecutorPath || serverConfig.demoTaskExecutorPath);
|
|
63
|
-
const configuredStepMachineCliPath = resolveFromConfig(serverConfig.stepMachineCliPath);
|
|
64
|
-
const configuredChatHandlerPath = resolveFromConfig(serverConfig.chatHandlerPath);
|
|
65
|
-
const configuredInferenceAdapterPath = resolveFromConfig(serverConfig.inferenceAdapterPath);
|
|
66
|
-
const configuredGandalfCardsDir = resolveFromConfig(serverConfig.gandalfCardsDir);
|
|
67
|
-
const configuredGandalfTaskExecutorPath = resolveFromConfig(serverConfig.gandalfTaskExecutorPath);
|
|
68
|
-
const configuredGandalfChatHandlerPath = resolveFromConfig(serverConfig.gandalfChatHandlerPath);
|
|
69
|
-
const configuredGandalfInferenceAdapterPath = resolveFromConfig(serverConfig.gandalfInferenceAdapterPath);
|
|
70
|
-
const configuredServerMetaStoreRef = resolveKindRefFromConfig(serverConfig.serverMetaStoreRef);
|
|
71
|
-
|
|
72
|
-
if (!process.env.DEMO_STEP_MACHINE_CLI_PATH && configuredStepMachineCliPath) {
|
|
73
|
-
process.env.DEMO_STEP_MACHINE_CLI_PATH = configuredStepMachineCliPath;
|
|
74
|
-
}
|
|
75
|
-
if (!process.env.DEMO_CHAT_HANDLER_PATH && configuredChatHandlerPath) {
|
|
76
|
-
process.env.DEMO_CHAT_HANDLER_PATH = configuredChatHandlerPath;
|
|
77
|
-
}
|
|
78
|
-
if (!process.env.DEMO_INFERENCE_ADAPTER_PATH && configuredInferenceAdapterPath) {
|
|
79
|
-
process.env.DEMO_INFERENCE_ADAPTER_PATH = configuredInferenceAdapterPath;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const PORT = Number(process.env.DEMO_SERVER_PORT || serverConfig.port || 7799);
|
|
83
|
-
const cardsPatternArgIndex = cliArgs.indexOf('--cards-pattern');
|
|
84
|
-
const cliCardsPattern = cardsPatternArgIndex !== -1 ? cliArgs[cardsPatternArgIndex + 1] : null;
|
|
85
|
-
const selectedCardsPattern = (process.env.DEMO_CARDS_PATTERN || cliCardsPattern || '').trim() || null;
|
|
86
|
-
|
|
87
|
-
const CORS_HEADERS = {
|
|
88
|
-
'Access-Control-Allow-Origin': '*',
|
|
89
|
-
'Access-Control-Allow-Headers': 'content-type,x-file-name',
|
|
90
|
-
'Access-Control-Allow-Methods': 'GET,POST,PATCH,OPTIONS',
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Setup directory & defaults
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
|
|
97
|
-
const setupDir = path.resolve(
|
|
98
|
-
process.env.DEMO_SETUP_DIR || path.join(__dirname, '.demo-setup'),
|
|
99
|
-
);
|
|
100
|
-
fs.mkdirSync(setupDir, { recursive: true });
|
|
101
|
-
|
|
102
|
-
const defaultCardsDir = path.resolve(
|
|
103
|
-
process.env.DEMO_CARDS_DIR || configuredCardsDir || path.join(__dirname, 'cards'),
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const defaultTaskExecutorPath = process.env.DEMO_TASK_EXECUTOR_PATH || configuredTaskExecutorPath || null;
|
|
107
|
-
const defaultChatHandlerPath = process.env.DEMO_CHAT_HANDLER_PATH || configuredChatHandlerPath || null;
|
|
108
|
-
const defaultInferenceAdapterPath = process.env.DEMO_INFERENCE_ADAPTER_PATH || configuredInferenceAdapterPath || null;
|
|
109
|
-
const defaultStepMachineCliPath = process.env.DEMO_STEP_MACHINE_CLI_PATH || configuredStepMachineCliPath || null;
|
|
110
|
-
const defaultGandalfCardsDir = process.env.DEMO_GANDALF_CARDS_DIR || configuredGandalfCardsDir || null;
|
|
111
|
-
const defaultGandalfTaskExecutorPath = process.env.DEMO_GANDALF_TASK_EXECUTOR_PATH || configuredGandalfTaskExecutorPath || null;
|
|
112
|
-
const defaultGandalfChatHandlerPath = process.env.DEMO_GANDALF_CHAT_HANDLER_PATH || configuredGandalfChatHandlerPath || null;
|
|
113
|
-
const defaultGandalfInferenceAdapterPath = process.env.DEMO_GANDALF_INFERENCE_ADAPTER_PATH || configuredGandalfInferenceAdapterPath || null;
|
|
114
|
-
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
// Host adapter factories — Node-specific implementations injected into the
|
|
117
|
-
// platform-free server runtime.
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
function wildcardToRegExp(pattern) {
|
|
121
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
122
|
-
return new RegExp(`^${escaped.replace(/\*/g, '.*').replace(/\?/g, '.')}$`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function createFsCardSource(cardsDir, cardPattern = null) {
|
|
126
|
-
const cardRegex = cardPattern ? wildcardToRegExp(cardPattern) : null;
|
|
127
|
-
return {
|
|
128
|
-
listCards() {
|
|
129
|
-
if (!fs.existsSync(cardsDir)) return [];
|
|
130
|
-
return fs.readdirSync(cardsDir)
|
|
131
|
-
.filter(f => {
|
|
132
|
-
if (!f.endsWith('.json')) return false;
|
|
133
|
-
if (!cardRegex) return true;
|
|
134
|
-
const cardId = path.basename(f, '.json');
|
|
135
|
-
return cardRegex.test(cardId);
|
|
136
|
-
})
|
|
137
|
-
.map(f => {
|
|
138
|
-
try { return JSON.parse(fs.readFileSync(path.join(cardsDir, f), 'utf-8')); }
|
|
139
|
-
catch { return null; }
|
|
140
|
-
})
|
|
141
|
-
.filter(Boolean);
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function namedPipePath(pipeName) {
|
|
147
|
-
if (process.platform === 'win32') return `\\\\.\\pipe\\${pipeName}`;
|
|
148
|
-
return path.join(os.tmpdir(), `${pipeName}.sock`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function makeExecutionRef(scriptPath, meta) {
|
|
152
|
-
if (!scriptPath) return undefined;
|
|
153
|
-
const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(process.cwd(), scriptPath);
|
|
154
|
-
return { howToRun: 'local-node', whatToRun: serializeRef({ kind: 'fs-path', value: resolved }), meta };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function createNodeSpawnInvocationAdapter() {
|
|
158
|
-
return {
|
|
159
|
-
async invoke(ref, args) {
|
|
160
|
-
if (ref.howToRun !== 'local-node') {
|
|
161
|
-
return { dispatched: false, error: `unsupported howToRun: ${ref.howToRun}` };
|
|
162
|
-
}
|
|
163
|
-
const whatToRun = ref.whatToRun;
|
|
164
|
-
let scriptPath = '';
|
|
165
|
-
if (whatToRun && typeof whatToRun === 'object') {
|
|
166
|
-
if (whatToRun.kind === 'fs-path') scriptPath = whatToRun.value;
|
|
167
|
-
} else if (typeof whatToRun === 'string' && whatToRun.startsWith('b64:')) {
|
|
168
|
-
try {
|
|
169
|
-
const parsed = parseRef(whatToRun);
|
|
170
|
-
if (parsed.kind === 'fs-path') scriptPath = parsed.value;
|
|
171
|
-
} catch {
|
|
172
|
-
scriptPath = '';
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
if (!scriptPath) {
|
|
176
|
-
return { dispatched: false, error: `no fs-path in whatToRun: ${JSON.stringify(whatToRun)}` };
|
|
177
|
-
}
|
|
178
|
-
// Resolve chatsKeyPrefix (blob key prefix) to absolute FS chatDir for handlers
|
|
179
|
-
const finalArgs = { ...args };
|
|
180
|
-
if (finalArgs.chatsKeyPrefix && finalArgs.chatsBlobBasePath) {
|
|
181
|
-
const cardPart = String(finalArgs.chatsKeyPrefix).split('/')[0];
|
|
182
|
-
finalArgs.chatDir = path.join(String(finalArgs.chatsBlobBasePath), cardPart);
|
|
183
|
-
}
|
|
184
|
-
delete finalArgs.chatsKeyPrefix;
|
|
185
|
-
delete finalArgs.chatsBlobBasePath;
|
|
186
|
-
const extra = Buffer.from(JSON.stringify(finalArgs)).toString('base64');
|
|
187
|
-
try {
|
|
188
|
-
const proc = spawn(process.execPath, [
|
|
189
|
-
scriptPath,
|
|
190
|
-
'--boardId', String(args.boardId || ''),
|
|
191
|
-
'--cardId', String(args.cardId || ''),
|
|
192
|
-
'--extraEncJson', extra,
|
|
193
|
-
], { stdio: 'ignore', windowsHide: true });
|
|
194
|
-
proc.unref();
|
|
195
|
-
return { dispatched: true };
|
|
196
|
-
} catch (err) {
|
|
197
|
-
return { dispatched: false, error: err?.message || String(err) };
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
async describe(ref) {
|
|
201
|
-
if (ref.howToRun !== 'local-node') return null;
|
|
202
|
-
const whatToRun = ref.whatToRun;
|
|
203
|
-
let scriptPath = '';
|
|
204
|
-
if (whatToRun && typeof whatToRun === 'object') {
|
|
205
|
-
if (whatToRun.kind === 'fs-path') scriptPath = whatToRun.value;
|
|
206
|
-
} else if (typeof whatToRun === 'string' && whatToRun.startsWith('b64:')) {
|
|
207
|
-
try {
|
|
208
|
-
const parsed = parseRef(whatToRun);
|
|
209
|
-
if (parsed.kind === 'fs-path') scriptPath = parsed.value;
|
|
210
|
-
} catch {
|
|
211
|
-
scriptPath = '';
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (!scriptPath) return null;
|
|
215
|
-
try {
|
|
216
|
-
const result = spawnSync(process.execPath, [scriptPath, 'describe'], {
|
|
217
|
-
timeout: 5000, encoding: 'utf-8', windowsHide: true,
|
|
218
|
-
});
|
|
219
|
-
if (result.status !== 0) return null;
|
|
220
|
-
return JSON.parse(String(result.stdout).trim());
|
|
221
|
-
} catch { return null; }
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function createNamedPipeNotificationTransport() {
|
|
227
|
-
return {
|
|
228
|
-
async subscribe(ref, onEvent) {
|
|
229
|
-
if (ref.kind !== 'named-pipe') {
|
|
230
|
-
console.warn(`[notification] unsupported transport kind: ${ref.kind}`);
|
|
231
|
-
return () => {};
|
|
232
|
-
}
|
|
233
|
-
const pipePath = ref.value;
|
|
234
|
-
if (process.platform !== 'win32' && fs.existsSync(pipePath)) {
|
|
235
|
-
try { fs.rmSync(pipePath, { force: true }); } catch { /* best-effort */ }
|
|
236
|
-
}
|
|
237
|
-
const server = net.createServer((socket) => {
|
|
238
|
-
let buf = '';
|
|
239
|
-
socket.on('data', (chunk) => {
|
|
240
|
-
buf += chunk.toString('utf-8');
|
|
241
|
-
while (true) {
|
|
242
|
-
const i = buf.indexOf('\n');
|
|
243
|
-
if (i < 0) break;
|
|
244
|
-
const line = buf.slice(0, i).trim();
|
|
245
|
-
buf = buf.slice(i + 1);
|
|
246
|
-
if (!line) continue;
|
|
247
|
-
try {
|
|
248
|
-
const msg = JSON.parse(line);
|
|
249
|
-
onEvent(msg?.notification ?? msg);
|
|
250
|
-
} catch { /* ignore malformed lines */ }
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
await new Promise((resolve, reject) => {
|
|
255
|
-
server.once('error', reject);
|
|
256
|
-
server.listen(pipePath, () => resolve());
|
|
257
|
-
});
|
|
258
|
-
return () => {
|
|
259
|
-
server.close();
|
|
260
|
-
if (process.platform !== 'win32') {
|
|
261
|
-
try { fs.rmSync(pipePath, { force: true }); } catch { /* best-effort */ }
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// ---------------------------------------------------------------------------
|
|
269
|
-
// Server meta store (multi-board registry)
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
|
-
|
|
272
|
-
const serverMetaRef = process.env.DEMO_SERVER_META_STORE_REF || configuredServerMetaStoreRef || serializeRef({ kind: 'fs-path', value: setupDir });
|
|
273
|
-
const serverMetaAdapter = createFsBoardPlatformAdapter(
|
|
274
|
-
parseRef(serverMetaRef), { suppressSpawn: true },
|
|
275
|
-
);
|
|
276
|
-
const serverMetaStore = createArtifactsStore(serverMetaAdapter.blobStorage('server-meta'));
|
|
277
|
-
|
|
278
|
-
// ---------------------------------------------------------------------------
|
|
279
|
-
// Build multi-board runtime
|
|
280
|
-
// ---------------------------------------------------------------------------
|
|
281
|
-
|
|
282
|
-
const apiBasePath = '/api/boards';
|
|
283
|
-
const invocationAdapter = createNodeSpawnInvocationAdapter();
|
|
284
|
-
const notificationTransport = createNamedPipeNotificationTransport();
|
|
285
|
-
const logger = { info: console.log, warn: console.warn, error: console.error };
|
|
286
|
-
|
|
287
|
-
// Track per-board host config for demo-setup (FS paths are host concerns, not runtime concerns)
|
|
288
|
-
const boardHostConfig = new Map();
|
|
289
|
-
|
|
290
|
-
function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerPath, infAdapterPath, boardId) {
|
|
291
|
-
fs.mkdirSync(boardDir, { recursive: true });
|
|
292
|
-
|
|
293
|
-
// Runtime card store lives inside the board's setup dir, isolated from the source cards dir.
|
|
294
|
-
// Layout: boardDir/cards/store — KV card store
|
|
295
|
-
// boardDir/cards/chats — chat blobs
|
|
296
|
-
// boardDir/cards/files — file uploads
|
|
297
|
-
const runtimeCardsDir = path.join(boardDir, 'cards');
|
|
298
|
-
const runtimeCardStoreDir = path.join(runtimeCardsDir, 'store');
|
|
299
|
-
fs.mkdirSync(runtimeCardStoreDir, { recursive: true });
|
|
300
|
-
|
|
301
|
-
const notifyChannel = `yaml-flow-server-${label}-${boardId}-${process.pid}`;
|
|
302
|
-
const baseRef = parseRef(serializeRef({ kind: 'fs-path', value: boardDir }));
|
|
303
|
-
const boardAdapter = createFsBoardPlatformAdapter(baseRef, {
|
|
304
|
-
notifyChannel,
|
|
305
|
-
});
|
|
306
|
-
// In the server context the drain loop is driven in-process; suppress the
|
|
307
|
-
// detached CLI spawn that the FS adapter would otherwise fire as a continuation.
|
|
308
|
-
boardAdapter.requestProcessAccumulated = () => {};
|
|
309
|
-
// Artifacts adapter rooted at runtimeCardsDir so chats/ and files/ are siblings of store/.
|
|
310
|
-
const artifactsRef = parseRef(serializeRef({ kind: 'fs-path', value: runtimeCardsDir }));
|
|
311
|
-
const artifactsAdapter = createFsBoardPlatformAdapter(artifactsRef, { suppressSpawn: true });
|
|
312
|
-
|
|
313
|
-
const cardStoreRef = serializeRef({ kind: 'fs-path', value: runtimeCardStoreDir });
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
label,
|
|
317
|
-
boardAdapter,
|
|
318
|
-
artifactsAdapter,
|
|
319
|
-
baseRef,
|
|
320
|
-
cardStoreRef,
|
|
321
|
-
outputsStoreRef: serializeRef({ kind: 'fs-path', value: path.join(path.dirname(boardDir), 'runtime-out', '.outputs') }),
|
|
322
|
-
notifyRef: { kind: 'named-pipe', value: namedPipePath(notifyChannel) },
|
|
323
|
-
taskExecutorRef: makeExecutionRef(taskExecPath, 'task-executor'),
|
|
324
|
-
chatHandlerRef: makeExecutionRef(chatHandlerPath, 'chat-handler'),
|
|
325
|
-
inferenceAdapterRef: makeExecutionRef(infAdapterPath, 'inference-adapter'),
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const runtime = createMultiBoardServerRuntime({
|
|
330
|
-
apiBasePath,
|
|
331
|
-
serverMetaStore,
|
|
332
|
-
logger,
|
|
333
|
-
boardRuntimeFactory: (boardId, entry) => {
|
|
334
|
-
// sourceCardsDir: read-only source used only for initial seeding.
|
|
335
|
-
const sourceCardsDir = typeof entry.cardsDir === 'string' ? path.resolve(entry.cardsDir) : defaultCardsDir;
|
|
336
|
-
const boardRoot = path.join(setupDir, `board-${boardId}`);
|
|
337
|
-
const boardDir = path.join(boardRoot, 'runtime');
|
|
338
|
-
|
|
339
|
-
const taskExecPath = typeof entry.taskExecutorPath === 'string' ? entry.taskExecutorPath : defaultTaskExecutorPath;
|
|
340
|
-
const chatHandlerPath_ = typeof entry.chatHandlerPath === 'string' ? entry.chatHandlerPath : defaultChatHandlerPath;
|
|
341
|
-
const infAdapterPath = typeof entry.inferenceAdapterPath === 'string' ? entry.inferenceAdapterPath : defaultInferenceAdapterPath;
|
|
342
|
-
const stepMachinePath = typeof entry.stepMachineCliPath === 'string' ? entry.stepMachineCliPath : defaultStepMachineCliPath;
|
|
343
|
-
|
|
344
|
-
const sourceGandalfCardsDir = typeof entry.gandalfCardsDir === 'string' ? path.resolve(entry.gandalfCardsDir) : defaultGandalfCardsDir;
|
|
345
|
-
const gandalfTaskExecPath = typeof entry.gandalfTaskExecutorPath === 'string' ? entry.gandalfTaskExecutorPath : defaultGandalfTaskExecutorPath;
|
|
346
|
-
const gandalfChatPath = typeof entry.gandalfChatHandlerPath === 'string' ? entry.gandalfChatHandlerPath : defaultGandalfChatHandlerPath;
|
|
347
|
-
const gandalfInfPath = typeof entry.gandalfInferenceAdapterPath === 'string' ? entry.gandalfInferenceAdapterPath : defaultGandalfInferenceAdapterPath;
|
|
348
|
-
|
|
349
|
-
const baseCfg = buildBoardContextConfig('base', boardDir, taskExecPath, chatHandlerPath_, infAdapterPath, boardId);
|
|
350
|
-
|
|
351
|
-
const boards = [baseCfg];
|
|
352
|
-
let gandalfBoardDir = null;
|
|
353
|
-
if (sourceGandalfCardsDir && gandalfTaskExecPath) {
|
|
354
|
-
gandalfBoardDir = path.join(boardRoot, 'gandalf-runtime');
|
|
355
|
-
const gandalfCfg = buildBoardContextConfig('gandalf', gandalfBoardDir, gandalfTaskExecPath, gandalfChatPath, gandalfInfPath, boardId);
|
|
356
|
-
gandalfCfg.outputsStoreRef = serializeRef({ kind: 'fs-path', value: path.join(boardRoot, 'gandalf-runtime-out', '.outputs') });
|
|
357
|
-
boards.push(gandalfCfg);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Store host config for demo-setup (FS paths are host concerns)
|
|
361
|
-
boardHostConfig.set(boardId, { cardsDir: sourceCardsDir, gandalfCardsDir: sourceGandalfCardsDir, boardDir, boardRoot });
|
|
362
|
-
|
|
363
|
-
// Auto-run demo-setup (write copilot-instructions.md) at board init time,
|
|
364
|
-
// so clients no longer need a separate /demo-setup request before bootstrapping.
|
|
365
|
-
demoPrepSetup(boardId);
|
|
366
|
-
|
|
367
|
-
// runtimeCardsDir is where the live card store lives (inside setupDir).
|
|
368
|
-
const runtimeCardsDir = path.join(boardDir, 'cards');
|
|
369
|
-
|
|
370
|
-
const singleBoardRuntime = createSingleBoardServerRuntime({
|
|
371
|
-
apiBasePath: `${apiBasePath}/${boardId}`,
|
|
372
|
-
boardId,
|
|
373
|
-
boards,
|
|
374
|
-
invocationAdapter,
|
|
375
|
-
notificationTransport,
|
|
376
|
-
logger,
|
|
377
|
-
serverUrl: `http://127.0.0.1:${PORT}`,
|
|
378
|
-
executionExtra: {
|
|
379
|
-
boardSetupRoot: boardRoot,
|
|
380
|
-
chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
|
|
381
|
-
...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
// Host concern: seed card store from source cardsDir only if the runtime store is empty.
|
|
386
|
-
const existing = singleBoardRuntime.cardStore.get({});
|
|
387
|
-
const isEmpty = existing.status !== 'success' || !existing.data?.cards?.length;
|
|
388
|
-
if (isEmpty) {
|
|
389
|
-
const cards = createFsCardSource(sourceCardsDir, selectedCardsPattern).listCards();
|
|
390
|
-
if (cards.length) singleBoardRuntime.cardStore.set({ body: cards });
|
|
391
|
-
}
|
|
392
|
-
// Seed gandalf board if present
|
|
393
|
-
if (gandalfBoardDir && sourceGandalfCardsDir) {
|
|
394
|
-
const gandalfRuntime = singleBoardRuntime.getBoardRuntime?.('gandalf');
|
|
395
|
-
if (gandalfRuntime) {
|
|
396
|
-
const gExisting = gandalfRuntime.cardStore.get({});
|
|
397
|
-
const gEmpty = gExisting.status !== 'success' || !gExisting.data?.cards?.length;
|
|
398
|
-
if (gEmpty) {
|
|
399
|
-
const gCards = createFsCardSource(sourceGandalfCardsDir).listCards();
|
|
400
|
-
if (gCards.length) gandalfRuntime.cardStore.set({ body: gCards });
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return singleBoardRuntime;
|
|
406
|
-
},
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
// ---------------------------------------------------------------------------
|
|
411
|
-
// Demo-setup — host-level concern (not a runtime concern).
|
|
412
|
-
// Writes concatenated copilot-instructions.md at the board setup root.
|
|
413
|
-
// ---------------------------------------------------------------------------
|
|
414
|
-
|
|
415
|
-
const BOARD_SEG_RE = /^\/api\/boards\/([^/]+)\/(.+)$/;
|
|
416
|
-
const _demoPrepSetupDone = new Map();
|
|
417
|
-
|
|
418
|
-
function isDemoSetupDone(boardId) {
|
|
419
|
-
const cfg = boardHostConfig.get(boardId);
|
|
420
|
-
return _demoPrepSetupDone.get(boardId) === true && cfg && fs.existsSync(cfg.cardsDir);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function demoPrepSetup(boardId) {
|
|
424
|
-
const cfg = boardHostConfig.get(boardId);
|
|
425
|
-
if (!cfg) return;
|
|
426
|
-
const { cardsDir, gandalfCardsDir, boardDir } = cfg;
|
|
427
|
-
|
|
428
|
-
const boardSetupRoot = path.dirname(boardDir);
|
|
429
|
-
fs.mkdirSync(boardSetupRoot, { recursive: true });
|
|
430
|
-
const srcDir = path.dirname(cardsDir);
|
|
431
|
-
const agentInstructionFiles = ['agent-instructions.md', 'agent-instructions-cardlayout.md'];
|
|
432
|
-
const parts = [];
|
|
433
|
-
for (const fname of agentInstructionFiles) {
|
|
434
|
-
const fpath = path.join(srcDir, fname);
|
|
435
|
-
if (fs.existsSync(fpath)) parts.push(fs.readFileSync(fpath, 'utf-8').trimEnd());
|
|
436
|
-
}
|
|
437
|
-
if (parts.length > 0) {
|
|
438
|
-
fs.writeFileSync(path.join(boardSetupRoot, 'copilot-instructions.md'), parts.join('\n\n') + '\n', 'utf-8');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
_demoPrepSetupDone.set(boardId, true);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function jsonReply(res, status, payload) {
|
|
445
|
-
const body = JSON.stringify(payload);
|
|
446
|
-
res.writeHead(status, { ...CORS_HEADERS, 'Content-Type': 'application/json; charset=utf-8' });
|
|
447
|
-
res.end(body);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
async function handleDemoSetup(req, res, boardId) {
|
|
451
|
-
try {
|
|
452
|
-
// requireBoardService triggers the factory which runs demoPrepSetup automatically.
|
|
453
|
-
// This endpoint is kept for backward compatibility but setup is now done at board
|
|
454
|
-
// init time inside boardRuntimeFactory — no extra work needed here.
|
|
455
|
-
runtime.requireBoardService(boardId);
|
|
456
|
-
jsonReply(res, 200, { ok: true, setupPerformed: false });
|
|
457
|
-
} catch (err) {
|
|
458
|
-
jsonReply(res, err.statusCode || 500, { error: String((err && err.message) || err) });
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// ---------------------------------------------------------------------------
|
|
463
|
-
// WorkIQ proxy — host-level concern
|
|
464
|
-
// ---------------------------------------------------------------------------
|
|
465
|
-
|
|
466
|
-
async function handleWorkiqAsk(req, res) {
|
|
467
|
-
let body = '';
|
|
468
|
-
for await (const chunk of req) body += chunk;
|
|
469
|
-
let query;
|
|
470
|
-
try {
|
|
471
|
-
query = JSON.parse(body).query;
|
|
472
|
-
} catch {
|
|
473
|
-
return jsonReply(res, 400, { error: 'Invalid JSON body' });
|
|
474
|
-
}
|
|
475
|
-
if (!query || typeof query !== 'string') {
|
|
476
|
-
return jsonReply(res, 400, { error: '{ query } string is required' });
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const workiqJs = path.join(
|
|
480
|
-
process.env.APPDATA || os.homedir(),
|
|
481
|
-
'npm', 'node_modules', '@microsoft', 'workiq', 'bin', 'workiq.js'
|
|
482
|
-
);
|
|
483
|
-
if (!fs.existsSync(workiqJs)) {
|
|
484
|
-
return jsonReply(res, 503, { error: `WorkIQ CLI not found at: ${workiqJs}` });
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
await new Promise((resolve) => {
|
|
488
|
-
let stdout = '';
|
|
489
|
-
let stderr = '';
|
|
490
|
-
let responded = false;
|
|
491
|
-
const child = spawn(process.execPath, [workiqJs, 'ask', '-q', query], {
|
|
492
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
493
|
-
});
|
|
494
|
-
child.stdout.on('data', chunk => { stdout += chunk; });
|
|
495
|
-
child.stderr.on('data', chunk => { stderr += chunk; });
|
|
496
|
-
child.on('error', (err) => {
|
|
497
|
-
if (!responded) {
|
|
498
|
-
responded = true;
|
|
499
|
-
clearTimeout(timeoutId);
|
|
500
|
-
jsonReply(res, 500, { error: `workiq spawn error: ${err.message}` });
|
|
501
|
-
}
|
|
502
|
-
resolve();
|
|
503
|
-
});
|
|
504
|
-
child.on('close', (code) => {
|
|
505
|
-
if (!responded) {
|
|
506
|
-
responded = true;
|
|
507
|
-
clearTimeout(timeoutId);
|
|
508
|
-
if (code !== 0) {
|
|
509
|
-
jsonReply(res, 500, { error: `workiq exited ${code}`, stderr });
|
|
510
|
-
} else {
|
|
511
|
-
jsonReply(res, 200, { response: stdout });
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
resolve();
|
|
515
|
-
});
|
|
516
|
-
const timeoutId = setTimeout(() => {
|
|
517
|
-
if (!responded) {
|
|
518
|
-
responded = true;
|
|
519
|
-
child.kill();
|
|
520
|
-
jsonReply(res, 504, { error: 'workiq timed out after 60s' });
|
|
521
|
-
}
|
|
522
|
-
resolve();
|
|
523
|
-
}, 60_000);
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// ---------------------------------------------------------------------------
|
|
528
|
-
// HTTP server
|
|
529
|
-
// ---------------------------------------------------------------------------
|
|
530
|
-
|
|
531
|
-
const server = http.createServer((req, res) => {
|
|
532
|
-
const method = req.method || 'GET';
|
|
533
|
-
const url = new URL(req.url || '/', 'http://localhost');
|
|
534
|
-
const pathname = url.pathname;
|
|
535
|
-
|
|
536
|
-
if (method === 'OPTIONS') {
|
|
537
|
-
res.writeHead(204, CORS_HEADERS);
|
|
538
|
-
res.end();
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Route: POST /api/workiq/ask — proxy to WorkIQ (M365 Copilot) from server TTY
|
|
543
|
-
if (method === 'POST' && pathname === '/api/workiq/ask') {
|
|
544
|
-
void handleWorkiqAsk(req, res);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Route: demo-setup is handled here in demo-server (host concern)
|
|
549
|
-
const boardSegMatch = pathname.match(BOARD_SEG_RE);
|
|
550
|
-
if (boardSegMatch && boardSegMatch[2] === 'demo-setup') {
|
|
551
|
-
void handleDemoSetup(req, res, boardSegMatch[1]);
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// All other /api/boards routes are handled by the platform-free runtime
|
|
556
|
-
runtime.handleApi(req, res, url).then((handled) => {
|
|
557
|
-
if (!handled) {
|
|
558
|
-
jsonReply(res, 404, { error: 'Not found' });
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
server.listen(PORT, '127.0.0.1', () => {
|
|
564
|
-
console.log(`[demo-server] listening on http://127.0.0.1:${PORT}`);
|
|
565
|
-
console.log(`[demo-server] setup dir: ${setupDir}`);
|
|
566
|
-
console.log(`[demo-server] server-meta store: ${serverMetaRef}`);
|
|
567
|
-
console.log('[demo-server] endpoints:');
|
|
568
|
-
console.log(` GET ${apiBasePath} <- list boards`);
|
|
569
|
-
console.log(` POST ${apiBasePath} {id, label?} <- register board`);
|
|
570
|
-
console.log(` GET ${apiBasePath}/:boardId/demo-setup (no-op; setup now runs at board init)`);
|
|
571
|
-
console.log(` GET ${apiBasePath}/:boardId/init-board`);
|
|
572
|
-
console.log(` GET ${apiBasePath}/:boardId/sse`);
|
|
573
|
-
console.log(` GET ${apiBasePath}/:boardId/board-status`);
|
|
574
|
-
console.log(` PATCH ${apiBasePath}/:boardId/cards/:id`);
|
|
575
|
-
console.log(` POST ${apiBasePath}/:boardId/cards/:id/actions`);
|
|
576
|
-
console.log(` POST ${apiBasePath}/:boardId/cards/:id/files`);
|
|
577
|
-
console.log(` GET ${apiBasePath}/:boardId/cards/:id/files/:idx`);
|
|
578
|
-
console.log(` GET ${apiBasePath}/:boardId/cards/:id/chats`);
|
|
579
|
-
console.log(` POST /api/workiq/ask {query} <- WorkIQ (M365 Copilot) proxy`);
|
|
580
|
-
});
|