codex-overleaf-link 2.5.0 → 2.5.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 (168) hide show
  1. package/README.md +38 -26
  2. package/README.zh-CN.md +39 -27
  3. package/extension/bootstrap/background.js +52 -9
  4. package/extension/bootstrap/manifest.template.json +1 -1
  5. package/extension/bootstrap/popup.html +1 -0
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/updateStatus.js +1 -0
  8. package/extension/src/background.js +206 -53
  9. package/extension/src/backgroundBootstrapMigration.js +181 -0
  10. package/extension/src/backgroundSharedSessions.js +74 -14
  11. package/extension/src/backgroundUpdateCoordinator.js +118 -21
  12. package/extension/src/backgroundUpdateSafety.js +173 -0
  13. package/extension/src/content/activeTurnControl.js +8 -2
  14. package/extension/src/content/applyResultFormatters.js +46 -73
  15. package/extension/src/content/assetTransferBroker.js +72 -14
  16. package/extension/src/content/composerAttachments.js +93 -45
  17. package/extension/src/content/composerPanel.js +18 -0
  18. package/extension/src/content/contentRuntime.js +661 -676
  19. package/extension/src/content/diagnosticsController.js +122 -96
  20. package/extension/src/content/diagnosticsExport.js +19 -5
  21. package/extension/src/content/diagnosticsPanel.js +30 -0
  22. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  23. package/extension/src/content/generated/content.bundle.js +6879 -2786
  24. package/extension/src/content/generated/content.bundle.meta.json +11 -3
  25. package/extension/src/content/globalPreferencesController.js +1 -1
  26. package/extension/src/content/legacyGlobalRegistry.js +6 -0
  27. package/extension/src/content/localSkillsPanel.js +102 -12
  28. package/extension/src/content/markdownText.js +5 -6
  29. package/extension/src/content/modelPicker.js +7 -4
  30. package/extension/src/content/nativeCompatibilityController.js +45 -20
  31. package/extension/src/content/otWarmMirror.js +4 -5
  32. package/extension/src/content/otWarmMirrorController.js +17 -20
  33. package/extension/src/content/pageBridgeClient.js +15 -2
  34. package/extension/src/content/panelMaintenance.js +40 -12
  35. package/extension/src/content/panelRenderer.js +6 -0
  36. package/extension/src/content/projectProviderSelection.js +13 -4
  37. package/extension/src/content/projectSessionCleanup.js +109 -62
  38. package/extension/src/content/projectSettingsCoordinator.js +39 -2
  39. package/extension/src/content/providerSettingsCoordinator.js +46 -8
  40. package/extension/src/content/providerSettingsDialog.js +71 -13
  41. package/extension/src/content/recentProjects.js +11 -5
  42. package/extension/src/content/runActivitySummary.js +40 -6
  43. package/extension/src/content/runCompletionMeta.js +344 -0
  44. package/extension/src/content/runController.js +37 -14
  45. package/extension/src/content/runFailureNotice.js +21 -12
  46. package/extension/src/content/runGuidanceController.js +72 -27
  47. package/extension/src/content/runGuidanceView.js +59 -10
  48. package/extension/src/content/runQueueScheduler.js +148 -42
  49. package/extension/src/content/runRecoveryActions.js +146 -0
  50. package/extension/src/content/runResultActions.js +8 -125
  51. package/extension/src/content/runReviewController.js +574 -0
  52. package/extension/src/content/runScrollLayout.js +21 -2
  53. package/extension/src/content/runTimelineView.js +146 -153
  54. package/extension/src/content/scopedPersistencePanelState.js +51 -6
  55. package/extension/src/content/selectionContextView.js +1 -1
  56. package/extension/src/content/sensitivePreflight.js +140 -0
  57. package/extension/src/content/sessionForkController.js +80 -17
  58. package/extension/src/content/sessionManager.js +1 -1
  59. package/extension/src/content/settingsPanel.js +7 -3
  60. package/extension/src/content/settingsWorkbench.js +5 -0
  61. package/extension/src/content/sharedSessionViewSync.js +45 -8
  62. package/extension/src/content/updateIdle.js +4 -1
  63. package/extension/src/content/updateNotice.js +60 -8
  64. package/extension/src/content/updateSafetyReceiver.js +58 -0
  65. package/extension/src/content/writebackController.js +53 -4
  66. package/extension/src/content/writebackOrchestrator.js +82 -73
  67. package/extension/src/content/writingStyleSettings.js +35 -11
  68. package/extension/src/page/binaryAssetUploader.js +136 -14
  69. package/extension/src/page/fileMover.js +256 -0
  70. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  71. package/extension/src/page/referenceProjects.js +1 -0
  72. package/extension/src/page/reviewContext.js +113 -0
  73. package/extension/src/page/saveState.js +6 -4
  74. package/extension/src/page/snapshotRouter.js +84 -39
  75. package/extension/src/page/textFileCreator.js +105 -25
  76. package/extension/src/page/trackedChangeCapture.js +12 -2
  77. package/extension/src/page/trackedChangeOwnership.js +19 -1
  78. package/extension/src/page/trackedChangeReplay.js +41 -21
  79. package/extension/src/page/trackedChangesLifecycle.js +217 -199
  80. package/extension/src/page/treeOperations.js +7 -10
  81. package/extension/src/page/updateSafetyProbe.js +41 -0
  82. package/extension/src/page/writebackReceiptJournal.js +2 -0
  83. package/extension/src/page/writebackRouter.js +80 -79
  84. package/extension/src/pageBridge.js +19 -3
  85. package/extension/src/popup.js +62 -10
  86. package/extension/src/shared/agentTranscript.js +23 -0
  87. package/extension/src/shared/auditRecords.js +2 -0
  88. package/extension/src/shared/compatibility.js +22 -6
  89. package/extension/src/shared/compileAdapter.js +79 -57
  90. package/extension/src/shared/composerAttachmentDraft.js +151 -0
  91. package/extension/src/shared/directoryCleanupProof.js +43 -0
  92. package/extension/src/shared/failureReasons.js +309 -3
  93. package/extension/src/shared/globalPreferences.js +30 -13
  94. package/extension/src/shared/governanceRules.js +4 -37
  95. package/extension/src/shared/i18n.js +298 -2
  96. package/extension/src/shared/managedUpdateProjection.js +2 -2
  97. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  98. package/extension/src/shared/pageRpcContract.js +5 -4
  99. package/extension/src/shared/projectFiles.js +21 -44
  100. package/extension/src/shared/providerProfiles.js +5 -3
  101. package/extension/src/shared/runInputQueue.js +48 -5
  102. package/extension/src/shared/selectionContext.js +12 -3
  103. package/extension/src/shared/sessionState.js +72 -27
  104. package/extension/src/shared/settlementFacts.js +10 -2
  105. package/extension/src/shared/sharedSessionBridge.js +37 -14
  106. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  107. package/extension/src/shared/staleGuard.js +118 -13
  108. package/extension/src/shared/storageDb.js +58 -26
  109. package/extension/src/shared/storageMigration.js +71 -111
  110. package/extension/src/shared/storageRunActions.js +209 -26
  111. package/extension/src/shared/subagentActivityModel.js +1 -1
  112. package/extension/src/shared/undoOperations.js +136 -13
  113. package/extension/src/shared/updateConsent.js +6 -4
  114. package/extension/src/shared/updateRuntimeIdentity.js +1 -1
  115. package/extension/src/shared/writebackIntent.js +16 -0
  116. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  117. package/extension/src/shared/writebackSettlement.js +38 -38
  118. package/extension/styles/panel.css +19 -5
  119. package/native-host/src/anthropicBridgeRequest.js +4 -2
  120. package/native-host/src/anthropicBridgeResponse.js +6 -2
  121. package/native-host/src/anthropicMessagesBridge.js +24 -16
  122. package/native-host/src/bootstrapMigration.js +296 -0
  123. package/native-host/src/bootstrapMigrationContract.js +101 -0
  124. package/native-host/src/bootstrapUpdate.js +233 -0
  125. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  126. package/native-host/src/chatBridgeResponse.js +10 -0
  127. package/native-host/src/chatCompletionsBridge.js +21 -15
  128. package/native-host/src/codexConfigPaths.js +106 -0
  129. package/native-host/src/codexHome.js +66 -73
  130. package/native-host/src/codexHomeScope.js +124 -0
  131. package/native-host/src/codexInheritedAuth.js +90 -0
  132. package/native-host/src/codexLocalSkills.js +301 -0
  133. package/native-host/src/codexPromptAssembly.js +31 -12
  134. package/native-host/src/codexProviderTest.js +24 -26
  135. package/native-host/src/codexSessionRunner.js +110 -183
  136. package/native-host/src/codexSessionTiming.js +18 -8
  137. package/native-host/src/codexSkillIsolation.js +170 -0
  138. package/native-host/src/codexThreadFork.js +19 -3
  139. package/native-host/src/index.js +19 -7
  140. package/native-host/src/managedInstall.js +110 -35
  141. package/native-host/src/managedInstallRecovery.js +140 -0
  142. package/native-host/src/managedRuntimeEvidence.js +57 -0
  143. package/native-host/src/mirrorWorkspace.js +92 -22
  144. package/native-host/src/nativeDoctor.js +15 -0
  145. package/native-host/src/nativeHelperLifecycle.js +21 -0
  146. package/native-host/src/nativeHostRegistration.js +33 -0
  147. package/native-host/src/nativeRunLifecycle.js +71 -0
  148. package/native-host/src/processStopBarrier.js +42 -7
  149. package/native-host/src/projectPathPlatform.js +30 -0
  150. package/native-host/src/providerHttp.js +80 -0
  151. package/native-host/src/providerProfile.js +4 -2
  152. package/native-host/src/providerReasoning.js +2 -7
  153. package/native-host/src/providerRuntime.js +1 -0
  154. package/native-host/src/providerStore.js +72 -14
  155. package/native-host/src/responsesPassthroughBridge.js +266 -52
  156. package/native-host/src/runtimeInstaller.js +21 -1
  157. package/native-host/src/selectionScope.js +36 -8
  158. package/native-host/src/subagentAdmission.js +26 -0
  159. package/native-host/src/subagentBroker.js +79 -66
  160. package/native-host/src/subagentTelemetry.js +19 -10
  161. package/native-host/src/subagentWorkspacePath.js +5 -2
  162. package/native-host/src/taskRunnerRuntime.js +77 -54
  163. package/native-host/src/tomlStatements.js +140 -0
  164. package/native-host/src/updateManager.js +297 -39
  165. package/native-host/src/writingStyleRuntime.js +57 -29
  166. package/native-host/src/writingStyleStore.js +73 -0
  167. package/package.json +1 -1
  168. package/scripts/install-managed.mjs +1 -0
