mixdog 0.9.50 → 0.9.52
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/package.json +5 -3
- package/scripts/abort-recovery-test.mjs +17 -1
- package/scripts/agent-model-liveness-test.mjs +79 -2
- package/scripts/anthropic-admission-retry-integration-test.mjs +119 -0
- package/scripts/anthropic-transport-policy-test.mjs +466 -0
- package/scripts/atomic-lock-tryonce-test.mjs +60 -1
- package/scripts/build-tui.mjs +13 -1
- package/scripts/channel-daemon-smoke.mjs +630 -10
- package/scripts/code-graph-aggregate-cwd-test.mjs +41 -37
- package/scripts/code-graph-disk-hit-test.mjs +11 -0
- package/scripts/code-graph-root-federation-test.mjs +273 -0
- package/scripts/compact-pressure-test.mjs +55 -1
- package/scripts/compact-smoke.mjs +80 -0
- package/scripts/context-mcp-metering-test.mjs +1350 -19
- package/scripts/deferred-tool-loading-test.mjs +17 -0
- package/scripts/gemini-provider-test.mjs +1053 -0
- package/scripts/hook-bus-test.mjs +23 -0
- package/scripts/internal-tools-normalization-test.mjs +10 -0
- package/scripts/interrupted-turn-history-test.mjs +371 -0
- package/scripts/lifecycle-api-test.mjs +76 -0
- package/scripts/max-output-recovery-test.mjs +55 -0
- package/scripts/mcp-grace-deferred-test.mjs +89 -13
- package/scripts/memory-pg-recovery-test.mjs +59 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +391 -4
- package/scripts/process-lifecycle-test.mjs +389 -0
- package/scripts/provider-admission-scheduler-test.mjs +582 -0
- package/scripts/provider-contract-test.mjs +268 -0
- package/scripts/provider-toolcall-test.mjs +520 -3
- package/scripts/reactive-compact-persist-smoke.mjs +59 -0
- package/scripts/resource-admission-test.mjs +789 -0
- package/scripts/session-bench-cache-break-test.mjs +102 -0
- package/scripts/session-bench.mjs +101 -42
- package/scripts/shell-failure-diagnostics-test.mjs +73 -4
- package/scripts/shell-jobs-windows-hide-test.mjs +1 -1
- package/scripts/smoke-loop-failure-summary-test.mjs +38 -0
- package/scripts/smoke-loop-failure-summary.mjs +16 -0
- package/scripts/smoke-loop.mjs +2 -7
- package/scripts/steering-drain-buckets-test.mjs +18 -0
- package/scripts/tool-failures.mjs +15 -1
- package/scripts/toolcall-args-test.mjs +14 -6
- package/scripts/tui-transcript-perf-test.mjs +43 -7
- package/scripts/web-fetch-routing-test.mjs +158 -0
- package/src/cli.mjs +15 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +26 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -0
- package/src/runtime/agent/orchestrator/internal-tools.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +331 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +118 -26
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +29 -17
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +136 -38
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +24 -4
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +554 -42
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +67 -18
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +95 -46
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +54 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +10 -80
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +89 -17
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +93 -26
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +144 -51
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +22 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +74 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +111 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +13 -17
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +78 -12
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +44 -5
- package/src/runtime/agent/orchestrator/providers/registry.mjs +49 -8
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +224 -104
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +127 -55
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +99 -32
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +69 -37
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +8 -28
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +6 -7
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +42 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +220 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -4
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +17 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +89 -22
- package/src/runtime/agent/orchestrator/stall-policy.mjs +2 -12
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +42 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +74 -37
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +223 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +380 -37
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +176 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +108 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +93 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +12 -3
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -14
- package/src/runtime/memory/index.mjs +22 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +84 -10
- package/src/runtime/memory/lib/pg/process.mjs +91 -47
- package/src/runtime/memory/lib/pg/supervisor.mjs +50 -13
- package/src/runtime/search/index.mjs +41 -0
- package/src/runtime/search/lib/http-fetch.mjs +154 -0
- package/src/runtime/search/tool-defs.mjs +23 -0
- package/src/runtime/shared/atomic-file.mjs +28 -150
- package/src/runtime/shared/process-lifecycle.mjs +363 -0
- package/src/runtime/shared/process-shutdown.mjs +27 -4
- package/src/runtime/shared/resource-admission.mjs +359 -0
- package/src/runtime/shared/staged-child-result.mjs +19 -0
- package/src/session-runtime/context-status.mjs +38 -27
- package/src/session-runtime/lifecycle-api.mjs +26 -6
- package/src/session-runtime/provider-request-tools.mjs +72 -0
- package/src/session-runtime/runtime-core.mjs +43 -18
- package/src/session-runtime/session-turn-api.mjs +5 -4
- package/src/session-runtime/tool-catalog.mjs +375 -15
- package/src/standalone/agent-tool.mjs +17 -38
- package/src/standalone/channel-daemon-client.mjs +200 -38
- package/src/standalone/channel-daemon-transport.mjs +136 -9
- package/src/standalone/channel-worker.mjs +79 -12
- package/src/standalone/hook-bus/handlers.mjs +4 -0
- package/src/standalone/hook-bus/rules.mjs +7 -1
- package/src/standalone/hook-bus.mjs +10 -2
- package/src/tui/App.jsx +17 -11
- package/src/tui/app/live-spinner-visibility.mjs +20 -0
- package/src/tui/dist/index.mjs +101 -281
- package/src/tui/engine/session-api-ext.mjs +13 -2
- package/src/tui/engine/session-api.mjs +1 -1
- package/src/tui/engine/turn.mjs +10 -6
- package/src/tui/engine.mjs +13 -4
- package/src/tui/index.jsx +4 -1
- package/src/tui/lib/voice-setup.mjs +16 -0
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
// no module imports its own path. The URL below is resolved against THIS
|
|
12
12
|
// module's dir (tools/code-graph/), so it walks one level up to reach the
|
|
13
13
|
// worker at tools/.
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
isAbsolute,
|
|
16
|
+
relative as pathRelative,
|
|
17
|
+
resolve as pathResolve,
|
|
18
|
+
win32 as pathWin32,
|
|
19
|
+
} from 'node:path';
|
|
15
20
|
import { Worker } from 'node:worker_threads';
|
|
16
21
|
import { acquire as acquireChildSpawnSlot, hasSpareCapacity as childSpawnHasSpareCapacity } from '../../../../shared/child-spawn-gate.mjs';
|
|
17
22
|
import {
|
|
@@ -56,6 +61,46 @@ import { _findDirProjectRoot } from './project-root.mjs';
|
|
|
56
61
|
// spawn instead of fanning out. Entry removed on settle so the next caller
|
|
57
62
|
// after a failure can retry.
|
|
58
63
|
const _inflightAsyncBuilds = new Map();
|
|
64
|
+
const CODE_GRAPH_FILES_ARG_MAX_CHARS = 16_000;
|
|
65
|
+
|
|
66
|
+
function _usesWindowsPathSemantics(value) {
|
|
67
|
+
return /^[A-Za-z]:[\\/]/.test(value) || /^\\\\[^\\]+\\[^\\]+/.test(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _normalizedExcludedPrefixes(cwd, excludedProjectRoots) {
|
|
71
|
+
const windows = _usesWindowsPathSemantics(cwd);
|
|
72
|
+
const relative = windows ? pathWin32.relative : pathRelative;
|
|
73
|
+
const resolve = windows ? pathWin32.resolve : pathResolve;
|
|
74
|
+
return [...new Set((excludedProjectRoots || []).map((root) => {
|
|
75
|
+
let rel = relative(cwd, resolve(root)).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
76
|
+
if (windows) rel = rel.toLowerCase();
|
|
77
|
+
const absolute = windows ? pathWin32.isAbsolute(rel) : isAbsolute(rel);
|
|
78
|
+
return rel && !rel.startsWith('../') && !absolute ? rel : null;
|
|
79
|
+
}).filter(Boolean))].sort();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Exported for the focused pre-cap exclusion regression.
|
|
83
|
+
export function _scopeCodeGraphManifest(manifest, cwd, {
|
|
84
|
+
excludedProjectRoots = [],
|
|
85
|
+
maxFiles = CODE_GRAPH_MAX_FILES,
|
|
86
|
+
} = {}) {
|
|
87
|
+
const prefixes = _normalizedExcludedPrefixes(cwd, excludedProjectRoots);
|
|
88
|
+
const windows = _usesWindowsPathSemantics(cwd);
|
|
89
|
+
const excluded = (rel) => {
|
|
90
|
+
const normalized = rel.replace(/\\/g, '/');
|
|
91
|
+
const comparable = windows ? normalized.toLowerCase() : normalized;
|
|
92
|
+
return prefixes.some((prefix) => comparable === prefix || comparable.startsWith(`${prefix}/`));
|
|
93
|
+
};
|
|
94
|
+
const scoped = (Array.isArray(manifest) ? manifest : [])
|
|
95
|
+
.filter((meta) => meta && typeof meta.rel === 'string' && !excluded(meta.rel));
|
|
96
|
+
const cap = Math.max(1, Math.floor(Number(maxFiles) || CODE_GRAPH_MAX_FILES));
|
|
97
|
+
return {
|
|
98
|
+
manifest: scoped,
|
|
99
|
+
indexed: scoped.length > cap ? scoped.slice(0, cap) : scoped,
|
|
100
|
+
truncated: scoped.length > cap,
|
|
101
|
+
prefixes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
59
104
|
|
|
60
105
|
export function _isCompatibleDiskCodeGraphEntry(entry, maxFiles = CODE_GRAPH_MAX_FILES) {
|
|
61
106
|
return Number.isFinite(entry?.maxFiles) && entry.maxFiles === maxFiles;
|
|
@@ -150,11 +195,80 @@ export function _prepareDiskCodeGraphFastPath({
|
|
|
150
195
|
// Worker disk writes are debounced with an unref'd timer. Drain before sending
|
|
151
196
|
// success so legacy migration persists every loaded cwd before this Worker can
|
|
152
197
|
// exit; a drain failure is intentionally thrown to the worker's failure path.
|
|
153
|
-
export function _postCodeGraphWorkerSuccess(
|
|
154
|
-
|
|
198
|
+
export function _postCodeGraphWorkerSuccess(
|
|
199
|
+
graph,
|
|
200
|
+
postMessage,
|
|
201
|
+
drainCache = drainCodeGraphCacheStrict,
|
|
202
|
+
{ cache = true } = {},
|
|
203
|
+
) {
|
|
204
|
+
if (cache) drainCache();
|
|
155
205
|
postMessage({ ok: true, signature: graph.signature, graph });
|
|
156
206
|
}
|
|
157
207
|
|
|
208
|
+
// Structured keys avoid both separator collisions inside roots/prefixes and
|
|
209
|
+
// collisions between scoped and ordinary builds.
|
|
210
|
+
export function _codeGraphInflightKey(graphCwd, {
|
|
211
|
+
scoped = false,
|
|
212
|
+
maxFiles = CODE_GRAPH_MAX_FILES,
|
|
213
|
+
prefixes = [],
|
|
214
|
+
} = {}) {
|
|
215
|
+
return scoped
|
|
216
|
+
? JSON.stringify(['scope', graphCwd, maxFiles, prefixes])
|
|
217
|
+
: JSON.stringify(['root', graphCwd]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Keep the existing binary protocol while bounding Windows command-line size.
|
|
221
|
+
// Each invocation still resolves against the whole tree. Duplicate lightweight
|
|
222
|
+
// reused records are merged so relationship fields survive every chunk.
|
|
223
|
+
export async function _runGraphFilesChunked(
|
|
224
|
+
absRoot,
|
|
225
|
+
rels,
|
|
226
|
+
reusedMetas,
|
|
227
|
+
{
|
|
228
|
+
maxArgChars = CODE_GRAPH_FILES_ARG_MAX_CHARS,
|
|
229
|
+
runGraphFiles = _runGraphFiles,
|
|
230
|
+
} = {},
|
|
231
|
+
) {
|
|
232
|
+
const budget = Math.max(1, Math.floor(Number(maxArgChars) || CODE_GRAPH_FILES_ARG_MAX_CHARS));
|
|
233
|
+
const chunks = [];
|
|
234
|
+
let chunk = [];
|
|
235
|
+
let chars = 0;
|
|
236
|
+
for (const rel of Array.isArray(rels) ? rels : []) {
|
|
237
|
+
const cost = String(rel).length + 3; // separator plus conservative quoting margin
|
|
238
|
+
if (cost > budget) throw new Error(`code-graph relative path exceeds --files argument budget: ${rel}`);
|
|
239
|
+
if (chunk.length && chars + cost > budget) {
|
|
240
|
+
chunks.push(chunk);
|
|
241
|
+
chunk = [];
|
|
242
|
+
chars = 0;
|
|
243
|
+
}
|
|
244
|
+
chunk.push(rel);
|
|
245
|
+
chars += cost;
|
|
246
|
+
}
|
|
247
|
+
if (chunk.length) chunks.push(chunk);
|
|
248
|
+
|
|
249
|
+
const merged = new Map();
|
|
250
|
+
for (const relChunk of chunks) {
|
|
251
|
+
const records = await runGraphFiles(absRoot, relChunk, reusedMetas);
|
|
252
|
+
for (const rec of Array.isArray(records) ? records : []) {
|
|
253
|
+
if (!rec || typeof rec.rel !== 'string') continue;
|
|
254
|
+
const previous = merged.get(rec.rel);
|
|
255
|
+
if (!previous) {
|
|
256
|
+
merged.set(rec.rel, rec);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const next = { ...previous, ...rec };
|
|
260
|
+
for (const field of ['resolvedImports', 'importedBy']) {
|
|
261
|
+
next[field] = [...new Set([
|
|
262
|
+
...(Array.isArray(previous[field]) ? previous[field] : []),
|
|
263
|
+
...(Array.isArray(rec[field]) ? rec[field] : []),
|
|
264
|
+
])];
|
|
265
|
+
}
|
|
266
|
+
merged.set(rec.rel, next);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return [...merged.values()];
|
|
270
|
+
}
|
|
271
|
+
|
|
158
272
|
// Exported for the focused worker-protocol regression test.
|
|
159
273
|
export function _codeGraphWorkerFailure(message) {
|
|
160
274
|
const error = typeof message?.error === 'string' ? message.error.trim() : '';
|
|
@@ -195,15 +309,23 @@ export function prewarmCodeGraphIfProject(cwd) {
|
|
|
195
309
|
return true;
|
|
196
310
|
}
|
|
197
311
|
|
|
198
|
-
export async function buildCodeGraphAsync(cwd, signal = null, {
|
|
312
|
+
export async function buildCodeGraphAsync(cwd, signal = null, {
|
|
313
|
+
bestEffort = false,
|
|
314
|
+
excludedProjectRoots = [],
|
|
315
|
+
maxFiles = CODE_GRAPH_MAX_FILES,
|
|
316
|
+
} = {}) {
|
|
199
317
|
if (signal?.aborted) throw new Error('aborted');
|
|
200
318
|
const graphCwd = _canonicalGraphCwd(cwd);
|
|
319
|
+
const prefixes = _normalizedExcludedPrefixes(graphCwd, excludedProjectRoots);
|
|
320
|
+
const scoped = prefixes.length > 0 || maxFiles !== CODE_GRAPH_MAX_FILES;
|
|
321
|
+
const scopeOptions = scoped ? { excludedProjectRoots, maxFiles, cache: false } : null;
|
|
322
|
+
const inflightKey = _codeGraphInflightKey(graphCwd, { scoped, maxFiles, prefixes });
|
|
201
323
|
const cached = _codeGraphCache.get(graphCwd);
|
|
202
|
-
if (cached?.graph && Date.now() - cached.ts < CODE_GRAPH_TTL_MS) {
|
|
324
|
+
if (!scoped && cached?.graph && Date.now() - cached.ts < CODE_GRAPH_TTL_MS) {
|
|
203
325
|
_touchCodeGraphCache(graphCwd);
|
|
204
326
|
return cached.graph;
|
|
205
327
|
}
|
|
206
|
-
const existing = _inflightAsyncBuilds.get(
|
|
328
|
+
const existing = _inflightAsyncBuilds.get(inflightKey);
|
|
207
329
|
if (existing) {
|
|
208
330
|
if (!signal) return existing;
|
|
209
331
|
let onAbort = null;
|
|
@@ -230,6 +352,12 @@ export async function buildCodeGraphAsync(cwd, signal = null, { bestEffort = fal
|
|
|
230
352
|
// Loading the compact disk manifest/one candidate entry is synchronous but
|
|
231
353
|
// bounded I/O. Do not run a manifest at all when no disk candidate exists:
|
|
232
354
|
// cold/dirty misses remain entirely on the existing Worker path.
|
|
355
|
+
if (scoped) {
|
|
356
|
+
return _spawnCodeGraphWorker(
|
|
357
|
+
cwd, graphCwd, _genAtStart, signal, null, null, null,
|
|
358
|
+
{ buildOptions: scopeOptions, cacheResult: false },
|
|
359
|
+
);
|
|
360
|
+
}
|
|
233
361
|
return _prepareDiskCodeGraphFastPath({
|
|
234
362
|
graphCwd,
|
|
235
363
|
runFastPath: (diskProbe) => _runDiskCodeGraphFastPath({
|
|
@@ -251,11 +379,11 @@ export async function buildCodeGraphAsync(cwd, signal = null, { bestEffort = fal
|
|
|
251
379
|
}),
|
|
252
380
|
});
|
|
253
381
|
})();
|
|
254
|
-
_inflightAsyncBuilds.set(
|
|
382
|
+
_inflightAsyncBuilds.set(inflightKey, promise);
|
|
255
383
|
try {
|
|
256
384
|
return await promise;
|
|
257
385
|
} finally {
|
|
258
|
-
_inflightAsyncBuilds.delete(
|
|
386
|
+
_inflightAsyncBuilds.delete(inflightKey);
|
|
259
387
|
}
|
|
260
388
|
}
|
|
261
389
|
|
|
@@ -272,6 +400,8 @@ export function _spawnCodeGraphWorker(
|
|
|
272
400
|
getGeneration = _getCodeGraphGen,
|
|
273
401
|
setMemoryCache = _setCodeGraphCache,
|
|
274
402
|
setDiskCache = _setDiskCodeGraphEntry,
|
|
403
|
+
buildOptions = null,
|
|
404
|
+
cacheResult = true,
|
|
275
405
|
} = {},
|
|
276
406
|
) {
|
|
277
407
|
let _worker = null;
|
|
@@ -299,7 +429,7 @@ export function _spawnCodeGraphWorker(
|
|
|
299
429
|
const workerUrl = new URL('../code-graph-prewarm-worker.mjs', import.meta.url);
|
|
300
430
|
try {
|
|
301
431
|
_worker = createWorker(workerUrl, {
|
|
302
|
-
workerData: { cwd, manifest, signature },
|
|
432
|
+
workerData: { cwd, manifest, signature, buildOptions },
|
|
303
433
|
execArgv: [],
|
|
304
434
|
});
|
|
305
435
|
} catch (e) {
|
|
@@ -323,7 +453,7 @@ export function _spawnCodeGraphWorker(
|
|
|
323
453
|
try {
|
|
324
454
|
if (msg && msg.ok && msg.graph && typeof msg.signature === 'string') {
|
|
325
455
|
const genStillCurrent = getGeneration(graphCwd) === genAtStart;
|
|
326
|
-
if (genStillCurrent) {
|
|
456
|
+
if (genStillCurrent && cacheResult) {
|
|
327
457
|
setMemoryCache(graphCwd, { ts: Date.now(), signature: msg.signature, graph: msg.graph });
|
|
328
458
|
setDiskCache(graphCwd, msg.graph);
|
|
329
459
|
}
|
|
@@ -344,27 +474,34 @@ export function _spawnCodeGraphWorker(
|
|
|
344
474
|
* buildCodeGraphAsync (worker-thread isolated) or the code_graph / find_symbol
|
|
345
475
|
* tools, never this synchronous form on the main event loop.
|
|
346
476
|
*/
|
|
347
|
-
export async function _buildCodeGraph(cwd, {
|
|
477
|
+
export async function _buildCodeGraph(cwd, {
|
|
478
|
+
manifest: suppliedManifest = null,
|
|
479
|
+
signature: suppliedSignature = null,
|
|
480
|
+
excludedProjectRoots = [],
|
|
481
|
+
maxFiles = CODE_GRAPH_MAX_FILES,
|
|
482
|
+
cache = true,
|
|
483
|
+
} = {}) {
|
|
348
484
|
const now = Date.now();
|
|
349
485
|
let _tp = performance.now();
|
|
350
486
|
const _trace = (label) => { if (process.env.MIXDOG_GRAPH_TRACE) { const n = performance.now(); process.stderr.write(`[cg-trace] ${label}=${(n - _tp).toFixed(0)}ms\n`); _tp = n; } };
|
|
351
487
|
const graphCwd = _canonicalGraphCwd(cwd);
|
|
352
488
|
const absRoot = graphCwd;
|
|
353
489
|
const _genAtStart = _getCodeGraphGen(graphCwd);
|
|
354
|
-
const cached = _codeGraphCache.get(graphCwd);
|
|
490
|
+
const cached = cache ? _codeGraphCache.get(graphCwd) : null;
|
|
355
491
|
let previousGraph = cached?.graph || null;
|
|
356
|
-
_consumeCodeGraphDirtyPaths(graphCwd);
|
|
492
|
+
if (cache) _consumeCodeGraphDirtyPaths(graphCwd);
|
|
357
493
|
|
|
358
494
|
// 1. Change-detect via Rust --manifest (fp/rel/size only, no parse). A
|
|
359
495
|
// main-thread disk-cache validation may hand its just-computed manifest to
|
|
360
496
|
// this Worker after a miss, avoiding a duplicate native process.
|
|
361
|
-
const
|
|
497
|
+
const unscopedManifest = Array.isArray(suppliedManifest) ? suppliedManifest : await _runGraphManifest(absRoot);
|
|
498
|
+
const scoped = _scopeCodeGraphManifest(unscopedManifest, absRoot, { excludedProjectRoots, maxFiles });
|
|
499
|
+
const manifest = scoped.manifest;
|
|
362
500
|
const signature = typeof suppliedSignature === 'string'
|
|
363
501
|
? suppliedSignature
|
|
364
502
|
: _computeGraphSignature(manifest);
|
|
365
503
|
if (!Array.isArray(suppliedManifest)) _trace('manifest+sig');
|
|
366
|
-
const truncated
|
|
367
|
-
const indexed = truncated ? manifest.slice(0, CODE_GRAPH_MAX_FILES) : manifest;
|
|
504
|
+
const { truncated, indexed } = scoped;
|
|
368
505
|
|
|
369
506
|
// 2. Memory cache hit.
|
|
370
507
|
if (cached && cached.signature === signature && now - cached.ts < CODE_GRAPH_TTL_MS) {
|
|
@@ -373,8 +510,8 @@ export async function _buildCodeGraph(cwd, { manifest: suppliedManifest = null,
|
|
|
373
510
|
}
|
|
374
511
|
|
|
375
512
|
// 3. Disk cache hit.
|
|
376
|
-
ensureDiskCodeGraphLoaded(now);
|
|
377
|
-
const diskEntry = getDiskCodeGraphEntry(graphCwd);
|
|
513
|
+
if (cache) ensureDiskCodeGraphLoaded(now);
|
|
514
|
+
const diskEntry = cache ? getDiskCodeGraphEntry(graphCwd) : null;
|
|
378
515
|
if (_isCompatibleDiskCodeGraphEntry(diskEntry) && diskEntry.signature === signature) {
|
|
379
516
|
const graph = _deserializeGraph(graphCwd, diskEntry);
|
|
380
517
|
if (graph) {
|
|
@@ -406,7 +543,7 @@ export async function _buildCodeGraph(cwd, { manifest: suppliedManifest = null,
|
|
|
406
543
|
if (freshRels.length === 0) {
|
|
407
544
|
fileInfos = reusable;
|
|
408
545
|
} else if (reusable.length > 0 && freshRels.length <= 256) {
|
|
409
|
-
const recs = await
|
|
546
|
+
const recs = await _runGraphFilesChunked(absRoot, freshRels, reusable);
|
|
410
547
|
const reusedByRel = new Map(reusable.map((info) => [info.rel, info]));
|
|
411
548
|
const freshSet = new Set(freshRels);
|
|
412
549
|
fileInfos = [...reusable];
|
|
@@ -425,11 +562,29 @@ export async function _buildCodeGraph(cwd, { manifest: suppliedManifest = null,
|
|
|
425
562
|
}
|
|
426
563
|
}
|
|
427
564
|
}
|
|
565
|
+
} else if (scoped.prefixes.length) {
|
|
566
|
+
const recs = await _runGraphFilesChunked(absRoot, freshRels, reusable);
|
|
567
|
+
fileInfos = recs.map((rec) => _fileInfoFromRustRecord(rec, absRoot));
|
|
428
568
|
} else {
|
|
429
569
|
let recs = await _runGraphWalk(absRoot);
|
|
430
|
-
if (recs.length >
|
|
570
|
+
if (recs.length > maxFiles) recs = recs.slice(0, maxFiles);
|
|
431
571
|
fileInfos = recs.map((rec) => _fileInfoFromRustRecord(rec, absRoot));
|
|
432
572
|
}
|
|
573
|
+
const allowedRels = new Set(indexed.map((meta) => meta.rel));
|
|
574
|
+
for (const info of fileInfos) {
|
|
575
|
+
info.resolvedImports = (info.resolvedImports || []).filter((rel) => allowedRels.has(rel));
|
|
576
|
+
info.importedBy = [];
|
|
577
|
+
}
|
|
578
|
+
const importedBy = new Map(fileInfos.map((info) => [info.rel, []]));
|
|
579
|
+
for (const info of fileInfos) {
|
|
580
|
+
for (const targetRel of info.resolvedImports) {
|
|
581
|
+
const sources = importedBy.get(targetRel);
|
|
582
|
+
if (sources) sources.push(info.rel);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
for (const info of fileInfos) {
|
|
586
|
+
info.importedBy = [...new Set(importedBy.get(info.rel) || [])];
|
|
587
|
+
}
|
|
433
588
|
_trace('walk+parse');
|
|
434
589
|
const nodes = new Map();
|
|
435
590
|
const reverse = new Map();
|
|
@@ -470,7 +625,7 @@ export async function _buildCodeGraph(cwd, { manifest: suppliedManifest = null,
|
|
|
470
625
|
}
|
|
471
626
|
}
|
|
472
627
|
graph._symbolTokenIndexDirty = true;
|
|
473
|
-
if (_getCodeGraphGen(graphCwd) === _genAtStart) {
|
|
628
|
+
if (cache && _getCodeGraphGen(graphCwd) === _genAtStart) {
|
|
474
629
|
_setCodeGraphCache(graphCwd, { ts: now, signature, graph });
|
|
475
630
|
_setDiskCodeGraphEntry(graphCwd, graph);
|
|
476
631
|
_trace('cache+disk');
|
|
@@ -437,6 +437,20 @@ export function ensureDiskCodeGraphLoaded(now = Date.now()) {
|
|
|
437
437
|
_loadDiskCodeGraphCache(now);
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
+
// Read-only inventory used by filesystem-root federation. This deliberately
|
|
441
|
+
// does not initialize, migrate, prune, or load graph payloads: manifest keys
|
|
442
|
+
// are sufficient to identify roots the user has already indexed.
|
|
443
|
+
export function listCachedCodeGraphRoots() {
|
|
444
|
+
try {
|
|
445
|
+
const file = join(_codeGraphDiskDir(), 'manifest.json');
|
|
446
|
+
if (!existsSync(file)) return [];
|
|
447
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
448
|
+
return parsed && typeof parsed === 'object' ? Object.keys(parsed) : [];
|
|
449
|
+
} catch {
|
|
450
|
+
return [];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
440
454
|
export function _setDiskCodeGraphEntry(cwd, graph) {
|
|
441
455
|
_loadDiskCodeGraphCache();
|
|
442
456
|
// Stamp the cache entry with the persistence timestamp (not the build
|
|
@@ -19,6 +19,12 @@ import {
|
|
|
19
19
|
_stripEmptyArgs,
|
|
20
20
|
} from './project-root.mjs';
|
|
21
21
|
import { buildCodeGraphAsync, prewarmCodeGraph, prewarmCodeGraphSymbols } from './build.mjs';
|
|
22
|
+
import {
|
|
23
|
+
_isFilesystemRootPath,
|
|
24
|
+
collectTrustedCodeGraphRoots,
|
|
25
|
+
formatFederatedProjectLabel,
|
|
26
|
+
owningTrustedCodeGraphRoot,
|
|
27
|
+
} from './trusted-roots.mjs';
|
|
22
28
|
import {
|
|
23
29
|
_findSymbolHits,
|
|
24
30
|
_findSymbolAcrossGraph,
|
|
@@ -51,6 +57,10 @@ function _collectGraphSymbolList(args) {
|
|
|
51
57
|
|
|
52
58
|
const CODE_GRAPH_FILE_BATCH_CAP = 20;
|
|
53
59
|
const _AGGREGATE_FILE_WILDCARD_RE = /[*?[\]{}]/;
|
|
60
|
+
const ROOT_FEDERATED_MODES = new Set([
|
|
61
|
+
'overview', 'symbol', 'find_symbol', 'symbol_search', 'search',
|
|
62
|
+
'references', 'callers', 'callees', 'symbols', 'prewarm',
|
|
63
|
+
]);
|
|
54
64
|
|
|
55
65
|
// Absorb: file/files arriving as a JSON-stringified array
|
|
56
66
|
// (file:"[\"a.mjs\",\"b.mjs\"]") — parse to a real array so the graph lookup
|
|
@@ -181,7 +191,9 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
181
191
|
return `prewarm scheduled: cwd=${cwd} symbols=${symbols.length}${symbols.length ? ` (${symbols.slice(0, 5).join(',')}${symbols.length > 5 ? `,+${symbols.length - 5}` : ''})` : ''}`;
|
|
182
192
|
}
|
|
183
193
|
|
|
184
|
-
const graph = await buildCodeGraphAsync(cwd, signal
|
|
194
|
+
const graph = await buildCodeGraphAsync(cwd, signal, {
|
|
195
|
+
excludedProjectRoots: options?.excludedProjectRoots,
|
|
196
|
+
});
|
|
185
197
|
if (!graph || graph.nodes.size === 0) {
|
|
186
198
|
throw new Error(`code_graph: cwd '${cwd}' is not an indexed/known project root or contains zero eligible files`);
|
|
187
199
|
}
|
|
@@ -476,7 +488,9 @@ async function findSymbolTool(args, cwd, signal = null, options = {}) {
|
|
|
476
488
|
else prewarmCodeGraph(cwd);
|
|
477
489
|
return `prewarm scheduled: cwd=${cwd} symbols=${symbols.length}${symbols.length ? ` (${symbols.slice(0, 5).join(',')}${symbols.length > 5 ? `,+${symbols.length - 5}` : ''})` : ''}`;
|
|
478
490
|
}
|
|
479
|
-
const graph = await buildCodeGraphAsync(cwd, signal
|
|
491
|
+
const graph = await buildCodeGraphAsync(cwd, signal, {
|
|
492
|
+
excludedProjectRoots: options?.excludedProjectRoots,
|
|
493
|
+
});
|
|
480
494
|
if (!graph) throw new Error(`find_symbol: cwd '${cwd}' is not an indexed/known project root or contains zero eligible files`);
|
|
481
495
|
if (options?.scopedCacheOutcome && graph.truncated) {
|
|
482
496
|
markScopedCacheIncomplete(options.scopedCacheOutcome);
|
|
@@ -594,6 +608,98 @@ export async function executeCodeGraphTool(name, args, cwd, signal = null, optio
|
|
|
594
608
|
const hasAggregateFileArgs = _hasAggregateFileArgs(args);
|
|
595
609
|
let effectiveCwd = baseCwd;
|
|
596
610
|
const baseProjectRoot = _findDirProjectRoot(baseCwd);
|
|
611
|
+
const filesystemRootCwd = !baseProjectRoot && _isFilesystemRootPath(baseCwd);
|
|
612
|
+
if (filesystemRootCwd) {
|
|
613
|
+
const trustedRoots = collectTrustedCodeGraphRoots(baseCwd);
|
|
614
|
+
const files = _collectGraphFileList(args, { cap: false });
|
|
615
|
+
const rawMode = String(args?.mode || '').trim();
|
|
616
|
+
const canFederate = files.length > 0 || ROOT_FEDERATED_MODES.has(rawMode);
|
|
617
|
+
if (files.length) {
|
|
618
|
+
if (files.some((file) => _AGGREGATE_FILE_WILDCARD_RE.test(file))) {
|
|
619
|
+
return `Error: ${name}: wildcard-shaped file anchors are not allowed at a filesystem root`;
|
|
620
|
+
}
|
|
621
|
+
if (files.length > CODE_GRAPH_FILE_BATCH_CAP) {
|
|
622
|
+
return `Error: ${name}: file list exceeds cap of ${CODE_GRAPH_FILE_BATCH_CAP}`;
|
|
623
|
+
}
|
|
624
|
+
const routed = files.map((file) => {
|
|
625
|
+
const abs = isAbsolute(file) ? pathResolve(file) : pathResolve(baseCwd, file);
|
|
626
|
+
return {
|
|
627
|
+
file,
|
|
628
|
+
abs,
|
|
629
|
+
exists: existsSync(abs),
|
|
630
|
+
root: owningTrustedCodeGraphRoot(abs, trustedRoots),
|
|
631
|
+
};
|
|
632
|
+
});
|
|
633
|
+
const missing = routed.find((row) => !row.exists);
|
|
634
|
+
if (missing) return `Error: ${name}: file not found: ${missing.file}`;
|
|
635
|
+
const untrusted = routed.find((row) => !row.root);
|
|
636
|
+
if (untrusted) {
|
|
637
|
+
return `Error: ${name}: file anchor is not owned by a trusted project: ${untrusted.file}`;
|
|
638
|
+
}
|
|
639
|
+
if (!canFederate) {
|
|
640
|
+
return `Error: ${name}: mode '${rawMode}' cannot be routed from a filesystem root`;
|
|
641
|
+
}
|
|
642
|
+
const projectArgs = { ...args };
|
|
643
|
+
delete projectArgs.cwd;
|
|
644
|
+
const sections = [];
|
|
645
|
+
for (const { file, abs, root } of routed) {
|
|
646
|
+
const excludedProjectRoots = trustedRoots.filter((candidate) => candidate !== root
|
|
647
|
+
&& owningTrustedCodeGraphRoot(candidate, [root]) === root);
|
|
648
|
+
let body;
|
|
649
|
+
try {
|
|
650
|
+
body = await executeCodeGraphTool(
|
|
651
|
+
name,
|
|
652
|
+
{ ...projectArgs, file: abs, files: undefined },
|
|
653
|
+
root,
|
|
654
|
+
signal,
|
|
655
|
+
{ ...options, excludedProjectRoots },
|
|
656
|
+
);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
body = `Error: ${err?.message || String(err)}`;
|
|
659
|
+
}
|
|
660
|
+
sections.push(`# ${rawMode} ${file}\n# project ${formatFederatedProjectLabel(root)}\n${body}`);
|
|
661
|
+
}
|
|
662
|
+
return sections.join('\n\n');
|
|
663
|
+
}
|
|
664
|
+
if (trustedRoots.length && canFederate) {
|
|
665
|
+
const projectArgs = { ...args };
|
|
666
|
+
delete projectArgs.cwd;
|
|
667
|
+
const runOne = async (root, nextArgs) => executeCodeGraphTool(
|
|
668
|
+
name,
|
|
669
|
+
nextArgs,
|
|
670
|
+
root,
|
|
671
|
+
signal,
|
|
672
|
+
{
|
|
673
|
+
...options,
|
|
674
|
+
excludedProjectRoots: trustedRoots.filter((candidate) => candidate !== root
|
|
675
|
+
&& owningTrustedCodeGraphRoot(candidate, [root]) === root),
|
|
676
|
+
},
|
|
677
|
+
);
|
|
678
|
+
let federationWork;
|
|
679
|
+
federationWork = (async () => {
|
|
680
|
+
const sections = [];
|
|
681
|
+
for (const root of trustedRoots) {
|
|
682
|
+
let body;
|
|
683
|
+
try { body = await runOne(root, projectArgs); }
|
|
684
|
+
catch (err) { body = `Error: ${err?.message || String(err)}`; }
|
|
685
|
+
sections.push(`# project ${formatFederatedProjectLabel(root)}\n${body}`);
|
|
686
|
+
}
|
|
687
|
+
return sections.join('\n\n');
|
|
688
|
+
})();
|
|
689
|
+
if (federationWork) {
|
|
690
|
+
if (!signal) return federationWork;
|
|
691
|
+
let onAbort = null;
|
|
692
|
+
const abortP = new Promise((_, reject) => {
|
|
693
|
+
if (signal.aborted) { reject(new Error('aborted')); return; }
|
|
694
|
+
onAbort = () => reject(new Error('aborted'));
|
|
695
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
696
|
+
});
|
|
697
|
+
return Promise.race([federationWork, abortP]).finally(() => {
|
|
698
|
+
if (onAbort) signal.removeEventListener('abort', onAbort);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
597
703
|
if (hasAggregateFileArgs && !baseProjectRoot) {
|
|
598
704
|
const aggregateRoot = _resolveAggregateFileProjectRoot(args, baseCwd);
|
|
599
705
|
if (!aggregateRoot) {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { statSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
+
import { listProjects } from '../../../../../standalone/projects.mjs';
|
|
4
|
+
import {
|
|
5
|
+
explicitSessionCwd,
|
|
6
|
+
readLastSessionCwd,
|
|
7
|
+
} from '../../../../shared/user-cwd.mjs';
|
|
8
|
+
import { listCachedCodeGraphRoots } from './disk-cache.mjs';
|
|
9
|
+
import { _findDirProjectRoot } from './project-root.mjs';
|
|
10
|
+
|
|
11
|
+
function isDirectory(path) {
|
|
12
|
+
try { return statSync(path).isDirectory(); } catch { return false; }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function _isFilesystemRootPath(value) {
|
|
16
|
+
const raw = String(value || '').trim();
|
|
17
|
+
if (!raw) return false;
|
|
18
|
+
// A bare drive designator is drive-relative on Windows. Guard it before
|
|
19
|
+
// resolve(), which may expand C: to that drive's active directory (even C:\).
|
|
20
|
+
if (/^[a-zA-Z]:$/.test(raw)) return false;
|
|
21
|
+
// Keep this platform-neutral so Windows drive-root semantics can also be
|
|
22
|
+
// regression-tested on Unix hosts.
|
|
23
|
+
if (/^[a-zA-Z]:[\\/]$/.test(raw)) return true;
|
|
24
|
+
try {
|
|
25
|
+
const abs = resolve(raw);
|
|
26
|
+
return dirname(abs) === abs;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function pathKey(path) {
|
|
33
|
+
const abs = resolve(path);
|
|
34
|
+
return process.platform === 'win32' ? abs.toLowerCase() : abs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function _pathIsWithin(root, candidate) {
|
|
38
|
+
try {
|
|
39
|
+
const rel = relative(resolve(root), resolve(candidate));
|
|
40
|
+
return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return live, non-root graph targets from explicit registrations, current /
|
|
48
|
+
* recent session selection, and cache manifests. Dependencies are injectable
|
|
49
|
+
* to keep trust-source and Windows/Unix semantics tests hermetic.
|
|
50
|
+
*/
|
|
51
|
+
export function collectTrustedCodeGraphRoots(filesystemRoot, {
|
|
52
|
+
registered = () => listProjects().map((entry) => entry.path),
|
|
53
|
+
selected = () => [
|
|
54
|
+
explicitSessionCwd(),
|
|
55
|
+
readLastSessionCwd(),
|
|
56
|
+
],
|
|
57
|
+
cached = listCachedCodeGraphRoots,
|
|
58
|
+
directory = isDirectory,
|
|
59
|
+
projectRoot = _findDirProjectRoot,
|
|
60
|
+
} = {}) {
|
|
61
|
+
const root = resolve(filesystemRoot);
|
|
62
|
+
const rows = [
|
|
63
|
+
...registered().map((path) => ({ path, detect: false })),
|
|
64
|
+
...selected().map((path) => ({ path, detect: true })),
|
|
65
|
+
...cached().map((path) => ({ path, detect: false })),
|
|
66
|
+
];
|
|
67
|
+
const found = new Map();
|
|
68
|
+
for (const row of rows) {
|
|
69
|
+
if (!row.path) continue;
|
|
70
|
+
let candidate;
|
|
71
|
+
try { candidate = resolve(row.path); } catch { continue; }
|
|
72
|
+
if (!directory(candidate) || _isFilesystemRootPath(candidate) || !_pathIsWithin(root, candidate)) continue;
|
|
73
|
+
// A selected directory inside a sentinel project belongs to the nearest
|
|
74
|
+
// project graph. Explicit registrations and cache keys remain exact roots
|
|
75
|
+
// so registered non-sentinel projects are retained.
|
|
76
|
+
const detected = row.detect ? projectRoot(candidate) : null;
|
|
77
|
+
if (detected) candidate = resolve(detected);
|
|
78
|
+
if (_isFilesystemRootPath(candidate) || !directory(candidate) || !_pathIsWithin(root, candidate)) continue;
|
|
79
|
+
const key = pathKey(candidate);
|
|
80
|
+
if (!found.has(key)) found.set(key, candidate);
|
|
81
|
+
}
|
|
82
|
+
return [...found.values()].sort((a, b) => a.localeCompare(b));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function owningTrustedCodeGraphRoot(file, roots) {
|
|
86
|
+
const matches = (roots || []).filter((root) => _pathIsWithin(root, file));
|
|
87
|
+
matches.sort((a, b) => resolve(b).length - resolve(a).length);
|
|
88
|
+
return matches[0] || null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function formatFederatedProjectLabel(root) {
|
|
92
|
+
return `${basename(root) || root} [${root}]`;
|
|
93
|
+
}
|
|
@@ -27,12 +27,21 @@ try {
|
|
|
27
27
|
if (!cwd) {
|
|
28
28
|
parentPort.postMessage({ ok: false });
|
|
29
29
|
} else {
|
|
30
|
-
const
|
|
30
|
+
const buildOptions = {
|
|
31
31
|
manifest: Array.isArray(workerData.manifest) ? workerData.manifest : null,
|
|
32
32
|
signature: typeof workerData.signature === 'string' ? workerData.signature : null,
|
|
33
|
-
|
|
33
|
+
...(workerData.buildOptions && typeof workerData.buildOptions === 'object'
|
|
34
|
+
? workerData.buildOptions
|
|
35
|
+
: {}),
|
|
36
|
+
};
|
|
37
|
+
const graph = await _buildCodeGraph(cwd, buildOptions);
|
|
34
38
|
if (graph && typeof graph.signature === 'string') {
|
|
35
|
-
_postCodeGraphWorkerSuccess(
|
|
39
|
+
_postCodeGraphWorkerSuccess(
|
|
40
|
+
graph,
|
|
41
|
+
(message) => parentPort.postMessage(message),
|
|
42
|
+
undefined,
|
|
43
|
+
{ cache: buildOptions.cache },
|
|
44
|
+
);
|
|
36
45
|
} else {
|
|
37
46
|
parentPort.postMessage({ ok: false });
|
|
38
47
|
}
|