@danielblomma/cortex-mcp 2.4.0 → 2.4.1
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 +201 -0
- package/README.md +19 -3
- package/bin/cortex.mjs +308 -75
- package/bin/daemon-control.mjs +162 -0
- package/package.json +7 -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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { connect } from "node:net";
|
|
3
|
+
import { tmpdir, userInfo } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
function daemonSocketPath() {
|
|
7
|
+
if (process.env.CORTEX_DAEMON_SOCKET_PATH?.trim()) {
|
|
8
|
+
return process.env.CORTEX_DAEMON_SOCKET_PATH.trim();
|
|
9
|
+
}
|
|
10
|
+
let uid = "anon";
|
|
11
|
+
try {
|
|
12
|
+
const info = userInfo();
|
|
13
|
+
uid =
|
|
14
|
+
typeof info.uid === "number" && info.uid >= 0
|
|
15
|
+
? String(info.uid)
|
|
16
|
+
: info.username || uid;
|
|
17
|
+
} catch {
|
|
18
|
+
// Keep anonymous fallback.
|
|
19
|
+
}
|
|
20
|
+
return join(tmpdir(), `cortex-${uid}.sock`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function callDaemon(
|
|
24
|
+
type,
|
|
25
|
+
payload,
|
|
26
|
+
options = {},
|
|
27
|
+
) {
|
|
28
|
+
const timeoutMs = options.timeoutMs ?? 750;
|
|
29
|
+
const id = randomUUID();
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const socket = connect(daemonSocketPath());
|
|
32
|
+
let buffer = "";
|
|
33
|
+
let settled = false;
|
|
34
|
+
const finish = (result) => {
|
|
35
|
+
if (settled) return;
|
|
36
|
+
settled = true;
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
socket.destroy();
|
|
39
|
+
resolve(result);
|
|
40
|
+
};
|
|
41
|
+
const timer = setTimeout(
|
|
42
|
+
() => finish({ ok: false, error: "timeout" }),
|
|
43
|
+
timeoutMs,
|
|
44
|
+
);
|
|
45
|
+
timer.unref();
|
|
46
|
+
socket.once("connect", () => {
|
|
47
|
+
socket.write(`${JSON.stringify({ id, type, payload })}\n`);
|
|
48
|
+
});
|
|
49
|
+
socket.on("data", (chunk) => {
|
|
50
|
+
buffer += chunk.toString("utf8");
|
|
51
|
+
const newline = buffer.indexOf("\n");
|
|
52
|
+
if (newline < 0) return;
|
|
53
|
+
try {
|
|
54
|
+
const response = JSON.parse(buffer.slice(0, newline));
|
|
55
|
+
if (response.id !== id) {
|
|
56
|
+
finish({ ok: false, error: "invalid_response" });
|
|
57
|
+
} else if (response.ok) {
|
|
58
|
+
finish({ ok: true, result: response.result });
|
|
59
|
+
} else {
|
|
60
|
+
finish({ ok: false, error: response.error ?? "unknown_error" });
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
finish({ ok: false, error: "invalid_response" });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
socket.once("error", () => {
|
|
67
|
+
finish({ ok: false, error: "daemon_unreachable" });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function probeVerifiedDaemon(deps, timeoutMs = 750) {
|
|
73
|
+
const pidFromFile = deps.readPid();
|
|
74
|
+
let response;
|
|
75
|
+
try {
|
|
76
|
+
response = await deps.call("ping", {}, { timeoutMs, autoStart: false });
|
|
77
|
+
} catch {
|
|
78
|
+
response = { ok: false, error: "daemon_unreachable" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const socketPid =
|
|
82
|
+
response?.ok &&
|
|
83
|
+
response.result?.pong === true &&
|
|
84
|
+
Number.isInteger(response.result?.pid) &&
|
|
85
|
+
response.result.pid > 0
|
|
86
|
+
? response.result.pid
|
|
87
|
+
: null;
|
|
88
|
+
if (socketPid && deps.isPidAlive(socketPid)) {
|
|
89
|
+
if (pidFromFile && pidFromFile !== socketPid) {
|
|
90
|
+
return {
|
|
91
|
+
running: true,
|
|
92
|
+
verified: false,
|
|
93
|
+
pid: socketPid,
|
|
94
|
+
reason: "pid_file_socket_mismatch",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
running: true,
|
|
99
|
+
verified: true,
|
|
100
|
+
pid: socketPid,
|
|
101
|
+
reason: null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (pidFromFile && deps.isPidAlive(pidFromFile)) {
|
|
106
|
+
return {
|
|
107
|
+
running: true,
|
|
108
|
+
verified: false,
|
|
109
|
+
pid: pidFromFile,
|
|
110
|
+
reason: socketPid
|
|
111
|
+
? "pid_file_socket_mismatch"
|
|
112
|
+
: "live_pid_without_daemon_handshake",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
running: false,
|
|
117
|
+
verified: false,
|
|
118
|
+
pid: null,
|
|
119
|
+
reason: pidFromFile ? "stale_pid_file" : "not_running",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function stopVerifiedDaemon(
|
|
124
|
+
deps,
|
|
125
|
+
options = {},
|
|
126
|
+
) {
|
|
127
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
128
|
+
const pollMs = options.pollMs ?? 50;
|
|
129
|
+
const probe = await probeVerifiedDaemon(deps);
|
|
130
|
+
if (!probe.running) return { stopped: false, pid: null };
|
|
131
|
+
if (!probe.verified || !probe.pid) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Refusing to signal an unverified process (${probe.reason ?? "unknown identity"}).`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const response = await deps.call(
|
|
138
|
+
"shutdown",
|
|
139
|
+
{},
|
|
140
|
+
{ timeoutMs: Math.min(timeoutMs, 1_000), autoStart: false },
|
|
141
|
+
);
|
|
142
|
+
if (!response?.ok) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Daemon rejected the authenticated shutdown request: ${response?.error ?? "unknown error"}`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const deadline = Date.now() + timeoutMs;
|
|
149
|
+
while (deps.isPidAlive(probe.pid) && Date.now() < deadline) {
|
|
150
|
+
await (options.wait ?? defaultWait)(pollMs);
|
|
151
|
+
}
|
|
152
|
+
if (deps.isPidAlive(probe.pid)) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Verified daemon pid ${probe.pid} did not stop within ${timeoutMs}ms.`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return { stopped: true, pid: probe.pid };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function defaultWait(ms) {
|
|
161
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
162
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielblomma/cortex-mcp",
|
|
3
3
|
"mcpName": "io.github.DanielBlomma/cortex",
|
|
4
|
-
"version": "2.4.
|
|
4
|
+
"version": "2.4.1",
|
|
5
5
|
"description": "Local, repo-scoped context platform for coding assistants. Semantic search, graph relationships, and architectural rule context.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Daniel Blomma",
|
|
@@ -54,22 +54,26 @@
|
|
|
54
54
|
"scaffold/scripts/parsers/dotnet/*/*.csproj",
|
|
55
55
|
"scaffold/scripts/parsers/dotnet/*/Program.cs",
|
|
56
56
|
"scaffold/mcp/src",
|
|
57
|
+
"scaffold/mcp/dist",
|
|
57
58
|
"scaffold/mcp/tests",
|
|
58
59
|
"scaffold/mcp/build.mjs",
|
|
59
60
|
"scaffold/mcp/package.json",
|
|
60
61
|
"scaffold/mcp/package-lock.json",
|
|
61
62
|
"scaffold/mcp/tsconfig.json",
|
|
62
63
|
"README.md",
|
|
64
|
+
"CHANGELOG.md",
|
|
63
65
|
"mcp-registry-submission.json"
|
|
64
66
|
],
|
|
65
67
|
"scripts": {
|
|
68
|
+
"prepack": "npm --prefix scaffold/mcp run build",
|
|
66
69
|
"pretest": "test -d scaffold/scripts/parsers/node_modules || npm --prefix scaffold/scripts/parsers install --no-fund --no-update-notifier --silent",
|
|
67
|
-
"test": "node tests/context-regressions.test.mjs && node --test tests/ingest-units.test.mjs tests/ingest-parallel.test.mjs tests/ingest-worker-crash.test.mjs tests/javascript-parser.test.mjs tests/markdown-parser.test.mjs tests/sql-parser.test.mjs tests/config-parser.test.mjs tests/resources-parser.test.mjs tests/vbnet-parser.test.mjs tests/cpp-parser.test.mjs tests/dashboard.test.mjs tests/init-config.test.mjs tests/init-agents.test.mjs tests/multi-level.test.mjs tests/no-legacy-paths.test.mjs tests/tree-sitter-error-reporting.test.mjs tests/tree-sitter-body-cap.test.mjs tests/tree-sitter-exported.test.mjs tests/tree-sitter-robustness.test.mjs tests/bootstrapbench-run.test.mjs tests/bootstrapbench-stats.test.mjs tests/bootstrapbench-aggregate.test.mjs tests/bootstrapbench-cleanup.test.mjs tests/bootstrapbench-query-packs.test.mjs tests/query-cli-shim.test.mjs tests/session-bootstrap.test.mjs tests/plugin-skills.test.mjs tests/plugin-manifests.test.mjs",
|
|
70
|
+
"test": "node tests/context-regressions.test.mjs && node --test tests/ingest-units.test.mjs tests/ingest-parallel.test.mjs tests/ingest-worker-crash.test.mjs tests/javascript-parser.test.mjs tests/markdown-parser.test.mjs tests/sql-parser.test.mjs tests/config-parser.test.mjs tests/resources-parser.test.mjs tests/vbnet-parser.test.mjs tests/cpp-parser.test.mjs tests/dashboard.test.mjs tests/init-config.test.mjs tests/init-agents.test.mjs tests/multi-level.test.mjs tests/no-legacy-paths.test.mjs tests/tree-sitter-error-reporting.test.mjs tests/tree-sitter-body-cap.test.mjs tests/tree-sitter-exported.test.mjs tests/tree-sitter-robustness.test.mjs tests/bootstrapbench-run.test.mjs tests/bootstrapbench-stats.test.mjs tests/bootstrapbench-aggregate.test.mjs tests/bootstrapbench-cleanup.test.mjs tests/bootstrapbench-query-packs.test.mjs tests/query-cli-shim.test.mjs tests/enterprise-cli-security.test.mjs tests/session-bootstrap.test.mjs tests/plugin-skills.test.mjs tests/plugin-manifests.test.mjs",
|
|
68
71
|
"release:sync-version": "node scripts/sync-release-version.mjs",
|
|
69
72
|
"release:check-version-sync": "node scripts/sync-release-version.mjs --check",
|
|
73
|
+
"audit:dependencies": "npm audit --package-lock-only --audit-level=low --prefix frontend && npm audit --package-lock-only --audit-level=low --prefix scaffold/mcp && npm audit --package-lock-only --audit-level=low --prefix scaffold/scripts/parsers && npm audit --package-lock-only --audit-level=low --prefix scripts/parsers",
|
|
70
74
|
"prepublishOnly": "echo 'Ready to publish to npm'"
|
|
71
75
|
},
|
|
72
76
|
"engines": {
|
|
73
|
-
"node": ">=20"
|
|
77
|
+
"node": ">=20.9.0"
|
|
74
78
|
}
|
|
75
79
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
866649c8d7e3d7b844cf95c614725e8b3b438cd347f13dc2207fe88bb3d5cb89
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { chmodSync, existsSync, mkdirSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { enterpriseCredentialId, isAllowedLicenseEndpoint, verifyLicense, } from "../core/license.js";
|
|
5
|
+
import { claimEnterpriseHostIdentity, inspectEnterpriseHostIdentity, } from "../core/enterprise-host-identity.js";
|
|
6
|
+
import { prepareEnterpriseCredentialRotation } from "../core/enterprise-rotation.js";
|
|
7
|
+
/**
|
|
8
|
+
* One-liner enterprise onboarding.
|
|
9
|
+
*
|
|
10
|
+
* $ printenv CORTEX_API_KEY | cortex enterprise install --api-key-stdin
|
|
11
|
+
*
|
|
12
|
+
* Replaces the manual `.context/enterprise.yml` editing flow that's been
|
|
13
|
+
* the friction point for new users. Validates the key against the
|
|
14
|
+
* license endpoint before writing config — so a typo'd key fails fast
|
|
15
|
+
* with a clear error rather than going silently into community-mode.
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_ENDPOINT = "https://cortex-web-rho.vercel.app";
|
|
18
|
+
const API_KEY_RE = /^(?:ctx|ent)_[A-Za-z0-9._-]{8,}$/;
|
|
19
|
+
function buildEnterpriseYaml(baseUrl, apiKey) {
|
|
20
|
+
const lines = [
|
|
21
|
+
"# Cortex enterprise configuration. Generated by `cortex enterprise install --api-key-stdin`.",
|
|
22
|
+
"# Single api_key is used for telemetry, policy, audit and govern services.",
|
|
23
|
+
"enterprise:",
|
|
24
|
+
` api_key: ${apiKey}`,
|
|
25
|
+
` base_url: ${baseUrl}`,
|
|
26
|
+
"",
|
|
27
|
+
"telemetry:",
|
|
28
|
+
" enabled: true",
|
|
29
|
+
" interval_minutes: 1",
|
|
30
|
+
"",
|
|
31
|
+
"compliance:",
|
|
32
|
+
" frameworks: [iso27001, iso42001, soc2]",
|
|
33
|
+
" eu_addons: false",
|
|
34
|
+
"",
|
|
35
|
+
"govern:",
|
|
36
|
+
" mode: off",
|
|
37
|
+
" sync_on_startup: true",
|
|
38
|
+
" sync_interval_minutes: 60",
|
|
39
|
+
" tier_claude: prevent",
|
|
40
|
+
" tier_codex: prevent",
|
|
41
|
+
" tier_copilot: wrap",
|
|
42
|
+
" detect_ungoverned: true",
|
|
43
|
+
"",
|
|
44
|
+
];
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
}
|
|
47
|
+
export async function runEnterpriseSetup(options) {
|
|
48
|
+
const cwd = options.cwd ?? process.cwd();
|
|
49
|
+
const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
50
|
+
const apiKey = options.apiKey.trim();
|
|
51
|
+
if (!API_KEY_RE.test(apiKey)) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
message: "API key must start with 'ctx_' or 'ent_' followed by at least 8 alphanumeric/._- chars.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (!isAllowedLicenseEndpoint(endpoint)) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
message: "Endpoint must use HTTPS. Plain HTTP is allowed only for localhost loopback development.",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const contextDir = join(cwd, ".context");
|
|
64
|
+
if (!existsSync(contextDir)) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
message: `No .context/ at ${cwd}. Run 'cortex init --bootstrap' first.`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Validate key BEFORE writing config — fail fast, no half-configured state.
|
|
71
|
+
const license = await verifyLicense(contextDir, endpoint, apiKey, {
|
|
72
|
+
client_version: process.env.CORTEX_VERSION,
|
|
73
|
+
});
|
|
74
|
+
if (!license.valid) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
message: `License rejected: ${license.reason} (source=${license.source}). Verify the API key and endpoint are correct.`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Write enterprise.yml.
|
|
81
|
+
const configPath = join(contextDir, "enterprise.yml");
|
|
82
|
+
const temporaryConfigPath = join(contextDir, `.enterprise.yml.${process.pid}.${randomUUID()}.tmp`);
|
|
83
|
+
try {
|
|
84
|
+
mkdirSync(contextDir, { recursive: true });
|
|
85
|
+
writeFileSync(temporaryConfigPath, buildEnterpriseYaml(endpoint, apiKey), {
|
|
86
|
+
encoding: "utf8",
|
|
87
|
+
mode: 0o600,
|
|
88
|
+
flag: "wx",
|
|
89
|
+
});
|
|
90
|
+
chmodSync(temporaryConfigPath, 0o600);
|
|
91
|
+
renameSync(temporaryConfigPath, configPath);
|
|
92
|
+
// Preserve restrictive permissions even when replacing an existing file.
|
|
93
|
+
chmodSync(configPath, 0o600);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
try {
|
|
97
|
+
unlinkSync(temporaryConfigPath);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// The rename may already have completed, or the temp file was not made.
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
message: `Failed to write ${configPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
ok: true,
|
|
109
|
+
message: "Enterprise configuration written.",
|
|
110
|
+
configPath,
|
|
111
|
+
edition: license.edition,
|
|
112
|
+
expiresAt: license.expires_at,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function bindEnterpriseIdentity(options) {
|
|
116
|
+
const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
|
|
117
|
+
const apiKey = options.apiKey.trim();
|
|
118
|
+
if (!API_KEY_RE.test(apiKey) || !isAllowedLicenseEndpoint(endpoint)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const credentialId = enterpriseCredentialId(endpoint, apiKey);
|
|
122
|
+
const identityStatus = inspectEnterpriseHostIdentity(credentialId, endpoint, { homeDir: options.homeDir, owner: options.owner });
|
|
123
|
+
if (identityStatus === "conflict" || identityStatus === "invalid") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (identityStatus === "rotation" &&
|
|
127
|
+
!prepareEnterpriseCredentialRotation(options.homeDir)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return claimEnterpriseHostIdentity(credentialId, endpoint, {
|
|
131
|
+
homeDir: options.homeDir,
|
|
132
|
+
owner: options.owner,
|
|
133
|
+
});
|
|
134
|
+
}
|