codex-overleaf-link 1.1.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +457 -0
  3. package/bin/codex-overleaf-link.mjs +223 -0
  4. package/extension/src/shared/agentTranscript.js +1175 -0
  5. package/extension/src/shared/auditRecords.js +568 -0
  6. package/extension/src/shared/compatibility.js +372 -0
  7. package/extension/src/shared/compileAdapter.js +176 -0
  8. package/extension/src/shared/governanceRules.js +252 -0
  9. package/extension/src/shared/i18n.js +565 -0
  10. package/extension/src/shared/models.js +106 -0
  11. package/extension/src/shared/otText.js +505 -0
  12. package/extension/src/shared/projectFiles.js +180 -0
  13. package/extension/src/shared/reviewing.js +99 -0
  14. package/extension/src/shared/sensitiveScan.js +116 -0
  15. package/extension/src/shared/sessionState.js +1084 -0
  16. package/extension/src/shared/staleGuard.js +150 -0
  17. package/extension/src/shared/storageDb.js +986 -0
  18. package/extension/src/shared/storageKeys.js +29 -0
  19. package/extension/src/shared/storageMigration.js +168 -0
  20. package/extension/src/shared/summary.js +248 -0
  21. package/extension/src/shared/undoOperations.js +369 -0
  22. package/native-host/src/codexArgs.js +43 -0
  23. package/native-host/src/codexHome.js +538 -0
  24. package/native-host/src/codexModels.js +247 -0
  25. package/native-host/src/codexPrompt.js +192 -0
  26. package/native-host/src/codexPromptAssembly.js +411 -0
  27. package/native-host/src/codexSessionRunner.js +1247 -0
  28. package/native-host/src/commandApproval.js +914 -0
  29. package/native-host/src/debugLog.js +78 -0
  30. package/native-host/src/diffEngine.js +247 -0
  31. package/native-host/src/index.js +132 -0
  32. package/native-host/src/launcher.js +81 -0
  33. package/native-host/src/localSkills.js +476 -0
  34. package/native-host/src/manifest.js +226 -0
  35. package/native-host/src/mirrorSensitiveScan.js +119 -0
  36. package/native-host/src/mirrorWorkspace.js +1019 -0
  37. package/native-host/src/nativeDoctor.js +826 -0
  38. package/native-host/src/nativeEnvironment.js +315 -0
  39. package/native-host/src/nativeHostPlatform.js +112 -0
  40. package/native-host/src/nativeMessaging.js +60 -0
  41. package/native-host/src/nativeQuotas.js +294 -0
  42. package/native-host/src/nativeResponseBudget.js +194 -0
  43. package/native-host/src/runtimeInstaller.js +357 -0
  44. package/native-host/src/taskRunner.js +3 -0
  45. package/native-host/src/taskRunnerRuntime.js +1083 -0
  46. package/native-host/src/textPatch.js +287 -0
  47. package/package.json +40 -0
  48. package/scripts/codex-json-agent.mjs +269 -0
  49. package/scripts/install-native-host.mjs +255 -0
  50. package/scripts/npm-package-files-v1.1.1.txt +52 -0
  51. package/scripts/uninstall-native-host.mjs +298 -0
  52. package/scripts/verify-npm-package.mjs +296 -0
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { createRequire } from 'node:module';
6
+ import { fileURLToPath } from 'node:url';
7
+ import {
8
+ DEFAULT_CHROME_EXTENSION_ID,
9
+ buildAllowedOrigin,
10
+ buildHostManifest,
11
+ validateChromeExtensionId
12
+ } from '../native-host/src/manifest.js';
13
+ import { buildLauncher } from '../native-host/src/launcher.js';
14
+ import {
15
+ getDefaultBridgePath,
16
+ getDefaultRuntimeRoot,
17
+ getNativeHostRegistrationTarget
18
+ } from '../native-host/src/nativeHostPlatform.js';
19
+
20
+ const require = createRequire(import.meta.url);
21
+ const { installRuntimeFromPackage } = require('../native-host/src/runtimeInstaller.js');
22
+ const scriptPath = fileURLToPath(import.meta.url);
23
+ const scriptDir = path.dirname(scriptPath);
24
+ const defaultPackageRoot = path.resolve(scriptDir, '..');
25
+
26
+ export function installNativeHost(options = {}) {
27
+ const extensionId = options.extensionId || DEFAULT_CHROME_EXTENSION_ID;
28
+ if (!validateChromeExtensionId(extensionId)) {
29
+ throw new Error(`Invalid Chrome extension id: ${extensionId}`);
30
+ }
31
+
32
+ const packageRoot = path.resolve(options.packageRoot || defaultPackageRoot);
33
+ const extensionIds = [extensionId];
34
+ const platform = options.platform || process.platform;
35
+ const browser = options.browser || 'chrome';
36
+ assertSupportedBrowser(browser);
37
+
38
+ const env = options.env || process.env;
39
+ const platformPath = platform === 'win32' ? path.win32 : path.posix;
40
+ const runtimePlatformPath = options.runtimeRoot && path.isAbsolute(options.runtimeRoot) ? path : platformPath;
41
+ const defaultInstallRoot = getDefaultRuntimeRoot({ platform, browser, env });
42
+ const installRoot = resolveForPlatform(options.runtimeRoot || defaultInstallRoot, platformPath);
43
+ const bridgeEntryPath = platformPath.join(installRoot, 'native-host', 'src', 'index.js');
44
+ const agentPath = platformPath.join(installRoot, 'scripts', 'codex-json-agent.mjs');
45
+ const defaultBridgePath = getDefaultBridgePath({ platform, browser, env });
46
+ const bridgePath = resolveForPlatform(options.bridgePath || defaultBridgePath, platformPath);
47
+ const registrationTarget = getNativeHostRegistrationTarget({
48
+ platform,
49
+ browser,
50
+ env
51
+ });
52
+ const manifestPath = registrationTarget.manifestPath;
53
+ const manifest = buildHostManifest({
54
+ extensionId,
55
+ extensionIds,
56
+ bridgePath,
57
+ platform
58
+ });
59
+
60
+ const runtimeInstall = installRuntimeFromPackage({
61
+ packageRoot,
62
+ runtimeRoot: installRoot,
63
+ defaultRuntimeRoot: defaultInstallRoot,
64
+ platformPath: runtimePlatformPath
65
+ });
66
+ const runtimePackageVersion = readRuntimePackageVersion(path.join(installRoot, 'package.json'));
67
+
68
+ fs.mkdirSync(platformPath.dirname(bridgePath), { recursive: true });
69
+ fs.writeFileSync(bridgePath, buildLauncher({
70
+ platform,
71
+ nodePath: process.execPath,
72
+ bridgeEntryPath,
73
+ agentPath
74
+ }), 'utf8');
75
+ if (platform !== 'win32') {
76
+ fs.chmodSync(bridgePath, 0o755);
77
+ }
78
+
79
+ fs.mkdirSync(platformPath.dirname(manifestPath), { recursive: true });
80
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
81
+ if (registrationTarget.kind === 'registry') {
82
+ addWindowsRegistryValue(registrationTarget.registryKey, manifestPath);
83
+ }
84
+
85
+ return {
86
+ ok: true,
87
+ action: runtimeInstall.action,
88
+ browser: registrationTarget.browser,
89
+ extensionId,
90
+ manifest: {
91
+ path: manifestPath,
92
+ allowedOrigin: buildAllowedOrigin(extensionId),
93
+ allowedOrigins: manifest.allowed_origins
94
+ },
95
+ bridge: {
96
+ path: bridgePath
97
+ },
98
+ runtime: {
99
+ root: installRoot,
100
+ action: runtimeInstall.action,
101
+ packageVersion: runtimePackageVersion
102
+ },
103
+ registry: registrationTarget.kind === 'registry'
104
+ ? { key: registrationTarget.registryKey }
105
+ : undefined,
106
+ warning: runtimeInstall.warning
107
+ };
108
+ }
109
+
110
+ export function formatInstallNativeHostHuman(result) {
111
+ const lines = [`Installed Native Messaging host manifest: ${result.manifest.path}`];
112
+ if (result.registry) {
113
+ lines.push(`Registered Native Messaging host registry key: ${result.registry.key}`);
114
+ }
115
+ lines.push(`Bridge executable: ${result.bridge.path}`);
116
+ lines.push(`Runtime root: ${result.runtime.root}`);
117
+ lines.push(`Runtime package version: ${result.runtime.packageVersion}`);
118
+ lines.push(`Allowed Chrome extension id: ${result.extensionId}`);
119
+ lines.push(`Allowed Chrome extension ids: ${result.manifest.allowedOrigins.map(origin => origin.replace(/^chrome-extension:\/\//, '').replace(/\/$/, '')).join(', ')}`);
120
+ if (result.warning) {
121
+ lines.push(`Warning: ${result.warning}`);
122
+ }
123
+ return `${lines.join('\n')}\n`;
124
+ }
125
+
126
+ function resolveForPlatform(targetPath, platformPathModule) {
127
+ if (platformPathModule.isAbsolute(targetPath)) {
128
+ return targetPath;
129
+ }
130
+ return platformPathModule.resolve(targetPath);
131
+ }
132
+
133
+ function readRuntimePackageVersion(packagePath) {
134
+ try {
135
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
136
+ return typeof pkg.version === 'string' && pkg.version ? pkg.version : 'unknown';
137
+ } catch {
138
+ return 'unknown';
139
+ }
140
+ }
141
+
142
+ function addWindowsRegistryValue(registryKey, manifestPath) {
143
+ const registryCommand = getWindowsRegistryCommand();
144
+ const result = spawnSync(registryCommand.file, [
145
+ ...registryCommand.args,
146
+ 'add',
147
+ registryKey,
148
+ '/ve',
149
+ '/t',
150
+ 'REG_SZ',
151
+ '/d',
152
+ manifestPath,
153
+ '/f'
154
+ ], {
155
+ encoding: 'utf8'
156
+ });
157
+ if (result.error) {
158
+ throw new Error(`Failed to run ${registryCommand.file}: ${result.error.message}`);
159
+ }
160
+ if (result.status !== 0) {
161
+ throw new Error(result.stderr || result.stdout || `reg.exe add failed with status ${result.status}`);
162
+ }
163
+ }
164
+
165
+ function getWindowsRegistryCommand() {
166
+ return {
167
+ file: process.env.CODEX_OVERLEAF_REG_EXE || 'reg.exe',
168
+ args: parseStringArrayEnv(process.env.CODEX_OVERLEAF_REG_EXE_ARGS_JSON)
169
+ };
170
+ }
171
+
172
+ function parseStringArrayEnv(value) {
173
+ if (!value) {
174
+ return [];
175
+ }
176
+ const parsed = JSON.parse(value);
177
+ if (!Array.isArray(parsed) || !parsed.every(item => typeof item === 'string')) {
178
+ throw new Error('CODEX_OVERLEAF_REG_EXE_ARGS_JSON must be a JSON array of strings');
179
+ }
180
+ return parsed;
181
+ }
182
+
183
+ function assertSupportedBrowser(browser) {
184
+ if (!['auto', 'chrome', 'chromium'].includes(browser)) {
185
+ throw new Error('Usage: --browser must be one of chrome, chromium, or auto');
186
+ }
187
+ }
188
+
189
+ function parseArgs(argv) {
190
+ const parsed = {};
191
+ for (let index = 0; index < argv.length; index += 1) {
192
+ const arg = argv[index];
193
+ if (arg === '--extension-id') {
194
+ parsed.extensionId = readOptionValue(argv, index, arg);
195
+ index += 1;
196
+ } else if (arg === '--bridge-path') {
197
+ parsed.bridgePath = readOptionValue(argv, index, arg);
198
+ index += 1;
199
+ } else if (arg === '--runtime-root') {
200
+ parsed.runtimeRoot = readOptionValue(argv, index, arg);
201
+ index += 1;
202
+ } else if (arg === '--browser') {
203
+ parsed.browser = readOptionValue(argv, index, arg);
204
+ index += 1;
205
+ } else if (arg === '--platform') {
206
+ parsed.platform = readOptionValue(argv, index, arg);
207
+ index += 1;
208
+ } else if (arg === '--force') {
209
+ parsed.force = true;
210
+ } else if (arg === '--json') {
211
+ parsed.json = true;
212
+ } else if (arg === '--help') {
213
+ parsed.help = true;
214
+ } else {
215
+ throw new Error(`Unknown option: ${arg}`);
216
+ }
217
+ }
218
+ return parsed;
219
+ }
220
+
221
+ function readOptionValue(argv, index, optionName) {
222
+ const value = argv[index + 1];
223
+ if (!value || value.startsWith('--')) {
224
+ throw new Error(`Missing value for ${optionName}`);
225
+ }
226
+ return value;
227
+ }
228
+
229
+ function printUsage() {
230
+ console.log('Usage: install-native-host.mjs [--extension-id <id>] [--browser chrome|chromium|auto] [--runtime-root <path>] [--json]');
231
+ }
232
+
233
+ async function main() {
234
+ let args;
235
+ try {
236
+ args = parseArgs(process.argv.slice(2));
237
+ if (args.help) {
238
+ printUsage();
239
+ return;
240
+ }
241
+ const result = installNativeHost(args);
242
+ if (args.json) {
243
+ console.log(JSON.stringify(result, null, 2));
244
+ } else {
245
+ process.stdout.write(formatInstallNativeHostHuman(result));
246
+ }
247
+ } catch (error) {
248
+ console.error(error instanceof Error ? error.message : String(error));
249
+ process.exit(1);
250
+ }
251
+ }
252
+
253
+ if (path.resolve(process.argv[1] || '') === scriptPath) {
254
+ await main();
255
+ }
@@ -0,0 +1,52 @@
1
+ package/LICENSE
2
+ package/extension/src/shared/agentTranscript.js
3
+ package/extension/src/shared/auditRecords.js
4
+ package/native-host/src/codexArgs.js
5
+ package/native-host/src/codexHome.js
6
+ package/native-host/src/codexModels.js
7
+ package/native-host/src/codexPrompt.js
8
+ package/native-host/src/codexPromptAssembly.js
9
+ package/native-host/src/codexSessionRunner.js
10
+ package/native-host/src/commandApproval.js
11
+ package/extension/src/shared/compatibility.js
12
+ package/extension/src/shared/compileAdapter.js
13
+ package/native-host/src/debugLog.js
14
+ package/native-host/src/diffEngine.js
15
+ package/extension/src/shared/governanceRules.js
16
+ package/extension/src/shared/i18n.js
17
+ package/native-host/src/index.js
18
+ package/native-host/src/launcher.js
19
+ package/native-host/src/localSkills.js
20
+ package/native-host/src/manifest.js
21
+ package/native-host/src/mirrorSensitiveScan.js
22
+ package/native-host/src/mirrorWorkspace.js
23
+ package/extension/src/shared/models.js
24
+ package/native-host/src/nativeDoctor.js
25
+ package/native-host/src/nativeEnvironment.js
26
+ package/native-host/src/nativeHostPlatform.js
27
+ package/native-host/src/nativeMessaging.js
28
+ package/native-host/src/nativeQuotas.js
29
+ package/native-host/src/nativeResponseBudget.js
30
+ package/extension/src/shared/otText.js
31
+ package/extension/src/shared/projectFiles.js
32
+ package/extension/src/shared/reviewing.js
33
+ package/native-host/src/runtimeInstaller.js
34
+ package/extension/src/shared/sensitiveScan.js
35
+ package/extension/src/shared/sessionState.js
36
+ package/extension/src/shared/staleGuard.js
37
+ package/extension/src/shared/storageDb.js
38
+ package/extension/src/shared/storageKeys.js
39
+ package/extension/src/shared/storageMigration.js
40
+ package/extension/src/shared/summary.js
41
+ package/native-host/src/taskRunner.js
42
+ package/native-host/src/taskRunnerRuntime.js
43
+ package/native-host/src/textPatch.js
44
+ package/extension/src/shared/undoOperations.js
45
+ package/package.json
46
+ package/README.md
47
+ package/scripts/codex-json-agent.mjs
48
+ package/bin/codex-overleaf-link.mjs
49
+ package/scripts/install-native-host.mjs
50
+ package/scripts/uninstall-native-host.mjs
51
+ package/scripts/verify-npm-package.mjs
52
+ package/scripts/npm-package-files-v1.1.1.txt
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { createRequire } from 'node:module';
6
+ import { fileURLToPath } from 'node:url';
7
+ import {
8
+ getDefaultBridgePath,
9
+ getDefaultRuntimeRoot,
10
+ getNativeHostRegistrationTarget
11
+ } from '../native-host/src/nativeHostPlatform.js';
12
+
13
+ const require = createRequire(import.meta.url);
14
+ const { uninstallManagedRuntime } = require('../native-host/src/runtimeInstaller.js');
15
+ const scriptPath = fileURLToPath(import.meta.url);
16
+
17
+ export function uninstallNativeHost(options = {}) {
18
+ const platform = options.platform || process.platform;
19
+ const browser = options.browser || 'chrome';
20
+ assertSupportedBrowser(browser);
21
+
22
+ const env = options.env || process.env;
23
+ const platformPath = platform === 'win32' ? path.win32 : path.posix;
24
+ const runtimePlatformPath = options.runtimeRoot && path.isAbsolute(options.runtimeRoot) ? path : platformPath;
25
+ const defaultInstallRoot = getDefaultRuntimeRoot({ platform, browser, env });
26
+ const installRoot = resolveForPlatform(options.runtimeRoot || defaultInstallRoot, platformPath);
27
+ const defaultBridgePath = getDefaultBridgePath({ platform, browser, env });
28
+ const bridgePath = resolveForPlatform(options.bridgePath || defaultBridgePath, platformPath);
29
+ const registrationTarget = getNativeHostRegistrationTarget({
30
+ platform,
31
+ browser,
32
+ env
33
+ });
34
+ const manifestPath = registrationTarget.manifestPath;
35
+
36
+ let runtimeResult;
37
+ if (options.keepRuntime) {
38
+ runtimeResult = {
39
+ ok: true,
40
+ action: fs.existsSync(installRoot) ? 'kept' : 'not-found',
41
+ runtimeRoot: installRoot,
42
+ removed: false,
43
+ kept: fs.existsSync(installRoot)
44
+ };
45
+ } else {
46
+ uninstallManagedRuntime({
47
+ runtimeRoot: installRoot,
48
+ defaultRuntimeRoot: defaultInstallRoot,
49
+ platformPath: runtimePlatformPath,
50
+ keepRuntime: true
51
+ });
52
+ }
53
+
54
+ const manifest = removeManifestPath(manifestPath);
55
+ const registry = registrationTarget.kind === 'registry'
56
+ ? removeRegistryValue(registrationTarget.registryKey)
57
+ : undefined;
58
+ const bridge = removeBridgePath(bridgePath);
59
+ if (!options.keepRuntime) {
60
+ runtimeResult = uninstallManagedRuntime({
61
+ runtimeRoot: installRoot,
62
+ defaultRuntimeRoot: defaultInstallRoot,
63
+ platformPath: runtimePlatformPath
64
+ });
65
+ }
66
+ const runtime = summarizeRuntimeResult(runtimeResult);
67
+
68
+ return {
69
+ ok: true,
70
+ status: deriveStatus({ manifest, runtime, keepRuntime: Boolean(options.keepRuntime) }),
71
+ keepRuntime: Boolean(options.keepRuntime),
72
+ browser: registrationTarget.browser,
73
+ manifest,
74
+ registry,
75
+ bridge,
76
+ runtime
77
+ };
78
+ }
79
+
80
+ export function formatUninstallNativeHostHuman(result) {
81
+ const lines = [];
82
+ if (result.manifest.removed) {
83
+ lines.push(`Removed Native Messaging host manifest: ${result.manifest.path}`);
84
+ } else {
85
+ lines.push(`Native Messaging host manifest not found: ${result.manifest.path}`);
86
+ }
87
+ if (result.registry) {
88
+ if (result.registry.removed) {
89
+ lines.push(`Removed Native Messaging host registry key: ${result.registry.key}`);
90
+ } else {
91
+ lines.push(`Native Messaging host registry key not found: ${result.registry.key}`);
92
+ }
93
+ }
94
+ if (result.bridge.action === 'kept') {
95
+ lines.push(`Kept Bridge executable: ${result.bridge.path}`);
96
+ } else if (result.bridge.removed) {
97
+ lines.push(`Removed Bridge executable: ${result.bridge.path}`);
98
+ } else {
99
+ lines.push(`Bridge executable not found: ${result.bridge.path}`);
100
+ }
101
+ if (result.runtime.action === 'not-found') {
102
+ lines.push(`Runtime root not found: ${result.runtime.root}`);
103
+ } else if (result.runtime.kept) {
104
+ lines.push(`Kept Runtime root: ${result.runtime.root}`);
105
+ } else if (result.runtime.removed) {
106
+ lines.push(`Removed Runtime root: ${result.runtime.root}`);
107
+ }
108
+ lines.push('Codex Overleaf native host uninstall finished.');
109
+ lines.push('Project mirrors and plugin Codex history under ~/.codex-overleaf are left intact.');
110
+ return `${lines.join('\n')}\n`;
111
+ }
112
+
113
+ function resolveForPlatform(targetPath, platformPathModule) {
114
+ if (platformPathModule.isAbsolute(targetPath)) {
115
+ return targetPath;
116
+ }
117
+ return platformPathModule.resolve(targetPath);
118
+ }
119
+
120
+ function removeManifestPath(target) {
121
+ let stat;
122
+ try {
123
+ stat = fs.lstatSync(target);
124
+ } catch (error) {
125
+ if (error && error.code === 'ENOENT') {
126
+ return { path: target, action: 'not-found', removed: false };
127
+ }
128
+ throw error;
129
+ }
130
+
131
+ if (stat.isDirectory()) {
132
+ throw new Error(`Refusing to remove Native Messaging host manifest directory: ${target}`);
133
+ }
134
+
135
+ fs.rmSync(target, { force: true });
136
+ return { path: target, action: 'removed', removed: true };
137
+ }
138
+
139
+ function removeBridgePath(target) {
140
+ let stat;
141
+ try {
142
+ stat = fs.lstatSync(target);
143
+ } catch (error) {
144
+ if (error && error.code === 'ENOENT') {
145
+ return { path: target, action: 'not-found', removed: false };
146
+ }
147
+ throw error;
148
+ }
149
+
150
+ if (stat.isDirectory()) {
151
+ throw new Error(`Refusing to remove bridge directory: ${target}`);
152
+ }
153
+
154
+ fs.rmSync(target, { force: true });
155
+ return { path: target, action: 'removed', removed: true };
156
+ }
157
+
158
+ function removeRegistryValue(registryKey) {
159
+ if (deleteWindowsRegistryValue(registryKey)) {
160
+ return { key: registryKey, action: 'removed', removed: true };
161
+ }
162
+ return { key: registryKey, action: 'not-found', removed: false };
163
+ }
164
+
165
+ function deleteWindowsRegistryValue(registryKey) {
166
+ const registryCommand = getWindowsRegistryCommand();
167
+ const result = spawnSync(registryCommand.file, [
168
+ ...registryCommand.args,
169
+ 'delete',
170
+ registryKey,
171
+ '/f'
172
+ ], {
173
+ encoding: 'utf8'
174
+ });
175
+ if (result.error) {
176
+ throw new Error(`Failed to run ${registryCommand.file}: ${result.error.message}`);
177
+ }
178
+ if (result.status !== 0) {
179
+ const output = `${result.stderr || ''}\n${result.stdout || ''}`;
180
+ if (/unable to find the specified registry key or value/i.test(output)) {
181
+ return false;
182
+ }
183
+ throw new Error(result.stderr || result.stdout || `reg.exe delete failed with status ${result.status}`);
184
+ }
185
+ return true;
186
+ }
187
+
188
+ function getWindowsRegistryCommand() {
189
+ return {
190
+ file: process.env.CODEX_OVERLEAF_REG_EXE || 'reg.exe',
191
+ args: parseStringArrayEnv(process.env.CODEX_OVERLEAF_REG_EXE_ARGS_JSON)
192
+ };
193
+ }
194
+
195
+ function parseStringArrayEnv(value) {
196
+ if (!value) {
197
+ return [];
198
+ }
199
+ const parsed = JSON.parse(value);
200
+ if (!Array.isArray(parsed) || !parsed.every(item => typeof item === 'string')) {
201
+ throw new Error('CODEX_OVERLEAF_REG_EXE_ARGS_JSON must be a JSON array of strings');
202
+ }
203
+ return parsed;
204
+ }
205
+
206
+ function summarizeRuntimeResult(result) {
207
+ return {
208
+ root: result.runtimeRoot,
209
+ action: result.action,
210
+ removed: Boolean(result.removed),
211
+ kept: Boolean(result.kept),
212
+ markerManagedBy: result.marker && result.marker.managedBy,
213
+ markerVersion: result.marker && result.marker.version
214
+ };
215
+ }
216
+
217
+ function deriveStatus({ manifest, runtime, keepRuntime }) {
218
+ if (keepRuntime) {
219
+ return manifest.removed ? 'manifest-removed-runtime-kept' : 'manifest-not-found-runtime-kept';
220
+ }
221
+ if (manifest.action === 'not-found' && runtime.action === 'not-found') {
222
+ return 'not-found';
223
+ }
224
+ return 'uninstalled';
225
+ }
226
+
227
+ function assertSupportedBrowser(browser) {
228
+ if (!['auto', 'chrome', 'chromium'].includes(browser)) {
229
+ throw new Error('Usage: --browser must be one of chrome, chromium, or auto');
230
+ }
231
+ }
232
+
233
+ function parseArgs(argv) {
234
+ const parsed = {};
235
+ for (let index = 0; index < argv.length; index += 1) {
236
+ const arg = argv[index];
237
+ if (arg === '--bridge-path') {
238
+ parsed.bridgePath = readOptionValue(argv, index, arg);
239
+ index += 1;
240
+ } else if (arg === '--runtime-root') {
241
+ parsed.runtimeRoot = readOptionValue(argv, index, arg);
242
+ index += 1;
243
+ } else if (arg === '--keep-runtime') {
244
+ parsed.keepRuntime = true;
245
+ } else if (arg === '--browser') {
246
+ parsed.browser = readOptionValue(argv, index, arg);
247
+ index += 1;
248
+ } else if (arg === '--platform') {
249
+ parsed.platform = readOptionValue(argv, index, arg);
250
+ index += 1;
251
+ } else if (arg === '--force') {
252
+ parsed.force = true;
253
+ } else if (arg === '--json') {
254
+ parsed.json = true;
255
+ } else if (arg === '--help') {
256
+ parsed.help = true;
257
+ } else {
258
+ throw new Error(`Unknown option: ${arg}`);
259
+ }
260
+ }
261
+ return parsed;
262
+ }
263
+
264
+ function readOptionValue(argv, index, optionName) {
265
+ const value = argv[index + 1];
266
+ if (!value || value.startsWith('--')) {
267
+ throw new Error(`Missing value for ${optionName}`);
268
+ }
269
+ return value;
270
+ }
271
+
272
+ function printUsage() {
273
+ console.log('Usage: uninstall-native-host.mjs [--browser chrome|chromium|auto] [--runtime-root <path>] [--keep-runtime] [--json]');
274
+ }
275
+
276
+ async function main() {
277
+ let args;
278
+ try {
279
+ args = parseArgs(process.argv.slice(2));
280
+ if (args.help) {
281
+ printUsage();
282
+ return;
283
+ }
284
+ const result = uninstallNativeHost(args);
285
+ if (args.json) {
286
+ console.log(JSON.stringify(result, null, 2));
287
+ } else {
288
+ process.stdout.write(formatUninstallNativeHostHuman(result));
289
+ }
290
+ } catch (error) {
291
+ console.error(error instanceof Error ? error.message : String(error));
292
+ process.exit(1);
293
+ }
294
+ }
295
+
296
+ if (path.resolve(process.argv[1] || '') === scriptPath) {
297
+ await main();
298
+ }