@tyvm/knowhow 0.0.104 → 0.0.106

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 (233) hide show
  1. package/CONFIG.md +8 -5
  2. package/package.json +3 -2
  3. package/scripts/check-model-pricing.ts +509 -0
  4. package/scripts/compare-openrouter-coverage.ts +576 -0
  5. package/src/agents/base/base.ts +127 -2
  6. package/src/agents/tools/execCommand.ts +4 -0
  7. package/src/agents/tools/executeScript/definition.ts +1 -1
  8. package/src/agents/tools/index.ts +0 -1
  9. package/src/agents/tools/list.ts +3 -43
  10. package/src/agents/tools/writeFile.ts +1 -1
  11. package/src/auth/browserLogin.ts +9 -4
  12. package/src/chat/modules/RemoteSyncModule.ts +3 -0
  13. package/src/cli.ts +31 -1
  14. package/src/clients/cerebras.ts +10 -0
  15. package/src/clients/contextLimits.ts +7 -2
  16. package/src/clients/copilot.ts +23 -0
  17. package/src/clients/deepseek.ts +16 -0
  18. package/src/clients/fireworks.ts +15 -0
  19. package/src/clients/gemini.ts +45 -2
  20. package/src/clients/github.ts +16 -0
  21. package/src/clients/groq.ts +15 -0
  22. package/src/clients/http.ts +190 -6
  23. package/src/clients/index.ts +215 -9
  24. package/src/clients/llama.ts +16 -0
  25. package/src/clients/mistral.ts +16 -0
  26. package/src/clients/nvidia.ts +16 -0
  27. package/src/clients/openai.ts +41 -11
  28. package/src/clients/openrouter.ts +17 -0
  29. package/src/clients/pricing/anthropic.ts +105 -78
  30. package/src/clients/pricing/cerebras.ts +11 -0
  31. package/src/clients/pricing/copilot.ts +60 -0
  32. package/src/clients/pricing/deepseek.ts +15 -0
  33. package/src/clients/pricing/fireworks.ts +32 -0
  34. package/src/clients/pricing/github.ts +69 -0
  35. package/src/clients/pricing/google.ts +245 -206
  36. package/src/clients/pricing/groq.ts +56 -0
  37. package/src/clients/pricing/index.ts +43 -6
  38. package/src/clients/pricing/llama.ts +18 -0
  39. package/src/clients/pricing/mistral.ts +34 -0
  40. package/src/clients/pricing/models.ts +23 -0
  41. package/src/clients/pricing/nvidia.ts +102 -0
  42. package/src/clients/pricing/openai.ts +347 -171
  43. package/src/clients/pricing/openrouter.ts +36 -0
  44. package/src/clients/pricing/types.ts +110 -0
  45. package/src/clients/pricing/xai.ts +123 -66
  46. package/src/clients/types.ts +4 -0
  47. package/src/clients/xai.ts +152 -2
  48. package/src/fileSync.ts +8 -2
  49. package/src/login.ts +11 -3
  50. package/src/services/AgentSyncFs.ts +36 -12
  51. package/src/services/KnowhowClient.ts +11 -0
  52. package/src/services/LazyToolsService.ts +6 -0
  53. package/src/services/S3.ts +0 -7
  54. package/src/services/SyncedAgentWatcher.ts +13 -298
  55. package/src/services/index.ts +1 -0
  56. package/src/services/modules/index.ts +11 -2
  57. package/src/services/watchers/FsSyncer.ts +155 -0
  58. package/src/services/watchers/RemoteSyncer.ts +153 -0
  59. package/src/services/watchers/index.ts +2 -0
  60. package/src/types.ts +56 -279
  61. package/src/worker.ts +174 -0
  62. package/tests/clients/pricing.test.ts +37 -0
  63. package/tests/manual/clients/completions.json +838 -226
  64. package/tests/manual/clients/completions.test.ts +46 -31
  65. package/ts_build/package.json +3 -2
  66. package/ts_build/src/agents/base/base.d.ts +17 -1
  67. package/ts_build/src/agents/base/base.js +82 -1
  68. package/ts_build/src/agents/base/base.js.map +1 -1
  69. package/ts_build/src/agents/tools/execCommand.js +3 -0
  70. package/ts_build/src/agents/tools/execCommand.js.map +1 -1
  71. package/ts_build/src/agents/tools/executeScript/definition.js +1 -1
  72. package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -1
  73. package/ts_build/src/agents/tools/index.d.ts +0 -1
  74. package/ts_build/src/agents/tools/index.js +0 -1
  75. package/ts_build/src/agents/tools/index.js.map +1 -1
  76. package/ts_build/src/agents/tools/list.js +3 -38
  77. package/ts_build/src/agents/tools/list.js.map +1 -1
  78. package/ts_build/src/agents/tools/visionTool.d.ts +1 -1
  79. package/ts_build/src/agents/tools/writeFile.js +1 -1
  80. package/ts_build/src/agents/tools/writeFile.js.map +1 -1
  81. package/ts_build/src/ai.d.ts +1 -1
  82. package/ts_build/src/auth/browserLogin.d.ts +2 -1
  83. package/ts_build/src/auth/browserLogin.js +10 -3
  84. package/ts_build/src/auth/browserLogin.js.map +1 -1
  85. package/ts_build/src/chat/modules/RemoteSyncModule.js +1 -0
  86. package/ts_build/src/chat/modules/RemoteSyncModule.js.map +1 -1
  87. package/ts_build/src/cli.js +19 -0
  88. package/ts_build/src/cli.js.map +1 -1
  89. package/ts_build/src/clients/anthropic.d.ts +1 -82
  90. package/ts_build/src/clients/cerebras.d.ts +4 -0
  91. package/ts_build/src/clients/cerebras.js +14 -0
  92. package/ts_build/src/clients/cerebras.js.map +1 -0
  93. package/ts_build/src/clients/contextLimits.js +7 -2
  94. package/ts_build/src/clients/contextLimits.js.map +1 -1
  95. package/ts_build/src/clients/copilot.d.ts +4 -0
  96. package/ts_build/src/clients/copilot.js +15 -0
  97. package/ts_build/src/clients/copilot.js.map +1 -0
  98. package/ts_build/src/clients/deepseek.d.ts +4 -0
  99. package/ts_build/src/clients/deepseek.js +15 -0
  100. package/ts_build/src/clients/deepseek.js.map +1 -0
  101. package/ts_build/src/clients/fireworks.d.ts +4 -0
  102. package/ts_build/src/clients/fireworks.js +15 -0
  103. package/ts_build/src/clients/fireworks.js.map +1 -0
  104. package/ts_build/src/clients/gemini.d.ts +1 -0
  105. package/ts_build/src/clients/gemini.js +28 -1
  106. package/ts_build/src/clients/gemini.js.map +1 -1
  107. package/ts_build/src/clients/github.d.ts +4 -0
  108. package/ts_build/src/clients/github.js +15 -0
  109. package/ts_build/src/clients/github.js.map +1 -0
  110. package/ts_build/src/clients/groq.d.ts +4 -0
  111. package/ts_build/src/clients/groq.js +15 -0
  112. package/ts_build/src/clients/groq.js.map +1 -0
  113. package/ts_build/src/clients/http.d.ts +22 -1
  114. package/ts_build/src/clients/http.js +132 -7
  115. package/ts_build/src/clients/http.js.map +1 -1
  116. package/ts_build/src/clients/index.d.ts +22 -0
  117. package/ts_build/src/clients/index.js +150 -5
  118. package/ts_build/src/clients/index.js.map +1 -1
  119. package/ts_build/src/clients/llama.d.ts +4 -0
  120. package/ts_build/src/clients/llama.js +15 -0
  121. package/ts_build/src/clients/llama.js.map +1 -0
  122. package/ts_build/src/clients/mistral.d.ts +4 -0
  123. package/ts_build/src/clients/mistral.js +15 -0
  124. package/ts_build/src/clients/mistral.js.map +1 -0
  125. package/ts_build/src/clients/nvidia.d.ts +4 -0
  126. package/ts_build/src/clients/nvidia.js +15 -0
  127. package/ts_build/src/clients/nvidia.js.map +1 -0
  128. package/ts_build/src/clients/openai.d.ts +4 -206
  129. package/ts_build/src/clients/openai.js +27 -9
  130. package/ts_build/src/clients/openai.js.map +1 -1
  131. package/ts_build/src/clients/openrouter.d.ts +4 -0
  132. package/ts_build/src/clients/openrouter.js +15 -0
  133. package/ts_build/src/clients/openrouter.js.map +1 -0
  134. package/ts_build/src/clients/pricing/anthropic.d.ts +26 -78
  135. package/ts_build/src/clients/pricing/anthropic.js +75 -78
  136. package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
  137. package/ts_build/src/clients/pricing/cerebras.d.ts +4 -0
  138. package/ts_build/src/clients/pricing/cerebras.js +11 -0
  139. package/ts_build/src/clients/pricing/cerebras.js.map +1 -0
  140. package/ts_build/src/clients/pricing/copilot.d.ts +5 -0
  141. package/ts_build/src/clients/pricing/copilot.js +35 -0
  142. package/ts_build/src/clients/pricing/copilot.js.map +1 -0
  143. package/ts_build/src/clients/pricing/deepseek.d.ts +5 -0
  144. package/ts_build/src/clients/pricing/deepseek.js +10 -0
  145. package/ts_build/src/clients/pricing/deepseek.js.map +1 -0
  146. package/ts_build/src/clients/pricing/fireworks.d.ts +5 -0
  147. package/ts_build/src/clients/pricing/fireworks.js +21 -0
  148. package/ts_build/src/clients/pricing/fireworks.js.map +1 -0
  149. package/ts_build/src/clients/pricing/github.d.ts +4 -0
  150. package/ts_build/src/clients/pricing/github.js +58 -0
  151. package/ts_build/src/clients/pricing/github.js.map +1 -0
  152. package/ts_build/src/clients/pricing/google.d.ts +59 -6
  153. package/ts_build/src/clients/pricing/google.js +214 -167
  154. package/ts_build/src/clients/pricing/google.js.map +1 -1
  155. package/ts_build/src/clients/pricing/groq.d.ts +5 -0
  156. package/ts_build/src/clients/pricing/groq.js +41 -0
  157. package/ts_build/src/clients/pricing/groq.js.map +1 -0
  158. package/ts_build/src/clients/pricing/index.d.ts +17 -6
  159. package/ts_build/src/clients/pricing/index.js +65 -10
  160. package/ts_build/src/clients/pricing/index.js.map +1 -1
  161. package/ts_build/src/clients/pricing/llama.d.ts +4 -0
  162. package/ts_build/src/clients/pricing/llama.js +14 -0
  163. package/ts_build/src/clients/pricing/llama.js.map +1 -0
  164. package/ts_build/src/clients/pricing/mistral.d.ts +5 -0
  165. package/ts_build/src/clients/pricing/mistral.js +23 -0
  166. package/ts_build/src/clients/pricing/mistral.js.map +1 -0
  167. package/ts_build/src/clients/pricing/models.d.ts +9 -0
  168. package/ts_build/src/clients/pricing/models.js +19 -0
  169. package/ts_build/src/clients/pricing/models.js.map +1 -0
  170. package/ts_build/src/clients/pricing/nvidia.d.ts +8 -0
  171. package/ts_build/src/clients/pricing/nvidia.js +96 -0
  172. package/ts_build/src/clients/pricing/nvidia.js.map +1 -0
  173. package/ts_build/src/clients/pricing/openai.d.ts +86 -197
  174. package/ts_build/src/clients/pricing/openai.js +294 -168
  175. package/ts_build/src/clients/pricing/openai.js.map +1 -1
  176. package/ts_build/src/clients/pricing/openrouter.d.ts +4 -0
  177. package/ts_build/src/clients/pricing/openrouter.js +29 -0
  178. package/ts_build/src/clients/pricing/openrouter.js.map +1 -0
  179. package/ts_build/src/clients/pricing/types.d.ts +46 -0
  180. package/ts_build/src/clients/pricing/types.js +49 -0
  181. package/ts_build/src/clients/pricing/types.js.map +1 -0
  182. package/ts_build/src/clients/pricing/xai.d.ts +39 -64
  183. package/ts_build/src/clients/pricing/xai.js +93 -60
  184. package/ts_build/src/clients/pricing/xai.js.map +1 -1
  185. package/ts_build/src/clients/types.d.ts +1 -0
  186. package/ts_build/src/clients/xai.d.ts +2 -58
  187. package/ts_build/src/clients/xai.js +123 -2
  188. package/ts_build/src/clients/xai.js.map +1 -1
  189. package/ts_build/src/fileSync.js +7 -2
  190. package/ts_build/src/fileSync.js.map +1 -1
  191. package/ts_build/src/login.js +8 -2
  192. package/ts_build/src/login.js.map +1 -1
  193. package/ts_build/src/services/AgentSyncFs.js +1 -0
  194. package/ts_build/src/services/AgentSyncFs.js.map +1 -1
  195. package/ts_build/src/services/KnowhowClient.d.ts +1 -0
  196. package/ts_build/src/services/KnowhowClient.js +7 -0
  197. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  198. package/ts_build/src/services/LazyToolsService.d.ts +1 -0
  199. package/ts_build/src/services/LazyToolsService.js +3 -0
  200. package/ts_build/src/services/LazyToolsService.js.map +1 -1
  201. package/ts_build/src/services/S3.js +0 -7
  202. package/ts_build/src/services/S3.js.map +1 -1
  203. package/ts_build/src/services/SyncedAgentWatcher.d.ts +0 -51
  204. package/ts_build/src/services/SyncedAgentWatcher.js +1 -282
  205. package/ts_build/src/services/SyncedAgentWatcher.js.map +1 -1
  206. package/ts_build/src/services/index.d.ts +1 -0
  207. package/ts_build/src/services/index.js +1 -0
  208. package/ts_build/src/services/index.js.map +1 -1
  209. package/ts_build/src/services/modules/index.js +41 -1
  210. package/ts_build/src/services/modules/index.js.map +1 -1
  211. package/ts_build/src/services/watchers/FsSyncer.d.ts +27 -0
  212. package/ts_build/src/services/watchers/FsSyncer.js +135 -0
  213. package/ts_build/src/services/watchers/FsSyncer.js.map +1 -0
  214. package/ts_build/src/services/watchers/RemoteSyncer.d.ts +28 -0
  215. package/ts_build/src/services/watchers/RemoteSyncer.js +126 -0
  216. package/ts_build/src/services/watchers/RemoteSyncer.js.map +1 -0
  217. package/ts_build/src/services/watchers/index.d.ts +2 -0
  218. package/ts_build/src/services/watchers/index.js +19 -0
  219. package/ts_build/src/services/watchers/index.js.map +1 -0
  220. package/ts_build/src/types.d.ts +163 -124
  221. package/ts_build/src/types.js +33 -213
  222. package/ts_build/src/types.js.map +1 -1
  223. package/ts_build/src/worker.d.ts +4 -0
  224. package/ts_build/src/worker.js +140 -0
  225. package/ts_build/src/worker.js.map +1 -1
  226. package/ts_build/tests/clients/pricing.test.js +21 -0
  227. package/ts_build/tests/clients/pricing.test.js.map +1 -1
  228. package/ts_build/tests/manual/clients/completions.test.js +27 -24
  229. package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
  230. package/src/clients/pricing/catalog.ts +0 -287
  231. package/ts_build/src/clients/pricing/catalog.d.ts +0 -28
  232. package/ts_build/src/clients/pricing/catalog.js +0 -179
  233. package/ts_build/src/clients/pricing/catalog.js.map +0 -1
