codex-overleaf-link 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +53 -69
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/contentRuntime.js +650 -1449
- 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 +41370 -0
- package/extension/src/content/generated/content.bundle.meta.json +102 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +5 -7
- 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 +7 -5
- 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 +11 -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 +91 -23
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +6 -1
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +8 -5
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- 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 +177 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +168 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +181 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +238 -0
- package/extension/src/shared/sessionState.js +64 -48
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +19 -5
- 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 +143 -0
- package/extension/src/shared/writebackSettlement.js +705 -0
- 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/mirrorWorkspace.js +15 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +3 -3
- package/package.json +13 -5
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
(function initPostNavigationSettlementPersistence(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafPostNavigationSettlementPersistence = api;
|
|
5
|
+
})(typeof window !== 'undefined' ? window : globalThis, function postNavigationSettlementPersistenceFactory() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
async function persist(options = {}) {
|
|
9
|
+
if (options.__insideScopedTransaction !== true) {
|
|
10
|
+
const accountScopeId = String(options.accountScopeId || '').trim();
|
|
11
|
+
const projectId = String(options.projectId || '').trim();
|
|
12
|
+
if (!accountScopeId) {
|
|
13
|
+
return { ok: false, reason: 'account_scope_unavailable' };
|
|
14
|
+
}
|
|
15
|
+
if (!projectId) {
|
|
16
|
+
return { ok: false, reason: 'project_scope_unavailable' };
|
|
17
|
+
}
|
|
18
|
+
if (!options.PersistenceCoordinator
|
|
19
|
+
|| typeof options.PersistenceCoordinator.commitDetached !== 'function') {
|
|
20
|
+
return { ok: false, reason: 'scoped_persistence_unavailable' };
|
|
21
|
+
}
|
|
22
|
+
const committed = await options.PersistenceCoordinator.commitDetached(
|
|
23
|
+
projectId,
|
|
24
|
+
accountScopeId,
|
|
25
|
+
{ rebase: true },
|
|
26
|
+
() => persist({
|
|
27
|
+
...options,
|
|
28
|
+
__insideScopedTransaction: true
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
if (!committed || typeof committed !== 'object') {
|
|
32
|
+
return { ok: false, reason: 'scoped_persistence_invalid_result' };
|
|
33
|
+
}
|
|
34
|
+
if (committed.ok !== true) return committed;
|
|
35
|
+
if (!Object.prototype.hasOwnProperty.call(committed, 'value')) return committed;
|
|
36
|
+
return committed.value && typeof committed.value === 'object'
|
|
37
|
+
? committed.value
|
|
38
|
+
: { ok: false, reason: 'scoped_persistence_action_result_unavailable' };
|
|
39
|
+
}
|
|
40
|
+
const {
|
|
41
|
+
StorageDb,
|
|
42
|
+
WritebackSettlement,
|
|
43
|
+
sessionId,
|
|
44
|
+
runId,
|
|
45
|
+
status,
|
|
46
|
+
statusText,
|
|
47
|
+
finishedAt,
|
|
48
|
+
settlementResult
|
|
49
|
+
} = options;
|
|
50
|
+
if (!StorageDb || !WritebackSettlement || !sessionId || !runId || !status) {
|
|
51
|
+
return { ok: false, reason: 'invalid_post_navigation_settlement' };
|
|
52
|
+
}
|
|
53
|
+
const record = await StorageDb.getRecord('sessions', sessionId);
|
|
54
|
+
if (!record) return { ok: false, reason: 'session_not_found' };
|
|
55
|
+
if (String(record.accountScopeId || '').trim() !== String(options.accountScopeId || '').trim()) {
|
|
56
|
+
return { ok: false, reason: 'account_scope_mismatch' };
|
|
57
|
+
}
|
|
58
|
+
if (String(record.projectId || '').trim() !== String(options.projectId || '').trim()) {
|
|
59
|
+
return { ok: false, reason: 'project_scope_mismatch' };
|
|
60
|
+
}
|
|
61
|
+
const runs = Array.isArray(record.runs) ? [...record.runs] : [];
|
|
62
|
+
const runIndex = runs.findIndex(run => run && run.id === runId);
|
|
63
|
+
if (runIndex < 0) return { ok: false, reason: 'run_not_found' };
|
|
64
|
+
|
|
65
|
+
let transitioned = settlementResult
|
|
66
|
+
? WritebackSettlement.applySettlementTransition(runs[runIndex], settlementResult)
|
|
67
|
+
: { ...runs[runIndex] };
|
|
68
|
+
transitioned = WritebackSettlement.applySettlementTransition(
|
|
69
|
+
transitioned,
|
|
70
|
+
WritebackSettlement.transitionPostNavigationStatus({
|
|
71
|
+
status,
|
|
72
|
+
statusText,
|
|
73
|
+
finishedAt: finishedAt || transitioned.finishedAt || new Date().toISOString()
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
runs[runIndex] = transitioned;
|
|
77
|
+
|
|
78
|
+
const now = options.now || new Date().toISOString();
|
|
79
|
+
record.runs = runs;
|
|
80
|
+
record.lastActivityAt = now;
|
|
81
|
+
record.updatedAt = now;
|
|
82
|
+
await StorageDb.putRecord('sessions', record);
|
|
83
|
+
return { ok: true, run: transitioned };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function persistRequired(options = {}) {
|
|
87
|
+
const result = await persist(options);
|
|
88
|
+
if (result?.ok === true) return result;
|
|
89
|
+
const reason = String(result?.reason || 'unknown_failure');
|
|
90
|
+
throw Object.assign(
|
|
91
|
+
new Error(`Post-navigation settlement persistence failed: ${reason}`),
|
|
92
|
+
{
|
|
93
|
+
code: 'post_navigation_settlement_persistence_failed',
|
|
94
|
+
reason
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return Object.freeze({ persist, persistRequired });
|
|
100
|
+
});
|
|
@@ -30,24 +30,16 @@
|
|
|
30
30
|
const nextSpeedTier = deps.readSelectedSpeed?.() || state.speedTier;
|
|
31
31
|
const runSelection = deps.getRunSelection?.(nextProviderId);
|
|
32
32
|
const nextProviderRevision = Number(runSelection?.providerRevision || 0);
|
|
33
|
+
const providerConfigurationChanged = nextProviderId === (state.providerId || 'builtin')
|
|
34
|
+
&& nextProviderRevision !== Number(state.providerRevision || 0);
|
|
35
|
+
const resetProviderThread = providerChanged || providerConfigurationChanged;
|
|
33
36
|
const sessions = (state.sessions || []).map(session => ({
|
|
34
37
|
...session,
|
|
35
38
|
providerId: nextProviderId,
|
|
36
39
|
model: nextModel,
|
|
37
40
|
reasoningEffort: nextReasoningEffort,
|
|
38
41
|
speedTier: nextSpeedTier,
|
|
39
|
-
codexThreadId:
|
|
40
|
-
pendingInputs: (session.pendingInputs || []).map(item => ({
|
|
41
|
-
...item,
|
|
42
|
-
payload: {
|
|
43
|
-
...(item.payload || {}),
|
|
44
|
-
providerId: nextProviderId,
|
|
45
|
-
providerRevision: nextProviderRevision,
|
|
46
|
-
model: nextModel,
|
|
47
|
-
reasoningEffort: nextReasoningEffort,
|
|
48
|
-
speedTier: nextSpeedTier
|
|
49
|
-
}
|
|
50
|
-
}))
|
|
42
|
+
codexThreadId: resetProviderThread ? '' : session.codexThreadId
|
|
51
43
|
}));
|
|
52
44
|
deps.setState?.(deps.normalizeState({
|
|
53
45
|
...state,
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async function clearProjectSessions(projectId, projectName) {
|
|
26
|
-
const StorageDb =
|
|
27
|
-
const SessionState =
|
|
26
|
+
const StorageDb = deps.StorageDb;
|
|
27
|
+
const SessionState = deps.SessionState;
|
|
28
28
|
if (!StorageDb || !SessionState) {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
if (!approved) {
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
|
-
const Migration =
|
|
52
|
+
const Migration = deps.StorageMigration;
|
|
53
53
|
if (Migration?.addSessionTombstones) {
|
|
54
54
|
await Migration.addSessionTombstones(projectId, records.map(record => record.id));
|
|
55
55
|
}
|
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
function create(options = {}) {
|
|
5
|
-
const Profiles =
|
|
5
|
+
const Profiles = options.ProviderProfiles;
|
|
6
|
+
const Dialog = options.ProviderSettingsDialog;
|
|
7
|
+
if (!Profiles || !Dialog?.create) {
|
|
8
|
+
throw new Error('Provider settings require explicit profile and dialog dependencies.');
|
|
9
|
+
}
|
|
6
10
|
const instance = {
|
|
11
|
+
Profiles,
|
|
7
12
|
tx: options.tx || ((english) => english),
|
|
8
13
|
sendBackgroundNative: options.sendBackgroundNative,
|
|
9
14
|
getSettingsPanelInstance: options.getSettingsPanelInstance || (() => null),
|
|
@@ -21,12 +26,24 @@
|
|
|
21
26
|
channel: null,
|
|
22
27
|
dialog: null
|
|
23
28
|
};
|
|
24
|
-
|
|
29
|
+
const projectProviderChanged = options.onProviderChanged
|
|
25
30
|
|| ((catalog, change) => reconcileProviderSelection(instance, catalog, change));
|
|
26
|
-
instance.
|
|
31
|
+
instance.onProviderChanged = async (catalog, change) => {
|
|
32
|
+
const previousProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
33
|
+
const result = await projectProviderChanged(catalog, change);
|
|
34
|
+
const nextProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
35
|
+
updateSettingsSummary(instance);
|
|
36
|
+
if (previousProviderId !== nextProviderId && instance.dialog?.isOpen?.()) {
|
|
37
|
+
instance.dialog.setCatalog(instance.catalog, nextProviderId);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
instance.dialog = Dialog.create({
|
|
27
42
|
document: options.document || document,
|
|
28
43
|
tx: instance.tx,
|
|
44
|
+
ProviderProfiles: Profiles,
|
|
29
45
|
callbacks: {
|
|
46
|
+
getCurrentProjectProviderId: () => instance.getSelectedProviderId() || 'builtin',
|
|
30
47
|
onTest: context => testProvider(instance, context),
|
|
31
48
|
onCancelTest: () => cancelProviderTest(instance),
|
|
32
49
|
onSave: (context, action) => saveProvider(instance, context, action),
|
|
@@ -135,7 +152,7 @@
|
|
|
135
152
|
await ensureLoadedWithRetry(instance);
|
|
136
153
|
} catch (_error) {
|
|
137
154
|
return instance.refreshModelOptions(
|
|
138
|
-
|
|
155
|
+
instance.Profiles.buildRunSelection(instance.catalog, instance.getSelectedProviderId())
|
|
139
156
|
);
|
|
140
157
|
}
|
|
141
158
|
return reconcileProviderSelection(instance, instance.catalog, { forceSessionRefresh: true });
|
|
@@ -143,12 +160,18 @@
|
|
|
143
160
|
|
|
144
161
|
async function reconcileProviderSelection(instance, catalog, change = {}) {
|
|
145
162
|
const previousProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
146
|
-
|
|
163
|
+
let requestedProviderId = typeof change.sessionProviderId === 'string' && change.sessionProviderId
|
|
147
164
|
? change.sessionProviderId
|
|
148
165
|
: previousProviderId;
|
|
166
|
+
const requestedProviderAvailable = requestedProviderId === 'builtin'
|
|
167
|
+
|| Boolean(instance.Profiles.getProviderById(catalog, requestedProviderId));
|
|
168
|
+
const selectedProviderWasRemoved = !requestedProviderAvailable;
|
|
169
|
+
if (selectedProviderWasRemoved) {
|
|
170
|
+
requestedProviderId = 'builtin';
|
|
171
|
+
}
|
|
149
172
|
const projectProviderChanged = requestedProviderId !== previousProviderId;
|
|
150
|
-
if (projectProviderChanged && change.providerSwitchApproved !== true) {
|
|
151
|
-
const requestedProvider =
|
|
173
|
+
if (projectProviderChanged && !selectedProviderWasRemoved && change.providerSwitchApproved !== true) {
|
|
174
|
+
const requestedProvider = instance.Profiles.getProviderById(catalog, requestedProviderId);
|
|
152
175
|
const approved = await instance.confirmProviderSwitch({
|
|
153
176
|
providerId: requestedProviderId,
|
|
154
177
|
providerName: requestedProvider?.name || requestedProviderId
|
|
@@ -158,13 +181,14 @@
|
|
|
158
181
|
}
|
|
159
182
|
}
|
|
160
183
|
const changedProviderIds = Array.isArray(change.changedProviderIds) ? change.changedProviderIds : [];
|
|
161
|
-
|
|
184
|
+
const providerConfigurationChanged = changedProviderIds.includes(requestedProviderId);
|
|
185
|
+
if (!change.forceSessionRefresh && !projectProviderChanged && !providerConfigurationChanged) {
|
|
162
186
|
return;
|
|
163
187
|
}
|
|
164
188
|
const loadResult = change.modelsPreloaded === true
|
|
165
189
|
? { stale: false, selectedModel: change.preloadedModelId || '' }
|
|
166
190
|
: await instance.refreshModelOptions(
|
|
167
|
-
|
|
191
|
+
instance.Profiles.buildRunSelection(catalog, requestedProviderId),
|
|
168
192
|
{ persist: false }
|
|
169
193
|
);
|
|
170
194
|
if (loadResult?.stale || loadResult?.error) {
|
|
@@ -175,7 +199,7 @@
|
|
|
175
199
|
error.details = loadResult?.error || {};
|
|
176
200
|
throw error;
|
|
177
201
|
}
|
|
178
|
-
if (projectProviderChanged) {
|
|
202
|
+
if (projectProviderChanged || providerConfigurationChanged) {
|
|
179
203
|
await instance.setSelectedProviderId(requestedProviderId, loadResult?.selectedModel || '');
|
|
180
204
|
}
|
|
181
205
|
await instance.persistInputs();
|
|
@@ -185,7 +209,7 @@
|
|
|
185
209
|
|
|
186
210
|
async function prepareProviderActivation(instance, providerId) {
|
|
187
211
|
const previousProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
188
|
-
const provider =
|
|
212
|
+
const provider = instance.Profiles.getProviderById(instance.catalog, providerId);
|
|
189
213
|
if (providerId !== previousProviderId) {
|
|
190
214
|
const approved = await instance.confirmProviderSwitch({
|
|
191
215
|
providerId,
|
|
@@ -196,13 +220,13 @@
|
|
|
196
220
|
}
|
|
197
221
|
}
|
|
198
222
|
const result = await instance.refreshModelOptions(
|
|
199
|
-
|
|
223
|
+
instance.Profiles.buildRunSelection(instance.catalog, providerId),
|
|
200
224
|
{ persist: false }
|
|
201
225
|
);
|
|
202
226
|
if (result?.stale || result?.error) {
|
|
203
227
|
if (providerId !== previousProviderId) {
|
|
204
228
|
await instance.refreshModelOptions(
|
|
205
|
-
|
|
229
|
+
instance.Profiles.buildRunSelection(instance.catalog, previousProviderId),
|
|
206
230
|
{ persist: false }
|
|
207
231
|
).catch(() => {});
|
|
208
232
|
}
|
|
@@ -293,7 +317,11 @@
|
|
|
293
317
|
selectedId: result.savedProviderId
|
|
294
318
|
});
|
|
295
319
|
await notifyChanged(instance, applied.change, action.activate === true
|
|
296
|
-
? {
|
|
320
|
+
? {
|
|
321
|
+
sessionProviderId: result.savedProviderId,
|
|
322
|
+
providerSwitchApproved: true,
|
|
323
|
+
requireProjection: true
|
|
324
|
+
}
|
|
297
325
|
: {});
|
|
298
326
|
instance.dialog.setBusy('', '');
|
|
299
327
|
const fellBackToBuiltin = previousCatalog.activeProviderId === result.savedProviderId
|
|
@@ -389,7 +417,7 @@
|
|
|
389
417
|
} catch (error) {
|
|
390
418
|
if (preparation?.previousProviderId && preparation.previousProviderId !== context.profileId) {
|
|
391
419
|
await instance.refreshModelOptions(
|
|
392
|
-
|
|
420
|
+
instance.Profiles.buildRunSelection(instance.catalog, preparation.previousProviderId),
|
|
393
421
|
{ persist: false }
|
|
394
422
|
).catch(() => {});
|
|
395
423
|
}
|
|
@@ -423,7 +451,7 @@
|
|
|
423
451
|
} catch (error) {
|
|
424
452
|
if (preparation?.previousProviderId && preparation.previousProviderId !== 'builtin') {
|
|
425
453
|
await instance.refreshModelOptions(
|
|
426
|
-
|
|
454
|
+
instance.Profiles.buildRunSelection(instance.catalog, preparation.previousProviderId),
|
|
427
455
|
{ persist: false }
|
|
428
456
|
).catch(() => {});
|
|
429
457
|
}
|
|
@@ -448,7 +476,7 @@
|
|
|
448
476
|
}
|
|
449
477
|
|
|
450
478
|
function applyCatalog(instance, result, options = {}) {
|
|
451
|
-
const nextCatalog =
|
|
479
|
+
const nextCatalog = instance.Profiles.normalizeCatalog(result);
|
|
452
480
|
if (instance.loaded && nextCatalog.storeRevision < instance.catalog.storeRevision) {
|
|
453
481
|
return { applied: false, change: emptyCatalogChange() };
|
|
454
482
|
}
|
|
@@ -463,19 +491,19 @@
|
|
|
463
491
|
instance.dialog.setCatalog(instance.catalog, options.selectedId);
|
|
464
492
|
}
|
|
465
493
|
}
|
|
466
|
-
return { applied: true, change: describeCatalogChange(previousCatalog, nextCatalog) };
|
|
494
|
+
return { applied: true, change: describeCatalogChange(instance, previousCatalog, nextCatalog) };
|
|
467
495
|
}
|
|
468
496
|
|
|
469
497
|
function updateSettingsSummary(instance, override = {}) {
|
|
470
498
|
const selectedProviderId = instance.getSelectedProviderId() || 'builtin';
|
|
471
|
-
const catalogProvider =
|
|
499
|
+
const catalogProvider = instance.Profiles.getProviderById(
|
|
472
500
|
instance.catalog,
|
|
473
501
|
selectedProviderId
|
|
474
502
|
);
|
|
475
503
|
const selectedProvider = catalogProvider?.id === selectedProviderId
|
|
476
504
|
? catalogProvider
|
|
477
505
|
: (selectedProviderId === 'builtin'
|
|
478
|
-
?
|
|
506
|
+
? instance.Profiles.getActiveProvider(instance.catalog)
|
|
479
507
|
: {
|
|
480
508
|
id: selectedProviderId,
|
|
481
509
|
name: selectedProviderId,
|
|
@@ -528,16 +556,16 @@
|
|
|
528
556
|
}
|
|
529
557
|
}
|
|
530
558
|
|
|
531
|
-
function describeCatalogChange(previous, next) {
|
|
532
|
-
const previousActive =
|
|
533
|
-
const nextActive =
|
|
559
|
+
function describeCatalogChange(instance, previous, next) {
|
|
560
|
+
const previousActive = instance.Profiles.getActiveProvider(previous);
|
|
561
|
+
const nextActive = instance.Profiles.getActiveProvider(next);
|
|
534
562
|
const providerIds = new Set([
|
|
535
563
|
...(previous.providers || []).map(provider => provider.id),
|
|
536
564
|
...(next.providers || []).map(provider => provider.id)
|
|
537
565
|
]);
|
|
538
566
|
const changedProviderIds = Array.from(providerIds).filter(providerId => {
|
|
539
|
-
const previousProvider =
|
|
540
|
-
const nextProvider =
|
|
567
|
+
const previousProvider = instance.Profiles.getProviderById(previous, providerId);
|
|
568
|
+
const nextProvider = instance.Profiles.getProviderById(next, providerId);
|
|
541
569
|
return modelCatalogSignature(previousProvider) !== modelCatalogSignature(nextProvider);
|
|
542
570
|
});
|
|
543
571
|
return {
|
|
@@ -548,8 +576,13 @@
|
|
|
548
576
|
}
|
|
549
577
|
|
|
550
578
|
function modelCatalogSignature(provider = {}) {
|
|
579
|
+
provider = provider || {};
|
|
551
580
|
return JSON.stringify({
|
|
552
581
|
id: provider.id || '',
|
|
582
|
+
revision: Number(provider.revision || 0),
|
|
583
|
+
baseUrl: provider.baseUrl || '',
|
|
584
|
+
wireApiPreference: provider.wireApiPreference || '',
|
|
585
|
+
resolvedWireApi: provider.resolvedWireApi || '',
|
|
553
586
|
defaultModelId: provider.defaultModelId || '',
|
|
554
587
|
reasoningAdapter: provider.reasoningAdapter || '',
|
|
555
588
|
reasoningCapability: provider.reasoningCapability || '',
|
|
@@ -567,7 +600,7 @@
|
|
|
567
600
|
|
|
568
601
|
function getProviderSnapshot(instance, providerId) {
|
|
569
602
|
const requestedId = providerId || instance.getSelectedProviderId() || 'builtin';
|
|
570
|
-
const provider =
|
|
603
|
+
const provider = instance.Profiles.getProviderById(instance.catalog, requestedId);
|
|
571
604
|
if (!provider) {
|
|
572
605
|
return {
|
|
573
606
|
providerId: requestedId,
|
|
@@ -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');
|