@adversity/coding-tool-x 3.1.2 → 3.1.4

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 (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/web/assets/Analytics-Blo8_rhE.css +1 -0
  3. package/dist/web/assets/Analytics-DEIGaSFz.js +39 -0
  4. package/dist/web/assets/{ConfigTemplates-DvcbKKdS.js → ConfigTemplates-BZaehll1.js} +1 -1
  5. package/dist/web/assets/{Home-BJKPCBuk.css → Home-CyCIx4BA.css} +1 -1
  6. package/dist/web/assets/{Home-Cw-F_Wnu.js → Home-DIIH5bAk.js} +1 -1
  7. package/dist/web/assets/{PluginManager-jy_4GVxI.js → PluginManager-b4IlavFA.js} +1 -1
  8. package/dist/web/assets/{ProjectList-Df1-NcNr.js → ProjectList-QaqzjEea.js} +1 -1
  9. package/dist/web/assets/SessionList-CXUr6S7w.css +1 -0
  10. package/dist/web/assets/SessionList-Cz_hrGmL.js +1 -0
  11. package/dist/web/assets/{SkillManager-IRdseMKB.js → SkillManager-B-Rcb9xY.js} +1 -1
  12. package/dist/web/assets/{Terminal-BasTyDut.js → Terminal-C8CFJEkx.js} +1 -1
  13. package/dist/web/assets/{WorkspaceManager-D-D2kK1V.js → WorkspaceManager-XaQj8BRE.js} +1 -1
  14. package/dist/web/assets/icons-BxcwoY5F.js +1 -0
  15. package/dist/web/assets/index-B_kPXCbH.js +2 -0
  16. package/dist/web/assets/index-DUNAVDGb.css +1 -0
  17. package/dist/web/assets/naive-ui-BIXcURHZ.js +1 -0
  18. package/dist/web/assets/{vendors-CO3Upi1d.js → vendors-i5CBGnlm.js} +1 -1
  19. package/dist/web/assets/{vue-vendor-DqyWIXEb.js → vue-vendor-PKd8utv_.js} +1 -1
  20. package/dist/web/index.html +6 -6
  21. package/package.json +1 -1
  22. package/src/config/default.js +7 -29
  23. package/src/config/loader.js +6 -3
  24. package/src/config/model-metadata.js +102 -350
  25. package/src/config/model-metadata.json +125 -0
  26. package/src/server/api/channels.js +16 -39
  27. package/src/server/api/codex-channels.js +15 -43
  28. package/src/server/api/commands.js +0 -77
  29. package/src/server/api/config.js +4 -1
  30. package/src/server/api/gemini-channels.js +16 -40
  31. package/src/server/api/opencode-channels.js +25 -51
  32. package/src/server/api/opencode-proxy.js +1 -1
  33. package/src/server/api/opencode-sessions.js +0 -7
  34. package/src/server/api/sessions.js +11 -68
  35. package/src/server/api/settings.js +66 -39
  36. package/src/server/api/skills.js +0 -44
  37. package/src/server/api/statistics.js +115 -1
  38. package/src/server/codex-proxy-server.js +26 -55
  39. package/src/server/gemini-proxy-server.js +15 -14
  40. package/src/server/index.js +0 -3
  41. package/src/server/opencode-proxy-server.js +45 -121
  42. package/src/server/proxy-server.js +2 -4
  43. package/src/server/services/commands-service.js +0 -29
  44. package/src/server/services/config-templates-service.js +38 -28
  45. package/src/server/services/env-checker.js +73 -8
  46. package/src/server/services/plugins-service.js +37 -28
  47. package/src/server/services/pty-manager.js +22 -18
  48. package/src/server/services/skill-service.js +1 -49
  49. package/src/server/services/speed-test.js +40 -3
  50. package/src/server/services/statistics-service.js +238 -1
  51. package/src/server/utils/pricing.js +51 -60
  52. package/dist/web/assets/SessionList-BGJWyneI.css +0 -1
  53. package/dist/web/assets/SessionList-UWcZtC2r.js +0 -1
  54. package/dist/web/assets/icons-kcfLIMBB.js +0 -1
  55. package/dist/web/assets/index-CoB3zF0K.css +0 -1
  56. package/dist/web/assets/index-CryrSLv8.js +0 -2
  57. package/dist/web/assets/naive-ui-CSrLusZZ.js +0 -1
  58. package/src/server/api/convert.js +0 -260
  59. package/src/server/services/session-converter.js +0 -577
@@ -1,4 +1,4 @@
1
- import{g as t,c as e,a as n}from"./markdown-C9MYpaSi.js";import{S as r}from"./vue-vendor-DqyWIXEb.js";var o={exports:{}};
1
+ import{g as t,c as e,a as n}from"./markdown-C9MYpaSi.js";import{S as r}from"./vue-vendor-PKd8utv_.js";var o={exports:{}};
2
2
  /**!
3
3
  * Sortable 1.14.0
4
4
  * @author RubaXa <trash@rubaxa.org>
@@ -42,4 +42,4 @@ let lc;const cc=e=>lc=e,ac=Symbol();function uc(e){return e&&"object"==typeof e&
42
42
  * vue-router v4.6.4
43
43
  * (c) 2025 Eduardo San Martin Morote
44
44
  * @license MIT
45
- */(e,t);n=Pa(o.reverse(),"beforeRouteLeave",e,t);for(const s of o)s.leaveGuards.forEach(o=>{n.push(Na(o,e,t))});const c=y.bind(null,e,t);return n.push(c),M(n).then(()=>{n=[];for(const o of s.list())n.push(Na(o,e,t));return n.push(c),M(n)}).then(()=>{n=Pa(r,"beforeRouteUpdate",e,t);for(const o of r)o.updateGuards.forEach(o=>{n.push(Na(o,e,t))});return n.push(c),M(n)}).then(()=>{n=[];for(const o of l)if(o.beforeEnter)if(Ic(o.beforeEnter))for(const r of o.beforeEnter)n.push(Na(r,e,t));else n.push(Na(o.beforeEnter,e,t));return n.push(c),M(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=Pa(l,"beforeRouteEnter",e,t,b),n.push(c),M(n))).then(()=>{n=[];for(const o of i.list())n.push(Na(o,e,t));return n.push(c),M(n)}).catch(e=>Ea(e,_a.NAVIGATION_CANCELLED)?e:Promise.reject(e))}function S(e,t,n){l.list().forEach(o=>b(()=>o(e,t,n)))}function C(e,t,n,o,s){const i=g(e,t);if(i)return i;const l=t===la,a=kc?history.state:{};n&&(o||l?r.replace(e.fullPath,Rc({scroll:l&&a&&a.scroll},s)):r.push(e.fullPath,s)),c.value=e,N(e,t,n,l),R()}let w;function x(){w||(w=r.listen((e,t,n)=>{if(!L.listening)return;const o=d(e),s=v(o,L.currentRoute.value);if(s)return void _(Rc(s,{replace:!0,force:!0}),o).catch(Pc);a=o;const i=c.value;var l,u;kc&&(l=ga(i.fullPath,n.delta),u=da(),ma.set(l,u)),E(o,i).catch(e=>Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_CANCELLED)?e:Ea(e,_a.NAVIGATION_GUARD_REDIRECT)?(_(Rc(h(e.to),{force:!0}),o).then(e=>{Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_DUPLICATED)&&!n.delta&&n.type===ca.pop&&r.go(-1,!1)}).catch(Pc),Promise.reject()):(n.delta&&r.go(-n.delta,!1),O(e,o,i))).then(e=>{(e=e||C(o,i,!1))&&(n.delta&&!Ea(e,_a.NAVIGATION_CANCELLED)?r.go(-n.delta,!1):n.type===ca.pop&&Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_DUPLICATED)&&r.go(-1,!1)),S(o,i,e)}).catch(Pc)}))}let A,T=Ra(),k=Ra();function O(e,t,n){R(e);const o=k.list();return o.length&&o.forEach(o=>o(e,t,n)),Promise.reject(e)}function R(e){return A||(A=!e,x(),T.list().forEach(([t,n])=>e?n(e):t()),T.reset()),e}function N(t,n,o,r){const{scrollBehavior:s}=e;if(!kc||!s)return Promise.resolve();const i=!o&&function(e){const t=ma.get(e);return ma.delete(e),t}(ga(t.fullPath,0))||(r||!o)&&history.state&&history.state.scroll||null;return un().then(()=>s(t,n,i)).then(e=>e&&ha(e)).catch(e=>O(e,t,n))}const P=e=>r.go(e);let I;const D=new Set,L={currentRoute:c,listening:!0,addRoute:function(e,n){let o,r;return va(e)?(o=t.getRecordMatcher(e),r=n):r=e,t.addRoute(r,o)},removeRoute:function(e){const n=t.getRecordMatcher(e);n&&t.removeRoute(n)},clearRoutes:t.clearRoutes,hasRoute:function(e){return!!t.getRecordMatcher(e)},getRoutes:function(){return t.getRoutes().map(e=>e.record)},resolve:d,options:e,push:m,replace:function(e){return m(Rc(h(e),{replace:!0}))},go:P,back:()=>P(-1),forward:()=>P(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:k.add,isReady:function(){return A&&c.value!==la?Promise.resolve():new Promise((e,t)=>{T.add([e,t])})},install(e){e.component("RouterLink",ru),e.component("RouterView",cu),e.config.globalProperties.$router=L,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>Lt(c)}),kc&&!I&&c.value===la&&(I=!0,m(r.location).catch(e=>{}));const t={};for(const o in la)Object.defineProperty(t,o,{get:()=>c.value[o],enumerable:!0});e.provide(Ta,L),e.provide(ka,_t(t)),e.provide(Oa,c);const n=e.unmount;D.add(e),e.unmount=function(){D.delete(e),D.size<1&&(a=la,w&&w(),w=null,c.value=la,I=!1,A=!1),n()}}};function M(e){return e.reduce((e,t)=>e.then(()=>b(t)),Promise.resolve())}return L}function uu(){return Tn(Ta)}function fu(e){return Tn(ka)}const pu=e(ic);export{er as $,Pt as A,Nn as B,_s as C,Ni as D,wl as E,ms as F,qi as G,Ms as H,vs as I,Tt as J,Ho as K,K as L,tc as M,Lt as N,xt as O,At as P,Ut as Q,B as R,pu as S,Kn as T,au as U,Ma as V,js as W,Ds as X,Cn as Y,Kl as Z,Z as _,Is as a,ks as a0,Ac as a1,tr as a2,Xo as a3,Tc as a4,fu as a5,uu as a6,zl as a7,Yo as a8,ko as a9,dc as aa,Vs as b,Ts as c,ao as d,yt as e,fi as f,Ys as g,jo as h,$o as i,Vo as j,vt as k,Tn as l,Ro as m,No as n,Ss as o,Fs as p,An as q,Nt as r,wn as s,pi as t,Ht as u,un as v,In as w,nr as x,Hs as y,Os as z};
45
+ */(e,t);n=Pa(o.reverse(),"beforeRouteLeave",e,t);for(const s of o)s.leaveGuards.forEach(o=>{n.push(Na(o,e,t))});const c=y.bind(null,e,t);return n.push(c),M(n).then(()=>{n=[];for(const o of s.list())n.push(Na(o,e,t));return n.push(c),M(n)}).then(()=>{n=Pa(r,"beforeRouteUpdate",e,t);for(const o of r)o.updateGuards.forEach(o=>{n.push(Na(o,e,t))});return n.push(c),M(n)}).then(()=>{n=[];for(const o of l)if(o.beforeEnter)if(Ic(o.beforeEnter))for(const r of o.beforeEnter)n.push(Na(r,e,t));else n.push(Na(o.beforeEnter,e,t));return n.push(c),M(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=Pa(l,"beforeRouteEnter",e,t,b),n.push(c),M(n))).then(()=>{n=[];for(const o of i.list())n.push(Na(o,e,t));return n.push(c),M(n)}).catch(e=>Ea(e,_a.NAVIGATION_CANCELLED)?e:Promise.reject(e))}function S(e,t,n){l.list().forEach(o=>b(()=>o(e,t,n)))}function C(e,t,n,o,s){const i=g(e,t);if(i)return i;const l=t===la,a=kc?history.state:{};n&&(o||l?r.replace(e.fullPath,Rc({scroll:l&&a&&a.scroll},s)):r.push(e.fullPath,s)),c.value=e,N(e,t,n,l),R()}let w;function x(){w||(w=r.listen((e,t,n)=>{if(!L.listening)return;const o=d(e),s=v(o,L.currentRoute.value);if(s)return void _(Rc(s,{replace:!0,force:!0}),o).catch(Pc);a=o;const i=c.value;var l,u;kc&&(l=ga(i.fullPath,n.delta),u=da(),ma.set(l,u)),E(o,i).catch(e=>Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_CANCELLED)?e:Ea(e,_a.NAVIGATION_GUARD_REDIRECT)?(_(Rc(h(e.to),{force:!0}),o).then(e=>{Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_DUPLICATED)&&!n.delta&&n.type===ca.pop&&r.go(-1,!1)}).catch(Pc),Promise.reject()):(n.delta&&r.go(-n.delta,!1),O(e,o,i))).then(e=>{(e=e||C(o,i,!1))&&(n.delta&&!Ea(e,_a.NAVIGATION_CANCELLED)?r.go(-n.delta,!1):n.type===ca.pop&&Ea(e,_a.NAVIGATION_ABORTED|_a.NAVIGATION_DUPLICATED)&&r.go(-1,!1)),S(o,i,e)}).catch(Pc)}))}let A,T=Ra(),k=Ra();function O(e,t,n){R(e);const o=k.list();return o.length&&o.forEach(o=>o(e,t,n)),Promise.reject(e)}function R(e){return A||(A=!e,x(),T.list().forEach(([t,n])=>e?n(e):t()),T.reset()),e}function N(t,n,o,r){const{scrollBehavior:s}=e;if(!kc||!s)return Promise.resolve();const i=!o&&function(e){const t=ma.get(e);return ma.delete(e),t}(ga(t.fullPath,0))||(r||!o)&&history.state&&history.state.scroll||null;return un().then(()=>s(t,n,i)).then(e=>e&&ha(e)).catch(e=>O(e,t,n))}const P=e=>r.go(e);let I;const D=new Set,L={currentRoute:c,listening:!0,addRoute:function(e,n){let o,r;return va(e)?(o=t.getRecordMatcher(e),r=n):r=e,t.addRoute(r,o)},removeRoute:function(e){const n=t.getRecordMatcher(e);n&&t.removeRoute(n)},clearRoutes:t.clearRoutes,hasRoute:function(e){return!!t.getRecordMatcher(e)},getRoutes:function(){return t.getRoutes().map(e=>e.record)},resolve:d,options:e,push:m,replace:function(e){return m(Rc(h(e),{replace:!0}))},go:P,back:()=>P(-1),forward:()=>P(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:k.add,isReady:function(){return A&&c.value!==la?Promise.resolve():new Promise((e,t)=>{T.add([e,t])})},install(e){e.component("RouterLink",ru),e.component("RouterView",cu),e.config.globalProperties.$router=L,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>Lt(c)}),kc&&!I&&c.value===la&&(I=!0,m(r.location).catch(e=>{}));const t={};for(const o in la)Object.defineProperty(t,o,{get:()=>c.value[o],enumerable:!0});e.provide(Ta,L),e.provide(ka,_t(t)),e.provide(Oa,c);const n=e.unmount;D.add(e),e.unmount=function(){D.delete(e),D.size<1&&(a=la,w&&w(),w=null,c.value=la,I=!1,A=!1),n()}}};function M(e){return e.reduce((e,t)=>e.then(()=>b(t)),Promise.resolve())}return L}function uu(){return Tn(Ta)}function fu(e){return Tn(ka)}const pu=e(ic);export{er as $,Pt as A,Nn as B,_s as C,Ni as D,wl as E,ms as F,qi as G,Ms as H,vs as I,Tt as J,Ho as K,K as L,tc as M,Lt as N,xt as O,At as P,Ut as Q,B as R,pu as S,Kn as T,au as U,Ma as V,js as W,Ds as X,Cn as Y,Kl as Z,Z as _,Is as a,ks as a0,Ac as a1,tr as a2,Xo as a3,Tc as a4,fu as a5,uu as a6,zl as a7,Yo as a8,ko as a9,dc as aa,Rt as ab,Vs as b,Ts as c,ao as d,yt as e,fi as f,Ys as g,jo as h,$o as i,Vo as j,vt as k,Tn as l,Ro as m,No as n,Ss as o,Fs as p,An as q,Nt as r,wn as s,pi as t,Ht as u,un as v,In as w,nr as x,Hs as y,Os as z};
@@ -5,14 +5,14 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>CC-TOOL - ClaudeCode增强工作助手</title>
8
- <script type="module" crossorigin src="/assets/index-CryrSLv8.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-B_kPXCbH.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-C9MYpaSi.js">
10
- <link rel="modulepreload" crossorigin href="/assets/vue-vendor-DqyWIXEb.js">
11
- <link rel="modulepreload" crossorigin href="/assets/vendors-CO3Upi1d.js">
12
- <link rel="modulepreload" crossorigin href="/assets/naive-ui-CSrLusZZ.js">
13
- <link rel="modulepreload" crossorigin href="/assets/icons-kcfLIMBB.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/vue-vendor-PKd8utv_.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/vendors-i5CBGnlm.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/naive-ui-BIXcURHZ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/icons-BxcwoY5F.js">
14
14
  <link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