package/src/types.ts CHANGED
@@ -4,7 +4,6 @@ import { WebSocket } from "ws";
4
4
  export type Hashes = {
5
5
  [file: string]: {
6
6
  [promptHash: string]: string;
7
-
8
7
  promptHash: string;
9
8
  fileHash: string;
10
9
  };
@@ -38,37 +37,25 @@ export type Config = {
38
37
  openaiBaseUrl?: string;
39
38
  promptsDir: string;
40
39
  lintCommands?: { [fileExtension: string]: string };
41
-
40
+ orgId?: string;
42
41
  syncRemote?: boolean;
43
-
44
42
  micCommand?: string;
45
43
  defaultMic?: string;
46
-
47
44
  sources: GenerationSource[];
48
45
  embedSources: EmbedSource[];
49
46
  embeddingModel: string;
50
-
51
47
  skills?: string[];
52
-
53
48
  plugins: { enabled: string[]; disabled: string[] };
54
-
55
49
  chat?: {
56
- /** Path to a custom root chat module (npm package or local file) */
57
50
  rootModule?: string;
58
- /** Path to a custom renderer (npm package or local file, can be .ts) */
59
51
  renderer?: string;
60
- /** Additional chat modules to load (npm packages or local files, can be .ts) */
61
52
  modules?: string[];
62
53
  };
63
-
64
54
  modules: string[];
65
-
66
55
  pluginPackages?: Record<string, string>;
67
-
68
56
  agents: Assistant[];
69
57
  mcps: McpConfig[];
70
58
  modelProviders: ModelProvider[];
71
-
72
59
  ycmd?: {
73
60
  enabled?: boolean;
74
61
  installPath?: string;
@@ -76,13 +63,11 @@ export type Config = {
76
63
  logLevel?: "debug" | "info" | "warning" | "error";
77
64
  completionTimeout?: number;
78
65
  };
79
-
80
66
  files?: {
81
- remotePath: string; // path in Knowhow FS, e.g. "/docs/readme.md" or "/configs/"
82
- localPath: string; // local path to write to, e.g. "./docs/readme.md" or "./configs/"
83
- direction?: "download" | "upload" | "sync"; // default: "download"
67
+ remotePath: string;
68
+ localPath: string;
69
+ direction?: "download" | "upload" | "sync";
84
70
  }[];
85
-
86
71
  worker?: {
87
72
  allowedTools?: string[];
88
73
  workerId?: string;
@@ -92,24 +77,20 @@ export type Config = {
92
77
  auth?: {
93
78
  required?: boolean;
94
79
  passkey?: {
95
- publicKey?: string; // base64-encoded public key
96
- credentialId?: string; // base64-encoded credential ID
97
- algorithm?: string; // e.g. "ES256"
80
+ publicKey?: string;
81
+ credentialId?: string;
82
+ algorithm?: string;
98
83
  };
99
84
  sessionDurationHours?: number;
100
85
  };
101
- commandAuth?: {
102
- [toolName: string]: "always" | "session" | "never";
103
- };
86
+ commandAuth?: { [toolName: string]: "always" | "session" | "never" };
104
87
  tunnel?: {
105
88
  enabled?: boolean;
106
89
  allowedPorts?: number[];
107
90
  maxConcurrentStreams?: number;
108
- portMapping?: {
109
- [containerPort: number]: number; // containerPort -> hostPort
110
- };
111
- localHost?: string; // Default: "127.0.0.1", can be "host.docker.internal" for Docker
112
- enableUrlRewriting?: boolean; // Enable URL rewriting for localhost URLs (default: true)
91
+ portMapping?: { [containerPort: number]: number };
92
+ localHost?: string;
93
+ enableUrlRewriting?: boolean;
113
94
  };
114
95
  };
115
96
  };
@@ -124,7 +105,7 @@ export type Assistant = {
124
105
 
125
106
  export type McpConfig = {
126
107
  name: string;
127
- autoConnect?: boolean; // Default: true - connect at startup. Set to false for on-demand connection
108
+ autoConnect?: boolean;
128
109
  command?: string;
129
110
  url?: string;
130
111
  args?: string[];
@@ -140,6 +121,10 @@ export type ModelProvider = {
140
121
  envKey?: string;
141
122
  headers?: { [key: string]: string };
142
123
  jwtFile?: string;
124
+ timeout?: number;
125
+ extra_body?: Record<string, any>;
126
+ /** Optional pricing map (model id → per-million-token prices) passed to HttpClient.setPrices() */
127
+ pricing?: Record<string, { input?: number; output?: number; cached_input?: number; cache_hit?: number }>;
143
128
  };
144
129
 
145
130
  export type AssistantConfig = {
@@ -153,17 +138,13 @@ export interface Embeddable<T = any> {
153
138
  metadata: T;
154
139
  }
155
140
 
156
- export type MinimalEmbedding<T = any> = Pick<
157
- Embeddable<T>,
158
- "id" | "text" | "metadata"
159
- >;
141
+ export type MinimalEmbedding<T = any> = Pick<Embeddable<T>, "id" | "text" | "metadata">;
160
142
 
161
143
  export interface EmbeddingBase<T = any> extends Embeddable<T> {
162
144
  similarity?: number;
163
145
  }
164
146
 
165
147
  export type GptQuestionEmbedding = any & EmbeddingBase;
166
-
167
148
  export type DatasourceType = "file" | "url" | "text";
168
149
 
169
150
  export interface IDatasource {
@@ -186,150 +167,55 @@ export type ChatInteraction = {
186
167
  taskId: string;
187
168
  };
188
169
 
189
- export const Models = {
190
- anthropic: {
191
- Opus4_6: "claude-opus-4-6",
192
- Sonnet4_6: "claude-sonnet-4-6",
193
- Opus4_5: "claude-opus-4-5",
194
- Opus4: "claude-opus-4",
195
- Opus4_1: "claude-opus-4-1",
196
- Sonnet4_5: "claude-sonnet-4-5",
197
- Haiku4_5: "claude-haiku-4-5",
198
- Sonnet4: "claude-sonnet-4",
199
- Sonnet3_7: "claude-3-7-sonnet",
200
- Sonnet3_5: "claude-3-5-sonnet",
201
- Opus3: "claude-3-opus",
202
- Haiku3: "claude-3-haiku",
203
- },
204
- xai: {
205
- Grok_4_20_Reasoning: "grok-4.20-0309-reasoning",
206
- Grok_4_20_NonReasoning: "grok-4.20-0309-non-reasoning",
207
- Grok4_1_Fast_Reasoning: "grok-4-1-fast-reasoning",
208
- Grok4_1_Fast_NonReasoning: "grok-4-1-fast-non-reasoning",
209
- GrokCodeFast: "grok-code-fast-1",
210
- Grok4: "grok-4-0709",
211
- Grok3Beta: "grok-3-beta",
212
- Grok3MiniBeta: "grok-3-mini-beta",
213
- Grok3FastBeta: "grok-3-fast-beta",
214
- Grok3MiniFastBeta: "grok-3-mini-fast-beta",
215
- Grok21212: "grok-2-1212",
216
- Grok2Vision1212: "grok-2-vision-1212",
217
- GrokImagineImage: "grok-imagine-image",
218
- GrokImagineVideo: "grok-imagine-video",
219
- },
220
- openai: {
221
- GPT_5_2: "gpt-5.2",
222
- GPT_5_1: "gpt-5.1",
223
- GPT_54: "gpt-5.4",
224
- GPT_54_Mini: "gpt-5.4-mini",
225
- GPT_54_Nano: "gpt-5.4-nano",
226
- GPT_54_Pro: "gpt-5.4-pro",
227
- GPT_53_Chat: "gpt-5.3-chat-latest",
228
- GPT_53_Codex: "gpt-5.3-codex",
229
- GPT_5: "gpt-5",
230
- GPT_5_Mini: "gpt-5-mini",
231
- GPT_5_Nano: "gpt-5-nano",
232
- GPT_41: "gpt-4.1-2025-04-14",
233
- GPT_41_Mini: "gpt-4.1-mini-2025-04-14",
234
- GPT_41_Nano: "gpt-4.1-nano-2025-04-14",
235
- GPT_45: "gpt-4.5-preview-2025-02-27",
236
- GPT_4o: "gpt-4o-2024-08-06",
237
- GPT_4o_Audio: "gpt-4o-audio-preview-2024-12-17",
238
- GPT_4o_Realtime: "gpt-4o-realtime-preview-2024-12-17",
239
- GPT_4o_Mini: "gpt-4o-mini-2024-07-18",
240
- GPT_4o_Mini_Audio: "gpt-4o-mini-audio-preview-2024-12-17",
241
- GPT_4o_Mini_Realtime: "gpt-4o-mini-realtime-preview-2024-12-17",
242
- o1: "o1-2024-12-17",
243
- o1_Pro: "o1-pro-2025-03-19",
244
- o3: "o3-2025-04-16",
245
- o3_Pro: "o3-pro-2025-01-31",
246
- o4_Mini: "o4-mini-2025-04-16",
247
- o3_Mini: "o3-mini-2025-01-31",
248
- o1_Mini: "o1-mini-2024-09-12",
249
- GPT_4o_Mini_Search: "gpt-4o-mini-search-preview-2025-03-11",
250
- GPT_4o_Search: "gpt-4o-search-preview-2025-03-11",
170
+ // ─── Model IDs ────────────────────────────────────────────────────────────────
171
+ // Each provider's pricing file is the single source of truth.
172
+
173
+ import { AnthropicModels, AnthropicTextModels } from "./clients/pricing/anthropic";
174
+ import {
175
+ OpenAiModels, OpenAiEmbeddingModels,
176
+ OpenAiReasoningModels, OpenAiChatModels, OpenAiEmbeddingModelsList,
177
+ OpenAiResponsesOnlyModels, OpenAiImageModels, OpenAiVideoModels,
178
+ OpenAiTTSModels, OpenAiTranscriptionModels, OpenAiRealtimeModels,
179
+ } from "./clients/pricing/openai";
180
+ import {
181
+ GoogleModels, GoogleEmbeddingModels,
182
+ GoogleTextModels, GoogleImageModels, GoogleVideoModels,
183
+ GoogleTTSModels, GoogleEmbeddingModelsList, GoogleThinkingLevelModels, GoogleThinkingBudgetModels,
184
+ } from "./clients/pricing/google";
185
+ import {
186
+ XaiModels, XaiTextModels, XaiImageModels, XaiVideoModels, XaiResponsesOnlyModels, XaiReasoningModels,
187
+ } from "./clients/pricing/xai";
251
188
 
252
- GPT_4o_Transcribe: "gpt-4o-transcribe",
253
- GPT_4o_Mini_Transcribe: "gpt-4o-mini-transcribe",
254
- GPT_Realtime_15: "gpt-realtime-1.5",
255
- GPT_Realtime_Mini: "gpt-realtime-mini",
256
- GPT_Image_15: "gpt-image-1.5",
257
- GPT_Image_1_Mini: "gpt-image-1-mini",
258
- TTS_1: "tts-1",
259
- Whisper_1: "whisper-1",
260
- DALL_E_3: "dall-e-3",
261
- DALL_E_2: "dall-e-2",
262
- Sora: "sora",
263
- Sora_2: "sora-2",
264
- Sora_2_Pro: "sora-2-pro",
265
- // Computer_Use: "computer-use-preview-2025-03-11",
266
- // Codex_Mini: "codex-mini-latest",
267
- },
268
- google: {
269
- // Gemini 3.x
270
- Gemini_31_Pro_Preview: "gemini-3.1-pro-preview",
271
- Gemini_31_Flash_Image_Preview: "gemini-3.1-flash-image-preview",
272
- Gemini_31_Flash_Lite_Preview: "gemini-3.1-flash-lite-preview",
273
- Gemini_31_Flash_Live_Preview: "gemini-3.1-flash-live-preview",
274
- Gemini_3_Flash_Preview: "gemini-3-flash-preview",
275
- Gemini_3_Pro_Image_Preview: "gemini-3-pro-image-preview",
276
- // Gemini 2.5
277
- Gemini_25_Pro: "gemini-2.5-pro",
278
- Gemini_25_Flash: "gemini-2.5-flash",
279
- Gemini_25_Flash_Lite: "gemini-2.5-flash-lite",
280
- Gemini_25_Flash_Preview: "gemini-2.5-flash-preview-05-20",
281
- Gemini_25_Pro_Preview: "gemini-2.5-pro-preview-05-06",
282
- Gemini_25_Flash_Image: "gemini-2.5-flash-image",
283
- Gemini_25_Flash_Live: "gemini-2.5-flash-live-preview",
284
- Gemini_25_Flash_Native_Audio:
285
- "gemini-2.5-flash-native-audio-preview-12-2025",
286
- Gemini_25_Pro_TTS: "gemini-2.5-pro-preview-tts",
287
- // Gemini 2.0 (deprecated)
288
- Gemini_20_Flash: "gemini-2.0-flash",
289
- Gemini_20_Flash_Preview_Image_Generation:
290
- "gemini-2.0-flash-exp-image-generation",
291
- // Gemini 1.5 (legacy)
292
- Gemini_15_Flash: "gemini-1.5-flash",
293
- Gemini_15_Flash_8B: "gemini-1.5-flash-8b",
294
- Gemini_15_Pro: "gemini-1.5-pro",
295
- // Media generation
296
- Imagen_3: "imagen-4.0-generate-001",
297
- Imagen_4_Fast: "imagen-4.0-fast-generate-001",
298
- Imagen_4_Ultra: "imagen-4.0-ultra-generate-001",
299
- Veo_2: "veo-2.0-generate-001",
300
- Veo_3: "veo-3.0-generate-001",
301
- Veo_3_Fast: "veo-3.0-fast-generate-001",
302
- Veo_3_1: "veo-3.1-generate-preview",
303
- Veo_3_1_Fast: "veo-3.1-fast-generate-preview",
304
- // Audio / Live
305
- Gemini_20_Flash_Live: "gemini-2.0-flash-live-001",
306
- Gemini_25_Flash_TTS: "gemini-2.5-flash-preview-tts",
307
- Gemini_20_Flash_TTS: "gemini-2.0-flash-preview-tts",
308
- },
189
+ export const Models = {
190
+ anthropic: AnthropicModels,
191
+ xai: XaiModels,
192
+ openai: OpenAiModels,
193
+ google: GoogleModels,
309
194
  };
310
195
 
311
196
  export const EmbeddingModels = {
312
- openai: {
313
- EmbeddingAda2: "text-embedding-ada-002",
314
- EmbeddingLarge3: "text-embedding-3-large",
315
- EmbeddingSmall3: "text-embedding-3-small",
316
- },
317
- google: {
318
- Gemini_Embedding: "gemini-embedding-exp",
319
- Gemini_Embedding_001: "gemini-embedding-001",
320
- },
197
+ openai: OpenAiEmbeddingModels,
198
+ google: GoogleEmbeddingModels,
199
+ };
200
+
201
+ // Re-export modality arrays for consumers
202
+ export {
203
+ OpenAiReasoningModels, OpenAiChatModels, OpenAiEmbeddingModelsList,
204
+ OpenAiResponsesOnlyModels, OpenAiImageModels, OpenAiVideoModels,
205
+ OpenAiTTSModels, OpenAiTranscriptionModels, OpenAiRealtimeModels,
206
+ GoogleTextModels as GoogleReasoningModels,
207
+ GoogleImageModels, GoogleVideoModels, GoogleTTSModels, GoogleEmbeddingModelsList,
208
+ GoogleThinkingLevelModels, GoogleThinkingBudgetModels,
209
+ AnthropicTextModels,
210
+ XaiTextModels, XaiImageModels, XaiVideoModels, XaiResponsesOnlyModels, XaiReasoningModels,
321
211
  };
322
212
 
323
- export function getEnabledPlugins(
324
- plugins: Config["plugins"] | undefined
325
- ): string[] {
213
+ export function getEnabledPlugins(plugins: Config["plugins"] | undefined): string[] {
326
214
  if (!plugins) return [];
327
215
  return plugins.enabled ?? [];
328
216
  }
329
217
 
330
- export function getDisabledPlugins(
331
- plugins: Config["plugins"] | undefined
332
- ): string[] {
218
+ export function getDisabledPlugins(plugins: Config["plugins"] | undefined): string[] {
333
219
  if (!plugins) return [];
334
220
  return plugins.disabled ?? [];
335
221
  }
@@ -338,112 +224,3 @@ export const Providers = Object.keys(Models).reduce((obj, key) => {
338
224
  obj[key] = key;
339
225
  return obj;
340
226
  }, {}) as { [key in keyof typeof Models]: keyof typeof Models };
341
-
342
- export const OpenAiReasoningModels = [
343
- Models.openai.o1,
344
- Models.openai.o1_Mini,
345
- Models.openai.o3_Mini,
346
- Models.openai.o3,
347
- Models.openai.o3_Pro,
348
- Models.openai.o4_Mini,
349
- Models.openai.GPT_54,
350
- Models.openai.GPT_54_Mini,
351
- Models.openai.GPT_54_Nano,
352
- Models.openai.GPT_54_Pro,
353
- Models.openai.GPT_53_Chat,
354
- Models.openai.GPT_53_Codex,
355
- Models.openai.GPT_5,
356
- Models.openai.GPT_5_Mini,
357
- Models.openai.GPT_5_Nano,
358
- Models.openai.GPT_5_1,
359
- Models.openai.GPT_5_2,
360
- ];
361
-
362
- export const OpenAiEmbeddingModels = [
363
- EmbeddingModels.openai.EmbeddingAda2,
364
- EmbeddingModels.openai.EmbeddingLarge3,
365
- EmbeddingModels.openai.EmbeddingSmall3,
366
- ];
367
-
368
- // Models that ONLY support the Responses API (not Chat Completions)
369
- export const OpenAiResponsesOnlyModels = [
370
- Models.openai.GPT_53_Codex,
371
- Models.openai.GPT_54,
372
- Models.openai.GPT_54_Mini,
373
- Models.openai.GPT_54_Nano,
374
- Models.openai.GPT_54_Pro,
375
- ];
376
-
377
- export const GoogleReasoningModels = [
378
- Models.google.Gemini_31_Pro_Preview,
379
- Models.google.Gemini_31_Flash_Lite_Preview,
380
- Models.google.Gemini_3_Flash_Preview,
381
- Models.google.Gemini_25_Pro,
382
- Models.google.Gemini_25_Flash,
383
- Models.google.Gemini_25_Flash_Lite,
384
- Models.google.Gemini_25_Flash_Preview,
385
- Models.google.Gemini_25_Pro_Preview,
386
- Models.google.Gemini_20_Flash,
387
- Models.google.Gemini_15_Flash,
388
- Models.google.Gemini_15_Flash_8B,
389
- Models.google.Gemini_15_Pro,
390
- ];
391
-
392
- export const GoogleImageModels = [
393
- Models.google.Gemini_31_Flash_Image_Preview,
394
- Models.google.Gemini_3_Pro_Image_Preview,
395
- Models.google.Gemini_25_Flash_Image,
396
- Models.google.Gemini_20_Flash_Preview_Image_Generation,
397
- Models.google.Imagen_3,
398
- Models.google.Imagen_4_Fast,
399
- Models.google.Imagen_4_Ultra,
400
- ];
401
-
402
- export const OpenAiImageModels = [
403
- Models.openai.DALL_E_3,
404
- Models.openai.DALL_E_2,
405
- Models.openai.GPT_Image_15,
406
- Models.openai.GPT_Image_1_Mini,
407
- ];
408
-
409
- export const OpenAiVideoModels = [
410
- Models.openai.Sora,
411
- Models.openai.Sora_2,
412
- Models.openai.Sora_2_Pro,
413
- ];
414
-
415
- export const OpenAiTTSModels = [Models.openai.TTS_1];
416
-
417
- export const XaiImageModels = [Models.xai.GrokImagineImage];
418
- export const OpenAiTranscriptionModels = [
419
- Models.openai.Whisper_1,
420
- Models.openai.GPT_4o_Transcribe,
421
- Models.openai.GPT_4o_Mini_Transcribe,
422
- ];
423
-
424
- export const OpenAiRealtimeModels = [
425
- Models.openai.GPT_4o_Realtime,
426
- Models.openai.GPT_4o_Mini_Realtime,
427
- Models.openai.GPT_Realtime_15,
428
- Models.openai.GPT_Realtime_Mini,
429
- ];
430
- export const XaiVideoModels = [Models.xai.GrokImagineVideo];
431
-
432
- export const GoogleTTSModels = [
433
- Models.google.Gemini_25_Flash_TTS,
434
- Models.google.Gemini_25_Pro_TTS,
435
- Models.google.Gemini_20_Flash_TTS,
436
- ];
437
-
438
- export const GoogleVideoModels = [
439
- Models.google.Veo_2,
440
- Models.google.Veo_3,
441
- Models.google.Veo_3_Fast,
442
- Models.google.Veo_3_1,
443
- Models.google.Veo_3_1_Fast,
444
- ];
445
-
446
- export const GoogleEmbeddingModels = [
447
- EmbeddingModels.google.Gemini_Embedding,
448
- EmbeddingModels.google.Gemini_Embedding_001,
449
- ];
package/src/worker.ts CHANGED
@@ -578,3 +578,177 @@ export async function worker(options?: {
578
578
  await wait(5000);
579
579
  }
580
580
  }
581
+
582
+ /**
583
+ * Run tunnel-only mode: connects to the Knowhow tunnel WebSocket without
584
+ * registering any MCP tools. Useful for users who only want the web tunnel
585
+ * feature to expose local ports to the cloud.
586
+ */
587
+ export async function tunnel(options?: {
588
+ share?: boolean;
589
+ unshare?: boolean;
590
+ }) {
591
+ const config = await getConfig();
592
+
593
+ const isInsideDocker = process.env.KNOWHOW_DOCKER === "true";
594
+
595
+ // Determine localHost based on environment
596
+ let tunnelLocalHost = config.worker?.tunnel?.localHost;
597
+ if (!tunnelLocalHost) {
598
+ if (isInsideDocker) {
599
+ tunnelLocalHost = "host.docker.internal";
600
+ console.log(
601
+ "🐳 Docker detected: tunnel will use host.docker.internal to reach host services"
602
+ );
603
+ } else {
604
+ tunnelLocalHost = "127.0.0.1";
605
+ }
606
+ }
607
+
608
+ // Check for port mapping configuration
609
+ const portMapping = config.worker?.tunnel?.portMapping || {};
610
+ if (Object.keys(portMapping).length > 0) {
611
+ console.log("🔀 Port mapping configured:");
612
+ for (const [containerPort, hostPort] of Object.entries(portMapping)) {
613
+ console.log(` Container port ${containerPort} → Host port ${hostPort}`);
614
+ }
615
+ }
616
+
617
+ const tunnelPorts = config.worker?.tunnel?.allowedPorts || [];
618
+ if (tunnelPorts.length === 0) {
619
+ console.warn(
620
+ "⚠️ No allowedPorts configured. Add worker.tunnel.allowedPorts to knowhow.json"
621
+ );
622
+ } else {
623
+ console.log(`🌐 Tunnel mode for ports: ${tunnelPorts.join(", ")}`);
624
+ }
625
+
626
+ // Extract tunnel domain from API_URL
627
+ function extractTunnelDomain(apiUrl: string): {
628
+ domain: string;
629
+ useHttps: boolean;
630
+ } {
631
+ try {
632
+ const url = new URL(apiUrl);
633
+ const useHttps = url.protocol === "https:";
634
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
635
+ return {
636
+ domain: `worker.${url.hostname}:${url.port || "80"}`,
637
+ useHttps,
638
+ };
639
+ }
640
+ return { domain: `worker.${url.hostname}`, useHttps };
641
+ } catch (err) {
642
+ console.error("Failed to parse API_URL for tunnel domain:", err);
643
+ return { domain: "worker.localhost:4000", useHttps: false };
644
+ }
645
+ }
646
+
647
+ let connected = false;
648
+ let tunnelHandler: TunnelHandler | null = null;
649
+ let lastJwt: string | null = null;
650
+ let unauthorizedJwt: string | null = null;
651
+
652
+ async function connectTunnel() {
653
+ const jwt = await loadJwt();
654
+ lastJwt = jwt;
655
+ console.log(`Connecting tunnel to ${API_URL}`);
656
+
657
+ const dir = process.cwd();
658
+ const homedir = os.homedir();
659
+ const hostname = process.env.WORKER_HOSTNAME || os.hostname();
660
+ const root =
661
+ process.env.WORKER_ROOT ||
662
+ (dir === homedir ? "~" : dir.replace(homedir, "~"));
663
+
664
+ const headers: Record<string, string> = {
665
+ Authorization: `Bearer ${jwt}`,
666
+ "User-Agent": `knowhow-tunnel/1.0.0/${hostname}`,
667
+ Root: root,
668
+ };
669
+
670
+ if (options?.share) {
671
+ headers.Shared = "true";
672
+ console.log("🔓 Tunnel shared with organization");
673
+ } else if (options?.unshare) {
674
+ headers.Shared = "false";
675
+ console.log("🔒 Tunnel is now private (unshared)");
676
+ } else {
677
+ console.log("🔒 Tunnel is private (only you can use it)");
678
+ }
679
+
680
+ const { domain: tunnelDomain, useHttps: tunnelUseHttps } =
681
+ extractTunnelDomain(API_URL);
682
+
683
+ const tunnelConnection = new WebSocket(`${API_URL}/ws/tunnel`, { headers });
684
+
685
+ tunnelConnection.on("open", () => {
686
+ console.log("🌐 Tunnel WebSocket connected");
687
+ connected = true;
688
+
689
+ const allowedPorts = config.worker?.tunnel?.allowedPorts || [];
690
+ const urlRewriter = (port: number, metadata?: any) => {
691
+ const workerId = metadata?.workerId;
692
+ const secret = metadata?.secret;
693
+ const subdomain = secret ? `${secret}-p${port}` : `${workerId}-p${port}`;
694
+ return `${subdomain}.${tunnelDomain}`;
695
+ };
696
+
697
+ const tunnelConfig = {
698
+ allowedPorts,
699
+ maxConcurrentStreams: config.worker?.tunnel?.maxConcurrentStreams || 50,
700
+ tunnelUseHttps,
701
+ localHost: tunnelLocalHost,
702
+ urlRewriter,
703
+ enableUrlRewriting: config.worker?.tunnel?.enableUrlRewriting !== false,
704
+ portMapping,
705
+ logLevel: "debug" as const,
706
+ };
707
+
708
+ tunnelHandler = createTunnelHandler(tunnelConnection, tunnelConfig);
709
+ console.log("🌐 Tunnel handler initialized");
710
+ console.log(tunnelConfig);
711
+ });
712
+
713
+ tunnelConnection.on("close", (code, reason) => {
714
+ console.log(`Tunnel WebSocket closed. Code: ${code}, Reason: ${reason.toString()}`);
715
+ if (code === 1008) {
716
+ unauthorizedJwt = lastJwt;
717
+ console.error("❌ Tunnel received Unauthorized (1008). The JWT may be expired.");
718
+ console.error(" Run 'knowhow login' to refresh your token, then restart.");
719
+ console.error(" Pausing reconnection until JWT changes...");
720
+ } else {
721
+ console.log("Tunnel connection will reconnect on next cycle...");
722
+ }
723
+ if (tunnelHandler) {
724
+ tunnelHandler.cleanup();
725
+ tunnelHandler = null;
726
+ }
727
+ connected = false;
728
+ });
729
+
730
+ tunnelConnection.on("error", (error) => {
731
+ console.error("Tunnel WebSocket error:", error);
732
+ connected = false;
733
+ });
734
+
735
+ return tunnelConnection;
736
+ }
737
+
738
+ while (true) {
739
+ if (!connected) {
740
+ if (unauthorizedJwt !== null) {
741
+ const currentJwt = await loadJwt().catch(() => null);
742
+ if (currentJwt === unauthorizedJwt) {
743
+ await wait(5000);
744
+ continue;
745
+ }
746
+ console.log("🔄 JWT has changed, attempting to reconnect tunnel...");
747
+ unauthorizedJwt = null;
748
+ }
749
+ console.log("Attempting to connect tunnel...");
750
+ await connectTunnel();
751
+ }
752
+ await wait(5000);
753
+ }
754
+ }
@@ -5,6 +5,9 @@
5
5
  * Models that are image-only, video-only, TTS, transcription, realtime, or live streaming
6
6
  * are exempt from text pricing requirements — they should have their own pricing entries
7
7
  * in the appropriate pricing tables (image, video, audio, etc.).
8
+ *
9
+ * Also verifies that every model in Models.* and EmbeddingModels.* has a catalog entry,
10
+ * ensuring the catalog stays in sync with the model definitions.
8
11
  */
9
12
 
10
13
  import {
@@ -27,6 +30,7 @@ import {
27
30
  GeminiTextPricing,
28
31
  AnthropicTextPricing,
29
32
  XaiTextPricing,
33
+ ALL_MODEL_CATALOG,
30
34
  XaiImagePricing,
31
35
  XaiVideoPricing,
32
36
  } from "../../src/clients/pricing";
@@ -140,5 +144,38 @@ describe("Model Pricing Coverage", () => {
140
144
  expect(entry).toBeDefined();
141
145
  });
142
146
  }
147
+
148
+ describe("Model Catalog Coverage", () => {
149
+ /**
150
+ * Every model defined in Models.* and EmbeddingModels.* must have an entry
151
+ * in ALL_MODEL_CATALOG. This ensures the catalog stays in sync and is the
152
+ * single source of truth for model metadata and pricing.
153
+ */
154
+ const catalogIds = new Set(ALL_MODEL_CATALOG.map((m) => m.id));
155
+
156
+ describe("All Models.* entries are in the catalog", () => {
157
+ for (const [provider, providerModels] of Object.entries(Models)) {
158
+ for (const [modelKey, modelId] of Object.entries(
159
+ providerModels as Record<string, string>
160
+ )) {
161
+ it(`Models.${provider}.${modelKey} (${modelId}) is in ALL_MODEL_CATALOG`, () => {
162
+ expect(catalogIds.has(modelId)).toBe(true);
163
+ });
164
+ }
165
+ }
166
+ });
167
+
168
+ describe("All EmbeddingModels.* entries are in the catalog", () => {
169
+ for (const [provider, providerModels] of Object.entries(EmbeddingModels)) {
170
+ for (const [modelKey, modelId] of Object.entries(
171
+ providerModels as Record<string, string>
172
+ )) {
173
+ it(`EmbeddingModels.${provider}.${modelKey} (${modelId}) is in ALL_MODEL_CATALOG`, () => {
174
+ expect(catalogIds.has(modelId)).toBe(true);
175
+ });
176
+ }
177
+ }
178
+ });
179
+ });
143
180
  });
144
181
  });