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,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
+ });