@@ -8,6 +8,7 @@ const { getCodexOverleafHome } = require('./nativeHostPlatform');
8
8
  const Shared = require('../../extension/src/shared/writingStyle');
9
9
  const REFERENCE_LIMITS = Shared.REFERENCE_LIMITS;
10
10
  const StyleProfile = require('./writingStyleProfile');
11
+ const { writeJson, withConfigLock } = require('./writingStyleStore');
11
12
  const sha = value => crypto.createHash('sha256').update(value).digest('hex');
12
13
  const fail = (code, message) => Object.assign(new Error(message), { code });
13
14
  const checkAbort = signal => { if (signal?.aborted) throw signal.reason || fail('codex_cancelled', 'Writing-style generation was cancelled.'); };
@@ -38,12 +39,6 @@ function readJson(file, fallback, max = 2 * 1024 * 1024) {
38
39
  } catch (error) { if (error.code === 'ENOENT') return fallback; throw error; }
39
40
  }
40
41
 
41
- function writeJson(file, value) {
42
- const temp = file + '.' + crypto.randomUUID() + '.tmp';
43
- fs.writeFileSync(temp, JSON.stringify(value), { mode: 0o600 });
44
- fs.renameSync(temp, file);
45
- }
46
-
47
42
  function config(scope) { return readJson(path.join(scope.root, 'config.json'), { revision: 0, enabled: false, version: '' }); }
