codexmate 0.0.23 → 0.0.24
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 +4 -3
- package/README.zh.md +4 -2
- package/cli/auth-profiles.js +23 -7
- package/cli/doctor-core.js +903 -0
- package/cli/import-skills-url.js +334 -0
- package/cli.js +304 -208
- package/lib/cli-models-utils.js +0 -40
- package/lib/cli-network-utils.js +28 -2
- package/package.json +5 -2
- package/plugins/README.md +20 -0
- package/plugins/README.zh-CN.md +20 -0
- package/plugins/prompt-templates/comment-polish/index.mjs +25 -0
- package/plugins/prompt-templates/computed.mjs +253 -0
- package/plugins/prompt-templates/index.mjs +8 -0
- package/plugins/prompt-templates/manifest.mjs +15 -0
- package/plugins/prompt-templates/methods.mjs +619 -0
- package/plugins/prompt-templates/overview.mjs +90 -0
- package/plugins/prompt-templates/ownership.mjs +19 -0
- package/plugins/prompt-templates/rule-ack/index.mjs +21 -0
- package/plugins/prompt-templates/storage.mjs +64 -0
- package/plugins/registry.mjs +16 -0
- package/res/logo-pack.webp +0 -0
- package/web-ui/app.js +15 -32
- package/web-ui/index.html +4 -3
- package/web-ui/modules/app.computed.dashboard.mjs +22 -22
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
- package/web-ui/modules/app.methods.agents.mjs +91 -3
- package/web-ui/modules/app.methods.codex-config.mjs +153 -164
- package/web-ui/modules/app.methods.navigation.mjs +34 -1
- package/web-ui/modules/app.methods.runtime.mjs +24 -2
- package/web-ui/modules/app.methods.session-browser.mjs +9 -2
- package/web-ui/modules/config-mode.computed.mjs +1 -3
- package/web-ui/modules/i18n.dict.mjs +2039 -0
- package/web-ui/modules/i18n.mjs +2 -1769
- package/web-ui/partials/index/layout-header.html +36 -32
- package/web-ui/partials/index/modal-config-template-agents.html +3 -4
- package/web-ui/partials/index/modal-health-check.html +33 -60
- package/web-ui/partials/index/panel-config-claude.html +35 -15
- package/web-ui/partials/index/panel-config-codex.html +47 -19
- package/web-ui/partials/index/panel-config-openclaw.html +8 -3
- package/web-ui/partials/index/panel-dashboard.html +186 -0
- package/web-ui/partials/index/panel-docs.html +1 -1
- package/web-ui/partials/index/panel-market.html +3 -0
- package/web-ui/partials/index/panel-orchestration.html +3 -0
- package/web-ui/partials/index/panel-plugins.html +16 -10
- package/web-ui/partials/index/panel-sessions.html +4 -1
- package/web-ui/partials/index/panel-settings.html +1 -1
- package/web-ui/partials/index/panel-usage.html +2 -1
- package/web-ui/styles/controls-forms.css +9 -2
- package/web-ui/styles/dashboard.css +274 -0
- package/web-ui/styles/layout-shell.css +2 -2
- package/web-ui/styles/sessions-list.css +3 -3
- package/web-ui/styles/sessions-usage.css +9 -0
- package/web-ui/styles.css +1 -0
- package/res/logo.png +0 -0
|
@@ -271,53 +271,164 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
271
271
|
});
|
|
272
272
|
},
|
|
273
273
|
|
|
274
|
-
async runHealthCheck() {
|
|
274
|
+
async runHealthCheck(options = {}) {
|
|
275
275
|
this.healthCheckLoading = true;
|
|
276
276
|
this.healthCheckResult = null;
|
|
277
|
-
|
|
277
|
+
this.healthCheckBatchTotal = 0;
|
|
278
|
+
this.healthCheckBatchDone = 0;
|
|
279
|
+
this.healthCheckBatchFailed = 0;
|
|
278
280
|
try {
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
281
|
+
const silent = !!(options && options.silent);
|
|
282
|
+
const forceRefresh = !!(options && options.forceRefresh);
|
|
283
|
+
const useDoctor = !!(options && options.doctor);
|
|
284
|
+
if (useDoctor) {
|
|
285
|
+
const res = await api('doctor', {
|
|
286
|
+
lang: this.lang,
|
|
287
|
+
remote: true,
|
|
288
|
+
range: this.sessionsUsageTimeRange,
|
|
289
|
+
targetApp: this.skillsTargetApp,
|
|
290
|
+
includeUsage: true,
|
|
291
|
+
includeTasks: true,
|
|
292
|
+
includeSkills: true,
|
|
293
|
+
includeInstall: true,
|
|
294
|
+
forceRefresh
|
|
295
|
+
});
|
|
296
|
+
if (hasResponseError(res)) {
|
|
297
|
+
this.healthCheckResult = null;
|
|
298
|
+
if (!silent) {
|
|
299
|
+
this.showMessage(getResponseMessage(res, '检查失败'), 'error');
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (res && typeof res === 'object') {
|
|
304
|
+
this.healthCheckResult = res;
|
|
305
|
+
const report = res.report && typeof res.report === 'object' ? res.report : null;
|
|
306
|
+
const summary = report && report.summary && typeof report.summary === 'object' ? report.summary : null;
|
|
307
|
+
const total = summary && Number.isFinite(Number(summary.total))
|
|
308
|
+
? Math.max(0, Math.floor(Number(summary.total)))
|
|
309
|
+
: (Array.isArray(res.issues) ? res.issues.length : 0);
|
|
310
|
+
const errors = summary && Number.isFinite(Number(summary.error)) ? Math.max(0, Math.floor(Number(summary.error))) : 0;
|
|
311
|
+
const warns = summary && Number.isFinite(Number(summary.warn)) ? Math.max(0, Math.floor(Number(summary.warn))) : 0;
|
|
312
|
+
this.healthCheckBatchTotal = total;
|
|
313
|
+
this.healthCheckBatchDone = total;
|
|
314
|
+
this.healthCheckBatchFailed = errors + warns;
|
|
315
|
+
if (!silent && res.ok) {
|
|
316
|
+
this.showMessage('检查通过', 'success');
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
this.healthCheckResult = null;
|
|
321
|
+
if (!silent) {
|
|
322
|
+
this.showMessage('检查失败', 'error');
|
|
323
|
+
}
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (this.configMode === 'claude') {
|
|
328
|
+
const entries = Object.entries(this.claudeConfigs || {});
|
|
329
|
+
this.healthCheckBatchTotal = entries.length;
|
|
330
|
+
|
|
331
|
+
const speedTasks = entries.map(([name, config]) => this.runClaudeSpeedTest(name, config)
|
|
332
|
+
.then((result) => {
|
|
333
|
+
if (!result || result.ok !== true) {
|
|
334
|
+
this.healthCheckBatchFailed += 1;
|
|
335
|
+
}
|
|
336
|
+
return { name, result };
|
|
337
|
+
})
|
|
338
|
+
.catch((err) => {
|
|
339
|
+
this.healthCheckBatchFailed += 1;
|
|
340
|
+
return {
|
|
341
|
+
name,
|
|
342
|
+
result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
|
|
343
|
+
};
|
|
344
|
+
})
|
|
345
|
+
.finally(() => {
|
|
346
|
+
this.healthCheckBatchDone += 1;
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
const pairs = await Promise.all(speedTasks);
|
|
351
|
+
const results = {};
|
|
352
|
+
const issues = [];
|
|
353
|
+
for (const pair of pairs) {
|
|
354
|
+
results[pair.name] = pair.result || null;
|
|
355
|
+
if (typeof this.buildSpeedTestIssue === 'function') {
|
|
356
|
+
const issue = this.buildSpeedTestIssue(pair.name, pair.result);
|
|
357
|
+
if (issue) issues.push(issue);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const ok = issues.length === 0 && this.healthCheckBatchFailed === 0;
|
|
361
|
+
this.healthCheckResult = {
|
|
362
|
+
ok,
|
|
363
|
+
issues,
|
|
364
|
+
remote: {
|
|
365
|
+
type: 'speed-test',
|
|
366
|
+
speedTests: results
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
if (ok && !silent) {
|
|
370
|
+
this.showMessage('检查通过', 'success');
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const shouldRunSpeedTests = this.configMode === 'codex';
|
|
376
|
+
const speedTimeoutMs = shouldRunSpeedTests ? 3500 : 0;
|
|
377
|
+
const providers = shouldRunSpeedTests
|
|
378
|
+
? (this.providersList || [])
|
|
379
|
+
.map((provider) => typeof provider === 'string'
|
|
380
|
+
? provider.trim()
|
|
381
|
+
: String((provider && provider.name) || '').trim())
|
|
382
|
+
.filter(Boolean)
|
|
383
|
+
: [];
|
|
384
|
+
const currentProvider = String(this.currentProvider || '').trim();
|
|
385
|
+
const orderedProviders = currentProvider && providers.includes(currentProvider)
|
|
386
|
+
? [currentProvider, ...providers.filter((name) => name !== currentProvider)]
|
|
387
|
+
: providers;
|
|
388
|
+
this.healthCheckBatchTotal = orderedProviders.length;
|
|
389
|
+
|
|
390
|
+
const speedTasks = orderedProviders.map((provider) => this.runSpeedTest(provider, { silent: true, timeoutMs: speedTimeoutMs })
|
|
391
|
+
.then((result) => {
|
|
392
|
+
if (!result || result.ok !== true) {
|
|
393
|
+
this.healthCheckBatchFailed += 1;
|
|
394
|
+
}
|
|
395
|
+
return { name: provider, result };
|
|
396
|
+
})
|
|
397
|
+
.catch((err) => {
|
|
398
|
+
this.healthCheckBatchFailed += 1;
|
|
399
|
+
return {
|
|
400
|
+
name: provider,
|
|
401
|
+
result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
|
|
402
|
+
};
|
|
403
|
+
})
|
|
404
|
+
.finally(() => {
|
|
405
|
+
this.healthCheckBatchDone += 1;
|
|
406
|
+
})
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const configTask = api('config-health-check', { remote: this.configMode === 'codex' });
|
|
410
|
+
const [res, pairs] = await Promise.all([
|
|
411
|
+
configTask,
|
|
412
|
+
Promise.all(speedTasks)
|
|
413
|
+
]);
|
|
282
414
|
if (hasResponseError(res)) {
|
|
283
415
|
this.healthCheckResult = null;
|
|
284
|
-
|
|
416
|
+
if (!silent) {
|
|
417
|
+
this.showMessage(getResponseMessage(res, '检查失败'), 'error');
|
|
418
|
+
}
|
|
285
419
|
} else if (res && typeof res === 'object') {
|
|
286
|
-
shouldRunClaudeSpeedTests = true;
|
|
287
420
|
const issues = Array.isArray(res.issues) ? [...res.issues] : [];
|
|
288
421
|
let remote = res.remote || null;
|
|
289
|
-
{
|
|
290
|
-
const providers = (this.providersList || [])
|
|
291
|
-
.map((provider) => typeof provider === 'string'
|
|
292
|
-
? provider.trim()
|
|
293
|
-
: String((provider && provider.name) || '').trim())
|
|
294
|
-
.filter(Boolean);
|
|
295
|
-
const tasks = providers.map(provider =>
|
|
296
|
-
this.runSpeedTest(provider, { silent: true })
|
|
297
|
-
.then(result => ({ name: provider, result }))
|
|
298
|
-
.catch(err => ({
|
|
299
|
-
name: provider,
|
|
300
|
-
result: { ok: false, error: err && err.message ? err.message : 'Speed test failed' }
|
|
301
|
-
}))
|
|
302
|
-
);
|
|
303
|
-
const pairs = await Promise.all(tasks);
|
|
422
|
+
if (shouldRunSpeedTests) {
|
|
304
423
|
const results = {};
|
|
305
424
|
for (const pair of pairs) {
|
|
306
425
|
results[pair.name] = pair.result || null;
|
|
307
426
|
const issue = this.buildSpeedTestIssue(pair.name, pair.result);
|
|
308
427
|
if (issue) issues.push(issue);
|
|
309
428
|
}
|
|
310
|
-
|
|
311
|
-
remote
|
|
312
|
-
|
|
313
|
-
speedTests: results
|
|
314
|
-
};
|
|
315
|
-
} else {
|
|
316
|
-
remote = {
|
|
317
|
-
type: 'speed-test',
|
|
318
|
-
speedTests: results
|
|
319
|
-
};
|
|
320
|
-
}
|
|
429
|
+
remote = remote && typeof remote === 'object'
|
|
430
|
+
? { ...remote, speedTests: results }
|
|
431
|
+
: { type: 'speed-test', speedTests: results };
|
|
321
432
|
}
|
|
322
433
|
|
|
323
434
|
const ok = issues.length === 0;
|
|
@@ -327,146 +438,24 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
327
438
|
issues,
|
|
328
439
|
remote
|
|
329
440
|
};
|
|
330
|
-
if (ok) {
|
|
441
|
+
if (ok && !silent) {
|
|
331
442
|
this.showMessage('检查通过', 'success');
|
|
332
443
|
}
|
|
333
444
|
} else {
|
|
334
445
|
this.healthCheckResult = null;
|
|
335
|
-
|
|
446
|
+
if (!silent) {
|
|
447
|
+
this.showMessage('检查失败', 'error');
|
|
448
|
+
}
|
|
336
449
|
}
|
|
337
450
|
} catch (e) {
|
|
338
451
|
this.healthCheckResult = null;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (shouldRunClaudeSpeedTests && this.configMode === 'claude') {
|
|
342
|
-
try {
|
|
343
|
-
const entries = Object.entries(this.claudeConfigs || {});
|
|
344
|
-
await Promise.all(entries.map(([name, config]) => this.runClaudeSpeedTest(name, config)));
|
|
345
|
-
} catch (e) {}
|
|
346
|
-
}
|
|
347
|
-
this.healthCheckLoading = false;
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
|
|
351
|
-
buildDefaultHealthCheckPrompt() {
|
|
352
|
-
return '请简短回复:连接正常。';
|
|
353
|
-
},
|
|
354
|
-
|
|
355
|
-
openHealthCheckDialog(options = {}) {
|
|
356
|
-
const providerName = typeof options.providerName === 'string'
|
|
357
|
-
? options.providerName.trim()
|
|
358
|
-
: '';
|
|
359
|
-
const locked = !!options.locked && !!providerName;
|
|
360
|
-
if (locked && providerName && providerName !== String(this.currentProvider || '').trim()) {
|
|
361
|
-
if (typeof this.showMessage === 'function') {
|
|
362
|
-
this.showMessage('请先切换到该提供商再进行健康聊天测试', 'info');
|
|
363
|
-
}
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
const nextProvider = providerName
|
|
367
|
-
|| String(this.healthCheckDialogSelectedProvider || '').trim()
|
|
368
|
-
|| String(this.currentProvider || '').trim()
|
|
369
|
-
|| String(((this.displayProvidersList || [])[0] || {}).name || '').trim();
|
|
370
|
-
|
|
371
|
-
this.showHealthCheckDialog = true;
|
|
372
|
-
this.healthCheckDialogLockedProvider = locked ? nextProvider : '';
|
|
373
|
-
this.healthCheckDialogSelectedProvider = nextProvider;
|
|
374
|
-
this.healthCheckDialogPrompt = this.buildDefaultHealthCheckPrompt();
|
|
375
|
-
this.healthCheckDialogMessages = [];
|
|
376
|
-
this.healthCheckDialogLastResult = null;
|
|
377
|
-
},
|
|
378
|
-
|
|
379
|
-
closeHealthCheckDialog(options = {}) {
|
|
380
|
-
if (this.healthCheckDialogSending && !options.force) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
this.showHealthCheckDialog = false;
|
|
384
|
-
this.healthCheckDialogLockedProvider = '';
|
|
385
|
-
this.healthCheckDialogSelectedProvider = '';
|
|
386
|
-
this.healthCheckDialogPrompt = this.buildDefaultHealthCheckPrompt();
|
|
387
|
-
this.healthCheckDialogMessages = [];
|
|
388
|
-
this.healthCheckDialogLastResult = null;
|
|
389
|
-
},
|
|
390
|
-
|
|
391
|
-
async sendHealthCheckDialogMessage() {
|
|
392
|
-
if (this.healthCheckDialogSending) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const provider = String(
|
|
397
|
-
this.healthCheckDialogLockedProvider || this.healthCheckDialogSelectedProvider || ''
|
|
398
|
-
).trim();
|
|
399
|
-
const prompt = String(this.healthCheckDialogPrompt || '').trim();
|
|
400
|
-
if (!provider) {
|
|
401
|
-
this.showMessage('请先选择提供商', 'error');
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
if (!prompt) {
|
|
405
|
-
this.showMessage('请输入消息内容', 'error');
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this.healthCheckDialogMessages.push({
|
|
410
|
-
id: `user-${Date.now()}`,
|
|
411
|
-
role: 'user',
|
|
412
|
-
text: prompt
|
|
413
|
-
});
|
|
414
|
-
this.healthCheckDialogSending = true;
|
|
415
|
-
this.healthCheckDialogLastResult = null;
|
|
416
|
-
|
|
417
|
-
try {
|
|
418
|
-
const res = await api('provider-chat-check', {
|
|
419
|
-
name: provider,
|
|
420
|
-
prompt
|
|
421
|
-
});
|
|
422
|
-
this.healthCheckDialogLastResult = res;
|
|
423
|
-
|
|
424
|
-
if (hasResponseError(res) || res.ok === false) {
|
|
425
|
-
const message = getResponseMessage(res, '健康聊天测试失败');
|
|
426
|
-
this.healthCheckDialogMessages.push({
|
|
427
|
-
id: `assistant-${Date.now()}`,
|
|
428
|
-
role: 'assistant',
|
|
429
|
-
text: message,
|
|
430
|
-
ok: false,
|
|
431
|
-
status: Number.isFinite(res && res.status) ? res.status : 0,
|
|
432
|
-
durationMs: Number.isFinite(res && res.durationMs) ? res.durationMs : 0,
|
|
433
|
-
model: typeof (res && res.model) === 'string' ? res.model : '',
|
|
434
|
-
rawPreview: typeof (res && res.rawPreview) === 'string' ? res.rawPreview : ''
|
|
435
|
-
});
|
|
436
|
-
this.showMessage(message, 'error');
|
|
437
|
-
return;
|
|
452
|
+
if (!(options && options.silent)) {
|
|
453
|
+
this.showMessage('检查失败', 'error');
|
|
438
454
|
}
|
|
439
|
-
|
|
440
|
-
const reply = typeof res.reply === 'string' && res.reply.trim()
|
|
441
|
-
? res.reply.trim()
|
|
442
|
-
: '已收到回复,但未解析到可展示文本。';
|
|
443
|
-
this.healthCheckDialogMessages.push({
|
|
444
|
-
id: `assistant-${Date.now()}`,
|
|
445
|
-
role: 'assistant',
|
|
446
|
-
text: reply,
|
|
447
|
-
ok: true,
|
|
448
|
-
status: Number.isFinite(res.status) ? res.status : 0,
|
|
449
|
-
durationMs: Number.isFinite(res.durationMs) ? res.durationMs : 0,
|
|
450
|
-
model: typeof res.model === 'string' ? res.model : '',
|
|
451
|
-
rawPreview: typeof res.rawPreview === 'string' ? res.rawPreview : ''
|
|
452
|
-
});
|
|
453
|
-
this.healthCheckDialogPrompt = '';
|
|
454
|
-
} catch (e) {
|
|
455
|
-
const message = e && e.message ? e.message : '健康聊天测试失败';
|
|
456
|
-
this.healthCheckDialogMessages.push({
|
|
457
|
-
id: `assistant-${Date.now()}`,
|
|
458
|
-
role: 'assistant',
|
|
459
|
-
text: message,
|
|
460
|
-
ok: false,
|
|
461
|
-
status: 0,
|
|
462
|
-
durationMs: 0,
|
|
463
|
-
model: '',
|
|
464
|
-
rawPreview: ''
|
|
465
|
-
});
|
|
466
|
-
this.healthCheckDialogLastResult = { ok: false, error: message };
|
|
467
|
-
this.showMessage(message, 'error');
|
|
468
455
|
} finally {
|
|
469
|
-
this.
|
|
456
|
+
this.healthCheckBatchTotal = this.healthCheckBatchTotal || 0;
|
|
457
|
+
this.healthCheckBatchDone = Math.min(this.healthCheckBatchDone || 0, this.healthCheckBatchTotal || 0);
|
|
458
|
+
this.healthCheckLoading = false;
|
|
470
459
|
}
|
|
471
460
|
},
|
|
472
461
|
|
|
@@ -6,6 +6,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
6
6
|
} = options;
|
|
7
7
|
const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
|
|
8
8
|
const MAIN_TAB_SET = new Set([
|
|
9
|
+
'dashboard',
|
|
9
10
|
'config',
|
|
10
11
|
'sessions',
|
|
11
12
|
'usage',
|
|
@@ -15,6 +16,29 @@ export function createNavigationMethods(options = {}) {
|
|
|
15
16
|
'docs',
|
|
16
17
|
'settings'
|
|
17
18
|
]);
|
|
19
|
+
const loadDoctorOverview = async (vm, options = {}) => {
|
|
20
|
+
if (!vm || typeof vm !== 'object') return false;
|
|
21
|
+
if (vm.__doctorLoading) return false;
|
|
22
|
+
const forceRefresh = !!(options && options.forceRefresh);
|
|
23
|
+
vm.__doctorLoading = true;
|
|
24
|
+
let ok = true;
|
|
25
|
+
try {
|
|
26
|
+
if (typeof vm.runHealthCheck === 'function') {
|
|
27
|
+
await vm.runHealthCheck({ doctor: true, silent: true, forceRefresh });
|
|
28
|
+
}
|
|
29
|
+
vm.__doctorLoadedOnce = true;
|
|
30
|
+
return true;
|
|
31
|
+
} catch (_) {
|
|
32
|
+
ok = false;
|
|
33
|
+
vm.__doctorLoadedOnce = true;
|
|
34
|
+
return false;
|
|
35
|
+
} finally {
|
|
36
|
+
vm.__doctorLoading = false;
|
|
37
|
+
if (!ok) {
|
|
38
|
+
vm.__doctorLoadedOnce = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
18
42
|
const readNavState = () => {
|
|
19
43
|
if (typeof localStorage === 'undefined') return null;
|
|
20
44
|
let raw = '';
|
|
@@ -37,7 +61,7 @@ export function createNavigationMethods(options = {}) {
|
|
|
37
61
|
const mainTab = typeof vm.mainTab === 'string' ? vm.mainTab.trim().toLowerCase() : '';
|
|
38
62
|
const configMode = typeof vm.configMode === 'string' ? vm.configMode.trim().toLowerCase() : '';
|
|
39
63
|
const snapshot = {
|
|
40
|
-
mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : '
|
|
64
|
+
mainTab: MAIN_TAB_SET.has(mainTab) ? mainTab : 'dashboard',
|
|
41
65
|
configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex'
|
|
42
66
|
};
|
|
43
67
|
try {
|
|
@@ -341,6 +365,9 @@ export function createNavigationMethods(options = {}) {
|
|
|
341
365
|
if (targetTab === previousTab) {
|
|
342
366
|
switchState.ticket += 1;
|
|
343
367
|
switchState.pendingTarget = '';
|
|
368
|
+
if (targetTab === 'dashboard' && !this.__doctorLoadedOnce) {
|
|
369
|
+
void loadDoctorOverview(this);
|
|
370
|
+
}
|
|
344
371
|
if (
|
|
345
372
|
targetTab === 'sessions'
|
|
346
373
|
&& typeof this.prepareSessionTabRender === 'function'
|
|
@@ -366,6 +393,9 @@ export function createNavigationMethods(options = {}) {
|
|
|
366
393
|
switchState.pendingTarget = '';
|
|
367
394
|
const result = switchMainTabHelper.call(this, targetTab);
|
|
368
395
|
persistNavState(this);
|
|
396
|
+
if (targetTab === 'dashboard') {
|
|
397
|
+
void loadDoctorOverview(this);
|
|
398
|
+
}
|
|
369
399
|
this.scheduleAfterFrame(() => {
|
|
370
400
|
this.clearMainTabSwitchIntent(normalizedTab);
|
|
371
401
|
});
|
|
@@ -381,6 +411,9 @@ export function createNavigationMethods(options = {}) {
|
|
|
381
411
|
liveState.pendingTarget = '';
|
|
382
412
|
switchMainTabHelper.call(this, pendingTarget);
|
|
383
413
|
persistNavState(this);
|
|
414
|
+
if (pendingTarget === 'dashboard') {
|
|
415
|
+
void loadDoctorOverview(this);
|
|
416
|
+
}
|
|
384
417
|
this.clearMainTabSwitchIntent(normalizedTab);
|
|
385
418
|
});
|
|
386
419
|
},
|
|
@@ -37,7 +37,12 @@ export function createRuntimeMethods(options = {}) {
|
|
|
37
37
|
const silent = !!options.silent;
|
|
38
38
|
this.speedLoading[name] = true;
|
|
39
39
|
try {
|
|
40
|
-
const
|
|
40
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) ? Math.max(1000, Number(options.timeoutMs)) : 0;
|
|
41
|
+
const payload = { name };
|
|
42
|
+
if (timeoutMs) {
|
|
43
|
+
payload.timeoutMs = timeoutMs;
|
|
44
|
+
}
|
|
45
|
+
const res = await api('speed-test', payload);
|
|
41
46
|
if (res.error) {
|
|
42
47
|
this.speedResults[name] = { ok: false, error: res.error };
|
|
43
48
|
if (!silent) {
|
|
@@ -66,6 +71,8 @@ export function createRuntimeMethods(options = {}) {
|
|
|
66
71
|
async runClaudeSpeedTest(name, config) {
|
|
67
72
|
if (!name || this.claudeSpeedLoading[name]) return null;
|
|
68
73
|
const baseUrl = config && typeof config.baseUrl === 'string' ? config.baseUrl.trim() : '';
|
|
74
|
+
const apiKey = config && typeof config.apiKey === 'string' ? config.apiKey.trim() : '';
|
|
75
|
+
const model = config && typeof config.model === 'string' ? config.model.trim() : '';
|
|
69
76
|
this.claudeSpeedLoading[name] = true;
|
|
70
77
|
try {
|
|
71
78
|
if (!baseUrl) {
|
|
@@ -73,7 +80,22 @@ export function createRuntimeMethods(options = {}) {
|
|
|
73
80
|
this.claudeSpeedResults[name] = res;
|
|
74
81
|
return res;
|
|
75
82
|
}
|
|
76
|
-
|
|
83
|
+
if (!apiKey) {
|
|
84
|
+
const res = { ok: false, error: 'Missing API key' };
|
|
85
|
+
this.claudeSpeedResults[name] = res;
|
|
86
|
+
return res;
|
|
87
|
+
}
|
|
88
|
+
if (!model) {
|
|
89
|
+
const res = { ok: false, error: 'Missing model' };
|
|
90
|
+
this.claudeSpeedResults[name] = res;
|
|
91
|
+
return res;
|
|
92
|
+
}
|
|
93
|
+
const res = await api('speed-test', {
|
|
94
|
+
kind: 'claude',
|
|
95
|
+
url: baseUrl,
|
|
96
|
+
apiKey,
|
|
97
|
+
model
|
|
98
|
+
});
|
|
77
99
|
if (res.error) {
|
|
78
100
|
this.claudeSpeedResults[name] = { ok: false, error: res.error };
|
|
79
101
|
return { ok: false, error: res.error };
|
|
@@ -442,12 +442,19 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
442
442
|
await this.onSessionSourceChange();
|
|
443
443
|
},
|
|
444
444
|
|
|
445
|
-
copySessionsFilterShareUrl() {
|
|
445
|
+
async copySessionsFilterShareUrl() {
|
|
446
446
|
const url = buildSessionsFilterShareUrl(this);
|
|
447
447
|
if (!url) {
|
|
448
|
-
this.showMessage('
|
|
448
|
+
this.showMessage(typeof this.t === 'function' ? this.t('sessions.filters.urlBuildFail') : 'Failed to build link', 'error');
|
|
449
449
|
return;
|
|
450
450
|
}
|
|
451
|
+
try {
|
|
452
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
453
|
+
await navigator.clipboard.writeText(url);
|
|
454
|
+
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
} catch (_) {}
|
|
451
458
|
const ok = typeof this.fallbackCopyText === 'function' ? this.fallbackCopyText(url) : false;
|
|
452
459
|
if (ok) {
|
|
453
460
|
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
|
|
@@ -47,6 +47,7 @@ export function createConfigModeComputed() {
|
|
|
47
47
|
return `${this.activeProviderModeLabel} 当前复用 Codex Provider / Model 管理链路。`;
|
|
48
48
|
},
|
|
49
49
|
inspectorMainTabLabel() {
|
|
50
|
+
if (this.mainTab === 'dashboard') return '概览';
|
|
50
51
|
if (this.mainTab === 'config') return '配置中心';
|
|
51
52
|
if (this.mainTab === 'sessions') return '会话浏览';
|
|
52
53
|
if (this.mainTab === 'usage') return 'Usage';
|
|
@@ -56,7 +57,6 @@ export function createConfigModeComputed() {
|
|
|
56
57
|
return '未知';
|
|
57
58
|
},
|
|
58
59
|
inspectorConfigModeLabel() {
|
|
59
|
-
if (this.mainTab !== 'config') return '--';
|
|
60
60
|
const providerMeta = getProviderConfigModeMeta(this.configMode);
|
|
61
61
|
if (providerMeta) return providerMeta.label;
|
|
62
62
|
if (this.configMode === 'claude') return 'Claude Code';
|
|
@@ -64,7 +64,6 @@ export function createConfigModeComputed() {
|
|
|
64
64
|
return '未选择';
|
|
65
65
|
},
|
|
66
66
|
inspectorCurrentConfigLabel() {
|
|
67
|
-
if (this.mainTab !== 'config') return '--';
|
|
68
67
|
if (getProviderConfigModeMeta(this.configMode)) {
|
|
69
68
|
const provider = typeof this.currentProvider === 'string' ? this.currentProvider.trim() : '';
|
|
70
69
|
return provider || '未选择';
|
|
@@ -80,7 +79,6 @@ export function createConfigModeComputed() {
|
|
|
80
79
|
return '未选择';
|
|
81
80
|
},
|
|
82
81
|
inspectorCurrentModelLabel() {
|
|
83
|
-
if (this.mainTab !== 'config') return '--';
|
|
84
82
|
if (getProviderConfigModeMeta(this.configMode)) {
|
|
85
83
|
const model = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
|
|
86
84
|
return model || '未选择';
|