yaml-flow 8.2.0 → 8.2.2
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/fs-board-adapter.d.ts +6 -33
- package/cli/node/fs-board-adapter.js +10 -8
- package/cli/node/step-machine-cli.js +2 -2
- package/cli/{types-CSiGbY__.d.ts → types--rXGWbSR.d.ts} +76 -4
- 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-DfU9t5-S.d.cts → artifacts-store-lib-public-C5UL5tyG.d.cts} +3 -31
- package/lib/{artifacts-store-lib-public-BPW_C15z.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-W2zK59m0.d.cts → board-live-cards-public-BLXbcBNk.d.cts} +1 -1
- package/lib/{board-live-cards-public-B8b_0k_j.d.ts → board-live-cards-public-BZaNb2mi.d.ts} +1 -1
- 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-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.d.cts +1 -1
- package/lib/step-machine-public/index.d.ts +1 -1
- package/lib/{storage-interface-BhAON-gW.d.cts → storage-interface-B6ecOulj.d.cts} +25 -3
- package/lib/{storage-interface-BhAON-gW.d.ts → 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-seTI8zta.d.cts → types-Bztd1KoK.d.cts} +55 -3
- package/lib/{types-Bm7IFD7r.d.ts → types-D-xVWPdY.d.ts} +55 -3
- package/package.json +1 -1
- package/examples/board/demo-chat-copilot.flow.json +0 -38
- package/examples/board/demo-chat-copilot.js +0 -185
- package/examples/board/demo-chat-echo.flow.json +0 -38
- package/examples/board/demo-chat-echo.js +0 -92
- package/examples/board/demo-server-config.copilot-chat.json +0 -10
- package/examples/board/demo-server-config.json +0 -10
- package/examples/board/demo-server.js +0 -629
- 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 -362
- /package/examples/board/{source-def-flows → server/board-worker/source-def-flows}/mock.flow.json +0 -0
|
@@ -0,0 +1,593 @@
|
|
|
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 net from 'node:net';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import { 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
|
+
createFsBoardChatStorage,
|
|
19
|
+
createNodeSpawnInvocationAdapter,
|
|
20
|
+
createArtifactsStore,
|
|
21
|
+
parseRef,
|
|
22
|
+
serializeRef,
|
|
23
|
+
} from 'yaml-flow/board-live-cards-node';
|
|
24
|
+
import {
|
|
25
|
+
createStepMachineChatFlowRunner,
|
|
26
|
+
} from 'yaml-flow/step-machine-public';
|
|
27
|
+
|
|
28
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
29
|
+
const SERVER_DIR = path.dirname(__filename);
|
|
30
|
+
const BOARD_ROOT = path.resolve(SERVER_DIR, '..');
|
|
31
|
+
const cliArgs = process.argv.slice(2);
|
|
32
|
+
const SERVER_CONFIG = path.join(BOARD_ROOT, 'server-config.json');
|
|
33
|
+
|
|
34
|
+
function loadServerConfig() {
|
|
35
|
+
const cliConfigIndex = cliArgs.indexOf('--config');
|
|
36
|
+
const cliConfigPath = cliConfigIndex !== -1 ? cliArgs[cliConfigIndex + 1] : '';
|
|
37
|
+
const configuredPath = String(cliConfigPath || '').trim();
|
|
38
|
+
const configPath = configuredPath
|
|
39
|
+
? (path.isAbsolute(configuredPath) ? configuredPath : path.join(BOARD_ROOT, configuredPath))
|
|
40
|
+
: SERVER_CONFIG;
|
|
41
|
+
if (!fs.existsSync(configPath)) return {};
|
|
42
|
+
try {
|
|
43
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
44
|
+
const parsed = JSON.parse(raw);
|
|
45
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
46
|
+
} catch {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveFromConfig(configValue) {
|
|
52
|
+
if (typeof configValue !== 'string' || !configValue.trim()) return null;
|
|
53
|
+
return path.resolve(BOARD_ROOT, configValue);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function loadJsonFromConfig(configValue) {
|
|
57
|
+
const resolved = resolveFromConfig(configValue);
|
|
58
|
+
if (!resolved || !fs.existsSync(resolved)) return null;
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(fs.readFileSync(resolved, 'utf-8'));
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizeTimeoutMs(value, fallback = null) {
|
|
67
|
+
const n = Number(value);
|
|
68
|
+
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
69
|
+
return Math.floor(n);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pickTimeoutMs(...values) {
|
|
73
|
+
for (const value of values) {
|
|
74
|
+
const n = normalizeTimeoutMs(value, null);
|
|
75
|
+
if (n !== null) return n;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function applyFlowTimeout(flow, timeoutMs) {
|
|
81
|
+
if (!flow || typeof flow !== 'object') return flow;
|
|
82
|
+
const normalized = normalizeTimeoutMs(timeoutMs, null);
|
|
83
|
+
if (normalized === null) return flow;
|
|
84
|
+
return {
|
|
85
|
+
...flow,
|
|
86
|
+
settings: {
|
|
87
|
+
...(flow.settings && typeof flow.settings === 'object' ? flow.settings : {}),
|
|
88
|
+
timeout_ms: normalized,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function buildChatHandlerFlowFromScript(scriptPath, timeoutMs = null) {
|
|
94
|
+
if (!scriptPath) return null;
|
|
95
|
+
const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(BOARD_ROOT, scriptPath);
|
|
96
|
+
const resolvedTimeoutMs = normalizeTimeoutMs(timeoutMs, 300000);
|
|
97
|
+
return {
|
|
98
|
+
id: 'demo-chat-script-handler',
|
|
99
|
+
settings: { start_step: 'respond', max_total_steps: 5, timeout_ms: resolvedTimeoutMs },
|
|
100
|
+
steps: {
|
|
101
|
+
respond: {
|
|
102
|
+
description: 'Run the demo board chat responder from a script path',
|
|
103
|
+
handler: {
|
|
104
|
+
type: 'ref',
|
|
105
|
+
howToRun: 'local-node',
|
|
106
|
+
whatToRun: { kind: 'fs-path', value: resolved },
|
|
107
|
+
meta: 'chat-handler',
|
|
108
|
+
},
|
|
109
|
+
transitions: { success: 'completed', failure: 'failed' },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
terminal_states: {
|
|
113
|
+
completed: { description: 'Chat response completed', return_intent: 'success', return_artifacts: false },
|
|
114
|
+
failed: { description: 'Chat response failed', return_intent: 'failure', return_artifacts: false },
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveKindRefFromConfig(configValue) {
|
|
120
|
+
if (typeof configValue !== 'string' || !configValue.trim()) return null;
|
|
121
|
+
const trimmed = configValue.trim();
|
|
122
|
+
if (!trimmed.startsWith('b64:')) return trimmed;
|
|
123
|
+
try {
|
|
124
|
+
const parsed = parseRef(trimmed);
|
|
125
|
+
if (parsed.kind !== 'fs-path') return trimmed;
|
|
126
|
+
const rawPath = parsed.value.trim();
|
|
127
|
+
if (!rawPath) return null;
|
|
128
|
+
const resolved = path.isAbsolute(rawPath) ? rawPath : path.resolve(BOARD_ROOT, rawPath);
|
|
129
|
+
return serializeRef({ kind: 'fs-path', value: resolved });
|
|
130
|
+
} catch {
|
|
131
|
+
return trimmed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const serverConfig = loadServerConfig();
|
|
136
|
+
const configuredChatFlowTimeoutMs = normalizeTimeoutMs(serverConfig.chatFlowTimeoutMs, null);
|
|
137
|
+
const configuredInvokeRefTimeoutMs = normalizeTimeoutMs(serverConfig.chatInvokeRefTimeoutMs, 300000);
|
|
138
|
+
const configuredCopilotTimeoutMs = normalizeTimeoutMs(serverConfig.chatCopilotTimeoutMs, 300000);
|
|
139
|
+
|
|
140
|
+
// Resolve top-level config defaults (used as fallbacks for per-board config)
|
|
141
|
+
const configuredTaskExecutorPath = resolveFromConfig(serverConfig.taskExecutorPath);
|
|
142
|
+
const configuredChatHandlerPath = resolveFromConfig(serverConfig.chatHandlerPath);
|
|
143
|
+
const configuredFlowFromPath = loadJsonFromConfig(serverConfig.chatHandlerFlowPath);
|
|
144
|
+
const configuredChatHandlerFlow = applyFlowTimeout(
|
|
145
|
+
configuredFlowFromPath || buildChatHandlerFlowFromScript(configuredChatHandlerPath, configuredChatFlowTimeoutMs),
|
|
146
|
+
configuredChatFlowTimeoutMs,
|
|
147
|
+
);
|
|
148
|
+
const configuredInferenceAdapterPath = resolveFromConfig(serverConfig.inferenceAdapterPath);
|
|
149
|
+
const configuredStepMachineCliPath = resolveFromConfig(serverConfig.stepMachineCliPath);
|
|
150
|
+
const configuredServerMetaStoreRef = resolveKindRefFromConfig(serverConfig.serverMetaStoreRef);
|
|
151
|
+
|
|
152
|
+
if (!process.env.DEMO_STEP_MACHINE_CLI_PATH && configuredStepMachineCliPath) {
|
|
153
|
+
process.env.DEMO_STEP_MACHINE_CLI_PATH = configuredStepMachineCliPath;
|
|
154
|
+
}
|
|
155
|
+
if (!process.env.DEMO_CHAT_HANDLER_PATH && configuredChatHandlerPath) {
|
|
156
|
+
process.env.DEMO_CHAT_HANDLER_PATH = configuredChatHandlerPath;
|
|
157
|
+
}
|
|
158
|
+
if (!process.env.DEMO_INFERENCE_ADAPTER_PATH && configuredInferenceAdapterPath) {
|
|
159
|
+
process.env.DEMO_INFERENCE_ADAPTER_PATH = configuredInferenceAdapterPath;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const PORT = Number(process.env.DEMO_SERVER_PORT || serverConfig.port || 7799);
|
|
163
|
+
const cardsPatternArgIndex = cliArgs.indexOf('--cards-pattern');
|
|
164
|
+
const cliCardsPattern = cardsPatternArgIndex !== -1 ? cliArgs[cardsPatternArgIndex + 1] : null;
|
|
165
|
+
const selectedCardsPattern = (process.env.DEMO_CARDS_PATTERN || cliCardsPattern || '').trim() || null;
|
|
166
|
+
|
|
167
|
+
const CORS_HEADERS = {
|
|
168
|
+
'Access-Control-Allow-Origin': '*',
|
|
169
|
+
'Access-Control-Allow-Headers': 'content-type,x-file-name',
|
|
170
|
+
'Access-Control-Allow-Methods': 'GET,POST,PATCH,OPTIONS',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Setup directory
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
const setupDir = path.resolve(
|
|
178
|
+
process.env.DEMO_SETUP_DIR || path.join(BOARD_ROOT, '.demo-setup'),
|
|
179
|
+
);
|
|
180
|
+
fs.mkdirSync(setupDir, { recursive: true });
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Host adapter factories — Node-specific implementations injected into the
|
|
184
|
+
// platform-free server runtime.
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function wildcardToRegExp(pattern) {
|
|
188
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
189
|
+
return new RegExp(`^${escaped.replace(/\*/g, '.*').replace(/\?/g, '.')}$`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function createFsCardSource(cardsDir, cardPattern = null) {
|
|
193
|
+
const cardRegex = cardPattern ? wildcardToRegExp(cardPattern) : null;
|
|
194
|
+
return {
|
|
195
|
+
listCards() {
|
|
196
|
+
if (!fs.existsSync(cardsDir)) return [];
|
|
197
|
+
return fs.readdirSync(cardsDir)
|
|
198
|
+
.filter(f => {
|
|
199
|
+
if (!f.endsWith('.json')) return false;
|
|
200
|
+
if (!cardRegex) return true;
|
|
201
|
+
const cardId = path.basename(f, '.json');
|
|
202
|
+
return cardRegex.test(cardId);
|
|
203
|
+
})
|
|
204
|
+
.map(f => {
|
|
205
|
+
try { return JSON.parse(fs.readFileSync(path.join(cardsDir, f), 'utf-8')); }
|
|
206
|
+
catch { return null; }
|
|
207
|
+
})
|
|
208
|
+
.filter(Boolean);
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function namedPipePath(pipeName) {
|
|
214
|
+
if (process.platform === 'win32') return `\\\\.\\pipe\\${pipeName}`;
|
|
215
|
+
return path.join(os.tmpdir(), `${pipeName}.sock`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function makeExecutionRef(scriptPath, extra) {
|
|
219
|
+
if (!scriptPath) return undefined;
|
|
220
|
+
const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(process.cwd(), scriptPath);
|
|
221
|
+
return {
|
|
222
|
+
howToRun: 'local-node',
|
|
223
|
+
whatToRun: serializeRef({ kind: 'fs-path', value: resolved }),
|
|
224
|
+
...(extra !== undefined ? { meta: extra } : {}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function createNamedPipeNotificationTransport() {
|
|
229
|
+
return {
|
|
230
|
+
async subscribe(ref, onEvent) {
|
|
231
|
+
if (ref.kind !== 'named-pipe') return () => {};
|
|
232
|
+
const pipePath = ref.value;
|
|
233
|
+
if (process.platform !== 'win32' && fs.existsSync(pipePath)) {
|
|
234
|
+
try { fs.rmSync(pipePath, { force: true }); } catch { /* */ }
|
|
235
|
+
}
|
|
236
|
+
const server = net.createServer((socket) => {
|
|
237
|
+
let buf = '';
|
|
238
|
+
socket.on('data', (chunk) => {
|
|
239
|
+
buf += chunk.toString('utf-8');
|
|
240
|
+
while (true) {
|
|
241
|
+
const i = buf.indexOf('\n');
|
|
242
|
+
if (i < 0) break;
|
|
243
|
+
const line = buf.slice(0, i).trim();
|
|
244
|
+
buf = buf.slice(i + 1);
|
|
245
|
+
if (!line) continue;
|
|
246
|
+
try { onEvent(JSON.parse(line)?.notification ?? JSON.parse(line)); } catch { /* */ }
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
await new Promise((resolve, reject) => {
|
|
251
|
+
server.once('error', reject);
|
|
252
|
+
server.listen(pipePath, () => resolve());
|
|
253
|
+
});
|
|
254
|
+
return () => {
|
|
255
|
+
server.close();
|
|
256
|
+
if (process.platform !== 'win32') {
|
|
257
|
+
try { fs.rmSync(pipePath, { force: true }); } catch { /* */ }
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Server meta store (multi-board registry)
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
const serverMetaRef = process.env.DEMO_SERVER_META_STORE_REF || configuredServerMetaStoreRef || serializeRef({ kind: 'fs-path', value: setupDir });
|
|
269
|
+
const serverMetaAdapter = createFsBoardPlatformAdapter(
|
|
270
|
+
parseRef(serverMetaRef), { suppressSpawn: true },
|
|
271
|
+
);
|
|
272
|
+
const serverMetaStore = createArtifactsStore(serverMetaAdapter.blobStorage('server-meta'));
|
|
273
|
+
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
// Build multi-board runtime
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
const apiBasePath = '/api/boards';
|
|
279
|
+
const invocationAdapter = createNodeSpawnInvocationAdapter();
|
|
280
|
+
const notificationTransport = createNamedPipeNotificationTransport();
|
|
281
|
+
const logger = { info: console.log, warn: console.warn, error: console.error };
|
|
282
|
+
|
|
283
|
+
// Map config keys to board entries for the factory
|
|
284
|
+
const boardConfigEntries = serverConfig.boards ? Object.entries(serverConfig.boards) : [];
|
|
285
|
+
const boardConfigMap = new Map(boardConfigEntries);
|
|
286
|
+
|
|
287
|
+
function buildBoardContextConfig(label, boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, executionExtra = {}) {
|
|
288
|
+
fs.mkdirSync(boardDir, { recursive: true });
|
|
289
|
+
const runtimeCardsDir = path.join(path.dirname(boardDir), 'cards');
|
|
290
|
+
const runtimeCardStoreDir = path.join(runtimeCardsDir, 'store');
|
|
291
|
+
fs.mkdirSync(runtimeCardStoreDir, { recursive: true });
|
|
292
|
+
|
|
293
|
+
const notifyChannel = `yaml-flow-server-${label}-${boardId}-${process.pid}`;
|
|
294
|
+
const baseRef = parseRef(serializeRef({ kind: 'fs-path', value: boardDir }));
|
|
295
|
+
const boardAdapter = createFsBoardPlatformAdapter(baseRef, { notifyChannel });
|
|
296
|
+
boardAdapter.requestProcessAccumulated = () => {};
|
|
297
|
+
|
|
298
|
+
const artifactsRef = parseRef(serializeRef({ kind: 'fs-path', value: runtimeCardsDir }));
|
|
299
|
+
const artifactsAdapter = createFsBoardPlatformAdapter(artifactsRef, { suppressSpawn: true });
|
|
300
|
+
const cardStoreRef = serializeRef({ kind: 'fs-path', value: runtimeCardStoreDir });
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
label,
|
|
304
|
+
boardAdapter,
|
|
305
|
+
artifactsAdapter,
|
|
306
|
+
baseRef,
|
|
307
|
+
cardStoreRef,
|
|
308
|
+
outputsStoreRef: serializeRef({ kind: 'fs-path', value: path.join(path.dirname(boardDir), 'runtime-out', '.outputs') }),
|
|
309
|
+
notifyRef: { kind: 'named-pipe', value: namedPipePath(notifyChannel) },
|
|
310
|
+
taskExecutorRef: makeExecutionRef(taskExecPath, executionExtra),
|
|
311
|
+
chatHandlerFlow,
|
|
312
|
+
inferenceAdapterRef: makeExecutionRef(infAdapterPath),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Pre-register configured boards in the server meta store
|
|
317
|
+
const persistedBoardsConfigText = serverMetaStore.getText('boards-config.json');
|
|
318
|
+
let persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
|
|
319
|
+
if (persistedBoardsConfigText) {
|
|
320
|
+
try {
|
|
321
|
+
const parsedBoardsConfig = JSON.parse(persistedBoardsConfigText);
|
|
322
|
+
if (parsedBoardsConfig && Array.isArray(parsedBoardsConfig.boards)) {
|
|
323
|
+
persistedBoardsConfig = parsedBoardsConfig;
|
|
324
|
+
}
|
|
325
|
+
} catch {
|
|
326
|
+
persistedBoardsConfig = { boards: [{ id: 'default', label: 'Default Board' }] };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const persistedBoardsById = new Map(
|
|
331
|
+
(persistedBoardsConfig.boards || []).map((board) => [board?.id, board])
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
for (const [key, cfg] of boardConfigEntries) {
|
|
335
|
+
const existing = serverMetaStore.getText(`boards/${key}.json`);
|
|
336
|
+
if (!existing) {
|
|
337
|
+
serverMetaStore.putText(`boards/${key}.json`, JSON.stringify({ id: key, label: cfg.label || key }));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
persistedBoardsById.set(key, {
|
|
341
|
+
...(persistedBoardsById.get(key) || {}),
|
|
342
|
+
id: key,
|
|
343
|
+
label: cfg.label || key,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
serverMetaStore.putText(
|
|
348
|
+
'boards-config.json',
|
|
349
|
+
JSON.stringify({ boards: Array.from(persistedBoardsById.values()) }, null, 2)
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Async local-node ref invoker — uses spawn() instead of spawnSync() so the
|
|
354
|
+
* Node.js event loop is never blocked. Required for chat-flow handlers that
|
|
355
|
+
* must call back to the same server process (avoids a deadlock).
|
|
356
|
+
*/
|
|
357
|
+
function invokeLocalNodeRefAsync(ref, args, opts) {
|
|
358
|
+
return new Promise((resolve) => {
|
|
359
|
+
const whatToRun = ref.whatToRun;
|
|
360
|
+
let scriptPath = '';
|
|
361
|
+
if (whatToRun && typeof whatToRun === 'object' && whatToRun.kind === 'fs-path') {
|
|
362
|
+
scriptPath = String(whatToRun.value || '');
|
|
363
|
+
} else if (typeof whatToRun === 'string') {
|
|
364
|
+
if (whatToRun.startsWith('b64:')) {
|
|
365
|
+
try {
|
|
366
|
+
const parsed = parseRef(whatToRun);
|
|
367
|
+
if (parsed.kind === 'fs-path') scriptPath = String(parsed.value || '');
|
|
368
|
+
} catch { /* fall through */ }
|
|
369
|
+
} else {
|
|
370
|
+
scriptPath = whatToRun;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!scriptPath) {
|
|
374
|
+
return resolve({ result: 'failure', data: { error: 'cannot resolve script path from ref' } });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const cliDir = opts?.cliDir || BOARD_ROOT;
|
|
378
|
+
const resolved = path.isAbsolute(scriptPath) ? scriptPath : path.resolve(cliDir, scriptPath);
|
|
379
|
+
const stdinData = JSON.stringify(args);
|
|
380
|
+
const timeoutMs = typeof opts?.timeoutMs === 'number' && opts.timeoutMs > 0 ? opts.timeoutMs : 30_000;
|
|
381
|
+
|
|
382
|
+
let stdout = '';
|
|
383
|
+
let stderr = '';
|
|
384
|
+
let settled = false;
|
|
385
|
+
const settle = (val) => {
|
|
386
|
+
if (settled) return;
|
|
387
|
+
settled = true;
|
|
388
|
+
clearTimeout(timer);
|
|
389
|
+
resolve(val);
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const child = spawn(process.execPath, [resolved], {
|
|
393
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
394
|
+
windowsHide: true,
|
|
395
|
+
cwd: opts?.cwd || cliDir,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const timer = setTimeout(() => {
|
|
399
|
+
try { child.kill(); } catch { /* best-effort */ }
|
|
400
|
+
settle({ result: 'failure', data: { error: `timeout after ${timeoutMs}ms` } });
|
|
401
|
+
}, timeoutMs);
|
|
402
|
+
|
|
403
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
404
|
+
child.stderr.on('data', (d) => { stderr += d; });
|
|
405
|
+
|
|
406
|
+
child.on('close', (code) => {
|
|
407
|
+
if (settled) return;
|
|
408
|
+
if (code !== 0) {
|
|
409
|
+
settle({ result: 'failure', data: { error: stderr.trim() || `exit code ${code}` } });
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
const parsed = JSON.parse(stdout.trim());
|
|
414
|
+
settle(typeof parsed?.result === 'string' ? parsed : { result: 'success', data: parsed });
|
|
415
|
+
} catch {
|
|
416
|
+
settle({ result: 'success', data: { stdout: stdout.trim() } });
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
child.on('error', (err) => {
|
|
421
|
+
settle({ result: 'failure', data: { error: err.message } });
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
child.stdin.end(stdinData);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
settle({ result: 'failure', data: { error: `stdin write failed: ${err.message}` } });
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const runtime = createMultiBoardServerRuntime({
|
|
433
|
+
apiBasePath,
|
|
434
|
+
serverMetaStore,
|
|
435
|
+
logger,
|
|
436
|
+
boardRuntimeFactory: (boardId, entry) => {
|
|
437
|
+
const cfg = boardConfigMap.get(boardId);
|
|
438
|
+
const regular = cfg?.regular || {};
|
|
439
|
+
|
|
440
|
+
const cardsDir = resolveFromConfig(regular.seedCardsDir) || (entry?.cardsDir ? path.resolve(entry.cardsDir) : null);
|
|
441
|
+
const taskExecPath = resolveFromConfig(regular.taskExecutorPath) || (entry?.taskExecutorPath || configuredTaskExecutorPath);
|
|
442
|
+
const chatHandlerPath = resolveFromConfig(regular.chatHandlerPath) || (entry?.chatHandlerPath || configuredChatHandlerPath);
|
|
443
|
+
const boardFlowTimeoutMs = configuredChatFlowTimeoutMs;
|
|
444
|
+
const chatHandlerFlow = applyFlowTimeout(
|
|
445
|
+
loadJsonFromConfig(regular.chatHandlerFlowPath)
|
|
446
|
+
|| entry?.chatHandlerFlow
|
|
447
|
+
|| buildChatHandlerFlowFromScript(chatHandlerPath, boardFlowTimeoutMs)
|
|
448
|
+
|| configuredChatHandlerFlow,
|
|
449
|
+
boardFlowTimeoutMs,
|
|
450
|
+
);
|
|
451
|
+
const infAdapterPath = resolveFromConfig(regular.inferenceAdapterPath) || (entry?.inferenceAdapterPath || configuredInferenceAdapterPath);
|
|
452
|
+
const stepMachinePath = resolveFromConfig(regular.stepMachineCliPath || cfg?.stepMachineCliPath) || (entry?.stepMachineCliPath || configuredStepMachineCliPath);
|
|
453
|
+
const chatInvokeRefTimeoutMs = configuredInvokeRefTimeoutMs;
|
|
454
|
+
const chatCopilotTimeoutMs = configuredCopilotTimeoutMs;
|
|
455
|
+
|
|
456
|
+
if (chatHandlerPath && !process.env.DEMO_CHAT_HANDLER_PATH) {
|
|
457
|
+
process.env.DEMO_CHAT_HANDLER_PATH = chatHandlerPath;
|
|
458
|
+
}
|
|
459
|
+
if (infAdapterPath && !process.env.DEMO_INFERENCE_ADAPTER_PATH) {
|
|
460
|
+
process.env.DEMO_INFERENCE_ADAPTER_PATH = infAdapterPath;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const boardSetupRootOverride = (process.env.DEMO_BOARD_SETUP_ROOT || '').trim();
|
|
464
|
+
const boardRoot = boardSetupRootOverride
|
|
465
|
+
? path.resolve(boardSetupRootOverride, `board-${boardId}`)
|
|
466
|
+
: (cfg?.setupDir ? path.resolve(BOARD_ROOT, cfg.setupDir) : path.join(setupDir, `board-${boardId}`));
|
|
467
|
+
const chatFlowRoot = path.resolve(BOARD_ROOT, 'server', 'chat-flow');
|
|
468
|
+
fs.mkdirSync(boardRoot, { recursive: true });
|
|
469
|
+
const boardDir = path.join(boardRoot, 'runtime');
|
|
470
|
+
const runtimeCardsDir = path.join(boardRoot, 'cards');
|
|
471
|
+
const flowRunner = createStepMachineChatFlowRunner({
|
|
472
|
+
invokeRef: (ref, stepArgs) => invokeLocalNodeRefAsync(ref, stepArgs, {
|
|
473
|
+
cliDir: BOARD_ROOT,
|
|
474
|
+
cwd: BOARD_ROOT,
|
|
475
|
+
timeoutMs: chatInvokeRefTimeoutMs,
|
|
476
|
+
}),
|
|
477
|
+
});
|
|
478
|
+
const baseExecutionExtra = {
|
|
479
|
+
boardSetupRoot: boardRoot,
|
|
480
|
+
projectRoot: BOARD_ROOT,
|
|
481
|
+
chatFlowRoot,
|
|
482
|
+
apiBasePath: `${apiBasePath}/${boardId}`,
|
|
483
|
+
chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
|
|
484
|
+
serverUrl: `http://127.0.0.1:${PORT}`,
|
|
485
|
+
chatCopilotTimeoutMs,
|
|
486
|
+
...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const baseCfg = buildBoardContextConfig('base', boardDir, taskExecPath, chatHandlerFlow, infAdapterPath, boardId, baseExecutionExtra);
|
|
490
|
+
const boards = [baseCfg];
|
|
491
|
+
|
|
492
|
+
demoPrepSetup({ cardsDir, boardDir });
|
|
493
|
+
|
|
494
|
+
const chatStorage = createFsBoardChatStorage(boardDir);
|
|
495
|
+
|
|
496
|
+
const singleBoardRuntime = createSingleBoardServerRuntime({
|
|
497
|
+
apiBasePath: `${apiBasePath}/${boardId}`,
|
|
498
|
+
boardId,
|
|
499
|
+
chatStorage,
|
|
500
|
+
boards,
|
|
501
|
+
invocationAdapter,
|
|
502
|
+
chatFlowRunner: flowRunner,
|
|
503
|
+
notificationTransport,
|
|
504
|
+
logger,
|
|
505
|
+
serverUrl: `http://127.0.0.1:${PORT}`,
|
|
506
|
+
executionExtra: {
|
|
507
|
+
boardSetupRoot: boardRoot,
|
|
508
|
+
projectRoot: BOARD_ROOT,
|
|
509
|
+
chatFlowRoot,
|
|
510
|
+
apiBasePath: `${apiBasePath}/${boardId}`,
|
|
511
|
+
chatsBlobBasePath: path.join(runtimeCardsDir, 'chats'),
|
|
512
|
+
chatCopilotTimeoutMs,
|
|
513
|
+
...(stepMachinePath ? { stepMachineCliPath: stepMachinePath } : {}),
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Seed card store from source cardsDir if empty
|
|
518
|
+
const existing = singleBoardRuntime.cardStore.get({});
|
|
519
|
+
const isEmpty = existing.status !== 'success' || !existing.data?.cards?.length;
|
|
520
|
+
if (isEmpty && cardsDir) {
|
|
521
|
+
const cards = createFsCardSource(cardsDir, selectedCardsPattern).listCards();
|
|
522
|
+
if (cards.length) singleBoardRuntime.cardStore.set({ body: cards });
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return singleBoardRuntime;
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// ---------------------------------------------------------------------------
|
|
530
|
+
// Host setup — writes copilot-instructions.md into the board setup root.
|
|
531
|
+
// ---------------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
function demoPrepSetup({ cardsDir, boardDir }) {
|
|
534
|
+
if (!cardsDir) return;
|
|
535
|
+
|
|
536
|
+
const boardSetupRoot = path.dirname(boardDir);
|
|
537
|
+
fs.mkdirSync(boardSetupRoot, { recursive: true });
|
|
538
|
+
const srcDir = path.dirname(cardsDir);
|
|
539
|
+
const agentInstructionFiles = ['agent-instructions.md', 'agent-instructions-cardlayout.md'];
|
|
540
|
+
const parts = [];
|
|
541
|
+
for (const fname of agentInstructionFiles) {
|
|
542
|
+
const fpath = path.join(srcDir, fname);
|
|
543
|
+
if (fs.existsSync(fpath)) parts.push(fs.readFileSync(fpath, 'utf-8').trimEnd());
|
|
544
|
+
}
|
|
545
|
+
if (parts.length > 0) {
|
|
546
|
+
fs.writeFileSync(path.join(boardSetupRoot, 'copilot-instructions.md'), parts.join('\n\n') + '\n', 'utf-8');
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function jsonReply(res, status, payload) {
|
|
551
|
+
const body = JSON.stringify(payload);
|
|
552
|
+
res.writeHead(status, { ...CORS_HEADERS, 'Content-Type': 'application/json; charset=utf-8' });
|
|
553
|
+
res.end(body);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const server = http.createServer((req, res) => {
|
|
557
|
+
const method = req.method || 'GET';
|
|
558
|
+
const url = new URL(req.url || '/', 'http://localhost');
|
|
559
|
+
const pathname = url.pathname;
|
|
560
|
+
|
|
561
|
+
if (method === 'OPTIONS') {
|
|
562
|
+
res.writeHead(204, CORS_HEADERS);
|
|
563
|
+
res.end();
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// All other /api/boards routes are handled by the platform-free runtime
|
|
568
|
+
runtime.handleApi(req, res, url).then((handled) => {
|
|
569
|
+
if (!handled) {
|
|
570
|
+
jsonReply(res, 404, { error: 'Not found' });
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
576
|
+
console.log(`[board-server] listening on http://127.0.0.1:${PORT}`);
|
|
577
|
+
console.log(`[board-server] setup dir: ${setupDir}`);
|
|
578
|
+
console.log(`[board-server] server-meta store: ${serverMetaRef}`);
|
|
579
|
+
console.log('[board-server] endpoints:');
|
|
580
|
+
console.log(` GET ${apiBasePath} <- list boards`);
|
|
581
|
+
console.log(` POST ${apiBasePath} {id, label?} <- register board`);
|
|
582
|
+
console.log(` GET ${apiBasePath}/:boardId/init-board`);
|
|
583
|
+
console.log(` GET ${apiBasePath}/:boardId/sse`);
|
|
584
|
+
console.log(` GET ${apiBasePath}/:boardId/board-status`);
|
|
585
|
+
console.log(` GET ${apiBasePath}/:boardId/cards/:id`);
|
|
586
|
+
console.log(` PATCH ${apiBasePath}/:boardId/cards/:id`);
|
|
587
|
+
console.log(` POST ${apiBasePath}/:boardId/cards/:id/actions <- card actions, including chat-send`);
|
|
588
|
+
console.log(` POST ${apiBasePath}/:boardId/cards/:id/files`);
|
|
589
|
+
console.log(` GET ${apiBasePath}/:boardId/cards/:id/files/:idx`);
|
|
590
|
+
console.log(` GET ${apiBasePath}/:boardId/cards/:id/chats`);
|
|
591
|
+
console.log(` POST ${apiBasePath}/:boardId/cards/:id/chats/subscribe-sse`);
|
|
592
|
+
console.log(` POST ${apiBasePath}/:boardId/cards/:id/chats/unsubscribe-sse`);
|
|
593
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const MOCK_DB = {
|
|
2
|
+
quotes: {
|
|
3
|
+
quoteResponse: {
|
|
4
|
+
result: [
|
|
5
|
+
{ symbol: 'AAPL', shortName: 'Apple Inc.', regularMarketPrice: 198.15, regularMarketChange: 2.15, regularMarketChangePercent: 1.10 },
|
|
6
|
+
{ symbol: 'MSFT', shortName: 'Microsoft Corp.', regularMarketPrice: 415.32, regularMarketChange: -1.23, regularMarketChangePercent: -0.30 },
|
|
7
|
+
{ symbol: 'GOOGL', shortName: 'Alphabet Inc.', regularMarketPrice: 174.89, regularMarketChange: 0.89, regularMarketChangePercent: 0.51 },
|
|
8
|
+
{ symbol: 'TSLA', shortName: 'Tesla Inc.', regularMarketPrice: 247.12, regularMarketChange: 5.43, regularMarketChangePercent: 2.25 }
|
|
9
|
+
],
|
|
10
|
+
error: null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
};
|
package/examples/board/server/board-worker/source-def-flows/sqlite-handler/.retain/compliance.db
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* query.cjs — SQLite query runner for the board-worker sqlite handler.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node query.cjs --db <path> --sql <query> [--params <json-array>] [--mode query|exec]
|
|
8
|
+
*
|
|
9
|
+
* Modes:
|
|
10
|
+
* query (default) — SELECT; outputs JSON array of row objects to stdout.
|
|
11
|
+
* exec — INSERT/UPDATE/DELETE; outputs { changes, lastInsertRowid }.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const Database = require('better-sqlite3');
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
function getArg(name) {
|
|
18
|
+
const idx = args.indexOf(name);
|
|
19
|
+
return idx !== -1 && args[idx + 1] !== undefined ? args[idx + 1] : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const dbArg = getArg('--db');
|
|
23
|
+
const sql = getArg('--sql');
|
|
24
|
+
const paramsJson = getArg('--params');
|
|
25
|
+
const mode = getArg('--mode') || 'query';
|
|
26
|
+
|
|
27
|
+
if (!dbArg || !sql) {
|
|
28
|
+
console.error('Usage: query.cjs --db <path> --sql <query> [--params <json>] [--mode query|exec]');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let params = [];
|
|
33
|
+
if (paramsJson) {
|
|
34
|
+
try { params = JSON.parse(paramsJson); }
|
|
35
|
+
catch { console.error('Invalid --params JSON'); process.exit(1); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const db = new Database(dbArg, { readonly: mode === 'query' });
|
|
40
|
+
if (mode === 'exec') {
|
|
41
|
+
const info = db.prepare(sql).run(...params);
|
|
42
|
+
console.log(JSON.stringify({ changes: info.changes, lastInsertRowid: Number(info.lastInsertRowid) }));
|
|
43
|
+
} else {
|
|
44
|
+
const rows = db.prepare(sql).all(...params);
|
|
45
|
+
console.log(JSON.stringify(rows));
|
|
46
|
+
}
|
|
47
|
+
db.close();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(err.message);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|