codex-overleaf-link 2.4.1 → 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.
- package/README.md +275 -305
- package/README.zh-CN.md +290 -320
- package/extension/bootstrap/background.js +99 -19
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +11 -16
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/update.js +25 -2
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +234 -56
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +440 -0
- package/extension/src/backgroundUpdateCoordinator.js +541 -381
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +16 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +244 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +117 -67
- package/extension/src/content/composerPanel.js +41 -8
- package/extension/src/content/contentRuntime.js +1084 -1099
- package/extension/src/content/diagnosticsController.js +122 -97
- package/extension/src/content/diagnosticsExport.js +236 -0
- package/extension/src/content/diagnosticsPanel.js +78 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +14680 -3606
- package/extension/src/content/generated/content.bundle.meta.json +30 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +25 -1
- package/extension/src/content/localSkillsPanel.js +141 -16
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +8 -5
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +33 -5
- package/extension/src/content/panelMaintenance.js +87 -132
- package/extension/src/content/panelRenderer.js +10 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +63 -14
- package/extension/src/content/providerSettingsDialog.js +724 -113
- package/extension/src/content/recentProjects.js +21 -6
- package/extension/src/content/runActivitySummary.js +665 -0
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +40 -15
- package/extension/src/content/runFailureNotice.js +34 -12
- package/extension/src/content/runGuidanceController.js +76 -30
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +150 -43
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +11 -8
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +315 -290
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/sessionPersistence.js +6 -1
- package/extension/src/content/settingsPanel.js +84 -55
- package/extension/src/content/settingsWorkbench.js +379 -0
- package/extension/src/content/sharedSessionViewSync.js +109 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +179 -56
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +308 -2
- package/extension/src/content/writebackOrchestrator.js +213 -199
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +600 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +223 -23
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +149 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +20 -6
- package/extension/src/page/snapshotRouter.js +124 -48
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +442 -71
- package/extension/src/page/trackedChangeCapture.js +53 -57
- package/extension/src/page/trackedChangeOwnership.js +216 -0
- package/extension/src/page/trackedChangeReplay.js +184 -0
- package/extension/src/page/trackedChangesLifecycle.js +343 -339
- package/extension/src/page/treeOperations.js +33 -12
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +114 -0
- package/extension/src/page/writebackRouter.js +242 -201
- package/extension/src/pageBridge.js +73 -102
- package/extension/src/popup.js +261 -65
- package/extension/src/shared/agentTranscript.js +41 -4
- package/extension/src/shared/auditRecords.js +73 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +101 -37
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +329 -3
- package/extension/src/shared/globalPreferences.js +31 -14
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +389 -25
- package/extension/src/shared/managedUpdateProjection.js +8 -5
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +9 -2
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +114 -3
- package/extension/src/shared/runActivityModel.js +361 -0
- package/extension/src/shared/runExecutionSnapshot.js +8 -2
- package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +37 -0
- package/extension/src/shared/sessionState.js +166 -30
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +311 -0
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +77 -78
- package/extension/src/shared/storageMigration.js +74 -111
- package/extension/src/shared/storageRunActions.js +211 -22
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +216 -6
- package/extension/src/shared/updateConsent.js +42 -11
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +78 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +67 -48
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +1987 -385
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +6 -3
- 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 +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -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/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +33 -12
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +211 -318
- package/native-host/src/codexSessionTiming.js +21 -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 +115 -39
- 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/nativeEnvironment.js +70 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +115 -0
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +17 -3
- package/native-host/src/providerReasoning.js +10 -11
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +76 -14
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +86 -0
- package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
- package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +176 -159
- package/native-host/src/subagentTelemetry.js +325 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +34 -0
- package/native-host/src/taskRunnerRuntime.js +130 -57
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +406 -38
- package/native-host/src/updateNetworkControl.js +75 -0
- package/native-host/src/updateTrust.js +21 -2
- package/native-host/src/vendor/pdfjs/LICENSE +177 -0
- package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
- package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/native-host/src/writingStyleEvidence.js +102 -0
- package/native-host/src/writingStylePdfWorker.js +35 -0
- package/native-host/src/writingStyleProfile.js +165 -0
- package/native-host/src/writingStyleRuntime.js +475 -0
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -24,14 +24,18 @@
|
|
|
24
24
|
const OVERLEAF_MATCHES = [
|
|
25
25
|
'https://www.overleaf.com/project',
|
|
26
26
|
'https://overleaf.com/project',
|
|
27
|
+
'https://cn.overleaf.com/project',
|
|
27
28
|
'https://www.overleaf.com/project/*',
|
|
28
|
-
'https://overleaf.com/project/*'
|
|
29
|
+
'https://overleaf.com/project/*',
|
|
30
|
+
'https://cn.overleaf.com/project/*'
|
|
29
31
|
];
|
|
30
32
|
const MESSAGE_TYPES = new Set([
|
|
31
33
|
'codex-overleaf/consent-update-get-state',
|
|
32
34
|
'codex-overleaf/consent-update-check',
|
|
33
35
|
'codex-overleaf/consent-update-install',
|
|
34
36
|
'codex-overleaf/consent-update-later',
|
|
37
|
+
'codex-overleaf/consent-update-cancel',
|
|
38
|
+
'codex-overleaf/consent-update-recover',
|
|
35
39
|
'codex-overleaf/consent-update-dismiss',
|
|
36
40
|
'codex-overleaf/consent-update-reload'
|
|
37
41
|
]);
|
|
@@ -47,6 +51,11 @@
|
|
|
47
51
|
let runtimeStatusAt = 0;
|
|
48
52
|
let runtimeStatusRead = null;
|
|
49
53
|
let runtimeReloadMessage = '';
|
|
54
|
+
let operationGeneration = 0;
|
|
55
|
+
let activeOperation = null;
|
|
56
|
+
let cancellationPromise = null;
|
|
57
|
+
let recoveryPromise = null;
|
|
58
|
+
let stateWriteTail = Promise.resolve();
|
|
50
59
|
|
|
51
60
|
function init(options = {}) {
|
|
52
61
|
if (initialized || !policy || !revocation) return;
|
|
@@ -59,7 +68,10 @@
|
|
|
59
68
|
sendResponse({ ok: false, error: { code: 'forbidden_sender', message: 'Update actions are limited to this extension and Overleaf project tabs.' } });
|
|
60
69
|
return false;
|
|
61
70
|
}
|
|
62
|
-
|
|
71
|
+
const direct = ['codex-overleaf/consent-update-get-state',
|
|
72
|
+
'codex-overleaf/consent-update-cancel', 'codex-overleaf/consent-update-later',
|
|
73
|
+
'codex-overleaf/consent-update-recover'].includes(message.type);
|
|
74
|
+
(direct ? handleMessage(message) : enqueuePolicyAction(() => handleMessage(message)))
|
|
63
75
|
.then(result => sendResponse({ ok: true, result }))
|
|
64
76
|
.catch(error => sendResponse({ ok: false, error: safeError(error) }));
|
|
65
77
|
return true;
|
|
@@ -70,7 +82,7 @@
|
|
|
70
82
|
void enqueuePolicyAction(() => checkOnly({ manual: false })).catch(() => {});
|
|
71
83
|
}
|
|
72
84
|
if (alarm?.name === WATCHDOG_ALARM) {
|
|
73
|
-
void
|
|
85
|
+
void reconcileRecoveryState().catch(() => {});
|
|
74
86
|
}
|
|
75
87
|
if (alarm?.name === IDLE_ALARM) {
|
|
76
88
|
void enqueuePolicyAction(() => tryActivateStagedUpdate()).catch(() => {});
|
|
@@ -86,6 +98,7 @@
|
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
async function startup() {
|
|
101
|
+
if (!await waitForBootstrapOwnership()) return;
|
|
89
102
|
chrome.alarms?.create?.(CHECK_ALARM, {
|
|
90
103
|
delayInMinutes: 0.5,
|
|
91
104
|
periodInMinutes: CHECK_INTERVAL_MINUTES
|
|
@@ -128,11 +141,16 @@
|
|
|
128
141
|
switch (message.type) {
|
|
129
142
|
case 'codex-overleaf/consent-update-get-state':
|
|
130
143
|
return getView();
|
|
144
|
+
case 'codex-overleaf/consent-update-recover':
|
|
145
|
+
await reconcileRecoveryState();
|
|
146
|
+
return getView();
|
|
131
147
|
case 'codex-overleaf/consent-update-check':
|
|
132
148
|
return checkOnly({ manual: true });
|
|
133
149
|
case 'codex-overleaf/consent-update-install':
|
|
134
150
|
return installUpdate();
|
|
135
151
|
case 'codex-overleaf/consent-update-later':
|
|
152
|
+
return snoozeManualInstall().then(view => view || postponeUpdate());
|
|
153
|
+
case 'codex-overleaf/consent-update-cancel':
|
|
136
154
|
return postponeUpdate();
|
|
137
155
|
case 'codex-overleaf/consent-update-dismiss':
|
|
138
156
|
return dismissCompletedUpdate();
|
|
@@ -143,106 +161,90 @@
|
|
|
143
161
|
}
|
|
144
162
|
}
|
|
145
163
|
|
|
146
|
-
async function checkOnly({ manual }) {
|
|
147
|
-
const
|
|
148
|
-
if (consent.authorizedVersion || policy.isExecutionState(currentState.state)) {
|
|
149
|
-
return getView();
|
|
150
|
-
}
|
|
151
|
-
await setUpdateState({
|
|
152
|
-
...currentState,
|
|
153
|
-
state: 'checking',
|
|
154
|
-
initiatedBy: manual ? 'manual' : 'automatic',
|
|
155
|
-
code: '',
|
|
156
|
-
message: '',
|
|
157
|
-
blocker: '',
|
|
158
|
-
blockers: []
|
|
159
|
-
});
|
|
160
|
-
|
|
164
|
+
async function checkOnly({ manual }, inheritedOperation) {
|
|
165
|
+
const operation = inheritedOperation || beginOperation();
|
|
161
166
|
try {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
167
|
+
const [currentState, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
168
|
+
assertOperation(operation);
|
|
169
|
+
if (currentState.cancelRequested || currentState.recoveryPending
|
|
170
|
+
|| consent.authorizedVersion || policy.isExecutionState(currentState.state)) return getView();
|
|
171
|
+
operation.phase = 'checking';
|
|
172
|
+
await setUpdateState({ ...currentState, state: 'checking', operationId: operation.id,
|
|
173
|
+
cancelRequested: false, initiatedBy: manual ? 'manual' : 'automatic',
|
|
174
|
+
code: '', message: '', blocker: '', blockers: [], cooldownUntil: 0, postponeUntil: LEGACY_GUARD }, { operation });
|
|
175
|
+
try {
|
|
176
|
+
const result = await withTimeout(requestNative('update.check', {
|
|
177
|
+
operationId: operation.id, currentVersion: currentVersion(), etag: currentState.etag || ''
|
|
178
|
+
}), CHECK_REQUEST_TIMEOUT_MS, codedError('update_check_timeout', 'The managed update check did not complete in time.'));
|
|
179
|
+
assertOperation(operation);
|
|
180
|
+
const checkedAt = Date.now();
|
|
181
|
+
if (result.available) {
|
|
182
|
+
const latestVersion = result.latestVersion || currentState.latestVersion;
|
|
183
|
+
await setConsentState({ ...consent,
|
|
184
|
+
...(latestVersion !== consent.snoozedVersion ? { snoozedVersion: '', snoozedUntil: 0 } : {}),
|
|
185
|
+
lastPromptedVersion: latestVersion,
|
|
186
|
+
lastPromptedAt: manual ? checkedAt : consent.lastPromptedAt
|
|
187
|
+
}, { operation });
|
|
188
|
+
await setUpdateState({ ...currentState, state: 'update_available', operationId: operation.id,
|
|
189
|
+
currentVersion: result.currentVersion || currentVersion(), latestVersion,
|
|
190
|
+
managed: true, etag: result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
191
|
+
postponeUntil: LEGACY_GUARD, code: '', message: '', cooldownUntil: 0, manualReason: {}, cancelRequested: false
|
|
192
|
+
}, { operation });
|
|
193
|
+
} else if ((result.reason === 'manual_install_required'
|
|
194
|
+
&& policy.compareStableVersions(result.latestVersion, currentVersion()) > 0)
|
|
195
|
+
|| (result.reason === 'prerelease_promotion_requires_manual_update'
|
|
196
|
+
&& result.latestVersion === (result.currentVersion || currentVersion()))) {
|
|
197
|
+
await setUpdateState({ ...currentState, state: 'manual_install_required', operationId: operation.id,
|
|
198
|
+
currentVersion: result.currentVersion || currentVersion(), latestVersion: result.latestVersion,
|
|
199
|
+
managed: true, etag: result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
200
|
+
postponeUntil: LEGACY_GUARD, code: result.reason === 'prerelease_promotion_requires_manual_update'
|
|
201
|
+
? result.reason : 'update_bootstrap_upgrade_required', message: '', cooldownUntil: 0,
|
|
202
|
+
manualReason: result.manualInstall?.reason || {}, cancelRequested: false
|
|
203
|
+
}, { operation });
|
|
204
|
+
} else {
|
|
205
|
+
await setUpdateState({ ...currentState, state: 'idle', operationId: operation.id,
|
|
206
|
+
currentVersion: result.currentVersion || currentVersion(),
|
|
207
|
+
latestVersion: result.latestVersion || result.currentVersion || currentVersion(),
|
|
208
|
+
// A 304 without a verified Native candidate is a cache miss. Drop
|
|
209
|
+
// its validator so the next normal check can obtain fresh bytes.
|
|
210
|
+
etag: result.reason === 'not_modified' ? '' : result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
211
|
+
postponeUntil: LEGACY_GUARD, transactionId: '',
|
|
212
|
+
code: result.reason === 'cooldown' ? 'update_cooldown' : '',
|
|
213
|
+
cooldownUntil: result.reason === 'cooldown' ? Number(result.cooldownUntil || 0) : 0,
|
|
214
|
+
manualReason: {}, message: '', cancelRequested: false
|
|
215
|
+
}, { operation });
|
|
216
|
+
}
|
|
217
|
+
return getView();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
220
|
+
await requestNative('update.cancel', { operationId: operation.id }).catch(() => {});
|
|
221
|
+
assertOperation(operation);
|
|
222
|
+
await setUpdateState({ ...currentState, state: 'failed', operationId: operation.id,
|
|
223
|
+
initiatedBy: manual ? 'manual' : 'automatic', postponeUntil: LEGACY_GUARD,
|
|
224
|
+
code: safeCode(error), message: safeMessage(error)
|
|
225
|
+
}, { operation });
|
|
226
|
+
throw error;
|
|
215
227
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await setUpdateState({
|
|
219
|
-
...currentState,
|
|
220
|
-
state: 'failed',
|
|
221
|
-
initiatedBy: manual ? 'manual' : 'automatic',
|
|
222
|
-
postponeUntil: LEGACY_GUARD,
|
|
223
|
-
code: safeCode(error),
|
|
224
|
-
message: safeMessage(error)
|
|
225
|
-
});
|
|
226
|
-
throw error;
|
|
228
|
+
} finally {
|
|
229
|
+
if (!inheritedOperation) finishOperation(operation);
|
|
227
230
|
}
|
|
228
231
|
}
|
|
229
232
|
|
|
233
|
+
|
|
230
234
|
async function recoverInterruptedCheck() {
|
|
231
235
|
const state = await getUpdateState();
|
|
232
|
-
if (state.state !== 'checking') return;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
state:
|
|
239
|
-
blocker: '',
|
|
240
|
-
blockers: [],
|
|
241
|
-
code: '',
|
|
242
|
-
message: ''
|
|
236
|
+
if (state.state !== 'checking' || activeOperation || state.cancelRequested) return;
|
|
237
|
+
if (state.operationId) {
|
|
238
|
+
await postponeUpdate().catch(() => {});
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
await setUpdateState({ ...state,
|
|
242
|
+
state: policy.compareStableVersions(state.latestVersion, currentVersion()) > 0 ? 'update_available' : 'idle',
|
|
243
|
+
blocker: '', blockers: [], code: '', message: ''
|
|
243
244
|
});
|
|
244
245
|
}
|
|
245
246
|
|
|
247
|
+
|
|
246
248
|
async function dismissCompletedUpdate() {
|
|
247
249
|
const state = await getUpdateState();
|
|
248
250
|
if (state.state !== 'committed') return getView();
|
|
@@ -263,194 +265,177 @@
|
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
async function installUpdate() {
|
|
266
|
-
|
|
267
|
-
let consent =
|
|
268
|
-
|
|
269
|
-
if (state.state !== 'update_available' || candidateStale) {
|
|
270
|
-
await checkOnly({ manual: true });
|
|
268
|
+
const operation = beginOperation();
|
|
269
|
+
let state, consent, authorizationId = '';
|
|
270
|
+
try {
|
|
271
271
|
state = await getUpdateState();
|
|
272
272
|
consent = await getConsentState();
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
assertOperation(operation);
|
|
274
|
+
if (state.cancelRequested || state.recoveryPending || policy.isExecutionState(state.state)) return getView();
|
|
275
|
+
const candidateStale = !state.lastCheckedAt || Date.now() - state.lastCheckedAt > CANDIDATE_MAX_AGE_MS;
|
|
276
|
+
if (state.state !== 'update_available' || candidateStale) {
|
|
277
|
+
await checkOnly({ manual: true }, operation);
|
|
278
|
+
assertOperation(operation);
|
|
279
|
+
state = await getUpdateState();
|
|
280
|
+
consent = await getConsentState();
|
|
281
|
+
assertOperation(operation);
|
|
282
|
+
}
|
|
283
|
+
// The fresh check found a release only a reinstall can apply; show that notice.
|
|
284
|
+
if (state.state === 'manual_install_required') return getView();
|
|
285
|
+
if (state.state !== 'update_available'
|
|
286
|
+
|| policy.compareStableVersions(state.latestVersion, currentVersion()) <= 0) {
|
|
287
|
+
throw codedError('update_candidate_missing', 'No newer signed stable update is available.');
|
|
288
|
+
}
|
|
289
|
+
authorizationId = crypto.randomUUID();
|
|
290
|
+
operation.authorizationId = authorizationId;
|
|
291
|
+
operation.targetVersion = state.latestVersion;
|
|
292
|
+
const intent = revocation.prepareAuthorization(consent, authorizationId, state.latestVersion, Date.now());
|
|
293
|
+
await setConsentState(intent, { operation });
|
|
294
|
+
assertOperation(operation);
|
|
288
295
|
await requestNative('update.authorize', {
|
|
289
|
-
authorizationId,
|
|
290
|
-
targetVersion: state.latestVersion,
|
|
291
|
-
currentVersion: currentVersion()
|
|
296
|
+
operationId: operation.id, authorizationId, targetVersion: state.latestVersion, currentVersion: currentVersion()
|
|
292
297
|
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (!executor || typeof executor.installAuthorizedUpdate !== 'function') {
|
|
304
|
-
throw codedError(
|
|
305
|
-
'update_executor_unavailable',
|
|
306
|
-
'The managed update executor is unavailable.'
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
await executor.installAuthorizedUpdate();
|
|
298
|
+
assertOperation(operation);
|
|
299
|
+
await setConsentState(revocation.clear({ ...intent, snoozedVersion: '', snoozedUntil: 0,
|
|
300
|
+
authorizedVersion: state.latestVersion, authorizationId, authorizedAt: Date.now()
|
|
301
|
+
}), { operation });
|
|
302
|
+
await setUpdateState({ ...state, operationId: operation.id, postponeUntil: LEGACY_GUARD,
|
|
303
|
+
cancelRequested: false, recoveryPending: false, code: '', message: ''
|
|
304
|
+
}, { operation });
|
|
305
|
+
// Replaceable runtime owns this transfer. The immutable Bootstrap keeps
|
|
306
|
+
// its health-confirmation role and cannot resume a postponed operation.
|
|
307
|
+
await stageAuthorizedUpdate(operation);
|
|
310
308
|
return getView();
|
|
311
309
|
} catch (error) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
} catch (revokeError) {
|
|
333
|
-
if (error && typeof error === 'object') {
|
|
310
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
311
|
+
if (authorizationId) {
|
|
312
|
+
const observed = await getConsentState();
|
|
313
|
+
assertOperation(operation);
|
|
314
|
+
const pending = revocation.prepareAuthorization({ ...observed,
|
|
315
|
+
authorizedVersion: state.latestVersion, authorizationId,
|
|
316
|
+
authorizedAt: observed.authorizedAt || Date.now()
|
|
317
|
+
}, authorizationId, state.latestVersion, Date.now());
|
|
318
|
+
await setConsentState(pending, { operation });
|
|
319
|
+
try {
|
|
320
|
+
await requestNative('update.revoke', {
|
|
321
|
+
operationId: operation.id, authorizationId, targetVersion: state.latestVersion
|
|
322
|
+
});
|
|
323
|
+
assertOperation(operation);
|
|
324
|
+
await setConsentState(revocation.clear({ ...pending,
|
|
325
|
+
authorizedVersion: '', authorizationId: '', authorizedAt: 0
|
|
326
|
+
}), { operation });
|
|
327
|
+
} catch (revokeError) {
|
|
328
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
329
|
+
operation.revocationPending = true;
|
|
334
330
|
error.revocationError = safeMessage(revokeError);
|
|
335
331
|
}
|
|
336
332
|
}
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
message: safeMessage(error)
|
|
347
|
-
});
|
|
333
|
+
const observedState = await getUpdateState();
|
|
334
|
+
assertOperation(operation);
|
|
335
|
+
if (!['awaiting_health', 'committed', 'rolled_back'].includes(observedState.state)) {
|
|
336
|
+
await setUpdateState({ ...observedState, state: 'failed', initiatedBy: 'manual',
|
|
337
|
+
blocker: '', blockers: [], code: safeCode(error), message: safeMessage(error),
|
|
338
|
+
operationId: operation.id,
|
|
339
|
+
cancelRequested: operation.phase !== 'applying' && operation.revocationPending === true,
|
|
340
|
+
recoveryPending: operation.phase === 'applying'
|
|
341
|
+
}, { operation });
|
|
348
342
|
}
|
|
349
|
-
await armLegacyGuard();
|
|
350
343
|
throw error;
|
|
351
|
-
}
|
|
344
|
+
} finally { finishOperation(operation); }
|
|
352
345
|
}
|
|
353
346
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
initiatedBy: 'manual',
|
|
370
|
-
latestVersion: staged.targetVersion,
|
|
371
|
-
transactionId: staged.transactionId,
|
|
372
|
-
stagedAt: Date.now(),
|
|
373
|
-
blocker: '',
|
|
374
|
-
blockers: [],
|
|
375
|
-
code: '',
|
|
376
|
-
message: ''
|
|
377
|
-
});
|
|
378
|
-
return tryActivateStagedUpdate();
|
|
347
|
+
|
|
348
|
+
async function stageAuthorizedUpdate(operation) {
|
|
349
|
+
operation.phase = 'downloading';
|
|
350
|
+
await setUpdateState({ state: 'downloading', operationId: operation.id,
|
|
351
|
+
initiatedBy: 'manual', blocker: '', blockers: [], code: '', message: '',
|
|
352
|
+
postponeUntil: LEGACY_GUARD
|
|
353
|
+
}, { operation });
|
|
354
|
+
assertOperation(operation);
|
|
355
|
+
const staged = await requestNative('update.stage', { operationId: operation.id });
|
|
356
|
+
assertOperation(operation);
|
|
357
|
+
await setUpdateState({ state: 'staged', operationId: operation.id, initiatedBy: 'manual',
|
|
358
|
+
latestVersion: staged.targetVersion, transactionId: staged.transactionId, stagedAt: Date.now(),
|
|
359
|
+
blocker: '', blockers: [], code: '', message: '', postponeUntil: LEGACY_GUARD
|
|
360
|
+
}, { operation });
|
|
361
|
+
return tryActivateStagedUpdate(operation);
|
|
379
362
|
}
|
|
380
363
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (typeof executor?.installAuthorizedUpdate === 'function') {
|
|
384
|
-
if (activationPromise) return activationPromise;
|
|
385
|
-
activationPromise = Promise.resolve(executor.installAuthorizedUpdate()).finally(() => {
|
|
386
|
-
activationPromise = null;
|
|
387
|
-
});
|
|
388
|
-
return activationPromise;
|
|
389
|
-
}
|
|
364
|
+
|
|
365
|
+
async function tryActivateStagedUpdate(inheritedOperation) {
|
|
390
366
|
if (activationPromise) return activationPromise;
|
|
391
|
-
|
|
367
|
+
const state = await getUpdateState();
|
|
368
|
+
if (state.cancelRequested || state.recoveryPending || cancellationPromise
|
|
369
|
+
|| !['staged', 'waiting_for_idle'].includes(state.state)) return state;
|
|
370
|
+
const operation = inheritedOperation || beginOperation(state.operationId);
|
|
371
|
+
assertOperation(operation);
|
|
372
|
+
activationPromise = tryActivateStagedUpdateCore(operation).finally(() => {
|
|
392
373
|
activationPromise = null;
|
|
374
|
+
if (!inheritedOperation) finishOperation(operation);
|
|
393
375
|
});
|
|
394
376
|
return activationPromise;
|
|
395
377
|
}
|
|
396
378
|
|
|
397
|
-
|
|
379
|
+
|
|
380
|
+
async function tryActivateStagedUpdateCore(operation) {
|
|
398
381
|
clearIdleRetryTimer();
|
|
399
382
|
const state = await getUpdateState();
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
const surfaceTabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES }).catch(() => []);
|
|
383
|
+
assertOperation(operation);
|
|
384
|
+
if (state.cancelRequested || !['staged', 'waiting_for_idle'].includes(state.state)) return state;
|
|
385
|
+
operation.phase = 'waiting_for_idle';
|
|
386
|
+
const surfaceTabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES });
|
|
405
387
|
const editorTabs = surfaceTabs.filter(isUsableEditorTab);
|
|
406
|
-
const reloadTabs = surfaceTabs
|
|
407
|
-
.filter(tab => Number.isInteger(tab?.id) && !tab.discarded && tab.status !== 'unloaded')
|
|
408
|
-
.map(tab => tab.id);
|
|
388
|
+
const reloadTabs = surfaceTabs.filter(tab => Number.isInteger(tab?.id) && !tab.discarded && tab.status !== 'unloaded').map(tab => tab.id);
|
|
409
389
|
const probes = await Promise.all(editorTabs.map(tab => probeTabIdle(tab.id)));
|
|
410
390
|
const nativeGate = await requestNative('update.canApply')
|
|
411
|
-
.then(result => ({ ok: true, result }))
|
|
412
|
-
|
|
391
|
+
.then(result => ({ ok: true, result })).catch(error => ({ ok: false, error: safeError(error) }));
|
|
392
|
+
assertOperation(operation);
|
|
413
393
|
const blockers = root.CodexOverleafUpdateStatus?.collectBlockers(probes, nativeGate) || ['busy'];
|
|
394
|
+
if (nativeBridge?.getPendingState?.().executionRequests > 0) blockers.push('background_execution_pending');
|
|
414
395
|
if (blockers.length) {
|
|
415
|
-
const waiting = await setUpdateState({
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
blockers
|
|
420
|
-
});
|
|
421
|
-
scheduleIdleRetry(blockers);
|
|
396
|
+
const waiting = await setUpdateState({ state: 'waiting_for_idle',
|
|
397
|
+
operationId: operation.id, blocker: blockers[0], blockers
|
|
398
|
+
}, { operation });
|
|
399
|
+
if (isCurrentOperation(operation)) scheduleIdleRetry(blockers);
|
|
422
400
|
return waiting;
|
|
423
401
|
}
|
|
424
|
-
|
|
402
|
+
operation.phase = 'applying';
|
|
425
403
|
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
404
|
+
assertOperation(operation);
|
|
426
405
|
await chrome.storage.local.set({ [UPDATE_RELOAD_TABS_KEY]: reloadTabs });
|
|
427
|
-
await setUpdateState({
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
406
|
+
await setUpdateState({ state: 'applying', operationId: operation.id,
|
|
407
|
+
blocker: '', blockers: [], code: '', message: ''
|
|
408
|
+
}, { operation });
|
|
409
|
+
assertOperation(operation);
|
|
410
|
+
// requestInternal rechecks the safe point and blocks new native work.
|
|
411
|
+
let applied;
|
|
412
|
+
try {
|
|
413
|
+
applied = await requestNative('update.apply', {
|
|
414
|
+
operationId: operation.id, transactionId: state.transactionId
|
|
415
|
+
});
|
|
416
|
+
} catch (error) {
|
|
417
|
+
assertOperation(operation);
|
|
418
|
+
if (!['update_not_idle', 'update_native_busy'].includes(error.code)) throw error;
|
|
419
|
+
operation.phase = 'waiting_for_idle';
|
|
420
|
+
const waiting = await setUpdateState({ state: 'waiting_for_idle',
|
|
421
|
+
blocker: 'busy', blockers: ['busy'], recoveryPending: false
|
|
422
|
+
}, { operation, observed: true });
|
|
423
|
+
scheduleIdleRetry(waiting.blockers);
|
|
424
|
+
return waiting;
|
|
441
425
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
message: ''
|
|
449
|
-
});
|
|
426
|
+
assertOperation(operation);
|
|
427
|
+
if (applied.state !== 'awaiting_health' || applied.transactionId !== state.transactionId) {
|
|
428
|
+
throw codedError('update_apply_unconfirmed', 'Update activation did not return matching transaction evidence.');
|
|
429
|
+
}
|
|
430
|
+
await setUpdateState({ state: 'awaiting_health', operationId: operation.id,
|
|
431
|
+
latestVersion: applied.targetVersion, transactionId: applied.transactionId,
|
|
432
|
+
recoveryPending: false, code: '', message: ''
|
|
433
|
+
}, { operation });
|
|
450
434
|
chrome.runtime.reload();
|
|
451
435
|
return { state: 'awaiting_health' };
|
|
452
436
|
}
|
|
453
437
|
|
|
438
|
+
|
|
454
439
|
async function waitForActivatedTransaction(transactionId) {
|
|
455
440
|
const deadline = Date.now() + ACTIVATION_TIMEOUT_MS;
|
|
456
441
|
while (Date.now() <= deadline) {
|
|
@@ -486,7 +471,7 @@
|
|
|
486
471
|
try {
|
|
487
472
|
const url = new URL(tab.url || '');
|
|
488
473
|
return url.protocol === 'https:' &&
|
|
489
|
-
(
|
|
474
|
+
(['overleaf.com', 'www.overleaf.com', 'cn.overleaf.com'].includes(url.hostname)) &&
|
|
490
475
|
/^\/project\/[^/]+(?:\/|$)/.test(url.pathname);
|
|
491
476
|
} catch (_error) {
|
|
492
477
|
return false;
|
|
@@ -505,143 +490,253 @@
|
|
|
505
490
|
}
|
|
506
491
|
}
|
|
507
492
|
|
|
508
|
-
|
|
493
|
+
// A reinstall notice has no download or authorization to cancel; Later only
|
|
494
|
+
// hides it for a day.
|
|
495
|
+
async function snoozeManualInstall() {
|
|
509
496
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
let pendingConsent = consent;
|
|
514
|
-
if (consent.authorizationId) {
|
|
515
|
-
pendingConsent = revocation.begin(consent, state, Date.now());
|
|
516
|
-
await setConsentState(pendingConsent);
|
|
517
|
-
try {
|
|
518
|
-
await requestNative('update.revoke', {
|
|
519
|
-
authorizationId: pendingConsent.revokingAuthorizationId,
|
|
520
|
-
targetVersion: pendingConsent.revokingVersion,
|
|
521
|
-
transactionId: pendingConsent.revokingTransactionId
|
|
522
|
-
});
|
|
523
|
-
} catch (error) {
|
|
524
|
-
if (error?.code === 'update_revoke_too_late') {
|
|
525
|
-
await setConsentState(revocation.clear(pendingConsent));
|
|
526
|
-
return getView();
|
|
527
|
-
}
|
|
528
|
-
throw error;
|
|
529
|
-
}
|
|
530
|
-
} else if (['staged', 'waiting_for_idle'].includes(state.state)) {
|
|
531
|
-
throw codedError('update_consent_mismatch', 'The staged update has no matching runtime authorization.');
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
clearIdleRetryTimer();
|
|
535
|
-
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
536
|
-
const completed = revocation.complete(state, pendingConsent, {
|
|
537
|
-
now: Date.now(),
|
|
538
|
-
snoozeMs: SNOOZE_MS,
|
|
539
|
-
postponeUntil: LEGACY_GUARD
|
|
540
|
-
});
|
|
541
|
-
await setUpdateAndConsentState(completed.updateState, completed.consentState);
|
|
497
|
+
if (state.state !== 'manual_install_required') return null;
|
|
498
|
+
await setConsentState({ ...consent, snoozedVersion: state.latestVersion, snoozedUntil: Date.now() + SNOOZE_MS });
|
|
542
499
|
return getView();
|
|
543
500
|
}
|
|
544
501
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
502
|
+
function postponeUpdate() {
|
|
503
|
+
if (cancellationPromise) return cancellationPromise;
|
|
504
|
+
cancellationPromise = cancelUpdateCore().finally(() => { cancellationPromise = null; });
|
|
505
|
+
return cancellationPromise;
|
|
548
506
|
}
|
|
549
507
|
|
|
550
|
-
async function
|
|
508
|
+
async function cancelUpdateCore() {
|
|
551
509
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
552
|
-
if (
|
|
553
|
-
|
|
510
|
+
if (['applying', 'awaiting_health', 'rolling_back'].includes(state.state)
|
|
511
|
+
|| activeOperation?.phase === 'applying' || state.recoveryPending) {
|
|
512
|
+
throw codedError('update_revoke_too_late', 'The update is being installed or recovered.');
|
|
513
|
+
}
|
|
514
|
+
const operation = activeOperation;
|
|
515
|
+
const generation = ++operationGeneration;
|
|
516
|
+
activeOperation = null;
|
|
517
|
+
clearIdleRetryTimer();
|
|
518
|
+
const operationId = operation?.id || state.operationId || '';
|
|
519
|
+
const authorizationId = consent.authorizationId || consent.revokingAuthorizationId || operation?.authorizationId || '';
|
|
520
|
+
const targetVersion = consent.authorizedVersion || consent.revokingVersion || operation?.targetVersion || state.latestVersion;
|
|
521
|
+
const pending = authorizationId ? revocation.begin({ ...consent, authorizationId,
|
|
522
|
+
authorizedVersion: targetVersion }, state, Date.now()) : consent;
|
|
523
|
+
await setUpdateAndConsentState({ ...state, operationId, cancelRequested: true,
|
|
524
|
+
postponeUntil: LEGACY_GUARD, code: '', message: ''
|
|
525
|
+
}, pending, { generation });
|
|
526
|
+
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
554
527
|
try {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
528
|
+
if (operationId) {
|
|
529
|
+
const result = await withTimeout(requestNative('update.cancel', { operationId }), 15000,
|
|
530
|
+
codedError('update_cancel_unconfirmed', 'Update cancellation has not been acknowledged yet.'));
|
|
531
|
+
if (result.state !== 'cancelled' || result.operationId !== operationId) {
|
|
532
|
+
throw codedError('update_cancel_unconfirmed', 'Update cancellation returned incomplete evidence.');
|
|
533
|
+
}
|
|
534
|
+
} else if (state.state === 'downloading') {
|
|
535
|
+
throw codedError('update_cancel_unconfirmed', 'This older download has no operation identity. Reload the extension to recover it.');
|
|
536
|
+
}
|
|
537
|
+
if (authorizationId) {
|
|
538
|
+
await withTimeout(requestNative('update.revoke', {
|
|
539
|
+
operationId, authorizationId, targetVersion
|
|
540
|
+
}), 15000, codedError('update_cancel_unconfirmed', 'Update authorization cleanup has not finished yet.'));
|
|
564
541
|
}
|
|
565
|
-
|
|
566
|
-
|
|
542
|
+
const completed = revocation.complete(state, pending, { now: Date.now(),
|
|
543
|
+
snoozeMs: SNOOZE_MS, postponeUntil: LEGACY_GUARD });
|
|
544
|
+
await setUpdateAndConsentState({ ...completed.updateState,
|
|
545
|
+
state: policy.compareStableVersions(state.latestVersion, currentVersion()) > 0 ? 'update_available' : 'idle',
|
|
546
|
+
operationId: '', cancelRequested: false, recoveryPending: false,
|
|
547
|
+
code: 'update_cancelled', message: ''
|
|
548
|
+
}, completed.consentState, { generation, observed: true });
|
|
549
|
+
return getView();
|
|
550
|
+
} catch (error) {
|
|
551
|
+
if (error.code === 'update_revoke_too_late') {
|
|
552
|
+
await setUpdateAndConsentState({ cancelRequested: false, recoveryPending: true,
|
|
553
|
+
state: 'failed', code: 'update_recovery_pending', message: 'The update reached installation before cancellation. Checking its result.'
|
|
554
|
+
}, revocation.clear(pending), { generation, observed: true });
|
|
555
|
+
} else {
|
|
556
|
+
await setUpdateState({ cancelRequested: true, code: 'update_cancel_unconfirmed',
|
|
557
|
+
message: safeMessage(error)
|
|
558
|
+
}, { generation });
|
|
567
559
|
}
|
|
560
|
+
throw error;
|
|
568
561
|
}
|
|
562
|
+
}
|
|
569
563
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
);
|
|
581
|
-
return
|
|
564
|
+
|
|
565
|
+
function reconcileRecoveryState() {
|
|
566
|
+
if (recoveryPromise) return recoveryPromise;
|
|
567
|
+
recoveryPromise = (async () => {
|
|
568
|
+
if (!await waitForBootstrapOwnership()) return getUpdateState();
|
|
569
|
+
const state = await getUpdateState();
|
|
570
|
+
if (state.recoveryPending) return reconcileExpiredPhase();
|
|
571
|
+
if (state.cancelRequested) return postponeUpdate();
|
|
572
|
+
if (!activeOperation) await reconcilePendingRevocation();
|
|
573
|
+
return reconcileExpiredPhase();
|
|
574
|
+
})().finally(() => { recoveryPromise = null; });
|
|
575
|
+
return recoveryPromise;
|
|
582
576
|
}
|
|
583
577
|
|
|
578
|
+
|
|
579
|
+
async function reconcilePendingRevocation() {
|
|
580
|
+
const consent = await getConsentState();
|
|
581
|
+
if (!revocation.hasPending(consent) || activeOperation) return getUpdateState();
|
|
582
|
+
return postponeUpdate().then(view => view.state);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
|
|
584
586
|
async function settleTerminalConsent() {
|
|
585
587
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
586
|
-
if (!policy.isTerminalState(state.state) ||
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
authorizationId: '',
|
|
591
|
-
authorizedAt: 0
|
|
592
|
-
});
|
|
588
|
+
if (!policy.isTerminalState(state.state) || state.cancelRequested || state.recoveryPending
|
|
589
|
+
|| revocation.hasPending(consent) || !consent.authorizationId) return;
|
|
590
|
+
await setConsentState({ ...consent, authorizedVersion: '', authorizationId: '', authorizedAt: 0 },
|
|
591
|
+
{ expectedAuthorizationId: consent.authorizationId });
|
|
593
592
|
}
|
|
594
593
|
|
|
594
|
+
|
|
595
595
|
async function reconcileExpiredPhase() {
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
596
|
+
const recoveryOwner = await captureRecoveryOwner();
|
|
597
|
+
const state = recoveryOwner.state;
|
|
598
|
+
const recoveryOptions = { observed: true, recoveryOwner };
|
|
599
|
+
if (!state.recoveryPending && (!state.deadlineAt || Date.now() <= state.deadlineAt
|
|
600
|
+
|| policy.isTerminalState(state.state))) return state;
|
|
601
|
+
if (['checking', 'downloading'].includes(state.state)) {
|
|
602
|
+
await postponeUpdate();
|
|
603
|
+
return setUpdateState({ state: 'failed', cancelRequested: false,
|
|
604
|
+
code: 'update_phase_timeout', message: 'The update timed out and its download was stopped. Retry when the connection is available.'
|
|
605
|
+
}, { observed: true });
|
|
599
606
|
}
|
|
600
|
-
if (['applying', 'awaiting_health', 'rolling_back'].includes(state.state)) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
await
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
607
|
+
if (state.recoveryPending || ['applying', 'awaiting_health', 'rolling_back'].includes(state.state)) {
|
|
608
|
+
try {
|
|
609
|
+
let status = await withTimeout(requestNative('update.status'), 10000,
|
|
610
|
+
codedError('update_status_timeout', 'Update recovery status is unavailable.'));
|
|
611
|
+
await assertRecoveryOwner(recoveryOwner);
|
|
612
|
+
let transaction = status.transaction;
|
|
613
|
+
if (state.transactionId && transaction?.id !== state.transactionId) {
|
|
614
|
+
throw codedError('update_transaction_mismatch', 'The update recovery transaction could not be matched.');
|
|
615
|
+
}
|
|
616
|
+
if (transaction?.state === 'superseded') {
|
|
617
|
+
return settleSupersededTransaction(status, recoveryOwner);
|
|
618
|
+
}
|
|
619
|
+
if (transaction?.state === 'staged' || (!transaction && status.installedAligned
|
|
620
|
+
&& status.activeVersion === state.currentVersion)) {
|
|
621
|
+
if (state.operationId) await requestNative('update.cancel', { operationId: state.operationId });
|
|
622
|
+
await assertRecoveryOwner(recoveryOwner);
|
|
623
|
+
const consent = await getConsentState();
|
|
624
|
+
const authorizationId = consent.authorizationId || consent.revokingAuthorizationId;
|
|
625
|
+
const targetVersion = consent.authorizedVersion || consent.revokingVersion || state.latestVersion;
|
|
626
|
+
await assertRecoveryOwner(recoveryOwner);
|
|
627
|
+
if (authorizationId) await requestNative('update.revoke', { authorizationId, targetVersion });
|
|
628
|
+
const completed = await setUpdateAndConsentState({ state: 'failed', recoveryPending: false, cancelRequested: false,
|
|
629
|
+
operationId: '', code: 'update_not_applied', message: 'The update was stopped before installation. Retry when ready.'
|
|
630
|
+
}, revocation.clear({ ...consent, authorizedVersion: '', authorizationId: '', authorizedAt: 0 }), recoveryOptions);
|
|
631
|
+
return completed.updateState;
|
|
632
|
+
}
|
|
633
|
+
if (transaction?.state === 'awaiting_health') {
|
|
634
|
+
const rolledBack = await requestNative('update.rollback', {
|
|
635
|
+
transactionId: transaction.id, expectedState: 'awaiting_health', reasonCode: 'update_health_timeout'
|
|
636
|
+
});
|
|
637
|
+
await assertRecoveryOwner(recoveryOwner);
|
|
638
|
+
if (rolledBack.state !== 'rolled_back' || rolledBack.version !== transaction.sourceVersion) {
|
|
639
|
+
throw codedError('update_rollback_unconfirmed', 'Rollback returned incomplete evidence.');
|
|
640
|
+
}
|
|
641
|
+
status = await requestNative('update.status');
|
|
642
|
+
await assertRecoveryOwner(recoveryOwner);
|
|
643
|
+
transaction = status.transaction;
|
|
644
|
+
}
|
|
645
|
+
if (transaction?.state === 'committed' && status.installedAligned
|
|
646
|
+
&& status.activeVersion === transaction.targetVersion) {
|
|
647
|
+
return setUpdateState({ state: 'committed', currentVersion: transaction.targetVersion,
|
|
648
|
+
latestVersion: transaction.targetVersion, transactionId: transaction.id,
|
|
649
|
+
recoveryPending: false, cancelRequested: false, code: '', message: ''
|
|
650
|
+
}, recoveryOptions);
|
|
651
|
+
}
|
|
652
|
+
if (transaction?.state === 'rolled_back' && status.installedAligned
|
|
653
|
+
&& status.activeVersion === transaction.sourceVersion
|
|
654
|
+
&& (!state.transactionId || transaction.id === state.transactionId)) {
|
|
655
|
+
const restored = await setUpdateState({ state: 'rolled_back', currentVersion: transaction.sourceVersion,
|
|
656
|
+
transactionId: transaction.id, recoveryPending: false, cancelRequested: false,
|
|
657
|
+
code: transaction.reasonCode || 'update_rolled_back', message: 'The previous version has been restored.'
|
|
658
|
+
}, recoveryOptions);
|
|
659
|
+
setTimeout(() => chrome.runtime.reload(), 0);
|
|
660
|
+
return restored;
|
|
661
|
+
}
|
|
662
|
+
throw codedError('update_recovery_pending', 'The installed update result has not been confirmed.');
|
|
663
|
+
} catch (error) {
|
|
664
|
+
if (error.code === 'update_recovery_superseded') return getUpdateState();
|
|
665
|
+
return setUpdateState({ state: 'failed', recoveryPending: true, cancelRequested: false,
|
|
666
|
+
code: safeCode(error), message: safeMessage(error)
|
|
667
|
+
}, recoveryOptions).catch(writeError => {
|
|
668
|
+
if (writeError.code === 'update_recovery_superseded') return getUpdateState();
|
|
669
|
+
throw writeError;
|
|
670
|
+
});
|
|
634
671
|
}
|
|
635
672
|
}
|
|
636
|
-
return
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
673
|
+
return state;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
async function captureRecoveryOwner() {
|
|
677
|
+
const generation = operationGeneration;
|
|
678
|
+
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
679
|
+
return { generation, state, consent };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function matchesRecoveryOwner(owner, state, consent) {
|
|
683
|
+
return owner && owner.generation === operationGeneration
|
|
684
|
+
&& ['state', 'transactionId', 'operationId', 'currentVersion', 'latestVersion',
|
|
685
|
+
'recoveryPending', 'cancelRequested', 'lastCheckedAt', 'phaseStartedAt']
|
|
686
|
+
.every(key => state[key] === owner.state[key])
|
|
687
|
+
&& ['authorizationId', 'authorizedVersion', 'revokingAuthorizationId', 'revokingVersion']
|
|
688
|
+
.every(key => consent[key] === owner.consent[key]);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
async function assertRecoveryOwner(owner) {
|
|
692
|
+
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
693
|
+
assertRecoverySnapshot(owner, state, consent);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function assertRecoverySnapshot(owner, state, consent) {
|
|
697
|
+
if (owner && !matchesRecoveryOwner(owner, state, consent)) {
|
|
698
|
+
throw codedError('update_recovery_superseded', 'A newer update state replaced this recovery request.');
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Both startup recovery and the coordinator retire the same Native journal
|
|
703
|
+
// through this queue. The snapshot was captured before requesting status.
|
|
704
|
+
function settleSupersededTransaction(status, owner) {
|
|
705
|
+
return enqueueStateWrite(async () => {
|
|
706
|
+
const [previous, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
707
|
+
if (!matchesRecoveryOwner(owner, previous, consent)) return previous;
|
|
708
|
+
const transaction = status?.transaction;
|
|
709
|
+
const sameTargetReinstall = transaction?.reasonCode === 'update_reinstalled'
|
|
710
|
+
&& status.activeVersion === transaction.targetVersion
|
|
711
|
+
&& status.reinstalledPair?.extensionVersion === status.activeVersion
|
|
712
|
+
&& status.reinstalledPair?.nativeVersion === status.activeVersion
|
|
713
|
+
&& /^[0-9a-f]{64}$/.test(status.reinstalledPair.extensionSha256 || '')
|
|
714
|
+
&& /^[0-9a-f]{64}$/.test(status.reinstalledPair.nativeSha256 || '');
|
|
715
|
+
if (activeOperation || transaction?.state !== 'superseded' || !transaction.id
|
|
716
|
+
|| (previous.transactionId && previous.transactionId !== transaction.id)
|
|
717
|
+
|| status.installedAligned !== true || status.activeVersion !== transaction.supersededByVersion
|
|
718
|
+
|| (!sameTargetReinstall && policy.compareStableVersions(status.activeVersion, transaction.targetVersion) <= 0)
|
|
719
|
+
|| policy.compareStableVersions(status.activeVersion, transaction.sourceVersion) <= 0
|
|
720
|
+
|| ['authorized', 'bound'].includes(status.authorization?.state)
|
|
721
|
+
|| (consent.authorizedVersion && consent.authorizedVersion !== transaction.targetVersion)
|
|
722
|
+
|| (consent.revokingVersion && consent.revokingVersion !== transaction.targetVersion)) return previous;
|
|
723
|
+
const next = projectUpdateState(previous, { state: 'idle', currentVersion: status.activeVersion,
|
|
724
|
+
latestVersion: status.activeVersion, transactionId: '', operationId: '', recoveryPending: false,
|
|
725
|
+
cancelRequested: false, blocker: '', blockers: [], code: '', message: '', manualReason: {}, cooldownUntil: 0
|
|
726
|
+
}, { observed: true });
|
|
727
|
+
// Invalidate older awaits before yielding to storage, including recovery
|
|
728
|
+
// responses that describe this same retired transaction.
|
|
729
|
+
operationGeneration += 1;
|
|
730
|
+
await chrome.storage.local.set({ [UPDATE_STATE_KEY]: next,
|
|
731
|
+
[CONSENT_STATE_KEY]: policy.normalizeConsentState({}), [UPDATE_RELOAD_TABS_KEY]: [] });
|
|
732
|
+
runtimeStatus = null;
|
|
733
|
+
return next;
|
|
641
734
|
});
|
|
642
735
|
}
|
|
643
736
|
|
|
737
|
+
|
|
644
738
|
async function handleObservedStateChange() {
|
|
739
|
+
if (!await waitForBootstrapOwnership()) return;
|
|
645
740
|
await settleTerminalConsent();
|
|
646
741
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
647
742
|
if (!consent.authorizationId && !policy.isExecutionState(state.state)) {
|
|
@@ -653,14 +748,14 @@
|
|
|
653
748
|
async function armLegacyGuard() {
|
|
654
749
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
655
750
|
if (consent.authorizationId || state.postponeUntil === LEGACY_GUARD) return state;
|
|
656
|
-
return setUpdateState({
|
|
751
|
+
return setUpdateState({ postponeUntil: LEGACY_GUARD });
|
|
657
752
|
}
|
|
658
753
|
|
|
659
754
|
async function getView() {
|
|
660
755
|
let [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
661
|
-
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId;
|
|
756
|
+
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId || state.cancelRequested;
|
|
662
757
|
const runtime = busy ? { state: 'transaction_active' } : await readRuntimeStatus();
|
|
663
|
-
if (runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
758
|
+
if (!state.recoveryPending && !state.cancelRequested && runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
664
759
|
policy.compareStableVersions(state.latestVersion || state.currentVersion, runtime.installedVersion) <= 0) {
|
|
665
760
|
state = await setUpdateState({ ...state, state: 'idle', currentVersion: runtime.installedVersion,
|
|
666
761
|
latestVersion: runtime.installedVersion, code: '', message: '', transactionId: '', blocker: '', blockers: [] });
|
|
@@ -769,17 +864,25 @@
|
|
|
769
864
|
return policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
770
865
|
}
|
|
771
866
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
867
|
+
function setUpdateState(value, options = {}) {
|
|
868
|
+
return enqueueStateWrite(async () => {
|
|
869
|
+
assertWriteOwner(options);
|
|
870
|
+
const stored = await chrome.storage.local.get(options.recoveryOwner
|
|
871
|
+
? [UPDATE_STATE_KEY, CONSENT_STATE_KEY] : UPDATE_STATE_KEY);
|
|
872
|
+
assertWriteOwner(options);
|
|
873
|
+
const previous = policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
874
|
+
assertRecoverySnapshot(options.recoveryOwner, previous, policy.normalizeConsentState(stored?.[CONSENT_STATE_KEY]));
|
|
875
|
+
const next = projectUpdateState(previous, value, options);
|
|
876
|
+
await chrome.storage.local.set({ [UPDATE_STATE_KEY]: next });
|
|
877
|
+
return next;
|
|
878
|
+
});
|
|
778
879
|
}
|
|
779
880
|
|
|
881
|
+
|
|
780
882
|
function projectUpdateState(previous, value, options = {}) {
|
|
781
883
|
const now = Date.now();
|
|
782
884
|
const candidate = {
|
|
885
|
+
...previous,
|
|
783
886
|
...value,
|
|
784
887
|
initiatedBy: value.initiatedBy || previous.initiatedBy
|
|
785
888
|
};
|
|
@@ -805,24 +908,36 @@
|
|
|
805
908
|
return policy.normalizeConsentState(stored?.[CONSENT_STATE_KEY]);
|
|
806
909
|
}
|
|
807
910
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
911
|
+
function setConsentState(value, options = {}) {
|
|
912
|
+
return enqueueStateWrite(async () => {
|
|
913
|
+
assertWriteOwner(options);
|
|
914
|
+
if (options.expectedAuthorizationId) {
|
|
915
|
+
const current = await getConsentState();
|
|
916
|
+
if (current.authorizationId !== options.expectedAuthorizationId) return current;
|
|
917
|
+
}
|
|
918
|
+
assertWriteOwner(options);
|
|
919
|
+
const next = policy.normalizeConsentState(value);
|
|
920
|
+
await chrome.storage.local.set({ [CONSENT_STATE_KEY]: next });
|
|
921
|
+
return next;
|
|
922
|
+
});
|
|
812
923
|
}
|
|
813
924
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
[UPDATE_STATE_KEY]
|
|
821
|
-
[CONSENT_STATE_KEY]
|
|
925
|
+
|
|
926
|
+
function setUpdateAndConsentState(updateValue, consentValue, options = {}) {
|
|
927
|
+
return enqueueStateWrite(async () => {
|
|
928
|
+
assertWriteOwner(options);
|
|
929
|
+
const stored = await chrome.storage.local.get([UPDATE_STATE_KEY, CONSENT_STATE_KEY]);
|
|
930
|
+
assertWriteOwner(options);
|
|
931
|
+
const previous = policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
932
|
+
assertRecoverySnapshot(options.recoveryOwner, previous, policy.normalizeConsentState(stored?.[CONSENT_STATE_KEY]));
|
|
933
|
+
const updateState = projectUpdateState(previous, updateValue, options);
|
|
934
|
+
const consentState = policy.normalizeConsentState(consentValue);
|
|
935
|
+
await chrome.storage.local.set({ [UPDATE_STATE_KEY]: updateState, [CONSENT_STATE_KEY]: consentState });
|
|
936
|
+
return { updateState, consentState };
|
|
822
937
|
});
|
|
823
|
-
return { updateState, consentState };
|
|
824
938
|
}
|
|
825
939
|
|
|
940
|
+
|
|
826
941
|
async function requestNative(method, params = {}) {
|
|
827
942
|
const response = await nativeBridge?.requestInternal?.({
|
|
828
943
|
id: crypto.randomUUID(),
|
|
@@ -839,11 +954,56 @@
|
|
|
839
954
|
}
|
|
840
955
|
|
|
841
956
|
function enqueuePolicyAction(action) {
|
|
842
|
-
const
|
|
957
|
+
const generation = operationGeneration;
|
|
958
|
+
const guarded = async () => !await waitForBootstrapOwnership()
|
|
959
|
+
|| generation !== operationGeneration || cancellationPromise ? getView() : action();
|
|
960
|
+
const result = policyTail.then(guarded, guarded);
|
|
843
961
|
policyTail = result.catch(() => {});
|
|
844
962
|
return result;
|
|
845
963
|
}
|
|
846
964
|
|
|
965
|
+
async function waitForBootstrapOwnership() {
|
|
966
|
+
const migration = await root.CodexOverleafBootstrapMigration?.ready?.();
|
|
967
|
+
if (migration?.continueInitialization === false) return false;
|
|
968
|
+
await root.__CODEX_OVERLEAF_BOOTSTRAP_READY__;
|
|
969
|
+
return true;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function beginOperation(id) {
|
|
973
|
+
const operation = { id: id || crypto.randomUUID(), generation: ++operationGeneration, phase: 'checking' };
|
|
974
|
+
activeOperation = operation;
|
|
975
|
+
return operation;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function isCurrentOperation(operation) {
|
|
979
|
+
return activeOperation === operation && operation.generation === operationGeneration;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function assertOperation(operation) {
|
|
983
|
+
if (!isCurrentOperation(operation)) throw codedError('update_network_cancelled', 'The update request was cancelled.');
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function finishOperation(operation) {
|
|
987
|
+
if (activeOperation === operation) activeOperation = null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function assertWriteOwner(options) {
|
|
991
|
+
if (options.recoveryOwner && options.recoveryOwner.generation !== operationGeneration) {
|
|
992
|
+
throw codedError('update_recovery_superseded', 'A newer update state replaced this recovery request.');
|
|
993
|
+
}
|
|
994
|
+
if (options.operation) assertOperation(options.operation);
|
|
995
|
+
if (options.generation !== undefined && options.generation !== operationGeneration) {
|
|
996
|
+
throw codedError('update_network_cancelled', 'The update request was superseded.');
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function enqueueStateWrite(action) {
|
|
1001
|
+
const result = stateWriteTail.then(action, action);
|
|
1002
|
+
stateWriteTail = result.catch(() => {});
|
|
1003
|
+
return result;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
|
|
847
1007
|
function isAllowedSender(sender) {
|
|
848
1008
|
if (sender?.id !== chrome.runtime.id) return false;
|
|
849
1009
|
try {
|
|
@@ -853,7 +1013,7 @@
|
|
|
853
1013
|
return url.pathname === '/bootstrap/popup.html';
|
|
854
1014
|
}
|
|
855
1015
|
return url.protocol === 'https:' &&
|
|
856
|
-
(
|
|
1016
|
+
(['overleaf.com', 'www.overleaf.com', 'cn.overleaf.com'].includes(url.hostname)) &&
|
|
857
1017
|
(url.pathname === '/project' || url.pathname.startsWith('/project/'));
|
|
858
1018
|
} catch (_error) {
|
|
859
1019
|
return false;
|
|
@@ -907,5 +1067,5 @@
|
|
|
907
1067
|
.slice(0, 300);
|
|
908
1068
|
}
|
|
909
1069
|
|
|
910
|
-
root.CodexOverleafUpdateCoordinator = Object.freeze({ init });
|
|
1070
|
+
root.CodexOverleafUpdateCoordinator = Object.freeze({ init, captureRecoveryOwner, settleSupersededTransaction });
|
|
911
1071
|
})(globalThis);
|