fullcourtdefense-cli 1.33.0 → 1.34.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.
@@ -0,0 +1,19 @@
1
+ export declare function resolveCodexExecutable(localAppData?: string): string;
2
+ export interface CodexHook {
3
+ eventName: string;
4
+ command?: string;
5
+ enabled: boolean;
6
+ isManaged: boolean;
7
+ trustStatus: string;
8
+ sourcePath: string;
9
+ }
10
+ export interface CodexHookStatus {
11
+ state: 'unavailable' | 'not_configured' | 'approval_required' | 'disabled' | 'configured_unverified' | 'ready_for_probe';
12
+ managed: boolean;
13
+ message: string;
14
+ }
15
+ export declare function isFcdCodexHook(command: unknown): boolean;
16
+ /** Configuration readiness is deliberately not a claim that the backend received an event. */
17
+ export declare function summarizeCodexHooks(hooks: CodexHook[]): CodexHookStatus;
18
+ /** Read-only app-server handshake. No thread, model turn, trust write, or hook execution. */
19
+ export declare function getCodexHookStatus(cwd?: string, executable?: string, timeoutMs?: number): Promise<CodexHookStatus>;
@@ -0,0 +1,156 @@
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.resolveCodexExecutable = resolveCodexExecutable;
37
+ exports.isFcdCodexHook = isFcdCodexHook;
38
+ exports.summarizeCodexHooks = summarizeCodexHooks;
39
+ exports.getCodexHookStatus = getCodexHookStatus;
40
+ const child_process_1 = require("child_process");
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const os = __importStar(require("os"));
44
+ function resolveCodexExecutable(localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')) {
45
+ if (process.platform !== 'win32')
46
+ return 'codex';
47
+ const bin = path.join(localAppData, 'OpenAI', 'Codex', 'bin');
48
+ try {
49
+ const candidates = fs.readdirSync(bin).map(version => path.join(bin, version, 'codex.exe'))
50
+ .filter(file => fs.existsSync(file) && fs.statSync(file).isFile());
51
+ candidates.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
52
+ if (candidates.length)
53
+ return candidates[0];
54
+ }
55
+ catch { /* CLI-only or desktop absent: let spawn resolve PATH. */ }
56
+ return 'codex';
57
+ }
58
+ const EVENTS = ['preToolUse', 'postToolUse', 'userPromptSubmit'];
59
+ function isFcdCodexHook(command) {
60
+ if (typeof command !== 'string')
61
+ return false;
62
+ const encoded = /-EncodedCommand ([A-Za-z0-9+/=]+)$/.exec(command)?.[1];
63
+ const decoded = encoded ? Buffer.from(encoded, 'base64').toString('utf16le') : command;
64
+ return decoded.includes('--fcd-native-managed true') && decoded.includes('--agent-client codex');
65
+ }
66
+ /** Configuration readiness is deliberately not a claim that the backend received an event. */
67
+ function summarizeCodexHooks(hooks) {
68
+ const ours = hooks.filter(hook => isFcdCodexHook(hook.command));
69
+ const usable = (h) => h.enabled && (h.isManaged || h.trustStatus === 'trusted');
70
+ const complete = (items) => EVENTS.every(event => items.some(h => h.eventName === event));
71
+ const managed = complete(ours.filter(h => usable(h) && h.isManaged));
72
+ if (complete(ours.filter(usable)))
73
+ return { state: 'ready_for_probe', managed,
74
+ message: 'Codex hooks are trusted and enabled. Capture is not yet verified: run an action in the Codex app and confirm its event in Activity.' };
75
+ if (!complete(ours))
76
+ return { state: 'not_configured', managed: false,
77
+ message: 'Codex capture is incomplete: one or more FullCourtDefense hooks are missing.' };
78
+ if (ours.some(h => h.enabled && !h.isManaged && h.trustStatus !== 'trusted'))
79
+ return {
80
+ state: 'approval_required', managed: false,
81
+ message: 'Codex capture is inactive: hook approval is required. Company devices should use the administrator-managed installation.'
82
+ };
83
+ return { state: 'disabled', managed: false, message: 'Codex capture is inactive: required hooks are disabled.' };
84
+ }
85
+ /** Read-only app-server handshake. No thread, model turn, trust write, or hook execution. */
86
+ function getCodexHookStatus(cwd = process.cwd(), executable = resolveCodexExecutable(), timeoutMs = 10_000) {
87
+ return new Promise(resolve => {
88
+ const child = (0, child_process_1.spawn)(executable, ['app-server', '--stdio'], { stdio: ['pipe', 'pipe', 'ignore'], windowsHide: true });
89
+ let finished = false;
90
+ let buffer = '';
91
+ let received = 0;
92
+ const finish = (status) => {
93
+ if (finished)
94
+ return;
95
+ finished = true;
96
+ clearTimeout(timer);
97
+ child.kill();
98
+ resolve(status);
99
+ };
100
+ const unavailable = (message) => finish({ state: 'unavailable', managed: false, message });
101
+ const timer = setTimeout(() => unavailable('Codex hook status timed out; capture is not verified.'), timeoutMs);
102
+ const send = (value) => child.stdin.write(JSON.stringify(value) + '\n');
103
+ child.on('error', () => unavailable('Codex could not be started for hook diagnostics; capture is not verified.'));
104
+ child.stdin.on('error', () => unavailable('Codex hook diagnostics disconnected; capture is not verified.'));
105
+ child.on('exit', () => unavailable('Codex exited before reporting hook status; capture is not verified.'));
106
+ child.stdout.setEncoding('utf8');
107
+ child.stdout.on('data', (chunk) => {
108
+ received += Buffer.byteLength(chunk);
109
+ if (received > 2_000_000)
110
+ return unavailable('Codex hook diagnostics exceeded the response limit.');
111
+ buffer += chunk;
112
+ let newline;
113
+ while (!finished && (newline = buffer.indexOf('\n')) >= 0) {
114
+ const line = buffer.slice(0, newline);
115
+ buffer = buffer.slice(newline + 1);
116
+ let reply;
117
+ try {
118
+ reply = JSON.parse(line);
119
+ }
120
+ catch {
121
+ continue;
122
+ }
123
+ if (![1, 2, 3].includes(reply.id))
124
+ continue;
125
+ if (reply.error)
126
+ return unavailable('Codex rejected hook diagnostics; update or repair the Codex installation.');
127
+ if (reply.id === 1) {
128
+ send({ method: 'initialized' });
129
+ send({ method: 'config/read', id: 3, params: { cwd, includeLayers: false } });
130
+ }
131
+ else if (reply.id === 3) {
132
+ const features = reply.result?.config?.features;
133
+ if (features?.hooks === false || (features?.hooks === undefined && features?.codex_hooks === false)) {
134
+ return finish({ state: 'disabled', managed: false, message: 'Codex capture is inactive: hooks are disabled in the effective Codex configuration.' });
135
+ }
136
+ if (!reply.result?.config)
137
+ return unavailable('Codex did not report its effective configuration; capture is not verified.');
138
+ send({ method: 'hooks/list', id: 2, params: { cwds: [cwd] } });
139
+ }
140
+ else {
141
+ const entry = reply.result?.data?.[0];
142
+ if (!entry || !Array.isArray(entry.hooks) || entry.errors?.length)
143
+ return unavailable('Codex reported invalid hook configuration; capture is not verified.');
144
+ if (!entry.hooks.every((h) => h && typeof h.eventName === 'string' && typeof h.enabled === 'boolean'
145
+ && typeof h.isManaged === 'boolean' && typeof h.trustStatus === 'string')) {
146
+ return unavailable('Codex returned an unsupported hook status format; capture is not verified.');
147
+ }
148
+ finish(summarizeCodexHooks(entry.hooks));
149
+ }
150
+ }
151
+ });
152
+ send({ method: 'initialize', id: 1, params: {
153
+ clientInfo: { name: 'fullcourtdefense_hook_status', version: '1.0' }, capabilities: { experimentalApi: true },
154
+ } });
155
+ });
156
+ }
@@ -215,6 +215,8 @@ function candidateConfigPaths(cwd, extra) {
215
215
  { path: path.join(xdg, 'Code', 'User', 'settings.json'), source: 'VS Code (user, linux)', clientKey: 'vscode' },
216
216
  { path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), source: 'Windsurf', clientKey: 'windsurf' },