15
- <link rel="stylesheet" crossorigin href="/assets/index-CoB3zF0K.css">
15
+ <link rel="stylesheet" crossorigin href="/assets/index-DUNAVDGb.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adversity/coding-tool-x",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  // 默认配置
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const modelMetadataConfig = require('./model-metadata.json');
4
5
 
5
6
  const DEFAULT_CONFIG = {
6
7
  projectsDir: path.join(os.homedir(), '.claude', 'projects'),
@@ -17,38 +18,15 @@ const DEFAULT_CONFIG = {
17
18
  },
18
19
  maxLogs: 100,
19
20
  statsInterval: 30,
20
- defaultModels: {
21
- claude: [
22
- 'claude-opus-4-6',
23
- 'claude-sonnet-4-6',
24
- 'claude-opus-4-5-20251101',
25
- 'claude-sonnet-4-5-20250929',
26
- 'claude-haiku-4-5-20251001'
27
- ],
28
- codex: [
29
- 'gpt-5.3-codex',
30
- 'gpt-5.2-codex',
31
- 'gpt-5.1-codex-max',
32
- 'gpt-5.1-codex',
33
- 'gpt-5.1-codex-mini',
34
- 'gpt-5-codex',
35
- 'gpt-5.2',
36
- 'gpt-5.1',
37
- 'gpt-5'
38
- ],
39
- gemini: [
40
- 'gemini-3.1-pro',
41
- 'gemini-3-pro-preview',
42
- 'gemini-3-flash-preview',
43
- 'gemini-2.5-pro',
44
- 'gemini-2.5-flash',
45
- 'gemini-2.5-flash-lite'
46
- ]
47
- },
48
21
  modelDiscovery: {
49
- // 是否优先使用 /v1/models 获取可用模型;默认关闭,直接走默认模型探测
50
22
  useV1ModelsEndpoint: false
51
23
  },
24
+ defaultModels: modelMetadataConfig.defaultModels || { claude: [], codex: [], gemini: [] },
25
+ defaultSpeedTestModels: modelMetadataConfig.defaultSpeedTestModels || {
26
+ claude: 'claude-haiku-4-5',
27
+ codex: 'gpt-5.2',
28
+ gemini: 'gemini-2.5-pro'
29
+ },
52
30
  pricing: {
53
31
  claude: {
54
32
  mode: 'auto',
@@ -49,9 +49,9 @@ function mergeDefaultModels(defaultModels, overrides = {}) {
49
49
  return merged;
50
50
  }
51
51
 
52
- function mergeModelDiscovery(defaultModelDiscovery, overrides = {}) {
52
+ function mergeDefaultSpeedTestModels(defaultModels, overrides = {}) {
53
53
  return {
54
- ...defaultModelDiscovery,
54
+ ...defaultModels,
55
55
  ...(overrides || {})
56
56
  };
57
57
  }
@@ -99,7 +99,10 @@ function loadConfig() {
99
99
  config.ports = { ...DEFAULT_CONFIG.ports, ...userConfig.ports };
100
100
  config.pricing = mergePricing(DEFAULT_CONFIG.pricing, userConfig.pricing);
101
101
  config.defaultModels = mergeDefaultModels(DEFAULT_CONFIG.defaultModels, userConfig.defaultModels);
102
- config.modelDiscovery = mergeModelDiscovery(DEFAULT_CONFIG.modelDiscovery, userConfig.modelDiscovery);
102
+ config.defaultSpeedTestModels = mergeDefaultSpeedTestModels(
103
+ DEFAULT_CONFIG.defaultSpeedTestModels,
104
+ userConfig.defaultSpeedTestModels
105
+ );
103
106
 
104
107
  // 确保有 currentProject,使用 defaultProject 作为 currentProject
105
108
  if (!config.currentProject && config.defaultProject) {
@@ -1,344 +1,44 @@
1
1
  /**
2
- * Centralized Model Metadata Configuration
3
- *
4
- * Single source of truth for all mainstream Claude, Codex (OpenAI), and Gemini model metadata.
5
- * Includes:
6
- * - limit: context window (tokens) and max output (tokens)
7
- * - pricing: input/output/cache prices in USD per million tokens
8
- *
9
- * Sources (as of 2026-02):
10
- * - Claude: https://docs.anthropic.com/en/docs/about-claude/models
11
- * - OpenAI: https://platform.openai.com/docs/models
12
- * - Gemini: https://ai.google.dev/gemini-api/docs/models
2
+ * Model metadata runtime helpers.
3
+ * Data source: ./model-metadata.json
13
4
  */
14
5
 
15
- const MODEL_METADATA = {
16
- // ─── Claude 4.6 ───────────────────────────────────────────────────────────
17
- 'claude-opus-4-6': {
18
- limit: { context: 200000, output: 32000 },
19
- pricing: { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.50 }
20
- },
21
- 'claude-sonnet-4-6': {
22
- limit: { context: 200000, output: 64000 },
23
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
24
- },
25
- 'claude-haiku-4-6': {
26
- limit: { context: 200000, output: 8096 },
27
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
28
- },
29
-
30
- // ─── Claude 4.5 ───────────────────────────────────────────────────────────
31
- 'claude-opus-4-5': {
32
- limit: { context: 200000, output: 32000 },
33
- pricing: { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.50 }
34
- },
35
- 'claude-opus-4-5-20250929': {
36
- limit: { context: 200000, output: 32000 },
37
- pricing: { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.50 }
38
- },
39
- 'claude-opus-4-5-20251101': {
40
- limit: { context: 200000, output: 32000 },
41
- pricing: { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.50 }
42
- },
43
- 'claude-sonnet-4-5': {
44
- limit: { context: 200000, output: 64000 },
45
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
46
- },
47
- 'claude-sonnet-4-5-20250929': {
48
- limit: { context: 200000, output: 64000 },
49
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
50
- },
51
- 'claude-haiku-4-5': {
52
- limit: { context: 200000, output: 8096 },
53
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
54
- },
55
- 'claude-haiku-4-5-20250929': {
56
- limit: { context: 200000, output: 8096 },
57
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
58
- },
59
- 'claude-haiku-4-5-20251001': {
60
- limit: { context: 200000, output: 8096 },
61
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
62
- },
63
-
64
- // ─── Claude 4 ─────────────────────────────────────────────────────────────
65
- 'claude-opus-4': {
66
- limit: { context: 200000, output: 32000 },
67
- pricing: { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.50 }
68
- },
69
- 'claude-opus-4-20250514': {
70
- limit: { context: 200000, output: 32000 },
71
- pricing: { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.50 }
72
- },
73
- 'claude-sonnet-4': {
74
- limit: { context: 200000, output: 64000 },
75
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
76
- },
77
- 'claude-sonnet-4-20250514': {
78
- limit: { context: 200000, output: 64000 },
79
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
80
- },
81
-
82
- // ─── Claude 3.7 ───────────────────────────────────────────────────────────
83
- 'claude-sonnet-3-7': {
84
- limit: { context: 200000, output: 64000 },
85
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
86
- },
87
- 'claude-sonnet-3-7-20250219': {
88
- limit: { context: 200000, output: 64000 },
89
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
90
- },
91
-
92
- // ─── Claude 3.5 ───────────────────────────────────────────────────────────
93
- 'claude-sonnet-3-5': {
94
- limit: { context: 200000, output: 8096 },
95
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
96
- },
97
- 'claude-sonnet-3-5-20241022': {
98
- limit: { context: 200000, output: 8096 },
99
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
100
- },
101
- 'claude-sonnet-3-5-20240620': {
102
- limit: { context: 200000, output: 8096 },
103
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
104
- },
105
- 'claude-3-5-sonnet-20241022': {
106
- limit: { context: 200000, output: 8096 },
107
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
108
- },
109
- 'claude-3-5-sonnet-20240620': {
110
- limit: { context: 200000, output: 8096 },
111
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
112
- },
113
- 'claude-haiku-3-5': {
114
- limit: { context: 200000, output: 8096 },
115
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
116
- },
117
- 'claude-haiku-3-5-20241022': {
118
- limit: { context: 200000, output: 8096 },
119
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
120
- },
121
- 'claude-haiku-3-5-20250307': {
122
- limit: { context: 200000, output: 8096 },
123
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
124
- },
125
- 'claude-3-5-haiku-20241022': {
126
- limit: { context: 200000, output: 8096 },
127
- pricing: { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.10 }
128
- },
129
-
130
- // ─── Claude 3 (legacy) ────────────────────────────────────────────────────
131
- 'claude-opus-3': {
132
- limit: { context: 200000, output: 4096 },
133
- pricing: { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.50 }
134
- },
135
- 'claude-opus-3-20240229': {
136
- limit: { context: 200000, output: 4096 },
137
- pricing: { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.50 }
138
- },
139
- 'claude-3-opus-20240229': {
140
- limit: { context: 200000, output: 4096 },
141
- pricing: { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.50 }
142
- },
143
-
144
- // ─── OpenAI GPT-4o ────────────────────────────────────────────────────────
145
- 'gpt-4o': {
146
- limit: { context: 128000, output: 16384 },
147
- pricing: { input: 2.50, output: 10.00 }
148
- },
149
- 'gpt-4o-2024-11-20': {
150
- limit: { context: 128000, output: 16384 },
151
- pricing: { input: 2.50, output: 10.00 }
152
- },
153
- 'gpt-4o-2024-08-06': {
154
- limit: { context: 128000, output: 16384 },
155
- pricing: { input: 2.50, output: 10.00 }
156
- },
157
- 'gpt-4o-mini': {
158
- limit: { context: 128000, output: 16384 },
159
- pricing: { input: 0.15, output: 0.60 }
160
- },
161
- 'gpt-4o-mini-2024-07-18': {
162
- limit: { context: 128000, output: 16384 },
163
- pricing: { input: 0.15, output: 0.60 }
164
- },
165
- 'gpt-4-turbo': {
166
- limit: { context: 128000, output: 4096 },
167
- pricing: { input: 10.00, output: 30.00 }
168
- },
169
-
170
- // ─── OpenAI o-series ──────────────────────────────────────────────────────
171
- 'o1': {
172
- limit: { context: 200000, output: 100000 },
173
- pricing: { input: 15.00, output: 60.00 }
174
- },
175
- 'o1-2024-12-17': {
176
- limit: { context: 200000, output: 100000 },
177
- pricing: { input: 15.00, output: 60.00 }
178
- },
179
- 'o1-mini': {
180
- limit: { context: 128000, output: 65536 },
181
- pricing: { input: 1.10, output: 4.40 }
182
- },
183
- 'o1-mini-2024-09-12': {
184
- limit: { context: 128000, output: 65536 },
185
- pricing: { input: 1.10, output: 4.40 }
186
- },
187
- 'o3': {
188
- limit: { context: 200000, output: 100000 },
189
- pricing: { input: 10.00, output: 40.00 }
190
- },
191
- 'o3-mini': {
192
- limit: { context: 200000, output: 100000 },
193
- pricing: { input: 1.10, output: 4.40 }
194
- },
195
- 'o3-mini-2025-01-31': {
196
- limit: { context: 200000, output: 100000 },
197
- pricing: { input: 1.10, output: 4.40 }
198
- },
199
- 'o4-mini': {
200
- limit: { context: 200000, output: 100000 },
201
- pricing: { input: 1.10, output: 4.40 }
202
- },
203
-
204
- // ─── OpenAI GPT-5 / Codex ─────────────────────────────────────────────────
205
- 'gpt-5': {
206
- limit: { context: 1000000, output: 32768 },
207
- pricing: { input: 2.00, output: 8.00 }
208
- },
209
- 'gpt-5.1': {
210
- limit: { context: 1000000, output: 32768 },
211
- pricing: { input: 2.00, output: 8.00 }
212
- },
213
- 'gpt-5.2': {
214
- limit: { context: 1000000, output: 32768 },
215
- pricing: { input: 2.00, output: 8.00 }
216
- },
217
- 'gpt-5-codex': {
218
- limit: { context: 1000000, output: 32768 },
219
- pricing: { input: 2.00, output: 8.00 }
220
- },
221
- 'gpt-5.1-codex': {
222
- limit: { context: 1000000, output: 32768 },
223
- pricing: { input: 2.00, output: 8.00 }
224
- },
225
- 'gpt-5.1-codex-mini': {
226
- limit: { context: 1000000, output: 32768 },
227
- pricing: { input: 1.50, output: 6.00 }
228
- },
229
- 'gpt-5.1-codex-max': {
230
- limit: { context: 1000000, output: 32768 },
231
- pricing: { input: 3.00, output: 12.00 }
232
- },
233
- 'gpt-5.2-codex': {
234
- limit: { context: 1000000, output: 32768 },
235
- pricing: { input: 2.00, output: 8.00 }
236
- },
237
- 'gpt-5.3-codex': {
238
- limit: { context: 1000000, output: 32768 },
239
- pricing: { input: 2.00, output: 8.00 }
240
- },
241
-
242
- // ─── Gemini 2.5 ───────────────────────────────────────────────────────────
243
- 'gemini-2.5-pro': {
244
- limit: { context: 1048576, output: 65536 },
245
- pricing: { input: 1.25, output: 10.00 }
246
- },
247
- 'gemini-2.5-pro-preview': {
248
- limit: { context: 1048576, output: 65536 },
249
- pricing: { input: 1.25, output: 10.00 }
250
- },
251
- 'gemini-2.5-pro-exp-03-25': {
252
- limit: { context: 1048576, output: 65536 },
253
- pricing: { input: 1.25, output: 10.00 }
254
- },
255
- 'gemini-2.5-flash': {
256
- limit: { context: 1048576, output: 65536 },
257
- pricing: { input: 0.15, output: 0.60 }
258
- },
259
- 'gemini-2.5-flash-preview': {
260
- limit: { context: 1048576, output: 65536 },
261
- pricing: { input: 0.15, output: 0.60 }
262
- },
263
- 'gemini-2.5-flash-lite': {
264
- limit: { context: 1048576, output: 65536 },
265
- pricing: { input: 0.10, output: 0.40 }
266
- },
267
-
268
- // ─── Gemini 2.0 ───────────────────────────────────────────────────────────
269
- 'gemini-2.0-flash': {
270
- limit: { context: 1048576, output: 8192 },
271
- pricing: { input: 0.10, output: 0.40 }
272
- },
273
- 'gemini-2.0-flash-exp': {
274
- limit: { context: 1048576, output: 8192 },
275
- pricing: { input: 0.10, output: 0.40 }
276
- },
277
- 'gemini-2.0-flash-lite': {
278
- limit: { context: 1048576, output: 8192 },
279
- pricing: { input: 0.075, output: 0.30 }
280
- },
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const metadataConfig = require('./model-metadata.json');
9
+ const METADATA_FILE_PATH = path.join(__dirname, 'model-metadata.json');
10
+
11
+ const MODEL_METADATA = metadataConfig.models || {};
12
+ const MODEL_ALIASES = metadataConfig.aliases || {};
13
+ const DEFAULT_MODELS = metadataConfig.defaultModels || { claude: [], codex: [], gemini: [] };
14
+ const DEFAULT_SPEED_TEST_MODELS = metadataConfig.defaultSpeedTestModels || {
15
+ claude: 'claude-haiku-4-5',
16
+ codex: 'gpt-5.2',
17
+ gemini: 'gemini-2.5-pro'
18
+ };
281
19
 
282
- // ─── Gemini 1.5 ───────────────────────────────────────────────────────────
283
- 'gemini-1.5-pro': {
284
- limit: { context: 2097152, output: 8192 },
285
- pricing: { input: 1.25, output: 5.00 }
286
- },
287
- 'gemini-1.5-pro-002': {
288
- limit: { context: 2097152, output: 8192 },
289
- pricing: { input: 1.25, output: 5.00 }
290
- },
291
- 'gemini-1.5-flash': {
292
- limit: { context: 1048576, output: 8192 },
293
- pricing: { input: 0.075, output: 0.30 }
294
- },
295
- 'gemini-1.5-flash-002': {
296
- limit: { context: 1048576, output: 8192 },
297
- pricing: { input: 0.075, output: 0.30 }
298
- },
299
- 'gemini-1.5-flash-8b': {
300
- limit: { context: 1048576, output: 8192 },
301
- pricing: { input: 0.0375, output: 0.15 }
302
- },
20
+ function normalizeNonEmptyString(value) {
21
+ if (typeof value !== 'string') return null;
22
+ const trimmed = value.trim();
23
+ return trimmed || null;
24
+ }
303
25
 
304
- // ─── Gemini 3 (future/preview) ────────────────────────────────────────────
305
- 'gemini-3-pro-preview': {
306
- limit: { context: 2097152, output: 65536 },
307
- pricing: { input: 2.50, output: 15.00 }
308
- },
309
- 'gemini-3.1-pro': {
310
- limit: { context: 2097152, output: 65536 },
311
- pricing: { input: 2.50, output: 15.00 }
312
- },
313
- 'gemini-3-flash-preview': {
314
- limit: { context: 1048576, output: 65536 },
315
- pricing: { input: 0.30, output: 1.20 }
26
+ function loadMetadataConfigFromFile() {
27
+ try {
28
+ const raw = fs.readFileSync(METADATA_FILE_PATH, 'utf8');
29
+ const parsed = JSON.parse(raw);
30
+ if (parsed && typeof parsed === 'object') {
31
+ return parsed;
32
+ }
33
+ } catch (error) {
34
+ console.warn(`[model-metadata] Failed to read metadata file, fallback to in-memory config: ${error.message}`);
316
35
  }
317
- };
318
-
319
- /**
320
- * Model aliases: short name → canonical model ID
321
- * Used for prefix/alias lookups
322
- */
323
- const MODEL_ALIASES = {
324
- // Claude aliases
325
- 'claude-opus-4-6': 'claude-opus-4-6',
326
- 'claude-sonnet-4-6': 'claude-sonnet-4-6',
327
- 'claude-haiku-4-6': 'claude-haiku-4-6',
328
- 'claude-opus-4-5': 'claude-opus-4-5-20250929',
329
- 'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929',
330
- 'claude-haiku-4-5': 'claude-haiku-4-5-20250929',
331
- 'claude-opus-4': 'claude-opus-4-20250514',
332
- 'claude-sonnet-4': 'claude-sonnet-4-20250514',
333
- 'claude-sonnet-3-7': 'claude-sonnet-3-7-20250219',
334
- 'claude-haiku-3-5': 'claude-haiku-3-5-20241022',
335
- 'claude-sonnet-3-5': 'claude-sonnet-3-5-20241022',
336
- 'claude-opus-3': 'claude-opus-3-20240229'
337
- };
36
+ return metadataConfig;
37
+ }
338
38
 
339
39
  /**
340
40
  * Resolve model metadata (limit + pricing) for a given model ID.
341
- * Supports: exact match alias match prefix match generic Claude fallback
41
+ * Supports: exact match -> alias match -> prefix match -> generic Claude fallback
342
42
  *
343
43
  * @param {string} modelId
344
44
  * @returns {{ limit: {context, output}, pricing: {input, output, cacheCreation?, cacheRead?} } | null}
@@ -352,7 +52,7 @@ function resolveModelMetadata(modelId) {
352
52
  if (id === key.toLowerCase()) return meta;
353
53
  }
354
54
 
355
- // Alias match canonical
55
+ // Alias match -> canonical
356
56
  for (const [alias, canonical] of Object.entries(MODEL_ALIASES)) {
357
57
  if (id === alias.toLowerCase()) {
358
58
  const meta = MODEL_METADATA[canonical];
@@ -360,7 +60,7 @@ function resolveModelMetadata(modelId) {
360
60
  }
361
61
  }
362
62
 
363
- // Prefix match (e.g. "claude-sonnet-4-6-xxx" matches "claude-sonnet-4-6")
63
+ // Prefix match
364
64
  for (const [key, meta] of Object.entries(MODEL_METADATA)) {
365
65
  if (id.startsWith(key.toLowerCase())) return meta;
366
66
  }
@@ -369,47 +69,99 @@ function resolveModelMetadata(modelId) {
369
69
  if (id.startsWith('claude-')) {
370
70
  return {
371
71
  limit: { context: 200000, output: 32000 },
372
- pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.30 }
72
+ pricing: { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 }
373
73
  };
374
74
  }
375
75
 
376
76
  return null;
377
77
  }
378
78
 
379
- /**
380
- * Get just the limit (context + output) for a model.
381
- * @param {string} modelId
382
- * @returns {{ context: number, output: number } | null}
383
- */
384
79
  function resolveModelLimit(modelId) {
385
80
  const meta = resolveModelMetadata(modelId);
386
81
  return meta ? meta.limit : null;
387
82
  }
388
83
 
389
- /**
390
- * Get just the pricing for a model.
391
- * @param {string} modelId
392
- * @returns {{ input: number, output: number, cacheCreation?: number, cacheRead?: number } | null}
393
- */
394
84
  function resolveModelPricing(modelId) {
395
85
  const meta = resolveModelMetadata(modelId);
396
86
  return meta ? meta.pricing : null;
397
87
  }
398
88
 
399
- /**
400
- * Get all model IDs in the metadata table.
401
- * @returns {string[]}
402
- */
403
89
  function getAllModelIds() {
404
90
  return Object.keys(MODEL_METADATA);
405
91
  }
406
92
 
93
+ function getDefaultModels() {
94
+ return {
95
+ claude: [...(DEFAULT_MODELS.claude || [])],
96
+ codex: [...(DEFAULT_MODELS.codex || [])],
97
+ gemini: [...(DEFAULT_MODELS.gemini || [])]
98
+ };
99
+ }
100
+
101
+ function getDefaultModelsByToolType(toolType) {
102
+ const key = String(toolType || '').trim().toLowerCase();
103
+ if (key === 'openai_compatible') return [...(DEFAULT_MODELS.codex || [])];
104
+ if (key === 'claude' || key === 'codex' || key === 'gemini') {
105
+ return [...(DEFAULT_MODELS[key] || [])];
106
+ }
107
+ return [];
108
+ }
109
+
110
+ function getDefaultSpeedTestModels() {
111
+ const fileConfig = loadMetadataConfigFromFile();
112
+ const raw = fileConfig.defaultSpeedTestModels || DEFAULT_SPEED_TEST_MODELS;
113
+ return {
114
+ claude: normalizeNonEmptyString(raw.claude) || DEFAULT_SPEED_TEST_MODELS.claude,
115
+ codex: normalizeNonEmptyString(raw.codex) || DEFAULT_SPEED_TEST_MODELS.codex,
116
+ gemini: normalizeNonEmptyString(raw.gemini) || DEFAULT_SPEED_TEST_MODELS.gemini
117
+ };
118
+ }
119
+
120
+ function getDefaultSpeedTestModelByToolType(toolType) {
121
+ const key = String(toolType || '').trim().toLowerCase();
122
+ const defaults = getDefaultSpeedTestModels();
123
+ if (key === 'openai_compatible') return defaults.codex;
124
+ if (key === 'claude' || key === 'codex' || key === 'gemini') {
125
+ return defaults[key];
126
+ }
127
+ return defaults.codex;
128
+ }
129
+
130
+ function saveDefaultSpeedTestModels(nextDefaults) {
131
+ const current = loadMetadataConfigFromFile();
132
+ const merged = {
133
+ ...getDefaultSpeedTestModels(),
134
+ ...(nextDefaults || {})
135
+ };
136
+
137
+ const normalized = {
138
+ claude: normalizeNonEmptyString(merged.claude) || DEFAULT_SPEED_TEST_MODELS.claude,
139
+ codex: normalizeNonEmptyString(merged.codex) || DEFAULT_SPEED_TEST_MODELS.codex,
140
+ gemini: normalizeNonEmptyString(merged.gemini) || DEFAULT_SPEED_TEST_MODELS.gemini
141
+ };
142
+
143
+ const nextConfig = {
144
+ ...current,
145
+ defaultSpeedTestModels: normalized
146
+ };
147
+ fs.writeFileSync(METADATA_FILE_PATH, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
148
+ metadataConfig.defaultSpeedTestModels = normalized;
149
+ return normalized;
150
+ }
151
+
407
152
  module.exports = {
408
153
  MODEL_METADATA,
409
154
  MODEL_ALIASES,
155
+ DEFAULT_MODELS,
156
+ DEFAULT_SPEED_TEST_MODELS,
410
157
  resolveModelMetadata,
411
158
  resolveModelLimit,
412
159
  resolveModelPricing,
413
160
  getAllModelIds,
414
- METADATA_LAST_UPDATED: '2026-02-27'
161
+ getDefaultModels,
162
+ getDefaultModelsByToolType,
163
+ getDefaultSpeedTestModels,
164
+ getDefaultSpeedTestModelByToolType,
165
+ saveDefaultSpeedTestModels,
166
+ METADATA_LAST_UPDATED: metadataConfig.lastUpdated || '2026-02-27'
415
167
  };