@tyvm/knowhow 0.0.108 ā 0.0.109-dev.05fe5a0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/package.json +9 -4
- package/scripts/build-for-node.sh +10 -24
- package/scripts/publish.sh +86 -0
- package/src/agents/base/base.ts +10 -0
- package/src/agents/tools/execCommand.ts +49 -6
- package/src/agents/tools/index.ts +0 -1
- package/src/agents/tools/list.ts +0 -2
- package/src/chat/CliChatService.ts +10 -1
- package/src/chat/modules/AgentModule.ts +61 -31
- package/src/chat/modules/SessionsModule.ts +47 -3
- package/src/chat/renderer/CompactRenderer.ts +20 -0
- package/src/chat/renderer/ConsoleRenderer.ts +19 -0
- package/src/chat/renderer/FancyRenderer.ts +19 -0
- package/src/chat/renderer/types.ts +11 -0
- package/src/cli.ts +91 -659
- package/src/clients/anthropic.ts +17 -16
- package/src/clients/index.ts +6 -5
- package/src/clients/types.ts +19 -4
- package/src/cloudWorker.ts +175 -113
- package/src/commands/agent.ts +246 -0
- package/src/commands/misc.ts +174 -0
- package/src/commands/modules.ts +217 -0
- package/src/commands/services.ts +77 -0
- package/src/commands/workers.ts +168 -0
- package/src/config.ts +37 -0
- package/src/fileSync.ts +50 -17
- package/src/index.ts +18 -0
- package/src/logger.ts +197 -0
- package/src/plugins/embedding.ts +11 -6
- package/src/plugins/plugins.ts +0 -21
- package/src/plugins/vim.ts +5 -16
- package/src/processors/JsonCompressor.ts +6 -6
- package/src/services/EventService.ts +61 -1
- package/src/services/KnowhowClient.ts +34 -4
- package/src/services/modules/index.ts +95 -51
- package/src/services/modules/types.ts +6 -0
- package/src/tunnel.ts +216 -0
- package/src/types.ts +0 -1
- package/src/worker.ts +105 -312
- package/src/workers/auth/WsMiddleware.ts +99 -0
- package/src/workers/auth/authMiddleware.ts +104 -0
- package/src/workers/auth/types.ts +14 -2
- package/src/workers/tools/index.ts +2 -0
- package/src/workers/tools/reloadConfig.ts +84 -0
- package/tests/services/WorkerReloadConfig.test.ts +141 -0
- package/tests/unit/commands/github-credentials.test.ts +211 -0
- package/tests/unit/modules/moduleLoading.test.ts +39 -37
- package/tests/unit/plugins/pluginLoading.test.ts +0 -85
- package/ts_build/package.json +9 -4
- package/ts_build/src/agents/base/base.js +11 -0
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.d.ts +1 -1
- package/ts_build/src/agents/tools/execCommand.js +39 -5
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/index.d.ts +0 -1
- package/ts_build/src/agents/tools/index.js +0 -1
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +0 -2
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/chat/CliChatService.js +13 -1
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +43 -20
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +37 -3
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/renderer/CompactRenderer.d.ts +4 -0
- package/ts_build/src/chat/renderer/CompactRenderer.js +16 -0
- package/ts_build/src/chat/renderer/CompactRenderer.js.map +1 -1
- package/ts_build/src/chat/renderer/ConsoleRenderer.d.ts +4 -0
- package/ts_build/src/chat/renderer/ConsoleRenderer.js +16 -0
- package/ts_build/src/chat/renderer/ConsoleRenderer.js.map +1 -1
- package/ts_build/src/chat/renderer/FancyRenderer.d.ts +4 -0
- package/ts_build/src/chat/renderer/FancyRenderer.js +16 -0
- package/ts_build/src/chat/renderer/FancyRenderer.js.map +1 -1
- package/ts_build/src/chat/renderer/types.d.ts +2 -0
- package/ts_build/src/cli.js +47 -519
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +5 -5
- package/ts_build/src/clients/anthropic.js +17 -16
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/index.js +2 -4
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +3 -2
- package/ts_build/src/cloudWorker.d.ts +14 -0
- package/ts_build/src/cloudWorker.js +105 -66
- package/ts_build/src/cloudWorker.js.map +1 -1
- package/ts_build/src/commands/agent.d.ts +6 -0
- package/ts_build/src/commands/agent.js +229 -0
- package/ts_build/src/commands/agent.js.map +1 -0
- package/ts_build/src/commands/misc.d.ts +10 -0
- package/ts_build/src/commands/misc.js +197 -0
- package/ts_build/src/commands/misc.js.map +1 -0
- package/ts_build/src/commands/modules.d.ts +3 -0
- package/ts_build/src/commands/modules.js +207 -0
- package/ts_build/src/commands/modules.js.map +1 -0
- package/ts_build/src/commands/services.d.ts +5 -0
- package/ts_build/src/commands/services.js +87 -0
- package/ts_build/src/commands/services.js.map +1 -0
- package/ts_build/src/commands/workers.d.ts +6 -0
- package/ts_build/src/commands/workers.js +168 -0
- package/ts_build/src/commands/workers.js.map +1 -0
- package/ts_build/src/config.d.ts +1 -0
- package/ts_build/src/config.js +32 -0
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/fileSync.d.ts +6 -0
- package/ts_build/src/fileSync.js +37 -12
- package/ts_build/src/fileSync.js.map +1 -1
- package/ts_build/src/index.d.ts +1 -0
- package/ts_build/src/index.js +17 -1
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/logger.d.ts +21 -0
- package/ts_build/src/logger.js +106 -0
- package/ts_build/src/logger.js.map +1 -0
- package/ts_build/src/plugins/embedding.js +4 -3
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +0 -2
- package/ts_build/src/plugins/plugins.js +0 -11
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/vim.js +3 -9
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/processors/JsonCompressor.js +4 -4
- package/ts_build/src/processors/JsonCompressor.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +6 -1
- package/ts_build/src/services/EventService.js +29 -0
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +13 -1
- package/ts_build/src/services/KnowhowClient.js +19 -2
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/modules/index.d.ts +33 -0
- package/ts_build/src/services/modules/index.js +67 -47
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +6 -0
- package/ts_build/src/tunnel.d.ts +27 -0
- package/ts_build/src/tunnel.js +112 -0
- package/ts_build/src/tunnel.js.map +1 -0
- package/ts_build/src/types.d.ts +0 -1
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/worker.d.ts +1 -4
- package/ts_build/src/worker.js +59 -227
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/src/workers/auth/WsMiddleware.d.ts +8 -0
- package/ts_build/src/workers/auth/WsMiddleware.js +65 -0
- package/ts_build/src/workers/auth/WsMiddleware.js.map +1 -0
- package/ts_build/src/workers/auth/authMiddleware.d.ts +3 -0
- package/ts_build/src/workers/auth/authMiddleware.js +60 -0
- package/ts_build/src/workers/auth/authMiddleware.js.map +1 -0
- package/ts_build/src/workers/auth/types.d.ts +8 -1
- package/ts_build/src/workers/tools/index.d.ts +2 -0
- package/ts_build/src/workers/tools/index.js +4 -1
- package/ts_build/src/workers/tools/index.js.map +1 -1
- package/ts_build/src/workers/tools/reloadConfig.d.ts +14 -0
- package/ts_build/src/workers/tools/reloadConfig.js +48 -0
- package/ts_build/src/workers/tools/reloadConfig.js.map +1 -0
- package/ts_build/tests/services/WorkerReloadConfig.test.d.ts +1 -0
- package/ts_build/tests/services/WorkerReloadConfig.test.js +86 -0
- package/ts_build/tests/services/WorkerReloadConfig.test.js.map +1 -0
- package/ts_build/tests/unit/commands/github-credentials.test.d.ts +1 -0
- package/ts_build/tests/unit/commands/github-credentials.test.js +146 -0
- package/ts_build/tests/unit/commands/github-credentials.test.js.map +1 -0
- package/ts_build/tests/unit/modules/moduleLoading.test.js +20 -26
- package/ts_build/tests/unit/modules/moduleLoading.test.js.map +1 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +0 -65
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
- package/src/agents/tools/executeScript/README.md +0 -94
- package/src/agents/tools/executeScript/definition.ts +0 -79
- package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +0 -272
- package/src/agents/tools/executeScript/examples/quick-test.ts +0 -74
- package/src/agents/tools/executeScript/examples/serialization-test.ts +0 -321
- package/src/agents/tools/executeScript/examples/test-runner.ts +0 -197
- package/src/agents/tools/executeScript/index.ts +0 -98
- package/src/services/script-execution/SandboxContext.ts +0 -282
- package/src/services/script-execution/ScriptExecutor.ts +0 -441
- package/src/services/script-execution/ScriptPolicy.ts +0 -194
- package/src/services/script-execution/ScriptTracer.ts +0 -249
- package/src/services/script-execution/types.ts +0 -134
- package/ts_build/src/agents/tools/executeScript/definition.d.ts +0 -2
- package/ts_build/src/agents/tools/executeScript/definition.js +0 -76
- package/ts_build/src/agents/tools/executeScript/definition.js.map +0 -1
- package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +0 -18
- package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +0 -192
- package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +0 -1
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +0 -3
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +0 -64
- package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +0 -1
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +0 -15
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +0 -266
- package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +0 -1
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +0 -4
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +0 -208
- package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +0 -1
- package/ts_build/src/agents/tools/executeScript/index.d.ts +0 -28
- package/ts_build/src/agents/tools/executeScript/index.js +0 -72
- package/ts_build/src/agents/tools/executeScript/index.js.map +0 -1
- package/ts_build/src/services/script-execution/SandboxContext.d.ts +0 -34
- package/ts_build/src/services/script-execution/SandboxContext.js +0 -189
- package/ts_build/src/services/script-execution/SandboxContext.js.map +0 -1
- package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +0 -19
- package/ts_build/src/services/script-execution/ScriptExecutor.js +0 -269
- package/ts_build/src/services/script-execution/ScriptExecutor.js.map +0 -1
- package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +0 -28
- package/ts_build/src/services/script-execution/ScriptPolicy.js +0 -115
- package/ts_build/src/services/script-execution/ScriptPolicy.js.map +0 -1
- package/ts_build/src/services/script-execution/ScriptTracer.d.ts +0 -19
- package/ts_build/src/services/script-execution/ScriptTracer.js +0 -186
- package/ts_build/src/services/script-execution/ScriptTracer.js.map +0 -1
- package/ts_build/src/services/script-execution/types.d.ts +0 -108
- package/ts_build/src/services/script-execution/types.js +0 -3
- package/ts_build/src/services/script-execution/types.js.map +0 -1
package/src/clients/anthropic.ts
CHANGED
|
@@ -40,11 +40,11 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
handleToolCaching(tools: Anthropic.Tool[]) {
|
|
43
|
+
handleToolCaching(tools: Anthropic.Tool[], longTtl = false) {
|
|
44
44
|
const lastTool = tools[tools.length - 1];
|
|
45
45
|
|
|
46
46
|
if (lastTool) {
|
|
47
|
-
lastTool.cache_control = { type: "ephemeral" };
|
|
47
|
+
lastTool.cache_control = longTtl ? { type: "ephemeral", ttl: "1h" } as any : { type: "ephemeral" };
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -94,7 +94,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
94
94
|
return cleaned;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
transformTools(tools?: Tool[]): Anthropic.Tool[] {
|
|
97
|
+
transformTools(tools?: Tool[], longTtl = false): Anthropic.Tool[] {
|
|
98
98
|
if (!tools) {
|
|
99
99
|
return [];
|
|
100
100
|
}
|
|
@@ -104,7 +104,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
104
104
|
input_schema: this.cleanSchemaForAnthropic(tool.function.parameters) as any,
|
|
105
105
|
}));
|
|
106
106
|
|
|
107
|
-
this.handleToolCaching(transformed);
|
|
107
|
+
this.handleToolCaching(transformed, longTtl);
|
|
108
108
|
|
|
109
109
|
return transformed;
|
|
110
110
|
}
|
|
@@ -153,16 +153,16 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
153
153
|
return messages;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
cacheLastContent(message: MessageParam) {
|
|
156
|
+
cacheLastContent(message: MessageParam, longTtl = false) {
|
|
157
157
|
if (Array.isArray(message.content)) {
|
|
158
158
|
const lastMessage = message.content[message.content.length - 1];
|
|
159
159
|
if (
|
|
160
160
|
lastMessage.type !== "thinking" &&
|
|
161
161
|
lastMessage.type !== "redacted_thinking"
|
|
162
162
|
) {
|
|
163
|
-
lastMessage.cache_control =
|
|
164
|
-
type: "ephemeral",
|
|
165
|
-
|
|
163
|
+
lastMessage.cache_control = longTtl
|
|
164
|
+
? ({ type: "ephemeral", ttl: "1h" } as any)
|
|
165
|
+
: { type: "ephemeral" };
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -179,7 +179,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
handleMessageCaching(groupedMessages: MessageParam[]) {
|
|
182
|
+
handleMessageCaching(groupedMessages: MessageParam[], longTtl = false) {
|
|
183
183
|
this.handleClearingCache(groupedMessages);
|
|
184
184
|
|
|
185
185
|
// find the last two messages and mark them as ephemeral
|
|
@@ -189,7 +189,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
189
189
|
|
|
190
190
|
for (const m of lastTwoUserMessages) {
|
|
191
191
|
if (Array.isArray(m.content)) {
|
|
192
|
-
this.cacheLastContent(m);
|
|
192
|
+
this.cacheLastContent(m, longTtl);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
}
|
|
@@ -203,7 +203,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
transformMessages(messages: Message[]): MessageParam[] {
|
|
206
|
+
transformMessages(messages: Message[], longTtl = false): MessageParam[] {
|
|
207
207
|
const toolCalls = messages.flatMap((msg) => msg.tool_calls || []);
|
|
208
208
|
const claudeMessages: MessageParam[] = messages
|
|
209
209
|
.filter((msg) => msg.role !== "system")
|
|
@@ -302,7 +302,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
302
302
|
|
|
303
303
|
const groupedMessages = this.combineMessages(claudeMessages);
|
|
304
304
|
|
|
305
|
-
this.handleMessageCaching(groupedMessages);
|
|
305
|
+
this.handleMessageCaching(groupedMessages, longTtl);
|
|
306
306
|
|
|
307
307
|
return groupedMessages;
|
|
308
308
|
}
|
|
@@ -349,14 +349,15 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
349
349
|
async createChatCompletion(
|
|
350
350
|
options: CompletionOptions
|
|
351
351
|
): Promise<CompletionResponse> {
|
|
352
|
+
const longTtl = !!options.long_ttl_cache;
|
|
352
353
|
const systemMessage = options.messages
|
|
353
354
|
.filter((msg) => msg.role === "system")
|
|
354
355
|
.map((msg) => msg.content || "")
|
|
355
356
|
.join("\n");
|
|
356
357
|
|
|
357
|
-
const claudeMessages = this.transformMessages(options.messages);
|
|
358
|
+
const claudeMessages = this.transformMessages(options.messages, longTtl);
|
|
358
359
|
|
|
359
|
-
const tools = this.transformTools(options.tools);
|
|
360
|
+
const tools = this.transformTools(options.tools, longTtl);
|
|
360
361
|
try {
|
|
361
362
|
const response = await this.client.messages.create({
|
|
362
363
|
model: options.model,
|
|
@@ -365,7 +366,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
365
366
|
? [
|
|
366
367
|
{
|
|
367
368
|
text: systemMessage,
|
|
368
|
-
cache_control: { type: "ephemeral" },
|
|
369
|
+
cache_control: longTtl ? ({ type: "ephemeral", ttl: "1h" } as any) : { type: "ephemeral" },
|
|
369
370
|
type: "text",
|
|
370
371
|
},
|
|
371
372
|
]
|
|
@@ -424,7 +425,7 @@ export class GenericAnthropicClient implements GenericClient {
|
|
|
424
425
|
usd_cost: this.calculateCost(options.model, response.usage),
|
|
425
426
|
};
|
|
426
427
|
} catch (err) {
|
|
427
|
-
if ("headers" in err && err.headers["x-should-retry"] === "true") {
|
|
428
|
+
if ("headers" in err && err.headers?.["x-should-retry"] === "true") {
|
|
428
429
|
console.warn("Retrying failed request", err);
|
|
429
430
|
await wait(2500);
|
|
430
431
|
return this.createChatCompletion(options);
|
package/src/clients/index.ts
CHANGED
|
@@ -577,11 +577,12 @@ export class AIClient {
|
|
|
577
577
|
* @param modelQuery - the model name to search for (can be partial/normalized)
|
|
578
578
|
* @param provider - optional provider to restrict search to
|
|
579
579
|
*/
|
|
580
|
-
findModelFuzzy(
|
|
580
|
+
findModelFuzzy(
|
|
581
|
+
modelQuery: string,
|
|
582
|
+
provider?: string
|
|
583
|
+
): { provider: string; model: string } | undefined {
|
|
581
584
|
const queryNorm = AIClient.normalizeModelId(modelQuery);
|
|
582
|
-
const providers = provider
|
|
583
|
-
? [provider]
|
|
584
|
-
: Object.keys(this.clientModels);
|
|
585
|
+
const providers = provider ? [provider] : Object.keys(this.clientModels);
|
|
585
586
|
|
|
586
587
|
for (const p of providers) {
|
|
587
588
|
const models = (this.clientModels[p] as string[]) ?? [];
|
|
@@ -835,7 +836,7 @@ export class AIClient {
|
|
|
835
836
|
const splitModel = m.id.split("/");
|
|
836
837
|
|
|
837
838
|
if (splitModel.length < 2) {
|
|
838
|
-
console.
|
|
839
|
+
console.warn(`Cannot parse model format: ${m.id}`);
|
|
839
840
|
}
|
|
840
841
|
|
|
841
842
|
const provider = splitModel.length > 1 ? splitModel[0] : "";
|
package/src/clients/types.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export type ModelModality =
|
|
1
|
+
export type ModelModality =
|
|
2
|
+
| "completion"
|
|
3
|
+
| "embedding"
|
|
4
|
+
| "image"
|
|
5
|
+
| "audio"
|
|
6
|
+
| "video"
|
|
7
|
+
| "transcription";
|
|
2
8
|
|
|
3
9
|
export type MessageContent =
|
|
4
10
|
| { type: "text"; text: string }
|
|
@@ -8,7 +14,7 @@ export type MessageContent =
|
|
|
8
14
|
|
|
9
15
|
export interface Message {
|
|
10
16
|
role: "system" | "user" | "assistant" | "tool";
|
|
11
|
-
content?: string | MessageContent[];
|
|
17
|
+
content?: string | MessageContent[] | null;
|
|
12
18
|
|
|
13
19
|
name?: string;
|
|
14
20
|
tool_call_id?: string;
|
|
@@ -16,7 +22,7 @@ export interface Message {
|
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
export interface OutputMessage extends Message {
|
|
19
|
-
content
|
|
25
|
+
content?: string | null;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
export interface ToolProp {
|
|
@@ -61,6 +67,13 @@ export interface CompletionOptions {
|
|
|
61
67
|
* Maps to: OpenAI reasoning_effort, xAI reasoning.effort, Gemini thinkingLevel/thinkingBudget, Anthropic thinking budget.
|
|
62
68
|
* "low" = minimal thinking, "medium" = balanced, "high" = maximum reasoning */
|
|
63
69
|
reasoning_effort?: "low" | "medium" | "high";
|
|
70
|
+
/**
|
|
71
|
+
* When true, hints to the client that this task is long-running and it should
|
|
72
|
+
* use a long-TTL cache where available.
|
|
73
|
+
* - Anthropic: enables the `extended-cache-ttl-2025-02-19` beta and sets
|
|
74
|
+
* `cache_control.ttl` to 3600 (1 hour) instead of the default 5-minute ephemeral cache.
|
|
75
|
+
*/
|
|
76
|
+
long_ttl_cache?: boolean;
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
/**
|
|
@@ -294,7 +307,9 @@ export interface GenericClient {
|
|
|
294
307
|
* When modality is provided, return only models for that modality (static list).
|
|
295
308
|
* When omitted, return ALL models (backward compat ā may do a live API call).
|
|
296
309
|
*/
|
|
297
|
-
getModels(
|
|
310
|
+
getModels(
|
|
311
|
+
modality?: ModelModality
|
|
312
|
+
): Promise<{ id: string; modality?: ModelModality[] }[]>;
|
|
298
313
|
/**
|
|
299
314
|
* Returns the context window limit and compression threshold for a given model,
|
|
300
315
|
* or undefined if the model is not known to this client.
|
package/src/cloudWorker.ts
CHANGED
|
@@ -4,12 +4,18 @@ import { KnowhowSimpleClient, KNOWHOW_API_URL } from "./services/KnowhowClient";
|
|
|
4
4
|
import { loadJwt } from "./login";
|
|
5
5
|
import { getConfig, updateConfig, getLanguageConfig } from "./config";
|
|
6
6
|
import { services } from "./services";
|
|
7
|
-
import { Language, Config } from "./types";
|
|
8
|
-
import {
|
|
7
|
+
import { Language, Config, McpConfig } from "./types";
|
|
8
|
+
import { uploadFile, uploadDirectory } from "./fileSync";
|
|
9
|
+
|
|
10
|
+
export interface CloudWorkerPullOptions {
|
|
11
|
+
id: string;
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
}
|
|
9
14
|
|
|
10
15
|
export interface CloudWorkerOptions {
|
|
11
16
|
create?: boolean;
|
|
12
17
|
push?: string; // uid of existing cloud worker
|
|
18
|
+
init?: boolean; // initialize config.files entries (mutates config)
|
|
13
19
|
name?: string; // optional name for create
|
|
14
20
|
apiUrl?: string;
|
|
15
21
|
dryRun?: boolean;
|
|
@@ -25,25 +31,6 @@ interface FileToSync {
|
|
|
25
31
|
isDirectory?: boolean; // true if this represents a whole directory
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
/**
|
|
29
|
-
* Recursively list all files in a local directory, returning relative paths
|
|
30
|
-
*/
|
|
31
|
-
function listFilesRecursively(dir: string): string[] {
|
|
32
|
-
const results: string[] = [];
|
|
33
|
-
if (!fs.existsSync(dir)) return results;
|
|
34
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
35
|
-
for (const entry of entries) {
|
|
36
|
-
if (entry.isDirectory()) {
|
|
37
|
-
listFilesRecursively(path.join(dir, entry.name)).forEach((f) =>
|
|
38
|
-
results.push(entry.name + "/" + f)
|
|
39
|
-
);
|
|
40
|
-
} else {
|
|
41
|
-
results.push(entry.name);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return results;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
34
|
/**
|
|
48
35
|
* Build the worker config JSON from the local knowhow config
|
|
49
36
|
*/
|
|
@@ -66,21 +53,19 @@ function buildWorkerConfigJson(config: Config, files: { remotePath: string; loca
|
|
|
66
53
|
}
|
|
67
54
|
|
|
68
55
|
/**
|
|
69
|
-
* Collect all files from the .knowhow directory that should be synced
|
|
70
|
-
*
|
|
71
|
-
*
|
|
56
|
+
* Collect all files from the .knowhow directory that should be synced.
|
|
57
|
+
* Only includes files/directories that currently exist locally.
|
|
58
|
+
* Used by --init to populate config.files.
|
|
72
59
|
*/
|
|
73
60
|
async function collectFilesToSync(projectName: string): Promise<FileToSync[]> {
|
|
74
61
|
const filesToSync: FileToSync[] = [];
|
|
75
62
|
|
|
76
|
-
// Helper to add file if it exists
|
|
77
63
|
const addIfExists = (localPath: string, remotePath: string) => {
|
|
78
64
|
if (fs.existsSync(localPath)) {
|
|
79
65
|
filesToSync.push({ localPath, remotePath });
|
|
80
66
|
}
|
|
81
67
|
};
|
|
82
68
|
|
|
83
|
-
// Helper to add a directory entry if it exists (trailing slash = directory mode)
|
|
84
69
|
const addDirIfExists = (localPath: string, remotePath: string) => {
|
|
85
70
|
if (fs.existsSync(localPath)) {
|
|
86
71
|
filesToSync.push({ localPath: localPath + "/", remotePath: remotePath + "/", isDirectory: true });
|
|
@@ -103,7 +88,9 @@ async function collectFilesToSync(projectName: string): Promise<FileToSync[]> {
|
|
|
103
88
|
}
|
|
104
89
|
|
|
105
90
|
/**
|
|
106
|
-
* Collect files referenced in language.json sources
|
|
91
|
+
* Collect files referenced in language.json sources.
|
|
92
|
+
* These are always re-collected on both --init and --push so that new
|
|
93
|
+
* language term sources are picked up automatically.
|
|
107
94
|
*/
|
|
108
95
|
async function collectLanguageReferencedFiles(
|
|
109
96
|
language: Language,
|
|
@@ -119,17 +106,14 @@ async function collectLanguageReferencedFiles(
|
|
|
119
106
|
if (source.kind !== "file" || !source.data) continue;
|
|
120
107
|
|
|
121
108
|
for (const filePath of source.data) {
|
|
122
|
-
// Normalize the path (strip leading ./)
|
|
123
109
|
const normalizedPath = filePath.replace(/^\.\//, "");
|
|
124
110
|
|
|
125
111
|
// Skip the main knowhow config ā it should not be synced to the language folder
|
|
126
|
-
// as it would overwrite the worker's own config
|
|
127
112
|
if (normalizedPath === ".knowhow/knowhow.json") continue;
|
|
128
113
|
|
|
129
114
|
if (fs.existsSync(normalizedPath)) {
|
|
130
115
|
const basename = path.basename(normalizedPath);
|
|
131
116
|
const remotePath = `${projectName}/.knowhow/language/${basename}`;
|
|
132
|
-
// localPath is the original path so the worker downloads it to the right place
|
|
133
117
|
filesToSync.push({ localPath: normalizedPath, remotePath, downloadLocalPath: normalizedPath });
|
|
134
118
|
}
|
|
135
119
|
}
|
|
@@ -140,37 +124,83 @@ async function collectLanguageReferencedFiles(
|
|
|
140
124
|
}
|
|
141
125
|
|
|
142
126
|
/**
|
|
143
|
-
*
|
|
127
|
+
* Collect language-referenced files if language.json is present in the
|
|
128
|
+
* given config.files entries. Returns empty array if language.json is not
|
|
129
|
+
* configured for sync.
|
|
130
|
+
*/
|
|
131
|
+
async function collectLanguageFilesIfConfigured(
|
|
132
|
+
configFiles: { remotePath: string; localPath: string }[],
|
|
133
|
+
projectName: string
|
|
134
|
+
): Promise<FileToSync[]> {
|
|
135
|
+
const syncingLanguage = configFiles.some(
|
|
136
|
+
(f) => !f.remotePath.endsWith("/") && f.remotePath.endsWith("language.json")
|
|
137
|
+
);
|
|
138
|
+
if (!syncingLanguage) return [];
|
|
139
|
+
|
|
140
|
+
const language = await getLanguageConfig();
|
|
141
|
+
return collectLanguageReferencedFiles(language, projectName);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Initialize the local config.files entries based on what exists in .knowhow/.
|
|
146
|
+
* This is the --init step ā mutates config. Run once to set up sync entries.
|
|
147
|
+
* language-referenced files are also collected if language.json is present.
|
|
144
148
|
*/
|
|
145
|
-
async function
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
)
|
|
152
|
-
console.log(` ā¬ļø Uploading ${localPath} ā ${remotePath}`);
|
|
153
|
-
|
|
154
|
-
if (dryRun) {
|
|
155
|
-
console.log(` [DRY RUN] Would upload from ${localPath}`);
|
|
156
|
-
return;
|
|
149
|
+
export async function initCloudWorker(options: { apiUrl?: string; dryRun?: boolean } = {}) {
|
|
150
|
+
const { dryRun = false } = options;
|
|
151
|
+
|
|
152
|
+
const config = await getConfig();
|
|
153
|
+
if (!config || Object.keys(config).length === 0) {
|
|
154
|
+
console.error("ā No knowhow config found. Please run 'knowhow init' first.");
|
|
155
|
+
process.exit(1);
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
const projectName = path.basename(process.cwd());
|
|
159
|
+
console.log(`š Project name: ${projectName}`);
|
|
160
|
+
|
|
161
|
+
console.log("\nš Collecting files to sync...");
|
|
162
|
+
const mainFiles = await collectFilesToSync(projectName);
|
|
163
|
+
const languageFiles = await collectLanguageFilesIfConfigured(mainFiles, projectName);
|
|
164
|
+
|
|
165
|
+
if (languageFiles.length === 0 && !mainFiles.some((f) => f.remotePath.endsWith("language.json"))) {
|
|
166
|
+
console.log(" ā¹ļø Skipping language-referenced files (language.json not found locally)");
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
// Deduplicate by remotePath
|
|
170
|
+
const allFilesMap = new Map<string, FileToSync>();
|
|
171
|
+
for (const f of [...mainFiles, ...languageFiles]) {
|
|
172
|
+
allFilesMap.set(f.remotePath, f);
|
|
173
|
+
}
|
|
174
|
+
const allFiles = Array.from(allFilesMap.values());
|
|
175
|
+
|
|
176
|
+
console.log(` Found ${allFiles.length} files to register`);
|
|
167
177
|
|
|
168
|
-
const
|
|
169
|
-
|
|
178
|
+
const configFilesEntries = allFiles.map((f) => ({
|
|
179
|
+
remotePath: f.remotePath,
|
|
180
|
+
localPath: f.downloadLocalPath ?? f.localPath,
|
|
181
|
+
direction: "download" as const,
|
|
182
|
+
}));
|
|
183
|
+
|
|
184
|
+
console.log("\nš¾ Updating config.files with sync entries...");
|
|
185
|
+
if (!dryRun) {
|
|
186
|
+
const existingFiles = config.files || [];
|
|
187
|
+
const newRemotePaths = new Set(configFilesEntries.map((e) => e.remotePath));
|
|
188
|
+
const preserved = existingFiles.filter((e) => !newRemotePaths.has(e.remotePath));
|
|
189
|
+
config.files = [...preserved, ...configFilesEntries];
|
|
190
|
+
await updateConfig(config);
|
|
191
|
+
console.log(` ā Updated config with ${config.files.length} file entries`);
|
|
192
|
+
} else {
|
|
193
|
+
console.log(` [DRY RUN] Would update config with ${configFilesEntries.length} file entries`);
|
|
194
|
+
for (const f of allFiles) {
|
|
195
|
+
console.log(` ${f.localPath} ā ${f.remotePath}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
170
198
|
}
|
|
171
199
|
|
|
172
200
|
/**
|
|
173
|
-
* Main cloudWorker command handler
|
|
201
|
+
* Main cloudWorker command handler ā push/create only.
|
|
202
|
+
* Reads config.files (set up by --init) and also re-collects any language-referenced
|
|
203
|
+
* files so new language term sources are always included without requiring --init again.
|
|
174
204
|
*/
|
|
175
205
|
export async function cloudWorker(options: CloudWorkerOptions) {
|
|
176
206
|
const {
|
|
@@ -200,10 +230,6 @@ export async function cloudWorker(options: CloudWorkerOptions) {
|
|
|
200
230
|
process.exit(1);
|
|
201
231
|
}
|
|
202
232
|
|
|
203
|
-
// Load language config
|
|
204
|
-
const language = await getLanguageConfig();
|
|
205
|
-
|
|
206
|
-
// Get project name from current directory
|
|
207
233
|
const projectName = path.basename(process.cwd());
|
|
208
234
|
console.log(`š Project name: ${projectName}`);
|
|
209
235
|
|
|
@@ -213,86 +239,63 @@ export async function cloudWorker(options: CloudWorkerOptions) {
|
|
|
213
239
|
// Get S3 service
|
|
214
240
|
const { AwsS3 } = services();
|
|
215
241
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// Deduplicate by remotePath
|
|
222
|
-
const allFilesMap = new Map<string, FileToSync>();
|
|
223
|
-
for (const f of [...mainFiles, ...languageFiles]) {
|
|
224
|
-
allFilesMap.set(f.remotePath, f);
|
|
242
|
+
// Start with config.files (set up via --init)
|
|
243
|
+
const configFiles = config.files || [];
|
|
244
|
+
if (configFiles.length === 0) {
|
|
245
|
+
console.warn("ā ļø No files configured. Run 'knowhow cloudworker --init' first to set up file sync entries.");
|
|
225
246
|
}
|
|
226
|
-
const allFiles = Array.from(allFilesMap.values());
|
|
227
247
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
console.log(` ${f.localPath} ā ${f.remotePath}`);
|
|
234
|
-
}
|
|
248
|
+
// Re-collect language-referenced files on every push (if language.json is in config.files)
|
|
249
|
+
// so that new language term sources are picked up without needing --init again.
|
|
250
|
+
const languageFiles = await collectLanguageFilesIfConfigured(configFiles, projectName);
|
|
251
|
+
if (languageFiles.length > 0) {
|
|
252
|
+
console.log(` + ${languageFiles.length} language-referenced file(s) to sync`);
|
|
235
253
|
}
|
|
236
254
|
|
|
237
|
-
//
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// Preserve any existing files entries not in our set
|
|
248
|
-
const existingFiles = config.files || [];
|
|
249
|
-
const newRemotePaths = new Set(configFilesEntries.map((e) => e.remotePath));
|
|
250
|
-
|
|
251
|
-
// Keep entries that don't overlap with new ones
|
|
252
|
-
const preserved = existingFiles.filter(
|
|
253
|
-
(e) => !newRemotePaths.has(e.remotePath)
|
|
254
|
-
);
|
|
255
|
+
// Merge language files into the upload list (deduplicate by remotePath)
|
|
256
|
+
const allFilesMap = new Map<string, { remotePath: string; localPath: string }>();
|
|
257
|
+
for (const f of configFiles) {
|
|
258
|
+
allFilesMap.set(f.remotePath, f);
|
|
259
|
+
}
|
|
260
|
+
for (const f of languageFiles) {
|
|
261
|
+
const entry = { remotePath: f.remotePath, localPath: f.downloadLocalPath ?? f.localPath };
|
|
262
|
+
allFilesMap.set(f.remotePath, entry);
|
|
263
|
+
}
|
|
264
|
+
const allFiles = Array.from(allFilesMap.values());
|
|
255
265
|
|
|
256
|
-
|
|
266
|
+
// If new language files were found, update config.files so they persist
|
|
267
|
+
if (languageFiles.length > 0 && !dryRun) {
|
|
268
|
+
config.files = allFiles.map((f) => ({ ...f, direction: "download" as const }));
|
|
257
269
|
await updateConfig(config);
|
|
258
|
-
console.log(` ā Updated config with ${config.files.length} file entries`);
|
|
259
|
-
} else {
|
|
260
|
-
console.log(` [DRY RUN] Would update config with ${configFilesEntries.length} file entries`);
|
|
261
270
|
}
|
|
262
271
|
|
|
263
|
-
//
|
|
264
|
-
const workerConfigJson = buildWorkerConfigJson(config,
|
|
272
|
+
// Build the workerConfigJson using the full file list
|
|
273
|
+
const workerConfigJson = buildWorkerConfigJson(config, allFiles.map((f) => ({ ...f, direction: "download" })));
|
|
265
274
|
|
|
266
|
-
//
|
|
267
|
-
console.log(`\nš Uploading ${allFiles.length} files...`);
|
|
275
|
+
// Upload all files
|
|
276
|
+
console.log(`\nš Uploading ${allFiles.length} configured files...`);
|
|
268
277
|
let successCount = 0;
|
|
269
278
|
let failCount = 0;
|
|
270
279
|
|
|
271
|
-
for (const
|
|
280
|
+
for (const mount of allFiles) {
|
|
281
|
+
const { remotePath, localPath } = mount;
|
|
272
282
|
try {
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const remoteDir = file.remotePath.endsWith("/") ? file.remotePath : file.remotePath + "/";
|
|
277
|
-
const relFiles = listFilesRecursively(localDir);
|
|
278
|
-
console.log(` š Uploading directory ${localDir} ā ${remoteDir} (${relFiles.length} files)`);
|
|
279
|
-
for (const relFile of relFiles) {
|
|
280
|
-
await uploadSingleFile(client, AwsS3, localDir + relFile, remoteDir + relFile, dryRun);
|
|
281
|
-
successCount++;
|
|
282
|
-
}
|
|
283
|
+
if (remotePath.endsWith("/") || localPath.endsWith("/")) {
|
|
284
|
+
const count = await uploadDirectory(client, AwsS3, remotePath, localPath, dryRun);
|
|
285
|
+
successCount += count;
|
|
283
286
|
} else {
|
|
284
|
-
await
|
|
287
|
+
await uploadFile(client, AwsS3, remotePath, localPath, dryRun);
|
|
285
288
|
successCount++;
|
|
286
289
|
}
|
|
287
290
|
} catch (error) {
|
|
288
|
-
console.error(` ā Failed to upload ${
|
|
291
|
+
console.error(` ā Failed to upload ${localPath}: ${error.message}`);
|
|
289
292
|
failCount++;
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
console.log(`\n ā Upload complete: ${successCount} succeeded, ${failCount} failed`);
|
|
294
297
|
|
|
295
|
-
//
|
|
298
|
+
// Create or update cloud worker
|
|
296
299
|
if (create) {
|
|
297
300
|
const workerName = name || `${projectName}-worker`;
|
|
298
301
|
console.log(`\nš©ļø Creating cloud worker "${workerName}"...`);
|
|
@@ -330,3 +333,62 @@ export async function cloudWorker(options: CloudWorkerOptions) {
|
|
|
330
333
|
console.log(`\nā
Cloud worker sync complete!`);
|
|
331
334
|
}
|
|
332
335
|
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Pull the latest workerConfigJson from the cloud worker API and update the
|
|
339
|
+
* local knowhow.json config to match.
|
|
340
|
+
*/
|
|
341
|
+
export async function pullCloudWorkerConfig(options: CloudWorkerPullOptions) {
|
|
342
|
+
const { id, apiUrl = KNOWHOW_API_URL } = options;
|
|
343
|
+
|
|
344
|
+
// Load JWT
|
|
345
|
+
const jwt = await loadJwt();
|
|
346
|
+
if (!jwt) {
|
|
347
|
+
console.error("ā No JWT token found. Please run 'knowhow login' first.");
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const client = new KnowhowSimpleClient(apiUrl, jwt);
|
|
352
|
+
|
|
353
|
+
console.log(`š Pulling config for cloud worker ${id}...`);
|
|
354
|
+
|
|
355
|
+
const resp = await client.getCloudWorker(id);
|
|
356
|
+
const remoteWorker = resp.data;
|
|
357
|
+
|
|
358
|
+
if (!remoteWorker) {
|
|
359
|
+
console.error(`ā Cloud worker ${id} not found.`);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const remoteConfig = (remoteWorker.workerConfigJson ?? {}) as {
|
|
364
|
+
mcps?: McpConfig[];
|
|
365
|
+
modules?: string[];
|
|
366
|
+
plugins?: Config["plugins"];
|
|
367
|
+
agents?: Config["agents"];
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// Load current local config
|
|
371
|
+
const localConfig = await getConfig();
|
|
372
|
+
|
|
373
|
+
// Merge remote fields into local config
|
|
374
|
+
if (remoteConfig.mcps !== undefined) {
|
|
375
|
+
localConfig.mcps = remoteConfig.mcps;
|
|
376
|
+
}
|
|
377
|
+
if (remoteConfig.modules !== undefined) {
|
|
378
|
+
localConfig.modules = remoteConfig.modules;
|
|
379
|
+
}
|
|
380
|
+
if (remoteConfig.plugins !== undefined) {
|
|
381
|
+
localConfig.plugins = remoteConfig.plugins;
|
|
382
|
+
}
|
|
383
|
+
if (remoteConfig.agents !== undefined) {
|
|
384
|
+
localConfig.agents = remoteConfig.agents;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
await updateConfig(localConfig);
|
|
388
|
+
|
|
389
|
+
const mcpCount = remoteConfig.mcps?.length ?? 0;
|
|
390
|
+
console.log(`ā
Config pulled! ${mcpCount} MCP(s) now configured locally.`);
|
|
391
|
+
console.log(` Run 'knowhow worker' or trigger reloadConfig to apply changes.`);
|
|
392
|
+
|
|
393
|
+
return { mcps: remoteConfig.mcps, modules: remoteConfig.modules };
|
|
394
|
+
}
|