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,372 @@
|
|
|
1
|
+
(function initCodexOverleafCompatibility(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafCompatibility = factory();
|
|
6
|
+
}
|
|
7
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function compatibilityFactory() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const EXTENSION_PROTOCOL_VERSION = 1;
|
|
11
|
+
const SUPPORTED_NATIVE_PROTOCOL = Object.freeze({ min: 1, max: 1 });
|
|
12
|
+
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
|
+
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
|
+
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
+
const BUILD_TARGET_VERSION = '1.1.1';
|
|
16
|
+
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
|
+
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
|
+
'bridgePing',
|
|
19
|
+
'mirrorSync',
|
|
20
|
+
'mirrorPatchFiles',
|
|
21
|
+
'mirrorStatus',
|
|
22
|
+
'codexRun',
|
|
23
|
+
'codexCancel',
|
|
24
|
+
'codexModels',
|
|
25
|
+
'historyClearPlugin',
|
|
26
|
+
'localSkills',
|
|
27
|
+
'mirrorSensitiveScan'
|
|
28
|
+
]);
|
|
29
|
+
const UPDATE_AVAILABLE_CAPABILITIES = Object.freeze([
|
|
30
|
+
'bridgePing',
|
|
31
|
+
'mirrorStatus',
|
|
32
|
+
'codexModels',
|
|
33
|
+
'codexCancel',
|
|
34
|
+
'localSkills'
|
|
35
|
+
]);
|
|
36
|
+
const COMPATIBILITY_CLASSIFICATIONS = Object.freeze({
|
|
37
|
+
compatible: 'compatible',
|
|
38
|
+
updateAvailable: 'update-available',
|
|
39
|
+
incompatible: 'incompatible'
|
|
40
|
+
});
|
|
41
|
+
const UPDATE_AVAILABLE_METHODS = new Set([
|
|
42
|
+
'bridge.ping',
|
|
43
|
+
'mirror.status',
|
|
44
|
+
'codex.models',
|
|
45
|
+
'codex.cancel',
|
|
46
|
+
'skills.list'
|
|
47
|
+
]);
|
|
48
|
+
const INCOMPATIBLE_ALLOWED_METHODS = new Set([
|
|
49
|
+
'bridge.ping'
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
function buildBridgePingParams(metadata = {}) {
|
|
53
|
+
const extensionVersion = resolveMetadataVersion(metadata);
|
|
54
|
+
return {
|
|
55
|
+
extensionVersion: extensionVersion.normalized,
|
|
56
|
+
extensionProtocolVersion: EXTENSION_PROTOCOL_VERSION,
|
|
57
|
+
supportedNativeProtocol: { ...SUPPORTED_NATIVE_PROTOCOL },
|
|
58
|
+
requiredCapabilities: REQUIRED_CAPABILITIES.slice()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function evaluateNativeCompatibility(response, metadata = {}) {
|
|
63
|
+
const native = unwrapNativeResponse(response);
|
|
64
|
+
if (!native) {
|
|
65
|
+
return compatibilityResult('native_missing', response, metadata, 'incompatible');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const analysis = analyzeNativeCompatibility(native, metadata);
|
|
69
|
+
return compatibilityResult(analysis.status, native, metadata, analysis.classification);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function classifyNativeCompatibility(pingResult, extensionVersion) {
|
|
73
|
+
const metadata = extensionVersion === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: typeof extensionVersion === 'object'
|
|
76
|
+
? extensionVersion
|
|
77
|
+
: { version: extensionVersion };
|
|
78
|
+
const native = unwrapNativeResponse(pingResult);
|
|
79
|
+
if (!native) {
|
|
80
|
+
return 'incompatible';
|
|
81
|
+
}
|
|
82
|
+
return analyzeNativeCompatibility(native, metadata).classification;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildInstallCommand(version = BUILD_TARGET_VERSION, platform, extensionId) {
|
|
86
|
+
const normalized = normalizeReleaseVersion(version) || BUILD_TARGET_VERSION;
|
|
87
|
+
const allowedExtensionId = normalizeInstallExtensionId(extensionId);
|
|
88
|
+
const extensionIdArg = allowedExtensionId && allowedExtensionId !== DEFAULT_CHROME_EXTENSION_ID
|
|
89
|
+
? ` --extension-id ${allowedExtensionId}`
|
|
90
|
+
: '';
|
|
91
|
+
|
|
92
|
+
return `npm exec --yes codex-overleaf-link@${normalized} -- install-native${extensionIdArg}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildReleaseUrl(version = BUILD_TARGET_VERSION) {
|
|
96
|
+
const normalized = normalizeReleaseVersion(version) || BUILD_TARGET_VERSION;
|
|
97
|
+
return `https://github.com/Ghqqqq/codex-overleaf-link/releases/tag/v${normalized}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isMethodAllowed(method, classification) {
|
|
101
|
+
const normalizedClassification = normalizeClassification(classification);
|
|
102
|
+
if (method === 'bridge.ping') {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (normalizedClassification === 'compatible') {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (normalizedClassification === 'update-available') {
|
|
109
|
+
return UPDATE_AVAILABLE_METHODS.has(method);
|
|
110
|
+
}
|
|
111
|
+
if (normalizedClassification === 'incompatible') {
|
|
112
|
+
return INCOMPATIBLE_ALLOWED_METHODS.has(method);
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isNativeMethodAllowed(method, compatibilityStatus) {
|
|
118
|
+
return isMethodAllowed(method, getClassification(compatibilityStatus));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function compatibilityResult(status, native, metadata = {}, classification = 'incompatible') {
|
|
122
|
+
const extensionVersion = resolveMetadataVersion(metadata);
|
|
123
|
+
const nativeVersion = native && typeof native === 'object' ? native.version : undefined;
|
|
124
|
+
const platform = native && typeof native === 'object' ? native.platform : undefined;
|
|
125
|
+
const updateCommand = buildInstallCommand(BUILD_TARGET_VERSION, platform, metadata.extensionId);
|
|
126
|
+
const updateAvailable = isOlderReleaseVersion(nativeVersion, BUILD_TARGET_VERSION);
|
|
127
|
+
return {
|
|
128
|
+
status,
|
|
129
|
+
classification,
|
|
130
|
+
native,
|
|
131
|
+
nativeVersion,
|
|
132
|
+
currentNativeVersion: nativeVersion,
|
|
133
|
+
extensionVersion: extensionVersion.normalized,
|
|
134
|
+
minimumNativeVersion: MIN_COMPATIBLE_NATIVE_VERSION,
|
|
135
|
+
requiredVersion: MIN_COMPATIBLE_NATIVE_VERSION,
|
|
136
|
+
recommendedVersion: BUILD_TARGET_VERSION,
|
|
137
|
+
updateAvailable,
|
|
138
|
+
installCommand: updateCommand,
|
|
139
|
+
updateCommand,
|
|
140
|
+
releaseUrl: buildReleaseUrl(BUILD_TARGET_VERSION),
|
|
141
|
+
platform: normalizePlatform(platform) || platform || undefined,
|
|
142
|
+
missingCapabilities: getMissingCapabilities(native?.capabilities, REQUIRED_CAPABILITIES),
|
|
143
|
+
missingUpdateCapabilities: getMissingCapabilities(native?.capabilities, UPDATE_AVAILABLE_CAPABILITIES)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function analyzeNativeCompatibility(native, metadata = {}) {
|
|
148
|
+
const extensionVersion = resolveMetadataVersion(metadata);
|
|
149
|
+
const nativeVersion = parseSemver(native.version);
|
|
150
|
+
const minimumNativeVersion = parseSemver(MIN_COMPATIBLE_NATIVE_VERSION);
|
|
151
|
+
|
|
152
|
+
if (!native.version || !nativeVersion) {
|
|
153
|
+
return { status: 'native_too_old', classification: 'incompatible' };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!reportsProtocolOne(native)) {
|
|
157
|
+
return { status: 'protocol_unsupported', classification: 'incompatible' };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!native.capabilities || typeof native.capabilities !== 'object') {
|
|
161
|
+
return { status: 'native_too_old', classification: 'incompatible' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const minExtensionVersion = native.minExtensionVersion ? parseSemver(native.minExtensionVersion) : null;
|
|
165
|
+
if (
|
|
166
|
+
native.minExtensionVersion &&
|
|
167
|
+
(!minExtensionVersion || !extensionVersion.valid || compareSemver(extensionVersion.parsed, minExtensionVersion) < 0)
|
|
168
|
+
) {
|
|
169
|
+
return { status: 'extension_too_old', classification: 'incompatible' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (native.environment?.codex?.ok === false) {
|
|
173
|
+
return { status: 'native_unhealthy', classification: 'incompatible' };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (compareSemver(nativeVersion, minimumNativeVersion) >= 0) {
|
|
177
|
+
return hasCapabilities(native.capabilities, REQUIRED_CAPABILITIES)
|
|
178
|
+
? { status: 'ok', classification: 'compatible' }
|
|
179
|
+
: { status: 'native_too_old', classification: 'incompatible' };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return hasCapabilities(native.capabilities, UPDATE_AVAILABLE_CAPABILITIES)
|
|
183
|
+
? { status: 'native_too_old', classification: 'update-available' }
|
|
184
|
+
: { status: 'native_too_old', classification: 'incompatible' };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function unwrapNativeResponse(response) {
|
|
188
|
+
if (!response || response.ok === false) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const native = response.ok === true ? response.result : response;
|
|
192
|
+
return native && typeof native === 'object' ? native : null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getClassification(compatibilityStatus) {
|
|
196
|
+
if (typeof compatibilityStatus === 'string') {
|
|
197
|
+
return normalizeClassification(compatibilityStatus) || mapLegacyStatusToClassification(compatibilityStatus);
|
|
198
|
+
}
|
|
199
|
+
if (!compatibilityStatus || typeof compatibilityStatus !== 'object') {
|
|
200
|
+
return 'incompatible';
|
|
201
|
+
}
|
|
202
|
+
return normalizeClassification(compatibilityStatus.classification) ||
|
|
203
|
+
normalizeClassification(compatibilityStatus.status) ||
|
|
204
|
+
mapLegacyStatusToClassification(compatibilityStatus.status);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function mapLegacyStatusToClassification(status) {
|
|
208
|
+
if (status === 'ok') {
|
|
209
|
+
return 'compatible';
|
|
210
|
+
}
|
|
211
|
+
return 'incompatible';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function normalizeClassification(classification) {
|
|
215
|
+
if (
|
|
216
|
+
classification === 'compatible' ||
|
|
217
|
+
classification === 'update-available' ||
|
|
218
|
+
classification === 'incompatible'
|
|
219
|
+
) {
|
|
220
|
+
return classification;
|
|
221
|
+
}
|
|
222
|
+
return '';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function hasCapabilities(capabilities, requiredCapabilities) {
|
|
226
|
+
return requiredCapabilities.every(capability => capabilities[capability] === true);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function getMissingCapabilities(capabilities, requiredCapabilities) {
|
|
230
|
+
if (!capabilities || typeof capabilities !== 'object') {
|
|
231
|
+
return requiredCapabilities.slice();
|
|
232
|
+
}
|
|
233
|
+
return requiredCapabilities.filter(capability => capabilities[capability] !== true);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function reportsProtocolOne(native) {
|
|
237
|
+
if (native.protocolVersion !== EXTENSION_PROTOCOL_VERSION) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
if (native.supportedProtocol === undefined || native.supportedProtocol === null) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
return protocolRangesOverlap(SUPPORTED_NATIVE_PROTOCOL, native.supportedProtocol);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function isProtocolRange(range) {
|
|
247
|
+
return range &&
|
|
248
|
+
Number.isInteger(range.min) &&
|
|
249
|
+
Number.isInteger(range.max) &&
|
|
250
|
+
range.min <= range.max;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function protocolRangesOverlap(left, right) {
|
|
254
|
+
return isProtocolRange(left) &&
|
|
255
|
+
isProtocolRange(right) &&
|
|
256
|
+
left.min <= right.max &&
|
|
257
|
+
right.min <= left.max;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function resolveMetadataVersion(metadata = {}) {
|
|
261
|
+
const hasVersion = metadata &&
|
|
262
|
+
typeof metadata === 'object' &&
|
|
263
|
+
Object.prototype.hasOwnProperty.call(metadata, 'version');
|
|
264
|
+
if (!hasVersion) {
|
|
265
|
+
return {
|
|
266
|
+
normalized: BUILD_TARGET_VERSION,
|
|
267
|
+
parsed: parseSemver(BUILD_TARGET_VERSION),
|
|
268
|
+
valid: true
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const normalized = normalizeReleaseVersion(metadata.version);
|
|
273
|
+
if (!normalized) {
|
|
274
|
+
return {
|
|
275
|
+
normalized: BUILD_TARGET_VERSION,
|
|
276
|
+
parsed: parseSemver(BUILD_TARGET_VERSION),
|
|
277
|
+
valid: false
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
normalized,
|
|
283
|
+
parsed: parseSemver(normalized),
|
|
284
|
+
valid: true
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function normalizeReleaseVersion(version) {
|
|
289
|
+
if (typeof version !== 'string') {
|
|
290
|
+
return '';
|
|
291
|
+
}
|
|
292
|
+
const match = /^v?(\d+\.\d+\.\d+)$/.exec(version);
|
|
293
|
+
return match ? match[1] : '';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function isOlderReleaseVersion(version, targetVersion) {
|
|
297
|
+
const parsedVersion = parseSemver(version);
|
|
298
|
+
const parsedTarget = parseSemver(targetVersion);
|
|
299
|
+
return Boolean(parsedVersion && parsedTarget && compareSemver(parsedVersion, parsedTarget) < 0);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function normalizePlatform(platform) {
|
|
303
|
+
const normalized = String(platform || '').toLowerCase();
|
|
304
|
+
if (!normalized) {
|
|
305
|
+
return '';
|
|
306
|
+
}
|
|
307
|
+
if (normalized === 'win32' || normalized === 'windows' || normalized.startsWith('win')) {
|
|
308
|
+
return 'windows';
|
|
309
|
+
}
|
|
310
|
+
if (normalized === 'darwin' || normalized === 'mac' || normalized === 'macos' || normalized.includes('mac')) {
|
|
311
|
+
return 'darwin';
|
|
312
|
+
}
|
|
313
|
+
if (normalized === 'linux' || normalized.includes('linux')) {
|
|
314
|
+
return 'linux';
|
|
315
|
+
}
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function normalizeInstallExtensionId(extensionId) {
|
|
320
|
+
return typeof extensionId === 'string' && /^[a-p]{32}$/.test(extensionId)
|
|
321
|
+
? extensionId
|
|
322
|
+
: '';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function detectCurrentPlatform() {
|
|
326
|
+
if (typeof navigator === 'undefined') {
|
|
327
|
+
return '';
|
|
328
|
+
}
|
|
329
|
+
return normalizePlatform(navigator.userAgentData?.platform || navigator.platform || '');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function parseSemver(version) {
|
|
333
|
+
const normalized = normalizeReleaseVersion(version);
|
|
334
|
+
if (!normalized) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const [major, minor, patch] = normalized.split('.').map(Number);
|
|
338
|
+
return {
|
|
339
|
+
major,
|
|
340
|
+
minor,
|
|
341
|
+
patch
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function compareSemver(left, right) {
|
|
346
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
347
|
+
if (left[key] !== right[key]) {
|
|
348
|
+
return left[key] < right[key] ? -1 : 1;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
EXTENSION_PROTOCOL_VERSION,
|
|
356
|
+
SUPPORTED_NATIVE_PROTOCOL,
|
|
357
|
+
MIN_NATIVE_VERSION,
|
|
358
|
+
MIN_COMPATIBLE_NATIVE_VERSION,
|
|
359
|
+
MIN_COMPATIBLE_EXTENSION_VERSION,
|
|
360
|
+
REQUIRED_CAPABILITIES,
|
|
361
|
+
UPDATE_AVAILABLE_CAPABILITIES,
|
|
362
|
+
BUILD_TARGET_VERSION,
|
|
363
|
+
COMPATIBILITY_CLASSIFICATIONS,
|
|
364
|
+
buildBridgePingParams,
|
|
365
|
+
evaluateNativeCompatibility,
|
|
366
|
+
classifyNativeCompatibility,
|
|
367
|
+
buildInstallCommand,
|
|
368
|
+
buildReleaseUrl,
|
|
369
|
+
isMethodAllowed,
|
|
370
|
+
isNativeMethodAllowed
|
|
371
|
+
};
|
|
372
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
(function initCompileAdapter(root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafCompileAdapter = factory();
|
|
6
|
+
}
|
|
7
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function compileAdapterFactory() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const MAX_LOG_BYTES = 32 * 1024;
|
|
11
|
+
const COMPILABLE_EXTENSIONS = new Set(['.tex', '.bib', '.sty', '.cls', '.bst', '.bbx', '.cbx']);
|
|
12
|
+
|
|
13
|
+
function parseCompileResponse(response) {
|
|
14
|
+
if (!response || typeof response !== 'object') {
|
|
15
|
+
return { ok: false, reason: 'Invalid compile response: not an object' };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!Array.isArray(response.outputFiles)) {
|
|
19
|
+
return { ok: false, reason: 'Invalid compile response: outputFiles is not an array' };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const logFile = response.outputFiles.find(function (file) {
|
|
23
|
+
if (!file) return false;
|
|
24
|
+
if (file.type === 'log') return true;
|
|
25
|
+
if (typeof file.path === 'string' && file.path.endsWith('.log')) return true;
|
|
26
|
+
return false;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!logFile) {
|
|
30
|
+
return { ok: false, reason: 'No log file found in compile output' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var logUrl = logFile.url || null;
|
|
34
|
+
if (!logUrl) {
|
|
35
|
+
return { ok: false, reason: 'Log file has no URL' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
status: response.status,
|
|
41
|
+
logUrl: logUrl,
|
|
42
|
+
logPath: logFile.path || null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function extractErrorBlocks(lines) {
|
|
47
|
+
var blocks = [];
|
|
48
|
+
var i = 0;
|
|
49
|
+
|
|
50
|
+
while (i < lines.length && blocks.length < 20) {
|
|
51
|
+
var line = lines[i];
|
|
52
|
+
if (line.startsWith('!') || /^l\.\d+/.test(line)) {
|
|
53
|
+
var block = [];
|
|
54
|
+
// Collect surrounding context: go back up to 2 lines for pre-context
|
|
55
|
+
var start = Math.max(0, i - 2);
|
|
56
|
+
for (var j = start; j < i; j++) {
|
|
57
|
+
block.push(lines[j]);
|
|
58
|
+
}
|
|
59
|
+
// Collect from the error line forward until blank line or capital letter line
|
|
60
|
+
while (i < lines.length) {
|
|
61
|
+
block.push(lines[i]);
|
|
62
|
+
i++;
|
|
63
|
+
if (i < lines.length) {
|
|
64
|
+
var next = lines[i];
|
|
65
|
+
if (next.trim() === '') {
|
|
66
|
+
block.push(next);
|
|
67
|
+
i++;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (/^[A-Z]/.test(next) && !next.startsWith('!') && !/^l\.\d+/.test(next)) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
blocks.push(block.join('\n'));
|
|
76
|
+
} else {
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return blocks;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function truncateLogForContext(log) {
|
|
85
|
+
if (!log || typeof log !== 'string') {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (new Blob([log]).size <= MAX_LOG_BYTES) {
|
|
90
|
+
return log;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var lines = log.split('\n');
|
|
94
|
+
var errorBlocks = extractErrorBlocks(lines);
|
|
95
|
+
var tail = lines.slice(-200).join('\n');
|
|
96
|
+
|
|
97
|
+
var separator = '\n\n--- [truncated] ---\n\n';
|
|
98
|
+
var combined = errorBlocks.join('\n\n') + separator + tail;
|
|
99
|
+
|
|
100
|
+
// Check if combined fits
|
|
101
|
+
if (new Blob([combined]).size <= MAX_LOG_BYTES) {
|
|
102
|
+
return combined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Slice to 90% of MAX_LOG_BYTES in characters as a final fallback
|
|
106
|
+
var maxChars = Math.floor(MAX_LOG_BYTES * 0.9);
|
|
107
|
+
return combined.slice(0, maxChars);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isCompileLogFresh(log, lastKnownSourceEditTimestamp) {
|
|
111
|
+
if (!log || typeof log !== 'object') {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (log.sourceChangeTimestamp == null) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return log.sourceChangeTimestamp >= lastKnownSourceEditTimestamp;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isCompilableFile(filePath) {
|
|
123
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var lastDot = filePath.lastIndexOf('.');
|
|
128
|
+
if (lastDot === -1) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
var ext = filePath.slice(lastDot).toLowerCase();
|
|
133
|
+
return COMPILABLE_EXTENSIONS.has(ext);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function parseLogErrors(logContent) {
|
|
137
|
+
var result = { errors: [], warnings: [] };
|
|
138
|
+
|
|
139
|
+
if (!logContent || typeof logContent !== 'string') {
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
var lines = logContent.split('\n');
|
|
144
|
+
|
|
145
|
+
// Extract errors: lines starting with '!' plus up to 3 following context lines
|
|
146
|
+
for (var i = 0; i < lines.length && result.errors.length < 50; i++) {
|
|
147
|
+
if (lines[i].startsWith('!')) {
|
|
148
|
+
var errorLines = [lines[i]];
|
|
149
|
+
for (var j = 1; j <= 3 && (i + j) < lines.length; j++) {
|
|
150
|
+
errorLines.push(lines[i + j]);
|
|
151
|
+
}
|
|
152
|
+
result.errors.push(errorLines.join('\n'));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Extract warnings: lines matching /Warning/i that don't start with whitespace
|
|
157
|
+
for (var k = 0; k < lines.length && result.warnings.length < 50; k++) {
|
|
158
|
+
if (/Warning/i.test(lines[k]) && !/^\s/.test(lines[k])) {
|
|
159
|
+
result.warnings.push(lines[k]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
COMPILABLE_EXTENSIONS,
|
|
168
|
+
MAX_LOG_BYTES,
|
|
169
|
+
extractErrorBlocks,
|
|
170
|
+
isCompilableFile,
|
|
171
|
+
isCompileLogFresh,
|
|
172
|
+
parseCompileResponse,
|
|
173
|
+
parseLogErrors,
|
|
174
|
+
truncateLogForContext
|
|
175
|
+
};
|
|
176
|
+
});
|