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.
- package/LICENSE +21 -0
- package/README.md +457 -0
- package/bin/codex-overleaf-link.mjs +223 -0
- package/extension/src/shared/agentTranscript.js +1175 -0
- package/extension/src/shared/auditRecords.js +568 -0
- package/extension/src/shared/compatibility.js +372 -0
- package/extension/src/shared/compileAdapter.js +176 -0
- package/extension/src/shared/governanceRules.js +252 -0
- package/extension/src/shared/i18n.js +565 -0
- package/extension/src/shared/models.js +106 -0
- package/extension/src/shared/otText.js +505 -0
- package/extension/src/shared/projectFiles.js +180 -0
- package/extension/src/shared/reviewing.js +99 -0
- package/extension/src/shared/sensitiveScan.js +116 -0
- package/extension/src/shared/sessionState.js +1084 -0
- package/extension/src/shared/staleGuard.js +150 -0
- package/extension/src/shared/storageDb.js +986 -0
- package/extension/src/shared/storageKeys.js +29 -0
- package/extension/src/shared/storageMigration.js +168 -0
- package/extension/src/shared/summary.js +248 -0
- package/extension/src/shared/undoOperations.js +369 -0
- package/native-host/src/codexArgs.js +43 -0
- package/native-host/src/codexHome.js +538 -0
- package/native-host/src/codexModels.js +247 -0
- package/native-host/src/codexPrompt.js +192 -0
- package/native-host/src/codexPromptAssembly.js +411 -0
- package/native-host/src/codexSessionRunner.js +1247 -0
- package/native-host/src/commandApproval.js +914 -0
- package/native-host/src/debugLog.js +78 -0
- package/native-host/src/diffEngine.js +247 -0
- package/native-host/src/index.js +132 -0
- package/native-host/src/launcher.js +81 -0
- package/native-host/src/localSkills.js +476 -0
- package/native-host/src/manifest.js +226 -0
- package/native-host/src/mirrorSensitiveScan.js +119 -0
- package/native-host/src/mirrorWorkspace.js +1019 -0
- package/native-host/src/nativeDoctor.js +826 -0
- package/native-host/src/nativeEnvironment.js +315 -0
- package/native-host/src/nativeHostPlatform.js +112 -0
- package/native-host/src/nativeMessaging.js +60 -0
- package/native-host/src/nativeQuotas.js +294 -0
- package/native-host/src/nativeResponseBudget.js +194 -0
- package/native-host/src/runtimeInstaller.js +357 -0
- package/native-host/src/taskRunner.js +3 -0
- package/native-host/src/taskRunnerRuntime.js +1083 -0
- package/native-host/src/textPatch.js +287 -0
- package/package.json +40 -0
- package/scripts/codex-json-agent.mjs +269 -0
- package/scripts/install-native-host.mjs +255 -0
- package/scripts/npm-package-files-v1.1.1.txt +52 -0
- package/scripts/uninstall-native-host.mjs +298 -0
- 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
|
+
}
|