codex-overleaf-link 2.5.0 → 2.6.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 +155 -392
- package/README.zh-CN.md +154 -393
- package/extension/bootstrap/background.js +52 -9
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/bootstrap/popup.html +1 -0
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/src/background.js +283 -55
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +129 -16
- package/extension/src/backgroundUpdateCoordinator.js +242 -29
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +171 -8
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +72 -14
- package/extension/src/content/composerAttachments.js +104 -47
- package/extension/src/content/composerPanel.js +110 -12
- package/extension/src/content/contentRuntime.js +1555 -926
- package/extension/src/content/contextTray.js +8 -2
- package/extension/src/content/contextUsageView.js +267 -0
- package/extension/src/content/diagnosticsController.js +122 -96
- package/extension/src/content/diagnosticsExport.js +19 -5
- package/extension/src/content/diagnosticsPanel.js +48 -0
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +11077 -3412
- package/extension/src/content/generated/content.bundle.meta.json +14 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +10 -0
- package/extension/src/content/localSkillsPanel.js +104 -14
- package/extension/src/content/markdownDomRenderer.js +5 -3
- package/extension/src/content/markdownText.js +47 -11
- package/extension/src/content/modelPicker.js +8 -4
- package/extension/src/content/nativeCompatibilityController.js +74 -30
- package/extension/src/content/otWarmMirror.js +5 -6
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +17 -4
- package/extension/src/content/panelMaintenance.js +46 -14
- package/extension/src/content/panelRenderer.js +57 -27
- package/extension/src/content/pendingInputView.js +1 -1
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +67 -6
- package/extension/src/content/providerSettingsCoordinator.js +46 -8
- package/extension/src/content/providerSettingsDialog.js +73 -14
- package/extension/src/content/recentProjects.js +15 -8
- package/extension/src/content/runActivitySummary.js +160 -18
- package/extension/src/content/runCompletionMeta.js +532 -0
- package/extension/src/content/runController.js +39 -15
- package/extension/src/content/runFailureNotice.js +29 -13
- package/extension/src/content/runGuidanceController.js +72 -27
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +18 -8
- package/extension/src/content/runQueueScheduler.js +157 -47
- package/extension/src/content/runRecoveryActions.js +147 -0
- package/extension/src/content/runResultActions.js +28 -143
- package/extension/src/content/runReviewController.js +633 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +367 -175
- package/extension/src/content/scopedPersistencePanelState.js +134 -20
- package/extension/src/content/selectionContextView.js +1 -1
- package/extension/src/content/sensitivePreflight.js +153 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +50 -4
- package/extension/src/content/sessionMenuView.js +54 -18
- package/extension/src/content/sessionPersistence.js +25 -5
- package/extension/src/content/settingsPanel.js +22 -5
- package/extension/src/content/settingsWorkbench.js +132 -89
- package/extension/src/content/sharedSessionViewSync.js +49 -11
- package/extension/src/content/subagentActivityView.js +23 -7
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +118 -12
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +53 -4
- package/extension/src/content/writebackOrchestrator.js +116 -94
- package/extension/src/content/writingStyleSettings.js +35 -11
- package/extension/src/page/binaryAssetUploader.js +136 -14
- package/extension/src/page/compileBridge.js +193 -180
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafCapabilities.js +4 -1
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +1 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +6 -4
- package/extension/src/page/snapshotRouter.js +84 -39
- package/extension/src/page/textCoordinates.js +17 -1
- package/extension/src/page/textFileCreator.js +114 -26
- package/extension/src/page/trackedChangeCapture.js +12 -2
- package/extension/src/page/trackedChangeOwnership.js +19 -1
- package/extension/src/page/trackedChangeReplay.js +41 -21
- package/extension/src/page/trackedChangesLifecycle.js +217 -199
- package/extension/src/page/treeOperations.js +7 -10
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +2 -0
- package/extension/src/page/writebackRouter.js +103 -155
- package/extension/src/pageBridge.js +126 -69
- package/extension/src/popup.js +62 -10
- package/extension/src/shared/agentTranscript.js +373 -17
- package/extension/src/shared/auditRecords.js +4 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +129 -66
- package/extension/src/shared/composerAttachmentDraft.js +156 -0
- package/extension/src/shared/contextUsage.js +125 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +349 -4
- package/extension/src/shared/globalPreferences.js +30 -13
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +590 -24
- package/extension/src/shared/lineReferences.js +38 -31
- package/extension/src/shared/managedUpdateProjection.js +2 -2
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +5 -4
- package/extension/src/shared/panelIcons.js +39 -0
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +5 -3
- package/extension/src/shared/reviewing.js +130 -1
- package/extension/src/shared/runActivityModel.js +70 -4
- package/extension/src/shared/runExecutionSnapshot.js +13 -0
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/selectionContext.js +12 -3
- package/extension/src/shared/sessionState.js +132 -28
- package/extension/src/shared/sessionTitle.js +14 -1
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +118 -27
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +166 -52
- package/extension/src/shared/storageDb.js +90 -26
- package/extension/src/shared/storageMigration.js +78 -112
- package/extension/src/shared/storageRunActions.js +267 -30
- package/extension/src/shared/subagentActivityModel.js +1 -1
- package/extension/src/shared/undoOperations.js +253 -32
- package/extension/src/shared/updateConsent.js +6 -4
- package/extension/src/shared/updateRuntimeIdentity.js +17 -3
- package/extension/src/shared/writebackIntent.js +16 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +58 -40
- package/extension/styles/panel.css +1123 -493
- package/native-host/src/anthropicBridgeRequest.js +4 -2
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +269 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeRequest.js +60 -6
- package/native-host/src/chatBridgeResponse.js +20 -1
- package/native-host/src/chatCompletionsBridge.js +48 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexPromptAssembly.js +114 -127
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexSessionRunner.js +234 -198
- package/native-host/src/codexSessionTiming.js +18 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +110 -35
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/processStopBarrier.js +42 -7
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerContextError.js +20 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerProfile.js +4 -2
- package/native-host/src/providerReasoning.js +2 -7
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +72 -14
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +36 -8
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +79 -66
- package/native-host/src/subagentTelemetry.js +19 -10
- package/native-host/src/subagentWorkspacePath.js +5 -2
- package/native-host/src/taskRunnerRuntime.js +92 -57
- package/native-host/src/textPatch.js +473 -377
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/updateManager.js +409 -46
- package/native-host/src/writingStyleRuntime.js +57 -29
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +5 -8
- package/scripts/install-managed.mjs +1 -0
|
@@ -12,27 +12,46 @@
|
|
|
12
12
|
const tx = options.tx;
|
|
13
13
|
const tr = options.tr;
|
|
14
14
|
|
|
15
|
+
let installIdentity = options.getExtensionCompatibilityMetadata?.()?.installIdentity || {};
|
|
16
|
+
function resolveInstallCommand(compatibility = {}) {
|
|
17
|
+
const metadata = options.getExtensionCompatibilityMetadata?.() || {};
|
|
18
|
+
installIdentity = { ...installIdentity, ...(metadata.installIdentity || {}), ...(compatibility.installIdentity || {}) };
|
|
19
|
+
if (installIdentity.managed) return Compatibility?.buildInstallCommand?.(
|
|
20
|
+
compatibility.recommendedVersion || Compatibility?.BUILD_TARGET_VERSION,
|
|
21
|
+
compatibility.native?.platform || compatibility.platform || installIdentity.platform,
|
|
22
|
+
metadata.extensionId, installIdentity
|
|
23
|
+
) || '';
|
|
24
|
+
return compatibility.updateCommand || compatibility.installCommand || options.installCommand || '';
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
function sendNative(payload) {
|
|
16
28
|
return dispatch(payload, 'sendNative');
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
function sendBackgroundNative(payload) {
|
|
20
|
-
return dispatch(payload, 'sendBackgroundNative');
|
|
31
|
+
function sendBackgroundNative(payload, context = {}) {
|
|
32
|
+
return dispatch(payload, 'sendBackgroundNative', context);
|
|
21
33
|
}
|
|
22
34
|
|
|
23
|
-
async function dispatch(payload, channelMethod) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
async function dispatch(payload, channelMethod, context = {}) {
|
|
36
|
+
let compatibilityGate;
|
|
37
|
+
try {
|
|
38
|
+
compatibilityGate = await ensureForMethod(payload?.method, context);
|
|
39
|
+
if (!compatibilityGate.ok) return compatibilityGate.response;
|
|
40
|
+
// Post-Undo cache bookkeeping must survive the ended run's cancellation.
|
|
41
|
+
if (gatedMethods.has(payload?.method) && payload.method !== 'mirror.invalidate') {
|
|
42
|
+
options.throwIfCancellationRequested?.();
|
|
43
|
+
}
|
|
44
|
+
context.assertCurrent?.();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (context.assertCurrent && error && typeof error === 'object') error.requestDispatched = false;
|
|
47
|
+
throw error;
|
|
29
48
|
}
|
|
30
49
|
return nativeChannel[channelMethod](
|
|
31
50
|
attachEvidence(payload, compatibilityGate.compatibility)
|
|
32
51
|
);
|
|
33
52
|
}
|
|
34
53
|
|
|
35
|
-
async function ensureForMethod(method) {
|
|
54
|
+
async function ensureForMethod(method, { assertCurrent } = {}) {
|
|
36
55
|
const metadata = options.getExtensionCompatibilityMetadata?.() || {};
|
|
37
56
|
const params = Compatibility?.buildBridgePingParams
|
|
38
57
|
? Compatibility.buildBridgePingParams(metadata)
|
|
@@ -41,9 +60,12 @@
|
|
|
41
60
|
method: 'bridge.ping',
|
|
42
61
|
params
|
|
43
62
|
});
|
|
63
|
+
// A retired run must not publish even a compatibility failure into a successor.
|
|
64
|
+
assertCurrent?.();
|
|
44
65
|
const compatibility = Compatibility?.evaluateNativeCompatibility
|
|
45
66
|
? Compatibility.evaluateNativeCompatibility(response, metadata)
|
|
46
67
|
: options.fallbackNativeCompatibility(response);
|
|
68
|
+
const installCommand = resolveInstallCommand(compatibility);
|
|
47
69
|
const allowed = !gatedMethods.has(method) ||
|
|
48
70
|
(Compatibility?.isNativeMethodAllowed
|
|
49
71
|
? Compatibility.isNativeMethodAllowed(method, compatibility)
|
|
@@ -51,14 +73,14 @@
|
|
|
51
73
|
if (allowed) return { ok: true, compatibility };
|
|
52
74
|
|
|
53
75
|
const message = formatBlockedMessage(method, compatibility);
|
|
54
|
-
const installCommand = options.installCommand;
|
|
55
76
|
const error = {
|
|
56
77
|
code: 'native_update_required',
|
|
57
78
|
message,
|
|
58
79
|
status: compatibility.status || 'unknown_native',
|
|
59
80
|
classification: options.getNativeCompatibilityClassification(compatibility),
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
installIdentity,
|
|
82
|
+
installCommand,
|
|
83
|
+
updateCommand: installCommand,
|
|
62
84
|
currentNativeVersion: compatibility.currentNativeVersion
|
|
63
85
|
|| compatibility.nativeVersion
|
|
64
86
|
|| compatibility.native?.version
|
|
@@ -66,7 +88,7 @@
|
|
|
66
88
|
requiredVersion: compatibility.requiredVersion || Compatibility?.BUILD_TARGET_VERSION || '',
|
|
67
89
|
releaseUrl: compatibility.releaseUrl || ''
|
|
68
90
|
};
|
|
69
|
-
notifyBlocked(error, compatibility);
|
|
91
|
+
notifyBlocked(error, compatibility, method);
|
|
70
92
|
return { ok: false, compatibility, response: { ok: false, error } };
|
|
71
93
|
}
|
|
72
94
|
|
|
@@ -81,7 +103,7 @@
|
|
|
81
103
|
};
|
|
82
104
|
}
|
|
83
105
|
|
|
84
|
-
function notifyBlocked(error, compatibility = {}) {
|
|
106
|
+
function notifyBlocked(error, compatibility = {}, method) {
|
|
85
107
|
const message = error?.message || String(error || '');
|
|
86
108
|
if (options.getCurrentRunView?.()) {
|
|
87
109
|
options.appendRunEvent?.({ title: message, status: 'failed' });
|
|
@@ -89,7 +111,17 @@
|
|
|
89
111
|
return;
|
|
90
112
|
}
|
|
91
113
|
showUpdateGuidance(compatibility);
|
|
92
|
-
|
|
114
|
+
const missing = compatibility.status === 'native_missing';
|
|
115
|
+
options.showPluginToast?.(missing ? tr('nativeCompatibilityMissingGuidance') : message, {
|
|
116
|
+
status: 'failed',
|
|
117
|
+
sticky: true,
|
|
118
|
+
...(missing ? {
|
|
119
|
+
title: tr('nativeCompatibilityMissingTitle'),
|
|
120
|
+
groupKey: 'native-missing',
|
|
121
|
+
groupItem: method || 'native request',
|
|
122
|
+
groupSummary: count => tr('nativeCompatibilityUnavailableFeatures', { count })
|
|
123
|
+
} : {})
|
|
124
|
+
});
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
async function refreshBadge() {
|
|
@@ -102,6 +134,7 @@
|
|
|
102
134
|
: {};
|
|
103
135
|
const response = await sendBackgroundNative({ method: 'bridge.ping', params });
|
|
104
136
|
const compatibility = Compatibility.evaluateNativeCompatibility(response, metadata);
|
|
137
|
+
resolveInstallCommand(compatibility);
|
|
105
138
|
const classification = options.getNativeCompatibilityClassification(compatibility);
|
|
106
139
|
if (classification === 'compatible') {
|
|
107
140
|
options.setBadge?.(renderer.headerEl, { type: 'none' });
|
|
@@ -158,9 +191,7 @@
|
|
|
158
191
|
|| compatibility.nativeVersion
|
|
159
192
|
|| native.version
|
|
160
193
|
|| tx('missing', '未安装');
|
|
161
|
-
const command = compatibility
|
|
162
|
-
|| compatibility.installCommand
|
|
163
|
-
|| options.installCommand;
|
|
194
|
+
const command = resolveInstallCommand(compatibility);
|
|
164
195
|
const releaseUrl = compatibility.releaseUrl || '';
|
|
165
196
|
const overlay = options.document.createElement('div');
|
|
166
197
|
overlay.className = 'codex-plugin-confirm';
|
|
@@ -176,11 +207,24 @@
|
|
|
176
207
|
);
|
|
177
208
|
const card = options.document.createElement('section');
|
|
178
209
|
card.className = 'codex-plugin-confirm-card';
|
|
210
|
+
const head = options.document.createElement('div');
|
|
211
|
+
head.className = 'codex-plugin-confirm-head';
|
|
212
|
+
const icon = options.document.createElement('img');
|
|
213
|
+
icon.className = 'codex-plugin-confirm-icon';
|
|
214
|
+
icon.alt = '';
|
|
215
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
216
|
+
icon.src = options.getIconUrl?.() || '';
|
|
217
|
+
const titleWrap = options.document.createElement('div');
|
|
218
|
+
const brand = options.document.createElement('div');
|
|
219
|
+
brand.className = 'codex-plugin-confirm-brand';
|
|
220
|
+
brand.textContent = 'Codex Overleaf Link';
|
|
179
221
|
const title = options.document.createElement('div');
|
|
180
222
|
title.className = 'codex-plugin-confirm-title';
|
|
181
223
|
title.textContent = nativeMissing
|
|
182
224
|
? tx('One step left: install the native host', '还差一步:安装 Native host')
|
|
183
225
|
: tx('Native host update available', 'Native host 可更新');
|
|
226
|
+
titleWrap.append(brand, title);
|
|
227
|
+
head.append(icon, titleWrap);
|
|
184
228
|
const body = options.document.createElement('div');
|
|
185
229
|
body.className = 'codex-plugin-confirm-body';
|
|
186
230
|
body.textContent = [
|
|
@@ -193,26 +237,24 @@
|
|
|
193
237
|
`Extension v${extensionVersion} / Native v${nativeVersion}`,
|
|
194
238
|
`扩展 v${extensionVersion} / Native v${nativeVersion}`
|
|
195
239
|
),
|
|
196
|
-
|
|
197
|
-
'
|
|
198
|
-
|
|
199
|
-
|
|
240
|
+
!command && installIdentity.managed
|
|
241
|
+
? tx('Repair Managed using the original managed root, browser and extension ID with install-managed.',
|
|
242
|
+
'请使用原管理根目录、浏览器及扩展 ID,通过 install-managed 修复托管安装。')
|
|
243
|
+
: tx('Run the command below, reload the extension, then refresh Overleaf.', '运行下面的命令,重新加载扩展,然后刷新 Overleaf。')
|
|
244
|
+
+ (installIdentity.managed && (native.platform || installIdentity.platform) === 'win32'
|
|
245
|
+
? tx(' Use PowerShell.', ' 请使用 PowerShell。') : '')
|
|
200
246
|
].join('\n\n');
|
|
201
247
|
const commandCode = options.document.createElement('code');
|
|
248
|
+
commandCode.className = 'codex-native-guidance-command';
|
|
202
249
|
commandCode.textContent = command;
|
|
203
|
-
Object.assign(commandCode.style, {
|
|
204
|
-
display: 'block',
|
|
205
|
-
whiteSpace: 'pre-wrap',
|
|
206
|
-
marginTop: '0.75rem'
|
|
207
|
-
});
|
|
208
250
|
body.append(commandCode);
|
|
209
251
|
if (releaseUrl) {
|
|
210
252
|
const link = options.document.createElement('a');
|
|
253
|
+
link.className = 'codex-native-guidance-link';
|
|
211
254
|
link.href = releaseUrl;
|
|
212
255
|
link.target = '_blank';
|
|
213
256
|
link.rel = 'noopener noreferrer';
|
|
214
257
|
link.textContent = tx('Open GitHub Release', '打开 GitHub Release');
|
|
215
|
-
Object.assign(link.style, { display: 'block', marginTop: '0.75rem' });
|
|
216
258
|
body.append(link);
|
|
217
259
|
}
|
|
218
260
|
const actions = options.document.createElement('div');
|
|
@@ -221,7 +263,9 @@
|
|
|
221
263
|
copy.type = 'button';
|
|
222
264
|
copy.className = 'codex-plugin-confirm-confirm';
|
|
223
265
|
copy.textContent = tx('Copy command', '复制命令');
|
|
266
|
+
copy.disabled = !command;
|
|
224
267
|
copy.addEventListener('click', async () => {
|
|
268
|
+
if (!command) return;
|
|
225
269
|
await options.navigator.clipboard.writeText(command);
|
|
226
270
|
copy.textContent = tx('Copied', '已复制');
|
|
227
271
|
});
|
|
@@ -231,13 +275,13 @@
|
|
|
231
275
|
close.textContent = tx('Close', '关闭');
|
|
232
276
|
close.addEventListener('click', () => overlay.remove());
|
|
233
277
|
actions.append(close, copy);
|
|
234
|
-
card.append(
|
|
278
|
+
card.append(head, body, actions);
|
|
235
279
|
overlay.append(card);
|
|
236
280
|
overlay.addEventListener('click', event => {
|
|
237
281
|
if (event.target === overlay) overlay.remove();
|
|
238
282
|
});
|
|
239
283
|
panel.append(overlay);
|
|
240
|
-
copy.focus();
|
|
284
|
+
(command ? copy : close).focus();
|
|
241
285
|
}
|
|
242
286
|
|
|
243
287
|
function scheduleFirstUseOnboardingTip() {
|
|
@@ -833,7 +833,7 @@
|
|
|
833
833
|
if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: 'project_changed' };
|
|
834
834
|
const otWarmStart = otWarmMirrorController.canUseOtWarmStart({
|
|
835
835
|
enabled: isExperimentalOtEnabled() && !otWarmMirrorState.failureActive,
|
|
836
|
-
focusFiles,
|
|
836
|
+
focusFiles, focusFilesAreLiteral: true,
|
|
837
837
|
mirrorStatus
|
|
838
838
|
});
|
|
839
839
|
if (!mirrorStatus?.exists || (!otWarmStart.ok && !isMirrorReusable(mirrorStatus))) {
|
|
@@ -858,10 +858,9 @@
|
|
|
858
858
|
return { useExistingMirror: false, reason: 'overlay_probe_failed', mirrorStatus };
|
|
859
859
|
}
|
|
860
860
|
if (getCurrentProjectId() !== projectId) return { useExistingMirror: false, reason: 'project_changed' };
|
|
861
|
-
const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles)
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}
|
|
861
|
+
const fileOverlays = buildSnapshotFileOverlays(overlayProject, focusFiles).filter(file =>
|
|
862
|
+
!otWarmStart.ok || focusFiles.some(path => normalizeSnapshotPath(path) === normalizeSnapshotPath(file.path)));
|
|
863
|
+
if (!fileOverlays.length) return { useExistingMirror: false, reason: 'missing_current_overlay', mirrorStatus };
|
|
865
864
|
if (focusFiles.length && !focusFiles.every(path => {
|
|
866
865
|
const normalized = normalizeSnapshotPath(path);
|
|
867
866
|
return fileOverlays.some(file => normalizeSnapshotPath(file.path) === normalized);
|
|
@@ -896,7 +895,7 @@
|
|
|
896
895
|
appendLog(tx(`Cannot continue: ${formatProjectSnapshotWarning(warning)}`, `无法继续:${formatProjectSnapshotWarning(warning)}`));
|
|
897
896
|
}
|
|
898
897
|
appendCompletionReport({
|
|
899
|
-
conclusion: tr('warmMirrorStaleBlockedConclusion'),
|
|
898
|
+
conclusion: tr('warmMirrorStaleBlockedConclusion'), conclusionKey: 'warm_mirror_stale',
|
|
900
899
|
status: 'blocked',
|
|
901
900
|
operations: [],
|
|
902
901
|
applyResults: [],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
(function initCodexOverleafOtWarmMirrorController(root, factory) {
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
|
-
module.exports = factory();
|
|
3
|
+
module.exports = factory(require('../shared/projectFiles'));
|
|
4
4
|
} else {
|
|
5
|
-
root.
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('OtWarmMirrorController', ['ProjectFiles'], factory);
|
|
6
6
|
}
|
|
7
|
-
})(typeof window !== 'undefined' ? window : globalThis, function otWarmMirrorControllerFactory() {
|
|
7
|
+
})(typeof window !== 'undefined' ? window : globalThis, function otWarmMirrorControllerFactory(projectFiles) {
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
10
|
const OT_POLL_INTERVAL_MS = 1000;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const OT_MAX_BATCH_BYTES = 2 * 1024 * 1024;
|
|
18
18
|
const encoder = typeof TextEncoder === 'function' ? new TextEncoder() : null;
|
|
19
19
|
const PAUSE_STATES = new Set(['running', 'writing', 'undoing', 'compiling']);
|
|
20
|
+
const normalizeLiteralPath = projectFiles.normalizeSafeProjectPath;
|
|
20
21
|
|
|
21
22
|
function buildPatchFilesRequest({ projectId, events, nativeCompatibility } = {}) {
|
|
22
23
|
const files = [];
|
|
@@ -24,8 +25,8 @@
|
|
|
24
25
|
if (files.length >= OT_MAX_PATCH_BATCH) {
|
|
25
26
|
break;
|
|
26
27
|
}
|
|
27
|
-
const filePath =
|
|
28
|
-
if (!
|
|
28
|
+
const filePath = normalizeLiteralPath(event?.path);
|
|
29
|
+
if (!filePath || typeof event?.baseHash !== 'string') {
|
|
29
30
|
continue;
|
|
30
31
|
}
|
|
31
32
|
const baseHash = event.baseHash.trim();
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
return { pause: false };
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
function canUseOtWarmStart({ enabled, focusFiles, mirrorStatus, now = Date.now() } = {}) {
|
|
77
|
+
function canUseOtWarmStart({ enabled, focusFiles, focusFilesAreLiteral = false, mirrorStatus, now = Date.now() } = {}) {
|
|
77
78
|
if (!enabled) {
|
|
78
79
|
return { ok: false, reason: 'disabled' };
|
|
79
80
|
}
|
|
@@ -82,7 +83,10 @@
|
|
|
82
83
|
}
|
|
83
84
|
if (mirrorStatus.dirty === true) return { ok: false, reason: 'mirror_dirty' };
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
if (focusFilesAreLiteral === true && Array.isArray(focusFiles) && focusFiles.some(path => !normalizeLiteralPath(path))) {
|
|
87
|
+
return { ok: false, reason: 'invalid_focus_paths' };
|
|
88
|
+
}
|
|
89
|
+
const normalizedFocusFiles = normalizePaths(focusFiles, focusFilesAreLiteral === true);
|
|
86
90
|
if (!normalizedFocusFiles.length) {
|
|
87
91
|
return { ok: false, reason: 'no_focus_files' };
|
|
88
92
|
}
|
|
@@ -94,7 +98,7 @@
|
|
|
94
98
|
}
|
|
95
99
|
const age = now - Date.parse(file.observedAt ?? file.lastPatchAt ?? '');
|
|
96
100
|
if (!Number.isFinite(age) || age < 0 || age > OT_FRESHNESS_MAX_AGE_MS) continue;
|
|
97
|
-
const filePath =
|
|
101
|
+
const filePath = normalizeLiteralPath(file.path);
|
|
98
102
|
if (filePath) {
|
|
99
103
|
freshFiles.add(filePath);
|
|
100
104
|
}
|
|
@@ -124,9 +128,9 @@
|
|
|
124
128
|
let bytes = next.reduce((sum, event) => sum + patchBytes(event), 0);
|
|
125
129
|
if (next.length > OT_MAX_PENDING_EVENTS || bytes > OT_MAX_PENDING_BYTES) return { ok: false, reason: 'ot_queue_limit' };
|
|
126
130
|
for (const input of events) {
|
|
127
|
-
const path =
|
|
131
|
+
const path = normalizeLiteralPath(input?.path);
|
|
128
132
|
const time = typeof input?.observedAt === 'number' ? input.observedAt : Date.parse(input?.observedAt || '');
|
|
129
|
-
if (!
|
|
133
|
+
if (!path || path.length > 1024 || typeof input?.nextContent !== 'string' ||
|
|
130
134
|
typeof input.baseHash !== 'string' || !input.baseHash || input.baseHash.length > 128) {
|
|
131
135
|
return { ok: false, reason: 'ot_event_invalid' };
|
|
132
136
|
}
|
|
@@ -165,7 +169,7 @@
|
|
|
165
169
|
}
|
|
166
170
|
if (!events.length || Number(result.appliedCount) !== events.length || !Array.isArray(result.appliedFiles) ||
|
|
167
171
|
result.appliedFiles.length !== events.length || !result.appliedFiles.every((file, index) =>
|
|
168
|
-
|
|
172
|
+
normalizeLiteralPath(file?.path) === events[index].path && (!events[index].nextHash || file.hash === events[index].nextHash))) {
|
|
169
173
|
return { ok: false, reason: 'mirror_patch_invalid_result' };
|
|
170
174
|
}
|
|
171
175
|
if (events.some(event => { const time = Date.parse(event.observedAt);
|
|
@@ -220,11 +224,11 @@
|
|
|
220
224
|
.replace(/^\/+/, '');
|
|
221
225
|
}
|
|
222
226
|
|
|
223
|
-
function normalizePaths(value) {
|
|
227
|
+
function normalizePaths(value, literal = false) {
|
|
224
228
|
const seen = new Set();
|
|
225
229
|
const paths = [];
|
|
226
230
|
for (const item of Array.isArray(value) ? value : []) {
|
|
227
|
-
const filePath = normalizePath(item);
|
|
231
|
+
const filePath = literal ? normalizeLiteralPath(item) : normalizePath(item);
|
|
228
232
|
if (!filePath || seen.has(filePath)) {
|
|
229
233
|
continue;
|
|
230
234
|
}
|
|
@@ -234,13 +238,6 @@
|
|
|
234
238
|
return paths;
|
|
235
239
|
}
|
|
236
240
|
|
|
237
|
-
function isSafeRelativePath(filePath) {
|
|
238
|
-
if (!filePath) {
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
return !filePath.split('/').some(part => part === '.' || part === '..');
|
|
242
|
-
}
|
|
243
|
-
|
|
244
241
|
function isPrimitiveMetadata(value) {
|
|
245
242
|
return ['string', 'number', 'boolean'].includes(typeof value);
|
|
246
243
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const PAGE_WORLD_SCRIPTS = Object.freeze([
|
|
9
|
-
['src/shared/reviewing.js', 'codex-overleaf-reviewing-script'],
|
|
9
|
+
['src/shared/reviewing.js', 'codex-overleaf-reviewing-script', true],
|
|
10
10
|
['src/shared/projectFiles.js', 'codex-overleaf-project-files-script'],
|
|
11
11
|
['src/shared/compatibility.js', 'codex-overleaf-compatibility-page-script'],
|
|
12
12
|
['src/shared/pageRpcContract.js', 'codex-overleaf-page-rpc-contract-script', true],
|
|
@@ -15,20 +15,23 @@
|
|
|
15
15
|
['src/shared/sensitiveScan.js', 'codex-overleaf-sensitive-scan-script'],
|
|
16
16
|
['src/shared/auditRecords.js', 'codex-overleaf-audit-records-script'],
|
|
17
17
|
['src/page/saveState.js', 'codex-overleaf-save-state-script', true],
|
|
18
|
-
['src/page/overleafCapabilities.js', 'codex-overleaf-capabilities-script'],
|
|
18
|
+
['src/page/overleafCapabilities.js', 'codex-overleaf-capabilities-script', true],
|
|
19
19
|
['src/page/compileBridge.js', 'codex-overleaf-compile-bridge-script'],
|
|
20
20
|
['src/page/overleafEditor.js', 'codex-overleaf-editor-script'],
|
|
21
21
|
['src/page/overleafProjectSnapshot.js', 'codex-overleaf-project-snapshot-script'],
|
|
22
22
|
['src/page/pageBridgeCapability.js', 'codex-overleaf-page-bridge-capability-script'],
|
|
23
23
|
['src/page/treeOperations.js', 'codex-overleaf-tree-operations-script', true],
|
|
24
|
+
['src/page/ownedDirectoryCleanup.js', 'codex-overleaf-owned-directory-cleanup-script', true],
|
|
24
25
|
['src/page/textFileCreator.js', 'codex-overleaf-text-file-creator-script', true],
|
|
25
26
|
['src/page/binaryAssetUploader.js', 'codex-overleaf-binary-asset-uploader-script', true],
|
|
26
27
|
['src/page/snapshotRouter.js', 'codex-overleaf-snapshot-router-script'],
|
|
27
28
|
['src/page/referenceProjects.js', 'codex-overleaf-reference-projects-script', true],
|
|
29
|
+
['src/page/fileMover.js', 'codex-overleaf-file-mover-script', true],
|
|
28
30
|
['src/page/writeGuard.js', 'codex-overleaf-write-guard-script', true],
|
|
29
31
|
['src/page/trackedChangeOwnership.js', 'codex-overleaf-tracked-change-ownership-script', true],
|
|
30
32
|
['src/page/trackedChangeCapture.js', 'codex-overleaf-tracked-change-capture-script', true],
|
|
31
33
|
['src/page/trackedChangeReplay.js', 'codex-overleaf-tracked-change-replay-script', true],
|
|
34
|
+
['src/page/reviewContext.js', 'codex-overleaf-review-context-script', true],
|
|
32
35
|
['src/page/trackedChangesLifecycle.js', 'codex-overleaf-tracked-changes-lifecycle-script', true],
|
|
33
36
|
['src/page/writebackRouter.js', 'codex-overleaf-writeback-router-script', true],
|
|
34
37
|
['src/page/writebackReceiptJournal.js', 'codex-overleaf-writeback-receipt-script', true],
|
|
@@ -75,6 +78,11 @@
|
|
|
75
78
|
const requestTimeoutMs = Number.isFinite(Number(policy.timeoutMs))
|
|
76
79
|
? Number(policy.timeoutMs)
|
|
77
80
|
: Contract.resolveTimeoutMs(method, timeoutOverrides);
|
|
81
|
+
// Review preparation and its nested writes share the transport budget.
|
|
82
|
+
if (Contract.getMethod(method)?.timeoutClass === 'lifecycle') {
|
|
83
|
+
params = { ...params, deadlineAt: params?.deadlineAt == null ? now() + requestTimeoutMs
|
|
84
|
+
: Number.isFinite(params.deadlineAt) ? Math.min(params.deadlineAt, now() + requestTimeoutMs) : params.deadlineAt };
|
|
85
|
+
}
|
|
78
86
|
const remainingMs = params?.deadlineAt == null ? Infinity
|
|
79
87
|
: Number.isFinite(params.deadlineAt) ? params.deadlineAt - now() : 0;
|
|
80
88
|
if (remainingMs <= 0) {
|
|
@@ -84,7 +92,7 @@
|
|
|
84
92
|
}
|
|
85
93
|
const timeoutMs = Math.min(requestTimeoutMs, remainingMs);
|
|
86
94
|
const cancellable = policy.cancellation === 'content_abort';
|
|
87
|
-
let settled = false;
|
|
95
|
+
let settled = false, dispatched = false;
|
|
88
96
|
let timeout = null;
|
|
89
97
|
function cleanup() {
|
|
90
98
|
if (timeout !== null) windowRef.clearTimeout(timeout);
|
|
@@ -107,8 +115,13 @@
|
|
|
107
115
|
const error = new Error('Codex run was cancelled by the user');
|
|
108
116
|
error.code = 'codex_cancelled';
|
|
109
117
|
error.cancelled = true;
|
|
118
|
+
error.requestDispatched = dispatched;
|
|
119
|
+
error.changedDocument = dispatched && Contract.getMethod(method)?.mutation === 'document';
|
|
120
|
+
if (dispatched && method === 'applyOperations') { error.requestId = id; error.rpcMethod = method; }
|
|
110
121
|
rejectOnce(error);
|
|
111
122
|
}
|
|
123
|
+
// Cancellation stops new requests; receipt settlement has its own non-cancellable policy.
|
|
124
|
+
if (cancellable && options.isCancellationRequested?.()) { cancelRequest(); return; }
|
|
112
125
|
timeout = windowRef.setTimeout(() => {
|
|
113
126
|
resolveOnce({ ok: false, error: 'Page bridge timed out',
|
|
114
127
|
...(method === 'applyOperations' ? { code: 'page_bridge_timeout', rpcMethod: method, requestId: id } : {}) });
|
|
@@ -126,11 +139,11 @@
|
|
|
126
139
|
}
|
|
127
140
|
if (cancellable) {
|
|
128
141
|
activeCancellationHandlers.set(id, cancelRequest);
|
|
129
|
-
if (options.isCancellationRequested?.()) windowRef.queueMicrotask(cancelRequest);
|
|
130
142
|
}
|
|
131
143
|
windowRef.addEventListener('message', onMessage);
|
|
132
144
|
// Every low-level request remains one attempt. Higher layers must opt
|
|
133
145
|
// into replay so a document mutation is never duplicated implicitly.
|
|
146
|
+
dispatched = true;
|
|
134
147
|
windowRef.postMessage({
|
|
135
148
|
source: 'codex-overleaf/content',
|
|
136
149
|
id,
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
openCustomInstructionsSettings,
|
|
25
25
|
onHistoryRowJump,
|
|
26
26
|
resetProjectNameCache,
|
|
27
|
+
clearHistoryComposer,
|
|
27
28
|
getPanel,
|
|
28
29
|
getState,
|
|
29
30
|
setState,
|
|
@@ -110,27 +111,54 @@
|
|
|
110
111
|
if (!confirmed) {
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
114
|
+
if (getCurrentRunView()) {
|
|
115
|
+
showPluginToast(tx('A run started while confirmation was open. Finish or cancel it before clearing history.', '确认期间有任务开始运行,请先完成或取消任务后再清理历史。'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let historyClearedAt;
|
|
113
119
|
try {
|
|
114
|
-
await StorageDb
|
|
120
|
+
await StorageDb.clearAllStores();
|
|
121
|
+
historyClearedAt = Date.now();
|
|
115
122
|
historyView.invalidate();
|
|
116
|
-
// v1.8.1: the dashboard's project-name cache lives in
|
|
117
|
-
// chrome.storage.local, outside the IndexedDB stores — clear it too so
|
|
118
|
-
// no (potentially sensitive) project titles outlive the wipe.
|
|
119
|
-
await resetProjectNameCache?.();
|
|
120
123
|
} catch (error) {
|
|
121
124
|
showPluginToast(tx(`Could not clear history: ${error.message}`, `清空历史失败:${error.message}`));
|
|
122
125
|
return;
|
|
123
126
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
const cleanupErrors = [];
|
|
128
|
+
try {
|
|
129
|
+
// Project names live outside the history store. Failure here must not
|
|
130
|
+
// leave deleted history in memory for a later autosave to replay.
|
|
131
|
+
await resetProjectNameCache?.();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
cleanupErrors.push(error.message);
|
|
134
|
+
}
|
|
135
|
+
// Clear the compatibility mirror as well as the session list; otherwise
|
|
136
|
+
// normalization can reconstruct old summary runs and the Native thread.
|
|
137
|
+
const clearedState = normalizePanelState({
|
|
138
|
+
...getState(), session: null, sessions: [], runs: [], activeSessionId: '',
|
|
139
|
+
task: '', focusFiles: [], selectionContext: null, composerAttachments: []
|
|
140
|
+
});
|
|
141
|
+
const freshSession = clearedState.sessions[0];
|
|
142
|
+
// The shared store rejects createdAt <= its clear marker. A new session
|
|
143
|
+
// in the same millisecond needs the next tick, at most 1 ms ahead of now.
|
|
144
|
+
freshSession.createdAt = freshSession.updatedAt = new Date(Math.max(Date.now(), historyClearedAt + 1)).toISOString();
|
|
145
|
+
setState(clearedState);
|
|
146
|
+
try {
|
|
147
|
+
clearHistoryComposer?.();
|
|
148
|
+
if (await saveState({ requireDurable: true }) !== true) {
|
|
149
|
+
throw new Error(tx('The blank session save was not confirmed.', '空白会话的保存结果尚未确认。'));
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
cleanupErrors.push(error.message);
|
|
153
|
+
}
|
|
154
|
+
// The store has already been cleared, including when the fresh-session
|
|
155
|
+
// save fails. Always remove old cards and draft inputs from the panel.
|
|
130
156
|
applyStateToPanel();
|
|
131
157
|
refreshStorageUsageSummary();
|
|
132
158
|
void renderAuditHistoryPanel();
|
|
133
|
-
showPluginToast(
|
|
159
|
+
showPluginToast(cleanupErrors.length
|
|
160
|
+
? tx(`Saved history was removed, but cleanup or blank-session saving needs attention: ${cleanupErrors.join('; ')}`, `已删除保存的历史,但清理或空白会话保存仍有问题:${cleanupErrors.join(';')}`)
|
|
161
|
+
: tx('Local history cleared. Overleaf files are unchanged.', '本地历史已清理,Overleaf 文件保持不变。'));
|
|
134
162
|
getPanel()?.querySelector('[data-task]')?.focus();
|
|
135
163
|
}
|
|
136
164
|
|
|
@@ -153,6 +181,7 @@
|
|
|
153
181
|
// the failed task usually needs a tweak before it is worth resending.
|
|
154
182
|
function refillComposerForRetry(run, textOverride) {
|
|
155
183
|
const input = getPanel()?.querySelector('[data-task]');
|
|
184
|
+
if (input?.readOnly) return;
|
|
156
185
|
const task = typeof textOverride === 'string' && textOverride.trim()
|
|
157
186
|
? textOverride
|
|
158
187
|
: String(run?.task || '');
|
|
@@ -282,7 +311,9 @@
|
|
|
282
311
|
// Settings history card, expanded and loaded.
|
|
283
312
|
function openChangeHistory() {
|
|
284
313
|
openCustomInstructionsSettings();
|
|
285
|
-
|
|
314
|
+
// The workbench shows one page at a time; switch to the page holding the card.
|
|
315
|
+
const card = getSettingsPanelInstance()?.revealGroup?.('history')
|
|
316
|
+
|| getSettingsPanelInstance()?.container?.querySelector('[data-history-card]');
|
|
286
317
|
if (card) {
|
|
287
318
|
if (!card.open) {
|
|
288
319
|
card.open = true;
|
|
@@ -294,7 +325,8 @@
|
|
|
294
325
|
|
|
295
326
|
function openStorageSettings() {
|
|
296
327
|
openCustomInstructionsSettings();
|
|
297
|
-
const card =
|
|
328
|
+
const card = getSettingsPanelInstance()?.revealGroup?.('storage')
|
|
329
|
+
|| getPanel()?.querySelector('[data-storage-card]');
|
|
298
330
|
if (card) {
|
|
299
331
|
card.open = true;
|
|
300
332
|
card.scrollIntoView({ block: 'center' });
|