@brainpilot/backend-core 0.0.9 → 0.0.11

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.
@@ -0,0 +1,593 @@
1
+ /**
2
+ * Single-user knowledge-base build orchestrator.
3
+ *
4
+ * Spawns ``KnowledgeBase/scripts/build_kb.py --json`` and exposes:
5
+ * - POST /api/kb/build start a run (returns immediately)
6
+ * - GET /api/kb/status current run state
7
+ * - GET /api/kb/events SSE — live stream of NDJSON events from the child
8
+ * - POST /api/kb/cancel best-effort SIGTERM
9
+ *
10
+ * Only one build is allowed at a time across the whole process. A new POST
11
+ * while a run is active 409s — that's the right behaviour because every run
12
+ * mutates the same on-disk files and a second concurrent run would corrupt
13
+ * the OCRed_pdf.json ledger.
14
+ *
15
+ * The child's stdout is line-buffered and parsed as NDJSON. Anything that
16
+ * doesn't parse is forwarded as a `{event: "log", msg: <line>}` so stray
17
+ * prints from a dependency don't drop on the floor.
18
+ */
19
+ import { spawn } from "node:child_process";
20
+ import { existsSync } from "node:fs";
21
+ import { resolve, join, dirname } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+ const EVENT_BUFFER_CAP = 5_000; // last N events kept for the status panel
24
+ const SLOTS = { build: null, envSetup: null, modelSetup: null };
25
+ const BUS = { events: [], listeners: new Set() };
26
+ /** Back-compat re-export: some diagnostic code path may still consult a
27
+ * "primary" run. Prefer `slotByKey` in new code. */
28
+ function anyActiveSlot() {
29
+ return SLOTS.build ?? SLOTS.envSetup ?? SLOTS.modelSetup;
30
+ }
31
+ /**
32
+ * Resolve the Python interpreter the build pipeline subprocess will run as.
33
+ *
34
+ * Priority (kept in lock-step with packages/runtime/src/tools/kb/python.ts —
35
+ * we duplicate here rather than importing across packages, so backend-core
36
+ * stays free of a runtime dependency):
37
+ *
38
+ * 1. ``BP_KB_PYTHON`` — explicit override
39
+ * 2. ``<kbRoot>/.venv/bin/python`` — venv created by setup_env.sh
40
+ * (``<kbRoot>\.venv\Scripts\python.exe`` on Windows)
41
+ * 3. ``PYTHON`` env var
42
+ * 4. ``python3`` (Unix) / ``python`` (Windows) on PATH
43
+ *
44
+ * If `kbRoot` is undefined we still check `process.cwd()/KnowledgeBase/.venv`
45
+ * which catches the common "ran build_kb.py from the repo root" case.
46
+ */
47
+ function pythonBin(kbRoot) {
48
+ const override = process.env.BP_KB_PYTHON?.trim();
49
+ if (override)
50
+ return override;
51
+ const rootCandidates = [];
52
+ if (kbRoot)
53
+ rootCandidates.push(kbRoot);
54
+ rootCandidates.push(join(process.cwd(), "KnowledgeBase"));
55
+ try {
56
+ rootCandidates.push(findKbRoot());
57
+ }
58
+ catch { /* findKbRoot fallback */ }
59
+ for (const root of rootCandidates) {
60
+ const venvPython = process.platform === "win32"
61
+ ? join(root, ".venv", "Scripts", "python.exe")
62
+ : join(root, ".venv", "bin", "python");
63
+ if (existsSync(venvPython))
64
+ return venvPython;
65
+ }
66
+ const pyEnv = process.env.PYTHON?.trim();
67
+ if (pyEnv)
68
+ return pyEnv;
69
+ return process.platform === "win32" ? "python" : "python3";
70
+ }
71
+ function defaultBuildScript() {
72
+ if (process.env.BP_KB_BUILD_SCRIPT)
73
+ return process.env.BP_KB_BUILD_SCRIPT;
74
+ return join(findKbRoot(), "scripts", "build_kb.py");
75
+ }
76
+ function buildArgv(opts, script) {
77
+ const argv = [script, "--json"];
78
+ if (opts.kbRoot)
79
+ argv.push("--kb-root", opts.kbRoot);
80
+ if (opts.ocrApiKey)
81
+ argv.push("--ocr-api-key", opts.ocrApiKey);
82
+ if (opts.ocrConcurrency != null)
83
+ argv.push("--ocr-concurrency", String(opts.ocrConcurrency));
84
+ if (opts.ocrLimit != null)
85
+ argv.push("--ocr-limit", String(opts.ocrLimit));
86
+ if (opts.metaApiKey)
87
+ argv.push("--meta-api-key", opts.metaApiKey);
88
+ if (opts.metaBaseUrl)
89
+ argv.push("--meta-base-url", opts.metaBaseUrl);
90
+ if (opts.metaModel)
91
+ argv.push("--meta-model", opts.metaModel);
92
+ if (opts.hfMirror)
93
+ argv.push("--hf-mirror", opts.hfMirror);
94
+ if (opts.only && opts.only.length) {
95
+ argv.push("--only", ...opts.only);
96
+ }
97
+ else if (opts.skip && opts.skip.length) {
98
+ for (const s of opts.skip)
99
+ argv.push(`--skip-${s}`);
100
+ }
101
+ return argv;
102
+ }
103
+ function broadcast(ev) {
104
+ BUS.events.push(ev);
105
+ if (BUS.events.length > EVENT_BUFFER_CAP) {
106
+ BUS.events.splice(0, BUS.events.length - EVENT_BUFFER_CAP);
107
+ }
108
+ for (const l of BUS.listeners) {
109
+ try {
110
+ l(ev);
111
+ }
112
+ catch {
113
+ /* one bad listener should not block the rest */
114
+ }
115
+ }
116
+ }
117
+ function parseLine(line) {
118
+ const trimmed = line.trim();
119
+ if (!trimmed)
120
+ return null;
121
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
122
+ try {
123
+ const ev = JSON.parse(trimmed);
124
+ if (typeof ev.stage === "string" && typeof ev.event === "string")
125
+ return ev;
126
+ }
127
+ catch {
128
+ /* fall through to log */
129
+ }
130
+ }
131
+ return {
132
+ ts: new Date().toISOString(),
133
+ stage: "log",
134
+ event: "log",
135
+ msg: trimmed,
136
+ };
137
+ }
138
+ function pipeOutput(stream) {
139
+ let buf = "";
140
+ stream.setEncoding("utf8");
141
+ stream.on("data", (chunk) => {
142
+ buf += chunk;
143
+ let idx;
144
+ while ((idx = buf.indexOf("\n")) !== -1) {
145
+ const line = buf.slice(0, idx);
146
+ buf = buf.slice(idx + 1);
147
+ const ev = parseLine(line);
148
+ if (ev)
149
+ broadcast(ev);
150
+ }
151
+ });
152
+ stream.on("end", () => {
153
+ if (buf.trim()) {
154
+ const ev = parseLine(buf);
155
+ if (ev)
156
+ broadcast(ev);
157
+ }
158
+ });
159
+ }
160
+ /**
161
+ * Bootstrap the KnowledgeBase Python venv via ``scripts/setup_env.py``.
162
+ *
163
+ * Owns SLOTS.envSetup. Can run concurrently with startKbModelSetup (they
164
+ * don't touch the same files); guarded against a second env-setup only.
165
+ * Build refuses to start while env-setup is running via its own check
166
+ * inside startKbBuild.
167
+ *
168
+ * The setup script is **pure stdlib Python**, so we deliberately do NOT
169
+ * resolve via the venv (which doesn't exist yet). Priority for the
170
+ * bootstrap interpreter:
171
+ * 1. ``opts.python`` — explicit override from the UI
172
+ * 2. ``BP_KB_PYTHON`` — env var (probably wrong here, but honour it)
173
+ * 3. ``python3`` / ``python`` on PATH
174
+ */
175
+ export function startKbEnvSetup(opts = {}) {
176
+ if (SLOTS.envSetup && SLOTS.envSetup.doneAt == null) {
177
+ return { ok: false, message: "Python environment setup is already running" };
178
+ }
179
+ const root = opts.kbRoot ? resolve(opts.kbRoot) : findKbRoot();
180
+ const script = join(root, "scripts", "setup_env.py");
181
+ if (!existsSync(script)) {
182
+ return {
183
+ ok: false,
184
+ message: `setup_env.py not found at ${script}; is the KnowledgeBase tree intact?`,
185
+ };
186
+ }
187
+ const bootstrapPython = opts.python?.trim() ||
188
+ process.env.BP_KB_PYTHON?.trim() ||
189
+ (process.platform === "win32" ? "python" : "python3");
190
+ const argv = [script, "--kb-root", root, "--json"];
191
+ if (opts.reinstall)
192
+ argv.push("--reinstall");
193
+ if (opts.python)
194
+ argv.push("--python", opts.python);
195
+ let proc;
196
+ try {
197
+ proc = spawn(bootstrapPython, argv, {
198
+ stdio: ["ignore", "pipe", "pipe"],
199
+ env: { ...process.env, BP_KB_ROOT: root },
200
+ });
201
+ }
202
+ catch (err) {
203
+ return {
204
+ ok: false,
205
+ message: `failed to spawn ${bootstrapPython}: ${err.message}`,
206
+ };
207
+ }
208
+ const slot = { startedAt: Date.now(), proc };
209
+ SLOTS.envSetup = slot;
210
+ broadcast({
211
+ ts: new Date().toISOString(),
212
+ stage: "setup-env",
213
+ event: "info",
214
+ msg: `spawned ${bootstrapPython} ${argv.join(" ")}`,
215
+ });
216
+ pipeOutput(proc.stdout);
217
+ pipeOutput(proc.stderr);
218
+ proc.on("error", (err) => {
219
+ broadcast({
220
+ ts: new Date().toISOString(),
221
+ stage: "setup-env",
222
+ event: "error",
223
+ msg: `failed to spawn: ${err.message}`,
224
+ });
225
+ slot.doneAt = Date.now();
226
+ slot.exitCode = null;
227
+ slot.error = err.message;
228
+ });
229
+ proc.on("exit", (code, signal) => {
230
+ broadcast({
231
+ ts: new Date().toISOString(),
232
+ stage: "setup-env",
233
+ event: code === 0 ? "done" : "error",
234
+ msg: code === 0
235
+ ? "venv ready"
236
+ : `setup-env exited code=${code} signal=${signal ?? ""}`,
237
+ exit_code: code,
238
+ signal,
239
+ });
240
+ slot.doneAt = Date.now();
241
+ slot.exitCode = code;
242
+ });
243
+ return { ok: true, startedAt: slot.startedAt };
244
+ }
245
+ /**
246
+ * Download the bge-m3 + bge-reranker-v2-m3 weights (~2.5 GB) via
247
+ * ``scripts/setup_models.py``. Runs in its own SLOTS.modelSetup slot so it
248
+ * can execute concurrently with setup_env — the two don't touch the same
249
+ * files. Reuses the venv's Python if present (needed for huggingface_hub);
250
+ * falls back to system Python otherwise (which will fail loudly if the
251
+ * package isn't there, letting the operator know to run env setup first).
252
+ */
253
+ export function startKbModelSetup(opts = {}) {
254
+ if (SLOTS.modelSetup && SLOTS.modelSetup.doneAt == null) {
255
+ return { ok: false, message: "model download is already running" };
256
+ }
257
+ const root = opts.kbRoot ? resolve(opts.kbRoot) : findKbRoot();
258
+ const script = join(root, "scripts", "setup_models.py");
259
+ if (!existsSync(script)) {
260
+ return {
261
+ ok: false,
262
+ message: `setup_models.py not found at ${script}; is the KnowledgeBase tree intact?`,
263
+ };
264
+ }
265
+ const py = pythonBin(root);
266
+ const argv = [script, "--kb-root", root, "--json"];
267
+ if (opts.hfMirror)
268
+ argv.push("--hf-mirror", opts.hfMirror);
269
+ let proc;
270
+ try {
271
+ proc = spawn(py, argv, {
272
+ stdio: ["ignore", "pipe", "pipe"],
273
+ env: { ...process.env, BP_KB_ROOT: root, BP_KB_PYTHON: py },
274
+ });
275
+ }
276
+ catch (err) {
277
+ return {
278
+ ok: false,
279
+ message: `failed to spawn ${py}: ${err.message}`,
280
+ };
281
+ }
282
+ const slot = { startedAt: Date.now(), proc };
283
+ SLOTS.modelSetup = slot;
284
+ broadcast({
285
+ ts: new Date().toISOString(),
286
+ stage: "setup-models",
287
+ event: "info",
288
+ msg: `spawned ${py} ${argv.join(" ")}`,
289
+ });
290
+ pipeOutput(proc.stdout);
291
+ pipeOutput(proc.stderr);
292
+ proc.on("error", (err) => {
293
+ broadcast({
294
+ ts: new Date().toISOString(),
295
+ stage: "setup-models",
296
+ event: "error",
297
+ msg: `failed to spawn: ${err.message}`,
298
+ });
299
+ slot.doneAt = Date.now();
300
+ slot.exitCode = null;
301
+ slot.error = err.message;
302
+ });
303
+ proc.on("exit", (code, signal) => {
304
+ broadcast({
305
+ ts: new Date().toISOString(),
306
+ stage: "setup-models",
307
+ event: code === 0 ? "done" : "error",
308
+ msg: code === 0
309
+ ? "models ready"
310
+ : `setup-models exited code=${code} signal=${signal ?? ""}`,
311
+ exit_code: code,
312
+ signal,
313
+ });
314
+ slot.doneAt = Date.now();
315
+ slot.exitCode = code;
316
+ });
317
+ return { ok: true, startedAt: slot.startedAt };
318
+ }
319
+ /**
320
+ * One-click orchestration: start venv setup, then chain model download when
321
+ * (and only when) the venv build exits 0. huggingface_hub lives inside the
322
+ * venv, so kicking off model download before it exists would fail — the
323
+ * chaining is required, not just a nice-to-have.
324
+ *
325
+ * Emits a synthetic ``setup-full`` info event so the frontend can display a
326
+ * single "combined setup" banner and know when the whole thing is done.
327
+ */
328
+ export function startKbFullSetup(opts = {}) {
329
+ if (SLOTS.envSetup && SLOTS.envSetup.doneAt == null) {
330
+ return { ok: false, message: "Python environment setup is already running" };
331
+ }
332
+ if (SLOTS.modelSetup && SLOTS.modelSetup.doneAt == null) {
333
+ return { ok: false, message: "model download is already running" };
334
+ }
335
+ broadcast({
336
+ ts: new Date().toISOString(),
337
+ stage: "setup-full",
338
+ event: "info",
339
+ msg: "starting Python env + model download (venv first, models will chain automatically on venv success)",
340
+ });
341
+ const envResult = startKbEnvSetup({
342
+ python: opts.python,
343
+ reinstall: opts.reinstall,
344
+ kbRoot: opts.kbRoot,
345
+ });
346
+ if (!envResult.ok)
347
+ return envResult;
348
+ // Chain: when the venv slot completes with code 0, kick off model download.
349
+ // We install this listener now so it fires exactly once — subsequent
350
+ // manual setupModels calls won't retrigger, and a non-zero venv exit
351
+ // leaves modelSetup untouched (letting the operator retry after fixing
352
+ // whatever broke venv creation).
353
+ const chain = (ev) => {
354
+ if (ev.stage !== "setup-env")
355
+ return;
356
+ if (ev.event !== "done" && ev.event !== "error")
357
+ return;
358
+ BUS.listeners.delete(chain);
359
+ if (ev.event === "error") {
360
+ broadcast({
361
+ ts: new Date().toISOString(),
362
+ stage: "setup-full",
363
+ event: "error",
364
+ msg: "venv setup failed; skipping model download",
365
+ });
366
+ return;
367
+ }
368
+ const modelResult = startKbModelSetup({
369
+ hfMirror: opts.hfMirror,
370
+ kbRoot: opts.kbRoot,
371
+ });
372
+ if (!modelResult.ok) {
373
+ broadcast({
374
+ ts: new Date().toISOString(),
375
+ stage: "setup-full",
376
+ event: "error",
377
+ msg: `venv done, but model download refused to start: ${modelResult.message}`,
378
+ });
379
+ return;
380
+ }
381
+ // Second listener: once model download finishes, emit the setup-full done.
382
+ const doneChain = (ev2) => {
383
+ if (ev2.stage !== "setup-models")
384
+ return;
385
+ if (ev2.event !== "done" && ev2.event !== "error")
386
+ return;
387
+ BUS.listeners.delete(doneChain);
388
+ broadcast({
389
+ ts: new Date().toISOString(),
390
+ stage: "setup-full",
391
+ event: ev2.event,
392
+ msg: ev2.event === "done"
393
+ ? "venv + models ready — knowledge base is ready to build"
394
+ : "model download failed after venv completed",
395
+ });
396
+ };
397
+ BUS.listeners.add(doneChain);
398
+ };
399
+ BUS.listeners.add(chain);
400
+ return envResult;
401
+ }
402
+ export function startKbBuild(opts = {}) {
403
+ if (SLOTS.build && SLOTS.build.doneAt == null) {
404
+ return { ok: false, message: "a knowledge-base build is already running" };
405
+ }
406
+ const script = defaultBuildScript();
407
+ if (!existsSync(script)) {
408
+ return {
409
+ ok: false,
410
+ message: `build script not found at ${script}; set BP_KB_BUILD_SCRIPT or ensure KnowledgeBase/ ships with the install.`,
411
+ };
412
+ }
413
+ // If neither BP_KB_PYTHON nor the bundled venv is available, refuse rather
414
+ // than spawning a "python3 not found" failure halfway through. PATH-only
415
+ // python3 can still work, but the user's pip-installed deps then need to
416
+ // be system-wide — almost certainly not what they want.
417
+ const env = describeKbEnvironment(opts.kbRoot);
418
+ if (!process.env.BP_KB_PYTHON && !env.venvExists) {
419
+ return {
420
+ ok: false,
421
+ message: `Python venv not found at ${env.expectedVenvPath}. ` +
422
+ `Run "bash ${join(env.kbRoot, "scripts", "setup_env.sh")}" first ` +
423
+ `(or set BP_KB_PYTHON to point at a Python with the requirements.txt deps installed).`,
424
+ };
425
+ }
426
+ const argv = buildArgv(opts, script);
427
+ const py = pythonBin(opts.kbRoot);
428
+ const proc = spawn(py, argv, {
429
+ stdio: ["ignore", "pipe", "pipe"],
430
+ env: {
431
+ ...process.env,
432
+ ...(opts.kbRoot ? { BP_KB_ROOT: resolve(opts.kbRoot) } : {}),
433
+ // Make sure the build_kb.py orchestrator AND every stage it spawns
434
+ // share the same interpreter — without this, each `subprocess.run`
435
+ // call in build_kb.py would re-resolve via PATH and could land on a
436
+ // different python (e.g. system python3 without our deps installed).
437
+ BP_KB_PYTHON: py,
438
+ },
439
+ });
440
+ const slot = { startedAt: Date.now(), proc };
441
+ SLOTS.build = slot;
442
+ // Banner so the SSE consumer sees something immediately.
443
+ broadcast({
444
+ ts: new Date().toISOString(),
445
+ stage: "build",
446
+ event: "info",
447
+ msg: `spawned ${py} ${argv.join(" ")}`,
448
+ });
449
+ pipeOutput(proc.stdout);
450
+ pipeOutput(proc.stderr);
451
+ proc.on("error", (err) => {
452
+ broadcast({
453
+ ts: new Date().toISOString(),
454
+ stage: "build",
455
+ event: "error",
456
+ msg: `failed to spawn: ${err.message}`,
457
+ });
458
+ slot.doneAt = Date.now();
459
+ slot.exitCode = null;
460
+ slot.error = err.message;
461
+ });
462
+ proc.on("exit", (code, signal) => {
463
+ broadcast({
464
+ ts: new Date().toISOString(),
465
+ stage: "build",
466
+ event: code === 0 ? "done" : "error",
467
+ msg: code === 0
468
+ ? "build finished successfully"
469
+ : `build exited code=${code} signal=${signal ?? ""}`,
470
+ exit_code: code,
471
+ signal,
472
+ });
473
+ slot.doneAt = Date.now();
474
+ slot.exitCode = code;
475
+ });
476
+ return { ok: true, startedAt: slot.startedAt };
477
+ }
478
+ function describeKbEnvironment(kbRoot) {
479
+ // Mirror defaultBuildScript()'s walk-up to find KnowledgeBase/.
480
+ const root = kbRoot ?? findKbRoot();
481
+ const expectedVenv = process.platform === "win32"
482
+ ? join(root, ".venv", "Scripts", "python.exe")
483
+ : join(root, ".venv", "bin", "python");
484
+ const venvExists = existsSync(expectedVenv);
485
+ const python = pythonBin(root);
486
+ return {
487
+ python,
488
+ pythonIsVenv: python === expectedVenv,
489
+ venvExists,
490
+ expectedVenvPath: expectedVenv,
491
+ scriptsPresent: existsSync(join(root, "scripts", "build_kb.py")),
492
+ kbRoot: root,
493
+ };
494
+ }
495
+ export function findKbRoot() {
496
+ if (process.env.BP_KB_ROOT?.trim())
497
+ return resolve(process.env.BP_KB_ROOT.trim());
498
+ let dir = dirname(fileURLToPath(import.meta.url));
499
+ for (let i = 0; i < 8; i++) {
500
+ const candidate = join(dir, "KnowledgeBase");
501
+ if (existsSync(join(candidate, "scripts", "build_kb.py")))
502
+ return candidate;
503
+ const parent = dirname(dir);
504
+ if (parent === dir)
505
+ break;
506
+ dir = parent;
507
+ }
508
+ return join(process.cwd(), "KnowledgeBase");
509
+ }
510
+ export function getKbBuildStatus() {
511
+ const environment = describeKbEnvironment();
512
+ // "Active" now means "any slot is running" — build, env-setup, or model-
513
+ // download. The frontend already fans out on ev.stage for UI display, so
514
+ // this rollup is just used for the "reopened the panel mid-run" banner.
515
+ const active = anyActiveSlot();
516
+ const primary = SLOTS.build ?? SLOTS.envSetup ?? SLOTS.modelSetup;
517
+ if (!primary) {
518
+ return {
519
+ active: false,
520
+ startedAt: null,
521
+ finishedAt: null,
522
+ exitCode: undefined,
523
+ recentEvents: BUS.events.slice(-200),
524
+ environment,
525
+ };
526
+ }
527
+ return {
528
+ active: active !== null && active.doneAt == null,
529
+ startedAt: primary.startedAt,
530
+ finishedAt: primary.doneAt ?? null,
531
+ exitCode: primary.exitCode,
532
+ error: primary.error,
533
+ // Last ~200 events across every slot — the SSE stream is the primary
534
+ // surface; this is for a panel that has just been opened and wants
535
+ // enough context to replay the recent progress.
536
+ recentEvents: BUS.events.slice(-200),
537
+ environment,
538
+ };
539
+ }
540
+ export function subscribeKbBuild(listener) {
541
+ BUS.listeners.add(listener);
542
+ const history = [...BUS.events];
543
+ const done = anyActiveSlot() == null
544
+ ? Promise.resolve()
545
+ : new Promise((resolve) => {
546
+ const check = () => {
547
+ const slot = anyActiveSlot();
548
+ if (slot == null || slot.doneAt != null) {
549
+ // "All idle" — no need to keep polling.
550
+ const anyRunning = (SLOTS.build && SLOTS.build.doneAt == null) ||
551
+ (SLOTS.envSetup && SLOTS.envSetup.doneAt == null) ||
552
+ (SLOTS.modelSetup && SLOTS.modelSetup.doneAt == null);
553
+ if (!anyRunning) {
554
+ resolve();
555
+ return;
556
+ }
557
+ }
558
+ setTimeout(check, 200);
559
+ };
560
+ check();
561
+ });
562
+ return {
563
+ unsubscribe: () => BUS.listeners.delete(listener),
564
+ history,
565
+ done,
566
+ };
567
+ }
568
+ export function cancelKbBuild() {
569
+ // Cancel every active slot — the user has one "Cancel" button in the UI
570
+ // and expects it to stop whatever's currently running. Order: build
571
+ // first (most likely target), then setup jobs.
572
+ const targets = [];
573
+ if (SLOTS.build && SLOTS.build.doneAt == null)
574
+ targets.push(SLOTS.build);
575
+ if (SLOTS.envSetup && SLOTS.envSetup.doneAt == null)
576
+ targets.push(SLOTS.envSetup);
577
+ if (SLOTS.modelSetup && SLOTS.modelSetup.doneAt == null)
578
+ targets.push(SLOTS.modelSetup);
579
+ if (!targets.length) {
580
+ return { ok: false, message: "no active job to cancel" };
581
+ }
582
+ const errs = [];
583
+ for (const t of targets) {
584
+ try {
585
+ t.proc.kill("SIGTERM");
586
+ }
587
+ catch (err) {
588
+ errs.push(err.message);
589
+ }
590
+ }
591
+ return errs.length ? { ok: false, message: errs.join("; ") } : { ok: true };
592
+ }
593
+ //# sourceMappingURL=kb-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kb-builder.js","sourceRoot":"","sources":["../src/kb-builder.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAMA,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExE,MAAM,WAAW,kBAAmB,SAAQ,OAAO,CAAC,gBAAgB,CAAC;IACnE,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAgBd;AAED,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAMA,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExE,MAAM,WAAW,kBAAmB,SAAQ,OAAO,CAAC,gBAAgB,CAAC;IACnE,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAgBd;AAED,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,aAAa,CAAC,CA4DxB"}
package/dist/server.js CHANGED
@@ -7,7 +7,7 @@ import { pathToFileURL } from "node:url";
7
7
  import { serve } from "@hono/node-server";
