@danielblomma/cortex-mcp 2.4.0 → 2.4.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/CHANGELOG.md +226 -0
- package/README.md +24 -3
- package/bin/cli/arguments.mjs +60 -0
- package/bin/cli/context-passthrough.mjs +129 -0
- package/bin/cli/daemon.mjs +157 -0
- package/bin/cli/enterprise.mjs +516 -0
- package/bin/cli/help.mjs +88 -0
- package/bin/cli/hooks.mjs +199 -0
- package/bin/cli/mcp-command.mjs +87 -0
- package/bin/cli/paths.mjs +14 -0
- package/bin/cli/process.mjs +49 -0
- package/bin/cli/project-commands.mjs +237 -0
- package/bin/cli/project-runtime.mjs +34 -0
- package/bin/cli/query-command.mjs +18 -0
- package/bin/cli/router.mjs +66 -0
- package/bin/cli/run-command.mjs +44 -0
- package/bin/cli/scaffold-ownership.mjs +936 -0
- package/bin/cli/scaffold.mjs +687 -0
- package/bin/cli/stage-command.mjs +9 -0
- package/bin/cli/telemetry-command.mjs +18 -0
- package/bin/cli/trusted-runtime.mjs +18 -0
- package/bin/cortex.mjs +12 -1607
- package/bin/daemon-control.mjs +162 -0
- package/mcp-registry-submission.json +1 -1
- package/package.json +8 -3
- package/scaffold/mcp/dist/.cortex-build-hash +1 -0
- package/scaffold/mcp/dist/cli/enterprise-setup.js +134 -0
- package/scaffold/mcp/dist/cli/govern.js +937 -0
- package/scaffold/mcp/dist/cli/query.js +409 -0
- package/scaffold/mcp/dist/cli/run.js +295 -0
- package/scaffold/mcp/dist/cli/stage.js +308 -0
- package/scaffold/mcp/dist/cli/telemetry-test.js +141 -0
- package/scaffold/mcp/dist/cli/ungoverned-detector.js +133 -0
- package/scaffold/mcp/dist/contextEntities.js +282 -0
- package/scaffold/mcp/dist/core/audit/query.js +72 -0
- package/scaffold/mcp/dist/core/audit/writer.js +28 -0
- package/scaffold/mcp/dist/core/config.js +235 -0
- package/scaffold/mcp/dist/core/enterprise-host-identity.js +193 -0
- package/scaffold/mcp/dist/core/enterprise-identity.js +18 -0
- package/scaffold/mcp/dist/core/enterprise-rotation.js +158 -0
- package/scaffold/mcp/dist/core/govern-paths.js +21 -0
- package/scaffold/mcp/dist/core/index.js +14 -0
- package/scaffold/mcp/dist/core/license.js +278 -0
- package/scaffold/mcp/dist/core/policy/enforce.js +66 -0
- package/scaffold/mcp/dist/core/policy/injection.js +172 -0
- package/scaffold/mcp/dist/core/policy/store.js +179 -0
- package/scaffold/mcp/dist/core/rbac/check.js +30 -0
- package/scaffold/mcp/dist/core/secure-endpoint.js +25 -0
- package/scaffold/mcp/dist/core/telemetry/collector.js +285 -0
- package/scaffold/mcp/dist/core/telemetry/state-dir.js +31 -0
- package/scaffold/mcp/dist/core/validators/builtins.js +632 -0
- package/scaffold/mcp/dist/core/validators/config.js +44 -0
- package/scaffold/mcp/dist/core/validators/engine.js +120 -0
- package/scaffold/mcp/dist/core/validators/evaluators/code_comments.js +236 -0
- package/scaffold/mcp/dist/core/validators/evaluators/regex.js +116 -0
- package/scaffold/mcp/dist/core/workflow/artifact-io.js +103 -0
- package/scaffold/mcp/dist/core/workflow/capabilities.js +88 -0
- package/scaffold/mcp/dist/core/workflow/default-workflows.js +102 -0
- package/scaffold/mcp/dist/core/workflow/enforcement.js +164 -0
- package/scaffold/mcp/dist/core/workflow/envelope.js +132 -0
- package/scaffold/mcp/dist/core/workflow/index.js +11 -0
- package/scaffold/mcp/dist/core/workflow/mcp-tools.js +175 -0
- package/scaffold/mcp/dist/core/workflow/resolution.js +69 -0
- package/scaffold/mcp/dist/core/workflow/run-lifecycle.js +123 -0
- package/scaffold/mcp/dist/core/workflow/schemas.js +141 -0
- package/scaffold/mcp/dist/core/workflow/synced-capability-registry.js +60 -0
- package/scaffold/mcp/dist/core/workflow/synced-registry.js +60 -0
- package/scaffold/mcp/dist/daemon/capability-sync-checker.js +242 -0
- package/scaffold/mcp/dist/daemon/client.js +130 -0
- package/scaffold/mcp/dist/daemon/egress-proxy.js +288 -0
- package/scaffold/mcp/dist/daemon/global-host-events.js +222 -0
- package/scaffold/mcp/dist/daemon/heartbeat-pusher.js +116 -0
- package/scaffold/mcp/dist/daemon/heartbeat-tracker.js +165 -0
- package/scaffold/mcp/dist/daemon/host-events-pusher.js +249 -0
- package/scaffold/mcp/dist/daemon/main.js +449 -0
- package/scaffold/mcp/dist/daemon/paths.js +36 -0
- package/scaffold/mcp/dist/daemon/project-service-registry.js +78 -0
- package/scaffold/mcp/dist/daemon/protocol.js +8 -0
- package/scaffold/mcp/dist/daemon/server.js +180 -0
- package/scaffold/mcp/dist/daemon/skill-sync-checker.js +557 -0
- package/scaffold/mcp/dist/daemon/sync-checker.js +165 -0
- package/scaffold/mcp/dist/daemon/ungoverned-scanner.js +136 -0
- package/scaffold/mcp/dist/daemon/workflow-sync-checker.js +249 -0
- package/scaffold/mcp/dist/defaults.js +6 -0
- package/scaffold/mcp/dist/embed.js +627 -0
- package/scaffold/mcp/dist/embedScheduler.js +479 -0
- package/scaffold/mcp/dist/embeddings.js +167 -0
- package/scaffold/mcp/dist/enterprise/audit/push.js +66 -0
- package/scaffold/mcp/dist/enterprise/index.js +327 -0
- package/scaffold/mcp/dist/enterprise/model/deploy.js +27 -0
- package/scaffold/mcp/dist/enterprise/policy/sync.js +129 -0
- package/scaffold/mcp/dist/enterprise/privacy/boundary.js +184 -0
- package/scaffold/mcp/dist/enterprise/reviews/changed-files.js +33 -0
- package/scaffold/mcp/dist/enterprise/reviews/pattern-context.js +202 -0
- package/scaffold/mcp/dist/enterprise/reviews/policy-selection.js +46 -0
- package/scaffold/mcp/dist/enterprise/reviews/push.js +102 -0
- package/scaffold/mcp/dist/enterprise/reviews/trust-state.js +186 -0
- package/scaffold/mcp/dist/enterprise/telemetry/sync.js +57 -0
- package/scaffold/mcp/dist/enterprise/tools/enterprise.js +826 -0
- package/scaffold/mcp/dist/enterprise/tools/harness.js +40 -0
- package/scaffold/mcp/dist/enterprise/tools/walk.js +73 -0
- package/scaffold/mcp/dist/enterprise/violations/push.js +77 -0
- package/scaffold/mcp/dist/enterprise/workflow/push.js +44 -0
- package/scaffold/mcp/dist/enterprise/workflow/state.js +329 -0
- package/scaffold/mcp/dist/frontmatter.js +33 -0
- package/scaffold/mcp/dist/graph.js +769 -0
- package/scaffold/mcp/dist/graphCsv.js +55 -0
- package/scaffold/mcp/dist/graphMetrics.js +8 -0
- package/scaffold/mcp/dist/hooks/permission-request.js +89 -0
- package/scaffold/mcp/dist/hooks/post-tool-use.js +105 -0
- package/scaffold/mcp/dist/hooks/pre-compact.js +29 -0
- package/scaffold/mcp/dist/hooks/pre-tool-use.js +78 -0
- package/scaffold/mcp/dist/hooks/session-end.js +43 -0
- package/scaffold/mcp/dist/hooks/session-start.js +41 -0
- package/scaffold/mcp/dist/hooks/shared.js +194 -0
- package/scaffold/mcp/dist/hooks/stop.js +28 -0
- package/scaffold/mcp/dist/hooks/user-prompt-submit.js +33 -0
- package/scaffold/mcp/dist/impactPresentation.js +137 -0
- package/scaffold/mcp/dist/impactRanking.js +191 -0
- package/scaffold/mcp/dist/impactResponse.js +30 -0
- package/scaffold/mcp/dist/impactResults.js +105 -0
- package/scaffold/mcp/dist/impactSeed.js +20 -0
- package/scaffold/mcp/dist/impactTraversal.js +64 -0
- package/scaffold/mcp/dist/jsonl.js +77 -0
- package/scaffold/mcp/dist/loadGraph.js +759 -0
- package/scaffold/mcp/dist/lruCache.js +38 -0
- package/scaffold/mcp/dist/paths.js +97 -0
- package/scaffold/mcp/dist/patternEvidence.js +272 -0
- package/scaffold/mcp/dist/plugin.js +81 -0
- package/scaffold/mcp/dist/presets.js +78 -0
- package/scaffold/mcp/dist/relatedResponse.js +18 -0
- package/scaffold/mcp/dist/relatedTraversal.js +78 -0
- package/scaffold/mcp/dist/rules.js +23 -0
- package/scaffold/mcp/dist/search.js +212 -0
- package/scaffold/mcp/dist/searchCore.js +457 -0
- package/scaffold/mcp/dist/searchResults.js +230 -0
- package/scaffold/mcp/dist/server.js +317 -0
- package/scaffold/mcp/dist/types.js +1 -0
- package/scaffold/mcp/package-lock.json +321 -200
- package/scaffold/mcp/package.json +15 -6
- package/scaffold/mcp/src/cli/enterprise-setup.ts +82 -8
- package/scaffold/mcp/src/cli/govern.ts +137 -52
- package/scaffold/mcp/src/cli/run.ts +53 -19
- package/scaffold/mcp/src/cli/stage.ts +8 -3
- package/scaffold/mcp/src/core/config.ts +9 -2
- package/scaffold/mcp/src/core/enterprise-host-identity.ts +259 -0
- package/scaffold/mcp/src/core/enterprise-identity.ts +20 -0
- package/scaffold/mcp/src/core/enterprise-rotation.ts +185 -0
- package/scaffold/mcp/src/core/govern-paths.ts +30 -0
- package/scaffold/mcp/src/core/license.ts +186 -17
- package/scaffold/mcp/src/core/secure-endpoint.ts +23 -0
- package/scaffold/mcp/src/core/workflow/enforcement.ts +8 -1
- package/scaffold/mcp/src/core/workflow/mcp-tools.ts +3 -0
- package/scaffold/mcp/src/core/workflow/resolution.ts +9 -1
- package/scaffold/mcp/src/core/workflow/synced-capability-registry.ts +15 -0
- package/scaffold/mcp/src/core/workflow/synced-registry.ts +15 -0
- package/scaffold/mcp/src/daemon/capability-sync-checker.ts +38 -2
- package/scaffold/mcp/src/daemon/egress-proxy.ts +14 -8
- package/scaffold/mcp/src/daemon/global-host-events.ts +288 -0
- package/scaffold/mcp/src/daemon/heartbeat-pusher.ts +4 -0
- package/scaffold/mcp/src/daemon/heartbeat-tracker.ts +2 -0
- package/scaffold/mcp/src/daemon/host-events-pusher.ts +5 -0
- package/scaffold/mcp/src/daemon/main.ts +99 -25
- package/scaffold/mcp/src/daemon/project-service-registry.ts +107 -0
- package/scaffold/mcp/src/daemon/skill-sync-checker.ts +368 -31
- package/scaffold/mcp/src/daemon/sync-checker.ts +4 -0
- package/scaffold/mcp/src/daemon/ungoverned-scanner.ts +57 -25
- package/scaffold/mcp/src/daemon/workflow-sync-checker.ts +41 -2
- package/scaffold/mcp/src/enterprise/audit/push.ts +8 -0
- package/scaffold/mcp/src/enterprise/policy/sync.ts +12 -0
- package/scaffold/mcp/src/enterprise/reviews/push.ts +9 -0
- package/scaffold/mcp/src/enterprise/reviews/trust-state.ts +3 -1
- package/scaffold/mcp/src/enterprise/telemetry/sync.ts +9 -0
- package/scaffold/mcp/src/enterprise/violations/push.ts +9 -0
- package/scaffold/mcp/src/enterprise/workflow/push.ts +7 -0
- package/scaffold/mcp/src/plugin.ts +20 -0
- package/scaffold/mcp/tests/copilot-shim.test.mjs +19 -1
- package/scaffold/mcp/tests/egress-proxy.test.mjs +37 -0
- package/scaffold/mcp/tests/enterprise-identity-sync.test.mjs +401 -0
- package/scaffold/mcp/tests/enterprise-setup.test.mjs +189 -0
- package/scaffold/mcp/tests/global-host-events.test.mjs +81 -0
- package/scaffold/mcp/tests/govern-install.test.mjs +95 -2
- package/scaffold/mcp/tests/govern-repair.test.mjs +20 -3
- package/scaffold/mcp/tests/heartbeat-tracker.test.mjs +26 -0
- package/scaffold/mcp/tests/license.test.mjs +367 -0
- package/scaffold/mcp/tests/project-service-registry.test.mjs +172 -0
- package/scaffold/mcp/tests/review-trust-contract.test.mjs +18 -0
- package/scaffold/mcp/tests/secure-enterprise-endpoint.test.mjs +46 -0
- package/scaffold/mcp/tests/skill-sync-checker.test.mjs +446 -2
- package/scaffold/mcp/tests/ungoverned-scanner.test.mjs +104 -22
- package/scaffold/mcp/tests/workflow-cli.test.mjs +14 -1
- package/scaffold/mcp/tests/workflow-synced-capabilities.test.mjs +37 -0
- package/scaffold/mcp/tests/workflow-synced-registry.test.mjs +40 -1
- package/scaffold/ownership/baseline-v2.4.1.json +22 -0
- package/scaffold/ownership/current.json +4 -0
- package/scaffold/ownership/v1.json +456 -0
- package/scaffold/scripts/ingest-parsers.mjs +1 -387
- package/scaffold/scripts/ingest-worker.mjs +4 -1
- package/scaffold/scripts/ingest.mjs +5 -3899
- package/scaffold/scripts/lib/ingest/arguments.mjs +78 -0
- package/scaffold/scripts/lib/ingest/chunks.mjs +212 -0
- package/scaffold/scripts/lib/ingest/config.mjs +120 -0
- package/scaffold/scripts/lib/ingest/constants.mjs +222 -0
- package/scaffold/scripts/lib/ingest/files.mjs +387 -0
- package/scaffold/scripts/lib/ingest/incremental-state.mjs +131 -0
- package/scaffold/scripts/lib/ingest/io.mjs +78 -0
- package/scaffold/scripts/lib/ingest/main.mjs +76 -0
- package/scaffold/scripts/lib/ingest/parser-composition.mjs +140 -0
- package/scaffold/scripts/lib/ingest/parser-registry.mjs +387 -0
- package/scaffold/scripts/lib/ingest/pipeline-stages.mjs +1625 -0
- package/scaffold/scripts/lib/ingest/projects.mjs +272 -0
- package/scaffold/scripts/lib/ingest/relations.mjs +860 -0
- package/scaffold/scripts/lib/ingest/runtime-paths.mjs +11 -0
- package/scaffold/scripts/lib/ingest/workers.mjs +264 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
export const SCRIPT_DIR = path.resolve(MODULE_DIR, "..", "..");
|
|
6
|
+
export const REPO_ROOT = process.env.CORTEX_PROJECT_ROOT
|
|
7
|
+
? path.resolve(process.env.CORTEX_PROJECT_ROOT)
|
|
8
|
+
: path.resolve(SCRIPT_DIR, "..", "..");
|
|
9
|
+
export const CONTEXT_DIR = path.join(REPO_ROOT, ".context");
|
|
10
|
+
export const CACHE_DIR = path.join(CONTEXT_DIR, "cache");
|
|
11
|
+
export const DB_IMPORT_DIR = path.join(CONTEXT_DIR, "db", "import");
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { Worker } from "node:worker_threads";
|
|
3
|
+
|
|
4
|
+
function resolveIngestWorkerCount(taskCount) {
|
|
5
|
+
const raw = process.env.CORTEX_INGEST_WORKERS;
|
|
6
|
+
const configured = raw !== undefined ? Number.parseInt(raw, 10) : Number.NaN;
|
|
7
|
+
const cpuBudget = Math.max(1, (os.availableParallelism?.() ?? os.cpus().length) - 1);
|
|
8
|
+
const defaultWorkerLimit = taskCount >= 1000 ? 4 : 8;
|
|
9
|
+
const desired =
|
|
10
|
+
Number.isFinite(configured) && configured >= 0
|
|
11
|
+
? configured
|
|
12
|
+
: Math.min(cpuBudget, defaultWorkerLimit);
|
|
13
|
+
if (desired <= 1) return 1;
|
|
14
|
+
// Worker spin-up plus per-worker WASM grammar init dominates on small or
|
|
15
|
+
// incremental runs; stay sequential until there is enough work to amortize.
|
|
16
|
+
if (taskCount < 50) return 1;
|
|
17
|
+
return Math.min(desired, taskCount);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createEmptyWorkerParseStream(tasks, workerCount) {
|
|
21
|
+
const stats = () => ({
|
|
22
|
+
worker_tasks: tasks.length,
|
|
23
|
+
worker_count: Number.isFinite(workerCount) ? workerCount : 0,
|
|
24
|
+
worker_tasks_assigned: 0,
|
|
25
|
+
worker_tasks_settled: 0,
|
|
26
|
+
worker_tasks_unsettled_fallback: tasks.length,
|
|
27
|
+
worker_results: 0,
|
|
28
|
+
worker_results_consumed: 0,
|
|
29
|
+
worker_results_retained: 0,
|
|
30
|
+
worker_results_retained_peak: 0,
|
|
31
|
+
worker_results_pending: 0,
|
|
32
|
+
worker_results_missing: tasks.length,
|
|
33
|
+
worker_waiters: 0
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
hasTask: () => false,
|
|
37
|
+
stats,
|
|
38
|
+
take: async () => undefined,
|
|
39
|
+
drain: async () => stats()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function startWorkerParseStream(tasks, { workerCount, verbose, workerUrl } = {}) {
|
|
44
|
+
if (tasks.length === 0) {
|
|
45
|
+
return createEmptyWorkerParseStream(tasks, workerCount);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resolvedWorkerUrl = workerUrl ?? new URL("../../ingest-worker.mjs", import.meta.url);
|
|
49
|
+
const poolSize = Math.min(workerCount, tasks.length);
|
|
50
|
+
// Defensive: an undefined/0/negative/NaN workerCount yields poolSize < 1, in
|
|
51
|
+
// which case no workers are ever spawned and the pool would never resolve.
|
|
52
|
+
// Return empty so the caller parses everything inline. (Math.min(undefined, n)
|
|
53
|
+
// is NaN, which is why this is `!(>= 1)` rather than `< 1`.)
|
|
54
|
+
if (!(poolSize >= 1)) {
|
|
55
|
+
return createEmptyWorkerParseStream(tasks, workerCount);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const taskIds = new Set(tasks.map((task) => task.id));
|
|
59
|
+
const results = new Map();
|
|
60
|
+
const missingResults = new Set();
|
|
61
|
+
const waiters = new Map();
|
|
62
|
+
const workers = [];
|
|
63
|
+
const inflight = new Map(); // worker -> taskId being parsed, or null when idle
|
|
64
|
+
let nextTask = 0;
|
|
65
|
+
let alive = poolSize;
|
|
66
|
+
let finished = false;
|
|
67
|
+
let resolveDone;
|
|
68
|
+
const done = new Promise((resolve) => {
|
|
69
|
+
resolveDone = resolve;
|
|
70
|
+
});
|
|
71
|
+
const state = {
|
|
72
|
+
assigned: 0,
|
|
73
|
+
settled: 0,
|
|
74
|
+
successful: 0,
|
|
75
|
+
consumed: 0,
|
|
76
|
+
retainedPeak: 0
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const stats = () => ({
|
|
80
|
+
worker_tasks: tasks.length,
|
|
81
|
+
worker_count: poolSize,
|
|
82
|
+
worker_tasks_assigned: state.assigned,
|
|
83
|
+
worker_tasks_settled: state.settled,
|
|
84
|
+
worker_tasks_unsettled_fallback: finished ? Math.max(0, tasks.length - state.settled) : 0,
|
|
85
|
+
worker_results: state.successful,
|
|
86
|
+
worker_results_consumed: state.consumed,
|
|
87
|
+
worker_results_retained: results.size,
|
|
88
|
+
worker_results_retained_peak: state.retainedPeak,
|
|
89
|
+
worker_results_pending: finished ? 0 : Math.max(0, tasks.length - state.settled),
|
|
90
|
+
worker_results_missing: tasks.length - state.successful,
|
|
91
|
+
worker_waiters: waiters.size
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const finish = () => {
|
|
95
|
+
if (finished) return;
|
|
96
|
+
finished = true;
|
|
97
|
+
for (const resolve of waiters.values()) {
|
|
98
|
+
resolve(undefined);
|
|
99
|
+
}
|
|
100
|
+
waiters.clear();
|
|
101
|
+
resolveDone();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// A task is "settled" once it has a result, was skipped, or its worker
|
|
105
|
+
// died holding it. Finish when every task is settled, or when no worker is
|
|
106
|
+
// left alive to make progress (any still-queued tasks then parse inline).
|
|
107
|
+
const maybeFinish = () => {
|
|
108
|
+
if (state.settled >= tasks.length || alive <= 0) {
|
|
109
|
+
finish();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const resolveWaiter = (taskId, result) => {
|
|
114
|
+
const resolve = waiters.get(taskId);
|
|
115
|
+
if (!resolve) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
waiters.delete(taskId);
|
|
119
|
+
if (result !== undefined) {
|
|
120
|
+
state.consumed += 1;
|
|
121
|
+
}
|
|
122
|
+
resolve(result);
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const settleTask = (taskId, result) => {
|
|
127
|
+
state.settled += 1;
|
|
128
|
+
if (result !== undefined) {
|
|
129
|
+
state.successful += 1;
|
|
130
|
+
if (!resolveWaiter(taskId, result)) {
|
|
131
|
+
results.set(taskId, result);
|
|
132
|
+
state.retainedPeak = Math.max(state.retainedPeak, results.size);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
missingResults.add(taskId);
|
|
136
|
+
resolveWaiter(taskId, undefined);
|
|
137
|
+
}
|
|
138
|
+
maybeFinish();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const assign = (worker) => {
|
|
142
|
+
if (nextTask >= tasks.length) {
|
|
143
|
+
inflight.set(worker, null);
|
|
144
|
+
worker.postMessage({ type: "shutdown" });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const task = tasks[nextTask++];
|
|
148
|
+
state.assigned += 1;
|
|
149
|
+
inflight.set(worker, task.id);
|
|
150
|
+
worker.postMessage({
|
|
151
|
+
taskId: task.id,
|
|
152
|
+
ext: task.ext,
|
|
153
|
+
content: task.content,
|
|
154
|
+
absolutePath: task.absolutePath,
|
|
155
|
+
contentLimit: task.contentLimit,
|
|
156
|
+
filePath: task.path
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const onMessage = (worker, message) => {
|
|
161
|
+
if (finished) return;
|
|
162
|
+
inflight.set(worker, null);
|
|
163
|
+
if (message.ok) {
|
|
164
|
+
settleTask(message.taskId, message.result);
|
|
165
|
+
} else {
|
|
166
|
+
if (verbose) {
|
|
167
|
+
console.log(`[ingest] worker skipped ${message.taskId}: ${message.reason}`);
|
|
168
|
+
}
|
|
169
|
+
settleTask(message.taskId, undefined);
|
|
170
|
+
}
|
|
171
|
+
if (!finished) {
|
|
172
|
+
assign(worker);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const onExit = (worker) => {
|
|
177
|
+
if (finished) return;
|
|
178
|
+
alive -= 1;
|
|
179
|
+
const taskId = inflight.get(worker);
|
|
180
|
+
if (taskId != null) {
|
|
181
|
+
// Worker exited mid-parse without posting a result (OOM, native abort,
|
|
182
|
+
// process.exit) and without an 'error' event. Count its in-flight task
|
|
183
|
+
// as settled so it falls back to inline parsing rather than leaving the
|
|
184
|
+
// pool waiting on a dead worker forever.
|
|
185
|
+
if (verbose) {
|
|
186
|
+
console.log(`[ingest] worker exited mid-task ${taskId}; will parse inline`);
|
|
187
|
+
}
|
|
188
|
+
inflight.set(worker, null);
|
|
189
|
+
settleTask(taskId, undefined);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
maybeFinish();
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
for (let i = 0; i < poolSize; i += 1) {
|
|
196
|
+
const worker = new Worker(resolvedWorkerUrl);
|
|
197
|
+
workers.push(worker);
|
|
198
|
+
worker.on("message", (message) => onMessage(worker, message));
|
|
199
|
+
worker.on("error", (error) => {
|
|
200
|
+
// Uncaught exception in the worker. The 'exit' event that always
|
|
201
|
+
// follows does the task accounting (idempotent via inflight), so we
|
|
202
|
+
// only log here to avoid double-counting.
|
|
203
|
+
if (verbose) {
|
|
204
|
+
console.log(`[ingest] worker error: ${error.message}`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
worker.on("exit", () => onExit(worker));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const worker of workers) {
|
|
211
|
+
assign(worker);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
hasTask(taskId) {
|
|
216
|
+
return taskIds.has(taskId);
|
|
217
|
+
},
|
|
218
|
+
stats,
|
|
219
|
+
async take(taskId) {
|
|
220
|
+
if (!taskIds.has(taskId)) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
if (results.has(taskId)) {
|
|
224
|
+
const result = results.get(taskId);
|
|
225
|
+
results.delete(taskId);
|
|
226
|
+
state.consumed += 1;
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
if (missingResults.has(taskId) || finished) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
return new Promise((resolve) => {
|
|
233
|
+
waiters.set(taskId, resolve);
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
async drain() {
|
|
237
|
+
await done;
|
|
238
|
+
await Promise.all(workers.map((worker) => worker.terminate().catch(() => {})));
|
|
239
|
+
return stats();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Compatibility helper for direct worker-pool tests. The ingest pipeline
|
|
245
|
+
// consumes the stream in sorted file-record order and does not retain this map.
|
|
246
|
+
async function parseFilesInWorkers(tasks, options = {}) {
|
|
247
|
+
const stream = startWorkerParseStream(tasks, options);
|
|
248
|
+
const results = new Map();
|
|
249
|
+
for (const task of tasks) {
|
|
250
|
+
const result = await stream.take(task.id);
|
|
251
|
+
if (result) {
|
|
252
|
+
results.set(task.id, result);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await stream.drain();
|
|
257
|
+
return results;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export {
|
|
261
|
+
parseFilesInWorkers,
|
|
262
|
+
resolveIngestWorkerCount,
|
|
263
|
+
startWorkerParseStream
|
|
264
|
+
};
|