codiedev 0.9.51 → 0.9.53
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 +8 -1
- package/dist/captureLog.d.ts +1 -1
- package/dist/cli.js +7 -22
- package/dist/codie.js +1 -146
- package/dist/codieLauncher.d.ts +0 -5
- package/dist/codieLauncher.js +1 -22
- package/dist/codieRuntime.d.ts +1 -11
- package/dist/codieRuntime.js +3 -20
- package/dist/codieRuntimeInstall.d.ts +11 -12
- package/dist/codieRuntimeInstall.js +1 -3
- package/dist/commands/doctor.js +0 -8
- package/dist/connect.js +4 -53
- package/dist/cursorHooks.d.ts +9 -0
- package/dist/cursorHooks.js +56 -0
- package/dist/hook.js +95 -227
- package/dist/limitEvent.d.ts +4 -1
- package/dist/limitEvent.js +54 -0
- package/dist/limitEventReporter.d.ts +21 -0
- package/dist/limitEventReporter.js +91 -0
- package/dist/mcp.js +1 -93
- package/dist/repoResolver.js +1 -1
- package/dist/upgradeNudge.d.ts +0 -1
- package/dist/upgradeNudge.js +0 -1
- package/dist/utils.d.ts +10 -8
- package/dist/utils.js +174 -199
- package/package.json +5 -11
- package/dist/codieStartup.d.ts +0 -24
- package/dist/codieStartup.js +0 -29
- package/dist/commands/post.d.ts +0 -2
- package/dist/commands/post.js +0 -96
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/security.js +0 -156
- package/dist/security.d.ts +0 -97
- package/dist/security.js +0 -422
- 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 -62
- package/dist/securityInstall.js +0 -227
- 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 -32
- package/dist/securityPolicy.js +0 -179
- package/dist/securitySession.d.ts +0 -6
- package/dist/securitySession.js +0 -72
package/dist/utils.js
CHANGED
|
@@ -64,7 +64,6 @@ exports.openCodeInstalled = openCodeInstalled;
|
|
|
64
64
|
exports.installOpenCodeCapturePlugin = installOpenCodeCapturePlugin;
|
|
65
65
|
exports.vscodeCopilotInstalled = vscodeCopilotInstalled;
|
|
66
66
|
exports.upsertCodiedevHook = upsertCodiedevHook;
|
|
67
|
-
exports.removeCodiedevHook = removeCodiedevHook;
|
|
68
67
|
exports.installHook = installHook;
|
|
69
68
|
exports.ensureDiscoveryHookInstalled = ensureDiscoveryHookInstalled;
|
|
70
69
|
exports.ensureBackupHookInstalled = ensureBackupHookInstalled;
|
|
@@ -83,7 +82,9 @@ exports.cleanupVSCodeMcp = cleanupVSCodeMcp;
|
|
|
83
82
|
exports.cleanupCodexMcp = cleanupCodexMcp;
|
|
84
83
|
exports.installCodexMcp = installCodexMcp;
|
|
85
84
|
exports.installCodexHook = installCodexHook;
|
|
85
|
+
exports.ensureCodexLimitHookInstalled = ensureCodexLimitHookInstalled;
|
|
86
86
|
exports.installCursorHook = installCursorHook;
|
|
87
|
+
exports.ensureCursorCodiedevHooksInstalled = ensureCursorCodiedevHooksInstalled;
|
|
87
88
|
exports.parseClaudeSteering = parseClaudeSteering;
|
|
88
89
|
exports.parseCodexSteering = parseCodexSteering;
|
|
89
90
|
exports.parseSteering = parseSteering;
|
|
@@ -99,6 +100,8 @@ exports.dispatchDeferredUpload = dispatchDeferredUpload;
|
|
|
99
100
|
exports.sweepPendingCaptures = sweepPendingCaptures;
|
|
100
101
|
exports.resolveCaptureFinal = resolveCaptureFinal;
|
|
101
102
|
exports.finishInputTmpFile = finishInputTmpFile;
|
|
103
|
+
exports.captureUploadTmpFile = captureUploadTmpFile;
|
|
104
|
+
exports.cancelDeferredCaptureUpload = cancelDeferredCaptureUpload;
|
|
102
105
|
exports.decideDispatch = decideDispatch;
|
|
103
106
|
exports.readSessionUploadState = readSessionUploadState;
|
|
104
107
|
exports.writeSessionUploadState = writeSessionUploadState;
|
|
@@ -109,6 +112,7 @@ exports.readLastCapture = readLastCapture;
|
|
|
109
112
|
exports.runCaptureUpload = runCaptureUpload;
|
|
110
113
|
exports.runDeferredUpload = runDeferredUpload;
|
|
111
114
|
exports.transcriptReadSkipReason = transcriptReadSkipReason;
|
|
115
|
+
exports.shouldReportCaptureSkip = shouldReportCaptureSkip;
|
|
112
116
|
const fs = __importStar(require("fs"));
|
|
113
117
|
const path = __importStar(require("path"));
|
|
114
118
|
const os = __importStar(require("os"));
|
|
@@ -852,7 +856,7 @@ function buildCapturePayload(input) {
|
|
|
852
856
|
sessionStartedAt: input.sessionStartedAt ?? undefined,
|
|
853
857
|
interruptCount: input.interruptCount,
|
|
854
858
|
rejectionCount: input.rejectionCount,
|
|
855
|
-
// final is only emitted when explicitly set (not for Codex
|
|
859
|
+
// final is only emitted when explicitly set (not for Codex).
|
|
856
860
|
final: input.final,
|
|
857
861
|
costUsd: input.costUsd,
|
|
858
862
|
};
|
|
@@ -1052,15 +1056,11 @@ function openCodeInstalled() {
|
|
|
1052
1056
|
/** Install a dormant global OpenCode adapter. Installing it unconditionally
|
|
1053
1057
|
* keeps `codiedev connect` as the only setup step even when Codie/OpenCode is
|
|
1054
1058
|
* installed later. OpenCode loads this file from its documented plugin dir. */
|
|
1055
|
-
function installOpenCodeCapturePlugin(
|
|
1059
|
+
function installOpenCodeCapturePlugin() {
|
|
1056
1060
|
const start = resolveHookInvocation("session-start-opencode");
|
|
1057
1061
|
const capture = resolveHookInvocation("capture-opencode");
|
|
1058
1062
|
const finish = resolveHookInvocation("capture-opencode-final");
|
|
1059
|
-
const backup = resolveHookInvocation("backup");
|
|
1060
1063
|
const prompt = resolveHookInvocation("discover-codie");
|
|
1061
|
-
const securityPre = resolveHookInvocation("security-pre-codie");
|
|
1062
|
-
const securityPost = resolveHookInvocation("security-post-codie");
|
|
1063
|
-
const securityFailure = resolveHookInvocation("security-failure-codie");
|
|
1064
1064
|
const source = `// Managed by codiedev connect. Safe to remove and reinstall.\n` +
|
|
1065
1065
|
`import { spawn, spawnSync } from "node:child_process";\n` +
|
|
1066
1066
|
`import { mkdir, writeFile } from "node:fs/promises";\n` +
|
|
@@ -1069,15 +1069,9 @@ function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
|
1069
1069
|
`const START = ${JSON.stringify(start)};\n` +
|
|
1070
1070
|
`const CAPTURE = ${JSON.stringify(capture)};\n` +
|
|
1071
1071
|
`const FINISH = ${JSON.stringify(finish)};\n` +
|
|
1072
|
-
`const BACKUP = ${JSON.stringify(backup)};\n` +
|
|
1073
1072
|
`const PROMPT = ${JSON.stringify(prompt)};\n` +
|
|
1074
|
-
`const SECURITY_PRE = ${JSON.stringify(securityPre)};\n` +
|
|
1075
|
-
`const SECURITY_POST = ${JSON.stringify(securityPost)};\n` +
|
|
1076
|
-
`const SECURITY_FAILURE = ${JSON.stringify(securityFailure)};\n` +
|
|
1077
|
-
`const AGENT_SECURITY_ENABLED = ${JSON.stringify(agentSecurityEnabled)};\n` +
|
|
1078
1073
|
`const fingerprints = new Map();\n\n` +
|
|
1079
1074
|
`const transcriptPaths = new Map();\n\n` +
|
|
1080
|
-
`const sessionDirectories = new Map();\n\n` +
|
|
1081
1075
|
`function sessionId(event) {\n` +
|
|
1082
1076
|
` return event?.properties?.sessionID ?? event?.properties?.sessionId ?? event?.properties?.info?.id ?? null;\n` +
|
|
1083
1077
|
`}\n\n` +
|
|
@@ -1091,47 +1085,17 @@ function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
|
1091
1085
|
`function dispatchSync(invocation, payload) {\n` +
|
|
1092
1086
|
` try { spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), stdio: ["pipe", "ignore", "ignore"], timeout: 1500 }); } catch {}\n` +
|
|
1093
1087
|
`}\n\n` +
|
|
1094
|
-
`function
|
|
1095
|
-
` const result = spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 1500 });\n` +
|
|
1096
|
-
` if (result.error) return null;\n` +
|
|
1088
|
+
`function invokeSync(invocation, payload) {\n` +
|
|
1097
1089
|
` try {\n` +
|
|
1098
|
-
` const
|
|
1099
|
-
`
|
|
1100
|
-
` return
|
|
1101
|
-
` } catch
|
|
1102
|
-
` if (error instanceof SyntaxError) return null;\n` +
|
|
1103
|
-
` return null;\n` +
|
|
1104
|
-
` }\n` +
|
|
1090
|
+
` const result = spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 1500 });\n` +
|
|
1091
|
+
` if (result.error) return null;\n` +
|
|
1092
|
+
` return JSON.parse(result.stdout || "{}");\n` +
|
|
1093
|
+
` } catch { return null; }\n` +
|
|
1105
1094
|
`}\n\n` +
|
|
1106
1095
|
`export const CodiedevCapture = async ({ client, directory, worktree }) => {\n` +
|
|
1107
|
-
` const enforce = async (invocation, payload) => {\n` +
|
|
1108
|
-
` const reason = enforceSync(invocation, payload);\n` +
|
|
1109
|
-
` if (!reason) return;\n` +
|
|
1110
|
-
` try { await client.tui.showToast({ title: "Codie Security", message: reason, variant: "error", duration: 10000 }); } catch {}\n` +
|
|
1111
|
-
` if (process.env.CODIEDEV_SECURITY_BLOCK_OUTPUT === "marker") {\n` +
|
|
1112
|
-
` process.stderr.write("__CODIEDEV_SECURITY_BLOCK__" + JSON.stringify({ reason }) + "\\n");\n` +
|
|
1113
|
-
` process.exit(1);\n` +
|
|
1114
|
-
` } else {\n` +
|
|
1115
|
-
` process.stderr.write("\\nCodie Security blocked this action:\\n" + reason + "\\n");\n` +
|
|
1116
|
-
` }\n` +
|
|
1117
|
-
` throw new Error(reason);\n` +
|
|
1118
|
-
` };\n` +
|
|
1119
|
-
` const resolveCwd = async (id, hint) => {\n` +
|
|
1120
|
-
` if (typeof hint === "string" && hint && hint !== "/") { sessionDirectories.set(id, hint); return hint; }\n` +
|
|
1121
|
-
` const remembered = sessionDirectories.get(id);\n` +
|
|
1122
|
-
` if (remembered) return remembered;\n` +
|
|
1123
|
-
` try {\n` +
|
|
1124
|
-
` const response = await client.session.get({ path: { id } });\n` +
|
|
1125
|
-
` const session = response?.data ?? response;\n` +
|
|
1126
|
-
` if (typeof session?.directory === "string" && session.directory) { sessionDirectories.set(id, session.directory); return session.directory; }\n` +
|
|
1127
|
-
` } catch {}\n` +
|
|
1128
|
-
` const fallback = worktree || directory || process.cwd();\n` +
|
|
1129
|
-
` sessionDirectories.set(id, fallback);\n` +
|
|
1130
|
-
` return fallback;\n` +
|
|
1131
|
-
` };\n` +
|
|
1132
1096
|
` const finalizeAll = () => {\n` +
|
|
1133
1097
|
` for (const [id, transcriptPath] of transcriptPaths) {\n` +
|
|
1134
|
-
` const cwd =
|
|
1098
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1135
1099
|
` dispatchSync(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1136
1100
|
` }\n` +
|
|
1137
1101
|
` };\n` +
|
|
@@ -1139,27 +1103,16 @@ function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
|
1139
1103
|
` return ({\n` +
|
|
1140
1104
|
` "chat.message": async (input, output) => {\n` +
|
|
1141
1105
|
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1142
|
-
` const cwd =
|
|
1106
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1143
1107
|
` const prompt = Array.isArray(output?.parts) ? output.parts.filter((part) => part?.type === "text").map((part) => part.text || "").join("\\n") : "";\n` +
|
|
1144
|
-
`
|
|
1145
|
-
`
|
|
1146
|
-
`
|
|
1147
|
-
` if (!AGENT_SECURITY_ENABLED) return;\n` +
|
|
1148
|
-
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1149
|
-
` const cwd = await resolveCwd(id);\n` +
|
|
1150
|
-
` await enforce(SECURITY_PRE, { sessionId: id, cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: output?.args ?? output });\n` +
|
|
1151
|
-
` },\n` +
|
|
1152
|
-
` "tool.execute.after": async (input, output) => {\n` +
|
|
1153
|
-
` if (!AGENT_SECURITY_ENABLED) return;\n` +
|
|
1154
|
-
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1155
|
-
` const cwd = await resolveCwd(id);\n` +
|
|
1156
|
-
` const payload = { sessionId: id, cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: input?.args, toolResponse: output };\n` +
|
|
1157
|
-
` dispatch(output?.error ? SECURITY_FAILURE : SECURITY_POST, payload);\n` +
|
|
1108
|
+
` const decision = invokeSync(PROMPT, { session_id: id, cwd, prompt });\n` +
|
|
1109
|
+
` const context = decision?.hookSpecificOutput?.additionalContext;\n` +
|
|
1110
|
+
` if (context && Array.isArray(output?.parts)) output.parts.push({ type: "text", text: context });\n` +
|
|
1158
1111
|
` },\n` +
|
|
1159
1112
|
` event: async ({ event }) => {\n` +
|
|
1160
1113
|
` const id = sessionId(event);\n` +
|
|
1161
1114
|
` if (!id) return;\n` +
|
|
1162
|
-
` const cwd =
|
|
1115
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1163
1116
|
` if (event.type === "session.created") {\n` +
|
|
1164
1117
|
` dispatch(START, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1165
1118
|
` return;\n` +
|
|
@@ -1168,7 +1121,6 @@ function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
|
1168
1121
|
` const transcriptPath = transcriptPaths.get(id);\n` +
|
|
1169
1122
|
` if (transcriptPath) dispatch(FINISH, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1170
1123
|
` transcriptPaths.delete(id);\n` +
|
|
1171
|
-
` sessionDirectories.delete(id);\n` +
|
|
1172
1124
|
` fingerprints.delete(id);\n` +
|
|
1173
1125
|
` return;\n` +
|
|
1174
1126
|
` }\n` +
|
|
@@ -1188,7 +1140,6 @@ function installOpenCodeCapturePlugin(agentSecurityEnabled = true) {
|
|
|
1188
1140
|
` await writeFile(transcriptPath, JSON.stringify(list), { mode: 0o600 });\n` +
|
|
1189
1141
|
` transcriptPaths.set(id, transcriptPath);\n` +
|
|
1190
1142
|
` dispatch(CAPTURE, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1191
|
-
` dispatch(BACKUP, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1192
1143
|
` } catch {}\n` +
|
|
1193
1144
|
` },\n` +
|
|
1194
1145
|
` });\n` +
|
|
@@ -1214,7 +1165,7 @@ function vscodeCopilotInstalled() {
|
|
|
1214
1165
|
return false;
|
|
1215
1166
|
}
|
|
1216
1167
|
}
|
|
1217
|
-
// Idempotently put a single codiedev hook (matcher ".*", running our hook.js
|
|
1168
|
+
// Idempotently put a single codiedev hook (matcher ".*" by default, running our hook.js
|
|
1218
1169
|
// with `subcommand`) into the given hook event, stripping any prior codiedev
|
|
1219
1170
|
// entries first so connect re-runs / upgrades refresh the resolved binary path.
|
|
1220
1171
|
// Mutates `hooks` in place. Exported for unit testing.
|
|
@@ -1246,41 +1197,12 @@ function upsertCodiedevHook(hooks, eventName, subcommand, matcher = ".*") {
|
|
|
1246
1197
|
return !(0, detection_1.isCodiedevHookCommand)(hook.matcher);
|
|
1247
1198
|
});
|
|
1248
1199
|
filtered.push({
|
|
1249
|
-
matcher,
|
|
1200
|
+
...(matcher === null ? {} : { matcher }),
|
|
1250
1201
|
hooks: [{ type: "command", command: resolveHookCommand(subcommand, preservedNodePath) }],
|
|
1251
1202
|
});
|
|
1252
1203
|
hooks[eventName] = filtered;
|
|
1253
1204
|
}
|
|
1254
|
-
|
|
1255
|
-
function removeCodiedevHook(hooks, eventName) {
|
|
1256
|
-
const entries = hooks[eventName];
|
|
1257
|
-
if (!Array.isArray(entries))
|
|
1258
|
-
return false;
|
|
1259
|
-
let changed = false;
|
|
1260
|
-
const filtered = entries.flatMap((entry) => {
|
|
1261
|
-
const record = entry;
|
|
1262
|
-
const inner = record.hooks;
|
|
1263
|
-
if (!Array.isArray(inner))
|
|
1264
|
-
return [record];
|
|
1265
|
-
const remaining = inner.filter((hook) => {
|
|
1266
|
-
const remove = (0, detection_1.isCodiedevHookCommand)(hook.command);
|
|
1267
|
-
if (remove)
|
|
1268
|
-
changed = true;
|
|
1269
|
-
return !remove;
|
|
1270
|
-
});
|
|
1271
|
-
if (remaining.length === 0)
|
|
1272
|
-
return [];
|
|
1273
|
-
return [{ ...record, hooks: remaining }];
|
|
1274
|
-
});
|
|
1275
|
-
if (!changed)
|
|
1276
|
-
return false;
|
|
1277
|
-
if (filtered.length === 0)
|
|
1278
|
-
delete hooks[eventName];
|
|
1279
|
-
else
|
|
1280
|
-
hooks[eventName] = filtered;
|
|
1281
|
-
return true;
|
|
1282
|
-
}
|
|
1283
|
-
function installHook(agentSecurityEnabled = true) {
|
|
1205
|
+
function installHook() {
|
|
1284
1206
|
let settings = {};
|
|
1285
1207
|
try {
|
|
1286
1208
|
if (fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
@@ -1297,18 +1219,6 @@ function installHook(agentSecurityEnabled = true) {
|
|
|
1297
1219
|
const hooks = settings.hooks;
|
|
1298
1220
|
// SessionEnd → capture (upload the finished session for the team library).
|
|
1299
1221
|
upsertCodiedevHook(hooks, "SessionEnd", "capture");
|
|
1300
|
-
// Tool boundaries → ADR shadow detection. PreToolUse is the eventual
|
|
1301
|
-
// enforcement point; alert mode always returns allow.
|
|
1302
|
-
if (agentSecurityEnabled) {
|
|
1303
|
-
upsertCodiedevHook(hooks, "PreToolUse", "security-pre");
|
|
1304
|
-
upsertCodiedevHook(hooks, "PostToolUse", "security-post");
|
|
1305
|
-
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure");
|
|
1306
|
-
}
|
|
1307
|
-
else {
|
|
1308
|
-
removeCodiedevHook(hooks, "PreToolUse");
|
|
1309
|
-
removeCodiedevHook(hooks, "PostToolUse");
|
|
1310
|
-
removeCodiedevHook(hooks, "PostToolUseFailure");
|
|
1311
|
-
}
|
|
1312
1222
|
// UserPromptSubmit → discover (surface a matching team skill for the task).
|
|
1313
1223
|
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover");
|
|
1314
1224
|
// Stop → backup (auto-back-up changed artifact files to your private library).
|
|
@@ -1410,7 +1320,7 @@ function ensureLimitHookInstalled() {
|
|
|
1410
1320
|
// shrinks that window and removes the intra-process version entirely.
|
|
1411
1321
|
// The per-hook ensure* functions below are kept for tests/back-compat but are
|
|
1412
1322
|
// no longer called from the hook entrypoint.
|
|
1413
|
-
function ensureAllCodiedevHooksInstalled(
|
|
1323
|
+
function ensureAllCodiedevHooksInstalled() {
|
|
1414
1324
|
try {
|
|
1415
1325
|
if (!fs.existsSync(CLAUDE_SETTINGS_PATH))
|
|
1416
1326
|
return; // not a Claude Code user
|
|
@@ -1423,17 +1333,8 @@ function ensureAllCodiedevHooksInstalled(agentSecurityEnabled = readConfig()?.ag
|
|
|
1423
1333
|
["StopFailure", "limit", "rate_limit"],
|
|
1424
1334
|
["SessionStart", "session-start"],
|
|
1425
1335
|
];
|
|
1426
|
-
if (agentSecurityEnabled) {
|
|
1427
|
-
wanted.splice(1, 0, ["PreToolUse", "security-pre"], ["PostToolUse", "security-post"], ["PostToolUseFailure", "security-failure"]);
|
|
1428
|
-
}
|
|
1429
|
-
let changed = false;
|
|
1430
|
-
if (!agentSecurityEnabled) {
|
|
1431
|
-
for (const eventName of ["PreToolUse", "PostToolUse", "PostToolUseFailure"]) {
|
|
1432
|
-
changed = removeCodiedevHook(hooks, eventName) || changed;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
1336
|
const missing = wanted.filter(([eventName]) => !hasCodiedevHook(hooks, eventName));
|
|
1436
|
-
if (missing.length === 0
|
|
1337
|
+
if (missing.length === 0)
|
|
1437
1338
|
return; // steady state: no write, no churn
|
|
1438
1339
|
settings.hooks = hooks;
|
|
1439
1340
|
for (const [eventName, subcommand, matcher] of missing) {
|
|
@@ -1512,7 +1413,6 @@ thought, use the \`codiedev\` CLI via Bash:**
|
|
|
1512
1413
|
| "note that X is a follow-up" / "remember X" | \`codiedev note "<text>"\` |
|
|
1513
1414
|
| "promote the extracted spec" | \`codiedev promote <artifact-id>\` |
|
|
1514
1415
|
| "create a ticket" / "scope this into tickets" / "ticket this for me" | \`codiedev create-ticket [optional-inline-prompt]\` |
|
|
1515
|
-
| "post this to the team" / "publish to the feed" / "share with the team" | \`codiedev post --title "<t>" --body "<b>" [--intent <intent>] [--filename <key>] [--mention <name>]\` |
|
|
1516
1416
|
| "share with a teammate" / "give someone access" (no notification) | \`codiedev share <key> <name> [--role read\\|edit]\` |
|
|
1517
1417
|
| "send this to a teammate" / "ask someone to look at this" | \`codiedev send <key> <name> ["<msg>"]\` |
|
|
1518
1418
|
| "find something about X" / "has anyone solved Y?" | \`codiedev search "<query>" [--limit N]\` |
|
|
@@ -1546,31 +1446,6 @@ read it back to the user so they can confirm.
|
|
|
1546
1446
|
**Teammate names:** first name usually works (\`codiedev ping <name> ...\`).
|
|
1547
1447
|
If ambiguous, the CLI returns candidates — retry with the full email.
|
|
1548
1448
|
|
|
1549
|
-
**Feed posts (broad team reach):** use \`codiedev post\` when the user
|
|
1550
|
-
wants visibility beyond one teammate — e.g., announcing a new skill,
|
|
1551
|
-
asking the team who knows about X, proposing a change. Always pass
|
|
1552
|
-
\`--intent\` to match the request: \`share\` (here's a skill),
|
|
1553
|
-
\`request_review\`, \`request_expertise\` (stuck, who knows this),
|
|
1554
|
-
\`link_share\` (fyi), or \`rfc\` (proposing a change). Pass \`--filename\`
|
|
1555
|
-
to link a specific artifact and \`--mention <name>\` (repeatable) to
|
|
1556
|
-
tag teammates.
|
|
1557
|
-
|
|
1558
|
-
**Default feed-post shape.** When the user explicitly says "push/share/post
|
|
1559
|
-
to the feed", default to this shape unless they ask for something else:
|
|
1560
|
-
- **Title** ≤10 words: *what + why now*. Imperative or declarative — never
|
|
1561
|
-
"Check this out".
|
|
1562
|
-
- **Body** — three short lines, grounded in the current session and the
|
|
1563
|
-
artifact:
|
|
1564
|
-
- **Why this exists** — 1–2 sentences from session context (the problem
|
|
1565
|
-
or insight that produced the artifact).
|
|
1566
|
-
- **What's in it** — 1 sentence pulled from the artifact's frontmatter
|
|
1567
|
-
\`description:\` (skills) or first paragraph (specs). Don't restate
|
|
1568
|
-
the whole file — the card preview carries the detail.
|
|
1569
|
-
- **Ask** — 1 sentence. *Use it / review it / ping me with thoughts.*
|
|
1570
|
-
- **Filename**: always attach so the card preview renders.
|
|
1571
|
-
This is the default. If the user wants a one-liner or a different
|
|
1572
|
-
structure, follow their lead.
|
|
1573
|
-
|
|
1574
1449
|
**Search before pushing.** For any "save this as …" request, run
|
|
1575
1450
|
\`codiedev search "<query>"\` first. If prior art exists, pull it and
|
|
1576
1451
|
iterate rather than duplicating.
|
|
@@ -1874,7 +1749,7 @@ function installCodexMcp() {
|
|
|
1874
1749
|
// Codex doesn't have a SessionEnd event yet — Stop fires after every turn.
|
|
1875
1750
|
// The backend dedupes on (companyId, sessionId) and replaces the stored
|
|
1876
1751
|
// transcript on re-upload, so the latest turn's snapshot wins.
|
|
1877
|
-
function installCodexHook(
|
|
1752
|
+
function installCodexHook() {
|
|
1878
1753
|
let hooksFile = {};
|
|
1879
1754
|
try {
|
|
1880
1755
|
if (fs.existsSync(CODEX_HOOKS_PATH)) {
|
|
@@ -1889,8 +1764,9 @@ function installCodexHook(agentSecurityEnabled = true) {
|
|
|
1889
1764
|
hooksFile.hooks = {};
|
|
1890
1765
|
}
|
|
1891
1766
|
const hooks = hooksFile.hooks;
|
|
1892
|
-
if (!hooks.Stop)
|
|
1767
|
+
if (!hooks.Stop) {
|
|
1893
1768
|
hooks.Stop = [];
|
|
1769
|
+
}
|
|
1894
1770
|
const existing = hooks.Stop;
|
|
1895
1771
|
const filtered = existing.filter((hook) => {
|
|
1896
1772
|
const inner = hook.hooks;
|
|
@@ -1898,6 +1774,15 @@ function installCodexHook(agentSecurityEnabled = true) {
|
|
|
1898
1774
|
return true;
|
|
1899
1775
|
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
1900
1776
|
});
|
|
1777
|
+
filtered.push({
|
|
1778
|
+
hooks: [
|
|
1779
|
+
{
|
|
1780
|
+
type: "command",
|
|
1781
|
+
command: resolveHookCommand("limit-codex"),
|
|
1782
|
+
timeout: 10,
|
|
1783
|
+
},
|
|
1784
|
+
],
|
|
1785
|
+
});
|
|
1901
1786
|
filtered.push({
|
|
1902
1787
|
hooks: [
|
|
1903
1788
|
{
|
|
@@ -1918,28 +1803,59 @@ function installCodexHook(agentSecurityEnabled = true) {
|
|
|
1918
1803
|
],
|
|
1919
1804
|
});
|
|
1920
1805
|
hooks.Stop = filtered;
|
|
1921
|
-
// Codex
|
|
1922
|
-
//
|
|
1923
|
-
|
|
1924
|
-
if (agentSecurityEnabled) {
|
|
1925
|
-
upsertCodiedevHook(hooks, "PreToolUse", "security-pre-codex");
|
|
1926
|
-
upsertCodiedevHook(hooks, "PostToolUse", "security-post-codex");
|
|
1927
|
-
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure-codex");
|
|
1928
|
-
}
|
|
1929
|
-
else {
|
|
1930
|
-
removeCodiedevHook(hooks, "PreToolUse");
|
|
1931
|
-
removeCodiedevHook(hooks, "PostToolUse");
|
|
1932
|
-
removeCodiedevHook(hooks, "PostToolUseFailure");
|
|
1933
|
-
}
|
|
1934
|
-
// Prompt guard + skill discovery. The guard runs locally before any prompt
|
|
1935
|
-
// content can reach either the model or Codiedev's discovery endpoint.
|
|
1936
|
-
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex");
|
|
1806
|
+
// Codex models UserPromptSubmit as a non-matcher event (the trust-key model
|
|
1807
|
+
// intentionally excludes it from MATCHER_EVENTS), so omit the matcher key.
|
|
1808
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex", null);
|
|
1937
1809
|
if (!fs.existsSync(CODEX_DIR)) {
|
|
1938
1810
|
fs.mkdirSync(CODEX_DIR, { recursive: true });
|
|
1939
1811
|
}
|
|
1940
1812
|
fs.writeFileSync(CODEX_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1941
1813
|
(0, codexHookTrust_1.writeCodexHookTrust)(CODEX_CONFIG_PATH, CODEX_HOOKS_PATH, hooksFile);
|
|
1942
1814
|
}
|
|
1815
|
+
// Existing Codex users already have capture-codex on Stop. After a CLI
|
|
1816
|
+
// upgrade, that hook repairs newer lifecycle hooks without requiring connect.
|
|
1817
|
+
function ensureCodexLimitHookInstalled() {
|
|
1818
|
+
try {
|
|
1819
|
+
if (!fs.existsSync(CODEX_HOOKS_PATH))
|
|
1820
|
+
return;
|
|
1821
|
+
const hooksFile = JSON.parse(fs.readFileSync(CODEX_HOOKS_PATH, "utf8"));
|
|
1822
|
+
const hooks = (hooksFile.hooks ?? {});
|
|
1823
|
+
const stop = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
1824
|
+
const hasCommand = (entries, subcommand) => entries.some((group) => {
|
|
1825
|
+
const record = group;
|
|
1826
|
+
const flat = record.command;
|
|
1827
|
+
if (typeof flat === "string" && flat.trim().endsWith(subcommand))
|
|
1828
|
+
return true;
|
|
1829
|
+
const inner = record.hooks;
|
|
1830
|
+
return Array.isArray(inner) && inner.some((hook) => typeof hook.command === "string" && hook.command.trim().endsWith(subcommand));
|
|
1831
|
+
});
|
|
1832
|
+
let changed = false;
|
|
1833
|
+
if (!hasCommand(stop, "limit-codex")) {
|
|
1834
|
+
stop.unshift({
|
|
1835
|
+
hooks: [{ type: "command", command: resolveHookCommand("limit-codex"), timeout: 10 }],
|
|
1836
|
+
});
|
|
1837
|
+
hooks.Stop = stop;
|
|
1838
|
+
changed = true;
|
|
1839
|
+
}
|
|
1840
|
+
const promptEntries = Array.isArray(hooks.UserPromptSubmit)
|
|
1841
|
+
? hooks.UserPromptSubmit
|
|
1842
|
+
: [];
|
|
1843
|
+
if (!hasCommand(promptEntries, "discover-codex")) {
|
|
1844
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex", null);
|
|
1845
|
+
changed = true;
|
|
1846
|
+
}
|
|
1847
|
+
if (!changed)
|
|
1848
|
+
return;
|
|
1849
|
+
hooksFile.hooks = hooks;
|
|
1850
|
+
fs.writeFileSync(CODEX_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1851
|
+
// Trust keys include each hook's positional index. Adding the new hook at
|
|
1852
|
+
// the front shifts capture/backup, so refresh every Codiedev hash together.
|
|
1853
|
+
(0, codexHookTrust_1.writeCodexHookTrust)(CODEX_CONFIG_PATH, CODEX_HOOKS_PATH, hooksFile);
|
|
1854
|
+
}
|
|
1855
|
+
catch {
|
|
1856
|
+
// Best-effort: capture must never fail because hook repair did.
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1943
1859
|
// Cursor's hooks.json uses { version: 1, hooks: { sessionEnd: [{ command, type, timeout }] } }.
|
|
1944
1860
|
// `sessionEnd` fires when a conversation closes (completed, aborted, error,
|
|
1945
1861
|
// window_close, user_close); the base stdin payload includes transcript_path
|
|
@@ -1965,25 +1881,61 @@ function installCursorHook() {
|
|
|
1965
1881
|
hooksFile.hooks = {};
|
|
1966
1882
|
}
|
|
1967
1883
|
const hooks = hooksFile.hooks;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1884
|
+
const replaceManaged = (eventName, subcommands) => {
|
|
1885
|
+
const existing = Array.isArray(hooks[eventName])
|
|
1886
|
+
? hooks[eventName]
|
|
1887
|
+
: [];
|
|
1888
|
+
const preserved = existing.filter((hook) => !(0, detection_1.isCodiedevHookCommand)(hook.command));
|
|
1889
|
+
const managed = subcommands.map((subcommand) => ({
|
|
1890
|
+
command: resolveHookCommand(subcommand),
|
|
1891
|
+
type: "command",
|
|
1892
|
+
timeout: 30,
|
|
1893
|
+
}));
|
|
1894
|
+
if (preserved.length + managed.length === 0)
|
|
1895
|
+
delete hooks[eventName];
|
|
1896
|
+
else
|
|
1897
|
+
hooks[eventName] = [...preserved, ...managed];
|
|
1898
|
+
};
|
|
1899
|
+
replaceManaged("sessionStart", ["session-start-cursor"]);
|
|
1900
|
+
replaceManaged("sessionEnd", ["capture-cursor"]);
|
|
1901
|
+
replaceManaged("stop", ["backup-cursor"]);
|
|
1902
|
+
// Cursor cannot inject dynamic context from beforeSubmitPrompt today.
|
|
1903
|
+
// Remove older Codiedev discovery entries so prompts are not uploaded for
|
|
1904
|
+
// a response the host will discard; preserve any third-party hooks.
|
|
1905
|
+
replaceManaged("beforeSubmitPrompt", []);
|
|
1985
1906
|
fs.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
|
|
1986
1907
|
}
|
|
1908
|
+
function ensureCursorCodiedevHooksInstalled() {
|
|
1909
|
+
try {
|
|
1910
|
+
if (!fs.existsSync(CURSOR_HOOKS_PATH))
|
|
1911
|
+
return;
|
|
1912
|
+
const parsed = JSON.parse(fs.readFileSync(CURSOR_HOOKS_PATH, "utf8"));
|
|
1913
|
+
const hooks = parsed.hooks ?? {};
|
|
1914
|
+
const required = [
|
|
1915
|
+
["sessionStart", "session-start-cursor"],
|
|
1916
|
+
["sessionEnd", "capture-cursor"],
|
|
1917
|
+
["stop", "backup-cursor"],
|
|
1918
|
+
];
|
|
1919
|
+
const has = (eventName, subcommand) => {
|
|
1920
|
+
const entries = hooks[eventName];
|
|
1921
|
+
return Array.isArray(entries) && entries.some((entry) => {
|
|
1922
|
+
const command = entry.command;
|
|
1923
|
+
return typeof command === "string"
|
|
1924
|
+
&& (0, detection_1.isCodiedevHookCommand)(command)
|
|
1925
|
+
&& command.trim().endsWith(subcommand);
|
|
1926
|
+
});
|
|
1927
|
+
};
|
|
1928
|
+
const promptEntries = hooks.beforeSubmitPrompt;
|
|
1929
|
+
const hasStalePromptHook = Array.isArray(promptEntries) && promptEntries.some((entry) => (0, detection_1.isCodiedevHookCommand)(entry.command));
|
|
1930
|
+
if (!required.every(([eventName, subcommand]) => has(eventName, subcommand)) ||
|
|
1931
|
+
hasStalePromptHook) {
|
|
1932
|
+
installCursorHook();
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
catch {
|
|
1936
|
+
// Best-effort repair only.
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1987
1939
|
function parseClaudeSteering(content) {
|
|
1988
1940
|
let interrupts = 0;
|
|
1989
1941
|
let rejections = 0;
|
|
@@ -2598,18 +2550,21 @@ exports.SWEEP_MIN_AGE_MS = exports.CAPTURE_THROTTLE_MS + 2 * 60 * 1000;
|
|
|
2598
2550
|
// hook with no subcommand) are a true final signal. capture-opencode/
|
|
2599
2551
|
// capture-desktop (per-turn snapshot) and capture-opencode-final/
|
|
2600
2552
|
// capture-desktop-final (real session end) get an explicit false/true.
|
|
2601
|
-
//
|
|
2602
|
-
//
|
|
2603
|
-
//
|
|
2604
|
-
//
|
|
2605
|
-
//
|
|
2606
|
-
// for every
|
|
2553
|
+
// Cursor's capture-cursor is wired only to sessionEnd, so it is also a true
|
|
2554
|
+
// final signal. Everything else — capture-codex (Codex's Stop fires per-turn;
|
|
2555
|
+
// final:true would close the server-side session lease every turn) and any
|
|
2556
|
+
// FUTURE mode (e.g. a capture-windsurf added to resolveMode but not audited
|
|
2557
|
+
// here) — gets undefined, i.e. legacy server behaviour. Defaulting unknowns
|
|
2558
|
+
// to true would silently re-introduce the per-turn lease-close bug for every
|
|
2559
|
+
// new agent.
|
|
2607
2560
|
function resolveCaptureFinal(argv2) {
|
|
2608
2561
|
if (argv2 === "capture-opencode" || argv2 === "capture-desktop")
|
|
2609
2562
|
return false;
|
|
2610
2563
|
if (argv2 === "capture-opencode-final" || argv2 === "capture-desktop-final")
|
|
2611
2564
|
return true;
|
|
2612
|
-
return argv2 === "capture" || argv2 ===
|
|
2565
|
+
return argv2 === "capture" || argv2 === "capture-cursor" || argv2 === undefined
|
|
2566
|
+
? true
|
|
2567
|
+
: undefined;
|
|
2613
2568
|
}
|
|
2614
2569
|
// Build a UNIQUE finish-input tmpFile name per dispatch. Both the Stop hook and
|
|
2615
2570
|
// SessionEnd dispatch capture-finish children, and the child unlinks its input
|
|
@@ -2622,7 +2577,29 @@ function resolveCaptureFinal(argv2) {
|
|
|
2622
2577
|
// negligible volume (one tiny JSON per crashed child), though note Windows
|
|
2623
2578
|
// %TEMP% is not auto-cleaned — acceptable.
|
|
2624
2579
|
function finishInputTmpFile(sessionId) {
|
|
2625
|
-
|
|
2580
|
+
const safeId = String(sessionId ?? process.pid).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
2581
|
+
return path.join(os.tmpdir(), `codiedev-finish-${safeId}-${process.pid}-${Date.now()}.json`);
|
|
2582
|
+
}
|
|
2583
|
+
function captureUploadTmpFile(sessionId, mode, pid = process.pid, now = Date.now()) {
|
|
2584
|
+
const id = String(sessionId ?? pid).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
2585
|
+
return path.join(os.tmpdir(), mode === "immediate"
|
|
2586
|
+
? `codiedev-capture-${id}-${pid}-${now}.json`
|
|
2587
|
+
: `codiedev-capture-${id}.json`);
|
|
2588
|
+
}
|
|
2589
|
+
// Best-effort cancellation for a scheduled per-turn upload when a real final
|
|
2590
|
+
// capture arrives. Deferred jobs deliberately share one canonical filename;
|
|
2591
|
+
// removing it makes every sleeping uploader lose its later claim-by-rename.
|
|
2592
|
+
// The backend's capturedAt guard covers the narrow race where a child claimed
|
|
2593
|
+
// the file immediately before this unlink.
|
|
2594
|
+
function cancelDeferredCaptureUpload(sessionId, deps = fs) {
|
|
2595
|
+
try {
|
|
2596
|
+
deps.unlinkSync(captureUploadTmpFile(sessionId, "deferred"));
|
|
2597
|
+
return true;
|
|
2598
|
+
}
|
|
2599
|
+
catch {
|
|
2600
|
+
// Missing means there was nothing pending (or another process won).
|
|
2601
|
+
return false;
|
|
2602
|
+
}
|
|
2626
2603
|
}
|
|
2627
2604
|
// Decide whether to upload immediately or defer until the throttle window expires.
|
|
2628
2605
|
// final=true → always immediate (SessionEnd: close the session now)
|
|
@@ -2787,14 +2764,6 @@ async function runCaptureUpload(jobFile, deps) {
|
|
|
2787
2764
|
const { storageId } = await deps.uploadBytes(uploadUrl, Buffer.from(transcript, "base64"), "application/json");
|
|
2788
2765
|
body = { ...body, transcriptStorageId: storageId };
|
|
2789
2766
|
delete body.transcript;
|
|
2790
|
-
// Persist the storage id before the metadata POST. Previously a failed
|
|
2791
|
-
// captureSession request retried the entire direct upload, creating one
|
|
2792
|
-
// unreferenced raw transcript blob per attempt. Once the PUT succeeds,
|
|
2793
|
-
// every retry must reuse that exact blob.
|
|
2794
|
-
job.body = body;
|
|
2795
|
-
if (deps.writeFileSync) {
|
|
2796
|
-
deps.writeFileSync(jobFile, JSON.stringify(job), { mode: 0o600 });
|
|
2797
|
-
}
|
|
2798
2767
|
}
|
|
2799
2768
|
await deps.post(job.url, body, job.token);
|
|
2800
2769
|
// success — drop a "last session captured" breadcrumb (before removing the
|
|
@@ -2947,3 +2916,9 @@ function transcriptReadSkipReason(err) {
|
|
|
2947
2916
|
return "no-transcript";
|
|
2948
2917
|
return `transcript-unreadable:${code || "unknown"}`;
|
|
2949
2918
|
}
|
|
2919
|
+
// Per-turn snapshots are backups, not session endings. Reporting their local
|
|
2920
|
+
// read/empty condition as a session skip would hide or close coverage while the
|
|
2921
|
+
// agent is still running; only final/legacy capture paths emit the skip ping.
|
|
2922
|
+
function shouldReportCaptureSkip(final) {
|
|
2923
|
+
return final !== false;
|
|
2924
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.53",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"dev": "tsc --watch",
|
|
13
13
|
"prepublishOnly": "tsc",
|
|
14
14
|
"test": "vitest run",
|
|
15
|
-
"test:
|
|
16
|
-
"test:
|
|
17
|
-
"test:
|
|
15
|
+
"test:functional": "vitest run src/__tests__/limitEvent.functional.test.ts src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts",
|
|
16
|
+
"test:harness-parity": "vitest run src/__tests__/harnessParity.functional.test.ts src/__tests__/cursorParity.functional.test.ts",
|
|
17
|
+
"test:harness-binaries": "node scripts/checkHarnessBinaries.mjs",
|
|
18
18
|
"test:watch": "vitest"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
@@ -38,18 +38,12 @@
|
|
|
38
38
|
"session-capture"
|
|
39
39
|
],
|
|
40
40
|
"homepage": "https://codiedev.com",
|
|
41
|
-
"repository": {
|
|
42
|
-
"type": "git",
|
|
43
|
-
"url": "git+https://github.com/Signal-and-Code/codiedev.git",
|
|
44
|
-
"directory": "packages/codiedev-cli"
|
|
45
|
-
},
|
|
46
41
|
"license": "MIT",
|
|
47
42
|
"engines": {
|
|
48
43
|
"node": ">=18"
|
|
49
44
|
},
|
|
50
45
|
"publishConfig": {
|
|
51
|
-
"access": "public"
|
|
52
|
-
"provenance": true
|
|
46
|
+
"access": "public"
|
|
53
47
|
},
|
|
54
48
|
"devDependencies": {
|
|
55
49
|
"@types/node": "^20.0.0",
|
package/dist/codieStartup.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
}
|
|
8
|
-
export type CodiePolicyRefreshResult = {
|
|
9
|
-
status: "disabled";
|
|
10
|
-
} | {
|
|
11
|
-
status: "synced";
|
|
12
|
-
policy: EndpointSecurityPolicy;
|
|
13
|
-
} | {
|
|
14
|
-
status: "cached";
|
|
15
|
-
error: string;
|
|
16
|
-
};
|
|
17
|
-
/** Refresh the endpoint policy before Codie hands the terminal to OpenCode.
|
|
18
|
-
* Enforcement remains available offline because hooks keep using the last
|
|
19
|
-
* verified company-scoped policy when this best-effort refresh cannot land. */
|
|
20
|
-
export declare function refreshCodieSecurityPolicy(config: CodieStartupConfig, syncPolicy?: (config: {
|
|
21
|
-
backendUrl: string;
|
|
22
|
-
token: string;
|
|
23
|
-
companyId: string;
|
|
24
|
-
}, source?: AgentSurface) => Promise<EndpointSecurityPolicy>): Promise<CodiePolicyRefreshResult>;
|