codex-overleaf-link 1.8.6 → 1.9.0
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 +45 -21
- package/bin/codex-overleaf-link.mjs +58 -0
- package/extension/assets/icons/codex-overleaf-dialog-icon.png +0 -0
- package/extension/assets/icons/codex-overleaf-icon.png +0 -0
- package/extension/assets/icons/icon128.png +0 -0
- package/extension/assets/icons/icon16.png +0 -0
- package/extension/assets/icons/icon32.png +0 -0
- package/extension/assets/icons/icon48.png +0 -0
- package/extension/bootstrap/background.js +404 -0
- package/extension/bootstrap/manifest.template.json +51 -0
- package/extension/bootstrap/popup.css +26 -0
- package/extension/bootstrap/popup.html +38 -0
- package/extension/bootstrap/popup.js +44 -0
- package/extension/bootstrap/runtimeContext.js +2 -0
- package/extension/runtime-manifest.json +60 -0
- package/extension/src/background.js +532 -0
- package/extension/src/content/applyResultFormatters.js +387 -0
- package/extension/src/content/composerAttachments.js +522 -0
- package/extension/src/content/composerPanel.js +254 -0
- package/extension/src/content/contentRuntime.js +8895 -0
- package/extension/src/content/contextTray.js +686 -0
- package/extension/src/content/diagnosticsController.js +659 -0
- package/extension/src/content/diagnosticsPanel.js +342 -0
- package/extension/src/content/diffReviewPanel.js +797 -0
- package/extension/src/content/localSkillsPanel.js +280 -0
- package/extension/src/content/markdownText.js +606 -0
- package/extension/src/content/mirrorHealth.js +92 -0
- package/extension/src/content/modelPicker.js +489 -0
- package/extension/src/content/nativeChannel.js +62 -0
- package/extension/src/content/otWarmMirror.js +964 -0
- package/extension/src/content/otWarmMirrorController.js +174 -0
- package/extension/src/content/panelMaintenance.js +367 -0
- package/extension/src/content/panelRenderer.js +253 -0
- package/extension/src/content/recentProjects.js +1063 -0
- package/extension/src/content/reviewHunks.js +247 -0
- package/extension/src/content/runController.js +242 -0
- package/extension/src/content/runTimelineView.js +1126 -0
- package/extension/src/content/sessionManager.js +487 -0
- package/extension/src/content/sessionPanel.js +263 -0
- package/extension/src/content/settingsPanel.js +527 -0
- package/extension/src/content/themeController.js +91 -0
- package/extension/src/content/updateIdle.js +67 -0
- package/extension/src/content/writebackController.js +300 -0
- package/extension/src/content/writebackOrchestrator.js +850 -0
- package/extension/src/contentScript.js +8 -0
- package/extension/src/page/compileBridge.js +460 -0
- package/extension/src/page/overleafCapabilities.js +100 -0
- package/extension/src/page/overleafEditor.js +282 -0
- package/extension/src/page/overleafProjectSnapshot.js +234 -0
- package/extension/src/page/overleafRealtimeObserver.js +491 -0
- package/extension/src/page/pageBridgeCapability.js +71 -0
- package/extension/src/page/snapshotRouter.js +948 -0
- package/extension/src/page/trackedChangesLifecycle.js +1506 -0
- package/extension/src/page/treeOperations.js +1584 -0
- package/extension/src/page/writeGuard.js +142 -0
- package/extension/src/page/writebackRouter.js +1934 -0
- package/extension/src/pageBridge.js +2224 -0
- package/extension/src/popup.js +247 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/styles/panel.css +4048 -0
- package/native-host/src/index.js +9 -2
- package/native-host/src/managedInstall.js +393 -0
- package/native-host/src/managedLauncherRuntime.js +71 -0
- package/native-host/src/nativeHostPlatform.js +16 -0
- package/native-host/src/runtimeInstaller.js +6 -1
- package/native-host/src/taskRunnerRuntime.js +8 -0
- package/native-host/src/updateArchive.js +253 -0
- package/native-host/src/updateManager.js +548 -0
- package/native-host/src/updateTrust.js +156 -0
- package/package.json +11 -2
- package/scripts/install-managed.mjs +64 -0
- package/scripts/uninstall-managed.mjs +48 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
const UPDATE_REPOSITORY = 'Ghqqqq/codex-overleaf-link';
|
|
6
|
+
const UPDATE_CHANNEL = 'stable';
|
|
7
|
+
const UPDATE_MANIFEST_SCHEMA = 2;
|
|
8
|
+
const BOOTSTRAP_PROTOCOL = 1;
|
|
9
|
+
const TRUSTED_UPDATE_KEYS = Object.freeze({
|
|
10
|
+
'release-2026-01': [
|
|
11
|
+
'-----BEGIN PUBLIC KEY-----',
|
|
12
|
+
'MCowBQYDK2VwAyEAhqNKNHdRuggyqyMz2knkMgNYgoUZS6FEjQlnYWgyDFA=',
|
|
13
|
+
'-----END PUBLIC KEY-----',
|
|
14
|
+
''
|
|
15
|
+
].join('\n')
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function verifySignedReleaseManifest(manifestBytes, signatureBytes, options = {}) {
|
|
19
|
+
const rawManifest = toBoundedBuffer(manifestBytes, 256 * 1024, 'update_manifest_too_large');
|
|
20
|
+
const rawSignature = toBoundedBuffer(signatureBytes, 16 * 1024, 'update_signature_too_large');
|
|
21
|
+
const envelope = parseJsonObject(rawSignature, 'update_signature_invalid');
|
|
22
|
+
if (envelope.algorithm !== 'Ed25519') {
|
|
23
|
+
throw updateError('update_signature_algorithm_unsupported', 'Update signature algorithm must be Ed25519.');
|
|
24
|
+
}
|
|
25
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(String(envelope.keyId || ''))) {
|
|
26
|
+
throw updateError('update_signature_key_invalid', 'Update signature key id is invalid.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const keys = options.publicKeys || TRUSTED_UPDATE_KEYS;
|
|
30
|
+
const publicKey = keys[envelope.keyId];
|
|
31
|
+
if (!publicKey) {
|
|
32
|
+
throw updateError('update_signature_key_unknown', 'Update signature key is not trusted.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let signature;
|
|
36
|
+
try {
|
|
37
|
+
signature = Buffer.from(String(envelope.signature || ''), 'base64');
|
|
38
|
+
} catch (_error) {
|
|
39
|
+
throw updateError('update_signature_invalid', 'Update signature encoding is invalid.');
|
|
40
|
+
}
|
|
41
|
+
if (signature.length !== 64 || !crypto.verify(null, rawManifest, publicKey, signature)) {
|
|
42
|
+
throw updateError('update_signature_invalid', 'Update manifest signature verification failed.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const manifest = parseJsonObject(rawManifest, 'update_manifest_invalid');
|
|
46
|
+
validateReleaseManifest(manifest, options);
|
|
47
|
+
return manifest;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateReleaseManifest(manifest, options = {}) {
|
|
51
|
+
const repository = options.repository || UPDATE_REPOSITORY;
|
|
52
|
+
const channel = options.channel || UPDATE_CHANNEL;
|
|
53
|
+
const bootstrapProtocol = Number(options.bootstrapProtocol || BOOTSTRAP_PROTOCOL);
|
|
54
|
+
if (manifest.schemaVersion !== UPDATE_MANIFEST_SCHEMA) {
|
|
55
|
+
throw updateError('update_manifest_schema_unsupported', 'Update manifest schema is not supported.');
|
|
56
|
+
}
|
|
57
|
+
if (manifest.repository !== repository || manifest.channel !== channel) {
|
|
58
|
+
throw updateError('update_manifest_origin_mismatch', 'Update manifest repository or channel does not match this installation.');
|
|
59
|
+
}
|
|
60
|
+
const version = parseSemver(manifest.version);
|
|
61
|
+
if (!version || manifest.tag !== 'v' + manifest.version) {
|
|
62
|
+
throw updateError('update_manifest_version_invalid', 'Update manifest version or tag is invalid.');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isInteger(manifest.bootstrapProtocol) || manifest.bootstrapProtocol !== bootstrapProtocol) {
|
|
65
|
+
throw updateError('update_bootstrap_upgrade_required', 'This release requires a different Bootstrap protocol.');
|
|
66
|
+
}
|
|
67
|
+
if (!/^[0-9a-f]{40}$/.test(String(manifest.gitCommit || ''))) {
|
|
68
|
+
throw updateError('update_manifest_commit_invalid', 'Update manifest commit is invalid.');
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isFinite(Date.parse(String(manifest.createdAt || '')))) {
|
|
71
|
+
throw updateError('update_manifest_timestamp_invalid', 'Update manifest timestamp is invalid.');
|
|
72
|
+
}
|
|
73
|
+
const bundle = manifest.updateBundle;
|
|
74
|
+
const expectedName = 'codex-overleaf-update-v' + manifest.version + '.tar.gz';
|
|
75
|
+
if (!bundle || bundle.name !== expectedName) {
|
|
76
|
+
throw updateError('update_bundle_name_invalid', 'Update bundle name does not match the release version.');
|
|
77
|
+
}
|
|
78
|
+
if (!Number.isSafeInteger(bundle.size) || bundle.size <= 0 || bundle.size > 32 * 1024 * 1024) {
|
|
79
|
+
throw updateError('update_bundle_size_invalid', 'Update bundle size is outside the supported limit.');
|
|
80
|
+
}
|
|
81
|
+
if (!/^[0-9a-f]{64}$/.test(String(bundle.sha256 || ''))) {
|
|
82
|
+
throw updateError('update_bundle_hash_invalid', 'Update bundle hash is invalid.');
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(manifest.artifacts)) {
|
|
85
|
+
throw updateError('update_manifest_artifacts_invalid', 'Update manifest artifacts must be an array.');
|
|
86
|
+
}
|
|
87
|
+
return manifest;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseSemver(value) {
|
|
91
|
+
const match = String(value || '').match(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/);
|
|
92
|
+
if (!match) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return match.slice(1).map(Number);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function compareSemver(left, right) {
|
|
99
|
+
const a = Array.isArray(left) ? left : parseSemver(left);
|
|
100
|
+
const b = Array.isArray(right) ? right : parseSemver(right);
|
|
101
|
+
if (!a || !b) {
|
|
102
|
+
throw updateError('update_version_invalid', 'A stable semantic version was expected.');
|
|
103
|
+
}
|
|
104
|
+
for (let index = 0; index < 3; index += 1) {
|
|
105
|
+
if (a[index] !== b[index]) {
|
|
106
|
+
return a[index] < b[index] ? -1 : 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isNewerStableVersion(candidate, current) {
|
|
113
|
+
return compareSemver(candidate, current) > 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseJsonObject(bytes, code) {
|
|
117
|
+
let value;
|
|
118
|
+
try {
|
|
119
|
+
value = JSON.parse(Buffer.from(bytes).toString('utf8'));
|
|
120
|
+
} catch (_error) {
|
|
121
|
+
throw updateError(code, 'Signed update metadata is not valid JSON.');
|
|
122
|
+
}
|
|
123
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
124
|
+
throw updateError(code, 'Signed update metadata must be an object.');
|
|
125
|
+
}
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function toBoundedBuffer(value, limit, code) {
|
|
130
|
+
const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value || '');
|
|
131
|
+
if (!buffer.length || buffer.length > limit) {
|
|
132
|
+
throw updateError(code, 'Signed update metadata exceeds its size limit.');
|
|
133
|
+
}
|
|
134
|
+
return buffer;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function updateError(code, message, details = {}) {
|
|
138
|
+
const error = new Error(message);
|
|
139
|
+
error.code = code;
|
|
140
|
+
Object.assign(error, details);
|
|
141
|
+
return error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
BOOTSTRAP_PROTOCOL,
|
|
146
|
+
TRUSTED_UPDATE_KEYS,
|
|
147
|
+
UPDATE_CHANNEL,
|
|
148
|
+
UPDATE_MANIFEST_SCHEMA,
|
|
149
|
+
UPDATE_REPOSITORY,
|
|
150
|
+
compareSemver,
|
|
151
|
+
isNewerStableVersion,
|
|
152
|
+
parseSemver,
|
|
153
|
+
updateError,
|
|
154
|
+
validateReleaseManifest,
|
|
155
|
+
verifySignedReleaseManifest
|
|
156
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-overleaf-link",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"verify:release": "node scripts/verify-release.mjs",
|
|
22
22
|
"verify:release-artifacts": "node scripts/verify-release-artifacts.mjs",
|
|
23
23
|
"build:release": "node scripts/build-release.mjs",
|
|
24
|
+
"sign:release": "node scripts/sign-release-manifest.mjs",
|
|
25
|
+
"install:managed": "node scripts/install-managed.mjs",
|
|
26
|
+
"uninstall:managed": "node scripts/uninstall-managed.mjs",
|
|
24
27
|
"install:native": "node scripts/install-native-host.mjs",
|
|
25
28
|
"uninstall:native": "node scripts/uninstall-native-host.mjs",
|
|
26
29
|
"bridge": "node native-host/src/index.js",
|
|
@@ -37,9 +40,15 @@
|
|
|
37
40
|
"files": [
|
|
38
41
|
"bin/",
|
|
39
42
|
"native-host/src/",
|
|
40
|
-
"extension/
|
|
43
|
+
"extension/assets/",
|
|
44
|
+
"extension/bootstrap/",
|
|
45
|
+
"extension/runtime-manifest.json",
|
|
46
|
+
"extension/src/",
|
|
47
|
+
"extension/styles/",
|
|
41
48
|
"scripts/codex-json-agent.mjs",
|
|
42
49
|
"scripts/install-native-host.mjs",
|
|
50
|
+
"scripts/install-managed.mjs",
|
|
51
|
+
"scripts/uninstall-managed.mjs",
|
|
43
52
|
"scripts/uninstall-native-host.mjs",
|
|
44
53
|
"scripts/verify-npm-package.mjs",
|
|
45
54
|
"README.md",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { installManagedDistribution } = require('../native-host/src/managedInstall.js');
|
|
8
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
9
|
+
const packageRoot = path.resolve(path.dirname(scriptPath), '..');
|
|
10
|
+
|
|
11
|
+
export function installManaged(options = {}) {
|
|
12
|
+
return installManagedDistribution({ ...options, packageRoot: options.packageRoot || packageRoot });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function formatInstallManagedHuman(result) {
|
|
16
|
+
return [
|
|
17
|
+
'Installed managed Codex Overleaf Link v' + result.version + '.',
|
|
18
|
+
'Managed extension path: ' + result.extensionRoot,
|
|
19
|
+
'Managed native root: ' + result.nativeRoot,
|
|
20
|
+
'One-time Chrome step:',
|
|
21
|
+
'1. Open chrome://extensions and enable Developer mode.',
|
|
22
|
+
'2. Remove the previous Codex Overleaf Link entry if Chrome shows a duplicate.',
|
|
23
|
+
'3. Choose Load unpacked and select: ' + result.loadUnpackedPath,
|
|
24
|
+
'Future stable extension and native-host updates will install automatically when Overleaf is saved and idle.',
|
|
25
|
+
''
|
|
26
|
+
].join('\n');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const parsed = {};
|
|
31
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
32
|
+
const arg = argv[index];
|
|
33
|
+
if (arg === '--extension-id') parsed.extensionId = readValue(argv, index++, arg);
|
|
34
|
+
else if (arg === '--browser') parsed.browser = readValue(argv, index++, arg);
|
|
35
|
+
else if (arg === '--managed-root') parsed.managedRoot = readValue(argv, index++, arg);
|
|
36
|
+
else if (arg === '--json') parsed.json = true;
|
|
37
|
+
else if (arg === '--help') parsed.help = true;
|
|
38
|
+
else throw new Error('Unknown option: ' + arg);
|
|
39
|
+
}
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readValue(argv, index, name) {
|
|
44
|
+
const value = argv[index + 1];
|
|
45
|
+
if (!value || value.startsWith('--')) throw new Error('Missing value for ' + name);
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function main() {
|
|
50
|
+
try {
|
|
51
|
+
const args = parseArgs(process.argv.slice(2));
|
|
52
|
+
if (args.help) {
|
|
53
|
+
console.log('Usage: install-managed [--extension-id <id>] [--browser chrome|chromium|auto] [--managed-root <path>] [--json]');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const result = installManaged(args);
|
|
57
|
+
process.stdout.write(args.json ? JSON.stringify(result, null, 2) + '\n' : formatInstallManagedHuman(result));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (path.resolve(process.argv[1] || '') === scriptPath) await main();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { uninstallManagedDistribution } = require('../native-host/src/managedInstall.js');
|
|
8
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
9
|
+
|
|
10
|
+
export function uninstallManaged(options = {}) {
|
|
11
|
+
return uninstallManagedDistribution(options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseArgs(argv) {
|
|
15
|
+
const parsed = {};
|
|
16
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
17
|
+
const arg = argv[index];
|
|
18
|
+
if (arg === '--browser') parsed.browser = readValue(argv, index++, arg);
|
|
19
|
+
else if (arg === '--managed-root') parsed.managedRoot = readValue(argv, index++, arg);
|
|
20
|
+
else if (arg === '--json') parsed.json = true;
|
|
21
|
+
else if (arg === '--help') parsed.help = true;
|
|
22
|
+
else throw new Error('Unknown option: ' + arg);
|
|
23
|
+
}
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readValue(argv, index, name) {
|
|
28
|
+
const value = argv[index + 1];
|
|
29
|
+
if (!value || value.startsWith('--')) throw new Error('Missing value for ' + name);
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function main() {
|
|
34
|
+
try {
|
|
35
|
+
const args = parseArgs(process.argv.slice(2));
|
|
36
|
+
if (args.help) {
|
|
37
|
+
console.log('Usage: uninstall-managed [--browser chrome|chromium|auto] [--managed-root <path>] [--json]');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const result = uninstallManaged(args);
|
|
41
|
+
process.stdout.write(args.json ? JSON.stringify(result, null, 2) + '\n' : 'Removed managed Codex Overleaf Link installation.\n');
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (path.resolve(process.argv[1] || '') === scriptPath) await main();
|