codiedev 0.9.52 → 0.10.0-beta.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/README.md +9 -1
- package/dist/captureLog.d.ts +1 -1
- package/dist/cli.js +17 -12
- package/dist/codie.js +146 -1
- package/dist/codieLauncher.d.ts +5 -0
- package/dist/codieLauncher.js +22 -1
- package/dist/codieRuntime.d.ts +11 -1
- package/dist/codieRuntime.js +20 -3
- package/dist/codieRuntimeInstall.d.ts +12 -11
- package/dist/codieRuntimeInstall.js +3 -1
- package/dist/codieStartup.d.ts +26 -0
- package/dist/codieStartup.js +30 -0
- package/dist/commands/doctor.js +8 -0
- package/dist/commands/security.d.ts +1 -0
- package/dist/commands/security.js +156 -0
- package/dist/connect.js +56 -4
- package/dist/cursorHooks.d.ts +23 -0
- package/dist/cursorHooks.js +81 -0
- package/dist/hook.js +272 -26
- package/dist/limitEvent.d.ts +5 -1
- package/dist/limitEvent.js +5 -1
- package/dist/mcp.js +1 -93
- package/dist/repoResolver.js +10 -4
- package/dist/security.d.ts +102 -0
- package/dist/security.js +437 -0
- package/dist/securityDeepScan.d.ts +41 -0
- package/dist/securityDeepScan.js +248 -0
- package/dist/securityDoctor.d.ts +32 -0
- package/dist/securityDoctor.js +63 -0
- package/dist/securityFeature.d.ts +5 -0
- package/dist/securityFeature.js +7 -0
- package/dist/securityInstall.d.ts +63 -0
- package/dist/securityInstall.js +239 -0
- package/dist/securityInventory.d.ts +54 -0
- package/dist/securityInventory.js +556 -0
- package/dist/securityPilot.d.ts +19 -0
- package/dist/securityPilot.js +61 -0
- package/dist/securityPolicy.d.ts +33 -0
- package/dist/securityPolicy.js +188 -0
- package/dist/securitySession.d.ts +6 -0
- package/dist/securitySession.js +72 -0
- package/dist/upgradeNudge.d.ts +1 -0
- package/dist/upgradeNudge.js +1 -0
- package/dist/utils.d.ts +16 -6
- package/dist/utils.js +311 -70
- package/package.json +14 -3
- package/dist/commands/post.d.ts +0 -2
- package/dist/commands/post.js +0 -96
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ The CLI will prompt you for an API token (get one at https://codiedev.com/portal
|
|
|
12
12
|
|
|
13
13
|
- **Claude Code** — `SessionEnd` hook in `~/.claude/settings.json`
|
|
14
14
|
- **Codex** — `Stop` hook in `~/.codex/hooks.json`
|
|
15
|
-
- **Cursor** —
|
|
15
|
+
- **Cursor** — native session, prompt-security, tool-security, backup, and capture hooks in `~/.cursor/hooks.json`
|
|
16
16
|
- **OpenCode / Codie** — plugin in `~/.config/opencode/plugins/codiedev.js`
|
|
17
17
|
|
|
18
18
|
Installed clients are wired automatically. The OpenCode adapter is staged even
|
|
@@ -49,6 +49,14 @@ container needs its own rule unless `~/.codiedev/config.json` is persisted.
|
|
|
49
49
|
|
|
50
50
|
Codex doesn't currently expose a `SessionEnd` event, so the hook runs on `Stop` (fires after each assistant turn). The backend dedupes by `(company, session_id)` and replaces the stored transcript on re-upload so the latest turn's snapshot wins. Summarization happens once per session; subsequent turn uploads keep the raw transcript fresh but don't re-summarize (to avoid running up LLM cost).
|
|
51
51
|
|
|
52
|
+
## Cursor note
|
|
53
|
+
|
|
54
|
+
Cursor uses its native hook events for session start/end, prompt blocking,
|
|
55
|
+
pre/post-tool security, and stop-time backup. Cursor's `beforeSubmitPrompt`
|
|
56
|
+
cannot inject dynamic context today, so task-specific discovery is not claimed.
|
|
57
|
+
Cursor sessions keep their source
|
|
58
|
+
label and real transcript statistics through the shared capture pipeline.
|
|
59
|
+
|
|
52
60
|
## OpenCode note
|
|
53
61
|
|
|
54
62
|
OpenCode exposes session lifecycle events through plugins. Codiedev snapshots
|
package/dist/captureLog.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
export declare const CAPTURE_LOG_MAX_BYTES: number;
|
|
3
3
|
export declare const CAPTURE_LOG_PATH: string;
|
|
4
4
|
export interface CaptureLogEntry {
|
|
5
|
-
stage: "capture-finish" | "upload" | "upload-deferred" | "stderr";
|
|
5
|
+
stage: "capture-finish" | "upload" | "upload-deferred" | "security" | "stderr";
|
|
6
6
|
sessionId: string | undefined;
|
|
7
7
|
outcome: string;
|
|
8
8
|
detail?: string;
|
package/dist/cli.js
CHANGED
|
@@ -29,11 +29,11 @@ const createTicket_1 = require("./commands/createTicket");
|
|
|
29
29
|
const doctor_1 = require("./commands/doctor");
|
|
30
30
|
const search_1 = require("./commands/search");
|
|
31
31
|
const library_1 = require("./commands/library");
|
|
32
|
-
const post_1 = require("./commands/post");
|
|
33
32
|
const share_1 = require("./commands/share");
|
|
34
33
|
const send_1 = require("./commands/send");
|
|
35
34
|
const react_1 = require("./commands/react");
|
|
36
35
|
const capture_1 = require("./commands/capture");
|
|
36
|
+
const security_1 = require("./commands/security");
|
|
37
37
|
const version_1 = require("./version");
|
|
38
38
|
const HELP = `
|
|
39
39
|
Codiedev CLI
|
|
@@ -43,8 +43,14 @@ Codiedev CLI
|
|
|
43
43
|
someone left off across sessions and providers.
|
|
44
44
|
|
|
45
45
|
Connect:
|
|
46
|
-
codiedev connect Link Claude Code / Codex to your org
|
|
47
|
-
codiedev doctor Verify
|
|
46
|
+
codiedev connect Link Claude Code / Codex / Cursor to your org
|
|
47
|
+
codiedev doctor Verify setup, capture, and Agent Security
|
|
48
|
+
codiedev security pilot Run safe synthetic policy checks
|
|
49
|
+
codiedev security inventory Inventory MCPs, skills, hooks, plugins,
|
|
50
|
+
instructions, and editor extensions
|
|
51
|
+
codiedev security scan --deep Run installed open-source scanners too
|
|
52
|
+
codiedev security sync Fetch administrator policy locally
|
|
53
|
+
codiedev security rollback --yes Restore pre-update hook configuration
|
|
48
54
|
|
|
49
55
|
Harness:
|
|
50
56
|
codie Launch the governed Codie harness.
|
|
@@ -98,9 +104,6 @@ Discovery:
|
|
|
98
104
|
List artifacts in your workspace
|
|
99
105
|
|
|
100
106
|
Feed:
|
|
101
|
-
codiedev post --title "<t>" --body "<b>" [--intent share|request_review|
|
|
102
|
-
request_expertise|link_share|rfc] [--filename <key>]
|
|
103
|
-
[--mention <name>]... Publish to the team feed
|
|
104
107
|
codiedev react <postId> <emoji> Toggle a reaction on a feed post
|
|
105
108
|
|
|
106
109
|
Privacy:
|
|
@@ -128,7 +131,7 @@ Other artifact types (rare — use the matching prefix):
|
|
|
128
131
|
|
|
129
132
|
Skills (slash commands):
|
|
130
133
|
Add 'name:' + 'description:' YAML frontmatter to any pushed file
|
|
131
|
-
and it shows up as /<name> in Claude Code / Codex. Filename
|
|
134
|
+
and it shows up as /<name> in Claude Code / Codex / Cursor. Filename
|
|
132
135
|
prefix doesn't matter — frontmatter is the signal.
|
|
133
136
|
|
|
134
137
|
Examples:
|
|
@@ -165,6 +168,8 @@ codiedev connect — link your agent CLI to Codiedev
|
|
|
165
168
|
- ~/.claude/CLAUDE.md instructions (teaches Claude the commands)
|
|
166
169
|
- ~/.codex/hooks.json hook (same, for Codex)
|
|
167
170
|
- ~/.codex/AGENTS.md instructions (same, for Codex)
|
|
171
|
+
- ~/.cursor/hooks.json hooks (session, prompt, security, capture)
|
|
172
|
+
- ~/.cursor/rules/codiedev.mdc (teaches Cursor the commands)
|
|
168
173
|
|
|
169
174
|
Re-run anytime to swap accounts or refresh the instruction block.
|
|
170
175
|
|
|
@@ -367,7 +372,7 @@ Default path:
|
|
|
367
372
|
# body...
|
|
368
373
|
3. codiedev push portal-design.md
|
|
369
374
|
|
|
370
|
-
It's now /portal-design in Claude Code / Codex.
|
|
375
|
+
It's now /portal-design in Claude Code / Codex / Cursor.
|
|
371
376
|
|
|
372
377
|
Filename prefix is irrelevant for skills — frontmatter is the only
|
|
373
378
|
signal. The stored artifact type still follows the filename rule
|
|
@@ -385,7 +390,7 @@ email notifications when pinged.
|
|
|
385
390
|
|
|
386
391
|
## Agent-native
|
|
387
392
|
|
|
388
|
-
If you use Claude Code or
|
|
393
|
+
If you use Claude Code, Codex, or Cursor, the agent already knows the commands —
|
|
389
394
|
'codiedev connect' installs instructions into your user-level config.
|
|
390
395
|
Just say things like:
|
|
391
396
|
|
|
@@ -453,6 +458,9 @@ async function main() {
|
|
|
453
458
|
case "verify":
|
|
454
459
|
await (0, doctor_1.runDoctor)(rest);
|
|
455
460
|
return;
|
|
461
|
+
case "security":
|
|
462
|
+
await (0, security_1.runSecurityCommand)(rest);
|
|
463
|
+
return;
|
|
456
464
|
case "push":
|
|
457
465
|
await (0, push_1.runPush)(rest);
|
|
458
466
|
return;
|
|
@@ -503,9 +511,6 @@ async function main() {
|
|
|
503
511
|
case "ls":
|
|
504
512
|
await (0, library_1.runLibrary)(rest);
|
|
505
513
|
return;
|
|
506
|
-
case "post":
|
|
507
|
-
await (0, post_1.runPost)(rest);
|
|
508
|
-
return;
|
|
509
514
|
case "share":
|
|
510
515
|
await (0, share_1.runShare)(rest);
|
|
511
516
|
return;
|
package/dist/codie.js
CHANGED
|
@@ -53,6 +53,7 @@ const configDir_1 = require("./configDir");
|
|
|
53
53
|
const codieLauncher_1 = require("./codieLauncher");
|
|
54
54
|
const codieRuntime_1 = require("./codieRuntime");
|
|
55
55
|
const codieRuntimeInstall_1 = require("./codieRuntimeInstall");
|
|
56
|
+
const codieStartup_1 = require("./codieStartup");
|
|
56
57
|
const upgradeNudge_1 = require("./upgradeNudge");
|
|
57
58
|
function git(args) {
|
|
58
59
|
const result = (0, child_process_1.spawnSync)("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -169,6 +170,120 @@ function installBrandWordmarkUnsafe(runtime) {
|
|
|
169
170
|
(0, child_process_1.spawnSync)("codesign", ["--force", "--sign", "-", runtime], { stdio: "ignore" });
|
|
170
171
|
}
|
|
171
172
|
}
|
|
173
|
+
async function runNonInteractive(runtime, runtimeArgs, env) {
|
|
174
|
+
const detached = process.platform !== "win32";
|
|
175
|
+
const child = (0, child_process_1.spawn)(runtime, runtimeArgs, {
|
|
176
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
177
|
+
env,
|
|
178
|
+
detached,
|
|
179
|
+
});
|
|
180
|
+
if (child.stdin)
|
|
181
|
+
process.stdin.pipe(child.stdin);
|
|
182
|
+
const signalChild = (signal) => {
|
|
183
|
+
if (detached && child.pid) {
|
|
184
|
+
try {
|
|
185
|
+
process.kill(-child.pid, signal);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Fall back to the direct child if its process group already exited.
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
child.kill(signal);
|
|
193
|
+
};
|
|
194
|
+
const forwardInterrupt = () => signalChild("SIGINT");
|
|
195
|
+
const forwardTermination = () => signalChild("SIGTERM");
|
|
196
|
+
process.once("SIGINT", forwardInterrupt);
|
|
197
|
+
process.once("SIGTERM", forwardTermination);
|
|
198
|
+
return await new Promise((resolve, reject) => {
|
|
199
|
+
let blockReason;
|
|
200
|
+
const errorPrefixes = [Buffer.from("\x1b[91m\x1b[1mError:"), Buffer.from("Error: {")];
|
|
201
|
+
const retainedPrefixBytes = Math.max(...errorPrefixes.map((prefix) => prefix.length)) - 1;
|
|
202
|
+
// OpenCode's generic UnknownError and the adapter's private reason can be
|
|
203
|
+
// delivered in either order — and the generic block can arrive on stdout
|
|
204
|
+
// or stderr. Gate both streams by content, retaining only a possible final
|
|
205
|
+
// error block. Ordinary output stays streaming apart from the few bytes
|
|
206
|
+
// needed to recognize a split prefix.
|
|
207
|
+
const createOutputGate = (write) => {
|
|
208
|
+
let tail = Buffer.alloc(0);
|
|
209
|
+
let possibleError;
|
|
210
|
+
return {
|
|
211
|
+
push(chunk, blocked) {
|
|
212
|
+
if (blocked) {
|
|
213
|
+
if (possibleError)
|
|
214
|
+
possibleError.push(Buffer.from(chunk));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (possibleError) {
|
|
218
|
+
possibleError.push(Buffer.from(chunk));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const combined = Buffer.concat([tail, chunk]);
|
|
222
|
+
let errorStart = -1;
|
|
223
|
+
for (const prefix of errorPrefixes) {
|
|
224
|
+
const index = combined.indexOf(prefix);
|
|
225
|
+
if (index >= 0 && (errorStart < 0 || index < errorStart))
|
|
226
|
+
errorStart = index;
|
|
227
|
+
}
|
|
228
|
+
if (errorStart >= 0) {
|
|
229
|
+
if (errorStart > 0)
|
|
230
|
+
write(combined.subarray(0, errorStart));
|
|
231
|
+
possibleError = [combined.subarray(errorStart)];
|
|
232
|
+
tail = Buffer.alloc(0);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const flushBytes = Math.max(0, combined.length - retainedPrefixBytes);
|
|
236
|
+
if (flushBytes > 0)
|
|
237
|
+
write(combined.subarray(0, flushBytes));
|
|
238
|
+
tail = combined.subarray(flushBytes);
|
|
239
|
+
},
|
|
240
|
+
finish(blocked) {
|
|
241
|
+
if (tail.length > 0)
|
|
242
|
+
write(tail);
|
|
243
|
+
const heldError = possibleError ? Buffer.concat(possibleError).toString() : "";
|
|
244
|
+
const genericBlockError = heldError.includes("UnknownError") &&
|
|
245
|
+
heldError.includes("Unexpected server error");
|
|
246
|
+
if (heldError && (!blocked || !genericBlockError))
|
|
247
|
+
write(heldError);
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
const stdoutGate = createOutputGate((chunk) => process.stdout.write(chunk));
|
|
252
|
+
const stderrGate = createOutputGate((chunk) => process.stderr.write(chunk));
|
|
253
|
+
child.stdout?.on("data", (chunk) => {
|
|
254
|
+
stdoutGate.push(Buffer.from(chunk), Boolean(blockReason));
|
|
255
|
+
});
|
|
256
|
+
child.stderr?.on("data", (chunk) => {
|
|
257
|
+
const text = chunk.toString();
|
|
258
|
+
const reason = (0, codieLauncher_1.extractCodieSecurityBlock)(text);
|
|
259
|
+
if (reason) {
|
|
260
|
+
blockReason = reason;
|
|
261
|
+
signalChild("SIGTERM");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
stderrGate.push(Buffer.from(chunk), Boolean(blockReason));
|
|
265
|
+
});
|
|
266
|
+
child.once("error", (error) => {
|
|
267
|
+
if (child.stdin)
|
|
268
|
+
process.stdin.unpipe(child.stdin);
|
|
269
|
+
process.removeListener("SIGINT", forwardInterrupt);
|
|
270
|
+
process.removeListener("SIGTERM", forwardTermination);
|
|
271
|
+
reject(error);
|
|
272
|
+
});
|
|
273
|
+
child.once("close", (status) => {
|
|
274
|
+
if (child.stdin)
|
|
275
|
+
process.stdin.unpipe(child.stdin);
|
|
276
|
+
process.removeListener("SIGINT", forwardInterrupt);
|
|
277
|
+
process.removeListener("SIGTERM", forwardTermination);
|
|
278
|
+
stdoutGate.finish(Boolean(blockReason));
|
|
279
|
+
stderrGate.finish(Boolean(blockReason));
|
|
280
|
+
if (blockReason) {
|
|
281
|
+
console.error(`Codie Security blocked this action:\n${blockReason}`);
|
|
282
|
+
}
|
|
283
|
+
resolve(status);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
172
287
|
async function main() {
|
|
173
288
|
(0, codieRuntime_1.assertCodieNodeVersion)(process.versions.node);
|
|
174
289
|
const connected = connectedCodiedevConfig();
|
|
@@ -186,6 +301,10 @@ async function main() {
|
|
|
186
301
|
process.exitCode = update.status ?? 0;
|
|
187
302
|
return;
|
|
188
303
|
}
|
|
304
|
+
const policyRefresh = await (0, codieStartup_1.refreshCodieSecurityPolicy)(connected);
|
|
305
|
+
if (policyRefresh.status === "cached") {
|
|
306
|
+
console.error(`codie: security policy refresh failed; using the cached policy (${policyRefresh.error})`);
|
|
307
|
+
}
|
|
189
308
|
const gatewayUrl = process.env.CODIEDEV_GATEWAY_URL || "https://gateway.codiedev.com/api/v1";
|
|
190
309
|
const catalog = await (0, codieLauncher_1.fetchApprovedCatalog)({ baseUrl: gatewayUrl, key });
|
|
191
310
|
const parsed = (0, codieLauncher_1.parseCodieArgs)(process.argv.slice(2));
|
|
@@ -196,11 +315,20 @@ async function main() {
|
|
|
196
315
|
}
|
|
197
316
|
(0, codieLauncher_1.validateCodieArgs)(parsed.args, catalog);
|
|
198
317
|
const stateDir = (0, configDir_1.codiedevStateDir)();
|
|
199
|
-
const runtime = (0, codieRuntimeInstall_1.ensureCodieRuntimeBinary)({
|
|
318
|
+
const runtime = await (0, codieRuntimeInstall_1.ensureCodieRuntimeBinary)({
|
|
200
319
|
resolve: () => (0, codieRuntime_1.resolveCodieRuntime)({
|
|
201
320
|
env: process.env,
|
|
202
321
|
stateDir,
|
|
203
322
|
existsSync: (candidate) => fs.existsSync(candidate),
|
|
323
|
+
readInstalledVersion: (candidate) => {
|
|
324
|
+
try {
|
|
325
|
+
const value = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
326
|
+
return typeof value.version === "string" ? value.version : null;
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
},
|
|
204
332
|
resolveBundled: () => {
|
|
205
333
|
try {
|
|
206
334
|
return require.resolve("opencode-ai/bin/opencode.exe");
|
|
@@ -211,6 +339,16 @@ async function main() {
|
|
|
211
339
|
},
|
|
212
340
|
}),
|
|
213
341
|
install: (missing) => (0, codieRuntimeInstall_1.installCodieRuntimeOnDisk)(missing, stateDir),
|
|
342
|
+
confirmUpdate: async (outdated) => {
|
|
343
|
+
console.error("");
|
|
344
|
+
console.error(`Codie harness ${outdated.expectedVersion} is available (you're on ${outdated.currentVersion}).`);
|
|
345
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
346
|
+
console.error("Restart `codie` in an interactive terminal to update the harness.");
|
|
347
|
+
console.error("");
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
return await (0, upgradeNudge_1.promptYesNo)(" Update the harness now? [y/N] ");
|
|
351
|
+
},
|
|
214
352
|
});
|
|
215
353
|
const metadataPlugin = (0, url_1.pathToFileURL)(assetPath("opencode-plugin.mjs")).href;
|
|
216
354
|
const capturePlugin = (0, codieLauncher_1.resolveCapturePlugin)({ homeDir: os.homedir(), existsSync: fs.existsSync });
|
|
@@ -232,6 +370,13 @@ async function main() {
|
|
|
232
370
|
OPENCODE_CONFIG_CONTENT: JSON.stringify(config),
|
|
233
371
|
};
|
|
234
372
|
const runtimeArgs = (0, codieLauncher_1.withCodieWorkingDirectory)(parsed.args, process.cwd());
|
|
373
|
+
const nonInteractiveRun = runtimeArgs[0] === "run";
|
|
374
|
+
if (nonInteractiveRun)
|
|
375
|
+
env.CODIEDEV_SECURITY_BLOCK_OUTPUT = "marker";
|
|
376
|
+
if (nonInteractiveRun) {
|
|
377
|
+
process.exitCode = (await runNonInteractive(runtime, runtimeArgs, env)) ?? 1;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
235
380
|
const result = (0, child_process_1.spawnSync)(runtime, runtimeArgs, { stdio: "inherit", env });
|
|
236
381
|
if (result.error)
|
|
237
382
|
throw result.error;
|
package/dist/codieLauncher.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export interface CatalogModel {
|
|
|
2
2
|
id: string;
|
|
3
3
|
name?: string;
|
|
4
4
|
}
|
|
5
|
+
export declare const CODIE_SECURITY_BLOCK_MARKER = "__CODIEDEV_SECURITY_BLOCK__";
|
|
6
|
+
/** Extract the private adapter marker used to carry a local block reason
|
|
7
|
+
* across OpenCode's generic 500 boundary. The marker never contains prompt or
|
|
8
|
+
* tool input — only the deterministic policy reason. */
|
|
9
|
+
export declare function extractCodieSecurityBlock(stderr: string): string | undefined;
|
|
5
10
|
/**
|
|
6
11
|
* Pin non-interactive runs to the directory where `codie` was launched.
|
|
7
12
|
* OpenCode can reuse a previously-known project instance; without an explicit
|
package/dist/codieLauncher.js
CHANGED
|
@@ -42,7 +42,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
42
42
|
};
|
|
43
43
|
})();
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.HIDDEN_WORDMARK = exports.CODIEDEV_WORDMARK = exports.LEGACY_CODIEDEV_WORDMARK = exports.STOCK_WORDMARK = exports.BRAND_PLUGIN_SPEC = void 0;
|
|
45
|
+
exports.HIDDEN_WORDMARK = exports.CODIEDEV_WORDMARK = exports.LEGACY_CODIEDEV_WORDMARK = exports.STOCK_WORDMARK = exports.BRAND_PLUGIN_SPEC = exports.CODIE_SECURITY_BLOCK_MARKER = void 0;
|
|
46
|
+
exports.extractCodieSecurityBlock = extractCodieSecurityBlock;
|
|
46
47
|
exports.withCodieWorkingDirectory = withCodieWorkingDirectory;
|
|
47
48
|
exports.bareModel = bareModel;
|
|
48
49
|
exports.buildCodieConfig = buildCodieConfig;
|
|
@@ -62,6 +63,26 @@ const DEFAULT_NAMES = {
|
|
|
62
63
|
"z-ai/glm-5.2": "GLM 5.2 · Fireworks ZDR",
|
|
63
64
|
"moonshotai/kimi-k2.7-code": "Kimi K2.7 Code · Fireworks ZDR",
|
|
64
65
|
};
|
|
66
|
+
exports.CODIE_SECURITY_BLOCK_MARKER = "__CODIEDEV_SECURITY_BLOCK__";
|
|
67
|
+
/** Extract the private adapter marker used to carry a local block reason
|
|
68
|
+
* across OpenCode's generic 500 boundary. The marker never contains prompt or
|
|
69
|
+
* tool input — only the deterministic policy reason. */
|
|
70
|
+
function extractCodieSecurityBlock(stderr) {
|
|
71
|
+
for (const line of stderr.split(/\r?\n/)) {
|
|
72
|
+
const markerIndex = line.indexOf(exports.CODIE_SECURITY_BLOCK_MARKER);
|
|
73
|
+
if (markerIndex < 0)
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(line.slice(markerIndex + exports.CODIE_SECURITY_BLOCK_MARKER.length));
|
|
77
|
+
if (typeof parsed?.reason === "string" && parsed.reason.trim())
|
|
78
|
+
return parsed.reason.trim();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Ignore malformed or foreign stderr and preserve the original runtime error.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
65
86
|
/**
|
|
66
87
|
* Pin non-interactive runs to the directory where `codie` was launched.
|
|
67
88
|
* OpenCode can reuse a previously-known project instance; without an explicit
|
package/dist/codieRuntime.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const OPENCODE_RUNTIME_SPEC = "opencode-ai@1.18.4";
|
|
2
|
+
export declare const OPENCODE_RUNTIME_VERSION: string;
|
|
2
3
|
export declare const CODIE_MIN_NODE_MAJOR = 20;
|
|
3
4
|
export type CodieRuntime = {
|
|
4
5
|
kind: "ready";
|
|
@@ -8,12 +9,21 @@ export type CodieRuntime = {
|
|
|
8
9
|
installDir: string;
|
|
9
10
|
binary: string;
|
|
10
11
|
spec: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "outdated";
|
|
14
|
+
installDir: string;
|
|
15
|
+
binary: string;
|
|
16
|
+
spec: string;
|
|
17
|
+
currentVersion: string;
|
|
18
|
+
expectedVersion: string;
|
|
11
19
|
};
|
|
12
20
|
export declare function codieRuntimeDir(stateDir: string): string;
|
|
13
|
-
export declare function
|
|
21
|
+
export declare function installedRuntimePackageJson(stateDir: string): string;
|
|
22
|
+
export declare function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion, resolveBundled, }: {
|
|
14
23
|
env: Record<string, string | undefined>;
|
|
15
24
|
stateDir: string;
|
|
16
25
|
existsSync: (candidate: string) => boolean;
|
|
26
|
+
readInstalledVersion?: (packageJson: string) => string | null;
|
|
17
27
|
resolveBundled: () => string | null;
|
|
18
28
|
}): CodieRuntime;
|
|
19
29
|
export declare function assertCodieNodeVersion(version: string): void;
|
package/dist/codieRuntime.js
CHANGED
|
@@ -43,8 +43,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
43
43
|
};
|
|
44
44
|
})();
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.CODIE_MIN_NODE_MAJOR = exports.OPENCODE_RUNTIME_SPEC = void 0;
|
|
46
|
+
exports.CODIE_MIN_NODE_MAJOR = exports.OPENCODE_RUNTIME_VERSION = exports.OPENCODE_RUNTIME_SPEC = void 0;
|
|
47
47
|
exports.codieRuntimeDir = codieRuntimeDir;
|
|
48
|
+
exports.installedRuntimePackageJson = installedRuntimePackageJson;
|
|
48
49
|
exports.resolveCodieRuntime = resolveCodieRuntime;
|
|
49
50
|
exports.assertCodieNodeVersion = assertCodieNodeVersion;
|
|
50
51
|
const path = __importStar(require("path"));
|
|
@@ -52,6 +53,7 @@ const path = __importStar(require("path"));
|
|
|
52
53
|
// recognizes byte-for-byte, so a floating version would silently regress the
|
|
53
54
|
// terminal branding whenever OpenCode ships a new build.
|
|
54
55
|
exports.OPENCODE_RUNTIME_SPEC = "opencode-ai@1.18.4";
|
|
56
|
+
exports.OPENCODE_RUNTIME_VERSION = exports.OPENCODE_RUNTIME_SPEC.slice(exports.OPENCODE_RUNTIME_SPEC.lastIndexOf("@") + 1);
|
|
55
57
|
// The harness needs 20; capture does not. package.json `engines` stays >=18 so
|
|
56
58
|
// `codiedev connect` still installs for Node 18 users — this floor is enforced
|
|
57
59
|
// at `codie` launch instead.
|
|
@@ -62,7 +64,10 @@ function codieRuntimeDir(stateDir) {
|
|
|
62
64
|
function installedBinary(stateDir) {
|
|
63
65
|
return path.join(codieRuntimeDir(stateDir), "node_modules", "opencode-ai", "bin", "opencode.exe");
|
|
64
66
|
}
|
|
65
|
-
function
|
|
67
|
+
function installedRuntimePackageJson(stateDir) {
|
|
68
|
+
return path.join(codieRuntimeDir(stateDir), "node_modules", "opencode-ai", "package.json");
|
|
69
|
+
}
|
|
70
|
+
function resolveCodieRuntime({ env, stateDir, existsSync, readInstalledVersion, resolveBundled, }) {
|
|
66
71
|
// 1. Explicit override — the air-gapped / pre-provisioned escape hatch.
|
|
67
72
|
const override = env.CODIEDEV_OPENCODE_BINARY?.trim();
|
|
68
73
|
if (override) {
|
|
@@ -78,8 +83,20 @@ function resolveCodieRuntime({ env, stateDir, existsSync, resolveBundled, }) {
|
|
|
78
83
|
return { kind: "ready", binary: bundled };
|
|
79
84
|
// 3. The lazily installed engine.
|
|
80
85
|
const binary = installedBinary(stateDir);
|
|
81
|
-
if (existsSync(binary))
|
|
86
|
+
if (existsSync(binary)) {
|
|
87
|
+
const currentVersion = readInstalledVersion?.(installedRuntimePackageJson(stateDir)) ?? null;
|
|
88
|
+
if (currentVersion && currentVersion !== exports.OPENCODE_RUNTIME_VERSION) {
|
|
89
|
+
return {
|
|
90
|
+
kind: "outdated",
|
|
91
|
+
installDir: codieRuntimeDir(stateDir),
|
|
92
|
+
binary,
|
|
93
|
+
spec: exports.OPENCODE_RUNTIME_SPEC,
|
|
94
|
+
currentVersion,
|
|
95
|
+
expectedVersion: exports.OPENCODE_RUNTIME_VERSION,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
82
98
|
return { kind: "ready", binary };
|
|
99
|
+
}
|
|
83
100
|
return { kind: "missing", installDir: codieRuntimeDir(stateDir), binary, spec: exports.OPENCODE_RUNTIME_SPEC };
|
|
84
101
|
}
|
|
85
102
|
function assertCodieNodeVersion(version) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CodieRuntime } from "./codieRuntime";
|
|
2
|
+
type InstallableCodieRuntime = Exclude<CodieRuntime, {
|
|
3
|
+
kind: "ready";
|
|
4
|
+
}>;
|
|
2
5
|
export declare function buildRuntimeInstallArgs(stagingDir: string, spec: string): string[];
|
|
3
6
|
export interface InstallDeps {
|
|
4
|
-
runtime:
|
|
5
|
-
kind: "missing";
|
|
6
|
-
}>;
|
|
7
|
+
runtime: InstallableCodieRuntime;
|
|
7
8
|
makeStagingDir: () => string;
|
|
8
9
|
run: (args: string[]) => {
|
|
9
10
|
status: number | null;
|
|
@@ -15,13 +16,13 @@ export interface InstallDeps {
|
|
|
15
16
|
log: (message: string) => void;
|
|
16
17
|
}
|
|
17
18
|
export declare function installCodieRuntime(deps: InstallDeps): void;
|
|
18
|
-
export declare function ensureCodieRuntimeBinary({ resolve, install, }: {
|
|
19
|
+
export declare function ensureCodieRuntimeBinary({ resolve, install, confirmUpdate, }: {
|
|
19
20
|
resolve: () => CodieRuntime;
|
|
20
|
-
install: (runtime:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}>, stateDir: string): void;
|
|
21
|
+
install: (runtime: InstallableCodieRuntime) => void;
|
|
22
|
+
confirmUpdate?: (runtime: Extract<CodieRuntime, {
|
|
23
|
+
kind: "outdated";
|
|
24
|
+
}>) => Promise<boolean>;
|
|
25
|
+
}): Promise<string>;
|
|
26
|
+
export declare function installCodieRuntimeOnDisk(runtime: InstallableCodieRuntime, stateDir: string): void;
|
|
27
27
|
export declare function defaultStagingRoot(): string;
|
|
28
|
+
export {};
|
|
@@ -98,10 +98,12 @@ function installCodieRuntime(deps) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
// Resolve the engine, downloading it once if this is the first `codie` run.
|
|
101
|
-
function ensureCodieRuntimeBinary({ resolve, install, }) {
|
|
101
|
+
async function ensureCodieRuntimeBinary({ resolve, install, confirmUpdate = async () => false, }) {
|
|
102
102
|
const runtime = resolve();
|
|
103
103
|
if (runtime.kind === "ready")
|
|
104
104
|
return runtime.binary;
|
|
105
|
+
if (runtime.kind === "outdated" && !(await confirmUpdate(runtime)))
|
|
106
|
+
return runtime.binary;
|
|
105
107
|
install(runtime);
|
|
106
108
|
return runtime.binary;
|
|
107
109
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentSurface, EndpointSecurityPolicy } from "./security";
|
|
2
|
+
import type { AgentSecurityEntitlement } from "./securityFeature";
|
|
3
|
+
export interface CodieStartupConfig extends AgentSecurityEntitlement {
|
|
4
|
+
backendUrl?: string;
|
|
5
|
+
token?: string;
|
|
6
|
+
companyId?: string;
|
|
7
|
+
deviceId?: string;
|
|
8
|
+
}
|
|
9
|
+
export type CodiePolicyRefreshResult = {
|
|
10
|
+
status: "disabled";
|
|
11
|
+
} | {
|
|
12
|
+
status: "synced";
|
|
13
|
+
policy: EndpointSecurityPolicy;
|
|
14
|
+
} | {
|
|
15
|
+
status: "cached";
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
18
|
+
/** Refresh the endpoint policy before Codie hands the terminal to OpenCode.
|
|
19
|
+
* Enforcement remains available offline because hooks keep using the last
|
|
20
|
+
* verified company-scoped policy when this best-effort refresh cannot land. */
|
|
21
|
+
export declare function refreshCodieSecurityPolicy(config: CodieStartupConfig, syncPolicy?: (config: {
|
|
22
|
+
backendUrl: string;
|
|
23
|
+
token: string;
|
|
24
|
+
companyId: string;
|
|
25
|
+
deviceId?: string;
|
|
26
|
+
}, source?: AgentSurface) => Promise<EndpointSecurityPolicy>): Promise<CodiePolicyRefreshResult>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.refreshCodieSecurityPolicy = refreshCodieSecurityPolicy;
|
|
4
|
+
const securityFeature_1 = require("./securityFeature");
|
|
5
|
+
const securityPolicy_1 = require("./securityPolicy");
|
|
6
|
+
/** Refresh the endpoint policy before Codie hands the terminal to OpenCode.
|
|
7
|
+
* Enforcement remains available offline because hooks keep using the last
|
|
8
|
+
* verified company-scoped policy when this best-effort refresh cannot land. */
|
|
9
|
+
async function refreshCodieSecurityPolicy(config, syncPolicy = securityPolicy_1.syncEndpointSecurityPolicy) {
|
|
10
|
+
if (!(0, securityFeature_1.isAgentSecurityEnabled)(config))
|
|
11
|
+
return { status: "disabled" };
|
|
12
|
+
if (!config.backendUrl || !config.token || !config.companyId) {
|
|
13
|
+
return { status: "cached", error: "connected account details are incomplete" };
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const policy = await syncPolicy({
|
|
17
|
+
backendUrl: config.backendUrl,
|
|
18
|
+
token: config.token,
|
|
19
|
+
companyId: config.companyId,
|
|
20
|
+
...(config.deviceId ? { deviceId: config.deviceId } : {}),
|
|
21
|
+
}, "codie-harness");
|
|
22
|
+
return { status: "synced", policy };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return {
|
|
26
|
+
status: "cached",
|
|
27
|
+
error: error instanceof Error ? error.message : String(error),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -45,6 +45,8 @@ const version_1 = require("../version");
|
|
|
45
45
|
const upgradeNudge_1 = require("../upgradeNudge");
|
|
46
46
|
const captureReadiness_1 = require("../captureReadiness");
|
|
47
47
|
const captureLog_1 = require("../captureLog");
|
|
48
|
+
const securityDoctor_1 = require("../securityDoctor");
|
|
49
|
+
const securityFeature_1 = require("../securityFeature");
|
|
48
50
|
const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), ".claude", "settings.json");
|
|
49
51
|
const CLAUDE_INSTRUCTIONS_PATH = path.join(os.homedir(), ".claude", "CLAUDE.md");
|
|
50
52
|
const CODEX_HOOKS_PATH = path.join(os.homedir(), ".codex", "hooks.json");
|
|
@@ -482,6 +484,12 @@ async function runDoctor(_args) {
|
|
|
482
484
|
detail: "not installed on this machine",
|
|
483
485
|
});
|
|
484
486
|
}
|
|
487
|
+
// Agent Security is part of installation health, not a separate setup
|
|
488
|
+
// concept. Keep `codiedev security doctor` as an alias for focused output,
|
|
489
|
+
// while the main doctor remains the one command users need to remember.
|
|
490
|
+
if ((0, securityFeature_1.isAgentSecurityEnabled)(config)) {
|
|
491
|
+
checks.push(...(0, securityDoctor_1.securityDoctorChecks)((0, securityDoctor_1.collectSecurityDoctorReport)(config.companyId)));
|
|
492
|
+
}
|
|
485
493
|
// 8. gh / glab CLIs (optional — only needed for reverse-ticket from a
|
|
486
494
|
// specific PR/MR URL; branch mode works without either).
|
|
487
495
|
checks.push({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSecurityCommand(args: string[]): Promise<void>;
|