codexmate 0.0.27 → 0.0.29

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 (51) hide show
  1. package/README.md +1 -1
  2. package/README.zh.md +1 -1
  3. package/cli/builtin-proxy.js +430 -4
  4. package/cli/openai-bridge.js +498 -13
  5. package/cli.js +130 -41
  6. package/lib/cli-models-utils.js +71 -10
  7. package/lib/cli-webhook.js +126 -0
  8. package/package.json +76 -74
  9. package/plugins/prompt-templates/computed.mjs +1 -1
  10. package/plugins/prompt-templates/methods.mjs +0 -66
  11. package/plugins/prompt-templates/overview.mjs +1 -0
  12. package/web-ui/app.js +21 -16
  13. package/web-ui/index.html +1 -0
  14. package/web-ui/logic.codex.mjs +69 -0
  15. package/web-ui/modules/app.computed.dashboard.mjs +54 -0
  16. package/web-ui/modules/app.computed.session.mjs +22 -17
  17. package/web-ui/modules/app.methods.claude-config.mjs +24 -8
  18. package/web-ui/modules/app.methods.codex-config.mjs +35 -3
  19. package/web-ui/modules/app.methods.index.mjs +2 -0
  20. package/web-ui/modules/app.methods.navigation.mjs +21 -3
  21. package/web-ui/modules/app.methods.providers.mjs +96 -7
  22. package/web-ui/modules/app.methods.session-actions.mjs +3 -6
  23. package/web-ui/modules/app.methods.session-browser.mjs +1 -6
  24. package/web-ui/modules/app.methods.session-trash.mjs +6 -7
  25. package/web-ui/modules/app.methods.startup-claude.mjs +8 -1
  26. package/web-ui/modules/app.methods.webhook.mjs +79 -0
  27. package/web-ui/modules/i18n.dict.mjs +1104 -104
  28. package/web-ui/modules/i18n.mjs +9 -3
  29. package/web-ui/modules/provider-url-display.mjs +17 -0
  30. package/web-ui/partials/index/layout-header.html +25 -0
  31. package/web-ui/partials/index/modals-basic.html +0 -3
  32. package/web-ui/partials/index/panel-config-claude.html +10 -3
  33. package/web-ui/partials/index/panel-config-codex.html +44 -4
  34. package/web-ui/partials/index/panel-plugins.html +3 -29
  35. package/web-ui/partials/index/panel-sessions.html +0 -10
  36. package/web-ui/partials/index/panel-settings.html +93 -177
  37. package/web-ui/partials/index/panel-trash.html +88 -0
  38. package/web-ui/session-helpers.mjs +2 -2
  39. package/web-ui/styles/base-theme.css +47 -34
  40. package/web-ui/styles/controls-forms.css +27 -28
  41. package/web-ui/styles/docs-panel.css +63 -39
  42. package/web-ui/styles/layout-shell.css +69 -46
  43. package/web-ui/styles/modals-core.css +12 -10
  44. package/web-ui/styles/navigation-panels.css +36 -35
  45. package/web-ui/styles/responsive.css +4 -4
  46. package/web-ui/styles/sessions-list.css +10 -6
  47. package/web-ui/styles/settings-panel.css +197 -33
  48. package/web-ui/styles/titles-cards.css +90 -26
  49. package/web-ui/styles/trash-panel.css +90 -0
  50. package/web-ui/styles/webhook.css +81 -0
  51. package/web-ui/styles.css +2 -0
