latticesql 4.3.0 → 4.3.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/dist/cli.js +176 -77
- package/dist/desktop-entry.js +172 -73
- package/dist/gui-assets/ort/ort-wasm-simd-threaded.asyncify.mjs +112 -0
- package/dist/gui-assets/ort/ort-wasm-simd-threaded.jsep.mjs +106 -0
- package/dist/gui-assets/ort/ort-wasm-simd-threaded.mjs +59 -0
- package/dist/index.cjs +130 -39
- package/dist/index.js +176 -77
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -23491,12 +23491,12 @@ var init_getNodeModulesParentDirs = __esm({
|
|
|
23491
23491
|
"node_modules/@aws-sdk/core/dist-es/submodules/client/util-user-agent-node/getNodeModulesParentDirs.js"() {
|
|
23492
23492
|
"use strict";
|
|
23493
23493
|
import_node_path21 = require("path");
|
|
23494
|
-
getNodeModulesParentDirs = (
|
|
23494
|
+
getNodeModulesParentDirs = (dirname19) => {
|
|
23495
23495
|
const cwd = process.cwd();
|
|
23496
|
-
if (!
|
|
23496
|
+
if (!dirname19) {
|
|
23497
23497
|
return [cwd];
|
|
23498
23498
|
}
|
|
23499
|
-
const normalizedPath = (0, import_node_path21.normalize)(
|
|
23499
|
+
const normalizedPath = (0, import_node_path21.normalize)(dirname19);
|
|
23500
23500
|
const parts = normalizedPath.split(import_node_path21.sep);
|
|
23501
23501
|
const nodeModulesIndex = parts.indexOf("node_modules");
|
|
23502
23502
|
const parentDir = nodeModulesIndex !== -1 ? parts.slice(0, nodeModulesIndex).join(import_node_path21.sep) : normalizedPath;
|
|
@@ -23574,8 +23574,8 @@ var init_getTypeScriptUserAgentPair = __esm({
|
|
|
23574
23574
|
tscVersion = null;
|
|
23575
23575
|
return void 0;
|
|
23576
23576
|
}
|
|
23577
|
-
const
|
|
23578
|
-
const nodeModulesParentDirs = getNodeModulesParentDirs(
|
|
23577
|
+
const dirname19 = typeof __dirname !== "undefined" ? __dirname : void 0;
|
|
23578
|
+
const nodeModulesParentDirs = getNodeModulesParentDirs(dirname19);
|
|
23579
23579
|
let versionFromApp;
|
|
23580
23580
|
for (const nodeModulesParentDir of nodeModulesParentDirs) {
|
|
23581
23581
|
try {
|
|
@@ -64302,12 +64302,16 @@ var tokensCss = `
|
|
|
64302
64302
|
surface (a bare input on a modal, the Database wizard, User Config, etc.):
|
|
64303
64303
|
dark text on a white field with a light border. Placeholders use the
|
|
64304
64304
|
muted token. Affects every input/select/textarea across the GUI. */
|
|
64305
|
-
|
|
64305
|
+
/* Text-like fields only \u2014 radios/checkboxes keep their native rendering
|
|
64306
|
+
(the box/border treatment mangles them). The accent color tints the native
|
|
64307
|
+
controls so checked radios/checkboxes are on-brand blue. */
|
|
64308
|
+
input:not([type='radio']):not([type='checkbox']):not([type='range']), select, textarea {
|
|
64306
64309
|
color: var(--text);
|
|
64307
64310
|
background: var(--surface);
|
|
64308
64311
|
border: 1px solid var(--border);
|
|
64309
64312
|
border-radius: 6px;
|
|
64310
64313
|
}
|
|
64314
|
+
input[type='radio'], input[type='checkbox'] { accent-color: var(--accent); }
|
|
64311
64315
|
input::placeholder, textarea::placeholder {
|
|
64312
64316
|
color: var(--text-muted);
|
|
64313
64317
|
opacity: 1;
|
|
@@ -65292,7 +65296,7 @@ var teamsCss = ` /* \u2500\u2500 Teams (Project Config + User Config) \u2500\
|
|
|
65292
65296
|
color: var(--text); font-weight: 500;
|
|
65293
65297
|
text-transform: uppercase; letter-spacing: 0.04em;
|
|
65294
65298
|
}
|
|
65295
|
-
.modal .field input, .modal .field textarea {
|
|
65299
|
+
.modal .field input:not([type='radio']):not([type='checkbox']), .modal .field textarea {
|
|
65296
65300
|
width: 100%; padding: 6px 8px;
|
|
65297
65301
|
background: var(--surface); color: var(--text);
|
|
65298
65302
|
border: 1px solid var(--border-strong);
|
|
@@ -65302,6 +65306,20 @@ var teamsCss = ` /* \u2500\u2500 Teams (Project Config + User Config) \u2500\
|
|
|
65302
65306
|
color: var(--text-muted);
|
|
65303
65307
|
}
|
|
65304
65308
|
.modal .field textarea { min-height: 60px; font-family: ui-monospace, monospace; font-size: 12px; }
|
|
65309
|
+
/* New-workspace "Kind" selector \u2014 clean cards, blue-highlighted selection. */
|
|
65310
|
+
.wiz-kind-opts { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
|
|
65311
|
+
.wiz-kind-card {
|
|
65312
|
+
flex: 1 1 0; min-width: 132px; display: flex; align-items: center; gap: 9px;
|
|
65313
|
+
padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px;
|
|
65314
|
+
background: var(--surface); cursor: pointer;
|
|
65315
|
+
transition: border-color 0.12s ease, background 0.12s ease;
|
|
65316
|
+
}
|
|
65317
|
+
.wiz-kind-card:hover { border-color: var(--border-strong); }
|
|
65318
|
+
.wiz-kind-card:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
|
|
65319
|
+
.wiz-kind-card input { margin: 0; flex: 0 0 auto; }
|
|
65320
|
+
.wiz-kind-name { font-size: 13px; color: var(--text); text-transform: none; letter-spacing: 0; }
|
|
65321
|
+
.wiz-kind-sub { color: var(--text-muted); font-size: 11px; margin-left: 2px; }
|
|
65322
|
+
.wiz-kind-card:has(input:checked) .wiz-kind-name { color: var(--accent-deep); font-weight: 500; }
|
|
65305
65323
|
.modal .copy-token {
|
|
65306
65324
|
padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--border);
|
|
65307
65325
|
border-radius: 4px; font-family: ui-monospace, monospace; font-size: 12px;
|
|
@@ -66547,6 +66565,9 @@ var bootJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
|
|
|
66547
66565
|
initRailDragDrop();
|
|
66548
66566
|
renderComposer();
|
|
66549
66567
|
initThreadControls();
|
|
66568
|
+
// Warm up on-device voice in the background shortly after boot so dictation
|
|
66569
|
+
// is ready on first use \u2014 no visible model-loading step, ever.
|
|
66570
|
+
if (typeof voicePreload === 'function') setTimeout(voicePreload, 1500);
|
|
66550
66571
|
checkNativeSetup();
|
|
66551
66572
|
// App is fully populated \u2014 reveal it (Feature C).
|
|
66552
66573
|
hideAppLoading();
|
|
@@ -70310,9 +70331,14 @@ var dashboardJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
70310
70331
|
}
|
|
70311
70332
|
var d = displayFor(table);
|
|
70312
70333
|
// The tab shows the open RECORD's name (e.g. an entity row), not the
|
|
70313
|
-
// object/table name.
|
|
70334
|
+
// object/table name. Guard: only retitle a RECORD ('item:') tab \u2014 never the
|
|
70335
|
+
// shared Brain Graph ('graph') tab, even if a record render briefly runs
|
|
70336
|
+
// while the hash still resolves to the graph (which would rename \u{1F9E0}).
|
|
70314
70337
|
if (typeof setTabTitle === 'function') {
|
|
70315
|
-
|
|
70338
|
+
var fsItemKey = tabKeyForHash(location.hash);
|
|
70339
|
+
if (fsItemKey && fsItemKey.indexOf('item:') === 0) {
|
|
70340
|
+
setTabTitle(fsItemKey, fsDisplayName(row) || d.label);
|
|
70341
|
+
}
|
|
70316
70342
|
}
|
|
70317
70343
|
// Files + artifacts get the two-view document layout (formatted display +
|
|
70318
70344
|
// a View Source toggle) with a Version History + Delete dropdown \u2014 not the
|
|
@@ -70382,7 +70408,10 @@ var dashboardJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
70382
70408
|
// The tab shows the FILE's name (e.g. "Properties Dashboard"), not the
|
|
70383
70409
|
// object name ("Files").
|
|
70384
70410
|
if (typeof setTabTitle === 'function') {
|
|
70385
|
-
|
|
70411
|
+
var fsDocKey = tabKeyForHash(location.hash);
|
|
70412
|
+
if (fsDocKey && fsDocKey.indexOf('item:') === 0) {
|
|
70413
|
+
setTabTitle(fsDocKey, fsDisplayName(row) || d.label);
|
|
70414
|
+
}
|
|
70386
70415
|
}
|
|
70387
70416
|
var mode = fileViewMode[id] || 'display';
|
|
70388
70417
|
// Actions live in a dropdown menu next to the title; View source / Version
|
|
@@ -72589,15 +72618,18 @@ var dataModelJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
72589
72618
|
return '' +
|
|
72590
72619
|
nameField +
|
|
72591
72620
|
'<div class="field"><label>Kind</label>' +
|
|
72592
|
-
'<div
|
|
72593
|
-
'<label
|
|
72594
|
-
'<input type="radio" name="wiz-kind" value="local"' + (kind === 'local' ? ' checked' : '') + ' />
|
|
72621
|
+
'<div class="wiz-kind-opts">' +
|
|
72622
|
+
'<label class="wiz-kind-card">' +
|
|
72623
|
+
'<input type="radio" name="wiz-kind" value="local"' + (kind === 'local' ? ' checked' : '') + ' />' +
|
|
72624
|
+
'<span class="wiz-kind-name">New local <span class="wiz-kind-sub">SQLite</span></span>' +
|
|
72595
72625
|
'</label>' +
|
|
72596
|
-
'<label
|
|
72597
|
-
'<input type="radio" name="wiz-kind" value="cloud"' + (kind === 'cloud' ? ' checked' : '') + ' />
|
|
72626
|
+
'<label class="wiz-kind-card">' +
|
|
72627
|
+
'<input type="radio" name="wiz-kind" value="cloud"' + (kind === 'cloud' ? ' checked' : '') + ' />' +
|
|
72628
|
+
'<span class="wiz-kind-name">New cloud <span class="wiz-kind-sub">Postgres</span></span>' +
|
|
72598
72629
|
'</label>' +
|
|
72599
|
-
'<label
|
|
72600
|
-
'<input type="radio" name="wiz-kind" value="join"' + (kind === 'join' ? ' checked' : '') + ' />
|
|
72630
|
+
'<label class="wiz-kind-card">' +
|
|
72631
|
+
'<input type="radio" name="wiz-kind" value="join"' + (kind === 'join' ? ' checked' : '') + ' />' +
|
|
72632
|
+
'<span class="wiz-kind-name">Join a team <span class="wiz-kind-sub">invite</span></span>' +
|
|
72601
72633
|
'</label>' +
|
|
72602
72634
|
'</div>' +
|
|
72603
72635
|
'<p style="font-size:11px;color:var(--text-muted);margin:6px 0 0">' +
|
|
@@ -74742,6 +74774,14 @@ var voiceLocalJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
|
|
|
74742
74774
|
return _voiceWorker;
|
|
74743
74775
|
}
|
|
74744
74776
|
|
|
74777
|
+
// Warm up the voice worker + model in the BACKGROUND on launch, so dictation is
|
|
74778
|
+
// ready by the time the user records and any first-run weight fetch happens
|
|
74779
|
+
// silently up front (never mid-recording, never with a visible "downloading"
|
|
74780
|
+
// state). Idempotent + best-effort \u2014 a failure just leaves the mic to lazy-init.
|
|
74781
|
+
function voicePreload() {
|
|
74782
|
+
try { getVoiceWorker(); } catch (_) { /* best-effort */ }
|
|
74783
|
+
}
|
|
74784
|
+
|
|
74745
74785
|
function voicePreferredDevice() {
|
|
74746
74786
|
// WebGPU when the browser exposes it, else WASM. The worker downgrades to
|
|
74747
74787
|
// wasm itself if the runtime can't honor webgpu, so this is just a hint.
|
|
@@ -74763,14 +74803,14 @@ var voiceLocalJs = ` // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
|
|
|
74763
74803
|
_voiceWorkerReady = false;
|
|
74764
74804
|
}
|
|
74765
74805
|
|
|
74766
|
-
//
|
|
74767
|
-
//
|
|
74768
|
-
//
|
|
74769
|
-
|
|
74770
|
-
|
|
74771
|
-
|
|
74772
|
-
|
|
74773
|
-
|
|
74806
|
+
// Model loading is SILENT \u2014 the user never sees a "downloading voice model"
|
|
74807
|
+
// state. The worker + model are warmed up in the background on launch (see
|
|
74808
|
+
// voicePreload), and the ORT runtime ships bundled, so by the time anyone
|
|
74809
|
+
// records, dictation is ready. We intentionally surface no loading progress in
|
|
74810
|
+
// the composer; if a first-run weight fetch is still in flight, the existing
|
|
74811
|
+
// "Transcribing\u2026" placeholder covers it.
|
|
74812
|
+
function onVoiceProgress(_file, _progress) {
|
|
74813
|
+
/* intentionally silent \u2014 no visible voice-model loading UI */
|
|
74774
74814
|
}
|
|
74775
74815
|
|
|
74776
74816
|
// Decode a recorded audio blob to mono 16 kHz PCM (Float32Array), entirely in
|
|
@@ -75774,12 +75814,33 @@ var sourcesJs = `
|
|
|
75774
75814
|
'<span class="src-name">' + escapeHtml(name) + '</span></div></li>';
|
|
75775
75815
|
}).join('') + '</ul>'
|
|
75776
75816
|
: '';
|
|
75817
|
+
// Preserve expanded folders across this re-render so an in-progress
|
|
75818
|
+
// ingest (which re-renders the sidebar) never snaps an open folder shut.
|
|
75819
|
+
// Capture each expanded folder's path + its lazily-loaded children markup,
|
|
75820
|
+
// then re-attach + re-open them after the rebuild (no re-fetch, no flicker).
|
|
75821
|
+
var openFolders = {};
|
|
75822
|
+
host.querySelectorAll('.src-folder[data-loaded="1"]').forEach(function (li) {
|
|
75823
|
+
var ul0 = li.querySelector(':scope > .src-children');
|
|
75824
|
+
if (ul0 && !ul0.hidden) openFolders[li.getAttribute('data-path')] = ul0.innerHTML;
|
|
75825
|
+
});
|
|
75777
75826
|
if (!rootsHtml && !looseHtml) {
|
|
75778
75827
|
host.innerHTML = '<div class="src-empty">No files yet.</div>';
|
|
75779
75828
|
return;
|
|
75780
75829
|
}
|
|
75781
75830
|
host.innerHTML = rootsHtml + looseHtml;
|
|
75782
75831
|
wireSourceTree(host);
|
|
75832
|
+
host.querySelectorAll('.src-folder').forEach(function (li) {
|
|
75833
|
+
var saved = openFolders[li.getAttribute('data-path')];
|
|
75834
|
+
if (saved == null) return;
|
|
75835
|
+
var ul1 = li.querySelector(':scope > .src-children');
|
|
75836
|
+
var caret = li.querySelector(':scope > .src-row > .src-caret');
|
|
75837
|
+
if (!ul1) return;
|
|
75838
|
+
ul1.innerHTML = saved;
|
|
75839
|
+
ul1.hidden = false;
|
|
75840
|
+
li.setAttribute('data-loaded', '1');
|
|
75841
|
+
if (caret) caret.textContent = '\u25BE';
|
|
75842
|
+
wireSourceTree(ul1);
|
|
75843
|
+
});
|
|
75783
75844
|
})
|
|
75784
75845
|
.catch(function () { host.innerHTML = ''; });
|
|
75785
75846
|
}
|
|
@@ -80231,20 +80292,49 @@ var MAX_LIST_ENTRIES = 2e3;
|
|
|
80231
80292
|
var MAX_INGEST_FILES = 500;
|
|
80232
80293
|
var MAX_INGEST_DEPTH = 8;
|
|
80233
80294
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".venv", "venv"]);
|
|
80234
|
-
function rootsFile() {
|
|
80235
|
-
return (0, import_node_path44.join)(
|
|
80295
|
+
function rootsFile(configPath) {
|
|
80296
|
+
return (0, import_node_path44.join)((0, import_node_path44.dirname)(configPath), "sources.json");
|
|
80297
|
+
}
|
|
80298
|
+
function migrateGlobalRootsIfNeeded(configPath) {
|
|
80299
|
+
const wsFile = rootsFile(configPath);
|
|
80300
|
+
if ((0, import_node_fs41.existsSync)(wsFile)) return;
|
|
80301
|
+
const globalFile = (0, import_node_path44.join)(configDir(), "sources.json");
|
|
80302
|
+
if (globalFile === wsFile || !(0, import_node_fs41.existsSync)(globalFile)) return;
|
|
80303
|
+
let roots;
|
|
80304
|
+
try {
|
|
80305
|
+
const parsed = JSON.parse((0, import_node_fs41.readFileSync)(globalFile, "utf8"));
|
|
80306
|
+
if (!Array.isArray(parsed.roots) || parsed.roots.length === 0) return;
|
|
80307
|
+
roots = parsed.roots;
|
|
80308
|
+
} catch {
|
|
80309
|
+
return;
|
|
80310
|
+
}
|
|
80311
|
+
try {
|
|
80312
|
+
(0, import_node_fs41.mkdirSync)((0, import_node_path44.dirname)(wsFile), { recursive: true });
|
|
80313
|
+
(0, import_node_fs41.writeFileSync)(wsFile, JSON.stringify({ roots }, null, 2), "utf8");
|
|
80314
|
+
} catch {
|
|
80315
|
+
return;
|
|
80316
|
+
}
|
|
80317
|
+
try {
|
|
80318
|
+
(0, import_node_fs41.rmSync)(globalFile, { force: true });
|
|
80319
|
+
} catch {
|
|
80320
|
+
try {
|
|
80321
|
+
(0, import_node_fs41.writeFileSync)(globalFile, JSON.stringify({ roots: [] }, null, 2), "utf8");
|
|
80322
|
+
} catch {
|
|
80323
|
+
}
|
|
80324
|
+
}
|
|
80236
80325
|
}
|
|
80237
|
-
function readRoots() {
|
|
80326
|
+
function readRoots(configPath) {
|
|
80327
|
+
migrateGlobalRootsIfNeeded(configPath);
|
|
80238
80328
|
try {
|
|
80239
|
-
const raw = (0, import_node_fs41.readFileSync)(rootsFile(), "utf8");
|
|
80329
|
+
const raw = (0, import_node_fs41.readFileSync)(rootsFile(configPath), "utf8");
|
|
80240
80330
|
const parsed = JSON.parse(raw);
|
|
80241
80331
|
return Array.isArray(parsed.roots) ? parsed.roots : [];
|
|
80242
80332
|
} catch {
|
|
80243
80333
|
return [];
|
|
80244
80334
|
}
|
|
80245
80335
|
}
|
|
80246
|
-
function writeRoots(roots) {
|
|
80247
|
-
(0, import_node_fs41.writeFileSync)(rootsFile(), JSON.stringify({ roots }, null, 2), "utf8");
|
|
80336
|
+
function writeRoots(configPath, roots) {
|
|
80337
|
+
(0, import_node_fs41.writeFileSync)(rootsFile(configPath), JSON.stringify({ roots }, null, 2), "utf8");
|
|
80248
80338
|
}
|
|
80249
80339
|
function safeResolveInside2(target, roots) {
|
|
80250
80340
|
const abs = (0, import_node_path44.resolve)(target);
|
|
@@ -80334,14 +80424,14 @@ async function ingestFolder(abs, ingestFile) {
|
|
|
80334
80424
|
return { ingested, skipped };
|
|
80335
80425
|
}
|
|
80336
80426
|
async function dispatchSourcesRoute(req, res, deps) {
|
|
80337
|
-
const { pathname, method, ingestFile } = deps;
|
|
80427
|
+
const { pathname, method, ingestFile, configPath } = deps;
|
|
80338
80428
|
if (!pathname.startsWith("/api/sources/")) return false;
|
|
80339
80429
|
if (!localFileOpenEnabled()) {
|
|
80340
80430
|
sendJson(res, { enabled: false });
|
|
80341
80431
|
return true;
|
|
80342
80432
|
}
|
|
80343
80433
|
if (pathname === "/api/sources/roots" && method === "GET") {
|
|
80344
|
-
sendJson(res, { enabled: true, roots: readRoots() });
|
|
80434
|
+
sendJson(res, { enabled: true, roots: readRoots(configPath) });
|
|
80345
80435
|
return true;
|
|
80346
80436
|
}
|
|
80347
80437
|
if (pathname === "/api/sources/roots" && method === "POST") {
|
|
@@ -80367,12 +80457,12 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80367
80457
|
sendJson(res, { error: `path not found: ${abs}` }, 400);
|
|
80368
80458
|
return true;
|
|
80369
80459
|
}
|
|
80370
|
-
const roots = readRoots();
|
|
80460
|
+
const roots = readRoots(configPath);
|
|
80371
80461
|
let root6 = roots.find((r6) => (0, import_node_path44.resolve)(r6.path) === abs);
|
|
80372
80462
|
if (!root6) {
|
|
80373
80463
|
root6 = { id: (0, import_node_crypto24.randomUUID)(), path: abs, kind, name: (0, import_node_path44.basename)(abs) || abs };
|
|
80374
80464
|
roots.push(root6);
|
|
80375
|
-
writeRoots(roots);
|
|
80465
|
+
writeRoots(configPath, roots);
|
|
80376
80466
|
}
|
|
80377
80467
|
let result;
|
|
80378
80468
|
if (kind === "folder") result = await ingestFolder(abs, ingestFile);
|
|
@@ -80383,8 +80473,8 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80383
80473
|
const delMatch = /^\/api\/sources\/roots\/([^/]+)$/.exec(pathname);
|
|
80384
80474
|
if (delMatch && method === "DELETE") {
|
|
80385
80475
|
const id = decodeURIComponent(delMatch[1] ?? "");
|
|
80386
|
-
const roots = readRoots().filter((r6) => r6.id !== id);
|
|
80387
|
-
writeRoots(roots);
|
|
80476
|
+
const roots = readRoots(configPath).filter((r6) => r6.id !== id);
|
|
80477
|
+
writeRoots(configPath, roots);
|
|
80388
80478
|
sendJson(res, { ok: true });
|
|
80389
80479
|
return true;
|
|
80390
80480
|
}
|
|
@@ -80402,7 +80492,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80402
80492
|
sendJson(res, { error: "path is required" }, 400);
|
|
80403
80493
|
return true;
|
|
80404
80494
|
}
|
|
80405
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
80495
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
80406
80496
|
if (!abs) {
|
|
80407
80497
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
80408
80498
|
return true;
|
|
@@ -80417,7 +80507,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80417
80507
|
if (pathname === "/api/sources/ingest-folder" && method === "POST") {
|
|
80418
80508
|
const body = await readJson(req).catch(() => ({}));
|
|
80419
80509
|
const target = typeof body.path === "string" ? body.path : "";
|
|
80420
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
80510
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
80421
80511
|
if (!abs) {
|
|
80422
80512
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
80423
80513
|
return true;
|
|
@@ -83056,6 +83146,7 @@ async function startGuiServer(options) {
|
|
|
83056
83146
|
return await dispatchSourcesRoute(req2, res2, {
|
|
83057
83147
|
db: active.db,
|
|
83058
83148
|
ingestFile: (p3) => ingestLocalFile(ingestCtx, mctx, p3, false),
|
|
83149
|
+
configPath: active.configPath,
|
|
83059
83150
|
pathname,
|
|
83060
83151
|
method
|
|
83061
83152
|
});
|