48
43
  function saveConfig(scope, value) { const next = { ...value, revision: (Number(value.revision) || 0) + 1 }; writeJson(path.join(scope.root, 'config.json'), next); return next; }
49
44
  function alive(pid) { try { process.kill(Number(pid), 0); return true; } catch (error) { return error.code !== 'ESRCH'; } }
@@ -57,6 +52,10 @@ function versionRoot(scope, version) {
57
52
 
58
53
  function readBundle(scope, version, bundleHash) {
59
54
  const root = versionRoot(scope, version);
55
+ return readBundleAtRoot(scope, version, root, bundleHash);
56
+ }
57
+
58
+ function readBundleAtRoot(scope, version, root, bundleHash) {
60
59
  const raw = fs.readFileSync(path.join(root, 'manifest.json'), 'utf8');
61
60
  if (raw.length > 100000 || (bundleHash && sha(raw) !== bundleHash)) throw fail('writing_style_changed', 'The saved writing skill changed. Rebuild it in General settings.');
62
61
  const manifest = JSON.parse(raw);
@@ -79,13 +78,25 @@ function publicBundle(scope, bundle) {
79
78
  function get(params, env, isRequestActive) {
80
79
  const scope = scopeOf(params, env);
81
80
  let state = config(scope);
82
- if (state.building && (!alive(state.building.pid)
83
- || (state.building.pid === process.pid && isRequestActive && !isRequestActive(state.building.requestId)))) {
84
- state = saveConfig(scope, { ...state, building: null, error: 'The previous style-generation task was interrupted.' });
81
+ const interrupted = value => value.building && (!alive(value.building.pid)
82
+ || (value.building.pid === process.pid && isRequestActive && !isRequestActive(value.building.requestId)));
83
+ if (interrupted(state)) {
84
+ state = withConfigLock(scope, () => {
85
+ const latest = config(scope);
86
+ return interrupted(latest) ? saveConfig(scope, { ...latest, building: null, error: 'The previous style-generation task was interrupted.' }) : latest;
87
+ });
88
+ }
89
+ let bundle = null, unavailable = '';
90
+ if (state.version) {
91
+ try { bundle = readBundle(scope, state.version); }
92
+ catch (error) {
93
+ if (state.enabled === true || params.includePreview) throw error;
94
+ unavailable = 'The saved writing skill changed. Rebuild it in General settings.';
95
+ }
85
96
  }
86
- const bundle = state.version ? readBundle(scope, state.version) : null;
87
97
  const result = { enabled: state.enabled === true, ready: Boolean(bundle),
88
- revision: state.revision, building: state.building || null, error: state.error || '',
98
+ revision: state.revision, building: state.building || null, error: state.error || unavailable,
99
+ errorCode: !state.error && unavailable ? 'writing_style_changed' : '',
89
100
  ...(bundle ? publicBundle(scope, bundle) : { sources: [], sections: [], snapshot: null }) };
90
101
  if (params.includePreview && bundle) {
91
102
  result.preview = ['SKILL.md', 'references/writing-style.md', ...bundle.manifest.sections.map(item => item.file)]
@@ -96,10 +107,12 @@ function get(params, env, isRequestActive) {
96
107
 
97
108
  function set(params, env) {
98
109
  const scope = scopeOf(params, env);
99
- const state = config(scope);
100
- if (params.enabled === true && !state.version) throw fail('writing_style_not_ready', 'Generate the writing skill before enabling it.');
101
- if (params.enabled === true) readBundle(scope, state.version);
102
- saveConfig(scope, { ...state, enabled: params.enabled === true });
110
+ withConfigLock(scope, () => {
111
+ const state = config(scope);
112
+ if (params.enabled === true && !state.version) throw fail('writing_style_not_ready', 'Generate the writing skill before enabling it.');
113
+ if (params.enabled === true) readBundle(scope, state.version);
114
+ saveConfig(scope, { ...state, enabled: params.enabled === true });
115
+ });
103
116
  return get(params, env);
104
117
  }
105
118
 
@@ -333,12 +346,16 @@ async function build({ params, env, signal, execute, providerLaunch, onControlRe
333
346
  const scope = scopeOf(params, env), input = params.writingStyleBuild;
334
347
  const id = crypto.randomUUID();
335
348
  const staging = path.join(scope.root, '.build-' + id);
336
- fs.mkdirSync(path.join(staging, 'corpus'), { recursive: true, mode: 0o700 });
337
- const before = config(scope);
338
- saveConfig(scope, { ...before, error: '', building: { id, pid: process.pid,
339
- requestId: String(input.requestId || ''), startedAt: new Date().toISOString() } });
340
- let keepStaging = false;
349
+ let keepStaging = false, registered = false;
341
350
  try {
351
+ fs.mkdirSync(path.join(staging, 'corpus'), { recursive: true, mode: 0o700 });
352
+ const before = withConfigLock(scope, () => {
353
+ const state = config(scope);
354
+ saveConfig(scope, { ...state, error: '', building: { id, pid: process.pid,
355
+ requestId: String(input.requestId || ''), startedAt: new Date().toISOString() } });
356
+ return state;
357
+ });
358
+ registered = true;
342
359
  const documents = await prepareSources(input, params, scope, env, signal);
343
360
  checkAbort(signal);
344
361
  for (const doc of documents) {
@@ -404,8 +421,6 @@ async function build({ params, env, signal, execute, providerLaunch, onControlRe
404
421
  + 'Preserve current facts, numbers, citations, notation, and mathematical qualifications. Never copy a source paper\'s results into the target paper. '
405
422
  + 'PDF examples support prose only, not original macros or exact equation notation. '
406
423
  + 'Do not expand the edit scope, search for additional sources, or change Track/Undo behavior.\n';
407
- const latest = config(scope);
408
- if (latest.building?.id !== id) throw fail('writing_style_build_superseded', 'A newer style-generation task replaced this one.');
409
424
  const versionDir = path.join(scope.root, 'versions', id);
410
425
  fs.mkdirSync(path.dirname(versionDir), { recursive: true, mode: 0o700 });
411
426
  const built = path.join(staging, 'bundle');
@@ -423,11 +438,18 @@ async function build({ params, env, signal, execute, providerLaunch, onControlRe
423
438
  traits: result.traits.map(({ id, dimension, title, exampleIds, sourceIds }) => ({ id, dimension, title, exampleIds, sourceIds })),
424
439
  sections, files: hashes };
425
440
  fs.writeFileSync(path.join(built, 'manifest.json'), JSON.stringify(manifest), { mode: 0o400 });
441
+ const bundle = readBundleAtRoot(scope, id, built);
442
+ for (const file of Object.keys(bundle.manifest.files)) safeBundleText(bundle, file);
426
443
  checkAbort(signal);
427
- fs.renameSync(built, versionDir);
428
- const unchangedPreference = latest.revision === before.revision + 1;
429
- saveConfig(scope, { ...latest, version: id, building: null, error: '',
430
- enabled: unchangedPreference ? input.enableAfterBuild !== false : latest.enabled === true });
444
+ withConfigLock(scope, () => {
445
+ checkAbort(signal);
446
+ const latest = config(scope);
447
+ if (latest.building?.id !== id) throw fail('writing_style_build_superseded', 'A newer style-generation task replaced this one.');
448
+ fs.renameSync(built, versionDir);
449
+ const unchangedPreference = latest.revision === before.revision + 1;
450
+ saveConfig(scope, { ...latest, version: id, building: null, error: '',
451
+ enabled: unchangedPreference ? input.enableAfterBuild !== false : latest.enabled === true });
452
+ });
431
453
  return { status: 'completed', assistantMessage: 'Writing skill generated: ' + result.label,
432
454
  syncChanges: [], unsupportedChanges: [], writingStyle: get({ ...params }, env) };
433
455
  } catch (error) {
@@ -435,9 +457,15 @@ async function build({ params, env, signal, execute, providerLaunch, onControlRe
435
457
  keepStaging = true;
436
458
  onStopUnconfirmed?.(error);
437
459
  }
438
- const state = config(scope);
439
- if (state.building?.id === id) saveConfig(scope, { ...state, building: null,
440
- error: error?.code === 'codex_cancelled' ? 'Style generation cancelled.' : String(error.message || 'Style generation failed.').replace(/\/Users\/\S+|\/home\/\S+|[A-Z]:\\\S+/g, '[local path]').slice(0, 300) });
460
+ if (registered) {
461
+ try {
462
+ withConfigLock(scope, () => {
463
+ const state = config(scope);
464
+ if (state.building?.id === id) saveConfig(scope, { ...state, building: null,
465
+ error: error?.code === 'codex_cancelled' ? 'Style generation cancelled.' : String(error.message || 'Style generation failed.').replace(/\/Users\/\S+|\/home\/\S+|[A-Z]:\\\S+/g, '[local path]').slice(0, 300) });
466
+ });
467
+ } catch (_) { /* Preserve the original failure if config is unavailable. */ }
468
+ }
441
469
  throw error;
442
470
  } finally {
443
471
  if (!keepStaging) fs.rmSync(staging, { recursive: true, force: true });
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const crypto = require('node:crypto');
5
+
6
+ function writeJson(file, value) {
7
+ const temp = file + '.' + crypto.randomUUID() + '.tmp';
8
+ let created = false;
9
+ try {
10
+ const fd = fs.openSync(temp, 'wx', 0o600);
11
+ created = true;
12
+ try { fs.writeFileSync(fd, JSON.stringify(value)); }
13
+ finally { fs.closeSync(fd); }
14
+ fs.renameSync(temp, file);
15
+ } finally {
16
+ if (created) fs.rmSync(temp, { force: true });
17
+ }
18
+ }
19
+
20
+ function withConfigLock(scope, action) {
21
+ const lock = path.join(scope.root, '.config-lock');
22
+ const owner = process.pid + '-' + crypto.randomUUID() + '.owner';
23
+ const pending = path.join(scope.root, '.config-lock-' + owner);
24
+ let acquired = false, prepared = false;
25
+ try {
26
+ // Publish a nonempty directory atomically, so a reaper can only remove an
27
+ // empty old lock; it cannot remove a replacement owner's marker.
28
+ fs.mkdirSync(pending, { mode: 0o700 });
29
+ prepared = true;
30
+ fs.writeFileSync(path.join(pending, owner), '', { mode: 0o600 });
31
+ const deadline = Date.now() + 2000;
32
+ while (!acquired) {
33
+ try { fs.renameSync(pending, lock); acquired = true; }
34
+ catch (error) {
35
+ if (!['EEXIST', 'ENOTEMPTY', 'EPERM', 'EACCES'].includes(error.code)) throw error;
36
+ const stat = fs.lstatSync(lock, { throwIfNoEntry: false });
37
+ if (!stat && ['EPERM', 'EACCES'].includes(error.code)) throw error;
38
+ if (stat && (!stat.isDirectory() || stat.isSymbolicLink())) throw error;
39
+ if (stat) reapDeadOwner(lock);
40
+ if (Date.now() >= deadline) throw Object.assign(new Error('Writing-style settings are being updated by another Native Host. Retry shortly.'), { code: 'writing_style_busy' });
41
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
42
+ }
43
+ }
44
+ return action();
45
+ } finally {
46
+ if (acquired) removeOwner(lock, owner);
47
+ if (prepared) fs.rmSync(pending, { recursive: true, force: true });
48
+ }
49
+ }
50
+
51
+ function reapDeadOwner(lock) {
52
+ let entries;
53
+ try { entries = fs.readdirSync(lock); } catch (error) { if (error.code === 'ENOENT') return; throw error; }
54
+ if (!entries.length) { removeEmptyLock(lock); return; }
55
+ if (entries.length !== 1) return;
56
+ const match = /^([1-9]\d*)-[a-f0-9-]{36}\.owner$/.exec(entries[0]);
57
+ if (!match || !Number.isSafeInteger(Number(match[1]))) return;
58
+ try { process.kill(Number(match[1]), 0); }
59
+ catch (error) { if (error.code === 'ESRCH') removeOwner(lock, entries[0]); }
60
+ }
61
+
62
+ function removeOwner(lock, owner) {
63
+ try { fs.unlinkSync(path.join(lock, owner)); }
64
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
65
+ removeEmptyLock(lock);
66
+ }
67
+
68
+ function removeEmptyLock(lock) {
69
+ try { fs.rmdirSync(lock); }
70
+ catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) throw error; }
71
+ }
72
+
73
+ module.exports = { writeJson, withConfigLock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -22,6 +22,7 @@ export function formatInstallManagedHuman(result) {
22
22
  '2. Remove the previous Codex Overleaf Link entry if Chrome shows a duplicate.',
23
23
  '3. Choose Load unpacked and select: ' + result.loadUnpackedPath,
24
24
  'Future signed stable updates are checked automatically. Choose Update now to authorize a version; installation waits until Overleaf is saved and idle.',
25
+ ...(result.warning ? ['Warning: ' + result.warning] : []),
25
26
  ''
26
27
  ].join('\n');
27
28
  }