package/package.json CHANGED
@@ -1,74 +1,76 @@
1
- {
2
- "name": "codexmate",
3
- "version": "0.0.27",
4
- "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
- "main": "cli.js",
6
- "bin": {
7
- "codexmate": "./cli.js"
8
- },
9
- "files": [
10
- "cli.js",
11
- "cli/",
12
- "plugins/",
13
- "web-ui.html",
14
- "lib/",
15
- "web-ui/",
16
- "doc/",
17
- "README.md",
18
- "README.zh.md",
19
- "LICENSE"
20
- ],
21
- "exports": {
22
- ".": "./cli.js",
23
- "./lib/*": "./lib/*.js",
24
- "./web-ui/*": "./web-ui/*",
25
- "./res/*": "./web-ui/res/*"
26
- },
27
- "scripts": {
28
- "dev": "node cli.js run",
29
- "start": "node cli.js",
30
- "reset": "node tools/dev/reset-main.js",
31
- "release:npm": "node tools/release/publish-npm.js",
32
- "docs:dev": "node ./node_modules/vitepress/dist/node/cli.js dev site",
33
- "docs:build": "node ./node_modules/vitepress/dist/node/cli.js build site",
34
- "docs:preview": "node ./node_modules/vitepress/dist/node/cli.js preview site",
35
- "ci:install": "node tools/ci/run-check.js install",
36
- "ci:lint": "node tools/ci/run-check.js lint",
37
- "ci:test": "node tools/ci/run-check.js test",
38
- "lint": "node tools/dev/lint.js",
39
- "test": "npm run test:unit && npm run test:e2e",
40
- "test:ci": "node tools/ci/run-check.js all",
41
- "test:unit": "node tests/unit/run.mjs",
42
- "test:e2e": "node tests/e2e/run.js",
43
- "pretest": "node tools/ci/ensure-test-deps.js"
44
- },
45
- "dependencies": {
46
- "@iarna/toml": "^2.2.5",
47
- "json5": "^2.2.3",
48
- "yauzl": "^3.2.1",
49
- "zip-lib": "^1.2.1"
50
- },
51
- "engines": {
52
- "node": ">=14"
53
- },
54
- "keywords": [
55
- "codex",
56
- "claude",
57
- "claude-code",
58
- "openclaw",
59
- "config",
60
- "session",
61
- "task-orchestration",
62
- "workflow",
63
- "web-ui",
64
- "provider",
65
- "ai",
66
- "llm",
67
- "cli"
68
- ],
69
- "author": "ymkiux",
70
- "license": "Apache-2.0",
71
- "devDependencies": {
72
- "vitepress": "^1.6.4"
73
- }
74
- }
1
+ {
2
+ "name": "codexmate",
3
+ "version": "0.0.29",
4
+ "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "codexmate": "./cli.js"
8
+ },
9
+ "files": [
10
+ "cli.js",
11
+ "cli/",
12
+ "plugins/",
13
+ "web-ui.html",
14
+ "lib/",
15
+ "web-ui/",
16
+ "doc/",
17
+ "README.md",
18
+ "README.zh.md",
19
+ "LICENSE"
20
+ ],
21
+ "exports": {
22
+ ".": "./cli.js",
23
+ "./lib/*": "./lib/*.js",
24
+ "./web-ui/*": "./web-ui/*",
25
+ "./res/*": "./web-ui/res/*"
26
+ },
27
+ "scripts": {
28
+ "dev": "node cli.js run",
29
+ "start": "node cli.js",
30
+ "reset": "node tools/dev/reset-main.js",
31
+ "release:npm": "node tools/release/publish-npm.js",
32
+ "docs:dev": "node ./node_modules/vitepress/dist/node/cli.js dev site",
33
+ "docs:build": "node ./node_modules/vitepress/dist/node/cli.js build site",
34
+ "docs:preview": "node ./node_modules/vitepress/dist/node/cli.js preview site",
35
+ "ci:install": "node tools/ci/run-check.js install",
36
+ "ci:lint": "node tools/ci/run-check.js lint",
37
+ "ci:test": "node tools/ci/run-check.js test",
38
+ "lint": "node tools/dev/lint.js",
39
+ "test": "npm run test:unit && npm run test:e2e",
40
+ "test:ci": "node tools/ci/run-check.js all",
41
+ "test:unit": "node tests/unit/run.mjs",
42
+ "test:e2e": "node tests/e2e/run.js",
43
+ "setup:git": "git remote set-url origin https://github.com/SakuraByteCore/codexmate.git && gh auth setup-git",
44
+ "reset:dev": "node tools/dev/reset-and-dev.js",
45
+ "pretest": "node tools/ci/ensure-test-deps.js"
46
+ },
47
+ "dependencies": {
48
+ "@iarna/toml": "^2.2.5",
49
+ "json5": "^2.2.3",
50
+ "yauzl": "^3.2.1",
51
+ "zip-lib": "^1.2.1"
52
+ },
53
+ "engines": {
54
+ "node": ">=14"
55
+ },
56
+ "keywords": [
57
+ "codex",
58
+ "claude",
59
+ "claude-code",
60
+ "openclaw",
61
+ "config",
62
+ "session",
63
+ "task-orchestration",
64
+ "workflow",
65
+ "web-ui",
66
+ "provider",
67
+ "ai",
68
+ "llm",
69
+ "cli"
70
+ ],
71
+ "author": "ymkiux",
72
+ "license": "Apache-2.0",
73
+ "devDependencies": {
74
+ "vitepress": "^1.6.4"
75
+ }
76
+ }
@@ -98,7 +98,7 @@ function renderTemplate(templateText, values = {}) {
98
98
  const name = String(key || '').trim();
99
99
  if (!name) return '';
100
100
  const value = map[name];
101
- return value == null ? '' : String(value);
101
+ return value == null || String(value).trim() === '' ? _whole : String(value);
102
102
  });
