@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
package/bin/cortex.mjs
CHANGED
|
@@ -5,6 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { normalizeProjectRoot } from "./wsl.mjs";
|
|
8
|
+
import {
|
|
9
|
+
callDaemon,
|
|
10
|
+
probeVerifiedDaemon,
|
|
11
|
+
stopVerifiedDaemon,
|
|
12
|
+
} from "./daemon-control.mjs";
|
|
8
13
|
import {
|
|
9
14
|
bullet,
|
|
10
15
|
printBullet,
|
|
@@ -88,14 +93,14 @@ function printHelp() {
|
|
|
88
93
|
console.log(helpRow("watch [start|stop|status|run|once]", "Background sync (--interval, --debounce, --mode)"));
|
|
89
94
|
|
|
90
95
|
console.log(helpSection("GOVERNANCE"));
|
|
91
|
-
console.log(helpRow("enterprise
|
|
96
|
+
console.log(helpRow("enterprise install --api-key-stdin", "Install enforcement + hooks + daemon (sudo)"));
|
|
92
97
|
console.log(helpRow(" ", "[--endpoint <url>] [--frameworks <csv>] [--no-hooks] [--no-daemon]"));
|
|
93
98
|
console.log(helpRow("enterprise status", "Show local enforcement state"));
|
|
94
99
|
console.log(helpRow("enterprise sync", "Force re-fetch + re-apply (sudo)"));
|
|
95
100
|
console.log(helpRow("enterprise uninstall", "Remove enforcement (sudo, --break-glass --reason)"));
|
|
96
101
|
console.log(helpRow("enterprise repair", "Verify managed paths, clear tamper-lock (sudo)"));
|
|
97
102
|
console.log(helpRow("run <claude|codex|copilot> [args...]", "Wrap an AI CLI in cortex enforcement"));
|
|
98
|
-
console.log(helpRow("daemon [start|stop|status]", "Local supervisor daemon"));
|
|
103
|
+
console.log(helpRow("daemon [start|stop|restart|status]", "Local supervisor daemon"));
|
|
99
104
|
console.log(helpRow("hooks [install|uninstall|status] [--project]", "Claude Code hooks"));
|
|
100
105
|
console.log(helpRow("telemetry test", "Smoke-test the push pipeline"));
|
|
101
106
|
|
|
@@ -284,6 +289,31 @@ function initializeScaffold(targetDir, force) {
|
|
|
284
289
|
}
|
|
285
290
|
}
|
|
286
291
|
|
|
292
|
+
function hardenEnterpriseConfigPermissions(targetDir) {
|
|
293
|
+
const contextDir = path.join(targetDir, ".context");
|
|
294
|
+
for (const filename of ["enterprise.yml", "enterprise.yaml"]) {
|
|
295
|
+
const configPath = path.join(contextDir, filename);
|
|
296
|
+
let stat;
|
|
297
|
+
try {
|
|
298
|
+
stat = fs.lstatSync(configPath);
|
|
299
|
+
} catch (err) {
|
|
300
|
+
if (err && typeof err === "object" && err.code === "ENOENT") continue;
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
303
|
+
if (stat.isSymbolicLink()) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
`Refusing symlinked Enterprise configuration: ${configPath}`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (!stat.isFile()) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Enterprise configuration is not a regular file: ${configPath}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
fs.chmodSync(configPath, 0o600);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
287
317
|
function ensurePathWritable(targetPath, force) {
|
|
288
318
|
if (!fs.existsSync(targetPath)) {
|
|
289
319
|
return;
|
|
@@ -851,6 +881,7 @@ async function run() {
|
|
|
851
881
|
printBanner("Cortex initializes repo-scoped context for AI coding agents.");
|
|
852
882
|
fs.mkdirSync(target, { recursive: true });
|
|
853
883
|
initializeScaffold(target, force);
|
|
884
|
+
hardenEnterpriseConfigPermissions(target);
|
|
854
885
|
const helpers = installAssistantHelpers(target);
|
|
855
886
|
await maybeInstallGitHooks(target);
|
|
856
887
|
|
|
@@ -891,6 +922,7 @@ async function run() {
|
|
|
891
922
|
|
|
892
923
|
if (bootstrap) {
|
|
893
924
|
await runContextCommand(target, ["bootstrap"]);
|
|
925
|
+
await restartDaemonAfterRuntimeUpgrade(target);
|
|
894
926
|
}
|
|
895
927
|
|
|
896
928
|
if (connect) {
|
|
@@ -981,23 +1013,34 @@ async function run() {
|
|
|
981
1013
|
}
|
|
982
1014
|
|
|
983
1015
|
await maybeMigrateScaffold(process.cwd(), command);
|
|
1016
|
+
if (command === "bootstrap") {
|
|
1017
|
+
hardenEnterpriseConfigPermissions(process.cwd());
|
|
1018
|
+
}
|
|
984
1019
|
await runContextCommand(process.cwd(), [command, ...rest]);
|
|
1020
|
+
if (command === "bootstrap") {
|
|
1021
|
+
await restartDaemonAfterRuntimeUpgrade(process.cwd());
|
|
1022
|
+
}
|
|
985
1023
|
}
|
|
986
1024
|
|
|
987
1025
|
// ---------------------------------------------------------------------------
|
|
988
1026
|
// v2.0.0: daemon + hooks commands
|
|
989
1027
|
// ---------------------------------------------------------------------------
|
|
990
1028
|
|
|
991
|
-
|
|
992
|
-
|
|
1029
|
+
function daemonDirPath() {
|
|
1030
|
+
return path.join(process.env.HOME || os.homedir(), ".cortex");
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function daemonPidFilePath() {
|
|
1034
|
+
return path.join(daemonDirPath(), "daemon.pid");
|
|
1035
|
+
}
|
|
993
1036
|
|
|
994
1037
|
function pidFileExists() {
|
|
995
|
-
return fs.existsSync(
|
|
1038
|
+
return fs.existsSync(daemonPidFilePath());
|
|
996
1039
|
}
|
|
997
1040
|
|
|
998
1041
|
function readPid() {
|
|
999
1042
|
try {
|
|
1000
|
-
const raw = fs.readFileSync(
|
|
1043
|
+
const raw = fs.readFileSync(daemonPidFilePath(), "utf8").trim();
|
|
1001
1044
|
const pid = Number.parseInt(raw, 10);
|
|
1002
1045
|
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
1003
1046
|
} catch {
|
|
@@ -1018,20 +1061,23 @@ function isPidAlive(pid) {
|
|
|
1018
1061
|
}
|
|
1019
1062
|
}
|
|
1020
1063
|
|
|
1021
|
-
function resolveProjectRuntimeDist() {
|
|
1064
|
+
function resolveProjectRuntimeDist(projectRoot) {
|
|
1022
1065
|
// v2.0.5: project layout was moved from <cwd>/mcp/ to <cwd>/.context/mcp/.
|
|
1023
1066
|
// The runtime still lives there for compatibility, but CLI commands now
|
|
1024
1067
|
// treat it as the local context runtime rather than an MCP-only surface.
|
|
1025
|
-
const target =
|
|
1068
|
+
const target =
|
|
1069
|
+
projectRoot ||
|
|
1070
|
+
process.env.CORTEX_PROJECT_ROOT?.trim() ||
|
|
1071
|
+
process.cwd();
|
|
1026
1072
|
return path.join(target, CONTEXT_RUNTIME_REL, "dist");
|
|
1027
1073
|
}
|
|
1028
1074
|
|
|
1029
|
-
function resolveProjectMcpDist() {
|
|
1030
|
-
return resolveProjectRuntimeDist();
|
|
1075
|
+
function resolveProjectMcpDist(projectRoot) {
|
|
1076
|
+
return resolveProjectRuntimeDist(projectRoot);
|
|
1031
1077
|
}
|
|
1032
1078
|
|
|
1033
|
-
function resolveDaemonEntry() {
|
|
1034
|
-
return path.join(resolveProjectRuntimeDist(), "daemon", "main.js");
|
|
1079
|
+
function resolveDaemonEntry(projectRoot) {
|
|
1080
|
+
return path.join(resolveProjectRuntimeDist(projectRoot), "daemon", "main.js");
|
|
1035
1081
|
}
|
|
1036
1082
|
|
|
1037
1083
|
function resolveHookEntry(name) {
|
|
@@ -1042,54 +1088,116 @@ function resolveCliEntry(name) {
|
|
|
1042
1088
|
return path.join(resolveProjectRuntimeDist(), "cli", `${name}.js`);
|
|
1043
1089
|
}
|
|
1044
1090
|
|
|
1045
|
-
|
|
1091
|
+
function resolveTrustedCliEntry(name) {
|
|
1092
|
+
return path.join(SCAFFOLD_ROOT, "mcp", "dist", "cli", `${name}.js`);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
async function daemonControlDeps() {
|
|
1096
|
+
return {
|
|
1097
|
+
readPid,
|
|
1098
|
+
isPidAlive,
|
|
1099
|
+
call: callDaemon,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
async function waitForVerifiedDaemon(deps, timeoutMs = 5_000) {
|
|
1104
|
+
const deadline = Date.now() + timeoutMs;
|
|
1105
|
+
while (Date.now() < deadline) {
|
|
1106
|
+
const probe = await probeVerifiedDaemon(deps, 250);
|
|
1107
|
+
if (probe.verified) return probe;
|
|
1108
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1109
|
+
}
|
|
1110
|
+
return probeVerifiedDaemon(deps, 250);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
async function runDaemonCommand(args, options = {}) {
|
|
1046
1114
|
const sub = args[0] || "status";
|
|
1115
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
1116
|
+
const deps = await daemonControlDeps();
|
|
1047
1117
|
if (sub === "start") {
|
|
1048
|
-
|
|
1118
|
+
const existing = await probeVerifiedDaemon(deps);
|
|
1119
|
+
if (existing.verified) {
|
|
1049
1120
|
console.log("Daemon already running.");
|
|
1050
1121
|
return;
|
|
1051
1122
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1123
|
+
if (existing.running) {
|
|
1124
|
+
throw new Error(
|
|
1125
|
+
`Refusing to replace unverified live pid ${existing.pid} (${existing.reason}).`,
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
const daemonDir = daemonDirPath();
|
|
1129
|
+
fs.mkdirSync(daemonDir, { recursive: true });
|
|
1130
|
+
const entry = resolveDaemonEntry(projectRoot);
|
|
1054
1131
|
if (!fs.existsSync(entry)) {
|
|
1055
1132
|
throw new Error(`Daemon entry not found: ${entry}. Build cortex first.`);
|
|
1056
1133
|
}
|
|
1057
|
-
const logFd = fs.openSync(path.join(
|
|
1134
|
+
const logFd = fs.openSync(path.join(daemonDir, "daemon.log"), "a");
|
|
1058
1135
|
const child = spawn(process.execPath, [entry], {
|
|
1059
1136
|
detached: true,
|
|
1060
1137
|
stdio: ["ignore", logFd, logFd],
|
|
1138
|
+
cwd: projectRoot,
|
|
1139
|
+
env: { ...process.env, CORTEX_PROJECT_ROOT: projectRoot },
|
|
1061
1140
|
});
|
|
1062
1141
|
child.unref();
|
|
1063
|
-
|
|
1142
|
+
fs.closeSync(logFd);
|
|
1143
|
+
const started = await waitForVerifiedDaemon(deps);
|
|
1144
|
+
if (!started.verified) {
|
|
1145
|
+
throw new Error(
|
|
1146
|
+
`Daemon did not complete its verified socket handshake (${started.reason}).`,
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
console.log(`Daemon started (pid=${started.pid}). Log: ${path.join(daemonDir, "daemon.log")}`);
|
|
1064
1150
|
return;
|
|
1065
1151
|
}
|
|
1066
1152
|
if (sub === "stop") {
|
|
1067
|
-
const
|
|
1068
|
-
if (!
|
|
1153
|
+
const result = await stopVerifiedDaemon(deps);
|
|
1154
|
+
if (!result.stopped) {
|
|
1069
1155
|
console.log("Daemon not running.");
|
|
1070
1156
|
return;
|
|
1071
1157
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1158
|
+
console.log(`Daemon stopped (verified pid=${result.pid}).`);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (sub === "restart") {
|
|
1162
|
+
await stopVerifiedDaemon(deps);
|
|
1163
|
+
await runDaemonCommand(["start"], { projectRoot });
|
|
1078
1164
|
return;
|
|
1079
1165
|
}
|
|
1080
1166
|
if (sub === "status") {
|
|
1081
|
-
const
|
|
1082
|
-
if (
|
|
1083
|
-
console.log(`Daemon running (pid=${pid})`);
|
|
1167
|
+
const probe = await probeVerifiedDaemon(deps);
|
|
1168
|
+
if (probe.verified) {
|
|
1169
|
+
console.log(`Daemon running (verified pid=${probe.pid})`);
|
|
1170
|
+
} else if (probe.running) {
|
|
1171
|
+
console.log(
|
|
1172
|
+
`Daemon state unsafe: live pid=${probe.pid}, identity not verified (${probe.reason}).`,
|
|
1173
|
+
);
|
|
1084
1174
|
} else {
|
|
1085
1175
|
console.log("Daemon not running.");
|
|
1086
1176
|
if (pidFileExists()) {
|
|
1087
|
-
console.log(`(stale pid file at ${
|
|
1177
|
+
console.log(`(stale pid file at ${daemonPidFilePath()})`);
|
|
1088
1178
|
}
|
|
1089
1179
|
}
|
|
1090
1180
|
return;
|
|
1091
1181
|
}
|
|
1092
|
-
throw new Error(`Unknown daemon subcommand: ${sub}. Try start|stop|status`);
|
|
1182
|
+
throw new Error(`Unknown daemon subcommand: ${sub}. Try start|stop|restart|status`);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
async function restartDaemonAfterRuntimeUpgrade(projectRoot) {
|
|
1186
|
+
const daemonEntry = resolveDaemonEntry(projectRoot);
|
|
1187
|
+
if (!fs.existsSync(daemonEntry)) return;
|
|
1188
|
+
const deps = await daemonControlDeps();
|
|
1189
|
+
const probe = await probeVerifiedDaemon(deps);
|
|
1190
|
+
if (!probe.running) return;
|
|
1191
|
+
if (!probe.verified) {
|
|
1192
|
+
throw new Error(
|
|
1193
|
+
`Runtime upgraded, but live pid ${probe.pid} could not be verified. ` +
|
|
1194
|
+
"Refusing to signal it; stop it manually after confirming ownership.",
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
console.log(
|
|
1198
|
+
`[cortex] restarting verified daemon pid=${probe.pid} to activate the upgraded runtime`,
|
|
1199
|
+
);
|
|
1200
|
+
await runDaemonCommand(["restart"], { projectRoot });
|
|
1093
1201
|
}
|
|
1094
1202
|
|
|
1095
1203
|
async function runHookShim(args) {
|
|
@@ -1182,7 +1290,8 @@ function writeJson(file, data) {
|
|
|
1182
1290
|
}
|
|
1183
1291
|
|
|
1184
1292
|
// Enterprise == govern. One command, sudo-elevated, hard-fail without it.
|
|
1185
|
-
// `cortex enterprise
|
|
1293
|
+
// `cortex enterprise install --api-key-stdin` does the full install.
|
|
1294
|
+
// Subcommands status/sync/uninstall
|
|
1186
1295
|
// dispatch to scaffold/mcp/dist/cli/govern.js.
|
|
1187
1296
|
|
|
1188
1297
|
function requireSudoElevation() {
|
|
@@ -1220,11 +1329,58 @@ function dropPrivileges(sudo) {
|
|
|
1220
1329
|
return sudoInfo.homedir;
|
|
1221
1330
|
}
|
|
1222
1331
|
|
|
1332
|
+
async function runAsSudoUser(sudo, operation) {
|
|
1333
|
+
const required = [
|
|
1334
|
+
"geteuid",
|
|
1335
|
+
"getegid",
|
|
1336
|
+
"seteuid",
|
|
1337
|
+
"setegid",
|
|
1338
|
+
"getgroups",
|
|
1339
|
+
"setgroups",
|
|
1340
|
+
];
|
|
1341
|
+
for (const name of required) {
|
|
1342
|
+
if (typeof process[name] !== "function") {
|
|
1343
|
+
throw new Error(
|
|
1344
|
+
`Secure Enterprise installation requires process.${name} support.`,
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
const previous = {
|
|
1350
|
+
euid: process.geteuid(),
|
|
1351
|
+
egid: process.getegid(),
|
|
1352
|
+
groups: process.getgroups(),
|
|
1353
|
+
home: process.env.HOME,
|
|
1354
|
+
user: process.env.USER,
|
|
1355
|
+
logname: process.env.LOGNAME,
|
|
1356
|
+
};
|
|
1357
|
+
const sudoInfo = os.userInfo({ uid: sudo.uid });
|
|
1358
|
+
try {
|
|
1359
|
+
process.setgroups([sudo.gid]);
|
|
1360
|
+
process.setegid(sudo.gid);
|
|
1361
|
+
process.seteuid(sudo.uid);
|
|
1362
|
+
process.env.HOME = sudoInfo.homedir;
|
|
1363
|
+
process.env.USER = sudo.user;
|
|
1364
|
+
process.env.LOGNAME = sudo.user;
|
|
1365
|
+
return await operation();
|
|
1366
|
+
} finally {
|
|
1367
|
+
process.seteuid(previous.euid);
|
|
1368
|
+
process.setegid(previous.egid);
|
|
1369
|
+
process.setgroups(previous.groups);
|
|
1370
|
+
if (previous.home === undefined) delete process.env.HOME;
|
|
1371
|
+
else process.env.HOME = previous.home;
|
|
1372
|
+
if (previous.user === undefined) delete process.env.USER;
|
|
1373
|
+
else process.env.USER = previous.user;
|
|
1374
|
+
if (previous.logname === undefined) delete process.env.LOGNAME;
|
|
1375
|
+
else process.env.LOGNAME = previous.logname;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1223
1379
|
function loadGovernModule() {
|
|
1224
|
-
const entry =
|
|
1380
|
+
const entry = resolveTrustedCliEntry("govern");
|
|
1225
1381
|
if (!fs.existsSync(entry)) {
|
|
1226
1382
|
throw new Error(
|
|
1227
|
-
`
|
|
1383
|
+
`The installed Cortex package is missing its trusted Enterprise runtime (${entry}). Reinstall Cortex.`
|
|
1228
1384
|
);
|
|
1229
1385
|
}
|
|
1230
1386
|
return import(pathToFileURL(entry).href);
|
|
@@ -1233,10 +1389,15 @@ function loadGovernModule() {
|
|
|
1233
1389
|
const ENTERPRISE_SUBCOMMANDS = new Set(["status", "sync", "uninstall", "repair", "help", "--help", "-h"]);
|
|
1234
1390
|
|
|
1235
1391
|
async function runEnterpriseCommand(args) {
|
|
1392
|
+
if (args[0] === "install") {
|
|
1393
|
+
return runEnterpriseInstall(args.slice(1));
|
|
1394
|
+
}
|
|
1236
1395
|
if (args.length === 0 || ENTERPRISE_SUBCOMMANDS.has(args[0])) {
|
|
1237
1396
|
return runEnterpriseSubcommand(args);
|
|
1238
1397
|
}
|
|
1239
|
-
|
|
1398
|
+
throw new Error(
|
|
1399
|
+
"Positional enterprise API keys are not accepted. Use 'cortex enterprise install --api-key-stdin'.",
|
|
1400
|
+
);
|
|
1240
1401
|
}
|
|
1241
1402
|
|
|
1242
1403
|
async function runEnterpriseSubcommand(args) {
|
|
@@ -1244,13 +1405,14 @@ async function runEnterpriseSubcommand(args) {
|
|
|
1244
1405
|
|
|
1245
1406
|
if (sub === "help" || sub === "--help" || sub === "-h" || !sub) {
|
|
1246
1407
|
console.log(gradient("cortex enterprise") + muted(" · governance, armed."));
|
|
1247
|
-
console.log(helpRow("enterprise
|
|
1408
|
+
console.log(helpRow("enterprise install --api-key-stdin", "Install (sudo). Managed enforcement + hooks + daemon."));
|
|
1248
1409
|
console.log(helpRow(" ", "[--endpoint <url>] [--frameworks <csv>] [--no-hooks] [--no-daemon]"));
|
|
1249
1410
|
console.log(helpRow("enterprise status [--verbose|--json]", "Show local enforcement state"));
|
|
1250
1411
|
console.log(helpRow("enterprise sync", "Force re-fetch + re-apply (sudo)"));
|
|
1251
1412
|
console.log(helpRow("enterprise uninstall", "Remove. [--break-glass --reason \"<text>\"] in enforced mode (sudo)"));
|
|
1252
1413
|
console.log(helpRow("enterprise repair", "Verify managed paths, clear .cortex-tamper.lock (sudo)"));
|
|
1253
1414
|
console.log("");
|
|
1415
|
+
console.log(muted("Example: printf '%s\\n' \"$CORTEX_API_KEY\" | sudo cortex enterprise install --api-key-stdin"));
|
|
1254
1416
|
console.log(muted("Default endpoint: https://cortex-web-rho.vercel.app"));
|
|
1255
1417
|
return;
|
|
1256
1418
|
}
|
|
@@ -1271,9 +1433,14 @@ async function runEnterpriseSubcommand(args) {
|
|
|
1271
1433
|
}
|
|
1272
1434
|
|
|
1273
1435
|
if (sub === "sync") {
|
|
1274
|
-
requireSudoElevation();
|
|
1436
|
+
const sudo = requireSudoElevation();
|
|
1275
1437
|
const mod = await loadGovernModule();
|
|
1276
|
-
|
|
1438
|
+
const projectOperation = (operation) => runAsSudoUser(sudo, operation);
|
|
1439
|
+
await mod.runGovernSync({
|
|
1440
|
+
cwd: process.cwd(),
|
|
1441
|
+
projectOperation,
|
|
1442
|
+
});
|
|
1443
|
+
dropPrivileges(sudo);
|
|
1277
1444
|
return;
|
|
1278
1445
|
}
|
|
1279
1446
|
|
|
@@ -1289,14 +1456,17 @@ async function runEnterpriseSubcommand(args) {
|
|
|
1289
1456
|
throw new Error(`Unknown enterprise uninstall option: ${args[i]}`);
|
|
1290
1457
|
}
|
|
1291
1458
|
}
|
|
1292
|
-
requireSudoElevation();
|
|
1459
|
+
const sudo = requireSudoElevation();
|
|
1293
1460
|
const mod = await loadGovernModule();
|
|
1461
|
+
const projectOperation = (operation) => runAsSudoUser(sudo, operation);
|
|
1294
1462
|
const result = await mod.runGovernUninstall({
|
|
1295
1463
|
cli: "all",
|
|
1296
1464
|
breakGlass,
|
|
1297
1465
|
reason,
|
|
1298
1466
|
cwd: process.cwd(),
|
|
1467
|
+
projectOperation,
|
|
1299
1468
|
});
|
|
1469
|
+
dropPrivileges(sudo);
|
|
1300
1470
|
if (!result.ok) {
|
|
1301
1471
|
printBullet("fail", result.message, process.stderr);
|
|
1302
1472
|
process.exit(1);
|
|
@@ -1315,9 +1485,15 @@ async function runEnterpriseSubcommand(args) {
|
|
|
1315
1485
|
throw new Error(`Unknown enterprise repair option: ${args[i]}`);
|
|
1316
1486
|
}
|
|
1317
1487
|
}
|
|
1318
|
-
requireSudoElevation();
|
|
1488
|
+
const sudo = requireSudoElevation();
|
|
1319
1489
|
const mod = await loadGovernModule();
|
|
1320
|
-
const
|
|
1490
|
+
const projectOperation = (operation) => runAsSudoUser(sudo, operation);
|
|
1491
|
+
const result = await mod.runGovernRepair({
|
|
1492
|
+
cwd: process.cwd(),
|
|
1493
|
+
reason,
|
|
1494
|
+
projectOperation,
|
|
1495
|
+
});
|
|
1496
|
+
dropPrivileges(sudo);
|
|
1321
1497
|
if (!result.ok) {
|
|
1322
1498
|
printBullet("fail", result.message, process.stderr);
|
|
1323
1499
|
process.exit(1);
|
|
@@ -1329,14 +1505,19 @@ async function runEnterpriseSubcommand(args) {
|
|
|
1329
1505
|
throw new Error(`Unknown enterprise subcommand: ${sub}`);
|
|
1330
1506
|
}
|
|
1331
1507
|
|
|
1332
|
-
async function runEnterpriseInstall(args) {
|
|
1333
|
-
|
|
1508
|
+
async function runEnterpriseInstall(args, injected = {}) {
|
|
1509
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
1510
|
+
return runEnterpriseSubcommand(["help"]);
|
|
1511
|
+
}
|
|
1512
|
+
let readKeyFromStdin = false;
|
|
1334
1513
|
let endpoint;
|
|
1335
1514
|
let frameworks;
|
|
1336
1515
|
let installHooks = true;
|
|
1337
1516
|
let startDaemon = true;
|
|
1338
|
-
for (let i =
|
|
1339
|
-
if (args[i] === "--
|
|
1517
|
+
for (let i = 0; i < args.length; i++) {
|
|
1518
|
+
if (args[i] === "--api-key-stdin") {
|
|
1519
|
+
readKeyFromStdin = true;
|
|
1520
|
+
} else if (args[i] === "--endpoint" && args[i + 1]) {
|
|
1340
1521
|
endpoint = args[i + 1];
|
|
1341
1522
|
i++;
|
|
1342
1523
|
} else if (args[i] === "--frameworks" && args[i + 1]) {
|
|
@@ -1348,23 +1529,61 @@ async function runEnterpriseInstall(args) {
|
|
|
1348
1529
|
startDaemon = false;
|
|
1349
1530
|
} else if (args[i].startsWith("-")) {
|
|
1350
1531
|
throw new Error(`Unknown enterprise install option: ${args[i]}`);
|
|
1532
|
+
} else {
|
|
1533
|
+
throw new Error(
|
|
1534
|
+
"Positional enterprise API keys are not accepted. Use --api-key-stdin.",
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
if (!readKeyFromStdin) {
|
|
1540
|
+
throw new Error(
|
|
1541
|
+
"Enterprise installation requires --api-key-stdin so the key is not exposed in process arguments.",
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
const secretInput = injected.stdin ?? process.stdin;
|
|
1545
|
+
if (secretInput.isTTY) {
|
|
1546
|
+
throw new Error(
|
|
1547
|
+
"Read the enterprise API key from a pipe or redirected stdin; interactive echo is disabled for secrets.",
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
let apiKey = "";
|
|
1551
|
+
for await (const chunk of secretInput) {
|
|
1552
|
+
apiKey += chunk.toString();
|
|
1553
|
+
if (apiKey.length > 4096) {
|
|
1554
|
+
throw new Error("Enterprise API key input is too large.");
|
|
1351
1555
|
}
|
|
1352
1556
|
}
|
|
1557
|
+
apiKey = apiKey.trim();
|
|
1558
|
+
if (!apiKey || apiKey.includes("\n") || apiKey.includes("\r")) {
|
|
1559
|
+
throw new Error("Expected exactly one enterprise API key on stdin.");
|
|
1560
|
+
}
|
|
1353
1561
|
|
|
1354
|
-
const sudo = requireSudoElevation();
|
|
1562
|
+
const sudo = (injected.requireSudoElevation ?? requireSudoElevation)();
|
|
1563
|
+
const projectOperation = (operation) =>
|
|
1564
|
+
(injected.runAsSudoUser ?? runAsSudoUser)(sudo, operation);
|
|
1355
1565
|
|
|
1356
1566
|
process.stdout.write(headerBanner({ tagline: " Cortex enterprise — activating governance" }));
|
|
1357
1567
|
|
|
1358
|
-
|
|
1359
|
-
if (!
|
|
1360
|
-
|
|
1361
|
-
|
|
1568
|
+
let enterpriseMod = injected.enterpriseMod;
|
|
1569
|
+
if (!enterpriseMod) {
|
|
1570
|
+
const enterpriseEntry = resolveTrustedCliEntry("enterprise-setup");
|
|
1571
|
+
if (!fs.existsSync(enterpriseEntry)) {
|
|
1572
|
+
printBullet("fail", `The installed Cortex package is missing its trusted Enterprise runtime (${enterpriseEntry}). Reinstall Cortex.`);
|
|
1573
|
+
process.exit(1);
|
|
1574
|
+
}
|
|
1575
|
+
enterpriseMod = await import(pathToFileURL(enterpriseEntry).href);
|
|
1362
1576
|
}
|
|
1363
|
-
const enterpriseMod = await import(pathToFileURL(enterpriseEntry).href);
|
|
1364
1577
|
|
|
1365
1578
|
// Step 1 — Initializing Cortex core (license validation + enterprise.yml).
|
|
1366
1579
|
const step1 = spinner("Initializing Cortex core");
|
|
1367
|
-
const setupResult = await
|
|
1580
|
+
const setupResult = await projectOperation(
|
|
1581
|
+
() => enterpriseMod.runEnterpriseSetup({
|
|
1582
|
+
apiKey,
|
|
1583
|
+
endpoint,
|
|
1584
|
+
cwd: process.cwd(),
|
|
1585
|
+
}),
|
|
1586
|
+
);
|
|
1368
1587
|
if (!setupResult.ok) {
|
|
1369
1588
|
step1.stop("fail", `Initializing Cortex core — ${setupResult.message}`);
|
|
1370
1589
|
process.exit(1);
|
|
@@ -1372,24 +1591,40 @@ async function runEnterpriseInstall(args) {
|
|
|
1372
1591
|
step1.stop("ok", `Initializing Cortex core — license ${setupResult.edition}, expires ${setupResult.expiresAt}`);
|
|
1373
1592
|
printBullet("info", muted(`config: ${setupResult.configPath}`));
|
|
1374
1593
|
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1594
|
+
const sudoHome =
|
|
1595
|
+
injected.sudoHome ??
|
|
1596
|
+
os.userInfo({ uid: sudo.uid }).homedir;
|
|
1597
|
+
const bindEnterpriseIdentity =
|
|
1598
|
+
injected.bindEnterpriseIdentity ??
|
|
1599
|
+
enterpriseMod.bindEnterpriseIdentity;
|
|
1600
|
+
if (
|
|
1601
|
+
typeof bindEnterpriseIdentity !== "function" ||
|
|
1602
|
+
!await projectOperation(
|
|
1603
|
+
() => bindEnterpriseIdentity({
|
|
1604
|
+
apiKey,
|
|
1605
|
+
endpoint,
|
|
1606
|
+
homeDir: sudoHome,
|
|
1607
|
+
}),
|
|
1608
|
+
)
|
|
1609
|
+
) {
|
|
1610
|
+
throw new Error(
|
|
1611
|
+
"Could not bind this user profile to the verified Enterprise identity. " +
|
|
1612
|
+
"A different endpoint is already enrolled; use a separate OS user boundary.",
|
|
1613
|
+
);
|
|
1380
1614
|
}
|
|
1381
1615
|
|
|
1382
1616
|
// Step 2 — Loading policy engine (govern install: managed config + frameworks).
|
|
1383
1617
|
const baseUrl = (endpoint ?? "https://cortex-web-rho.vercel.app").replace(/\/$/, "");
|
|
1384
1618
|
const step2 = spinner("Loading policy engine");
|
|
1385
|
-
const governMod = await loadGovernModule();
|
|
1619
|
+
const governMod = injected.governMod ?? await loadGovernModule();
|
|
1386
1620
|
const governResult = await governMod.runGovernInstall({
|
|
1387
1621
|
cli: "all",
|
|
1388
1622
|
mode: "enforced",
|
|
1389
1623
|
cwd: process.cwd(),
|
|
1390
1624
|
apiKey,
|
|
1391
1625
|
baseUrl,
|
|
1392
|
-
frameworks,
|
|
1626
|
+
frameworks: frameworks ?? ["iso27001", "iso42001", "soc2"],
|
|
1627
|
+
projectOperation,
|
|
1393
1628
|
});
|
|
1394
1629
|
if (!governResult.ok) {
|
|
1395
1630
|
step2.stop("fail", `Loading policy engine — ${governResult.message}`);
|
|
@@ -1397,31 +1632,21 @@ async function runEnterpriseInstall(args) {
|
|
|
1397
1632
|
}
|
|
1398
1633
|
step2.stop("ok", "Loading policy engine — policies armed");
|
|
1399
1634
|
|
|
1400
|
-
// govern.local.json was written as root in cwd/.context. chown it back.
|
|
1401
|
-
const governStatePath = path.join(process.cwd(), ".context", "govern.local.json");
|
|
1402
|
-
if (fs.existsSync(governStatePath)) {
|
|
1403
|
-
try {
|
|
1404
|
-
fs.chownSync(governStatePath, sudo.uid, sudo.gid);
|
|
1405
|
-
} catch (err) {
|
|
1406
|
-
printBullet("warn", `Could not chown ${governStatePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
1635
|
// Step 3 — Connecting audit pipeline (telemetry endpoint already wired by govern install).
|
|
1411
1636
|
const step3 = spinner("Connecting audit pipeline");
|
|
1412
1637
|
step3.stop("ok", `Connecting audit pipeline — endpoint ${baseUrl}`);
|
|
1413
1638
|
|
|
1414
1639
|
// Drop privileges before user-scope writes (Claude Code hooks in $HOME) and daemon spawn.
|
|
1415
|
-
dropPrivileges(sudo);
|
|
1640
|
+
(injected.dropPrivileges ?? dropPrivileges)(sudo);
|
|
1416
1641
|
|
|
1417
1642
|
// Step 4 — Preparing MCP gateway (Claude Code hooks bind the MCP surface).
|
|
1418
1643
|
if (installHooks) {
|
|
1419
1644
|
const step4 = spinner("Preparing MCP gateway");
|
|
1420
1645
|
try {
|
|
1421
|
-
if (hasManagedClaudeHooks()) {
|
|
1646
|
+
if ((injected.hasManagedClaudeHooks ?? hasManagedClaudeHooks)()) {
|
|
1422
1647
|
step4.stop("ok", "Preparing MCP gateway — managed Claude hooks active");
|
|
1423
1648
|
} else {
|
|
1424
|
-
await runHooksCommand(["install"]);
|
|
1649
|
+
await (injected.runHooksCommand ?? runHooksCommand)(["install"]);
|
|
1425
1650
|
step4.stop("ok", "Preparing MCP gateway — hooks installed");
|
|
1426
1651
|
}
|
|
1427
1652
|
} catch (err) {
|
|
@@ -1435,10 +1660,11 @@ async function runEnterpriseInstall(args) {
|
|
|
1435
1660
|
if (startDaemon) {
|
|
1436
1661
|
const step5 = spinner("Installing guardrails");
|
|
1437
1662
|
try {
|
|
1438
|
-
await runDaemonCommand(["
|
|
1663
|
+
await (injected.runDaemonCommand ?? runDaemonCommand)(["restart"]);
|
|
1439
1664
|
step5.stop("ok", "Installing guardrails — daemon online");
|
|
1440
1665
|
} catch (err) {
|
|
1441
1666
|
step5.stop("fail", `Installing guardrails — ${err instanceof Error ? err.message : String(err)}`);
|
|
1667
|
+
throw err;
|
|
1442
1668
|
}
|
|
1443
1669
|
} else {
|
|
1444
1670
|
printBullet("warn", "Installing guardrails — skipped (--no-daemon)");
|
|
@@ -1462,7 +1688,7 @@ async function runRunCommand(args) {
|
|
|
1462
1688
|
console.log("");
|
|
1463
1689
|
console.log("Wraps the named AI CLI in cortex enforcement:");
|
|
1464
1690
|
console.log(" claude/codex: passthrough — their own managed-config + sandbox");
|
|
1465
|
-
console.log(" cover Tier 1 enforcement after 'cortex enterprise
|
|
1691
|
+
console.log(" cover Tier 1 enforcement after 'cortex enterprise install --api-key-stdin'.");
|
|
1466
1692
|
console.log(" copilot: Tier 2 — OS-level sandbox (sandbox-exec on macOS,");
|
|
1467
1693
|
console.log(" bwrap on Linux). Denies writes to ~/.copilot/,");
|
|
1468
1694
|
console.log(" ~/.copilot.local/, /etc/copilot* so AI cannot");
|
|
@@ -1626,4 +1852,11 @@ if (invokedAsScript) {
|
|
|
1626
1852
|
});
|
|
1627
1853
|
}
|
|
1628
1854
|
|
|
1629
|
-
export {
|
|
1855
|
+
export {
|
|
1856
|
+
buildInitialConfig,
|
|
1857
|
+
detectInitialSourcePaths,
|
|
1858
|
+
hardenEnterpriseConfigPermissions,
|
|
1859
|
+
isScaffoldOutOfDate,
|
|
1860
|
+
runEnterpriseInstall,
|
|
1861
|
+
slugifyRepoId,
|
|
1862
|
+
};
|