@tyvm/knowhow 0.0.68 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/shell-commands.md +174 -0
- package/package.json +2 -2
- package/src/agents/base/base.ts +1 -3
- package/src/agents/developer/developer.ts +21 -16
- package/src/agents/tools/agentCall.ts +4 -2
- package/src/agents/tools/fileSearch.ts +5 -1
- package/src/agents/tools/list.ts +41 -37
- package/src/agents/tools/startAgentTask.ts +131 -22
- package/src/chat/CliChatService.ts +57 -11
- package/src/chat/modules/AgentModule.ts +72 -12
- package/src/chat/modules/CustomCommandsModule.ts +79 -0
- package/src/chat/modules/InternalChatModule.ts +11 -1
- package/src/chat/modules/ShellCommandModule.ts +96 -0
- package/src/chat/modules/index.ts +1 -0
- package/src/chat/types.ts +14 -2
- package/src/chat.ts +16 -13
- package/src/cli.ts +16 -6
- package/src/clients/anthropic.ts +88 -91
- package/src/clients/gemini.ts +495 -94
- package/src/clients/index.ts +125 -0
- package/src/clients/knowhow.ts +81 -0
- package/src/clients/openai.ts +256 -145
- package/src/clients/pricing/anthropic.ts +90 -0
- package/src/clients/pricing/google.ts +65 -0
- package/src/clients/pricing/index.ts +4 -0
- package/src/clients/pricing/openai.ts +134 -0
- package/src/clients/pricing/xai.ts +62 -0
- package/src/clients/types.ts +170 -1
- package/src/clients/xai.ts +275 -46
- package/src/config.ts +61 -15
- package/src/embeddings.ts +9 -1
- package/src/microphone.ts +15 -16
- package/src/migrations.ts +151 -0
- package/src/plugins/AgentsMdPlugin.ts +118 -0
- package/src/plugins/PluginBase.ts +8 -0
- package/src/plugins/downloader/downloader.ts +5 -6
- package/src/plugins/embedding.ts +10 -8
- package/src/plugins/exec.ts +70 -0
- package/src/plugins/github.ts +120 -74
- package/src/plugins/language.ts +11 -13
- package/src/plugins/plugins.ts +25 -4
- package/src/plugins/tmux.ts +132 -0
- package/src/plugins/types.ts +1 -0
- package/src/plugins/vim.ts +14 -1
- package/src/server/index.ts +2 -0
- package/src/services/AgentSyncFs.ts +417 -0
- package/src/services/{AgentSynchronization.ts → AgentSyncKnowhowWeb.ts} +2 -2
- package/src/services/EventService.ts +0 -1
- package/src/services/KnowhowClient.ts +106 -0
- package/src/services/index.ts +4 -2
- package/src/types.ts +57 -4
- package/src/worker.ts +25 -2
- package/tests/manual/modalities/README.md +157 -0
- package/tests/manual/modalities/google.modalities.test.ts +335 -0
- package/tests/manual/modalities/openai.modalities.test.ts +329 -0
- package/tests/manual/modalities/streaming.test.ts +260 -0
- package/tests/manual/modalities/xai.modalities.test.ts +307 -0
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +5 -5
- package/tests/plugins/language/languagePlugin-integration.test.ts +1 -1
- package/tests/plugins/language/languagePlugin.test.ts +17 -8
- package/ts_build/package.json +2 -2
- package/ts_build/src/agents/base/base.js +1 -1
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/developer/developer.js +21 -15
- package/ts_build/src/agents/developer/developer.js.map +1 -1
- package/ts_build/src/agents/tools/agentCall.js +4 -2
- package/ts_build/src/agents/tools/agentCall.js.map +1 -1
- package/ts_build/src/agents/tools/executeScript/index.d.ts +1 -1
- package/ts_build/src/agents/tools/fileSearch.js +2 -1
- package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
- package/ts_build/src/agents/tools/github/index.d.ts +1 -1
- package/ts_build/src/agents/tools/list.js +41 -37
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -1
- package/ts_build/src/agents/tools/startAgentTask.js +118 -17
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +4 -0
- package/ts_build/src/chat/CliChatService.js +39 -5
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +4 -1
- package/ts_build/src/chat/modules/AgentModule.js +49 -11
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +9 -0
- package/ts_build/src/chat/modules/CustomCommandsModule.js +58 -0
- package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -0
- package/ts_build/src/chat/modules/InternalChatModule.d.ts +2 -0
- package/ts_build/src/chat/modules/InternalChatModule.js +10 -0
- package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
- package/ts_build/src/chat/modules/ShellCommandModule.d.ts +8 -0
- package/ts_build/src/chat/modules/ShellCommandModule.js +83 -0
- package/ts_build/src/chat/modules/ShellCommandModule.js.map +1 -0
- package/ts_build/src/chat/modules/index.d.ts +1 -0
- package/ts_build/src/chat/modules/index.js +3 -1
- package/ts_build/src/chat/modules/index.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +11 -1
- package/ts_build/src/chat.js +16 -13
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.js +10 -3
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +6 -1
- package/ts_build/src/clients/anthropic.js +47 -92
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/gemini.d.ts +81 -2
- package/ts_build/src/clients/gemini.js +362 -79
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +9 -1
- package/ts_build/src/clients/index.js +65 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/knowhow.d.ts +9 -1
- package/ts_build/src/clients/knowhow.js +43 -0
- package/ts_build/src/clients/knowhow.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +9 -1
- package/ts_build/src/clients/openai.js +201 -133
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/anthropic.d.ts +17 -0
- package/ts_build/src/clients/pricing/anthropic.js +93 -0
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -0
- package/ts_build/src/clients/pricing/google.d.ts +73 -0
- package/ts_build/src/clients/pricing/google.js +68 -0
- package/ts_build/src/clients/pricing/google.js.map +1 -0
- package/ts_build/src/clients/pricing/index.d.ts +4 -0
- package/ts_build/src/clients/pricing/index.js +14 -0
- package/ts_build/src/clients/pricing/index.js.map +1 -0
- package/ts_build/src/clients/pricing/openai.d.ts +7 -0
- package/ts_build/src/clients/pricing/openai.js +137 -0
- package/ts_build/src/clients/pricing/openai.js.map +1 -0
- package/ts_build/src/clients/pricing/xai.d.ts +26 -0
- package/ts_build/src/clients/pricing/xai.js +59 -0
- package/ts_build/src/clients/pricing/xai.js.map +1 -0
- package/ts_build/src/clients/types.d.ts +135 -0
- package/ts_build/src/clients/xai.d.ts +9 -1
- package/ts_build/src/clients/xai.js +178 -46
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/config.d.ts +1 -0
- package/ts_build/src/config.js +45 -16
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/embeddings.js +8 -1
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/microphone.js +7 -9
- package/ts_build/src/microphone.js.map +1 -1
- package/ts_build/src/migrations.d.ts +17 -0
- package/ts_build/src/migrations.js +86 -0
- package/ts_build/src/migrations.js.map +1 -0
- package/ts_build/src/plugins/AgentsMdPlugin.d.ts +13 -0
- package/ts_build/src/plugins/AgentsMdPlugin.js +118 -0
- package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -0
- package/ts_build/src/plugins/PluginBase.d.ts +1 -0
- package/ts_build/src/plugins/PluginBase.js +3 -0
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/downloader/downloader.js +5 -5
- package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
- package/ts_build/src/plugins/embedding.js +9 -8
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/exec.d.ts +10 -0
- package/ts_build/src/plugins/exec.js +56 -0
- package/ts_build/src/plugins/exec.js.map +1 -0
- package/ts_build/src/plugins/github.js +93 -51
- package/ts_build/src/plugins/github.js.map +1 -1
- package/ts_build/src/plugins/language.js +14 -11
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +1 -0
- package/ts_build/src/plugins/plugins.js +19 -1
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/tmux.d.ts +14 -0
- package/ts_build/src/plugins/tmux.js +108 -0
- package/ts_build/src/plugins/tmux.js.map +1 -0
- package/ts_build/src/plugins/types.d.ts +1 -0
- package/ts_build/src/plugins/vim.js +11 -1
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/server/index.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.d.ts +34 -0
- package/ts_build/src/services/AgentSyncFs.js +325 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.d.ts +29 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.js +178 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.js.map +1 -0
- package/ts_build/src/services/AgentSynchronization.d.ts +1 -1
- package/ts_build/src/services/AgentSynchronization.js +3 -3
- package/ts_build/src/services/AgentSynchronization.js.map +1 -1
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +9 -1
- package/ts_build/src/services/KnowhowClient.js +58 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/index.d.ts +2 -1
- package/ts_build/src/services/index.js +2 -1
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/types.d.ts +26 -1
- package/ts_build/src/types.js +45 -4
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/utils/PersistentInputManager.d.ts +28 -0
- package/ts_build/src/utils/PersistentInputManager.js +293 -0
- package/ts_build/src/utils/PersistentInputManager.js.map +1 -0
- package/ts_build/src/worker.js +11 -2
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/manual/modalities/google.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/google.modalities.test.js +252 -0
- package/ts_build/tests/manual/modalities/google.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.js +252 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/streaming.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/streaming.test.js +206 -0
- package/ts_build/tests/manual/modalities/streaming.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.js +226 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/persistent-input-test.d.ts +1 -0
- package/ts_build/tests/manual/persistent-input-test.js +35 -0
- package/ts_build/tests/manual/persistent-input-test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +5 -5
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +1 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin.test.js +17 -7
- package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
package/src/clients/xai.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
+
import { XaiTextPricing, XaiImagePricing, XaiVideoPricing } from "./pricing";
|
|
2
3
|
import {
|
|
3
4
|
GenericClient,
|
|
4
5
|
CompletionOptions,
|
|
5
6
|
CompletionResponse,
|
|
6
7
|
EmbeddingOptions,
|
|
7
8
|
EmbeddingResponse,
|
|
9
|
+
AudioTranscriptionOptions,
|
|
10
|
+
AudioTranscriptionResponse,
|
|
11
|
+
AudioGenerationOptions,
|
|
12
|
+
AudioGenerationResponse,
|
|
13
|
+
ImageGenerationOptions,
|
|
14
|
+
ImageGenerationResponse,
|
|
15
|
+
VideoGenerationOptions,
|
|
16
|
+
VideoGenerationResponse,
|
|
17
|
+
VideoStatusOptions,
|
|
18
|
+
VideoStatusResponse,
|
|
19
|
+
FileUploadOptions,
|
|
20
|
+
FileUploadResponse,
|
|
21
|
+
FileDownloadOptions,
|
|
22
|
+
FileDownloadResponse,
|
|
8
23
|
} from "./types";
|
|
9
24
|
import {
|
|
10
25
|
ChatCompletionMessageParam,
|
|
@@ -78,52 +93,7 @@ export class GenericXAIClient implements GenericClient {
|
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
pricesPerMillion() {
|
|
81
|
-
return
|
|
82
|
-
[Models.xai.Grok4_1_Fast_NonReasoning]: {
|
|
83
|
-
input: 0.2,
|
|
84
|
-
cache_hit: 0.05,
|
|
85
|
-
output: 0.5,
|
|
86
|
-
},
|
|
87
|
-
[Models.xai.Grok4_1_Fast_Reasoning]: {
|
|
88
|
-
input: 0.2,
|
|
89
|
-
cache_hit: 0.05,
|
|
90
|
-
output: 0.5,
|
|
91
|
-
},
|
|
92
|
-
[Models.xai.GrokCodeFast]: {
|
|
93
|
-
input: 0.2,
|
|
94
|
-
cache_hit: 0.02,
|
|
95
|
-
output: 1.5,
|
|
96
|
-
},
|
|
97
|
-
[Models.xai.Grok4]: {
|
|
98
|
-
input: 3.0,
|
|
99
|
-
output: 15.0,
|
|
100
|
-
},
|
|
101
|
-
[Models.xai.Grok3Beta]: {
|
|
102
|
-
input: 3.0,
|
|
103
|
-
output: 15.0,
|
|
104
|
-
},
|
|
105
|
-
[Models.xai.Grok3MiniBeta]: {
|
|
106
|
-
input: 0.3,
|
|
107
|
-
output: 0.5,
|
|
108
|
-
},
|
|
109
|
-
[Models.xai.Grok3FastBeta]: {
|
|
110
|
-
input: 5.0,
|
|
111
|
-
output: 25.0,
|
|
112
|
-
},
|
|
113
|
-
[Models.xai.Grok3MiniFastBeta]: {
|
|
114
|
-
input: 0.6,
|
|
115
|
-
output: 4.0,
|
|
116
|
-
},
|
|
117
|
-
[Models.xai.Grok21212]: {
|
|
118
|
-
input: 2.0,
|
|
119
|
-
output: 10.0,
|
|
120
|
-
},
|
|
121
|
-
[Models.xai.Grok2Vision1212]: {
|
|
122
|
-
input: 2.0,
|
|
123
|
-
output: 10.0,
|
|
124
|
-
image_input: 2.0,
|
|
125
|
-
},
|
|
126
|
-
};
|
|
96
|
+
return XaiTextPricing;
|
|
127
97
|
}
|
|
128
98
|
|
|
129
99
|
calculateCost(
|
|
@@ -163,4 +133,263 @@ export class GenericXAIClient implements GenericClient {
|
|
|
163
133
|
async createEmbedding(options: EmbeddingOptions): Promise<EmbeddingResponse> {
|
|
164
134
|
throw new Error("XAI provider does not support embeddings");
|
|
165
135
|
}
|
|
136
|
+
|
|
137
|
+
async createAudioTranscription(
|
|
138
|
+
options: AudioTranscriptionOptions
|
|
139
|
+
): Promise<AudioTranscriptionResponse> {
|
|
140
|
+
throw new Error(
|
|
141
|
+
"Audio transcription is not supported by the XAI provider. Use OpenAI client with Whisper model instead."
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async createAudioGeneration(
|
|
146
|
+
options: AudioGenerationOptions
|
|
147
|
+
): Promise<AudioGenerationResponse> {
|
|
148
|
+
throw new Error(
|
|
149
|
+
"Audio generation is not supported by the XAI provider. Use OpenAI client with TTS model instead."
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async createImageGeneration(
|
|
154
|
+
options: ImageGenerationOptions
|
|
155
|
+
): Promise<ImageGenerationResponse> {
|
|
156
|
+
const response = await this.client.images.generate({
|
|
157
|
+
model: options.model || "grok-imagine-image",
|
|
158
|
+
prompt: options.prompt,
|
|
159
|
+
n: options.n,
|
|
160
|
+
size: options.size,
|
|
161
|
+
quality: options.quality,
|
|
162
|
+
style: options.style,
|
|
163
|
+
response_format: options.response_format,
|
|
164
|
+
user: options.user,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Calculate cost based on model name
|
|
168
|
+
const imageModel = options.model || "grok-imagine-image";
|
|
169
|
+
const costPerImage =
|
|
170
|
+
XaiImagePricing[imageModel as keyof typeof XaiImagePricing] || 0.02;
|
|
171
|
+
const usdCost = (options.n || 1) * costPerImage;
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
...response,
|
|
175
|
+
created: response.created || Math.floor(Date.now() / 1000),
|
|
176
|
+
usd_cost: usdCost,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async createVideoGeneration(
|
|
181
|
+
options: VideoGenerationOptions
|
|
182
|
+
): Promise<VideoGenerationResponse> {
|
|
183
|
+
const model = options.model || "grok-imagine-video";
|
|
184
|
+
|
|
185
|
+
// Step 1: Start the video generation request
|
|
186
|
+
const startPayload = {
|
|
187
|
+
model,
|
|
188
|
+
prompt: options.prompt,
|
|
189
|
+
} as {
|
|
190
|
+
model: string;
|
|
191
|
+
prompt: string;
|
|
192
|
+
duration?: number;
|
|
193
|
+
aspect_ratio?: string;
|
|
194
|
+
resolution?: string;
|
|
195
|
+
image_url?: string;
|
|
196
|
+
video_url?: string;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Add optional parameters if provided
|
|
200
|
+
if (options.duration !== undefined) {
|
|
201
|
+
startPayload.duration = options.duration;
|
|
202
|
+
}
|
|
203
|
+
if (options.aspect_ratio) {
|
|
204
|
+
startPayload.aspect_ratio = options.aspect_ratio;
|
|
205
|
+
}
|
|
206
|
+
if (options.resolution) {
|
|
207
|
+
startPayload.resolution = options.resolution;
|
|
208
|
+
}
|
|
209
|
+
if (options.image_url) {
|
|
210
|
+
startPayload.image_url = options.image_url;
|
|
211
|
+
}
|
|
212
|
+
if (options.video_url) {
|
|
213
|
+
startPayload.video_url = options.video_url;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const startResponse = await fetch(
|
|
217
|
+
"https://api.x.ai/v1/videos/generations",
|
|
218
|
+
{
|
|
219
|
+
method: "POST",
|
|
220
|
+
headers: {
|
|
221
|
+
"Content-Type": "application/json",
|
|
222
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
223
|
+
},
|
|
224
|
+
body: JSON.stringify(startPayload),
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (!startResponse.ok) {
|
|
229
|
+
const errorText = await startResponse.text();
|
|
230
|
+
throw new Error(
|
|
231
|
+
`XAI video generation start failed: ${startResponse.status} ${errorText}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const startData = await startResponse.json();
|
|
236
|
+
const requestId = startData.request_id;
|
|
237
|
+
|
|
238
|
+
if (!requestId) {
|
|
239
|
+
throw new Error("No request_id returned from XAI video generation start");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Return immediately with the jobId – do NOT poll here.
|
|
243
|
+
// Use getVideoStatus() to poll and downloadVideo() to fetch the result.
|
|
244
|
+
const duration = options.duration || 5;
|
|
245
|
+
const pricePerSecond = XaiVideoPricing[model] || 0.07;
|
|
246
|
+
const usdCost = duration * pricePerSecond;
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
created: Math.floor(Date.now() / 1000),
|
|
250
|
+
data: [],
|
|
251
|
+
jobId: requestId,
|
|
252
|
+
usd_cost: usdCost,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async getVideoStatus(
|
|
257
|
+
options: VideoStatusOptions
|
|
258
|
+
): Promise<VideoStatusResponse> {
|
|
259
|
+
const statusResponse = await fetch(
|
|
260
|
+
`https://api.x.ai/v1/videos/${options.jobId}`,
|
|
261
|
+
{
|
|
262
|
+
method: "GET",
|
|
263
|
+
headers: {
|
|
264
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
if (!statusResponse.ok) {
|
|
270
|
+
const errorText = await statusResponse.text();
|
|
271
|
+
throw new Error(
|
|
272
|
+
`XAI video status check failed: ${statusResponse.status} ${errorText}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const statusData = await statusResponse.json();
|
|
277
|
+
|
|
278
|
+
// Map XAI status to standard status
|
|
279
|
+
let mappedStatus: "queued" | "in_progress" | "completed" | "failed" | "expired";
|
|
280
|
+
switch (statusData.status) {
|
|
281
|
+
case "pending":
|
|
282
|
+
mappedStatus = "queued";
|
|
283
|
+
break;
|
|
284
|
+
case "processing":
|
|
285
|
+
mappedStatus = "in_progress";
|
|
286
|
+
break;
|
|
287
|
+
case "succeeded":
|
|
288
|
+
mappedStatus = "completed";
|
|
289
|
+
break;
|
|
290
|
+
case "failed":
|
|
291
|
+
mappedStatus = "failed";
|
|
292
|
+
break;
|
|
293
|
+
case "expired":
|
|
294
|
+
mappedStatus = "expired";
|
|
295
|
+
break;
|
|
296
|
+
default:
|
|
297
|
+
mappedStatus = "queued";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const response: VideoStatusResponse = {
|
|
301
|
+
jobId: options.jobId,
|
|
302
|
+
status: mappedStatus,
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// If completed, include the video URL
|
|
306
|
+
if (mappedStatus === "completed" && statusData.video?.url) {
|
|
307
|
+
response.data = [
|
|
308
|
+
{
|
|
309
|
+
url: statusData.video.url,
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return response;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async downloadVideo(
|
|
318
|
+
options: FileDownloadOptions
|
|
319
|
+
): Promise<FileDownloadResponse> {
|
|
320
|
+
// XAI returns a URL for the video, not raw bytes from their API
|
|
321
|
+
const url = options.uri || options.fileId;
|
|
322
|
+
|
|
323
|
+
const response = await fetch(url);
|
|
324
|
+
if (!response.ok) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`Failed to download video from XAI URL: ${response.status} ${response.statusText}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
331
|
+
return {
|
|
332
|
+
data: Buffer.from(arrayBuffer),
|
|
333
|
+
mimeType: "video/mp4",
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async uploadFile(
|
|
338
|
+
options: FileUploadOptions
|
|
339
|
+
): Promise<FileUploadResponse> {
|
|
340
|
+
const apiKey = this.apiKey || process.env.XAI_API_KEY;
|
|
341
|
+
if (!apiKey) {
|
|
342
|
+
throw new Error("XAI API key not set");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const formData = new FormData();
|
|
346
|
+
formData.append("purpose", "assistants");
|
|
347
|
+
const blob = new Blob([options.data], { type: options.mimeType || "application/octet-stream" });
|
|
348
|
+
formData.append("file", blob, options.fileName || "upload");
|
|
349
|
+
|
|
350
|
+
const response = await fetch("https://api.x.ai/v1/files", {
|
|
351
|
+
method: "POST",
|
|
352
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
353
|
+
body: formData,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (!response.ok) {
|
|
357
|
+
const errorText = await response.text();
|
|
358
|
+
throw new Error(`XAI uploadFile failed: ${response.status} ${errorText}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const data = await response.json();
|
|
362
|
+
return { fileId: data.id, uri: data.uri };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async downloadFile(
|
|
366
|
+
options: FileDownloadOptions
|
|
367
|
+
): Promise<FileDownloadResponse> {
|
|
368
|
+
const apiKey = this.apiKey || process.env.XAI_API_KEY;
|
|
369
|
+
if (!apiKey) {
|
|
370
|
+
throw new Error("XAI API key not set");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const fileId = options.fileId;
|
|
374
|
+
if (!fileId) {
|
|
375
|
+
throw new Error("downloadFile requires fileId");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const response = await fetch(`https://api.x.ai/v1/files/${fileId}/content`, {
|
|
379
|
+
method: "GET",
|
|
380
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
if (!response.ok) {
|
|
384
|
+
const errorText = await response.text();
|
|
385
|
+
throw new Error(`XAI downloadFile failed: ${response.status} ${errorText}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const mimeType = response.headers.get("content-type") || undefined;
|
|
389
|
+
const data = Buffer.from(await response.arrayBuffer());
|
|
390
|
+
return {
|
|
391
|
+
data,
|
|
392
|
+
mimeType,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
166
395
|
}
|
package/src/config.ts
CHANGED
|
@@ -12,23 +12,30 @@ import {
|
|
|
12
12
|
EmbeddingModels,
|
|
13
13
|
} from "./types";
|
|
14
14
|
import { mkdir, writeFile, readFile, fileExists } from "./utils";
|
|
15
|
+
import { applyMigrations } from "./migrations";
|
|
15
16
|
|
|
16
17
|
const defaultConfig = {
|
|
17
18
|
promptsDir: ".knowhow/prompts",
|
|
18
19
|
modules: [],
|
|
19
|
-
plugins:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
plugins: {
|
|
21
|
+
enabled: [
|
|
22
|
+
"embeddings",
|
|
23
|
+
"language",
|
|
24
|
+
"git",
|
|
25
|
+
"vim",
|
|
26
|
+
"github",
|
|
27
|
+
"asana",
|
|
28
|
+
"jira",
|
|
29
|
+
"linear",
|
|
30
|
+
"download",
|
|
31
|
+
"figma",
|
|
32
|
+
"url",
|
|
33
|
+
"tmux",
|
|
34
|
+
"agents-md",
|
|
35
|
+
"exec",
|
|
36
|
+
],
|
|
37
|
+
disabled: [],
|
|
38
|
+
},
|
|
32
39
|
lintCommands: {
|
|
33
40
|
js: "eslint",
|
|
34
41
|
ts: "tslint",
|
|
@@ -169,6 +176,7 @@ export async function getLanguageConfig() {
|
|
|
169
176
|
);
|
|
170
177
|
return language as Language;
|
|
171
178
|
} catch (e) {
|
|
179
|
+
console.error("Error reading .knowhow/language.json:", e);
|
|
172
180
|
return {} as Language;
|
|
173
181
|
}
|
|
174
182
|
}
|
|
@@ -217,7 +225,14 @@ export function getConfigSync() {
|
|
|
217
225
|
const config = JSON.parse(
|
|
218
226
|
fs.readFileSync(".knowhow/knowhow.json", "utf8").toString()
|
|
219
227
|
);
|
|
220
|
-
|
|
228
|
+
|
|
229
|
+
// Apply migrations synchronously
|
|
230
|
+
const { config: migratedConfig } = applyMigrations(config);
|
|
231
|
+
|
|
232
|
+
// Note: We don't save here in sync mode to avoid blocking operations
|
|
233
|
+
// The async getConfig() will handle saving on next call
|
|
234
|
+
|
|
235
|
+
return migratedConfig as Config;
|
|
221
236
|
} catch (e) {
|
|
222
237
|
return {} as Config;
|
|
223
238
|
}
|
|
@@ -238,13 +253,44 @@ export async function getConfig() {
|
|
|
238
253
|
}
|
|
239
254
|
try {
|
|
240
255
|
const config = await readFile(".knowhow/knowhow.json", "utf8");
|
|
241
|
-
|
|
256
|
+
const parsedConfig = JSON.parse(config);
|
|
257
|
+
|
|
258
|
+
return parsedConfig as Config;
|
|
242
259
|
} catch (error) {
|
|
243
260
|
console.error("Error reading .knowhow/knowhow.json:", error);
|
|
244
261
|
throw new Error("Failed to load KnowHow configuration.");
|
|
245
262
|
}
|
|
246
263
|
}
|
|
247
264
|
|
|
265
|
+
export async function migrateConfig() {
|
|
266
|
+
// Apply migrations, used to keep config structure up to date.
|
|
267
|
+
const parsedConfig = await getConfig();
|
|
268
|
+
const { modified, config: migratedConfig } = applyMigrations(parsedConfig);
|
|
269
|
+
|
|
270
|
+
// After migrations, check if any plugins from defaultConfig are missing
|
|
271
|
+
let configModified = modified;
|
|
272
|
+
if (migratedConfig.plugins) {
|
|
273
|
+
const enabled = migratedConfig.plugins.enabled || [];
|
|
274
|
+
const disabled = migratedConfig.plugins.disabled || [];
|
|
275
|
+
const missingPlugins = defaultConfig.plugins.enabled.filter(
|
|
276
|
+
(plugin) => !enabled.includes(plugin) && !disabled.includes(plugin)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
if (missingPlugins.length > 0) {
|
|
280
|
+
console.log(
|
|
281
|
+
`Adding missing plugins to enabled list: ${missingPlugins.join(", ")}`
|
|
282
|
+
);
|
|
283
|
+
migratedConfig.plugins.enabled = [...enabled, ...missingPlugins];
|
|
284
|
+
configModified = true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// If migrations were applied, save the updated config
|
|
289
|
+
if (configModified) {
|
|
290
|
+
await updateConfig(migratedConfig);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
248
294
|
export async function loadPrompt(promptName: string) {
|
|
249
295
|
const config = await getConfig();
|
|
250
296
|
if (!promptName) {
|
package/src/embeddings.ts
CHANGED
|
@@ -27,7 +27,15 @@ export { cosineSimilarity };
|
|
|
27
27
|
|
|
28
28
|
export async function loadEmbedding(filePath: string) {
|
|
29
29
|
if (await fileExists(filePath)) {
|
|
30
|
-
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(
|
|
32
|
+
await readFile(filePath, "utf8")
|
|
33
|
+
) as Embeddable[];
|
|
34
|
+
return parsed;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error("Error loading embedding file", filePath, e);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
31
39
|
}
|
|
32
40
|
return [];
|
|
33
41
|
}
|
package/src/microphone.ts
CHANGED
|
@@ -77,7 +77,7 @@ export async function recordAudio() {
|
|
|
77
77
|
const filePath = "/tmp/knowhow.wav";
|
|
78
78
|
const audioFile = fs.createWriteStream(filePath, { encoding: "binary" });
|
|
79
79
|
const defaultMic = getDefaultMic();
|
|
80
|
-
const hasSox = await execAsync("which sox");
|
|
80
|
+
const hasSox = await execAsync("which sox").catch(() => false);
|
|
81
81
|
|
|
82
82
|
if (!hasSox && !defaultMic) {
|
|
83
83
|
console.error("Sox is required to record audio");
|
|
@@ -117,26 +117,25 @@ sudo apt-get install sox libsox-fmt-all
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
export async function voiceToText() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return "/voice";
|
|
120
|
+
// Allow user to type commands to exit voice mode or press Enter to record
|
|
121
|
+
const startInput = await ask("Press Enter to Start Recording (or type a command to exit): ", []);
|
|
122
|
+
|
|
123
|
+
// If user typed anything other than empty string, return it (e.g., /voice, /exit, etc.)
|
|
124
|
+
if (startInput.trim() !== "") {
|
|
125
|
+
return startInput;
|
|
127
126
|
}
|
|
128
|
-
|
|
127
|
+
|
|
129
128
|
const recording = await recordAudio();
|
|
130
129
|
console.log("Recording audio...");
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
]);
|
|
130
|
+
|
|
131
|
+
const stopInput = await ask("Press Enter to Stop Recording (or type a command to cancel): ", []);
|
|
134
132
|
recording.stop();
|
|
135
133
|
console.log("Stopped recording");
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
|
|
135
|
+
// If user typed anything during recording, return it instead of transcribing
|
|
136
|
+
if (stopInput.trim() !== "") {
|
|
137
|
+
return stopInput;
|
|
139
138
|
}
|
|
140
|
-
|
|
139
|
+
|
|
141
140
|
return convertAudioToText("/tmp/knowhow.wav", false);
|
|
142
141
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Migration System
|
|
3
|
+
*
|
|
4
|
+
* This module provides a framework for migrating configuration files
|
|
5
|
+
* as the schema evolves over time. Each migration is a function that
|
|
6
|
+
* transforms the config and returns whether it made changes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Config } from "./types";
|
|
10
|
+
|
|
11
|
+
export type Migration = {
|
|
12
|
+
version: number;
|
|
13
|
+
description: string;
|
|
14
|
+
migrate: (config: any, context?: MigrationContext) => { modified: boolean; config: any };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type MigrationContext = {
|
|
18
|
+
allPluginKeys?: string[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Default list of all known plugins at time of migration
|
|
23
|
+
* This list is used if the migration context doesn't provide plugin keys
|
|
24
|
+
*/
|
|
25
|
+
const DEFAULT_PLUGINS = [
|
|
26
|
+
"embeddings",
|
|
27
|
+
"language",
|
|
28
|
+
"git",
|
|
29
|
+
"vim",
|
|
30
|
+
"linter",
|
|
31
|
+
"github",
|
|
32
|
+
"asana",
|
|
33
|
+
"jira",
|
|
34
|
+
"linear",
|
|
35
|
+
"notion",
|
|
36
|
+
"download",
|
|
37
|
+
"figma",
|
|
38
|
+
"url",
|
|
39
|
+
"tmux",
|
|
40
|
+
"agents-md",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Migration 1: Convert plugins from string[] to { enabled, disabled } format
|
|
45
|
+
* Also ensures all registered plugins are in the enabled list
|
|
46
|
+
*/
|
|
47
|
+
const migration1: Migration = {
|
|
48
|
+
version: 1,
|
|
49
|
+
description: "Convert plugins from string[] to { enabled, disabled } format and add missing plugins",
|
|
50
|
+
migrate: (config: any, context?: MigrationContext) => {
|
|
51
|
+
const allPluginKeys = context?.allPluginKeys || DEFAULT_PLUGINS;
|
|
52
|
+
let modified = false;
|
|
53
|
+
|
|
54
|
+
// If plugins doesn't exist, no migration needed
|
|
55
|
+
if (!config.plugins) {
|
|
56
|
+
// Create default plugins config with all plugins enabled
|
|
57
|
+
config.plugins = {
|
|
58
|
+
enabled: [...allPluginKeys],
|
|
59
|
+
disabled: [],
|
|
60
|
+
};
|
|
61
|
+
return { modified: true, config };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// If plugins is a string array, convert it
|
|
65
|
+
if (Array.isArray(config.plugins)) {
|
|
66
|
+
config.plugins = {
|
|
67
|
+
enabled: config.plugins,
|
|
68
|
+
disabled: [],
|
|
69
|
+
};
|
|
70
|
+
modified = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Ensure config.plugins is an object with enabled/disabled arrays
|
|
74
|
+
if (typeof config.plugins === "object" && !Array.isArray(config.plugins)) {
|
|
75
|
+
// Ensure enabled array exists
|
|
76
|
+
if (!config.plugins.enabled) {
|
|
77
|
+
config.plugins.enabled = [...allPluginKeys];
|
|
78
|
+
modified = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Ensure disabled array exists
|
|
82
|
+
if (!config.plugins.disabled) {
|
|
83
|
+
config.plugins.disabled = [];
|
|
84
|
+
modified = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Add any missing plugins to enabled list
|
|
88
|
+
const missingPlugins = allPluginKeys.filter(
|
|
89
|
+
(key) =>
|
|
90
|
+
!config.plugins.enabled.includes(key) &&
|
|
91
|
+
!config.plugins.disabled.includes(key)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (missingPlugins.length > 0) {
|
|
95
|
+
config.plugins.enabled.push(...missingPlugins);
|
|
96
|
+
modified = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { modified, config };
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* All migrations in order
|
|
106
|
+
*/
|
|
107
|
+
export const migrations: Migration[] = [migration1];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Apply all migrations to a config object
|
|
111
|
+
* @param config The config object to migrate
|
|
112
|
+
* @param context Optional context for migrations (e.g., list of all plugin keys)
|
|
113
|
+
* @returns Object containing the migrated config and whether any changes were made
|
|
114
|
+
*/
|
|
115
|
+
export function applyMigrations(config: any, context?: MigrationContext): {
|
|
116
|
+
modified: boolean;
|
|
117
|
+
config: any;
|
|
118
|
+
} {
|
|
119
|
+
let modified = false;
|
|
120
|
+
let currentConfig = { ...config };
|
|
121
|
+
|
|
122
|
+
for (const migration of migrations) {
|
|
123
|
+
try {
|
|
124
|
+
const result = migration.migrate(currentConfig, context);
|
|
125
|
+
if (result.modified) {
|
|
126
|
+
console.log(
|
|
127
|
+
`Applied migration ${migration.version}: ${migration.description}`
|
|
128
|
+
);
|
|
129
|
+
modified = true;
|
|
130
|
+
currentConfig = result.config;
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(
|
|
134
|
+
`Failed to apply migration ${migration.version}: ${migration.description}`,
|
|
135
|
+
error
|
|
136
|
+
);
|
|
137
|
+
// Continue with other migrations even if one fails
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { modified, config: currentConfig };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get the current migration version (highest migration number)
|
|
146
|
+
*/
|
|
147
|
+
export function getCurrentMigrationVersion(): number {
|
|
148
|
+
return migrations.length > 0
|
|
149
|
+
? Math.max(...migrations.map((m) => m.version))
|
|
150
|
+
: 0;
|
|
151
|
+
}
|