codex-overleaf-link 2.2.1 → 2.3.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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +8 -6
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +17 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
const CODEX_BLOSSOM_PATH = 'M13.798 23.976a5.7 5.7 0 0 1-2.26-.456 6.1 6.1 0 0 1-1.903-1.27 5.7 5.7 0 0 1-1.88.311 5.75 5.75 0 0 1-2.95-.79 6.2 6.2 0 0 1-2.188-2.159q-.81-1.366-.809-3.045 0-.695.19-1.51a6.4 6.4 0 0 1-1.475-2.038A5.95 5.95 0 0 1 0 10.573Q0 9.278.547 8.08q.547-1.2 1.523-2.062a5.5 5.5 0 0 1 2.307-1.223A5.7 5.7 0 0 1 5.472 2.35 6.1 6.1 0 0 1 7.565.623 5.8 5.8 0 0 1 10.206 0q1.19 0 2.26.456a6.1 6.1 0 0 1 1.903 1.27 5.7 5.7 0 0 1 1.88-.311q1.594 0 2.95.79a6 6 0 0 1 2.165 2.159q.832 1.366.832 3.045 0 .695-.19 1.51a6.3 6.3 0 0 1 1.475 2.062q.523 1.15.523 2.422a5.9 5.9 0 0 1-.547 2.493q-.547 1.2-1.546 2.086a5.4 5.4 0 0 1-2.284 1.199 5.56 5.56 0 0 1-1.118 2.445 5.9 5.9 0 0 1-2.07 1.727 5.8 5.8 0 0 1-2.64.623m-5.876-2.997q1.19 0 2.07-.504l4.472-2.589a.53.53 0 0 0 .238-.455v-2.062L8.945 18.7a.96.96 0 0 1-1.047 0l-4.496-2.613a.7.7 0 0 1-.024.168v.287q0 1.224.571 2.254a4.24 4.24 0 0 0 1.642 1.583q1.047.6 2.331.599m.238-3.908a.6.6 0 0 0 .262.072q.118 0 .238-.072l1.784-1.031-5.734-3.357q-.522-.312-.523-.935V6.545a4.3 4.3 0 0 0-1.903 1.63 4.25 4.25 0 0 0-.714 2.398q0 1.176.595 2.254.594 1.08 1.546 1.63zm5.638 5.323q1.26 0 2.284-.576a4.3 4.3 0 0 0 1.618-1.582q.595-1.008.595-2.254v-5.179a.47.47 0 0 0-.238-.431l-1.808-1.055v6.689q0 .624-.524.935l-4.496 2.613a4.3 4.3 0 0 0 2.57.84m.904-8.776v-3.26l-2.688-1.535-2.712 1.535v3.26l2.712 1.535zM7.756 5.97q0-.623.523-.935l4.496-2.613a4.3 4.3 0 0 0-2.569-.84q-1.26 0-2.284.576A4.3 4.3 0 0 0 6.304 3.74q-.57 1.008-.57 2.254v5.155q0 .287.237.455l1.785 1.055zM19.84 17.43a4.16 4.16 0 0 0 1.88-1.63 4.33 4.33 0 0 0 .713-2.397q0-1.176-.595-2.254-.594-1.08-1.546-1.63l-4.449-2.59q-.143-.096-.261-.072a.46.46 0 0 0-.238.072L13.56 7.936l5.758 3.38a.9.9 0 0 1 .38.384q.143.216.143.528zM15.059 5.25q.524-.335 1.047 0l4.52 2.662V7.48q0-1.15-.57-2.181A4.14 4.14 0 0 0 18.46 3.62q-1.023-.623-2.379-.623-1.19 0-2.07.503L9.54 6.09a.53.53 0 0 0-.238.455v2.062z';
|
|
6
6
|
|
|
7
7
|
function create(options = {}) {
|
|
8
|
+
const Profiles = options.ProviderProfiles;
|
|
9
|
+
if (!Profiles) {
|
|
10
|
+
throw new Error('Provider settings dialog requires provider profiles.');
|
|
11
|
+
}
|
|
8
12
|
const instance = {
|
|
13
|
+
Profiles,
|
|
9
14
|
document: options.document || document,
|
|
10
15
|
tx: options.tx || ((english) => english),
|
|
11
16
|
callbacks: options.callbacks || {},
|
|
12
|
-
catalog:
|
|
17
|
+
catalog: Profiles.normalizeCatalog({}),
|
|
13
18
|
selectedId: 'builtin',
|
|
14
19
|
draft: null,
|
|
15
20
|
dirty: false,
|
|
@@ -108,7 +113,7 @@
|
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
function setCatalog(instance, catalog, selectedId) {
|
|
111
|
-
instance.catalog =
|
|
116
|
+
instance.catalog = instance.Profiles.normalizeCatalog(catalog || {});
|
|
112
117
|
const nextId = selectedId || (
|
|
113
118
|
instance.catalog.providers.some(provider => provider.id === instance.selectedId)
|
|
114
119
|
? instance.selectedId
|
|
@@ -151,8 +156,8 @@
|
|
|
151
156
|
'第三方实验性'
|
|
152
157
|
);
|
|
153
158
|
instance.root.querySelector('[data-provider-dialog-subtitle]').textContent = tx(
|
|
154
|
-
|
|
155
|
-
'
|
|
159
|
+
"Configure experimental third-party integrations used by this project's future Codex runs. Compatibility varies by provider and gateway.",
|
|
160
|
+
'配置当前项目后续 Codex 任务使用的实验性第三方集成;兼容性取决于具体模型服务与网关。'
|
|
156
161
|
);
|
|
157
162
|
renderProviderList(instance);
|
|
158
163
|
renderDetail(instance);
|
|
@@ -165,10 +170,10 @@
|
|
|
165
170
|
const list = instance.root.querySelector('[data-provider-list]');
|
|
166
171
|
const rows = instance.catalog.providers.map(provider => {
|
|
167
172
|
const selected = provider.id === instance.selectedId;
|
|
168
|
-
const active = provider.id === instance
|
|
173
|
+
const active = provider.id === getCurrentProjectProviderId(instance);
|
|
169
174
|
const diagnosticCount = Object.values(provider.modelDiagnostics || {}).filter(item => item.status === 'tested').length;
|
|
170
175
|
const status = active
|
|
171
|
-
? tx('
|
|
176
|
+
? tx('Current project', '当前项目')
|
|
172
177
|
: diagnosticCount
|
|
173
178
|
? tx(`Tested ${diagnosticCount}/${Math.max(1, provider.models.length)}`, `已测试 ${diagnosticCount}/${Math.max(1, provider.models.length)}`)
|
|
174
179
|
: provider.kind === 'custom'
|
|
@@ -207,7 +212,7 @@
|
|
|
207
212
|
.map(model => model.id)
|
|
208
213
|
.filter(id => id !== draft.defaultModelId)
|
|
209
214
|
.join('\n');
|
|
210
|
-
const active = provider.id && provider.id === instance
|
|
215
|
+
const active = provider.id && provider.id === getCurrentProjectProviderId(instance);
|
|
211
216
|
const acceptedHost = getEndpointHost(draft.baseUrl);
|
|
212
217
|
const secretSavedAt = formatSecretSavedAt(instance, provider.secretUpdatedAt);
|
|
213
218
|
const disclosureSatisfied = Boolean(
|
|
@@ -219,9 +224,9 @@
|
|
|
219
224
|
<div class="codex-provider-detail-titleline">
|
|
220
225
|
<div>
|
|
221
226
|
<h3>${escapeHtml(draft.name || tx('Custom provider', '自定义模型服务'))}</h3>
|
|
222
|
-
<p>${escapeHtml(active ? tx('Used by new
|
|
227
|
+
<p>${escapeHtml(active ? tx('Used by new runs in this project', '当前项目的新任务将使用此服务') : tx('Saved provider profile', '已保存的模型服务配置'))}</p>
|
|
223
228
|
</div>
|
|
224
|
-
${active ? `<span class="codex-provider-active-badge">${escapeHtml(tx('
|
|
229
|
+
${active ? `<span class="codex-provider-active-badge">${escapeHtml(tx('Current project', '当前项目'))}</span>` : ''}
|
|
225
230
|
</div>
|
|
226
231
|
<div class="codex-provider-form-grid">
|
|
227
232
|
<label class="codex-provider-field">
|
|
@@ -440,7 +445,7 @@
|
|
|
440
445
|
|
|
441
446
|
function renderBuiltinDetail(instance, detail) {
|
|
442
447
|
const tx = instance.tx;
|
|
443
|
-
const active = instance
|
|
448
|
+
const active = getCurrentProjectProviderId(instance) === 'builtin';
|
|
444
449
|
detail.innerHTML = `
|
|
445
450
|
<div class="codex-provider-builtin">
|
|
446
451
|
<span class="codex-provider-builtin-mark" aria-hidden="true">
|
|
@@ -452,12 +457,19 @@
|
|
|
452
457
|
'使用本地 Codex CLI 管理的身份验证、模型目录和服务配置。'
|
|
453
458
|
))}</p>
|
|
454
459
|
${active
|
|
455
|
-
? `<span class="codex-provider-active-badge">${escapeHtml(tx('
|
|
456
|
-
: `<button type="button" class="codex-provider-primary-button" data-provider-action="activate-builtin">${escapeHtml(tx('Use
|
|
460
|
+
? `<span class="codex-provider-active-badge">${escapeHtml(tx('Current project', '当前项目'))}</span>`
|
|
461
|
+
: `<button type="button" class="codex-provider-primary-button" data-provider-action="activate-builtin">${escapeHtml(tx('Use for this project', '用于当前项目'))}</button>`}
|
|
457
462
|
</div>
|
|
458
463
|
`;
|
|
459
464
|
}
|
|
460
465
|
|
|
466
|
+
function getCurrentProjectProviderId(instance) {
|
|
467
|
+
const providerId = instance.callbacks.getCurrentProjectProviderId?.();
|
|
468
|
+
return typeof providerId === 'string' && providerId.trim()
|
|
469
|
+
? providerId.trim()
|
|
470
|
+
: 'builtin';
|
|
471
|
+
}
|
|
472
|
+
|
|
461
473
|
function renderFooter(instance) {
|
|
462
474
|
const actions = instance.root.querySelector('[data-provider-footer-actions]');
|
|
463
475
|
const provider = getSelectedProvider(instance);
|
|
@@ -466,7 +478,7 @@
|
|
|
466
478
|
actions.innerHTML = `<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(tx('Close', '关闭'))}</button>`;
|
|
467
479
|
return;
|
|
468
480
|
}
|
|
469
|
-
const active = provider.id && provider.id === instance
|
|
481
|
+
const active = provider.id && provider.id === getCurrentProjectProviderId(instance);
|
|
470
482
|
const isNew = !provider.id;
|
|
471
483
|
const destinationChanged = active && getCurrentBaseUrl(instance) !== provider.endpointDisclosureBaseUrl;
|
|
472
484
|
const actionState = getFooterActionState({
|
|
@@ -485,8 +497,8 @@
|
|
|
485
497
|
<span class="codex-provider-footer-spacer"></span>
|
|
486
498
|
<button type="button" class="codex-provider-secondary-button" data-provider-action="close">${escapeHtml(tx('Cancel', '取消'))}</button>
|
|
487
499
|
${actionState.showSave ? `<button type="button" class="codex-provider-secondary-button" data-provider-action="save"${saveDisabled}>${escapeHtml(tx('Save', '保存'))}</button>` : ''}
|
|
488
|
-
${actionState.showSaveAndUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="save-use"${saveDisabled}>${escapeHtml(tx('Save and use', '
|
|
489
|
-
${actionState.showUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="use"${useDisabled}>${escapeHtml(tx('Use', '
|
|
500
|
+
${actionState.showSaveAndUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="save-use"${saveDisabled}>${escapeHtml(tx('Save and use for this project', '保存并用于当前项目'))}</button>` : ''}
|
|
501
|
+
${actionState.showUse ? `<button type="button" class="codex-provider-primary-button" data-provider-action="use"${useDisabled}>${escapeHtml(tx('Use for this project', '用于当前项目'))}</button>` : ''}
|
|
490
502
|
`;
|
|
491
503
|
}
|
|
492
504
|
|
|
@@ -677,7 +689,7 @@
|
|
|
677
689
|
}
|
|
678
690
|
instance.selectedId = id;
|
|
679
691
|
instance.draft = id === '__new__'
|
|
680
|
-
?
|
|
692
|
+
? instance.Profiles.buildEmptyDraft()
|
|
681
693
|
: null;
|
|
682
694
|
instance.dirty = false;
|
|
683
695
|
delete instance.root.dataset.dirty;
|
|
@@ -689,7 +701,7 @@
|
|
|
689
701
|
}
|
|
690
702
|
|
|
691
703
|
function readContext(instance) {
|
|
692
|
-
const provider = getSelectedProvider(instance) ||
|
|
704
|
+
const provider = getSelectedProvider(instance) || instance.Profiles.buildEmptyDraft();
|
|
693
705
|
const get = name => instance.root.querySelector(`[data-provider-field="${name}"]`);
|
|
694
706
|
const defaultModelId = String(get('defaultModelId')?.value || '').trim();
|
|
695
707
|
const modelIds = [defaultModelId, ...String(get('additionalModels')?.value || '').split(/\r?\n/)]
|
|
@@ -759,7 +771,7 @@
|
|
|
759
771
|
|
|
760
772
|
function getSelectedProvider(instance) {
|
|
761
773
|
if (instance.selectedId === '__new__') {
|
|
762
|
-
return instance.draft ||
|
|
774
|
+
return instance.draft || instance.Profiles.buildEmptyDraft();
|
|
763
775
|
}
|
|
764
776
|
return instance.catalog.providers.find(provider => provider.id === instance.selectedId)
|
|
765
777
|
|| instance.catalog.providers.find(provider => provider.id === 'builtin');
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
sendBackgroundNative,
|
|
18
18
|
PANEL_STATE_BASE_KEY,
|
|
19
19
|
PROJECT_EDITOR_RESERVED_IDS,
|
|
20
|
-
STATUS_BADGE_CLASS
|
|
20
|
+
STATUS_BADGE_CLASS,
|
|
21
|
+
ProjectSessionCleanup, SessionPersistence, SessionState, StorageDb, StorageKeys, StorageMigration
|
|
21
22
|
} = deps;
|
|
22
|
-
const projectSessionCleanup =
|
|
23
|
+
const projectSessionCleanup = ProjectSessionCleanup.create({
|
|
23
24
|
tr, getAccountScopeId: getCachedAccountScopeId, showPluginConfirm, showPluginToast,
|
|
24
|
-
sendBackgroundNative, mutateProjectPanelState, renderRecentProjectsVariant
|
|
25
|
+
sendBackgroundNative, mutateProjectPanelState, renderRecentProjectsVariant,
|
|
26
|
+
SessionState, StorageDb, StorageMigration
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
// chrome.storage.local cache key (spec §5.6.3). The cache is keyed by
|
|
@@ -496,7 +498,6 @@
|
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
function sessionRecordDisplayTitle(record) {
|
|
499
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
500
501
|
if (record && typeof record.title === 'string' && record.title.trim()) {
|
|
501
502
|
return record.title.trim();
|
|
502
503
|
}
|
|
@@ -505,12 +506,11 @@
|
|
|
505
506
|
}
|
|
506
507
|
|
|
507
508
|
async function loadProjectSessionRecords(projectId) {
|
|
508
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
509
509
|
if (!StorageDb) {
|
|
510
510
|
return [];
|
|
511
511
|
}
|
|
512
512
|
var records = await StorageDb.getAllByIndex('sessions', 'projectId', projectId);
|
|
513
|
-
var Persistence =
|
|
513
|
+
var Persistence = SessionPersistence;
|
|
514
514
|
var deletedIds = await Persistence.getDeletedSessionIds(projectId);
|
|
515
515
|
var scope = getCachedAccountScopeId();
|
|
516
516
|
return (records || [])
|
|
@@ -521,7 +521,6 @@
|
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
async function renderProjectSessions(sessionsEl, projectId) {
|
|
524
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
525
524
|
sessionsEl.innerHTML = '';
|
|
526
525
|
var sessionsLoading = document.createElement('div');
|
|
527
526
|
sessionsLoading.className = 'recent-projects-loading';
|
|
@@ -544,7 +543,6 @@
|
|
|
544
543
|
}
|
|
545
544
|
|
|
546
545
|
function renderProjectSessionRow(sessionsEl, projectId, record) {
|
|
547
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
548
546
|
var running = Boolean(StorageDb)
|
|
549
547
|
&& settleDashboardRunStatus(StorageDb.derivePrimaryStatusBadge(record), record.updatedAt || record.lastActivityAt) === 'running';
|
|
550
548
|
var row = document.createElement('div');
|
|
@@ -614,17 +612,21 @@
|
|
|
614
612
|
}
|
|
615
613
|
|
|
616
614
|
async function activateSessionAndOpenProject(projectId, record) {
|
|
617
|
-
var Migration =
|
|
615
|
+
var Migration = StorageMigration;
|
|
618
616
|
try {
|
|
619
617
|
if (Migration && Migration.loadPrefs && Migration.savePrefs) {
|
|
620
|
-
var
|
|
618
|
+
var accountScopeId = getCachedAccountScopeId();
|
|
619
|
+
if (!accountScopeId) {
|
|
620
|
+
throw new Error('Account scope is unavailable.');
|
|
621
|
+
}
|
|
622
|
+
var prefs = await Migration.loadPrefs(accountScopeId, projectId);
|
|
621
623
|
prefs = prefs && typeof prefs === 'object' ? prefs : {};
|
|
622
624
|
var map = prefs.activeSessionByProject && typeof prefs.activeSessionByProject === 'object'
|
|
623
625
|
? prefs.activeSessionByProject
|
|
624
626
|
: {};
|
|
625
627
|
map[projectId] = record.id;
|
|
626
628
|
prefs.activeSessionByProject = map;
|
|
627
|
-
await Migration.savePrefs(prefs);
|
|
629
|
+
await Migration.savePrefs(prefs, accountScopeId, projectId);
|
|
628
630
|
}
|
|
629
631
|
} catch (_error) { /* fall through: opening the project still works */ }
|
|
630
632
|
openProjectFromRow(projectId);
|
|
@@ -636,7 +638,6 @@
|
|
|
636
638
|
// panel-state blob is deliberately left alone — an empty projectId would
|
|
637
639
|
// map getProjectStorageKey onto the global legacy key.
|
|
638
640
|
async function cleanupDeadProjectEntry(projectId) {
|
|
639
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
640
641
|
if (!StorageDb) {
|
|
641
642
|
return;
|
|
642
643
|
}
|
|
@@ -684,7 +685,6 @@
|
|
|
684
685
|
}
|
|
685
686
|
|
|
686
687
|
function projectPanelStateKey(projectId) {
|
|
687
|
-
var StorageKeys = window.CodexOverleafStorageKeys;
|
|
688
688
|
return StorageKeys.getProjectStorageKey(PANEL_STATE_BASE_KEY, 'https://www.overleaf.com/project/' + projectId);
|
|
689
689
|
}
|
|
690
690
|
|
|
@@ -694,7 +694,6 @@
|
|
|
694
694
|
// NOTE: if the project is open in another tab, that tab's in-memory state
|
|
695
695
|
// wins on its next save; the IndexedDB record mutation below still holds.
|
|
696
696
|
async function mutateProjectPanelState(projectId, mutate) {
|
|
697
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
698
697
|
var key = projectPanelStateKey(projectId);
|
|
699
698
|
var stored = await chrome.storage.local.get(key);
|
|
700
699
|
var blob = stored && stored[key];
|
|
@@ -709,8 +708,6 @@
|
|
|
709
708
|
}
|
|
710
709
|
|
|
711
710
|
async function deleteDashboardSession(sessionsEl, projectId, record) {
|
|
712
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
713
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
714
711
|
// v1.8.1 P1b: ANY record whose stored badge is 'running' gets the
|
|
715
712
|
// stronger confirm — even one the 30-minute heuristic settled to
|
|
716
713
|
// interrupted. Activity timestamps are only bumped once a minute during
|
|
@@ -731,7 +728,7 @@
|
|
|
731
728
|
if (!approved) {
|
|
732
729
|
return;
|
|
733
730
|
}
|
|
734
|
-
await
|
|
731
|
+
await SessionPersistence.addTombstones(projectId, [record.id]);
|
|
735
732
|
try {
|
|
736
733
|
await mutateProjectPanelState(projectId, function (state) {
|
|
737
734
|
return SessionState.deleteSession(state, record.id);
|
|
@@ -773,8 +770,6 @@
|
|
|
773
770
|
}
|
|
774
771
|
|
|
775
772
|
function beginDashboardSessionRename(sessionsEl, projectId, record, row, titleEl) {
|
|
776
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
777
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
778
773
|
if (row.querySelector('input')) {
|
|
779
774
|
return;
|
|
780
775
|
}
|
|
@@ -816,8 +811,6 @@
|
|
|
816
811
|
}
|
|
817
812
|
|
|
818
813
|
async function commitDashboardSessionRename(sessionsEl, projectId, record, rawTitle) {
|
|
819
|
-
var SessionState = window.CodexOverleafSessionState;
|
|
820
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
821
814
|
if (!SessionState) {
|
|
822
815
|
return;
|
|
823
816
|
}
|
|
@@ -963,9 +956,8 @@
|
|
|
963
956
|
// there really is more than one page of projects.
|
|
964
957
|
var pageLimit = 10;
|
|
965
958
|
try {
|
|
966
|
-
var StorageDb = window.CodexOverleafStorageDb;
|
|
967
959
|
if (StorageDb) {
|
|
968
|
-
var deletedSessionIdsByProject = await
|
|
960
|
+
var deletedSessionIdsByProject = await SessionPersistence.loadTombstones();
|
|
969
961
|
rows = await StorageDb.listRecentProjectsAcrossAccount({
|
|
970
962
|
accountScopeId: accountScopeId,
|
|
971
963
|
limit: showAll ? 500 : pageLimit + 1,
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function appendToView(event, view) {
|
|
50
|
-
const text =
|
|
50
|
+
const text = deps.RunGuidanceView?.getGuidanceText(event);
|
|
51
51
|
if (!text) return false;
|
|
52
52
|
const guidance = view.guidance || view.root?.querySelector('[data-run-guidance]');
|
|
53
53
|
const target = view.terminalStatus ? guidance : view.events;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
if (typeof module === 'object' && module.exports) {
|
|
3
3
|
module.exports = factory(require('../shared/runInputQueue'));
|
|
4
4
|
} else {
|
|
5
|
-
root.
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('RunQueueScheduler', ['RunInputQueue'], factory);
|
|
6
6
|
}
|
|
7
7
|
})(typeof window !== 'undefined' ? window : globalThis, function runQueueSchedulerFactory(Queue) {
|
|
8
8
|
'use strict';
|
|
@@ -19,18 +19,49 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function afterSettlement({ sessionId, status, queueItemId = '', continueQueue = false } = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
const originalQueue = options.getQueue?.(sessionId) || [];
|
|
23
|
+
const shouldContinue = status === 'completed' || continueQueue;
|
|
24
|
+
const shouldRemove = Boolean(queueItemId && shouldContinue);
|
|
25
|
+
const claimedItem = shouldRemove
|
|
26
|
+
? originalQueue.find(item => item?.id === queueItemId)
|
|
27
|
+
: null;
|
|
28
|
+
if (shouldRemove) {
|
|
29
|
+
await options.persist?.({
|
|
30
|
+
queueMutation: {
|
|
31
|
+
type: 'remove',
|
|
32
|
+
sessionId,
|
|
33
|
+
itemId: queueItemId,
|
|
34
|
+
claimToken: claimedItem?.claimToken
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const latestQueue = options.getQueue?.(sessionId) || originalQueue;
|
|
38
|
+
options.setQueue?.(
|
|
39
|
+
sessionId,
|
|
40
|
+
dequeue(latestQueue, queueItemId, {
|
|
41
|
+
sessionId,
|
|
42
|
+
reason: status === 'completed' ? 'completed' : 'continue_queue'
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
} else if (!shouldContinue) {
|
|
46
|
+
const paused = originalQueue.length
|
|
47
|
+
? Queue.pauseAll(originalQueue, status || 'run_did_not_complete')
|
|
48
|
+
: originalQueue;
|
|
49
|
+
options.setQueue?.(sessionId, paused);
|
|
50
|
+
try {
|
|
51
|
+
await options.persist?.({
|
|
52
|
+
queueMutation: { type: 'transition', sessionId }
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const latestQueue = options.getQueue?.(sessionId) || paused;
|
|
56
|
+
options.setQueue?.(
|
|
57
|
+
sessionId,
|
|
58
|
+
Queue.pauseAll(latestQueue, status || 'run_did_not_complete')
|
|
59
|
+
);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
29
62
|
}
|
|
30
|
-
options.setQueue?.(sessionId, queue);
|
|
31
|
-
await options.persist?.();
|
|
32
63
|
options.onChange?.(sessionId);
|
|
33
|
-
if (
|
|
64
|
+
if (shouldContinue) {
|
|
34
65
|
scheduleOne(sessionId);
|
|
35
66
|
}
|
|
36
67
|
}
|
|
@@ -39,15 +70,33 @@
|
|
|
39
70
|
if (scheduling || options.isRunning?.() || options.canStart?.(sessionId) === false) {
|
|
40
71
|
return false;
|
|
41
72
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
});
|
|
45
|
-
if (!claimed.ok) {
|
|
73
|
+
const initialQueue = options.getQueue?.(sessionId) || [];
|
|
74
|
+
if (!initialQueue.some(item => item?.status === 'queued')) {
|
|
46
75
|
return false;
|
|
47
76
|
}
|
|
48
77
|
scheduling = true;
|
|
49
|
-
|
|
50
|
-
Promise.resolve(options.
|
|
78
|
+
let claimed = null;
|
|
79
|
+
Promise.resolve(options.prepareClaim?.(sessionId))
|
|
80
|
+
.then(() => {
|
|
81
|
+
claimed = Queue.claimNext(options.getQueue?.(sessionId) || initialQueue, {
|
|
82
|
+
randomUUID: options.randomUUID,
|
|
83
|
+
resolveExecutionSnapshot: options.resolveExecutionSnapshot
|
|
84
|
+
});
|
|
85
|
+
if (!claimed.ok) {
|
|
86
|
+
const error = new Error(claimed.error?.message || claimed.error?.code || 'queue_claim_failed');
|
|
87
|
+
error.code = claimed.error?.code || 'queue_claim_failed';
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
options.setQueue?.(sessionId, claimed.queue);
|
|
91
|
+
return options.persist?.({
|
|
92
|
+
queueMutation: {
|
|
93
|
+
type: 'claim',
|
|
94
|
+
sessionId,
|
|
95
|
+
itemId: claimed.item.id,
|
|
96
|
+
claimToken: claimed.item.claimToken
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
})
|
|
51
100
|
.then(() => {
|
|
52
101
|
options.onChange?.(sessionId);
|
|
53
102
|
scheduling = false;
|
|
@@ -55,27 +104,64 @@
|
|
|
55
104
|
})
|
|
56
105
|
.catch(error => {
|
|
57
106
|
scheduling = false;
|
|
58
|
-
const paused = Queue.pauseAll(
|
|
107
|
+
const paused = Queue.pauseAll(
|
|
108
|
+
options.getQueue?.(sessionId) || claimed?.queue || initialQueue,
|
|
109
|
+
error?.code || 'start_failed'
|
|
110
|
+
);
|
|
59
111
|
options.setQueue?.(sessionId, paused);
|
|
60
112
|
options.onChange?.(sessionId);
|
|
61
|
-
return options.persist?.(
|
|
113
|
+
return Promise.resolve(options.persist?.({
|
|
114
|
+
queueMutation: claimed?.item?.id
|
|
115
|
+
? {
|
|
116
|
+
type: 'release',
|
|
117
|
+
sessionId,
|
|
118
|
+
itemId: claimed.item.id,
|
|
119
|
+
claimToken: claimed.item.claimToken
|
|
120
|
+
}
|
|
121
|
+
: { type: 'transition', sessionId }
|
|
122
|
+
})).catch(() => undefined);
|
|
62
123
|
});
|
|
63
124
|
return true;
|
|
64
125
|
}
|
|
65
126
|
|
|
66
127
|
async function markExecuting(sessionId, itemId, _runId) {
|
|
67
128
|
const queue = options.getQueue?.(sessionId) || [];
|
|
129
|
+
const item = queue.find(entry => entry?.id === itemId);
|
|
130
|
+
try {
|
|
131
|
+
await options.persist?.({
|
|
132
|
+
queueMutation: { type: 'remove', sessionId, itemId, claimToken: item?.claimToken }
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
const latestQueue = options.getQueue?.(sessionId) || queue;
|
|
136
|
+
options.setQueue?.(
|
|
137
|
+
sessionId,
|
|
138
|
+
Queue.pauseAll(latestQueue, error?.code || 'queue_promotion_persistence_failed')
|
|
139
|
+
);
|
|
140
|
+
options.onChange?.(sessionId);
|
|
141
|
+
try {
|
|
142
|
+
await options.persist?.({
|
|
143
|
+
queueMutation: { type: 'restore', sessionId, itemId, claimToken: item?.claimToken }
|
|
144
|
+
});
|
|
145
|
+
} catch (recoveryError) {
|
|
146
|
+
if (error && typeof error === 'object') {
|
|
147
|
+
error.persistenceRecoveryError = recoveryError?.message || String(recoveryError);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
const latestQueue = options.getQueue?.(sessionId) || queue;
|
|
68
153
|
options.setQueue?.(
|
|
69
154
|
sessionId,
|
|
70
|
-
dequeue(
|
|
155
|
+
dequeue(latestQueue, itemId, { sessionId, runId: _runId, reason: 'executing' })
|
|
71
156
|
);
|
|
72
|
-
await options.persist?.();
|
|
73
157
|
options.onChange?.(sessionId);
|
|
74
158
|
}
|
|
75
159
|
|
|
76
160
|
async function resume(sessionId) {
|
|
77
161
|
options.setQueue?.(sessionId, Queue.resumeAll(options.getQueue?.(sessionId) || []));
|
|
78
|
-
await options.persist?.(
|
|
162
|
+
await options.persist?.({
|
|
163
|
+
queueMutation: { type: 'transition', sessionId }
|
|
164
|
+
});
|
|
79
165
|
options.onChange?.(sessionId);
|
|
80
166
|
scheduleOne(sessionId);
|
|
81
167
|
}
|
|
@@ -88,7 +174,7 @@
|
|
|
88
174
|
const scheduler = create({
|
|
89
175
|
getQueue: sessionId => options.getSession?.(sessionId)?.pendingInputs || [],
|
|
90
176
|
setQueue: (sessionId, pendingInputs) => options.setQueue?.(sessionId, pendingInputs),
|
|
91
|
-
persist:
|
|
177
|
+
persist: persistenceOptions => options.save?.(persistenceOptions),
|
|
92
178
|
isRunning: () => Boolean(options.getCurrentRun?.()),
|
|
93
179
|
canStart: sessionId => options.canStart?.(sessionId) !== false,
|
|
94
180
|
start: (item, sessionId) => startQueuedInput(item, sessionId),
|
|
@@ -97,6 +183,8 @@
|
|
|
97
183
|
sessionId: context.sessionId,
|
|
98
184
|
recordId: item.sourceRunId || ''
|
|
99
185
|
}),
|
|
186
|
+
prepareClaim: options.prepareClaim,
|
|
187
|
+
resolveExecutionSnapshot: options.resolveExecutionSnapshot,
|
|
100
188
|
randomUUID: options.randomUUID
|
|
101
189
|
});
|
|
102
190
|
|
|
@@ -128,7 +216,9 @@
|
|
|
128
216
|
// active turn, or until the scheduler promotes it to the next run.
|
|
129
217
|
options.clearTask?.();
|
|
130
218
|
render();
|
|
131
|
-
options.saveSoon?.(
|
|
219
|
+
options.saveSoon?.({
|
|
220
|
+
queueMutation: { type: 'enqueue', sessionId: session.id, itemId: result.item.id }
|
|
221
|
+
});
|
|
132
222
|
}
|
|
133
223
|
|
|
134
224
|
async function guide(itemId) {
|
|
@@ -142,7 +232,9 @@
|
|
|
142
232
|
}
|
|
143
233
|
options.setQueue?.(session.id, Queue.markSteering(session.pendingInputs, itemId));
|
|
144
234
|
render();
|
|
145
|
-
await options.save?.(
|
|
235
|
+
await options.save?.({
|
|
236
|
+
queueMutation: { type: 'transition', sessionId: session.id, itemId }
|
|
237
|
+
});
|
|
146
238
|
try {
|
|
147
239
|
const response = await options.sendSteer?.({
|
|
148
240
|
requestId: run.nativeRequestId,
|
|
@@ -174,7 +266,11 @@
|
|
|
174
266
|
options.toast?.(response?.error?.message || options.tr?.('queuedInputGuideFailed'), 'warning');
|
|
175
267
|
}
|
|
176
268
|
render();
|
|
177
|
-
await options.save?.(
|
|
269
|
+
await options.save?.({
|
|
270
|
+
queueMutation: response?.ok
|
|
271
|
+
? { type: 'remove', sessionId: session.id, itemId }
|
|
272
|
+
: { type: 'transition', sessionId: session.id, itemId }
|
|
273
|
+
});
|
|
178
274
|
} catch (error) {
|
|
179
275
|
const latest = options.getSession?.(session.id);
|
|
180
276
|
const paused = (latest?.pendingInputs || []).map(entry => entry.id === itemId
|
|
@@ -183,7 +279,9 @@
|
|
|
183
279
|
options.setQueue?.(session.id, Queue.normalizeQueue(paused));
|
|
184
280
|
options.toast?.(error?.message || options.tr?.('queuedInputGuideFailed'), 'warning');
|
|
185
281
|
render();
|
|
186
|
-
await options.save?.(
|
|
282
|
+
await options.save?.({
|
|
283
|
+
queueMutation: { type: 'transition', sessionId: session.id, itemId }
|
|
284
|
+
});
|
|
187
285
|
}
|
|
188
286
|
}
|
|
189
287
|
|
|
@@ -201,7 +299,9 @@
|
|
|
201
299
|
}
|
|
202
300
|
options.setQueue?.(session.id, Queue.remove(session.pendingInputs || [], itemId));
|
|
203
301
|
render();
|
|
204
|
-
options.saveSoon?.(
|
|
302
|
+
options.saveSoon?.({
|
|
303
|
+
queueMutation: { type: 'remove', sessionId: session.id, itemId }
|
|
304
|
+
});
|
|
205
305
|
}
|
|
206
306
|
|
|
207
307
|
function resume() {
|
|
@@ -242,7 +342,7 @@
|
|
|
242
342
|
error.code = 'queue_session_not_active';
|
|
243
343
|
throw error;
|
|
244
344
|
}
|
|
245
|
-
options.applyQueuedInput?.(item);
|
|
345
|
+
await options.applyQueuedInput?.(item);
|
|
246
346
|
await options.run?.(item);
|
|
247
347
|
}
|
|
248
348
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
(function initCodexOverleafRunSettlementPersistence(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafRunSettlementPersistence = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function runSettlementPersistenceFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function create(options = {}) {
|
|
9
|
+
const Settlement = options.writebackSettlement;
|
|
10
|
+
|
|
11
|
+
function findTarget(recordId, sessionId) {
|
|
12
|
+
return typeof options.findRunRecord === 'function'
|
|
13
|
+
? options.findRunRecord(recordId, sessionId)
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function applyToCurrentRun(settlement) {
|
|
18
|
+
const view = options.getCurrentRunView?.();
|
|
19
|
+
if (!settlement || !view?.recordId) return;
|
|
20
|
+
const record = findTarget(view.recordId, view.sessionId);
|
|
21
|
+
if (!record) return;
|
|
22
|
+
Object.assign(record, Settlement.applySettlementTransition(record, settlement));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function settleMirrorRefresh(result = {}) {
|
|
26
|
+
const record = findTarget(result.recordId, result.sessionId);
|
|
27
|
+
if (!record) return;
|
|
28
|
+
if (result.runProjectId && result.runProjectId !== options.getCurrentProjectId?.()) return;
|
|
29
|
+
const existing = record.settlement && typeof record.settlement === 'object'
|
|
30
|
+
? record.settlement
|
|
31
|
+
: record.settlementFacts;
|
|
32
|
+
if (!existing || typeof existing !== 'object') return;
|
|
33
|
+
record.settlement = Settlement.compactSettlementFacts({
|
|
34
|
+
...existing,
|
|
35
|
+
evidence: {
|
|
36
|
+
...(existing.evidence || {}),
|
|
37
|
+
mirrored: result.state
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
delete record.settlementFacts;
|
|
41
|
+
options.saveStateSoon?.();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
applyToCurrentRun,
|
|
46
|
+
settleMirrorRefresh
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Object.freeze({ create });
|
|
51
|
+
});
|