codexmate 0.0.20 → 0.0.22

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.
Files changed (96) hide show
  1. package/README.md +289 -152
  2. package/README.zh.md +321 -0
  3. package/cli/agents-files.js +224 -0
  4. package/cli/archive-helpers.js +446 -0
  5. package/cli/auth-profiles.js +359 -0
  6. package/cli/builtin-proxy.js +1044 -0
  7. package/cli/claude-proxy.js +998 -0
  8. package/cli/config-bootstrap.js +384 -0
  9. package/cli/openai-bridge.js +950 -0
  10. package/cli/openclaw-config.js +629 -0
  11. package/cli/session-usage.concurrent.js +28 -0
  12. package/cli/session-usage.js +112 -0
  13. package/cli/session-usage.models.js +176 -0
  14. package/cli/skills.js +1141 -0
  15. package/cli/zip-commands.js +510 -0
  16. package/cli.js +9408 -9719
  17. package/lib/cli-models-utils.js +109 -1
  18. package/lib/cli-path-utils.js +69 -0
  19. package/lib/cli-sessions.js +386 -0
  20. package/lib/download-artifacts.js +77 -0
  21. package/lib/task-orchestrator.js +869 -0
  22. package/package.json +14 -10
  23. package/res/logo.png +0 -0
  24. package/res/vue.global.prod.js +13 -0
  25. package/web-ui/app.js +193 -15
  26. package/web-ui/index.html +5 -1
  27. package/web-ui/logic.agents-diff.mjs +1 -1
  28. package/web-ui/logic.claude.mjs +60 -0
  29. package/web-ui/logic.runtime.mjs +11 -7
  30. package/web-ui/logic.sessions.mjs +372 -21
  31. package/web-ui/modules/api.mjs +22 -1
  32. package/web-ui/modules/app.computed.dashboard.mjs +23 -10
  33. package/web-ui/modules/app.computed.index.mjs +4 -0
  34. package/web-ui/modules/app.computed.main-tabs.mjs +198 -0
  35. package/web-ui/modules/app.computed.session.mjs +521 -9
  36. package/web-ui/modules/app.methods.agents.mjs +62 -11
  37. package/web-ui/modules/app.methods.codex-config.mjs +189 -34
  38. package/web-ui/modules/app.methods.index.mjs +7 -1
  39. package/web-ui/modules/app.methods.install.mjs +24 -20
  40. package/web-ui/modules/app.methods.navigation.mjs +142 -1
  41. package/web-ui/modules/app.methods.openclaw-core.mjs +339 -39
  42. package/web-ui/modules/app.methods.openclaw-editing.mjs +39 -4
  43. package/web-ui/modules/app.methods.openclaw-persist.mjs +122 -4
  44. package/web-ui/modules/app.methods.providers.mjs +192 -53
  45. package/web-ui/modules/app.methods.session-actions.mjs +99 -19
  46. package/web-ui/modules/app.methods.session-browser.mjs +196 -5
  47. package/web-ui/modules/app.methods.session-timeline.mjs +22 -15
  48. package/web-ui/modules/app.methods.session-trash.mjs +3 -0
  49. package/web-ui/modules/app.methods.startup-claude.mjs +70 -71
  50. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
  51. package/web-ui/modules/config-mode.computed.mjs +2 -0
  52. package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
  53. package/web-ui/modules/i18n.mjs +1609 -0
  54. package/web-ui/modules/plugins.computed.mjs +220 -0
  55. package/web-ui/modules/plugins.methods.mjs +620 -0
  56. package/web-ui/modules/plugins.storage.mjs +37 -0
  57. package/web-ui/partials/index/layout-footer.html +1 -57
  58. package/web-ui/partials/index/layout-header.html +299 -175
  59. package/web-ui/partials/index/modal-config-template-agents.html +79 -29
  60. package/web-ui/partials/index/modal-confirm-toast.html +1 -1
  61. package/web-ui/partials/index/modal-health-check.html +14 -14
  62. package/web-ui/partials/index/modal-openclaw-config.html +47 -42
  63. package/web-ui/partials/index/modal-skills.html +130 -114
  64. package/web-ui/partials/index/modals-basic.html +71 -102
  65. package/web-ui/partials/index/panel-config-claude.html +50 -12
  66. package/web-ui/partials/index/panel-config-codex.html +34 -37
  67. package/web-ui/partials/index/panel-config-openclaw.html +10 -16
  68. package/web-ui/partials/index/panel-docs.html +147 -0
  69. package/web-ui/partials/index/panel-market.html +38 -38
  70. package/web-ui/partials/index/panel-orchestration.html +397 -0
  71. package/web-ui/partials/index/panel-plugins.html +243 -0
  72. package/web-ui/partials/index/panel-sessions.html +51 -146
  73. package/web-ui/partials/index/panel-settings.html +188 -96
  74. package/web-ui/partials/index/panel-usage.html +353 -0
  75. package/web-ui/session-helpers.mjs +221 -10
  76. package/web-ui/styles/base-theme.css +120 -229
  77. package/web-ui/styles/controls-forms.css +59 -51
  78. package/web-ui/styles/docs-panel.css +247 -0
  79. package/web-ui/styles/layout-shell.css +394 -128
  80. package/web-ui/styles/modals-core.css +18 -3
  81. package/web-ui/styles/navigation-panels.css +184 -183
  82. package/web-ui/styles/plugins-panel.css +518 -0
  83. package/web-ui/styles/responsive.css +102 -62
  84. package/web-ui/styles/sessions-list.css +13 -27
  85. package/web-ui/styles/sessions-preview.css +13 -7
  86. package/web-ui/styles/sessions-toolbar-trash.css +25 -0
  87. package/web-ui/styles/sessions-usage.css +581 -6
  88. package/web-ui/styles/settings-panel.css +166 -0
  89. package/web-ui/styles/skills-list.css +16 -11
  90. package/web-ui/styles/skills-market.css +63 -2
  91. package/web-ui/styles/task-orchestration.css +776 -0
  92. package/web-ui/styles/titles-cards.css +67 -66
  93. package/web-ui/styles.css +4 -0
  94. package/README.en.md +0 -259
  95. package/res/screenshot.png +0 -0
  96. package/res/vue.global.js +0 -18552
