codex-overleaf-link 2.3.5 → 2.3.6
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 +31 -24
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +4 -0
- package/extension/src/backgroundUpdateCoordinator.js +88 -4
- package/extension/src/content/contentRuntime.js +24 -7
- package/extension/src/content/diagnosticsController.js +13 -3
- package/extension/src/content/diagnosticsPanel.js +1 -1
- package/extension/src/content/generated/content.bundle.js +848 -245
- package/extension/src/content/generated/content.bundle.meta.json +6 -3
- package/extension/src/content/legacyGlobalRegistry.js +2 -0
- package/extension/src/content/otWarmMirror.js +95 -65
- package/extension/src/content/otWarmMirrorController.js +102 -1
- package/extension/src/content/panelMaintenance.js +26 -15
- package/extension/src/content/providerSettingsCoordinator.js +1 -0
- package/extension/src/content/runFailureNotice.js +104 -0
- package/extension/src/content/runScrollLayout.js +124 -0
- package/extension/src/content/runTimelineView.js +36 -10
- package/extension/src/content/settingsPanel.js +28 -11
- package/extension/src/content/updateNotice.js +24 -4
- package/extension/src/page/overleafRealtimeObserver.js +116 -24
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/i18n.js +77 -37
- package/extension/src/shared/managedUpdateProjection.js +1 -1
- package/extension/src/shared/otText.js +0 -1
- package/extension/src/shared/sessionState.js +17 -21
- package/extension/src/shared/sessionTitle.js +120 -0
- package/extension/src/shared/updateRuntimeIdentity.js +32 -0
- package/extension/styles/panel.css +73 -14
- package/native-host/src/mirrorWorkspace.js +20 -1
- package/native-host/src/releaseProxy.js +177 -0
- package/native-host/src/releaseTransport.js +154 -0
- package/native-host/src/updateManager.js +40 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<h1>Codex Overleaf Link</h1>
|
|
4
4
|
<p><strong>Empower Overleaf with Codex.</strong></p>
|
|
5
5
|
<p>
|
|
6
|
-
<img src="https://img.shields.io/badge/version-2.3.
|
|
6
|
+
<img src="https://img.shields.io/badge/version-2.3.6-blue" alt="version">
|
|
7
7
|
<img src="https://img.shields.io/badge/platform-macOS%20%2F%20Windows%20%2F%20Linux-lightgrey" alt="platform">
|
|
8
8
|
<img src="https://img.shields.io/badge/chrome-MV3-green" alt="chrome manifest v3">
|
|
9
9
|
<img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="node version">
|
|
@@ -45,6 +45,8 @@ Codex Overleaf Link adds a Codex panel directly inside Overleaf and mirrors the
|
|
|
45
45
|
- **Parallel subagents** — enable the `parallel-subagents` skill for decomposable tasks. The native host runs workers with assigned files; the skill can split a single file into section jobs. Worker progress appears in the timeline, and detected ownership violations are withheld from Overleaf writeback.
|
|
46
46
|
- **OT warm mirror** — optional, read-only observation of active Overleaf text edits keeps focused mirror files warm. It is off by default and falls back to the normal snapshot path when unavailable, stale, or inconsistent. Overleaf writeback still uses the page bridge.
|
|
47
47
|
|
|
48
|
+
OT freshness expires after 30 seconds, and focused warm starts still verify current file content through the page bridge. OT observation never establishes whole-project freshness or replaces the Overleaf writeback path.
|
|
49
|
+
|
|
48
50
|
## Requirements
|
|
49
51
|
|
|
50
52
|
| Requirement | Notes |
|
|
@@ -70,14 +72,14 @@ One command installs the managed native host **and** managed extension runtime.
|
|
|
70
72
|
macOS / Linux:
|
|
71
73
|
|
|
72
74
|
```bash
|
|
73
|
-
CODEX_OVERLEAF_REF=v2.3.
|
|
75
|
+
CODEX_OVERLEAF_REF=v2.3.6 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v2.3.6/install.sh)"
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
Windows PowerShell:
|
|
77
79
|
|
|
78
80
|
```powershell
|
|
79
|
-
iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v2.3.
|
|
80
|
-
$env:CODEX_OVERLEAF_REF='v2.3.
|
|
81
|
+
iwr https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v2.3.6/install.ps1 -OutFile install.ps1
|
|
82
|
+
$env:CODEX_OVERLEAF_REF='v2.3.6'
|
|
81
83
|
powershell -ExecutionPolicy Bypass -File install.ps1
|
|
82
84
|
```
|
|
83
85
|
|
|
@@ -88,7 +90,7 @@ Then open `chrome://extensions`, enable **Developer mode**, click **Load unpacke
|
|
|
88
90
|
`npm exec` installs the same managed native host and extension runtime without keeping a source checkout. Use it if you prefer a pinned npm package.
|
|
89
91
|
|
|
90
92
|
```bash
|
|
91
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
93
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- install-managed
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
Then, in `chrome://extensions`, enable **Developer mode**, click **Load unpacked**, and select the managed extension path printed by the command. The Release extension zip remains available for explicitly unmanaged/manual installations.
|
|
@@ -185,6 +187,11 @@ Generated binary writeback is a separate operation: supported assets up to **10
|
|
|
185
187
|
|
|
186
188
|
## Update
|
|
187
189
|
|
|
190
|
+
Update downloads retry transient network failures within a bounded time budget, including stalled response bodies. The updater respects HTTP/HTTPS environment proxies and supported macOS/Windows system proxy settings. TLS certificate and release-signature failures remain blocking errors. SOCKS/PAC-only configurations require an HTTP proxy endpoint.
|
|
191
|
+
|
|
192
|
+
After a manual reinstall, installed files and running components are shown separately. Use the reload action once Overleaf is saved and idle; pending Overleaf tabs can then refresh. A disk replacement alone is not reported as a successfully health-confirmed update.
|
|
193
|
+
|
|
194
|
+
|
|
188
195
|
Managed installations **check** for signed stable updates automatically. When an update is available, choose **Update now** in the update notice or **Settings → Software updates** to authorize that version. The updater then downloads and verifies the coordinated extension/native bundle, waits until connected Overleaf tabs are saved and idle and the native host has no active work, and applies both components together. A failed health check restores the previous version. The update notice also offers postponement and progress details.
|
|
189
196
|
|
|
190
197
|
Stable updates use signed release metadata and artifact hashes; draft and prerelease versions are not selected. Releases that require a different Bootstrap protocol need a managed reinstall. The updater does not silently add Chrome permissions.
|
|
@@ -201,9 +208,9 @@ npm installs, updates, and uninstalls the coordinated managed extension/native p
|
|
|
201
208
|
|
|
202
209
|
| Action | Command |
|
|
203
210
|
|--------|---------|
|
|
204
|
-
| Install / recover / migrate | `npm exec --yes codex-overleaf-link@2.3.
|
|
205
|
-
| Diagnose | `npm exec --yes codex-overleaf-link@2.3.
|
|
206
|
-
| Uninstall | `npm exec --yes codex-overleaf-link@2.3.
|
|
211
|
+
| Install / recover / migrate | `npm exec --yes codex-overleaf-link@2.3.6 -- install-managed` |
|
|
212
|
+
| Diagnose | `npm exec --yes codex-overleaf-link@2.3.6 -- doctor` |
|
|
213
|
+
| Uninstall | `npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed` |
|
|
207
214
|
|
|
208
215
|
Use `--extension-id <chrome-extension-id>` only for a custom/dev unpacked extension id that differs from the official bundled id.
|
|
209
216
|
|
|
@@ -214,7 +221,7 @@ Use `--extension-id <chrome-extension-id>` only for a custom/dev unpacked extens
|
|
|
214
221
|
Remove the managed extension/native installation (append `--browser chromium` on Linux Chromium):
|
|
215
222
|
|
|
216
223
|
```bash
|
|
217
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
224
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed
|
|
218
225
|
```
|
|
219
226
|
|
|
220
227
|
The same command works in Windows PowerShell. It also applies to current `install.sh` / `install.ps1` installations, which install the managed pair.
|
|
@@ -222,7 +229,7 @@ The same command works in Windows PowerShell. It also applies to current `instal
|
|
|
222
229
|
For an unmanaged checkout or native-only installation, use `npm run uninstall:native` from the checkout, or:
|
|
223
230
|
|
|
224
231
|
```bash
|
|
225
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
232
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-native
|
|
226
233
|
```
|
|
227
234
|
|
|
228
235
|
If you are removing an older native-only source installation and still have its source checkout, its bundled uninstaller can also be invoked directly:
|
|
@@ -248,7 +255,7 @@ Remove the extension entry from `chrome://extensions` as well. To erase saved Co
|
|
|
248
255
|
For a managed installation, rerun the [managed installer](#install), reload the extension in `chrome://extensions`, then refresh the Overleaf tab. This recovers the coordinated extension/native pair after an incomplete installation or incompatible runtime update.
|
|
249
256
|
|
|
250
257
|
```bash
|
|
251
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
258
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- install-managed
|
|
252
259
|
```
|
|
253
260
|
|
|
254
261
|
For an unmanaged checkout, rebuild the extension and reinstall its native host from the same checkout. Use PowerShell installation commands on Windows.
|
|
@@ -369,13 +376,13 @@ The shipped extension targets `https://overleaf.com/project` and `https://www.ov
|
|
|
369
376
|
Linux Chromium install or update:
|
|
370
377
|
|
|
371
378
|
```bash
|
|
372
|
-
CODEX_OVERLEAF_REF=v2.3.
|
|
379
|
+
CODEX_OVERLEAF_REF=v2.3.6 bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ghqqqq/codex-overleaf-link/v2.3.6/install.sh)" -- --browser chromium
|
|
373
380
|
```
|
|
374
381
|
|
|
375
382
|
Linux Chromium uninstall:
|
|
376
383
|
|
|
377
384
|
```bash
|
|
378
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
385
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed --browser chromium
|
|
379
386
|
```
|
|
380
387
|
|
|
381
388
|
## Extension ID
|
|
@@ -389,27 +396,27 @@ illdpneeeopfffmiepaejglgmhpmdhdc
|
|
|
389
396
|
The installer uses this id by default. For a managed installation with a custom id, rerun the managed installer with the id shown in `chrome://extensions`:
|
|
390
397
|
|
|
391
398
|
```bash
|
|
392
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
399
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- install-managed --extension-id <your-chrome-extension-id>
|
|
393
400
|
```
|
|
394
401
|
|
|
395
402
|
For an unmanaged extension, use the native-only installer:
|
|
396
403
|
|
|
397
404
|
```bash
|
|
398
|
-
npm exec --yes codex-overleaf-link@2.3.
|
|
405
|
+
npm exec --yes codex-overleaf-link@2.3.6 -- install-native --extension-id <your-chrome-extension-id>
|
|
399
406
|
```
|
|
400
407
|
|
|
401
408
|
Both npm commands work in PowerShell. Source installers also accept the `CODEX_OVERLEAF_EXTENSION_ID` environment variable. The Native Messaging manifest's `allowed_origins` must match the loaded extension id.
|
|
402
409
|
|
|
403
410
|
## GitHub Release Artifacts
|
|
404
411
|
|
|
405
|
-
The v2.3.
|
|
412
|
+
The v2.3.6 GitHub Release contains:
|
|
406
413
|
|
|
407
|
-
- `codex-overleaf-link-extension-v2.3.
|
|
408
|
-
- `codex-overleaf-native-host-v2.3.
|
|
409
|
-
- `codex-overleaf-update-v2.3.
|
|
410
|
-
- `codex-overleaf-link-2.3.
|
|
411
|
-
- `install.sh`: release-pinned macOS / Linux installer that defaults to `v2.3.
|
|
412
|
-
- `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v2.3.
|
|
414
|
+
- `codex-overleaf-link-extension-v2.3.6.zip`: loadable Chrome extension package for manual unpacked installation.
|
|
415
|
+
- `codex-overleaf-native-host-v2.3.6.tar.gz`: native host runtime files used by the installer and release verification.
|
|
416
|
+
- `codex-overleaf-update-v2.3.6.tar.gz`: coordinated extension/native bundle used by the managed updater.
|
|
417
|
+
- `codex-overleaf-link-2.3.6.tgz`: npm native host CLI package for pinned install, doctor, and uninstall flows.
|
|
418
|
+
- `install.sh`: release-pinned macOS / Linux installer that defaults to `v2.3.6` when run directly from the release artifact.
|
|
419
|
+
- `install.ps1`: release-pinned Windows PowerShell installer that defaults to `v2.3.6` when run directly from the release artifact.
|
|
413
420
|
- `uninstall-native-host.mjs`: native host uninstaller that removes the Chrome Native Messaging manifest, bridge executable, and runtime copy.
|
|
414
421
|
- `nativeHostPlatform.js`, `manifest.js`, `runtimeInstaller.js`: helper files required by the loose uninstaller asset.
|
|
415
422
|
- `SHA256SUMS`, `release-manifest.json`, and `release-manifest.sig`: checksums, release metadata, and its Ed25519 signature.
|
|
@@ -484,8 +491,8 @@ Use this matrix for release-candidate signoff and compatibility reports. Record
|
|
|
484
491
|
| Browser/channel/version | Google Chrome channel and version. | Google Chrome channel and version. | Google Chrome channel and version. | Chromium channel/package and version. |
|
|
485
492
|
| Install mode | Managed pair recommended; unmanaged Release zip or checkout also available. | Same as macOS Chrome. | Same as macOS Chrome. | Managed or unmanaged; register with `--browser chromium`. |
|
|
486
493
|
| Extension id | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. | Bundled id `illdpneeeopfffmiepaejglgmhpmdhdc`, or actual custom id passed with `--extension-id`. |
|
|
487
|
-
| Installer/update command | `npm exec --yes codex-overleaf-link@2.3.
|
|
488
|
-
| Uninstall command | `npm exec --yes codex-overleaf-link@2.3.
|
|
494
|
+
| Installer/update command | `npm exec --yes codex-overleaf-link@2.3.6 -- install-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- install-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- install-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- install-managed --browser chromium` |
|
|
495
|
+
| Uninstall command | `npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed` | `npm exec --yes codex-overleaf-link@2.3.6 -- uninstall-managed --browser chromium` |
|
|
489
496
|
| Manifest/registry path | `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.codex.overleaf.json` | `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.codex.overleaf` -> `%LOCALAPPDATA%\CodexOverleaf\native-host-runtime\com.codex.overleaf.json` | `~/.config/google-chrome/NativeMessagingHosts/com.codex.overleaf.json` | `~/.config/chromium/NativeMessagingHosts/com.codex.overleaf.json` |
|
|
490
497
|
| Managed runtime paths | `~/.codex-overleaf/managed/extension` and `~/.codex-overleaf/managed/native`. | `%LOCALAPPDATA%\CodexOverleaf\managed\extension` and `%LOCALAPPDATA%\CodexOverleaf\managed\native`. | Same as macOS Chrome. | Same as macOS Chrome. |
|
|
491
498
|
| Node/Git/Codex/TeX | Record exact versions; see [Requirements](#requirements) for installation and provider prerequisites. | Same as macOS Chrome. | Same as macOS Chrome. | Same as macOS Chrome. |
|
|
@@ -20,6 +20,10 @@ if (!globalThis.CodexOverleafGlobalPreferences) {
|
|
|
20
20
|
importScripts(runtimeBase ? chrome.runtime.getURL(`${runtimeBase}/src/shared/globalPreferences.js`) : 'shared/globalPreferences.js');
|
|
21
21
|
}
|
|
22
22
|
globalThis.CodexOverleafGlobalPreferences?.installBackground(chrome);
|
|
23
|
+
if (!globalThis.CodexOverleafUpdateRuntimeIdentity) {
|
|
24
|
+
const runtimeBase = String(globalThis.__CODEX_OVERLEAF_RUNTIME_BASE__ || '');
|
|
25
|
+
importScripts(runtimeBase ? chrome.runtime.getURL(`${runtimeBase}/src/shared/updateRuntimeIdentity.js`) : 'shared/updateRuntimeIdentity.js');
|
|
26
|
+
}
|
|
23
27
|
|
|
24
28
|
(function initBackground() {
|
|
25
29
|
'use strict';
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const UPDATE_STATE_KEY = 'codex-overleaf-managed-update-state-v1';
|
|
5
5
|
const CONSENT_STATE_KEY = 'codex-overleaf-update-consent-v1';
|
|
6
6
|
const UPDATE_RELOAD_TABS_KEY = 'codex-overleaf-managed-update-tabs-v1';
|
|
7
|
+
const MANUAL_RELOAD_TABS_KEY = 'codex-overleaf-manual-runtime-reload-v1';
|
|
7
8
|
const CHECK_ALARM = 'codex-overleaf-consent-update-check';
|
|
8
9
|
const IDLE_ALARM = 'codex-overleaf-consent-update-idle';
|
|
9
10
|
const WATCHDOG_ALARM = 'codex-overleaf-consent-update-watchdog';
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
const CHECK_INTERVAL_MINUTES = 24 * 60;
|
|
13
14
|
const SNOOZE_MS = 24 * 60 * 60 * 1000;
|
|
14
15
|
const CANDIDATE_MAX_AGE_MS = 5 * 60 * 1000;
|
|
15
|
-
const CHECK_REQUEST_TIMEOUT_MS =
|
|
16
|
+
const CHECK_REQUEST_TIMEOUT_MS = 55 * 1000;
|
|
16
17
|
const ACTIVATION_TIMEOUT_MS = 20 * 1000;
|
|
17
18
|
const ACTIVATION_POLL_MS = 250;
|
|
18
19
|
const FAST_IDLE_RETRY_MS = 4000;
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
'codex-overleaf/consent-update-check',
|
|
32
33
|
'codex-overleaf/consent-update-install',
|
|
33
34
|
'codex-overleaf/consent-update-later',
|
|
34
|
-
'codex-overleaf/consent-update-dismiss'
|
|
35
|
+
'codex-overleaf/consent-update-dismiss',
|
|
36
|
+
'codex-overleaf/consent-update-reload'
|
|
35
37
|
]);
|
|
36
38
|
|
|
37
39
|
const policy = root.CodexOverleafUpdateConsent;
|
|
@@ -41,6 +43,10 @@
|
|
|
41
43
|
let policyTail = Promise.resolve();
|
|
42
44
|
let idleRetryTimer = null;
|
|
43
45
|
let activationPromise = null;
|
|
46
|
+
let runtimeStatus = null;
|
|
47
|
+
let runtimeStatusAt = 0;
|
|
48
|
+
let runtimeStatusRead = null;
|
|
49
|
+
let runtimeReloadMessage = '';
|
|
44
50
|
|
|
45
51
|
function init(options = {}) {
|
|
46
52
|
if (initialized || !policy || !revocation) return;
|
|
@@ -92,6 +98,7 @@
|
|
|
92
98
|
await reconcileRecoveryState();
|
|
93
99
|
await settleTerminalConsent();
|
|
94
100
|
await armLegacyGuard();
|
|
101
|
+
await finishManualRuntimeReload();
|
|
95
102
|
await publishView();
|
|
96
103
|
const updateState = await getUpdateState();
|
|
97
104
|
if (['staged', 'waiting_for_idle'].includes(updateState.state)) {
|
|
@@ -129,6 +136,8 @@
|
|
|
129
136
|
return postponeUpdate();
|
|
130
137
|
case 'codex-overleaf/consent-update-dismiss':
|
|
131
138
|
return dismissCompletedUpdate();
|
|
139
|
+
case 'codex-overleaf/consent-update-reload':
|
|
140
|
+
return reloadInstalledRuntime();
|
|
132
141
|
default:
|
|
133
142
|
throw codedError('unknown_update_action', 'Unknown update action.');
|
|
134
143
|
}
|
|
@@ -648,11 +657,86 @@
|
|
|
648
657
|
}
|
|
649
658
|
|
|
650
659
|
async function getView() {
|
|
651
|
-
|
|
652
|
-
|
|
660
|
+
let [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
661
|
+
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId;
|
|
662
|
+
const runtime = busy ? { state: 'transaction_active' } : await readRuntimeStatus();
|
|
663
|
+
if (runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
664
|
+
policy.compareStableVersions(state.latestVersion || state.currentVersion, runtime.installedVersion) <= 0) {
|
|
665
|
+
state = await setUpdateState({ ...state, state: 'idle', currentVersion: runtime.installedVersion,
|
|
666
|
+
latestVersion: runtime.installedVersion, code: '', message: '', transactionId: '', blocker: '', blockers: [] });
|
|
667
|
+
}
|
|
668
|
+
const view = policy.deriveViewModel(state, consent, {
|
|
653
669
|
currentVersion: currentVersion(),
|
|
654
670
|
now: Date.now()
|
|
655
671
|
});
|
|
672
|
+
view.runtime = runtime;
|
|
673
|
+
const pending = (await chrome.storage.local.get(MANUAL_RELOAD_TABS_KEY))?.[MANUAL_RELOAD_TABS_KEY];
|
|
674
|
+
const pagesPending = runtime.state === 'aligned' && pending?.targetVersion === runtime.installedVersion && pending.tabIds?.length;
|
|
675
|
+
if (runtime.state === 'reload_required' || pagesPending) {
|
|
676
|
+
view.showPanel = true;
|
|
677
|
+
view.state = { ...state, state: pagesPending ? 'reload_tabs_required' : 'reload_required',
|
|
678
|
+
currentVersion: runtime.extensionVersion, latestVersion: runtime.installedVersion,
|
|
679
|
+
code: 'update_reload_required', message: runtimeReloadMessage };
|
|
680
|
+
view.badge = { text: 'UP', color: '#3578bd' };
|
|
681
|
+
}
|
|
682
|
+
return view;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async function readRuntimeStatus(force = false) {
|
|
686
|
+
if (!root.CodexOverleafUpdateRuntimeIdentity) return { state: 'unknown' };
|
|
687
|
+
if (!force && runtimeStatus && Date.now() - runtimeStatusAt < 5000) return runtimeStatus;
|
|
688
|
+
if (runtimeStatusRead) return runtimeStatusRead;
|
|
689
|
+
runtimeStatusRead = withTimeout(requestNative('update.status'), 4000,
|
|
690
|
+
codedError('update_status_timeout', 'Installed version inspection timed out.')).then(status => {
|
|
691
|
+
runtimeStatusAt = Date.now();
|
|
692
|
+
runtimeStatus = root.CodexOverleafUpdateRuntimeIdentity.inspectInstalledRuntime(status, {
|
|
693
|
+
extensionVersion: chrome.runtime.getManifest().version, runtimeVersion: currentVersion()
|
|
694
|
+
});
|
|
695
|
+
return runtimeStatus;
|
|
696
|
+
}).catch(() => ({ state: 'unknown' })).finally(() => { runtimeStatusRead = null; });
|
|
697
|
+
return runtimeStatusRead;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async function requireReloadSafePoint() {
|
|
701
|
+
const tabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES });
|
|
702
|
+
const probes = await Promise.all(tabs.filter(isUsableEditorTab).map(tab => probeTabIdle(tab.id)));
|
|
703
|
+
const nativeGate = await requestNative('update.canApply').then(result => ({ ok: true, result }))
|
|
704
|
+
.catch(error => ({ ok: false, error: safeError(error) }));
|
|
705
|
+
const blockers = root.CodexOverleafUpdateStatus?.collectBlockers(probes, nativeGate) || ['busy'];
|
|
706
|
+
if (nativeBridge?.getPendingState?.().executionRequests > 0) blockers.push('background_execution_pending');
|
|
707
|
+
if (blockers.length) {
|
|
708
|
+
runtimeReloadMessage = 'Reload is waiting for Overleaf to be saved and idle (' + blockers.join(', ') + ').';
|
|
709
|
+
throw codedError('update_reload_busy', runtimeReloadMessage);
|
|
710
|
+
}
|
|
711
|
+
runtimeReloadMessage = '';
|
|
712
|
+
return tabs.filter(tab => Number.isInteger(tab.id) && !tab.discarded && tab.status !== 'unloaded');
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async function reloadInstalledRuntime() {
|
|
716
|
+
const runtime = await readRuntimeStatus(true);
|
|
717
|
+
if (runtime.state === 'aligned') { await finishManualRuntimeReload(); return getView(); }
|
|
718
|
+
if (runtime.state !== 'reload_required') throw codedError('update_reload_unavailable', 'No verified installed update is awaiting reload.');
|
|
719
|
+
const tabs = await requireReloadSafePoint();
|
|
720
|
+
await chrome.storage.local.set({ [MANUAL_RELOAD_TABS_KEY]: {
|
|
721
|
+
targetVersion: runtime.installedVersion, tabIds: tabs.map(tab => tab.id)
|
|
722
|
+
} });
|
|
723
|
+
setTimeout(() => chrome.runtime.reload(), 0);
|
|
724
|
+
return getView();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
async function finishManualRuntimeReload() {
|
|
728
|
+
const pending = (await chrome.storage.local.get(MANUAL_RELOAD_TABS_KEY))?.[MANUAL_RELOAD_TABS_KEY];
|
|
729
|
+
if (!pending || !Array.isArray(pending.tabIds)) return;
|
|
730
|
+
const runtime = await readRuntimeStatus(true);
|
|
731
|
+
if (runtime.state !== 'aligned' || runtime.installedVersion !== pending.targetVersion) return;
|
|
732
|
+
let tabs;
|
|
733
|
+
try { tabs = await requireReloadSafePoint(); } catch (_error) { return; }
|
|
734
|
+
const remaining = [];
|
|
735
|
+
for (const tab of tabs.filter(tab => pending.tabIds.includes(tab.id))) {
|
|
736
|
+
try { await chrome.tabs.reload(tab.id); } catch (_error) { remaining.push(tab.id); }
|
|
737
|
+
}
|
|
738
|
+
if (remaining.length) await chrome.storage.local.set({ [MANUAL_RELOAD_TABS_KEY]: { ...pending, tabIds: remaining } });
|
|
739
|
+
else await chrome.storage.local.remove(MANUAL_RELOAD_TABS_KEY);
|
|
656
740
|
}
|
|
657
741
|
|
|
658
742
|
async function publishView() {
|
|
@@ -418,6 +418,8 @@
|
|
|
418
418
|
const runTimelineView = Modules.RunTimelineView.create({
|
|
419
419
|
RunGuidanceView: Modules.RunGuidanceView,
|
|
420
420
|
RunResultActions: runResultActions,
|
|
421
|
+
RunScrollLayout: Modules.RunScrollLayout,
|
|
422
|
+
RunFailureNotice: Modules.RunFailureNotice,
|
|
421
423
|
tr,
|
|
422
424
|
tx,
|
|
423
425
|
getLocale,
|
|
@@ -835,6 +837,7 @@
|
|
|
835
837
|
});
|
|
836
838
|
|
|
837
839
|
let panel = null;
|
|
840
|
+
let probeStatusSnapshot = null;
|
|
838
841
|
let panelRendererInstance = null;
|
|
839
842
|
let sessionPanelInstance = null;
|
|
840
843
|
let settingsPanelInstance = null;
|
|
@@ -1031,7 +1034,7 @@
|
|
|
1031
1034
|
providerSettingsCoordinator.syncSessionProvider().catch(() => {});
|
|
1032
1035
|
syncOtWarmMirrorController().catch(error => {
|
|
1033
1036
|
updateOtStatusDisplay('unavailable');
|
|
1034
|
-
appendPlainLog(tx(`Failed to sync
|
|
1037
|
+
appendPlainLog(tx(`Failed to sync OT warm mirror: ${error.message}`, `同步 OT 预热镜像失败:${error.message}`));
|
|
1035
1038
|
});
|
|
1036
1039
|
await refreshProbe({ quiet: true });
|
|
1037
1040
|
}
|
|
@@ -1274,6 +1277,7 @@
|
|
|
1274
1277
|
onProvidersOpen: () => providerSettingsCoordinator.open(),
|
|
1275
1278
|
onClearAllHistory: () => clearAllHistoryWithConfirm(),
|
|
1276
1279
|
onHistoryOpen: () => renderAuditHistoryPanel(),
|
|
1280
|
+
onStorageOpen: () => refreshStorageUsageSummary(),
|
|
1277
1281
|
onHistoryFilter: () => applyAuditHistoryFilter(),
|
|
1278
1282
|
// Experimental OT mirror: a single visible switch whose click is
|
|
1279
1283
|
// intercepted so the confirm-before-enable flow still runs.
|
|
@@ -1444,9 +1448,6 @@
|
|
|
1444
1448
|
function renderAttachmentPreviewList(attachments = [], container, options = {}) {
|
|
1445
1449
|
return composerAttachmentController.renderAttachmentPreviewList(attachments, container, options);
|
|
1446
1450
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
1451
|
function applyLocaleToPanel() {
|
|
1451
1452
|
if (!panel) {
|
|
1452
1453
|
return;
|
|
@@ -1535,6 +1536,17 @@
|
|
|
1535
1536
|
updateModelDisplay();
|
|
1536
1537
|
renderSessionList();
|
|
1537
1538
|
renderContextSelection();
|
|
1539
|
+
const probeStatus = panel.querySelector('[data-probe-status]');
|
|
1540
|
+
if (probeStatus?.dataset.refreshing === 'true') probeStatus.textContent = tr('refreshProbeLoading');
|
|
1541
|
+
else if (probeStatus && probeStatusSnapshot?.projectId === getCurrentProjectId()) {
|
|
1542
|
+
const { failed, probe, userInitiated } = probeStatusSnapshot;
|
|
1543
|
+
const text = failed ? tr('refreshProbeFailed') : formatProbeStatusBar(probe);
|
|
1544
|
+
probeStatus.textContent = userInitiated && !failed ? tr('refreshProbeDone', { status: text }) : text;
|
|
1545
|
+
if (!failed) updateExistingProbeNotice(probe);
|
|
1546
|
+
}
|
|
1547
|
+
updateSkillsEntrySummary();
|
|
1548
|
+
providerSettingsCoordinator.renderSummary();
|
|
1549
|
+
if (settingsPanelInstance?.container?.querySelector('[data-storage-card]')?.open) refreshStorageUsageSummary();
|
|
1538
1550
|
}
|
|
1539
1551
|
|
|
1540
1552
|
function setElementTitleAndAria(selector, title, ariaLabel) {
|
|
@@ -2393,7 +2405,7 @@
|
|
|
2393
2405
|
}
|
|
2394
2406
|
|
|
2395
2407
|
await saveState();
|
|
2396
|
-
applyStateToPanel();
|
|
2408
|
+
applyStateToPanel({ preserveScroll: true });
|
|
2397
2409
|
} catch (persistenceError) {
|
|
2398
2410
|
appendRunEvent({
|
|
2399
2411
|
title: tx('Codex result was generated, but saving local session history failed.', 'Codex 结果已生成,但保存本地会话记录失败。'),
|
|
@@ -4943,6 +4955,7 @@
|
|
|
4943
4955
|
}
|
|
4944
4956
|
|
|
4945
4957
|
async function refreshProbe(options = {}) {
|
|
4958
|
+
const projectId = getCurrentProjectId();
|
|
4946
4959
|
const userInitiated = options.userInitiated === true;
|
|
4947
4960
|
if (userInitiated) {
|
|
4948
4961
|
setRefreshProbeLoading(true);
|
|
@@ -4958,6 +4971,8 @@
|
|
|
4958
4971
|
const probe = await callPageBridge('probe', {
|
|
4959
4972
|
manualOverride: state?.requireReviewing === false
|
|
4960
4973
|
});
|
|
4974
|
+
if (projectId !== getCurrentProjectId()) return null;
|
|
4975
|
+
probeStatusSnapshot = { projectId, probe, userInitiated };
|
|
4961
4976
|
const status = panel?.querySelector('[data-probe-status]');
|
|
4962
4977
|
if (status) {
|
|
4963
4978
|
status.textContent = userInitiated
|
|
@@ -4976,6 +4991,8 @@
|
|
|
4976
4991
|
if (!userInitiated) {
|
|
4977
4992
|
throw error;
|
|
4978
4993
|
}
|
|
4994
|
+
if (projectId !== getCurrentProjectId()) return null;
|
|
4995
|
+
probeStatusSnapshot = { projectId, failed: true };
|
|
4979
4996
|
const status = panel?.querySelector('[data-probe-status]');
|
|
4980
4997
|
if (status) {
|
|
4981
4998
|
status.textContent = tr('refreshProbeFailed');
|
|
@@ -6069,7 +6086,7 @@
|
|
|
6069
6086
|
saveStateSoon();
|
|
6070
6087
|
}
|
|
6071
6088
|
|
|
6072
|
-
function applyStateToPanel() {
|
|
6089
|
+
function applyStateToPanel(options = {}) {
|
|
6073
6090
|
applyPanelTheme(getThemePreference());
|
|
6074
6091
|
if ((state.providerId || 'builtin') === 'builtin' && !modelSelectHasOption(state.model)) {
|
|
6075
6092
|
renderModelOptions(getModelCatalog().FALLBACK_MODELS, state.model);
|
|
@@ -6105,7 +6122,7 @@
|
|
|
6105
6122
|
syncModeControls();
|
|
6106
6123
|
applySessionLabel();
|
|
6107
6124
|
renderSessionList();
|
|
6108
|
-
renderRunHistory();
|
|
6125
|
+
renderRunHistory(options);
|
|
6109
6126
|
renderContextSelection();
|
|
6110
6127
|
renderContextSummary();
|
|
6111
6128
|
// v1.8.0 C4: refresh recovery — small attachments persisted in state are
|
|
@@ -359,7 +359,7 @@
|
|
|
359
359
|
if (!options.collectOnly) {
|
|
360
360
|
showDiagnosticsLoading(tr('diagnosticsOtTitle'), tr('diagnosticsLoading'));
|
|
361
361
|
}
|
|
362
|
-
// OT warm mirror is
|
|
362
|
+
// OT warm mirror is opt-in and off by default. When it is disabled
|
|
363
363
|
// there is nothing live to probe, so report a clean, healthy "disabled"
|
|
364
364
|
// state instead of querying (then warning about) absent OT metadata — or
|
|
365
365
|
// degrading to the generic "could not run" error if a probe throws. A
|
|
@@ -374,6 +374,14 @@
|
|
|
374
374
|
nextStep: '',
|
|
375
375
|
technical: ''
|
|
376
376
|
};
|
|
377
|
+
const mirrorState = getOtWarmMirrorState?.() || {};
|
|
378
|
+
if (mirrorState.failureActive && mirrorState.lastFailure) {
|
|
379
|
+
const failure = mirrorState.lastFailure;
|
|
380
|
+
disabledResult.status = 'warning';
|
|
381
|
+
disabledResult.summary = tr(otWarmMirrorController.getFailureMessageKey(failure.code));
|
|
382
|
+
disabledResult.nextStep = tr('otFailureNextStep');
|
|
383
|
+
disabledResult.technical = `lastErrorCode: ${failure.code}\nphase: ${failure.phase}\nfailedAt: ${new Date(failure.at).toISOString()}`;
|
|
384
|
+
}
|
|
377
385
|
if (options.collectOnly) {
|
|
378
386
|
return disabledResult;
|
|
379
387
|
}
|
|
@@ -434,6 +442,7 @@
|
|
|
434
442
|
const queuedEventCount = normalizeOtDiagnosticsCount(otStatus?.queuedEventCount);
|
|
435
443
|
const lastEventAt = formatOtDiagnosticValue(otStatus?.lastEventAt, tr('noneValue'));
|
|
436
444
|
const warmMirrorState = getOtWarmMirrorState() || {};
|
|
445
|
+
const failureActive = warmMirrorState.failureActive === true;
|
|
437
446
|
const lastOtPatchAt = formatOtDiagnosticValue(mirrorStatus?.lastOtPatchAt || warmMirrorState.lastPatchAt, tr('noneValue'));
|
|
438
447
|
const lastOtErrorCode = formatOtDiagnosticValue(mirrorStatus?.lastOtErrorCode || warmMirrorState.lastErrorCode, tr('noneValue'));
|
|
439
448
|
const lastErrorCode = formatOtDiagnosticValue(otStatus?.lastErrorCode || otStatus?.reason || otStatus?.error, tr('noneValue'));
|
|
@@ -445,13 +454,14 @@
|
|
|
445
454
|
const bridgeFailed = otStatus?.ok === false;
|
|
446
455
|
const focusFiles = getActiveFocusFiles();
|
|
447
456
|
const otWarmStart = otWarmMirrorController?.canUseOtWarmStart?.({ enabled, focusFiles, mirrorStatus }) || { ok: false };
|
|
448
|
-
const fallback = !enabled || statusValue !== 'observing' || bridgeFailed || otWarmStart?.ok !== true;
|
|
457
|
+
const fallback = !enabled || failureActive || statusValue !== 'observing' || bridgeFailed || otWarmStart?.ok !== true;
|
|
449
458
|
|
|
450
459
|
return {
|
|
451
460
|
title: tr('diagnosticsOtTitle'),
|
|
452
461
|
subtitle: tr('diagnosticsOtSubtitle'),
|
|
453
462
|
status: enabled && !fallback && !bridgeFailed ? 'completed' : (enabled || bridgeFailed ? 'warning' : 'completed'),
|
|
454
|
-
summary: tr(
|
|
463
|
+
summary: tr(failureActive ? otWarmMirrorController.getFailureMessageKey(warmMirrorState.lastErrorCode)
|
|
464
|
+
: enabled ? 'diagnosticsOtSummaryEnabled' : 'diagnosticsOtSummaryDisabled'),
|
|
455
465
|
bullets: [
|
|
456
466
|
`${tr('otStatus')}: ${formatOtStatusLabel(statusValue)}`,
|
|
457
467
|
`${tr('otFreshFiles')}: ${otFreshFileCount}`,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<small data-i18n="diagnosticsSnapshotSubtitle">Full project, assets, read source</small>
|
|
39
39
|
</button>
|
|
40
40
|
<button type="button" data-diagnostics-ot>
|
|
41
|
-
<span data-i18n="diagnosticsOtTitle">Check
|
|
41
|
+
<span data-i18n="diagnosticsOtTitle">Check OT Mirror</span>
|
|
42
42
|
<small data-i18n="diagnosticsOtSubtitle">Status, fresh files, fallback</small>
|
|
43
43
|
</button>
|
|
44
44
|
<button type="button" data-diagnostics-export>
|