codiedev 0.10.0-beta.4 → 0.10.0-beta.6
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/README.md +11 -5
- package/assets/tui-brand-plugin.tsx +14 -2
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +68 -3
- package/dist/cli.js +75 -64
- package/dist/codexHookTrust.js +125 -6
- package/dist/codexLimitScan.js +2 -109
- package/dist/codie.js +50 -114
- package/dist/codieBrandInstall.js +0 -18
- package/dist/codieLauncher.js +20 -92
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieUsage.js +38 -41
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1066 -0
- package/dist/commands/agentsSdk.generated.js +7 -0
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +53 -120
- package/dist/commands/evals.js +187 -0
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +573 -0
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +6 -10
- package/dist/commands/push.js +2 -12
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +344 -0
- package/dist/commands/tracesLabel.js +348 -0
- package/dist/configDir.js +0 -10
- package/dist/connect.js +19 -116
- package/dist/connectFlow.js +0 -9
- package/dist/copilotHooks.js +391 -0
- package/dist/copilotInstallation.js +64 -0
- package/dist/cursorHooks.js +38 -30
- package/dist/cursorLifecycle.js +109 -0
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +77 -0
- package/dist/hook.js +182 -469
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +140 -0
- package/dist/limitEvent.js +41 -35
- package/dist/limitEventReporter.js +1 -1
- package/dist/managedPi.js +240 -0
- package/dist/mcp.js +3 -384
- package/dist/nativeSteering.js +24 -0
- package/dist/packs/codie.js +182 -0
- package/dist/packs/harnesses.js +411 -0
- package/dist/packs/hookGate.js +256 -0
- package/dist/packs/install.js +286 -0
- package/dist/packs/manifest.js +126 -0
- package/dist/packs/paths.js +81 -0
- package/dist/packs/shim.js +214 -0
- package/dist/packs/store.js +155 -0
- package/dist/packs/sync.js +170 -0
- package/dist/piHooks.js +216 -0
- package/dist/piRuntime.js +120 -0
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/selfUpdate.js +376 -0
- package/dist/skillMarker.js +23 -0
- package/dist/subagents.js +135 -0
- package/dist/teamSkillContext.js +10 -0
- package/dist/transcriptModels.js +6 -8
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +494 -824
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +8 -9
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/captureSignals.d.ts +0 -19
- package/dist/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codexLimitScan.d.ts +0 -64
- package/dist/codie.d.ts +0 -2
- package/dist/codieBrandInstall.d.ts +0 -11
- package/dist/codieLauncher.d.ts +0 -86
- package/dist/codieRuntime.d.ts +0 -29
- package/dist/codieRuntimeInstall.d.ts +0 -28
- package/dist/codieStartup.d.ts +0 -26
- package/dist/codieStartup.js +0 -30
- package/dist/codieUsage.d.ts +0 -57
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/promote.d.ts +0 -1
- package/dist/commands/promote.js +0 -53
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/react.d.ts +0 -2
- package/dist/commands/react.js +0 -40
- package/dist/commands/reverseTicket.d.ts +0 -2
- package/dist/commands/reverseTicket.js +0 -477
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/security.js +0 -156
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/configDir.d.ts +0 -1
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -13
- package/dist/cursorHooks.d.ts +0 -23
- package/dist/detection.d.ts +0 -30
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/limitEvent.d.ts +0 -16
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/mcp.d.ts +0 -12
- package/dist/prUrl.d.ts +0 -9
- package/dist/prUrl.js +0 -33
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/security.d.ts +0 -102
- package/dist/security.js +0 -437
- package/dist/securityDeepScan.d.ts +0 -41
- package/dist/securityDeepScan.js +0 -248
- package/dist/securityDoctor.d.ts +0 -32
- package/dist/securityDoctor.js +0 -63
- package/dist/securityFeature.d.ts +0 -5
- package/dist/securityFeature.js +0 -7
- package/dist/securityInstall.d.ts +0 -63
- package/dist/securityInstall.js +0 -239
- package/dist/securityInventory.d.ts +0 -54
- package/dist/securityInventory.js +0 -556
- package/dist/securityPilot.d.ts +0 -19
- package/dist/securityPilot.js +0 -61
- package/dist/securityPolicy.d.ts +0 -33
- package/dist/securityPolicy.js +0 -188
- package/dist/securitySession.d.ts +0 -6
- package/dist/securitySession.js +0 -72
- package/dist/transcriptModels.d.ts +0 -18
- package/dist/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -346
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
package/dist/securityDeepScan.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.mergeDeepScanFindings = mergeDeepScanFindings;
|
|
37
|
-
exports.runDeepSecurityScan = runDeepSecurityScan;
|
|
38
|
-
const child_process_1 = require("child_process");
|
|
39
|
-
const fs = __importStar(require("fs"));
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
|
-
const path = __importStar(require("path"));
|
|
42
|
-
/** Adds component-addressable OSS scanner findings to the uploadable inventory.
|
|
43
|
-
* Workspace-level findings (for example OSV dependency results) stay in the
|
|
44
|
-
* deep-scan report because they do not identify one agent component. */
|
|
45
|
-
function mergeDeepScanFindings(report, deep) {
|
|
46
|
-
const byComponent = new Map();
|
|
47
|
-
for (const finding of deep.findings) {
|
|
48
|
-
if (!finding.componentKey)
|
|
49
|
-
continue;
|
|
50
|
-
const current = byComponent.get(finding.componentKey) ?? [];
|
|
51
|
-
current.push(finding);
|
|
52
|
-
byComponent.set(finding.componentKey, current);
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
...report,
|
|
56
|
-
components: report.components.map((component) => {
|
|
57
|
-
const external = byComponent.get(component.componentKey) ?? [];
|
|
58
|
-
const findings = [
|
|
59
|
-
...component.findings,
|
|
60
|
-
...external.map((finding) => ({
|
|
61
|
-
code: `oss:${finding.scanner}:${finding.ruleId ?? "finding"}`.slice(0, 240),
|
|
62
|
-
severity: finding.severity,
|
|
63
|
-
detail: finding.title,
|
|
64
|
-
})),
|
|
65
|
-
];
|
|
66
|
-
return {
|
|
67
|
-
...component,
|
|
68
|
-
findings: [...new Map(findings.map((finding) => [`${finding.code}:${finding.detail}`, finding])).values()].slice(0, 100),
|
|
69
|
-
};
|
|
70
|
-
}),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function defaultRunner(command, args, timeoutMs) {
|
|
74
|
-
return (0, child_process_1.spawnSync)(command, args, { encoding: "utf8", timeout: timeoutMs, maxBuffer: 8 * 1024 * 1024 });
|
|
75
|
-
}
|
|
76
|
-
function actualPath(source, homeDir, workspace) {
|
|
77
|
-
if (source === ".")
|
|
78
|
-
return workspace;
|
|
79
|
-
if (source.startsWith("~/"))
|
|
80
|
-
return path.join(homeDir, source.slice(2));
|
|
81
|
-
if (source.startsWith("[external:"))
|
|
82
|
-
return null;
|
|
83
|
-
return path.join(workspace, source);
|
|
84
|
-
}
|
|
85
|
-
function severity(value) {
|
|
86
|
-
const normalized = String(value ?? "medium").toLowerCase();
|
|
87
|
-
if (normalized === "critical" || normalized === "high" || normalized === "low")
|
|
88
|
-
return normalized;
|
|
89
|
-
return "medium";
|
|
90
|
-
}
|
|
91
|
-
function collectFindingObjects(value, scanner, componentKey) {
|
|
92
|
-
const findings = [];
|
|
93
|
-
const visit = (child, depth) => {
|
|
94
|
-
if (depth > 8 || findings.length >= 500 || child === null || child === undefined)
|
|
95
|
-
return;
|
|
96
|
-
if (Array.isArray(child)) {
|
|
97
|
-
for (const item of child.slice(0, 1_000))
|
|
98
|
-
visit(item, depth + 1);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (typeof child !== "object")
|
|
102
|
-
return;
|
|
103
|
-
const row = child;
|
|
104
|
-
const ruleId = row.rule_id ?? row.ruleId ?? row.id ?? row.code;
|
|
105
|
-
// Do not import scanner messages/descriptions: several engines include the
|
|
106
|
-
// matched source snippet there. Titles and rule names are bounded metadata.
|
|
107
|
-
const title = row.title ?? row.rule_name ?? row.ruleName ?? row.name ??
|
|
108
|
-
(ruleId !== undefined ? `Scanner finding ${String(ruleId)}` : undefined);
|
|
109
|
-
const severityValue = row.severity ?? row.level ?? row.risk;
|
|
110
|
-
const looksLikeFinding = typeof title === "string" && (severityValue !== undefined || ruleId !== undefined || row.match !== undefined || row.finding !== undefined);
|
|
111
|
-
if (looksLikeFinding) {
|
|
112
|
-
findings.push({
|
|
113
|
-
scanner,
|
|
114
|
-
...(componentKey ? { componentKey } : {}),
|
|
115
|
-
severity: severity(severityValue),
|
|
116
|
-
...(ruleId !== undefined ? { ruleId: String(ruleId).slice(0, 160) } : {}),
|
|
117
|
-
title: String(title).replace(/\s+/g, " ").slice(0, 500),
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
for (const nested of Object.values(row))
|
|
121
|
-
visit(nested, depth + 1);
|
|
122
|
-
};
|
|
123
|
-
visit(value, 0);
|
|
124
|
-
return [...new Map(findings.map((finding) => [
|
|
125
|
-
`${finding.scanner}:${finding.componentKey}:${finding.ruleId}:${finding.title}`,
|
|
126
|
-
finding,
|
|
127
|
-
])).values()];
|
|
128
|
-
}
|
|
129
|
-
function runJsonScanner(args) {
|
|
130
|
-
const result = args.runner(args.command, args.commandArgs, args.timeoutMs);
|
|
131
|
-
if (result.error && result.error.code === "ENOENT") {
|
|
132
|
-
return { scanner: args.scanner, status: "unavailable", detail: `${args.command} is not installed`, findings: [] };
|
|
133
|
-
}
|
|
134
|
-
const stdout = String(result.stdout ?? "").trim();
|
|
135
|
-
const stderr = String(result.stderr ?? "").trim();
|
|
136
|
-
let parsed = null;
|
|
137
|
-
if (stdout) {
|
|
138
|
-
try {
|
|
139
|
-
parsed = JSON.parse(stdout);
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
const start = stdout.indexOf("{");
|
|
143
|
-
const end = stdout.lastIndexOf("}");
|
|
144
|
-
if (start >= 0 && end > start) {
|
|
145
|
-
try {
|
|
146
|
-
parsed = JSON.parse(stdout.slice(start, end + 1));
|
|
147
|
-
}
|
|
148
|
-
catch { /* handled below */ }
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const findings = collectFindingObjects(parsed, args.scanner, args.componentKey);
|
|
153
|
-
if (result.status === 0 || findings.length > 0) {
|
|
154
|
-
return {
|
|
155
|
-
scanner: args.scanner,
|
|
156
|
-
status: findings.length > 0 ? "findings" : "passed",
|
|
157
|
-
detail: findings.length > 0 ? `${findings.length} finding(s)` : "scan completed with no findings",
|
|
158
|
-
findings,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
return {
|
|
162
|
-
scanner: args.scanner,
|
|
163
|
-
status: "error",
|
|
164
|
-
detail: (stderr || `scanner exited ${result.status ?? "without a status"}`).replace(/\s+/g, " ").slice(0, 500),
|
|
165
|
-
findings: [],
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function pathsFor(report, kind, homeDir, workspace) {
|
|
169
|
-
return report.components
|
|
170
|
-
.filter((component) => component.kind === kind)
|
|
171
|
-
.map((component) => ({ component, filePath: actualPath(component.source, homeDir, workspace) }))
|
|
172
|
-
.filter((item) => Boolean(item.filePath && fs.existsSync(item.filePath)));
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Runs optional open-source scanners. No scanner is downloaded implicitly.
|
|
176
|
-
* MCP server connection is a separate opt-in because scanning a live stdio
|
|
177
|
-
* server starts third-party code; the normal deep scan remains static.
|
|
178
|
-
*/
|
|
179
|
-
function runDeepSecurityScan(report, options = {}) {
|
|
180
|
-
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
181
|
-
const workspace = path.resolve(options.workspace ?? process.cwd());
|
|
182
|
-
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
183
|
-
const runner = options.runner ?? defaultRunner;
|
|
184
|
-
const runs = [];
|
|
185
|
-
const skillPaths = pathsFor(report, "skill", homeDir, workspace);
|
|
186
|
-
for (const { component, filePath } of skillPaths) {
|
|
187
|
-
// Cisco's scanner is preferred when present; ATR is the lightweight
|
|
188
|
-
// fallback. The current ATR CLI does not expose its internal detection
|
|
189
|
-
// lanes, so only high/critical findings are imported for review.
|
|
190
|
-
const cisco = runJsonScanner({
|
|
191
|
-
scanner: "cisco-skill-scanner", command: "skill-scanner",
|
|
192
|
-
commandArgs: ["scan", filePath, "--format", "json"], componentKey: component.componentKey,
|
|
193
|
-
timeoutMs, runner,
|
|
194
|
-
});
|
|
195
|
-
if (cisco.status !== "unavailable")
|
|
196
|
-
runs.push(cisco);
|
|
197
|
-
else
|
|
198
|
-
runs.push(runJsonScanner({
|
|
199
|
-
scanner: "agent-threat-rules", command: "atr",
|
|
200
|
-
commandArgs: ["scan", filePath, "--json", "--severity", "high", "--no-report"],
|
|
201
|
-
componentKey: component.componentKey, timeoutMs, runner,
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
if (skillPaths.length === 0) {
|
|
205
|
-
runs.push({ scanner: "skill", status: "skipped", detail: "no skills discovered", findings: [] });
|
|
206
|
-
}
|
|
207
|
-
runs.push(runJsonScanner({
|
|
208
|
-
scanner: "osv-scanner", command: "osv-scanner",
|
|
209
|
-
commandArgs: ["scan", "source", "-r", "--format", "json", workspace], timeoutMs, runner,
|
|
210
|
-
}));
|
|
211
|
-
if (options.connectMcp) {
|
|
212
|
-
const configPaths = [...new Set(pathsFor(report, "mcp-server", homeDir, workspace).map((item) => item.filePath))];
|
|
213
|
-
for (const configPath of configPaths) {
|
|
214
|
-
runs.push(runJsonScanner({
|
|
215
|
-
scanner: "cisco-mcp-scanner", command: "mcp-scanner",
|
|
216
|
-
commandArgs: ["--config-path", configPath, "--analyzers", "yara", "--format", "raw"],
|
|
217
|
-
timeoutMs, runner,
|
|
218
|
-
}));
|
|
219
|
-
}
|
|
220
|
-
if (configPaths.length === 0)
|
|
221
|
-
runs.push({ scanner: "cisco-mcp-scanner", status: "skipped", detail: "no MCP configurations discovered", findings: [] });
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
runs.push({ scanner: "cisco-mcp-scanner", status: "skipped", detail: "live MCP startup disabled; pass --connect-mcp to opt in", findings: [] });
|
|
225
|
-
}
|
|
226
|
-
if (options.supplyChain) {
|
|
227
|
-
for (const { component } of pathsFor(report, "extension", homeDir, workspace)) {
|
|
228
|
-
runs.push(runJsonScanner({
|
|
229
|
-
scanner: "guarddog", command: "guarddog",
|
|
230
|
-
commandArgs: [
|
|
231
|
-
"extension", "scan", component.name,
|
|
232
|
-
...(component.version ? ["--version", component.version] : []),
|
|
233
|
-
"--output-format=json",
|
|
234
|
-
],
|
|
235
|
-
componentKey: component.componentKey, timeoutMs, runner,
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
runs.push({ scanner: "guarddog", status: "skipped", detail: "registry downloads disabled; pass --supply-chain to opt in", findings: [] });
|
|
241
|
-
}
|
|
242
|
-
return {
|
|
243
|
-
schemaVersion: 1,
|
|
244
|
-
generatedAt: new Date((options.now ?? Date.now)()).toISOString(),
|
|
245
|
-
runs,
|
|
246
|
-
findings: runs.flatMap((run) => run.findings),
|
|
247
|
-
};
|
|
248
|
-
}
|
package/dist/securityDoctor.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { type SecurityInstallInspection, type SecurityInstallManifest } from "./securityInstall";
|
|
2
|
-
export type SecurityDoctorStatus = "pass" | "fail" | "warn";
|
|
3
|
-
export interface SecurityDoctorCheck {
|
|
4
|
-
name: string;
|
|
5
|
-
status: SecurityDoctorStatus;
|
|
6
|
-
detail: string;
|
|
7
|
-
}
|
|
8
|
-
export interface SecurityDoctorReport {
|
|
9
|
-
schemaVersion: 1;
|
|
10
|
-
ok: boolean;
|
|
11
|
-
integrity: {
|
|
12
|
-
ok: boolean;
|
|
13
|
-
detail: string;
|
|
14
|
-
};
|
|
15
|
-
manifest: SecurityInstallManifest | null;
|
|
16
|
-
installations: SecurityInstallInspection[];
|
|
17
|
-
policyExists: boolean;
|
|
18
|
-
/** Retained for `codiedev security doctor --json` compatibility. */
|
|
19
|
-
policySignature: {
|
|
20
|
-
ok: boolean;
|
|
21
|
-
trusted: boolean;
|
|
22
|
-
detail: string;
|
|
23
|
-
};
|
|
24
|
-
policySelfTest: {
|
|
25
|
-
ok: boolean;
|
|
26
|
-
cases: number;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
/** One security-health model shared by `doctor` and its legacy alias. */
|
|
30
|
-
export declare function collectSecurityDoctorReport(companyId?: string): SecurityDoctorReport;
|
|
31
|
-
/** Convert security health into the same compact rows used by core doctor. */
|
|
32
|
-
export declare function securityDoctorChecks(report: SecurityDoctorReport): SecurityDoctorCheck[];
|
package/dist/securityDoctor.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.collectSecurityDoctorReport = collectSecurityDoctorReport;
|
|
4
|
-
exports.securityDoctorChecks = securityDoctorChecks;
|
|
5
|
-
const securityInstall_1 = require("./securityInstall");
|
|
6
|
-
const securityPilot_1 = require("./securityPilot");
|
|
7
|
-
const securityPolicy_1 = require("./securityPolicy");
|
|
8
|
-
/** One security-health model shared by `doctor` and its legacy alias. */
|
|
9
|
-
function collectSecurityDoctorReport(companyId) {
|
|
10
|
-
const manifest = (0, securityInstall_1.readSecurityInstallManifest)();
|
|
11
|
-
const integrity = (0, securityInstall_1.verifySecurityInstallManifest)(manifest);
|
|
12
|
-
const installations = (0, securityInstall_1.inspectSecurityInstallation)();
|
|
13
|
-
const pilot = (0, securityPilot_1.runSyntheticSecurityPilot)();
|
|
14
|
-
const policy = (0, securityPolicy_1.inspectEndpointSecurityPolicy)(undefined, undefined, companyId);
|
|
15
|
-
const ok = integrity.ok &&
|
|
16
|
-
policy.verification.ok &&
|
|
17
|
-
pilot.ok &&
|
|
18
|
-
!installations.some((result) => result.status === "fail");
|
|
19
|
-
return {
|
|
20
|
-
schemaVersion: 1,
|
|
21
|
-
ok,
|
|
22
|
-
integrity,
|
|
23
|
-
manifest,
|
|
24
|
-
installations,
|
|
25
|
-
policyExists: policy.exists,
|
|
26
|
-
policySignature: policy.verification,
|
|
27
|
-
policySelfTest: { ok: pilot.ok, cases: pilot.cases.length },
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/** Convert security health into the same compact rows used by core doctor. */
|
|
31
|
-
function securityDoctorChecks(report) {
|
|
32
|
-
const checks = [
|
|
33
|
-
{
|
|
34
|
-
name: "Agent Security hook integrity",
|
|
35
|
-
status: report.integrity.ok ? "pass" : "fail",
|
|
36
|
-
detail: report.integrity.detail,
|
|
37
|
-
},
|
|
38
|
-
...report.installations.map((installation) => ({
|
|
39
|
-
name: `Agent Security ${installation.surface}`,
|
|
40
|
-
status: installation.status === "pass"
|
|
41
|
-
? "pass"
|
|
42
|
-
: installation.status === "not-installed"
|
|
43
|
-
? "warn"
|
|
44
|
-
: "fail",
|
|
45
|
-
detail: installation.detail,
|
|
46
|
-
})),
|
|
47
|
-
{
|
|
48
|
-
name: "Agent Security policy",
|
|
49
|
-
status: !report.policyExists
|
|
50
|
-
? "warn"
|
|
51
|
-
: report.policySignature.ok
|
|
52
|
-
? "pass"
|
|
53
|
-
: "fail",
|
|
54
|
-
detail: report.policySignature.detail,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "Agent Security policy self-test",
|
|
58
|
-
status: report.policySelfTest.ok ? "pass" : "fail",
|
|
59
|
-
detail: `${report.policySelfTest.cases} deterministic cases`,
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
return checks;
|
|
63
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export interface AgentSecurityEntitlement {
|
|
2
|
-
agentSecurityEnabled?: boolean;
|
|
3
|
-
}
|
|
4
|
-
/** Security is opt-in per company. Missing/stale entitlement data is dormant. */
|
|
5
|
-
export declare function isAgentSecurityEnabled(config: AgentSecurityEntitlement | null | undefined): boolean;
|
package/dist/securityFeature.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAgentSecurityEnabled = isAgentSecurityEnabled;
|
|
4
|
-
/** Security is opt-in per company. Missing/stale entitlement data is dormant. */
|
|
5
|
-
function isAgentSecurityEnabled(config) {
|
|
6
|
-
return config?.agentSecurityEnabled === true;
|
|
7
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
export type SecuritySurface = "claude-code" | "codex" | "cursor" | "codie-harness";
|
|
2
|
-
export interface SecurityInstallTarget {
|
|
3
|
-
surface: SecuritySurface;
|
|
4
|
-
configPath: string;
|
|
5
|
-
}
|
|
6
|
-
export interface SecurityRollbackFile {
|
|
7
|
-
originalPath: string;
|
|
8
|
-
snapshotPath: string;
|
|
9
|
-
}
|
|
10
|
-
export interface SecurityInstallSnapshot {
|
|
11
|
-
id: string;
|
|
12
|
-
createdAt: string;
|
|
13
|
-
files: SecurityRollbackFile[];
|
|
14
|
-
}
|
|
15
|
-
export interface SecurityInstallManifest {
|
|
16
|
-
schemaVersion: 1;
|
|
17
|
-
cliVersion: string;
|
|
18
|
-
installedAt: string;
|
|
19
|
-
hookScriptPath: string;
|
|
20
|
-
hookScriptSha256: string;
|
|
21
|
-
targets: SecurityInstallTarget[];
|
|
22
|
-
rollbackSnapshotId: string;
|
|
23
|
-
rollbackFiles: SecurityRollbackFile[];
|
|
24
|
-
}
|
|
25
|
-
export interface SecurityInstallInspection {
|
|
26
|
-
surface: SecuritySurface;
|
|
27
|
-
status: "pass" | "fail" | "not-installed";
|
|
28
|
-
detail: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Capture the exact pre-update hook configuration. This function never removes
|
|
32
|
-
* or restores anything; it creates the evidence needed for an explicit,
|
|
33
|
-
* administrator-approved rollback later.
|
|
34
|
-
*/
|
|
35
|
-
export declare function createSecurityInstallSnapshot(configPaths: string[], stateDir?: string, now?: () => number): SecurityInstallSnapshot;
|
|
36
|
-
export declare function writeSecurityInstallManifest(options: {
|
|
37
|
-
stateDir?: string;
|
|
38
|
-
hookScriptPath: string;
|
|
39
|
-
snapshot: SecurityInstallSnapshot;
|
|
40
|
-
targets: SecurityInstallTarget[];
|
|
41
|
-
now?: () => number;
|
|
42
|
-
}): SecurityInstallManifest;
|
|
43
|
-
export declare function readSecurityInstallManifest(stateDir?: string): SecurityInstallManifest | null;
|
|
44
|
-
export declare function verifySecurityInstallManifest(manifest: SecurityInstallManifest | null): {
|
|
45
|
-
ok: boolean;
|
|
46
|
-
detail: string;
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Restore only files recorded in the selected install manifest. The snapshot
|
|
50
|
-
* location is validated before any write and each replacement is atomic, so a
|
|
51
|
-
* partial copy cannot leave a hook configuration truncated.
|
|
52
|
-
*/
|
|
53
|
-
export declare function restoreSecurityInstallSnapshot(manifest: SecurityInstallManifest | null): {
|
|
54
|
-
restored: number;
|
|
55
|
-
snapshotId: string;
|
|
56
|
-
};
|
|
57
|
-
export declare function inspectSecurityInstallation(paths?: {
|
|
58
|
-
claudeSettingsPath?: string;
|
|
59
|
-
codexHooksPath?: string;
|
|
60
|
-
codexConfigPath?: string;
|
|
61
|
-
cursorHooksPath?: string;
|
|
62
|
-
codiePluginPath?: string;
|
|
63
|
-
}): SecurityInstallInspection[];
|
package/dist/securityInstall.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.createSecurityInstallSnapshot = createSecurityInstallSnapshot;
|
|
37
|
-
exports.writeSecurityInstallManifest = writeSecurityInstallManifest;
|
|
38
|
-
exports.readSecurityInstallManifest = readSecurityInstallManifest;
|
|
39
|
-
exports.verifySecurityInstallManifest = verifySecurityInstallManifest;
|
|
40
|
-
exports.restoreSecurityInstallSnapshot = restoreSecurityInstallSnapshot;
|
|
41
|
-
exports.inspectSecurityInstallation = inspectSecurityInstallation;
|
|
42
|
-
const crypto = __importStar(require("crypto"));
|
|
43
|
-
const fs = __importStar(require("fs"));
|
|
44
|
-
const os = __importStar(require("os"));
|
|
45
|
-
const path = __importStar(require("path"));
|
|
46
|
-
const configDir_1 = require("./configDir");
|
|
47
|
-
const detection_1 = require("./detection");
|
|
48
|
-
const codexHookTrust_1 = require("./codexHookTrust");
|
|
49
|
-
const version_1 = require("./version");
|
|
50
|
-
const MANIFEST_FILE = "security-install.json";
|
|
51
|
-
function sha256File(filePath) {
|
|
52
|
-
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
53
|
-
}
|
|
54
|
-
function safeSnapshotName(index, filePath) {
|
|
55
|
-
const digest = crypto.createHash("sha256").update(filePath).digest("hex").slice(0, 12);
|
|
56
|
-
return `${String(index + 1).padStart(2, "0")}-${digest}-${path.basename(filePath)}`;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Capture the exact pre-update hook configuration. This function never removes
|
|
60
|
-
* or restores anything; it creates the evidence needed for an explicit,
|
|
61
|
-
* administrator-approved rollback later.
|
|
62
|
-
*/
|
|
63
|
-
function createSecurityInstallSnapshot(configPaths, stateDir = (0, configDir_1.codiedevStateDir)(), now = Date.now) {
|
|
64
|
-
const timestamp = now();
|
|
65
|
-
const id = `${timestamp}-${process.pid}`;
|
|
66
|
-
const snapshotDir = path.join(stateDir, "security-rollbacks", id);
|
|
67
|
-
fs.mkdirSync(snapshotDir, { recursive: true, mode: 0o700 });
|
|
68
|
-
const files = [];
|
|
69
|
-
for (const [index, originalPath] of [...new Set(configPaths)].entries()) {
|
|
70
|
-
if (!fs.existsSync(originalPath) || !fs.statSync(originalPath).isFile())
|
|
71
|
-
continue;
|
|
72
|
-
const snapshotPath = path.join(snapshotDir, safeSnapshotName(index, originalPath));
|
|
73
|
-
fs.copyFileSync(originalPath, snapshotPath);
|
|
74
|
-
fs.chmodSync(snapshotPath, 0o600);
|
|
75
|
-
files.push({ originalPath, snapshotPath });
|
|
76
|
-
}
|
|
77
|
-
return { id, createdAt: new Date(timestamp).toISOString(), files };
|
|
78
|
-
}
|
|
79
|
-
function writeSecurityInstallManifest(options) {
|
|
80
|
-
const stateDir = options.stateDir ?? (0, configDir_1.codiedevStateDir)();
|
|
81
|
-
const now = options.now ?? Date.now;
|
|
82
|
-
if (!fs.existsSync(options.hookScriptPath)) {
|
|
83
|
-
throw new Error(`security hook script not found: ${options.hookScriptPath}`);
|
|
84
|
-
}
|
|
85
|
-
const manifest = {
|
|
86
|
-
schemaVersion: 1,
|
|
87
|
-
cliVersion: version_1.CLI_VERSION,
|
|
88
|
-
installedAt: new Date(now()).toISOString(),
|
|
89
|
-
hookScriptPath: path.resolve(options.hookScriptPath),
|
|
90
|
-
hookScriptSha256: sha256File(options.hookScriptPath),
|
|
91
|
-
targets: options.targets,
|
|
92
|
-
rollbackSnapshotId: options.snapshot.id,
|
|
93
|
-
rollbackFiles: options.snapshot.files,
|
|
94
|
-
};
|
|
95
|
-
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
96
|
-
const manifestPath = path.join(stateDir, MANIFEST_FILE);
|
|
97
|
-
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
|
|
98
|
-
fs.chmodSync(manifestPath, 0o600);
|
|
99
|
-
return manifest;
|
|
100
|
-
}
|
|
101
|
-
function readSecurityInstallManifest(stateDir = (0, configDir_1.codiedevStateDir)()) {
|
|
102
|
-
try {
|
|
103
|
-
const value = JSON.parse(fs.readFileSync(path.join(stateDir, MANIFEST_FILE), "utf8"));
|
|
104
|
-
return value?.schemaVersion === 1 ? value : null;
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function verifySecurityInstallManifest(manifest) {
|
|
111
|
-
if (!manifest)
|
|
112
|
-
return { ok: false, detail: "security install manifest is missing" };
|
|
113
|
-
if (!fs.existsSync(manifest.hookScriptPath)) {
|
|
114
|
-
return { ok: false, detail: "recorded security hook script is missing" };
|
|
115
|
-
}
|
|
116
|
-
if (sha256File(manifest.hookScriptPath) !== manifest.hookScriptSha256) {
|
|
117
|
-
return {
|
|
118
|
-
ok: false,
|
|
119
|
-
detail: "installed hook does not match the recorded SHA-256 integrity digest",
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
ok: true,
|
|
124
|
-
detail: `v${manifest.cliVersion}; rollback snapshot ${manifest.rollbackSnapshotId} available`,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Restore only files recorded in the selected install manifest. The snapshot
|
|
129
|
-
* location is validated before any write and each replacement is atomic, so a
|
|
130
|
-
* partial copy cannot leave a hook configuration truncated.
|
|
131
|
-
*/
|
|
132
|
-
function restoreSecurityInstallSnapshot(manifest) {
|
|
133
|
-
if (!manifest)
|
|
134
|
-
throw new Error("security install manifest is missing");
|
|
135
|
-
let restored = 0;
|
|
136
|
-
for (const file of manifest.rollbackFiles) {
|
|
137
|
-
const snapshotPath = path.resolve(file.snapshotPath);
|
|
138
|
-
const snapshotDir = path.dirname(snapshotPath);
|
|
139
|
-
if (path.basename(snapshotDir) !== manifest.rollbackSnapshotId ||
|
|
140
|
-
path.basename(path.dirname(snapshotDir)) !== "security-rollbacks") {
|
|
141
|
-
throw new Error(`rollback file does not match rollback snapshot ${manifest.rollbackSnapshotId}`);
|
|
142
|
-
}
|
|
143
|
-
if (!fs.existsSync(snapshotPath) || !fs.statSync(snapshotPath).isFile()) {
|
|
144
|
-
throw new Error(`rollback file is missing: ${snapshotPath}`);
|
|
145
|
-
}
|
|
146
|
-
const originalPath = path.resolve(file.originalPath);
|
|
147
|
-
fs.mkdirSync(path.dirname(originalPath), { recursive: true, mode: 0o700 });
|
|
148
|
-
const temporaryPath = `${originalPath}.codiedev-rollback-${process.pid}`;
|
|
149
|
-
fs.copyFileSync(snapshotPath, temporaryPath);
|
|
150
|
-
fs.chmodSync(temporaryPath, 0o600);
|
|
151
|
-
fs.renameSync(temporaryPath, originalPath);
|
|
152
|
-
restored += 1;
|
|
153
|
-
}
|
|
154
|
-
return { restored, snapshotId: manifest.rollbackSnapshotId };
|
|
155
|
-
}
|
|
156
|
-
function hookCommands(parsed, eventName, allowFlat) {
|
|
157
|
-
const entries = parsed?.hooks?.[eventName];
|
|
158
|
-
if (!Array.isArray(entries))
|
|
159
|
-
return [];
|
|
160
|
-
const commands = [];
|
|
161
|
-
for (const entry of entries) {
|
|
162
|
-
const flatCommand = entry.command;
|
|
163
|
-
if (allowFlat && typeof flatCommand === "string" && (0, detection_1.isCodiedevHookCommand)(flatCommand)) {
|
|
164
|
-
commands.push(flatCommand);
|
|
165
|
-
}
|
|
166
|
-
const nested = entry?.hooks;
|
|
167
|
-
if (!Array.isArray(nested))
|
|
168
|
-
continue;
|
|
169
|
-
for (const hook of nested) {
|
|
170
|
-
if (typeof hook.command === "string" && (0, detection_1.isCodiedevHookCommand)(hook.command)) {
|
|
171
|
-
commands.push(hook.command);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return commands;
|
|
176
|
-
}
|
|
177
|
-
function inspectJsonSurface(surface, filePath, codexConfigPath) {
|
|
178
|
-
if (!fs.existsSync(filePath)) {
|
|
179
|
-
return { surface, status: "not-installed", detail: `${filePath} not found` };
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
183
|
-
const expected = surface === "cursor"
|
|
184
|
-
? [
|
|
185
|
-
["beforeSubmitPrompt", "security-prompt-cursor"],
|
|
186
|
-
["preToolUse", "security-pre-cursor"],
|
|
187
|
-
["postToolUse", "security-post-cursor"],
|
|
188
|
-
["postToolUseFailure", "security-failure-cursor"],
|
|
189
|
-
]
|
|
190
|
-
: [
|
|
191
|
-
["UserPromptSubmit", surface === "codex" ? "discover-codex" : "discover"],
|
|
192
|
-
["PreToolUse", surface === "codex" ? "security-pre-codex" : "security-pre"],
|
|
193
|
-
["PostToolUse", surface === "codex" ? "security-post-codex" : "security-post"],
|
|
194
|
-
...(surface === "claude-code"
|
|
195
|
-
? [["PostToolUseFailure", "security-failure"]]
|
|
196
|
-
: []),
|
|
197
|
-
];
|
|
198
|
-
const missing = expected
|
|
199
|
-
.filter(([eventName, subcommand]) => !hookCommands(parsed, eventName, surface === "cursor").some((command) => command.trim().endsWith(subcommand)))
|
|
200
|
-
.map(([eventName]) => eventName);
|
|
201
|
-
if (missing.length > 0) {
|
|
202
|
-
return { surface, status: "fail", detail: `missing security hooks: ${missing.join(", ")}` };
|
|
203
|
-
}
|
|
204
|
-
if (surface === "codex") {
|
|
205
|
-
const trust = (0, codexHookTrust_1.inspectCodexHookTrust)(codexConfigPath ?? path.join(path.dirname(filePath), "config.toml"), filePath, parsed, ["discover-codex", "security-pre-codex", "security-post-codex"]);
|
|
206
|
-
if (!trust.ok) {
|
|
207
|
-
return { surface, status: "fail", detail: `Codex hook trust missing or stale (${trust.trusted}/${trust.total} trusted)` };
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return { surface, status: "pass", detail: "prompt guard and tool telemetry hooks installed and active" };
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
return { surface, status: "fail", detail: `invalid hook configuration: ${error.message}` };
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
function inspectSecurityInstallation(paths = {}) {
|
|
217
|
-
const home = os.homedir();
|
|
218
|
-
const claude = paths.claudeSettingsPath ?? path.join(home, ".claude", "settings.json");
|
|
219
|
-
const codex = paths.codexHooksPath ?? path.join(home, ".codex", "hooks.json");
|
|
220
|
-
const cursor = paths.cursorHooksPath ?? path.join(home, ".cursor", "hooks.json");
|
|
221
|
-
const codie = paths.codiePluginPath ?? path.join(home, ".config", "opencode", "plugins", "codiedev.js");
|
|
222
|
-
const results = [
|
|
223
|
-
inspectJsonSurface("claude-code", claude),
|
|
224
|
-
inspectJsonSurface("codex", codex, paths.codexConfigPath),
|
|
225
|
-
inspectJsonSurface("cursor", cursor),
|
|
226
|
-
];
|
|
227
|
-
if (!fs.existsSync(codie)) {
|
|
228
|
-
results.push({ surface: "codie-harness", status: "not-installed", detail: `${codie} not found` });
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
const source = fs.readFileSync(codie, "utf8");
|
|
232
|
-
const markers = ["security-pre-codie", "security-post-codie", "chat.message", "tool.execute.before", "tool.execute.after"];
|
|
233
|
-
const missing = markers.filter((marker) => !source.includes(marker));
|
|
234
|
-
results.push(missing.length === 0
|
|
235
|
-
? { surface: "codie-harness", status: "pass", detail: "prompt guard and tool telemetry adapter installed" }
|
|
236
|
-
: { surface: "codie-harness", status: "fail", detail: `adapter missing: ${missing.join(", ")}` });
|
|
237
|
-
}
|
|
238
|
-
return results;
|
|
239
|
-
}
|