@@ -277,6 +277,9 @@ export function createNavigationMethods(options = {}) {
277
277
  : '';
278
278
  const targetTab = normalizedTab || tab;
279
279
  if (!targetTab) return;
280
+ if (targetTab === 'orchestration' && this.taskOrchestrationTabEnabled !== true) {
281
+ return this.switchMainTab('config');
282
+ }
280
283
  this.cancelTouchNavIntentReset();
281
284
  if (targetTab === 'sessions') {
282
285
  this.cancelScheduledSessionTabDeferredTeardown();
@@ -297,6 +300,13 @@ export function createNavigationMethods(options = {}) {
297
300
  if (targetTab === previousTab) {
298
301
  switchState.ticket += 1;
299
302
  switchState.pendingTarget = '';
303
+ if (
304
+ targetTab === 'sessions'
305
+ && typeof this.prepareSessionTabRender === 'function'
306
+ && (!this.sessionListRenderEnabled || !this.sessionPreviewRenderEnabled)
307
+ ) {
308
+ this.prepareSessionTabRender();
309
+ }
300
310
  this.scheduleAfterFrame(() => {
301
311
  this.clearMainTabSwitchIntent(normalizedTab);
302
312
  });
@@ -307,6 +317,9 @@ export function createNavigationMethods(options = {}) {
307
317
  if (isLeavingSessions && !this.isSessionPanelFastHidden()) {
308
318
  this.setSessionPanelFastHidden(true);
309
319
  }
320
+ if (shouldDeferApply && typeof this.suspendSessionTabRender === 'function') {
321
+ this.suspendSessionTabRender();
322
+ }
310
323
  if (!shouldDeferApply) {
311
324
  switchState.ticket += 1;
312
325
  switchState.pendingTarget = '';
@@ -386,6 +399,116 @@ export function createNavigationMethods(options = {}) {
386
399
  this.__sessionTabDeferredTeardownHandle = null;
387
400
  },
388
401
 
402
+ setSessionListRef(element) {
403
+ this.__sessionListRef = element || null;
404
+ if (this.__sessionListRef && this.mainTab === 'sessions' && this.sessionListRenderEnabled) {
405
+ this.scheduleSessionListViewportFill();
406
+ }
407
+ },
408
+ getSessionListRenderSource() {
409
+ return Array.isArray(this.sortedSessionsList) ? this.sortedSessionsList : [];
410
+ },
411
+ cancelScheduledSessionListViewportFill() {
412
+ const handle = this.__sessionListViewportFillHandle || null;
413
+ if (!handle) return;
414
+ this.cancelIdleTask(handle);
415
+ this.__sessionListViewportFillHandle = null;
416
+ },
417
+ resetSessionListRender() {
418
+ this.cancelScheduledSessionListViewportFill();
419
+ this.sessionListVisibleCount = 0;
420
+ },
421
+ expandVisibleSessionList(stepSize, options = {}) {
422
+ const list = this.getSessionListRenderSource();
423
+ const total = list.length;
424
+ if (total <= 0) {
425
+ this.sessionListVisibleCount = 0;
426
+ return false;
427
+ }
428
+ const rawVisibleCount = Number(this.sessionListVisibleCount);
429
+ const currentCount = Number.isFinite(rawVisibleCount)
430
+ ? Math.max(0, Math.floor(rawVisibleCount))
431
+ : 0;
432
+ const initialBatchSize = Number.isFinite(this.sessionListInitialBatchSize)
433
+ ? Math.max(1, Math.floor(this.sessionListInitialBatchSize))
434
+ : 80;
435
+ const loadStep = Number.isFinite(stepSize)
436
+ ? Math.max(0, Math.floor(stepSize))
437
+ : (Number.isFinite(this.sessionListLoadStep)
438
+ ? Math.max(1, Math.floor(this.sessionListLoadStep))
439
+ : 120);
440
+ let nextCount = currentCount > 0
441
+ ? Math.min(total, currentCount + loadStep)
442
+ : Math.min(total, initialBatchSize);
443
+ if (options && options.ensureActive !== false) {
444
+ const activeKey = this.activeSession ? this.getSessionExportKey(this.activeSession) : '';
445
+ if (activeKey) {
446
+ const activeIndex = list.findIndex((session) => this.getSessionExportKey(session) === activeKey);
447
+ if (activeIndex >= 0) {
448
+ nextCount = Math.max(nextCount, activeIndex + 1);
449
+ }
450
+ }
451
+ }
452
+ nextCount = Math.min(total, nextCount);
453
+ if (nextCount <= currentCount) {
454
+ return false;
455
+ }
456
+ this.sessionListVisibleCount = nextCount;
457
+ return true;
458
+ },
459
+ scheduleSessionListViewportFill() {
460
+ this.cancelScheduledSessionListViewportFill();
461
+ if (this.mainTab !== 'sessions' || !this.sessionListRenderEnabled) {
462
+ return;
463
+ }
464
+ const run = () => {
465
+ this.__sessionListViewportFillHandle = null;
466
+ if (this.mainTab !== 'sessions' || !this.sessionListRenderEnabled) {
467
+ return;
468
+ }
469
+ const list = this.getSessionListRenderSource();
470
+ const total = list.length;
471
+ const visibleCount = Number(this.sessionListVisibleCount);
472
+ const normalizedVisibleCount = Number.isFinite(visibleCount)
473
+ ? Math.max(0, Math.floor(visibleCount))
474
+ : 0;
475
+ if (total <= 0 || normalizedVisibleCount >= total) {
476
+ return;
477
+ }
478
+ const listEl = this.__sessionListRef || null;
479
+ if (!listEl) {
480
+ return;
481
+ }
482
+ const clientHeight = Number(listEl.clientHeight) || 0;
483
+ const scrollHeight = Number(listEl.scrollHeight) || 0;
484
+ const scrollTop = Number(listEl.scrollTop) || 0;
485
+ const remaining = Math.max(0, scrollHeight - scrollTop - clientHeight);
486
+ const shouldGrow = scrollHeight <= (clientHeight + 160) || remaining <= Math.max(160, clientHeight);
487
+ if (!shouldGrow) {
488
+ return;
489
+ }
490
+ if (this.expandVisibleSessionList(undefined, { ensureActive: true })) {
491
+ this.scheduleSessionListViewportFill();
492
+ }
493
+ };
494
+ this.__sessionListViewportFillHandle = this.scheduleIdleTask(run, 120);
495
+ },
496
+ primeSessionListRender() {
497
+ this.resetSessionListRender();
498
+ if (this.mainTab !== 'sessions' || !this.sessionListRenderEnabled) {
499
+ return;
500
+ }
501
+ this.expandVisibleSessionList(undefined, { ensureActive: true });
502
+ this.scheduleSessionListViewportFill();
503
+ },
504
+ onSessionListScroll(event) {
505
+ const nextRef = event && event.currentTarget ? event.currentTarget : this.__sessionListRef;
506
+ if (nextRef) {
507
+ this.__sessionListRef = nextRef;
508
+ }
509
+ this.scheduleSessionListViewportFill();
510
+ },
511
+
389
512
  resetSessionPreviewMessageRender() {
390
513
  this.sessionPreviewVisibleCount = 0;
391
514
  this.invalidateSessionTimelineMeasurementCache();
@@ -424,18 +547,21 @@ export function createNavigationMethods(options = {}) {
424
547
  this.sessionTabRenderTicket += 1;
425
548
  this.sessionListRenderEnabled = false;
426
549
  this.sessionPreviewRenderEnabled = false;
550
+ this.cancelScheduledSessionListViewportFill();
427
551
  this.cancelSessionTimelineSync();
428
552
  this.sessionTimelineActiveKey = '';
429
553
  this.sessionTimelineLastSyncAt = 0;
430
554
  this.sessionTimelineLastScrollTop = 0;
431
555
  this.sessionTimelineLastAnchorY = 0;
432
556
  this.sessionTimelineLastDirection = 0;
557
+ this.__sessionListRef = null;
433
558
  this.sessionPreviewScrollEl = null;
434
559
  this.sessionPreviewContainerEl = null;
435
560
  this.sessionPreviewHeaderEl = null;
436
561
  },
437
562
 
438
563
  finalizeSessionTabTeardown() {
564
+ this.resetSessionListRender();
439
565
  this.resetSessionPreviewMessageRender();
440
566
  this.sessionPreviewPendingVisibleCount = 0;
441
567
  this.clearSessionTimelineRefs();
@@ -450,6 +576,7 @@ export function createNavigationMethods(options = {}) {
450
576
  const ticket = ++this.sessionTabRenderTicket;
451
577
  this.sessionListRenderEnabled = false;
452
578
  this.sessionPreviewRenderEnabled = false;
579
+ this.resetSessionListRender();
453
580
  this.resetSessionPreviewMessageRender();
454
581
 
455
582
  this.scheduleAfterFrame(() => {
@@ -457,6 +584,7 @@ export function createNavigationMethods(options = {}) {
457
584
  return;
458
585
  }
459
586
  this.sessionListRenderEnabled = true;
587
+ this.primeSessionListRender();
460
588
 
461
589
  this.scheduleAfterFrame(() => {
462
590
  if (ticket !== this.sessionTabRenderTicket || this.mainTab !== 'sessions') {
@@ -469,7 +597,20 @@ export function createNavigationMethods(options = {}) {
469
597
  }
470
598
  this.primeSessionPreviewMessageRender();
471
599
  this.updateSessionTimelineOffset();
472
- this.scheduleSessionTimelineSync();
600
+ if (!this.sessionTimelineEnabled) {
601
+ return;
602
+ }
603
+ const syncTask = () => {
604
+ if (ticket !== this.sessionTabRenderTicket || this.mainTab !== 'sessions') {
605
+ return;
606
+ }
607
+ this.scheduleSessionTimelineSync();
608
+ };
609
+ if (typeof this.scheduleAfterFrame === 'function') {
610
+ this.scheduleAfterFrame(syncTask);
611
+ return;
612
+ }
613
+ syncTask();
473
614
  });
474
615
  });
475
616
  });
@@ -1,3 +1,229 @@
1
+ function isPlainRecord(value) {
2
+ return !!(value && typeof value === 'object' && !Array.isArray(value));
3
+ }
4
+
5
+ function normalizeProviderId(provider) {
6
+ const normalized = typeof provider === 'string' ? provider.trim().toLowerCase() : '';
7
+ if (!normalized) return '';
8
+ if (normalized === 'modelstudio' || normalized === 'qwencloud') {
9
+ return 'qwen';
10
+ }
11
+ if (normalized === 'z.ai' || normalized === 'z-ai') {
12
+ return 'zai';
13
+ }
14
+ if (normalized === 'opencode-zen') {
15
+ return 'opencode';
16
+ }
17
+ if (normalized === 'opencode-go-auth') {
18
+ return 'opencode-go';
19
+ }
20
+ if (normalized === 'kimi' || normalized === 'kimi-code' || normalized === 'kimi-coding') {
21
+ return 'kimi';
22
+ }
23
+ if (normalized === 'bedrock' || normalized === 'aws-bedrock') {
24
+ return 'amazon-bedrock';
25
+ }
26
+ if (normalized === 'bytedance' || normalized === 'doubao') {
27
+ return 'volcengine';
28
+ }
29
+ return normalized;
30
+ }
31
+
32
+ function findNormalizedProviderKey(entries, provider) {
33
+ if (!isPlainRecord(entries)) {
34
+ return '';
35
+ }
36
+ const providerKey = normalizeProviderId(provider);
37
+ if (!providerKey) {
38
+ return '';
39
+ }
40
+ return Object.keys(entries).find((key) => normalizeProviderId(key) === providerKey) || '';
41
+ }
42
+
43
+ function collectDistinctProviderKeys(...providerMaps) {
44
+ const byNormalizedKey = new Map();
45
+ for (const providerMap of providerMaps) {
46
+ if (!isPlainRecord(providerMap)) continue;
47
+ for (const key of Object.keys(providerMap)) {
48
+ const normalizedKey = normalizeProviderId(key);
49
+ if (!normalizedKey || byNormalizedKey.has(normalizedKey)) continue;
50
+ byNormalizedKey.set(normalizedKey, key);
51
+ }
52
+ }
53
+ return Array.from(byNormalizedKey.values());
54
+ }
55
+
56
+ function isEnvTemplateString(value) {
57
+ return typeof value === 'string' && /^\$\{[A-Z][A-Z0-9_]{0,127}\}$/.test(value.trim());
58
+ }
59
+
60
+ function isSecretRefRecord(value) {
61
+ return isPlainRecord(value)
62
+ && typeof value.source === 'string'
63
+ && typeof value.provider === 'string'
64
+ && typeof value.id === 'string';
65
+ }
66
+
67
+ function isLegacySecretRefRecord(value) {
68
+ return isPlainRecord(value)
69
+ && typeof value.source === 'string'
70
+ && typeof value.id === 'string'
71
+ && (value.provider === undefined || value.provider === null || value.provider === '');
72
+ }
73
+
74
+ function coerceSecretRefRecord(value) {
75
+ if (isSecretRefRecord(value)) {
76
+ return {
77
+ source: value.source.trim(),
78
+ provider: value.provider.trim(),
79
+ id: value.id.trim()
80
+ };
81
+ }
82
+ if (isLegacySecretRefRecord(value)) {
83
+ return {
84
+ source: value.source.trim(),
85
+ provider: 'default',
86
+ id: value.id.trim()
87
+ };
88
+ }
89
+ return null;
90
+ }
91
+
92
+ const BUILTIN_PROVIDER_DEFAULTS = {
93
+ openai: {
94
+ baseUrl: 'https://api.openai.com/v1',
95
+ apiType: 'openai-responses'
96
+ },
97
+ 'openai-codex': {
98
+ baseUrl: 'https://chatgpt.com/backend-api',
99
+ apiType: 'openai-codex-responses'
100
+ }
101
+ };
102
+
103
+ function formatSecretRefLabel(ref) {
104
+ const normalized = coerceSecretRefRecord(ref);
105
+ if (!normalized) return '';
106
+ return `SecretRef(${normalized.source}:${normalized.provider}:${normalized.id})`;
107
+ }
108
+
109
+ function readFirstProviderDisplayValue(records, keys) {
110
+ for (const record of records) {
111
+ if (!isPlainRecord(record)) continue;
112
+ for (const key of keys) {
113
+ if (typeof record[key] === 'string' && record[key].trim()) {
114
+ return {
115
+ value: record[key].trim(),
116
+ readOnly: false,
117
+ kind: isEnvTemplateString(record[key]) ? 'env-template' : 'string'
118
+ };
119
+ }
120
+ if (coerceSecretRefRecord(record[key])) {
121
+ return {
122
+ value: formatSecretRefLabel(record[key]),
123
+ readOnly: true,
124
+ kind: 'secret-ref'
125
+ };
126
+ }
127
+ }
128
+ }
129
+ return {
130
+ value: '',
131
+ readOnly: false,
132
+ kind: 'missing'
133
+ };
134
+ }
135
+
136
+ function readOpenclawAuthProfileDisplayValue(authProfilesByProvider, providerName) {
137
+ const matchedKey = findNormalizedProviderKey(authProfilesByProvider, providerName) || providerName;
138
+ const summary = isPlainRecord(authProfilesByProvider) ? authProfilesByProvider[matchedKey] : null;
139
+ if (!isPlainRecord(summary)) {
140
+ return {
141
+ value: '',
142
+ readOnly: false,
143
+ kind: 'missing',
144
+ sourceKind: '',
145
+ sourceProfileId: '',
146
+ sourceWriteField: '',
147
+ sourceOriginalValue: '',
148
+ sourceCredentialType: ''
149
+ };
150
+ }
151
+ if (typeof summary.resolvedValue === 'string' && summary.resolvedValue.trim()) {
152
+ return {
153
+ value: summary.resolvedValue.trim(),
154
+ readOnly: false,
155
+ kind: 'auth-profile-value',
156
+ sourceKind: 'auth-profile',
157
+ sourceProfileId: typeof summary.profileId === 'string' ? summary.profileId.trim() : '',
158
+ sourceWriteField: typeof summary.resolvedField === 'string' ? summary.resolvedField.trim() : '',
159
+ sourceOriginalValue: summary.resolvedValue.trim(),
160
+ sourceCredentialType: typeof summary.type === 'string' ? summary.type.trim() : ''
161
+ };
162
+ }
163
+ const resolvedField = typeof summary.resolvedField === 'string' ? summary.resolvedField.trim() : '';
164
+ if (summary.editable === true && resolvedField) {
165
+ return {
166
+ value: '',
167
+ readOnly: false,
168
+ kind: 'auth-profile-value',
169
+ sourceKind: 'auth-profile',
170
+ sourceProfileId: typeof summary.profileId === 'string' ? summary.profileId.trim() : '',
171
+ sourceWriteField: resolvedField,
172
+ sourceOriginalValue: '',
173
+ sourceCredentialType: typeof summary.type === 'string' ? summary.type.trim() : ''
174
+ };
175
+ }
176
+ if (typeof summary.display !== 'string' || !summary.display.trim()) {
177
+ return {
178
+ value: '',
179
+ readOnly: false,
180
+ kind: 'missing',
181
+ sourceKind: '',
182
+ sourceProfileId: '',
183
+ sourceWriteField: '',
184
+ sourceOriginalValue: '',
185
+ sourceCredentialType: ''
186
+ };
187
+ }
188
+ return {
189
+ value: summary.display.trim(),
190
+ readOnly: true,
191
+ kind: 'auth-profile',
192
+ sourceKind: '',
193
+ sourceProfileId: typeof summary.profileId === 'string' ? summary.profileId.trim() : '',
194
+ sourceWriteField: '',
195
+ sourceOriginalValue: '',
196
+ sourceCredentialType: typeof summary.type === 'string' ? summary.type.trim() : ''
197
+ };
198
+ }
199
+
200
+ function readBuiltinProviderDisplayValue(providerName, field) {
201
+ const defaults = BUILTIN_PROVIDER_DEFAULTS[normalizeProviderId(providerName)];
202
+ if (!defaults) {
203
+ return {
204
+ value: '',
205
+ readOnly: false,
206
+ kind: 'missing'
207
+ };
208
+ }
209
+ const key = field === 'apiType' ? 'apiType' : 'baseUrl';
210
+ const value = typeof defaults[key] === 'string' ? defaults[key].trim() : '';
211
+ return {
212
+ value,
213
+ readOnly: false,
214
+ kind: value ? 'builtin-default' : 'missing'
215
+ };
216
+ }
217
+
218
+ function readPreferredProviderModels(records) {
219
+ for (const record of records) {
220
+ if (isPlainRecord(record) && Array.isArray(record.models) && record.models.length) {
221
+ return record.models;
222
+ }
223
+ }
224
+ return [];
225
+ }
226
+
1
227
  export function createOpenclawCoreMethods() {
2
228
  return {
3
229
  getOpenclawParser() {
@@ -68,7 +294,16 @@ export function createOpenclawCoreMethods() {
68
294
  return {
69
295
  providerName: '',
70
296
  baseUrl: '',
297
+ baseUrlReadOnly: false,
298
+ baseUrlDisplayKind: 'missing',
71
299
  apiKey: '',
300
+ apiKeyReadOnly: false,
301
+ apiKeyDisplayKind: 'missing',
302
+ apiKeySourceKind: '',
303
+ apiKeySourceProfileId: '',
304
+ apiKeySourceWriteField: '',
305
+ apiKeySourceOriginalValue: '',
306
+ apiKeySourceCredentialType: '',
72
307
  apiType: 'openai-responses',
73
308
  modelId: '',
74
309
  modelName: '',
@@ -89,24 +324,26 @@ export function createOpenclawCoreMethods() {
89
324
  this.openclawQuick.showKey = !this.openclawQuick.showKey;
90
325
  },
91
326
 
92
- fillOpenclawQuickFromConfig(config) {
327
+ fillOpenclawQuickFromConfig(config, options = {}) {
93
328
  const defaults = this.getOpenclawQuickDefaults();
94
- if (!config || typeof config !== 'object' || Array.isArray(config)) {
329
+ if (!isPlainRecord(config)) {
95
330
  this.openclawQuick = defaults;
96
331
  return;
97
332
  }
333
+ const authProfilesByProvider = isPlainRecord(options.authProfilesByProvider)
334
+ ? options.authProfilesByProvider
335
+ : (isPlainRecord(this.openclawAuthProfilesByProvider) ? this.openclawAuthProfilesByProvider : {});
98
336
 
99
- const agentDefaults = config.agents && typeof config.agents === 'object' && !Array.isArray(config.agents)
100
- && config.agents.defaults && typeof config.agents.defaults === 'object' && !Array.isArray(config.agents.defaults)
337
+ const agentDefaults = isPlainRecord(config.agents) && isPlainRecord(config.agents.defaults)
101
338
  ? config.agents.defaults
102
339
  : {};
103
340
  const modelConfig = agentDefaults.model;
104
- const legacyAgent = config.agent && typeof config.agent === 'object' && !Array.isArray(config.agent)
341
+ const legacyAgent = isPlainRecord(config.agent)
105
342
  ? config.agent
106
343
  : {};
107
344
 
108
345
  let primaryRef = '';
109
- if (modelConfig && typeof modelConfig === 'object' && !Array.isArray(modelConfig) && typeof modelConfig.primary === 'string') {
346
+ if (isPlainRecord(modelConfig) && typeof modelConfig.primary === 'string') {
110
347
  primaryRef = modelConfig.primary;
111
348
  } else if (typeof modelConfig === 'string') {
112
349
  primaryRef = modelConfig;
@@ -114,7 +351,7 @@ export function createOpenclawCoreMethods() {
114
351
  if (!primaryRef) {
115
352
  if (typeof legacyAgent.model === 'string') {
116
353
  primaryRef = legacyAgent.model;
117
- } else if (legacyAgent.model && typeof legacyAgent.model === 'object' && typeof legacyAgent.model.primary === 'string') {
354
+ } else if (isPlainRecord(legacyAgent.model) && typeof legacyAgent.model.primary === 'string') {
118
355
  primaryRef = legacyAgent.model.primary;
119
356
  }
120
357
  }
@@ -129,56 +366,109 @@ export function createOpenclawCoreMethods() {
129
366
  }
130
367
  }
131
368
 
132
- const providers = config.models && typeof config.models === 'object' && !Array.isArray(config.models)
133
- && config.models.providers && typeof config.models.providers === 'object' && !Array.isArray(config.models.providers)
369
+ const modelProviders = isPlainRecord(config.models) && isPlainRecord(config.models.providers)
134
370
  ? config.models.providers
135
371
  : null;
136
- let providerConfig = providerName && providers ? providers[providerName] : null;
137
- if (!providerName && providers) {
138
- const providerKeys = Object.keys(providers);
139
- if (providerKeys.length === 1) {
140
- providerName = providerKeys[0];
141
- providerConfig = providers[providerName];
142
- }
372
+ const rootProviders = isPlainRecord(config.providers)
373
+ ? config.providers
374
+ : null;
375
+ const providerKeys = collectDistinctProviderKeys(modelProviders, rootProviders);
376
+ if (!providerName && providerKeys.length === 1) {
377
+ providerName = providerKeys[0];
378
+ }
379
+
380
+ const normalizedConfiguredProviderName = providerName
381
+ ? (findNormalizedProviderKey(modelProviders, providerName)
382
+ || findNormalizedProviderKey(rootProviders, providerName)
383
+ || providerName)
384
+ : '';
385
+ if (normalizedConfiguredProviderName) {
386
+ providerName = normalizedConfiguredProviderName;
143
387
  }
144
388
 
389
+ const buildProviderRecords = (name) => {
390
+ if (!name) return [];
391
+ const modelProviderKey = findNormalizedProviderKey(modelProviders, name) || name;
392
+ const rootProviderKey = findNormalizedProviderKey(rootProviders, name) || name;
393
+ return [
394
+ modelProviders && modelProviders[modelProviderKey],
395
+ rootProviders && rootProviders[rootProviderKey]
396
+ ];
397
+ };
398
+ let providerRecords = buildProviderRecords(providerName);
399
+ const hasProviderConfig = providerRecords.some((item) => isPlainRecord(item));
400
+ if (!hasProviderConfig && providerKeys.length === 1) {
401
+ providerName = providerKeys[0];
402
+ providerRecords = buildProviderRecords(providerName);
403
+ }
404
+ const providerConfig = providerRecords.find((item) => isPlainRecord(item)) || null;
405
+ const providerModels = readPreferredProviderModels(providerRecords);
406
+
145
407
  let modelEntry = null;
146
- if (providerConfig && typeof providerConfig === 'object' && Array.isArray(providerConfig.models)) {
408
+ if (providerModels.length) {
147
409
  if (modelId) {
148
- modelEntry = providerConfig.models.find(item => item && item.id === modelId);
410
+ modelEntry = providerModels.find(item => item && (item.id === modelId || item.model === modelId));
149
411
  }
150
- if (!modelEntry && providerConfig.models.length === 1) {
151
- modelEntry = providerConfig.models[0];
152
- if (!modelId && modelEntry && typeof modelEntry.id === 'string') {
153
- modelId = modelEntry.id;
412
+ if (!modelEntry && providerModels.length === 1) {
413
+ modelEntry = providerModels[0];
414
+ if (!modelId && modelEntry) {
415
+ if (typeof modelEntry.id === 'string' && modelEntry.id.trim()) {
416
+ modelId = modelEntry.id.trim();
417
+ } else if (typeof modelEntry.model === 'string' && modelEntry.model.trim()) {
418
+ modelId = modelEntry.model.trim();
419
+ }
154
420
  }
155
421
  }
156
422
  }
157
423
 
158
- const baseUrl = providerConfig && typeof providerConfig === 'object' && typeof providerConfig.baseUrl === 'string'
159
- ? providerConfig.baseUrl
160
- : '';
161
- const apiKey = providerConfig && typeof providerConfig === 'object' && typeof providerConfig.apiKey === 'string'
162
- ? providerConfig.apiKey
163
- : '';
164
- const apiType = providerConfig && typeof providerConfig === 'object' && typeof providerConfig.api === 'string'
165
- ? providerConfig.api
166
- : defaults.apiType;
424
+ const configuredBaseUrlField = readFirstProviderDisplayValue(providerRecords, ['baseUrl', 'base_url', 'url']);
425
+ const baseUrlField = configuredBaseUrlField.value
426
+ ? configuredBaseUrlField
427
+ : readBuiltinProviderDisplayValue(providerName, 'baseUrl');
428
+ const providerApiKeyField = readFirstProviderDisplayValue(providerRecords, ['apiKey', 'api_key', 'keyRef', 'key', 'authToken', 'auth_token', 'tokenRef', 'token']);
429
+ const authProfileField = providerName
430
+ ? readOpenclawAuthProfileDisplayValue(authProfilesByProvider, providerName)
431
+ : {
432
+ value: '',
433
+ readOnly: false,
434
+ kind: 'missing',
435
+ sourceKind: '',
436
+ sourceProfileId: '',
437
+ sourceWriteField: '',
438
+ sourceOriginalValue: '',
439
+ sourceCredentialType: ''
440
+ };
441
+ const apiKeyField = providerApiKeyField.value ? providerApiKeyField : authProfileField;
442
+ const configuredApiTypeField = readFirstProviderDisplayValue(providerRecords, ['api', 'apiType', 'api_type']);
443
+ const apiTypeField = configuredApiTypeField.value
444
+ ? configuredApiTypeField
445
+ : readBuiltinProviderDisplayValue(providerName, 'apiType');
167
446
 
168
447
  this.openclawQuick = {
169
448
  ...defaults,
170
449
  providerName,
171
- baseUrl,
172
- apiKey,
173
- apiType,
450
+ baseUrl: baseUrlField.value,
451
+ baseUrlReadOnly: baseUrlField.readOnly,
452
+ baseUrlDisplayKind: baseUrlField.kind,
453
+ apiKey: apiKeyField.value,
454
+ apiKeyReadOnly: apiKeyField.readOnly,
455
+ apiKeyDisplayKind: apiKeyField.kind,
456
+ apiKeySourceKind: apiKeyField.sourceKind || '',
457
+ apiKeySourceProfileId: apiKeyField.sourceProfileId || '',
458
+ apiKeySourceWriteField: apiKeyField.sourceWriteField || '',
459
+ apiKeySourceOriginalValue: apiKeyField.sourceOriginalValue || '',
460
+ apiKeySourceCredentialType: apiKeyField.sourceCredentialType || '',
461
+ apiType: apiTypeField.value || defaults.apiType,
174
462
  modelId: modelId || '',
175
- modelName: modelEntry && typeof modelEntry.name === 'string' ? modelEntry.name : '',
463
+ modelName: modelEntry && typeof modelEntry.name === 'string'
464
+ ? modelEntry.name
465
+ : (modelEntry && typeof modelEntry.displayName === 'string' ? modelEntry.displayName : ''),
176
466
  contextWindow: modelEntry && typeof modelEntry.contextWindow === 'number'
177
467
  ? String(modelEntry.contextWindow)
178
- : '',
468
+ : (modelEntry && typeof modelEntry.context_window === 'number' ? String(modelEntry.context_window) : ''),
179
469
  maxTokens: modelEntry && typeof modelEntry.maxTokens === 'number'
180
470
  ? String(modelEntry.maxTokens)
181
- : ''
471
+ : (modelEntry && typeof modelEntry.max_tokens === 'number' ? String(modelEntry.max_tokens) : '')
182
472
  };
183
473
  },
184
474
 
@@ -192,7 +482,9 @@ export function createOpenclawCoreMethods() {
192
482
  }
193
483
  return false;
194
484
  }
195
- this.fillOpenclawQuickFromConfig(parsed.data);
485
+ this.fillOpenclawQuickFromConfig(parsed.data, {
486
+ authProfilesByProvider: this.openclawAuthProfilesByProvider
487
+ });
196
488
  if (!silent) {
197
489
  this.showMessage('已读取配置', 'success');
198
490
  }
@@ -291,7 +583,9 @@ export function createOpenclawCoreMethods() {
291
583
  return false;
292
584
  }
293
585
  this.fillOpenclawStructured(parsed.data);
294
- this.fillOpenclawQuickFromConfig(parsed.data);
586
+ this.fillOpenclawQuickFromConfig(parsed.data, {
587
+ authProfilesByProvider: this.openclawAuthProfilesByProvider
588
+ });
295
589
  this.refreshOpenclawProviders(parsed.data);
296
590
  this.refreshOpenclawAgentsList(parsed.data);
297
591
  if (!silent) {
@@ -361,6 +655,9 @@ export function createOpenclawCoreMethods() {
361
655
  if (typeof value === 'undefined' || value === null) {
362
656
  return '';
363
657
  }
658
+ if (coerceSecretRefRecord(value)) {
659
+ return formatSecretRefLabel(value);
660
+ }
364
661
  let text = '';
365
662
  if (typeof value === 'string') {
366
663
  text = value;
@@ -374,6 +671,9 @@ export function createOpenclawCoreMethods() {
374
671
  }
375
672
  }
376
673
  if (!text) return '';
674
+ if (isEnvTemplateString(text)) {
675
+ return `EnvRef(${text.trim().slice(2, -1)})`;
676
+ }
377
677
  if (/key|token|secret|password/i.test(key)) {
378
678
  return this.maskProviderValue(text);
379
679
  }