217
217
  { path: path.join(home, '.kiro', 'mcp.json'), source: 'Kiro', clientKey: 'kiro' },
218
+ { path: path.join(home, '.kiro', 'settings', 'mcp.json'), source: 'Kiro (settings)', clientKey: 'kiro' },
219
+ { path: path.join(cwd, '.kiro', 'settings', 'mcp.json'), source: 'Kiro (project settings)', clientKey: 'kiro' },
218
220
  { path: path.join(cwd, '.mcp.json'), source: 'Repo (.mcp.json)', clientKey: 'other' },
219
221
  { path: path.join(cwd, 'mcp.json'), source: 'Repo (mcp.json)', clientKey: 'other' },
220
222
  ];
@@ -20,6 +20,7 @@ import { BotGuardConfig } from '../config';
20
20
  * human approves/rejects in the FullCourtDefense console (same as the gateway).
21
21
  */
22
22
  export interface HookArgs {
23
+ agentClient?: string;
23
24
  event?: string;
24
25
  apiUrl?: string;
25
26
  shieldId?: string;
@@ -191,10 +191,10 @@ function mapClaudeToolPayload(payload) {
191
191
  // a file read is `read_file { path }`. Built-ins with no enforcement surface
192
192
  // (search, task, web) are ignored. Nothing here decides anything.
193
193
  // ---------------------------------------------------------------------------
194
- const BUILTIN_SHELL_TOOLS = new Set(['bash', 'shell', 'runterminalcommand', 'runinterminal', 'powershell', 'terminal']);
195
- const BUILTIN_READ_TOOLS = new Set(['read', 'readfile', 'view']);
194
+ const BUILTIN_SHELL_TOOLS = new Set(['bash', 'shell', 'runterminalcommand', 'runinterminal', 'powershell', 'terminal', 'runshellcommand', 'executebash', 'execcommand']);
195
+ const BUILTIN_READ_TOOLS = new Set(['read', 'readfile', 'view', 'fsread']);
196
196
  const BUILTIN_WRITE_TOOLS = new Set([
197
- 'write', 'edit', 'multiedit', 'notebookedit', 'createfile', 'editfiles',
197
+ 'write', 'edit', 'multiedit', 'notebookedit', 'createfile', 'editfiles', 'writefile', 'fswrite', 'replace',
198
198
  'replacestringinfile', 'editfile', 'applypatch', 'strreplaceeditor', 'insertedittofile', 'strreplace', 'editnotebook',
199
199
  ]);
200
200
  /**
@@ -221,11 +221,11 @@ function mapBuiltinToolPayload(toolName, input, payload) {
221
221
  if (BUILTIN_SHELL_TOOLS.has(canon)) {
222
222
  return {
223
223
  event: 'shell',
224
- payload: { ...payload, command: str(input.command), cwd: str(input.cwd) || str(input.working_directory) || str(payload.cwd) || undefined },
224
+ payload: { ...payload, command: str(input.command) || str(input.cmd), cwd: str(input.cwd) || str(input.workdir) || str(input.working_directory) || str(payload.cwd) || undefined },
225
225
  };
226
226
  }
227
227
  if (BUILTIN_READ_TOOLS.has(canon)) {
228
- return { event: 'read', payload: { ...payload, file_path: str(input.file_path) || str(input.path) || str(input.target_file) } };
228
+ return { event: 'read', payload: { ...payload, file_path: str(input.file_path) || str(input.filePath) || str(input.absolute_path) || str(input.path) || str(input.target_file) } };
229
229
  }
230
230
  if (BUILTIN_WRITE_TOOLS.has(canon)) {
231
231
  const filePath = str(input.file_path) || str(input.notebook_path) || str(input.path) || str(input.filePath) || str(input.target_file)
@@ -235,7 +235,7 @@ function mapBuiltinToolPayload(toolName, input, payload) {
235
235
  // `new_source`) are reduced to the lines they add. Both then drop the
236
236
  // credential-shaped words the file on disk already contains — re-stating a
237
237
  // value that is already in the file is not putting a secret into it.
238
- const delta = str(input.new_string) || str(input.new_str) || str(input.new_text)
238
+ const delta = str(input.new_string) || str(input.newString) || str(input.newText) || str(input.new_str) || str(input.new_text)
239
239
  || (Array.isArray(input.edits)
240
240
  ? input.edits.map((e) => str(e?.new_string) || str(e?.new_text) || str(e?.newText) || str(e?.content)).filter(Boolean).join('\n')
241
241
  : '');
@@ -1345,7 +1345,8 @@ async function hookCommandInner(args, config, io) {
1345
1345
  // client that reads ~/.claude/settings.json.
1346
1346
  const claudeNormalized = normalizeClaudePayload(payload);
1347
1347
  const hookFormat = claudeNormalized ? 'claude' : 'cursor';
1348
- const client = claudeNormalized ? claudeFormatClient() : 'cursor';
1348
+ const client = args.agentClient || (claudeNormalized ? claudeFormatClient() : 'cursor');
1349
+ (0, telemetry_1.setVerdictClient)(client);
1349
1350
  let event;
1350
1351
  let ignoreEvent = false;
1351
1352
  if (claudeNormalized) {
@@ -1,10 +1,48 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.installAllCommand = installAllCommand;
4
37
  const config_1 = require("../config");
5
38
  const discover_1 = require("./discover");
6
39
  const installCursorHook_1 = require("./installCursorHook");
7
40
  const installClaudeHook_1 = require("./installClaudeHook");
41
+ const installNativeHooks_1 = require("./installNativeHooks");
42
+ const fs = __importStar(require("fs"));
43
+ const os = __importStar(require("os"));
44
+ const path = __importStar(require("path"));
45
+ const installCodexManagedHooks_1 = require("./installCodexManagedHooks");
8
46
  const discoverSchedule_1 = require("./discoverSchedule");
9
47
  const mcpGateway_1 = require("./mcpGateway");
10
48
  const autoProtect_1 = require("./autoProtect");
@@ -35,6 +73,23 @@ async function installAllCommand(args, config) {
35
73
  console.log('\x1b[2mAuto-protect MCP servers across AI clients + Cursor built-in action hooks. No --mcp-command or folder path needed.\x1b[0m\n');
36
74
  await (0, mcpGateway_1.protectAllCommand)(gatewayArgs, config);
37
75
  if (args.hooks !== 'false') {
76
+ try {
77
+ const nativeFiles = (0, installNativeHooks_1.installNativeHooks)();
78
+ for (const file of nativeFiles)
79
+ console.log(`Native action hooks configured: ${file}`);
80
+ if (fs.existsSync(path.join(os.homedir(), '.codex'))) {
81
+ const status = await (0, installCodexManagedHooks_1.configureCodexForInstall)();
82
+ console.log(status.message);
83
+ if (status.state !== 'ready_for_probe')
84
+ console.warn('Codex activation remains incomplete; other client setup can continue.');
85
+ else if (status.managed)
86
+ (0, installNativeHooks_1.installNativeHooks)({ clients: ['codex'], remove: true });
87
+ }
88
+ }
89
+ catch (error) {
90
+ console.error(`Native hooks incomplete: ${error instanceof Error ? error.message : String(error)}. Repair the settings and rerun install-native-hooks.`);
91
+ process.exitCode = 1;
92
+ }
38
93
  console.log('\n\x1b[1mInstalling Cursor runtime hooks…\x1b[0m');
39
94
  const hookArgs = {
40
95
  shieldId: creds.shieldId,
@@ -183,6 +238,7 @@ async function installAllCommand(args, config) {
183
238
  console.log('You can retry with: fullcourtdefense discover --schedule daily');
184
239
  }
185
240
  }
186
- console.log('\n\x1b[32mDone.\x1b[0m Run \x1b[1mfullcourtdefense protect-all --dry-run true\x1b[0m to verify gateways.');
241
+ console.log(process.exitCode ? '\nSetup is incomplete; resolve the activation problem reported above.'
242
+ : '\nSetup configuration finished. Verify a fresh action from each IDE in Activity before treating its capture as active.');
187
243
  await (0, restartNotice_1.promptRestartNotice)();
188
244
  }
@@ -35,5 +35,11 @@ export interface ClaudeManagedHookRepairResult {
35
35
  export declare function inspectClaudeManagedHooks(): ClaudeManagedHookRepairResult;
36
36
  /** Repair managed commands without changing matcher/event/fail-open behavior. */
37
37
  export declare function repairClaudeManagedHooks(): ClaudeManagedHookRepairResult;
38
+ /** Copilot CLI user settings are separate from Claude's global settings.
39
+ * Use inline settings, not ~/.copilot/hooks, which VS Code also loads and
40
+ * would therefore execute twice alongside its Claude hook.
41
+ */
42
+ export declare function syncCopilotToolHooks(command?: string): boolean;
43
+ export declare function installCopilotHookCommand(): void;
38
44
  export declare function installClaudeHookCommand(args: InstallClaudeHookArgs, config: BotGuardConfig): Promise<void>;
39
45
  export declare function uninstallClaudeHookCommand(args: InstallClaudeHookArgs): Promise<void>;
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.inspectClaudeManagedHooks = inspectClaudeManagedHooks;
37
37
  exports.repairClaudeManagedHooks = repairClaudeManagedHooks;
38
+ exports.syncCopilotToolHooks = syncCopilotToolHooks;
39
+ exports.installCopilotHookCommand = installCopilotHookCommand;
38
40
  exports.installClaudeHookCommand = installClaudeHookCommand;
39
41
  exports.uninstallClaudeHookCommand = uninstallClaudeHookCommand;
40
42
  const fs = __importStar(require("fs"));
@@ -211,7 +213,7 @@ function syncCopilotToolHooks(command) {
211
213
  const entries = Array.isArray(hooks[event]) ? hooks[event] : [];
212
214
  const kept = entries.filter(entry => !isManagedCommand(entry));
213
215
  if (command)
214
- kept.push({ type: 'command', command, timeoutSec: event === 'PreToolUse' ? TOOL_HOOK_TIMEOUT_SEC : POST_TOOL_HOOK_TIMEOUT_SEC });
216
+ kept.push({ type: 'command', command: `${command} --agent-client copilot`, timeoutSec: event === 'PreToolUse' ? TOOL_HOOK_TIMEOUT_SEC : POST_TOOL_HOOK_TIMEOUT_SEC });
215
217
  if (kept.length)
216
218
  hooks[event] = kept;
217
219
  else
@@ -227,6 +229,10 @@ function syncCopilotToolHooks(command) {
227
229
  fs.writeFileSync(file, JSON.stringify(settings, null, 2) + '\n', 'utf8');
228
230
  return true;
229
231
  }
232
+ function installCopilotHookCommand() {
233
+ syncCopilotToolHooks(buildHookCommand({ shadow: false, failClosed: false }));
234
+ console.log('Copilot CLI tool hooks configured. Restart Copilot CLI to load the changes.');
235
+ }
230
236
  // PostToolUse companion of PreToolUse: enforces nothing; it is how a developer's
231
237
  // Allow on the client's own `ask` prompt (developer-scoped approval) is learned.
232
238
  // Same command — the bridge reads `hook_event_name` from the payload.
@@ -0,0 +1,17 @@
1
+ export { isWindowsAdministrator } from '../windowsManagedAccess';
2
+ import { CodexHookStatus } from '../codexHookStatus';
3
+ export declare function codexSystemHookFile(): string;
4
+ /** Install only from the MSI's stable, administrator-controlled runtime. */
5
+ export declare function installedCodexRuntime(): {
6
+ executable: string;
7
+ entry: string;
8
+ };
9
+ /** npm-only administrators do not have an MSI runtime to deploy as managed code. */
10
+ export declare function configureCodexForInstall(): Promise<CodexHookStatus>;
11
+ /** Caller has checked administrator access and protected paths. Injectable read-only verification for tests. */
12
+ export declare function writeVerifiedCodexHooks(file: string, command: string, verify?: () => Promise<CodexHookStatus>): Promise<CodexHookStatus>;
13
+ export declare function codexTransactionJournal(directory: string, transaction: string): string;
14
+ export declare function installCodexManagedHooks(transaction?: string): Promise<CodexHookStatus>;
15
+ /** Fixed protected paths only; invoked by MSI rollback/commit in SYSTEM context. */
16
+ export declare function finishCodexManagedTransaction(rollback: boolean, transaction: string): void;
17
+ export declare function uninstallCodexManagedHooks(): void;
@@ -0,0 +1,166 @@
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.isWindowsAdministrator = void 0;
37
+ exports.codexSystemHookFile = codexSystemHookFile;
38
+ exports.installedCodexRuntime = installedCodexRuntime;
39
+ exports.configureCodexForInstall = configureCodexForInstall;
40
+ exports.writeVerifiedCodexHooks = writeVerifiedCodexHooks;
41
+ exports.codexTransactionJournal = codexTransactionJournal;
42
+ exports.installCodexManagedHooks = installCodexManagedHooks;
43
+ exports.finishCodexManagedTransaction = finishCodexManagedTransaction;
44
+ exports.uninstallCodexManagedHooks = uninstallCodexManagedHooks;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const windowsManagedAccess_1 = require("../windowsManagedAccess");
48
+ var windowsManagedAccess_2 = require("../windowsManagedAccess");
49
+ Object.defineProperty(exports, "isWindowsAdministrator", { enumerable: true, get: function () { return windowsManagedAccess_2.isWindowsAdministrator; } });
50
+ const crypto_1 = require("crypto");
51
+ const installNativeHooks_1 = require("./installNativeHooks");
52
+ const codexHookStatus_1 = require("../codexHookStatus");
53
+ function codexSystemHookFile() {
54
+ return path.join(process.env.ProgramData || 'C:\\ProgramData', 'OpenAI', 'Codex', 'hooks.json');
55
+ }
56
+ /** Install only from the MSI's stable, administrator-controlled runtime. */
57
+ function installedCodexRuntime() {
58
+ const root = path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'FullCourtDefense');
59
+ const runtime = { executable: path.join(root, 'runtime', 'node.exe'), entry: path.join(root, 'dist', 'nativeHook.js') };
60
+ for (const item of [root, path.dirname(runtime.entry), path.dirname(runtime.executable), runtime.executable, runtime.entry]) {
61
+ if (!fs.existsSync(item) || fs.lstatSync(item).isSymbolicLink())
62
+ throw new Error('Install or repair the FullCourtDefense MSI before activating managed Codex capture.');
63
+ }
64
+ return runtime;
65
+ }
66
+ /** npm-only administrators do not have an MSI runtime to deploy as managed code. */
67
+ async function configureCodexForInstall() {
68
+ if (!(0, windowsManagedAccess_1.isWindowsAdministrator)())
69
+ return (0, codexHookStatus_1.getCodexHookStatus)();
70
+ const root = path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'FullCourtDefense');
71
+ if (!fs.existsSync(root))
72
+ return { state: 'configured_unverified', managed: false,
73
+ message: 'Codex user hooks are configured. Check hook trust from your normal Codex account; machine-managed deployment requires the MSI.' };
74
+ return installCodexManagedHooks();
75
+ }
76
+ /** Caller has checked administrator access and protected paths. Injectable read-only verification for tests. */
77
+ async function writeVerifiedCodexHooks(file, command, verify = codexHookStatus_1.getCodexHookStatus) {
78
+ const before = fs.existsSync(file) ? fs.readFileSync(file) : undefined;
79
+ (0, installNativeHooks_1.installNativeHooks)({ clients: ['codex'], codexSystem: { file, command } });
80
+ try {
81
+ const status = await verify();
82
+ if (status.state !== 'ready_for_probe' || !status.managed)
83
+ throw new Error(status.message);
84
+ return status;
85
+ }
86
+ catch (error) {
87
+ if (before)
88
+ fs.writeFileSync(file, before);
89
+ else
90
+ fs.unlinkSync(file);
91
+ throw new Error(`Managed Codex activation could not be verified; previous configuration restored. ${error instanceof Error ? error.message : String(error)}`);
92
+ }
93
+ }
94
+ function codexTransactionJournal(directory, transaction) {
95
+ if (!transaction || transaction.length > 256)
96
+ throw new Error('An MSI transaction identifier is required.');
97
+ return path.join(directory, `fullcourtdefense-rollback-${(0, crypto_1.createHash)('sha256').update(transaction).digest('hex')}.json`);
98
+ }
99
+ async function installCodexManagedHooks(transaction) {
100
+ if (!(0, windowsManagedAccess_1.isWindowsAdministrator)())
101
+ throw new Error('Managed Codex capture requires administrator deployment. Run setup elevated through your IT deployment tool. No employee /hooks approval is required for managed hooks.');
102
+ const runtime = installedCodexRuntime();
103
+ const file = codexSystemHookFile();
104
+ const directory = path.dirname(file);
105
+ for (const ancestor of [path.dirname(directory), directory]) {
106
+ if (fs.existsSync(ancestor) && fs.lstatSync(ancestor).isSymbolicLink())
107
+ throw new Error('Managed capture refuses redirected configuration paths.');
108
+ }
109
+ // Never promote a user-writable directory into a trusted execution source.
110
+ if (!fs.existsSync(directory)) {
111
+ fs.mkdirSync(directory, { recursive: true });
112
+ (0, windowsManagedAccess_1.protectNewManagedDirectory)(directory);
113
+ }
114
+ const configFile = path.join(directory, 'config.toml');
115
+ for (const item of [path.dirname(directory), directory, ...[file, configFile].filter(p => fs.existsSync(p)),
116
+ path.dirname(path.dirname(runtime.entry)), path.dirname(runtime.entry), path.dirname(runtime.executable), runtime.executable, runtime.entry]) {
117
+ if (fs.lstatSync(item).isSymbolicLink())
118
+ throw new Error('Managed capture refuses redirected configuration or runtime paths.');
119
+ (0, windowsManagedAccess_1.verifyManagedPath)(item, item === path.dirname(directory));
120
+ }
121
+ // SYSTEM must never launch a user's Codex binary or alter the SYSTEM profile's
122
+ // user hooks. The impersonated onboarding step verifies the user's client.
123
+ if (transaction) {
124
+ const snapshot = [file, configFile].map(p => fs.existsSync(p) ? fs.readFileSync(p).toString('base64') : null);
125
+ fs.writeFileSync(codexTransactionJournal(directory, transaction), JSON.stringify(snapshot), { flag: 'wx' });
126
+ }
127
+ (0, installNativeHooks_1.installNativeHooks)({ clients: ['codex'], codexSystem: { file, command: (0, installNativeHooks_1.nativeHookCommand)('codex', runtime) } });
128
+ if (!fs.existsSync(configFile))
129
+ fs.writeFileSync(configFile, '# FullCourtDefense: activate the system hook configuration layer.\n', { flag: 'wx' });
130
+ return { state: 'configured_unverified', managed: false,
131
+ message: 'Managed Codex hooks installed. The customer Codex client must verify activation; Activity capture is not yet verified.' };
132
+ }
133
+ /** Fixed protected paths only; invoked by MSI rollback/commit in SYSTEM context. */
134
+ function finishCodexManagedTransaction(rollback, transaction) {
135
+ if (!(0, windowsManagedAccess_1.isWindowsAdministrator)())
136
+ throw new Error('Administrator deployment is required.');
137
+ const directory = path.dirname(codexSystemHookFile());
138
+ const journal = codexTransactionJournal(directory, transaction);
139
+ if (!fs.existsSync(journal))
140
+ return;
141
+ if (fs.lstatSync(directory).isSymbolicLink() || fs.lstatSync(journal).isSymbolicLink())
142
+ throw new Error('Refusing redirected rollback configuration.');
143
+ if (rollback) {
144
+ const snapshot = JSON.parse(fs.readFileSync(journal, 'utf8'));
145
+ if (!Array.isArray(snapshot) || snapshot.length !== 2 || !snapshot.every(s => s === null || typeof s === 'string'))
146
+ throw new Error('Invalid managed hook rollback snapshot.');
147
+ [codexSystemHookFile(), path.join(directory, 'config.toml')].forEach((file, i) => {
148
+ if (fs.existsSync(file) && fs.lstatSync(file).isSymbolicLink())
149
+ throw new Error('Refusing redirected rollback target.');
150
+ if (snapshot[i] === null) {
151
+ if (fs.existsSync(file))
152
+ fs.unlinkSync(file);
153
+ }
154
+ else
155
+ fs.writeFileSync(file, Buffer.from(snapshot[i], 'base64'));
156
+ });
157
+ }
158
+ fs.unlinkSync(journal);
159
+ }
160
+ function uninstallCodexManagedHooks() {
161
+ if (process.platform !== 'win32' || !fs.existsSync(codexSystemHookFile()))
162
+ return;
163
+ if (!(0, windowsManagedAccess_1.isWindowsAdministrator)())
164
+ throw new Error('Administrator access is required to remove machine-managed Codex hooks.');
165
+ (0, installNativeHooks_1.installNativeHooks)({ clients: ['codex'], remove: true, codexSystem: { file: codexSystemHookFile(), command: '' } });
166
+ }
@@ -0,0 +1,17 @@
1
+ import type { NativeClient } from '../nativeHook';
2
+ export declare function nativeHookCommand(client: NativeClient, runtime?: {
3
+ executable: string;
4
+ entry: string;
5
+ }): string;
6
+ /** Preserve unrelated settings and hooks; refuse malformed files. */
7
+ export declare function installNativeHooks(options?: {
8
+ home?: string;
9
+ project?: string;
10
+ clients?: NativeClient[];
11
+ remove?: boolean;
12
+ /** Internal target supplied by the administrator installer; never a CLI path override. */
13
+ codexSystem?: {
14
+ file: string;
15
+ command: string;
16
+ };
17
+ }): string[];