8
8
  import { createApp } from "./app.js";
9
9
  import { createOrchestrator } from "./create-orchestrator.js";
10
- import { bootstrapEnvProvider } from "./config.js";
10
+ import { bootstrapEnvProvider, migrateLegacySettings } from "./config.js";
11
11
  /**
12
12
  * Build the orchestrator for a server from its options. Exposed (and pure) so
13
13
  * the dataDir wiring is unit-testable without binding a socket.
@@ -47,12 +47,23 @@ export async function startServer(options = {}) {
47
47
  serveWeb: options.serveWeb,
48
48
  env: options.env,
49
49
  });
50
+ const providerDataDir = options.dataDir ?? process.env.BP_DATA_DIR ?? "./brainpilot";
51
+ // #202: migrate a legacy plaintext-key settings.json (pre-rewrite layout) into
52
+ // providers.json before the env fallback runs — settings.json was a user's
53
+ // explicit config, so it outranks env projection. No-op once providers.json
54
+ // has any profile. Best-effort: a failure must not block startup.
55
+ try {
56
+ await migrateLegacySettings(providerDataDir);
57
+ }
58
+ catch {
59
+ // ignore — legacy migration is a convenience, not a startup gate
60
+ }
50
61
  // #51: seed a provider profile from env on first launch so an env-only
51
62
  // quick-start (ANTHROPIC_API_KEY etc.) surfaces an active provider in the Web
52
63
  // UI. No-op once providers.json has any profile. Best-effort: a failure here
53
64
  // must not block the server from starting.
54
65
  try {
55
- await bootstrapEnvProvider(options.dataDir ?? process.env.BP_DATA_DIR ?? "./brainpilot", options.env);
66
+ await bootstrapEnvProvider(providerDataDir, options.env);
56
67
  }
57
68
  catch {
58
69
  // ignore — env projection is a convenience, not a startup gate