103
103
  }
104
104
 
@@ -358,72 +358,6 @@ export function createPluginsMethods() {
358
358
  this.promptTemplateVarValuesRaw = {};
359
359
  },
360
360
 
361
- addPromptTemplateVariable() {
362
- const draft = normalizePromptTemplateDraft(this.promptTemplateDraftRaw);
363
- if (!draft || !draft.id) return;
364
- if (draft.isBuiltin) {
365
- this.showMessage(typeof this.t === 'function' ? this.t('toast.templates.builtinNotEditable') : 'Built-in templates are not editable', 'error');
366
- return;
367
- }
368
- this.promptTemplateVarDraftName = 'var';
369
- this.promptTemplateVarDraftError = '';
370
- this.showPromptTemplateVarModal = true;
371
- if (typeof this.$nextTick === 'function') {
372
- this.$nextTick(() => {
373
- const input = this.$refs && this.$refs.promptTemplateVarNameInput
374
- ? this.$refs.promptTemplateVarNameInput
375
- : null;
376
- if (input && typeof input.focus === 'function') input.focus();
377
- });
378
- }
379
- },
380
-
381
- closePromptTemplateVarModal() {
382
- this.showPromptTemplateVarModal = false;
383
- this.promptTemplateVarDraftError = '';
384
- },
385
-
386
- confirmAddPromptTemplateVariable() {
387
- const draft = normalizePromptTemplateDraft(this.promptTemplateDraftRaw);
388
- if (!draft || !draft.id) return;
389
- if (draft.isBuiltin) {
390
- this.promptTemplateVarDraftError = typeof this.t === 'function'
391
- ? this.t('toast.templates.builtinNotEditable')
392
- : 'Built-in templates are not editable';
393
- return;
394
- }
395
- const key = typeof this.promptTemplateVarDraftName === 'string'
396
- ? this.promptTemplateVarDraftName.trim()
397
- : '';
398
- if (!key) {
399
- this.promptTemplateVarDraftError = typeof this.t === 'function'
400
- ? this.t('toast.templates.varNameRequired')
401
- : 'Variable name is required';
402
- return;
403
- }
404
- if (!/^[a-zA-Z0-9_.-]+$/.test(key)) {
405
- this.promptTemplateVarDraftError = typeof this.t === 'function'
406
- ? this.t('toast.templates.varNameInvalid')
407
- : 'Variable name may only contain letters, numbers, underscore, dash, dot';
408
- return;
409
- }
410
- const placeholder = `{{${key}}}`;
411
- const current = typeof draft.template === 'string' ? draft.template : '';
412
- if (current.includes(placeholder)) {
413
- this.promptTemplateVarDraftError = typeof this.t === 'function'
414
- ? this.t('toast.templates.varExists')
415
- : 'Variable already exists';
416
- return;
417
- }
418
- const nextText = current && !current.endsWith('\n')
419
- ? `${current}\n${placeholder}\n`
420
- : `${current}${placeholder}\n`;
421
- this.promptTemplateDraftRaw = { ...draft, template: nextText };
422
- this.showPromptTemplateVarModal = false;
423
- this.promptTemplateVarDraftError = '';
424
- this.showMessage(typeof this.t === 'function' ? this.t('toast.templates.varAdded') : 'Variable added', 'success');
425
- },
426
-
427
361
  setPromptVariableValue(name, value) {
428
362
  const key = typeof name === 'string' ? name.trim() : '';
429
363
  if (!key) return;
@@ -7,6 +7,7 @@ import {
7
7
  import { buildBuiltinCommentPolishTemplate } from './comment-polish/index.mjs';
8
8
  import { buildBuiltinRuleAckTemplate } from './rule-ack/index.mjs';
9
9
 
10
+
10
11
  function ensureBuiltinTemplates(rawList, builtins) {
11
12
  const list = Array.isArray(rawList) ? rawList.filter(Boolean) : [];
12
13
  const builtinList = Array.isArray(builtins) ? builtins.filter(Boolean) : [];
package/web-ui/app.js CHANGED
@@ -34,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
34
34
  configMode: 'codex',
35
35
  currentProvider: '',
36
36
  currentModel: '',
37
+ currentModels: {},
37
38
  serviceTier: 'fast',
38
39
  modelReasoningEffort: 'medium',
39
40
  modelContextWindowInput: String(DEFAULT_MODEL_CONTEXT_WINDOW),
@@ -81,9 +82,6 @@ document.addEventListener('DOMContentLoaded', () => {
81
82
  promptComposerPickerKeyword: '',
82
83
  promptComposerSelectedTemplateId: '',
83
84
  promptComposerVarValuesRaw: {},
84
- showPromptTemplateVarModal: false,
85
- promptTemplateVarDraftName: '',
86
- promptTemplateVarDraftError: '',
87
85
  showConfirmDialog: false,
88
86
  confirmDialogTitle: '',
89
87
  confirmDialogMessage: '',
@@ -174,7 +172,6 @@ document.addEventListener('DOMContentLoaded', () => {
174
172
  sessionRoleFilter: 'all',
175
173
  sessionTimePreset: 'all',
176
174
  sessionSortMode: 'time',
177
- sessionResumeWithYolo: true,
178
175
  sessionPathOptions: [],
179
176
  sessionPathOptionsLoading: false,
180
177
  sessionPathOptionsMap: {
@@ -258,7 +255,7 @@ document.addEventListener('DOMContentLoaded', () => {
258
255
  installRegistryPreset: 'default',
259
256
  installRegistryCustom: '',
260
257
  installStatusTargets: null,
261
- newProvider: { name: '', url: '', key: '', useTransform: false },
258
+ newProvider: { name: '', url: '', key: '', useTransform: false, _suggestedModel: '' },
262
259
  resetConfigLoading: false,
263
260
  editingProvider: { name: '', url: '', key: '', readOnly: false, nonEditable: false },
264
261
  newModelName: '',
@@ -277,8 +274,8 @@ document.addEventListener('DOMContentLoaded', () => {
277
274
  newClaudeConfig: {
278
275
  name: '',
279
276
  apiKey: '',
280
- baseUrl: 'https://open.bigmodel.cn/api/anthropic',
281
- model: 'glm-4.7'
277
+ baseUrl: '',
278
+ model: ''
282
279
  },
283
280
  currentOpenclawConfig: '',
284
281
  openclawConfigs: {
@@ -345,7 +342,7 @@ document.addEventListener('DOMContentLoaded', () => {
345
342
  codexDownloadLoading: false,
346
343
  codexDownloadProgress: 0,
347
344
  codexDownloadTimer: null,
348
- settingsTab: 'backup',
345
+ settingsTab: 'general',
349
346
  sessionTrashEnabled: true,
350
347
  sessionTrashItems: [],
351
348
  sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE,
@@ -401,7 +398,12 @@ document.addEventListener('DOMContentLoaded', () => {
401
398
  lastLoadedAt: '',
402
399
  lastError: ''
403
400
  },
404
- _taskOrchestrationPollTimer: 0
401
+ _taskOrchestrationPollTimer: 0,
402
+ webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
403
+ webhookEventOptions: ['provider-switch', 'claude-md-edit'],
404
+ webhookSaving: false,
405
+ webhookTestResult: null,
406
+ webhookTesting: false,
405
407
  };
406
408
  },
407
409
 
@@ -409,6 +411,9 @@ document.addEventListener('DOMContentLoaded', () => {
409
411
  if (typeof this.initI18n === 'function') {
410
412
  this.initI18n();
411
413
  }
414
+ if (typeof this.loadWebhookSettings === 'function') {
415
+ this.loadWebhookSettings();
416
+ }
412
417
  if (typeof this.t === 'function') {
413
418
  this.confirmDialogConfirmText = this.t('confirm.ok');
414
419
  this.confirmDialogCancelText = this.t('confirm.cancel');
@@ -417,7 +422,7 @@ document.addEventListener('DOMContentLoaded', () => {
417
422
  }
418
423
  {
419
424
  const NAV_STATE_STORAGE_KEY = 'codexmateNavState.v1';
420
- const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings']);
425
+ const mainTabSet = new Set(['dashboard', 'config', 'sessions', 'usage', 'orchestration', 'market', 'plugins', 'docs', 'settings', 'trash']);
421
426
  let restored = null;
422
427
  try {
423
428
  const raw = localStorage.getItem(NAV_STATE_STORAGE_KEY) || '';
@@ -431,6 +436,9 @@ document.addEventListener('DOMContentLoaded', () => {
431
436
  const nextConfigMode = restored && typeof restored.configMode === 'string'
432
437
  ? restored.configMode.trim().toLowerCase()
433
438
  : '';
439
+ const nextSettingsTab = restored && typeof restored.settingsTab === 'string'
440
+ ? restored.settingsTab.trim().toLowerCase()
441
+ : '';
434
442
  let urlMainTab = '';
435
443
  try {
436
444
  const url = new URL(window.location.href);
@@ -443,6 +451,9 @@ document.addEventListener('DOMContentLoaded', () => {
443
451
  const resolvedMainTab = urlMainTab && mainTabSet.has(urlMainTab)
444
452
  ? urlMainTab
445
453
  : nextMainTab;
454
+ if (nextSettingsTab && (nextSettingsTab === 'general' || nextSettingsTab === 'data')) {
455
+ this.settingsTab = nextSettingsTab;
456
+ }
446
457
  if (nextConfigMode && typeof this.switchConfigMode === 'function') {
447
458
  this.__navStateRestoring = true;
448
459
  try {
@@ -469,12 +480,6 @@ document.addEventListener('DOMContentLoaded', () => {
469
480
  if (!this.taskOrchestrationTabEnabled && this.mainTab === 'orchestration') {
470
481
  this.mainTab = 'config';
471
482
  }
472
- const savedSessionYolo = localStorage.getItem('codexmateSessionResumeYolo');
473
- if (savedSessionYolo === '0' || savedSessionYolo === 'false') {
474
- this.sessionResumeWithYolo = false;
475
- } else if (savedSessionYolo === '1' || savedSessionYolo === 'true') {
476
- this.sessionResumeWithYolo = true;
477
- }
478
483
  this.restoreSessionFilterCache();
479
484
  this.restoreSessionPinnedMap();
480
485
  this.shareCommandPrefix = this.normalizeShareCommandPrefix(localStorage.getItem('codexmateShareCommandPrefix'));
package/web-ui/index.html CHANGED
@@ -21,6 +21,7 @@
21
21
  <!-- @include ./partials/index/panel-orchestration.html -->
22
22
  <!-- @include ./partials/index/panel-docs.html -->
23
23
  <!-- @include ./partials/index/panel-settings.html -->
24
+ <!-- @include ./partials/index/panel-trash.html -->
24
25
  <!-- @include ./partials/index/panel-market.html -->
25
26
  <!-- @include ./partials/index/panel-plugins.html -->
26
27
  <!-- @include ./partials/index/layout-footer.html -->
@@ -0,0 +1,69 @@
1
+ // 仅供 web-ui 的 codex 模型选择器与新增 provider 模板按钮使用。
2
+ // 镜像 logic.claude.mjs 的派生方式,但 codex provider 元信息不带 wire_api,
3
+ // 所以 catalog 仅按 baseUrl 的 host/path 命中。
4
+
5
+ const DEFAULT_OPENAI_CODEX_CATALOG = Object.freeze([
6
+ 'gpt-5-codex',
7
+ 'gpt-5',
8
+ 'gpt-5-mini',
9
+ 'gpt-4.1',
10
+ 'o4-mini',
11
+ 'o3-mini'
12
+ ]);
13
+
14
+ const HOST_RULES = Object.freeze([
15
+ { match: (u) => /api\.openai\.com/i.test(u), models: DEFAULT_OPENAI_CODEX_CATALOG },
16
+ { match: (u) => /api\.deepseek\.com/i.test(u), models: ['deepseek-chat', 'deepseek-coder', 'deepseek-reasoner'] },
17
+ { match: (u) => /dashscope\.aliyuncs\.com/i.test(u), models: ['qwen3-coder-plus', 'qwen3-coder-flash', 'qwen-max', 'qwen-plus'] },
18
+ { match: (u) => /ark\..*volces\.com/i.test(u), models: ['doubao-seed-1-6-thinking', 'doubao-seed-1-6', 'doubao-1-5-pro-32k', 'doubao-pro-32k'] },
19
+ { match: (u) => /open\.bigmodel\.cn/i.test(u), models: ['glm-4.6', 'glm-4.5', 'glm-4-plus', 'glm-coding'] },
20
+ { match: (u) => /api\.moonshot\.cn|api\.kimi\.com/i.test(u), models: ['moonshot-v1-32k', 'moonshot-v1-128k', 'kimi-latest'] },
21
+ { match: (u) => /api\.minimax/i.test(u), models: ['MiniMax-M2', 'abab6.5s-chat', 'abab6.5-chat'] },
22
+ { match: (u) => /api-inference\.modelscope\.cn/i.test(u), models: ['Qwen/Qwen3-Coder-480B-A35B-Instruct', 'ZhipuAI/GLM-4.5'] },
23
+ { match: (u) => /xiaomimimo\.com/i.test(u), models: ['mimo-v2-pro', 'mimo-v2'] },
24
+ { match: (u) => /ai\.muapi\.cn/i.test(u), models: ['mimo-v2-pro'] }
25
+ ]);
26
+
27
+ function normalizeUrl(url) {
28
+ return typeof url === 'string' ? url.trim().toLowerCase() : '';
29
+ }
30
+
31
+ export function getCodexModelCatalogForProvider(provider) {
32
+ if (!provider || typeof provider !== 'object') return [];
33
+ const url = normalizeUrl(provider.url || provider.baseUrl || '');
34
+ const name = typeof provider.name === 'string' ? provider.name.toLowerCase() : '';
35
+ if (!url) {
36
+ if (/openai/.test(name)) return [...DEFAULT_OPENAI_CODEX_CATALOG];
37
+ return [];
38
+ }
39
+ for (const rule of HOST_RULES) {
40
+ if (rule.match(url)) return [...rule.models];
41
+ }
42
+ return [];
43
+ }
44
+
45
+ // 服务模板表:供面板上的预设按钮使用。
46
+ // model 字段为可选首选项(添加后由前端写入内存字典 currentModels[name])。
47
+ // useTransform=true 表示该服务需通过内建 OpenAI bridge 转发。
48
+ export const CODEX_PROVIDER_TEMPLATES = Object.freeze([
49
+ {
50
+ label: 'MuAPI',
51
+ name: 'muapi',
52
+ url: 'https://ai.muapi.cn/v1',
53
+ model: 'mimo-v2-pro',
54
+ useTransform: true
55
+ },
56
+ {
57
+ label: 'Telepub',
58
+ name: 'telepub',
59
+ url: 'https://voyage.prod.telepub.cn/voyage/api',
60
+ model: 'DeepSeek-V4-pro',
61
+ useTransform: true
62
+ },
63
+ {
64
+ label: 'AnyRouter',
65
+ name: 'anyrouter',
66
+ url: 'https://anyrouter.top/v1',
67
+ model: 'gpt-5.5'
68
+ }
69
+ ]);
@@ -1,3 +1,5 @@
1
+ import { getProviderDisplayUrl, checkIsTransformProvider } from './provider-url-display.mjs';
2
+ import { getCodexModelCatalogForProvider, CODEX_PROVIDER_TEMPLATES } from '../logic.codex.mjs';
1
3
  export function createDashboardComputed() {
2
4
  return {
3
5
  agentsDiffHasChanges() {
@@ -29,6 +31,50 @@ export function createDashboardComputed() {
29
31
  }
30
32
  return list;
31
33
  },
34
+ activeProviderModel() {
35
+ return (name) => {
36
+ const target = String(name || '').trim();
37
+ if (!target) return '';
38
+ const dict = this.currentModels && typeof this.currentModels === 'object' ? this.currentModels : {};
39
+ const fromDict = typeof dict[target] === 'string' ? dict[target].trim() : '';
40
+ if (fromDict) return fromDict;
41
+ const activeName = String(this.currentProvider || '').trim();
42
+ if (target === activeName) {
43
+ const top = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
44
+ if (top && top !== '未设置') return top;
45
+ }
46
+ return '';
47
+ };
48
+ },
49
+ codexModelOptions() {
50
+ const seen = new Set();
51
+ const out = [];
52
+ const push = (val) => {
53
+ const s = typeof val === 'string' ? val.trim() : '';
54
+ if (!s || seen.has(s)) return;
55
+ seen.add(s);
56
+ out.push(s);
57
+ };
58
+ const activeName = String(this.displayCurrentProvider || '').trim();
59
+ const current = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
60
+ if (current && current !== '未设置') push(current);
61
+ const dict = this.currentModels && typeof this.currentModels === 'object' ? this.currentModels : {};
62
+ if (activeName && typeof dict[activeName] === 'string') push(dict[activeName]);
63
+ const remote = Array.isArray(this.models) ? this.models : [];
64
+ for (const m of remote) push(m);
65
+ const list = Array.isArray(this.providersList) ? this.providersList : [];
66
+ const activeProvider = list.find((p) => p && p.name === activeName);
67
+ if (activeProvider) {
68
+ for (const m of getCodexModelCatalogForProvider(activeProvider)) push(m);
69
+ }
70
+ return out;
71
+ },
72
+ codexModelHasList() {
73
+ return this.codexModelOptions.length > 0;
74
+ },
75
+ codexProviderTemplates() {
76
+ return CODEX_PROVIDER_TEMPLATES;
77
+ },
32
78
  displayCurrentProvider() {
33
79
  const switching = String(this.providerSwitchDisplayTarget || '').trim();
34
80
  if (switching) return switching;
@@ -39,6 +85,14 @@ export function createDashboardComputed() {
39
85
  const list = Array.isArray(this.providersList) ? this.providersList : [];
40
86
  return list;
41
87
  },
88
+
89
+ displayProviderUrl() {
90
+ return (provider) => getProviderDisplayUrl(provider);
91
+ },
92
+
93
+ isTransformProvider() {
94
+ return (provider) => checkIsTransformProvider(provider);
95
+ },
42
96
  installTargetCards() {
43
97
  const targets = Array.isArray(this.installStatusTargets) && this.installStatusTargets.length
44
98
  ? this.installStatusTargets
@@ -153,7 +153,14 @@ const KNOWN_USAGE_MODEL_PRICING = Object.freeze({
153
153
  'gpt-5.4': Object.freeze({ input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 }),
154
154
  'gpt-5.4-mini': Object.freeze({ input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }),
155
155
  'gpt-5.3-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }),
156
- 'gpt-5.2-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 })
156
+ 'gpt-5.2-codex': Object.freeze({ input: 1.75, output: 14, cacheRead: 0.175, cacheWrite: 0 }),
157
+ 'claude-opus-4-6': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 }),
158
+ 'claude-opus-4-7': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 }),
159
+ 'claude-sonnet-4-6': Object.freeze({ input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, reasoningOutput: 15 }),
160
+ 'claude-haiku-4-5': Object.freeze({ input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1, reasoningOutput: 4 }),
161
+ 'claude-3-5-sonnet': Object.freeze({ input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75, reasoningOutput: 15 }),
162
+ 'claude-3-5-haiku': Object.freeze({ input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1, reasoningOutput: 4 }),
163
+ 'claude-3-opus': Object.freeze({ input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75, reasoningOutput: 75 })
157
164
  });
158
165
 
159
166
  function createUsagePricingEntry(pricing, source) {
@@ -234,22 +241,17 @@ function resolveUsagePricingForSession(session, pricingIndex, fallbackProvider =
234
241
  if (knownPricing) {
235
242
  return knownPricing;
236
243
  }
244
+ const strippedModel = model.replace(/-\d{8}(?=[-_]|$)/, '');
245
+ if (strippedModel !== model) {
246
+ const strippedKnown = pricingIndex.knownByModel instanceof Map ? pricingIndex.knownByModel.get(strippedModel) : null;
247
+ if (strippedKnown) {
248
+ return strippedKnown;
249
+ }
250
+ }
237
251
  return null;
238
252
  }
239
253
 
240
- function shouldEstimateUsageCostForSession(session) {
241
- if (!session || typeof session !== 'object') {
242
- return false;
243
- }
244
- const source = typeof session.source === 'string' ? session.source.trim().toLowerCase() : '';
245
- const provider = typeof session.provider === 'string' ? session.provider.trim().toLowerCase() : '';
246
- const model = typeof session.model === 'string' ? session.model.trim().toLowerCase() : '';
247
- if (source === 'claude' || provider === 'claude') {
248
- return false;
249
- }
250
- if (/^claude(?:[-_]|$)/.test(model)) {
251
- return false;
252
- }
254
+ function shouldEstimateUsageCostForSession() {
253
255
  return true;
254
256
  }
255
257
 
@@ -313,10 +315,11 @@ function estimateUsageCostSummary(sessions, providersList, currentProvider) {
313
315
  function estimateUsageCostForSession(session, pricingIndex, currentProvider) {
314
316
  const inputTokens = Number.isFinite(Number(session.inputTokens)) ? Math.max(0, Math.floor(Number(session.inputTokens))) : null;
315
317
  const cachedInputTokens = Number.isFinite(Number(session.cachedInputTokens)) ? Math.max(0, Math.floor(Number(session.cachedInputTokens))) : 0;
318
+ const cacheCreationInputTokens = Number.isFinite(Number(session.cacheCreationInputTokens)) ? Math.max(0, Math.floor(Number(session.cacheCreationInputTokens))) : 0;
316
319
  const outputTokens = Number.isFinite(Number(session.outputTokens)) ? Math.max(0, Math.floor(Number(session.outputTokens))) : null;
317
320
  const reasoningOutputTokens = Number.isFinite(Number(session.reasoningOutputTokens)) ? Math.max(0, Math.floor(Number(session.reasoningOutputTokens))) : 0;
318
- const billableInputTokens = Math.max(0, (inputTokens || 0) - cachedInputTokens);
319
- const fallbackSessionTokens = billableInputTokens + cachedInputTokens + (outputTokens || 0) + reasoningOutputTokens;
321
+ const billableInputTokens = Math.max(0, (inputTokens || 0) - cachedInputTokens - cacheCreationInputTokens);
322
+ const fallbackSessionTokens = billableInputTokens + cachedInputTokens + cacheCreationInputTokens + (outputTokens || 0) + reasoningOutputTokens;
320
323
  const totalSessionTokens = Number.isFinite(Number(session.totalTokens))
321
324
  ? Math.max(0, Math.floor(Number(session.totalTokens)))
322
325
  : fallbackSessionTokens;
@@ -325,12 +328,14 @@ function estimateUsageCostForSession(session, pricingIndex, currentProvider) {
325
328
  const reasoningRate = pricing
326
329
  ? ((pricing.reasoningOutput != null ? pricing.reasoningOutput : pricing.output) || 0)
327
330
  : 0;
331
+ const visibleOutputTokens = Math.max(0, (outputTokens || 0) - reasoningOutputTokens);
328
332
  const estimatedUsd = pricing && hasTokenBreakdown
329
333
  ? (
330
334
  ((pricing.input || 0) * billableInputTokens)
331
335
  + ((pricing.cacheRead || 0) * cachedInputTokens)
336
+ + ((pricing.cacheWrite || 0) * cacheCreationInputTokens)
332
337
  + (reasoningRate * reasoningOutputTokens)
333
- + ((pricing.output || 0) * (outputTokens || 0))
338
+ + ((pricing.output || 0) * visibleOutputTokens)
334
339
  ) / 1000000
335
340
  : 0;
336
341
  return {