@tyvm/knowhow 0.0.147 → 0.0.149
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/package.json +1 -1
- package/src/agents/base/base.ts +45 -32
- package/src/agents/tools/execCommand.ts +102 -320
- package/src/agents/tools/list.ts +2 -2
- package/src/agents/tools/startAgentTask.ts +55 -27
- package/src/ai.ts +4 -1
- package/src/auth/keyAuth.ts +10 -4
- package/src/auth/registerIdentity.ts +67 -0
- package/src/chat/CliChatService.ts +23 -2
- package/src/chat/modules/AgentModule.ts +39 -7
- package/src/chat/modules/InternalChatModule.ts +5 -0
- package/src/chat/modules/ToolCommandModule.ts +102 -0
- package/src/chat/modules/index.ts +1 -0
- package/src/chat/types.ts +1 -1
- package/src/cli.ts +9 -2
- package/src/clients/http.ts +67 -7
- package/src/clients/index.ts +36 -1
- package/src/commands/agent.ts +1 -0
- package/src/commands/agents.ts +57 -9
- package/src/commands/misc.ts +17 -2
- package/src/commands/processes.ts +203 -0
- package/src/commands/services.ts +23 -1
- package/src/index.ts +1 -0
- package/src/processes/ProcessManager.ts +95 -0
- package/src/processes/managedProcessShim.ts +127 -0
- package/src/processors/Base64ImageDetector.ts +28 -4
- package/src/processors/JsonCompressor.ts +14 -0
- package/src/processors/TokenCompressor.ts +17 -14
- package/src/services/AgentService.ts +2 -2
- package/src/services/AgentSyncFs.ts +12 -1
- package/src/services/ExtensionsService.ts +25 -0
- package/src/services/LazyToolsService.ts +15 -20
- package/src/services/Tools.ts +10 -0
- package/src/services/index.ts +4 -0
- package/src/services/modules/computerUse.ts +3 -1
- package/src/services/modules/index.ts +6 -0
- package/src/services/modules/types.ts +4 -0
- package/tests/clients/AIClient.test.ts +98 -15
- package/tests/processes/managedProcessShim.test.ts +59 -0
- package/tests/processors/Base64ImageDetector.test.ts +21 -0
- package/tests/processors/TokenCompressor.test.ts +18 -0
- package/tests/unit/auth/registerIdentity.test.ts +93 -0
- package/tests/unit/chat/ToolCommandModule.test.ts +71 -0
- package/tests/unit/commands/agents.test.ts +44 -1
- package/tests/unit/services/LazyToolsService.test.ts +24 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.d.ts +1 -1
- package/ts_build/src/agents/base/base.js +30 -21
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.d.ts +4 -1
- package/ts_build/src/agents/tools/execCommand.js +100 -223
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +2 -2
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/startAgentTask.d.ts +14 -1
- package/ts_build/src/agents/tools/startAgentTask.js +37 -21
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/ai.js +2 -1
- package/ts_build/src/ai.js.map +1 -1
- package/ts_build/src/auth/keyAuth.js +10 -3
- package/ts_build/src/auth/keyAuth.js.map +1 -1
- package/ts_build/src/auth/registerIdentity.d.ts +7 -0
- package/ts_build/src/auth/registerIdentity.js +62 -0
- package/ts_build/src/auth/registerIdentity.js.map +1 -0
- package/ts_build/src/chat/CliChatService.d.ts +6 -0
- package/ts_build/src/chat/CliChatService.js +17 -2
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
- package/ts_build/src/chat/modules/AgentModule.js +27 -7
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/InternalChatModule.d.ts +1 -0
- package/ts_build/src/chat/modules/InternalChatModule.js +5 -0
- package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
- package/ts_build/src/chat/modules/ToolCommandModule.d.ts +10 -0
- package/ts_build/src/chat/modules/ToolCommandModule.js +86 -0
- package/ts_build/src/chat/modules/ToolCommandModule.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 +1 -1
- package/ts_build/src/cli.js +6 -2
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/http.d.ts +4 -0
- package/ts_build/src/clients/http.js +68 -6
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.js +23 -1
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/commands/agent.js +1 -0
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/agents.d.ts +1 -0
- package/ts_build/src/commands/agents.js +43 -8
- package/ts_build/src/commands/agents.js.map +1 -1
- package/ts_build/src/commands/misc.js +12 -1
- package/ts_build/src/commands/misc.js.map +1 -1
- package/ts_build/src/commands/processes.d.ts +2 -0
- package/ts_build/src/commands/processes.js +282 -0
- package/ts_build/src/commands/processes.js.map +1 -0
- package/ts_build/src/commands/services.js +11 -1
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/index.d.ts +1 -0
- package/ts_build/src/index.js +1 -0
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/processes/ProcessManager.d.ts +51 -0
- package/ts_build/src/processes/ProcessManager.js +160 -0
- package/ts_build/src/processes/ProcessManager.js.map +1 -0
- package/ts_build/src/processes/managedProcessShim.d.ts +1 -0
- package/ts_build/src/processes/managedProcessShim.js +167 -0
- package/ts_build/src/processes/managedProcessShim.js.map +1 -0
- package/ts_build/src/processors/Base64ImageDetector.d.ts +1 -0
- package/ts_build/src/processors/Base64ImageDetector.js +19 -2
- package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
- package/ts_build/src/processors/JsonCompressor.js +7 -0
- package/ts_build/src/processors/JsonCompressor.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.d.ts +1 -1
- package/ts_build/src/processors/TokenCompressor.js +9 -8
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/services/AgentService.d.ts +1 -1
- package/ts_build/src/services/AgentService.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.js +6 -1
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/ExtensionsService.d.ts +10 -0
- package/ts_build/src/services/ExtensionsService.js +17 -0
- package/ts_build/src/services/ExtensionsService.js.map +1 -0
- package/ts_build/src/services/LazyToolsService.d.ts +1 -0
- package/ts_build/src/services/LazyToolsService.js +5 -7
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +1 -0
- package/ts_build/src/services/Tools.js +3 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +3 -0
- package/ts_build/src/services/index.js +3 -0
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/computerUse.d.ts +3 -1
- package/ts_build/src/services/modules/index.d.ts +2 -0
- package/ts_build/src/services/modules/index.js +5 -0
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +3 -0
- package/ts_build/tests/clients/AIClient.test.js +73 -11
- package/ts_build/tests/clients/AIClient.test.js.map +1 -1
- package/ts_build/tests/processes/managedProcessShim.test.d.ts +1 -0
- package/ts_build/tests/processes/managedProcessShim.test.js +92 -0
- package/ts_build/tests/processes/managedProcessShim.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +18 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +15 -0
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/unit/auth/registerIdentity.test.d.ts +1 -0
- package/ts_build/tests/unit/auth/registerIdentity.test.js +86 -0
- package/ts_build/tests/unit/auth/registerIdentity.test.js.map +1 -0
- package/ts_build/tests/unit/chat/ToolCommandModule.test.d.ts +1 -0
- package/ts_build/tests/unit/chat/ToolCommandModule.test.js +61 -0
- package/ts_build/tests/unit/chat/ToolCommandModule.test.js.map +1 -0
- package/ts_build/tests/unit/commands/agents.test.js +28 -0
- package/ts_build/tests/unit/commands/agents.test.js.map +1 -1
- package/ts_build/tests/unit/services/LazyToolsService.test.js +20 -0
- package/ts_build/tests/unit/services/LazyToolsService.test.js.map +1 -1
package/src/clients/http.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import http from "../utils/http";
|
|
1
|
+
import http, { HTTP_UNAUTHORIZED_HANDLER, RefreshableHeaders } from "../utils/http";
|
|
2
2
|
import {
|
|
3
3
|
GenericClient,
|
|
4
4
|
CompletionOptions,
|
|
@@ -17,16 +17,72 @@ export interface HttpClientOptions {
|
|
|
17
17
|
extra_body?: Record<string, any>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Optional callback that, when set, is called whenever a 401 is received
|
|
22
|
+
* (reactive refresh) or whenever `needsRefresh()` returns true before a
|
|
23
|
+
* request (proactive refresh). Override this in a subclass or set it
|
|
24
|
+
* directly on an instance:
|
|
25
|
+
*
|
|
26
|
+
* client.refreshToken = async () => { const newJwt = await ...; client.setJwt(newJwt); };
|
|
27
|
+
*/
|
|
28
|
+
export type RefreshTokenFn = () => Promise<void>;
|
|
29
|
+
|
|
20
30
|
export class HttpClient implements GenericClient {
|
|
21
31
|
/** Timeout in milliseconds for HTTP requests. Default: 30000 (30s). Use 0 to disable. */
|
|
22
32
|
private timeout: number;
|
|
23
|
-
private headers:
|
|
33
|
+
private headers: RefreshableHeaders;
|
|
24
34
|
private extra_body: Record<string, any>;
|
|
25
35
|
/** Optional pricing table: model id → per-million-token prices */
|
|
26
36
|
private pricingMap: Record<string, ModelPricing> = {};
|
|
27
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Override this to provide automatic token refresh. Called reactively on
|
|
40
|
+
* 401 responses (via HTTP_UNAUTHORIZED_HANDLER) and proactively before
|
|
41
|
+
* each request when `needsRefresh()` returns true.
|
|
42
|
+
*
|
|
43
|
+
* The implementation should obtain a new token and call `this.setJwt()`
|
|
44
|
+
* (or `this.setKey()`) before returning.
|
|
45
|
+
*/
|
|
46
|
+
refreshToken: RefreshTokenFn | undefined = undefined;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Override this to provide proactive expiry detection. When it returns
|
|
50
|
+
* `true`, `refreshToken()` is called before the next outgoing request.
|
|
51
|
+
* The default implementation decodes the JWT `exp` claim (< 5 min window).
|
|
52
|
+
*/
|
|
53
|
+
needsRefresh(): boolean {
|
|
54
|
+
const authHeader = (this.headers as Record<string, string>)["Authorization"];
|
|
55
|
+
if (!authHeader) return false;
|
|
56
|
+
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : authHeader;
|
|
57
|
+
try {
|
|
58
|
+
const parts = token.split(".");
|
|
59
|
+
if (parts.length !== 3) return false;
|
|
60
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
61
|
+
return typeof payload.exp === "number" && payload.exp * 1000 - Date.now() < 5 * 60 * 1000;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Call before each outgoing request to proactively refresh an expiring
|
|
69
|
+
* token. No-ops when no `refreshToken` hook is set.
|
|
70
|
+
*/
|
|
71
|
+
private async refreshIfNeeded(): Promise<void> {
|
|
72
|
+
if (this.refreshToken && this.needsRefresh()) {
|
|
73
|
+
await this.refreshToken();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
28
77
|
constructor(private baseUrl: string, options: HttpClientOptions = {}) {
|
|
29
78
|
this.headers = options.headers ?? {};
|
|
79
|
+
// Wire the reactive 401 handler so the low-level http util can retry once
|
|
80
|
+
// after a token refresh without callers needing to do anything extra.
|
|
81
|
+
Object.defineProperty(this.headers, HTTP_UNAUTHORIZED_HANDLER, {
|
|
82
|
+
enumerable: false,
|
|
83
|
+
configurable: true,
|
|
84
|
+
get: () => this.refreshToken ? () => this.refreshToken!() : undefined,
|
|
85
|
+
});
|
|
30
86
|
this.timeout = options.timeout ?? 30000;
|
|
31
87
|
this.extra_body = options.extra_body ?? {};
|
|
32
88
|
}
|
|
@@ -69,10 +125,8 @@ export class HttpClient implements GenericClient {
|
|
|
69
125
|
}
|
|
70
126
|
|
|
71
127
|
setJwt(jwt: string) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Authorization: `Bearer ${jwt}`,
|
|
75
|
-
};
|
|
128
|
+
// Mutate in-place to preserve the HTTP_UNAUTHORIZED_HANDLER symbol property.
|
|
129
|
+
this.headers.Authorization = `Bearer ${jwt}`;
|
|
76
130
|
}
|
|
77
131
|
|
|
78
132
|
setBaseUrl(baseUrl: string) {
|
|
@@ -163,7 +217,8 @@ export class HttpClient implements GenericClient {
|
|
|
163
217
|
if (options.timeout !== undefined) this.timeout = options.timeout;
|
|
164
218
|
if (options.extra_body !== undefined) this.extra_body = options.extra_body;
|
|
165
219
|
if (options.headers) {
|
|
166
|
-
|
|
220
|
+
// Mutate in-place to preserve the HTTP_UNAUTHORIZED_HANDLER symbol property.
|
|
221
|
+
Object.assign(this.headers, options.headers);
|
|
167
222
|
}
|
|
168
223
|
}
|
|
169
224
|
|
|
@@ -183,6 +238,7 @@ export class HttpClient implements GenericClient {
|
|
|
183
238
|
async createChatCompletion(
|
|
184
239
|
options: CompletionOptions
|
|
185
240
|
): Promise<CompletionResponse> {
|
|
241
|
+
await this.refreshIfNeeded();
|
|
186
242
|
return this.withRetry(async () => {
|
|
187
243
|
const body = {
|
|
188
244
|
...options,
|
|
@@ -232,6 +288,7 @@ export class HttpClient implements GenericClient {
|
|
|
232
288
|
async *createChatCompletionStream(
|
|
233
289
|
options: CompletionOptions
|
|
234
290
|
): AsyncGenerator<StreamChunk> {
|
|
291
|
+
await this.refreshIfNeeded();
|
|
235
292
|
const body = {
|
|
236
293
|
...options,
|
|
237
294
|
model: options.model,
|
|
@@ -316,6 +373,7 @@ export class HttpClient implements GenericClient {
|
|
|
316
373
|
options: CompletionOptions,
|
|
317
374
|
store = false
|
|
318
375
|
): Promise<CompletionResponse> {
|
|
376
|
+
await this.refreshIfNeeded();
|
|
319
377
|
return this.withRetry(async () => {
|
|
320
378
|
// Extract system messages as instructions
|
|
321
379
|
const systemMessages = options.messages.filter((m) => m.role === "system");
|
|
@@ -484,6 +542,7 @@ export class HttpClient implements GenericClient {
|
|
|
484
542
|
}
|
|
485
543
|
|
|
486
544
|
async createEmbedding(options: EmbeddingOptions): Promise<EmbeddingResponse> {
|
|
545
|
+
await this.refreshIfNeeded();
|
|
487
546
|
return this.withRetry(async () => {
|
|
488
547
|
const response = await http.post(
|
|
489
548
|
`${this.baseUrl}/v1/embeddings`,
|
|
@@ -511,6 +570,7 @@ export class HttpClient implements GenericClient {
|
|
|
511
570
|
}
|
|
512
571
|
|
|
513
572
|
async getModels(type = "all") {
|
|
573
|
+
await this.refreshIfNeeded();
|
|
514
574
|
return this.withRetry(async () => {
|
|
515
575
|
const response = await http.get(`${this.baseUrl}/v1/models?type=${type}`, {
|
|
516
576
|
headers: this.headers as Record<string, string>,
|
package/src/clients/index.ts
CHANGED
|
@@ -528,12 +528,14 @@ export class AIClient {
|
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
providerHasModel(provider: string, model: string): boolean {
|
|
531
|
+
if (!provider) return false;
|
|
531
532
|
const models = this.clientModels[provider];
|
|
532
533
|
if (!models) return false;
|
|
533
534
|
return models.includes(model);
|
|
534
535
|
}
|
|
535
536
|
|
|
536
537
|
findModel(modelPrefix: string) {
|
|
538
|
+
if (!modelPrefix) return undefined;
|
|
537
539
|
for (const provider of Object.keys(this.clientModels)) {
|
|
538
540
|
const models = this.clientModels[provider] as string[];
|
|
539
541
|
const foundModel = models.find((m) => m.startsWith(modelPrefix));
|
|
@@ -542,14 +544,17 @@ export class AIClient {
|
|
|
542
544
|
}
|
|
543
545
|
|
|
544
546
|
// Handle the case when model prefix is gpt-5 and the provider is knowhow, and the actual model is openai/gpt-5
|
|
547
|
+
// Also handles deep prefixes like "accounts/fireworks/models/kimi-k3" matched by "kimi-k3"
|
|
545
548
|
const inferredFound = models.find((m) => {
|
|
546
549
|
const split = m.split("/");
|
|
547
550
|
if (split.length < 2) return false;
|
|
548
551
|
const inferredModel = split.slice(1).join("/");
|
|
552
|
+
const lastSegment = split[split.length - 1];
|
|
549
553
|
return (
|
|
550
554
|
m === modelPrefix ||
|
|
551
555
|
inferredModel === modelPrefix ||
|
|
552
|
-
inferredModel.startsWith(modelPrefix)
|
|
556
|
+
inferredModel.startsWith(modelPrefix) ||
|
|
557
|
+
lastSegment === modelPrefix
|
|
553
558
|
);
|
|
554
559
|
});
|
|
555
560
|
if (inferredFound) {
|
|
@@ -624,6 +629,36 @@ export class AIClient {
|
|
|
624
629
|
return { provider, model };
|
|
625
630
|
}
|
|
626
631
|
|
|
632
|
+
// When an explicit provider is given and the model contains slashes (e.g.
|
|
633
|
+
// provider="knowhow", model="anthropic/claude-sonnet-4-6"), check if the
|
|
634
|
+
// explicit provider has a model that matches by ignoring the leading
|
|
635
|
+
// provider prefix segments. Also handle deep prefixes like
|
|
636
|
+
// "accounts/fireworks/models/kimi-k3" → search for "kimi-k3".
|
|
637
|
+
if (provider && model) {
|
|
638
|
+
const providerModels = this.clientModels[provider] ?? [];
|
|
639
|
+
if (model.includes("/")) {
|
|
640
|
+
// Model has slashes: try progressively stripping leading prefix segments
|
|
641
|
+
const modelParts = model.split("/");
|
|
642
|
+
for (let i = 1; i < modelParts.length; i++) {
|
|
643
|
+
const suffix = modelParts.slice(i).join("/");
|
|
644
|
+
// Exact match within the explicit provider
|
|
645
|
+
if (this.providerHasModel(provider, suffix)) {
|
|
646
|
+
return { provider, model: suffix };
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// For any model (with or without slashes): check if a registered model's
|
|
651
|
+
// last path segment matches the requested model name.
|
|
652
|
+
// e.g. provider="fireworks", model="kimi-k3" matches
|
|
653
|
+
// "accounts/fireworks/models/kimi-k3"
|
|
654
|
+
const lastSegmentMatch = providerModels.find(
|
|
655
|
+
(m) => m.split("/").pop() === model
|
|
656
|
+
);
|
|
657
|
+
if (lastSegmentMatch) {
|
|
658
|
+
return { provider, model: lastSegmentMatch };
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
627
662
|
// If an explicit provider was given, don't fall through to fuzzy cross-provider
|
|
628
663
|
// search — that would silently pick a completely different provider (e.g. nvidia
|
|
629
664
|
// instead of fireworks). Just pass through and let the API accept/reject the model.
|
package/src/commands/agent.ts
CHANGED
|
@@ -193,6 +193,7 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
|
|
|
193
193
|
|
|
194
194
|
const { taskCompleted } = await agentModule.setupAgent({
|
|
195
195
|
...options,
|
|
196
|
+
agentName: options.agentName ?? config.chat?.defaultAgent ?? "Patcher",
|
|
196
197
|
input,
|
|
197
198
|
maxTimeLimit: parseInt(options.maxTimeLimit, 10),
|
|
198
199
|
maxSpendLimit: parseFloat(options.maxSpendLimit),
|
package/src/commands/agents.ts
CHANGED
|
@@ -35,6 +35,18 @@ import { getConfig } from "../config";
|
|
|
35
35
|
const AGENTS_DIR = path.join(".knowhow", "processes", "agents");
|
|
36
36
|
const SESSIONS_DIR = path.join(".knowhow", "chats", "sessions");
|
|
37
37
|
|
|
38
|
+
const TERMINAL_STATUSES = new Set([
|
|
39
|
+
"completed",
|
|
40
|
+
"failed",
|
|
41
|
+
"killed",
|
|
42
|
+
"error",
|
|
43
|
+
"done",
|
|
44
|
+
]);
|
|
45
|
+
const ACTIVE_STATUSES = new Set(["running", "paused"]);
|
|
46
|
+
// Older task records did not persist a pid. After this grace period, report
|
|
47
|
+
// them as unverifiable rather than continuing to claim they are running.
|
|
48
|
+
const UNVERIFIED_TASK_GRACE_MS = 5 * 60 * 1000;
|
|
49
|
+
|
|
38
50
|
// ---------------------------------------------------------------------------
|
|
39
51
|
// Helpers
|
|
40
52
|
// ---------------------------------------------------------------------------
|
|
@@ -68,11 +80,42 @@ function loadStatus(taskId: string): string {
|
|
|
68
80
|
}
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
function pidAlive(pid: number): boolean {
|
|
84
|
+
try {
|
|
85
|
+
process.kill(pid, 0);
|
|
86
|
+
return true;
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
// EPERM means the process exists but cannot be signalled by this user.
|
|
89
|
+
return error?.code === "EPERM";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the effective status, reconciling persisted state with process liveness.
|
|
95
|
+
* An agent can be terminated before AgentSyncFs gets a chance to finalize its
|
|
96
|
+
* files; those records must not remain "running" forever.
|
|
97
|
+
*/
|
|
98
|
+
export function getStatus(taskId: string, meta: any | null): string {
|
|
99
|
+
const persisted = meta?.status || loadStatus(taskId) || "unknown";
|
|
100
|
+
if (TERMINAL_STATUSES.has(persisted)) return persisted;
|
|
101
|
+
if (meta?.inProgress === false) return "completed";
|
|
102
|
+
if (!ACTIVE_STATUSES.has(persisted)) return persisted;
|
|
103
|
+
|
|
104
|
+
const pid = meta?.pid ?? meta?.processId;
|
|
105
|
+
if (typeof pid === "number") {
|
|
106
|
+
return pidAlive(pid) ? persisted : "failed";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const lastKnownTime = meta?.lastUpdate ?? meta?.startTime;
|
|
110
|
+
const lastKnownMs = lastKnownTime ? new Date(lastKnownTime).getTime() : NaN;
|
|
111
|
+
if (
|
|
112
|
+
!Number.isFinite(lastKnownMs) ||
|
|
113
|
+
Date.now() - lastKnownMs > UNVERIFIED_TASK_GRACE_MS
|
|
114
|
+
) {
|
|
115
|
+
return "stale";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return persisted;
|
|
76
119
|
}
|
|
77
120
|
|
|
78
121
|
function elapsedStr(startIso: string | undefined, endIso?: string): string {
|
|
@@ -203,9 +246,9 @@ function buildRows(includeAll: boolean): Row[] {
|
|
|
203
246
|
|
|
204
247
|
for (const taskId of taskIds) {
|
|
205
248
|
const meta = loadMeta(taskId);
|
|
206
|
-
const status =
|
|
249
|
+
const status = getStatus(taskId, meta);
|
|
207
250
|
|
|
208
|
-
if (!includeAll && (status
|
|
251
|
+
if (!includeAll && (TERMINAL_STATUSES.has(status) || status === "stale")) {
|
|
209
252
|
const lastUpdate = meta?.lastUpdate ?? "";
|
|
210
253
|
if (lastUpdate) {
|
|
211
254
|
const age = Date.now() - new Date(lastUpdate).getTime();
|
|
@@ -221,7 +264,9 @@ function buildRows(includeAll: boolean): Row[] {
|
|
|
221
264
|
: "";
|
|
222
265
|
const elapsed = elapsedStr(
|
|
223
266
|
meta?.startTime,
|
|
224
|
-
|
|
267
|
+
TERMINAL_STATUSES.has(status) || status === "stale"
|
|
268
|
+
? meta?.lastUpdate
|
|
269
|
+
: undefined
|
|
225
270
|
);
|
|
226
271
|
const lastUpdate = meta?.lastUpdate
|
|
227
272
|
? new Date(meta.lastUpdate).toLocaleString()
|
|
@@ -247,12 +292,15 @@ function buildRows(includeAll: boolean): Row[] {
|
|
|
247
292
|
* - A numeric index string ("1", "2", …) — 1-based row from `agents list`
|
|
248
293
|
* - An exact task ID
|
|
249
294
|
* - A partial task ID (substring match)
|
|
295
|
+
*
|
|
296
|
+
* NOTE: numeric index always uses the same row set as `agents list` (includeAll=false)
|
|
297
|
+
* so that index numbers are consistent across all subcommands.
|
|
250
298
|
*/
|
|
251
299
|
function resolveTaskId(value: string, includeAll = false): string | null {
|
|
252
300
|
const n = parseInt(value, 10);
|
|
253
301
|
// Only treat as a numeric index if it's a small number (≤ 9999) — large numbers are timestamp-based task IDs
|
|
254
302
|
if (!isNaN(n) && String(n) === value.trim() && n <= 9999) {
|
|
255
|
-
const rows = buildRows(
|
|
303
|
+
const rows = buildRows(false);
|
|
256
304
|
const row = rows[n - 1];
|
|
257
305
|
return row ? row.taskId : null;
|
|
258
306
|
}
|
package/src/commands/misc.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { embed, upload, download, purge } from "../index";
|
|
|
6
6
|
import { generate, buildWaves, normalizeInputPattern, GenerateOptions } from "../generate";
|
|
7
7
|
import { init } from "../config";
|
|
8
8
|
import { login } from "../login";
|
|
9
|
+
import { registerIdentity } from "../auth/registerIdentity";
|
|
9
10
|
import { KnowhowSimpleClient } from "../services/KnowhowClient";
|
|
10
11
|
import { startChat } from "../chat";
|
|
11
12
|
|
|
@@ -24,8 +25,22 @@ export function addLoginCommand(program: Command): void {
|
|
|
24
25
|
.description("Login to knowhow")
|
|
25
26
|
.option("--jwt", "Use manual JWT input instead of browser login")
|
|
26
27
|
.option("-i, --identity <path>", "Ed25519 private key to use for public-key login")
|
|
27
|
-
.
|
|
28
|
-
|
|
28
|
+
.option("--register-identity", "Register an Ed25519 identity using the JWT in .knowhow/.jwt")
|
|
29
|
+
.option("--api-url <url>", "API base URL override (falls back to KNOWHOW_API_URL env)")
|
|
30
|
+
.action(async (options: {
|
|
31
|
+
jwt?: boolean;
|
|
32
|
+
identity?: string;
|
|
33
|
+
registerIdentity?: boolean;
|
|
34
|
+
apiUrl?: string;
|
|
35
|
+
}) => {
|
|
36
|
+
if (options.registerIdentity) {
|
|
37
|
+
await registerIdentity({
|
|
38
|
+
identityPath: options.identity,
|
|
39
|
+
apiUrl: options.apiUrl,
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
await login(options.jwt, options.identity);
|
|
43
|
+
}
|
|
29
44
|
});
|
|
30
45
|
}
|
|
31
46
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import {
|
|
5
|
+
getProcessesDir, listManagedProcesses, ManagedProcess,
|
|
6
|
+
ManagedProcessInfo, processDirectory,
|
|
7
|
+
} from "../processes/ProcessManager";
|
|
8
|
+
import { runManagedProcessShim } from "../processes/managedProcessShim";
|
|
9
|
+
|
|
10
|
+
const terminal = new Set(["exited", "failed", "stopped"]);
|
|
11
|
+
function resolveProcess(value?: string, index?: string, options: any = {}): ManagedProcess {
|
|
12
|
+
const root = getProcessesDir();
|
|
13
|
+
if (index !== undefined) {
|
|
14
|
+
const parsed = Number(index);
|
|
15
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
16
|
+
throw new Error(`Invalid process index '${index}'. Use the index shown by 'knowhow processes list'.`);
|
|
17
|
+
}
|
|
18
|
+
const items = filtered(options);
|
|
19
|
+
const item = items[parsed];
|
|
20
|
+
if (!item) {
|
|
21
|
+
throw new Error(`Managed process index ${parsed} not found. Run 'knowhow processes list${options.all ? " --all" : ""}' to see valid indices.`);
|
|
22
|
+
}
|
|
23
|
+
return new ManagedProcess(item.id, item.directory);
|
|
24
|
+
}
|
|
25
|
+
if (!value) {
|
|
26
|
+
throw new Error("Provide a process id, or use -i <index> from 'knowhow processes list'.");
|
|
27
|
+
}
|
|
28
|
+
const matches = listManagedProcesses(root).filter((p) => p.id === value || p.id.includes(value));
|
|
29
|
+
if (!matches.length) throw new Error(`Managed process not found: ${value}`);
|
|
30
|
+
const exact = matches.find((p) => p.id === value);
|
|
31
|
+
if (!exact && matches.length > 1) throw new Error(`Ambiguous process id '${value}': ${matches.map((p) => p.id).join(", ")}`);
|
|
32
|
+
const item = exact || matches[0];
|
|
33
|
+
return new ManagedProcess(item.id, item.directory);
|
|
34
|
+
}
|
|
35
|
+
function commandOptions(options: any, command?: Command): any {
|
|
36
|
+
const fromOptions = typeof options?.optsWithGlobals === "function" ? options.optsWithGlobals() : options || {};
|
|
37
|
+
const fromCommand = typeof command?.optsWithGlobals === "function" ? command.optsWithGlobals() : {};
|
|
38
|
+
return { ...fromOptions, ...fromCommand };
|
|
39
|
+
}
|
|
40
|
+
function filtered(options: any): ManagedProcessInfo[] {
|
|
41
|
+
return listManagedProcesses().filter((item) => {
|
|
42
|
+
if (!options.all && terminal.has(item.status.state)) return false;
|
|
43
|
+
if (options.parent && item.status.parentProcessId !== options.parent) return false;
|
|
44
|
+
if (options.background !== undefined && item.status.background !== options.background) return false;
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function truncate(value: string, width: number): string {
|
|
49
|
+
if (value.length <= width) return value;
|
|
50
|
+
if (width <= 1) return "…";
|
|
51
|
+
return `${value.slice(0, width - 1)}…`;
|
|
52
|
+
}
|
|
53
|
+
function truncateMiddle(value: string, width: number): string {
|
|
54
|
+
if (value.length <= width) return value;
|
|
55
|
+
if (width <= 1) return "…";
|
|
56
|
+
const left = Math.ceil((width - 1) / 2);
|
|
57
|
+
return `${value.slice(0, left)}…${value.slice(value.length - (width - left - 1))}`;
|
|
58
|
+
}
|
|
59
|
+
function printProcessTable(rows: Array<Record<string, string | number>>): void {
|
|
60
|
+
const terminalWidth = process.stdout.columns || 120;
|
|
61
|
+
const available = Math.max(80, Math.min(terminalWidth, 180));
|
|
62
|
+
const widths = {
|
|
63
|
+
index: Math.max(1, String(rows.length - 1).length),
|
|
64
|
+
state: Math.max(5, ...rows.map((row) => String(row.state).length)),
|
|
65
|
+
pid: Math.max(3, ...rows.map((row) => String(row.pid).length)),
|
|
66
|
+
background: 2,
|
|
67
|
+
id: Math.min(38, Math.max(12, ...rows.map((row) => String(row.id).length))),
|
|
68
|
+
parent: Math.min(20, Math.max(6, ...rows.map((row) => String(row.parent).length))),
|
|
69
|
+
};
|
|
70
|
+
const fixedWidth = widths.index + widths.state + widths.pid + widths.background
|
|
71
|
+
+ widths.id + widths.parent + 12;
|
|
72
|
+
const commandWidth = Math.max(20, available - fixedWidth);
|
|
73
|
+
const header = ["#".padEnd(widths.index), "STATE".padEnd(widths.state),
|
|
74
|
+
"PID".padEnd(widths.pid), "BG".padEnd(widths.background),
|
|
75
|
+
"ID".padEnd(widths.id), "PARENT".padEnd(widths.parent), "COMMAND"];
|
|
76
|
+
console.log(header.join(" "));
|
|
77
|
+
console.log("─".repeat(Math.min(available, fixedWidth + commandWidth)));
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
console.log([
|
|
80
|
+
String(row.index).padEnd(widths.index),
|
|
81
|
+
String(row.state).padEnd(widths.state),
|
|
82
|
+
String(row.pid).padEnd(widths.pid),
|
|
83
|
+
String(row.background).padEnd(widths.background),
|
|
84
|
+
truncateMiddle(String(row.id), widths.id).padEnd(widths.id),
|
|
85
|
+
truncateMiddle(String(row.parent), widths.parent).padEnd(widths.parent),
|
|
86
|
+
truncate(String(row.command).replace(/\s+/g, " "), commandWidth),
|
|
87
|
+
].join(" "));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function printList(options: any): void {
|
|
91
|
+
const rows = filtered(options).map(({ id, status }, index) => ({
|
|
92
|
+
index, id, state: status.state, pid: status.pid || "-", background: status.background ? "yes" : "no",
|
|
93
|
+
parent: status.parentProcessId || "-", command: [status.command, ...status.args].join(" "),
|
|
94
|
+
}));
|
|
95
|
+
if (options.json) console.log(JSON.stringify(rows, null, 2));
|
|
96
|
+
else if (rows.length) printProcessTable(rows);
|
|
97
|
+
else console.log("No managed processes.");
|
|
98
|
+
}
|
|
99
|
+
function addFilters(command: Command): Command {
|
|
100
|
+
return command.option("-a, --all", "include completed processes")
|
|
101
|
+
.option("--parent <id>", "filter by parent process/task id")
|
|
102
|
+
.option("--background", "show only background processes")
|
|
103
|
+
.option("--json", "emit JSON");
|
|
104
|
+
}
|
|
105
|
+
function tailText(file: string, lines: number): string {
|
|
106
|
+
if (!fs.existsSync(file)) return "";
|
|
107
|
+
return fs.readFileSync(file, "utf8").split("\n").slice(-lines - 1).join("\n");
|
|
108
|
+
}
|
|
109
|
+
async function followFiles(files: string[], fromEnd = true): Promise<void> {
|
|
110
|
+
const offsets = new Map<string, number>();
|
|
111
|
+
for (const file of files) offsets.set(file, fromEnd && fs.existsSync(file) ? fs.statSync(file).size : 0);
|
|
112
|
+
await new Promise<void>((resolve) => {
|
|
113
|
+
const poll = setInterval(() => {
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
if (!fs.existsSync(file)) continue;
|
|
116
|
+
const size = fs.statSync(file).size; const offset = offsets.get(file) || 0;
|
|
117
|
+
if (size < offset) offsets.set(file, 0);
|
|
118
|
+
if (size > (offsets.get(file) || 0)) {
|
|
119
|
+
const start = offsets.get(file) || 0;
|
|
120
|
+
const fd = fs.openSync(file, "r"); const data = Buffer.alloc(size - start);
|
|
121
|
+
fs.readSync(fd, data, 0, data.length, start); fs.closeSync(fd);
|
|
122
|
+
process.stdout.write(data); offsets.set(file, size);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}, 150);
|
|
126
|
+
const done = () => { clearInterval(poll); resolve(); };
|
|
127
|
+
process.once("SIGINT", done); process.once("SIGTERM", done);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function addProcessesCommand(program: Command): void {
|
|
132
|
+
const processes = addFilters(program.command("processes").description("inspect and control managed processes"));
|
|
133
|
+
processes.action((rawOptions, command) => printList(commandOptions(rawOptions, command)));
|
|
134
|
+
addFilters(processes.command("list").description("list managed processes"))
|
|
135
|
+
.action((rawOptions, command) => printList(commandOptions(rawOptions, command)));
|
|
136
|
+
|
|
137
|
+
processes.command("run").description("internal managed process shim").requiredOption("--config <path>")
|
|
138
|
+
.action(async ({ config }) => runManagedProcessShim(path.resolve(config)));
|
|
139
|
+
|
|
140
|
+
// Avoid Commander's built-in help exit here. The optional tracing module
|
|
141
|
+
// flushes asynchronously on process.exit, while Commander expects it never
|
|
142
|
+
// to return, which can otherwise produce a spurious "unknown option" error.
|
|
143
|
+
addFilters(processes.command("logs [id]").alias("tail"))
|
|
144
|
+
.helpOption(false)
|
|
145
|
+
.option("-h, --help", "display help for command")
|
|
146
|
+
.option("-i, --index <number>", "select by the zero-based index shown by processes list")
|
|
147
|
+
.option("-n, --lines <number>", "lines to show", "100")
|
|
148
|
+
.option("-f, --follow", "follow output").option("--stderr", "show stderr instead of stdout")
|
|
149
|
+
.option("--both", "show stdout and stderr").action(async (id, rawOptions, command) => {
|
|
150
|
+
const options = commandOptions(rawOptions, command);
|
|
151
|
+
if (options.help) { command.outputHelp(); return; }
|
|
152
|
+
const managed = resolveProcess(id, options.index, options);
|
|
153
|
+
const files = options.both ? [managed.stdoutPath, managed.stderrPath] : [options.stderr ? managed.stderrPath : managed.stdoutPath];
|
|
154
|
+
for (const file of files) process.stdout.write(tailText(file, Math.max(0, Number(options.lines) || 100)));
|
|
155
|
+
if (options.follow) await followFiles(files);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
processes.command("send <id> [input...]").option("-n, --no-newline", "do not append newline")
|
|
159
|
+
.action(async (id, input: string[], rawOptions, command) => {
|
|
160
|
+
const options = commandOptions(rawOptions, command);
|
|
161
|
+
const managed = resolveProcess(id);
|
|
162
|
+
await managed.write((input || []).join(" ") + (options.newline ? "\n" : ""));
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
processes.command("signal <id> [signal]").action((id, signal = "SIGTERM") => resolveProcess(id).signal(signal.toUpperCase() as NodeJS.Signals));
|
|
166
|
+
processes.command("stop <id>").action((id) => resolveProcess(id).signal("SIGTERM"));
|
|
167
|
+
processes.command("kill <id>").action((id) => resolveProcess(id).signal("SIGKILL"));
|
|
168
|
+
|
|
169
|
+
processes.command("attach <id>").description("attach terminal (Ctrl-] to detach)").action(async (id) => {
|
|
170
|
+
const managed = resolveProcess(id); let detached = false;
|
|
171
|
+
const raw = !!process.stdin.isTTY; if (raw) process.stdin.setRawMode(true);
|
|
172
|
+
process.stdin.resume();
|
|
173
|
+
const onData = async (data: Buffer) => {
|
|
174
|
+
if (data.includes(0x1d)) { detached = true; return; }
|
|
175
|
+
try { await managed.write(data.toString()); } catch (error) { console.error(String(error)); detached = true; }
|
|
176
|
+
};
|
|
177
|
+
process.stdin.on("data", onData);
|
|
178
|
+
const offsets = [managed.stdoutPath, managed.stderrPath].map((f) => fs.existsSync(f) ? fs.statSync(f).size : 0);
|
|
179
|
+
await new Promise<void>((resolve) => {
|
|
180
|
+
const timer = setInterval(() => {
|
|
181
|
+
[managed.stdoutPath, managed.stderrPath].forEach((file, i) => {
|
|
182
|
+
if (!fs.existsSync(file)) return; const size = fs.statSync(file).size;
|
|
183
|
+
if (size > offsets[i]) { const fd = fs.openSync(file, "r"); const b = Buffer.alloc(size - offsets[i]); fs.readSync(fd, b, 0, b.length, offsets[i]); fs.closeSync(fd); process.stdout.write(b); offsets[i] = size; }
|
|
184
|
+
});
|
|
185
|
+
if (detached || terminal.has(managed.status.state)) { clearInterval(timer); resolve(); }
|
|
186
|
+
}, 100);
|
|
187
|
+
});
|
|
188
|
+
process.stdin.off("data", onData); process.stdin.pause(); if (raw) process.stdin.setRawMode(false);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
processes.command("tree").option("-a, --all", "include completed processes").action((rawOptions, command) => {
|
|
192
|
+
const options = commandOptions(rawOptions, command);
|
|
193
|
+
const items = listManagedProcesses().filter((p) => options.all || !terminal.has(p.status.state));
|
|
194
|
+
const ids = new Set(items.map((p) => p.id));
|
|
195
|
+
const children = new Map<string | null, ManagedProcessInfo[]>();
|
|
196
|
+
for (const item of items) { const parent = item.status.parentProcessId && ids.has(item.status.parentProcessId) ? item.status.parentProcessId : null; children.set(parent, [...(children.get(parent) || []), item]); }
|
|
197
|
+
const render = (parent: string | null, prefix: string) => (children.get(parent) || []).forEach((item, index, array) => {
|
|
198
|
+
const last = index === array.length - 1; console.log(`${prefix}${last ? "└─" : "├─"}${item.id} [${item.status.state}] ${item.status.command}`);
|
|
199
|
+
render(item.id, prefix + (last ? " " : "│ "));
|
|
200
|
+
});
|
|
201
|
+
render(null, "");
|
|
202
|
+
});
|
|
203
|
+
}
|
package/src/commands/services.ts
CHANGED
|
@@ -3,7 +3,8 @@ import * as allTools from "../agents/tools";
|
|
|
3
3
|
import { LazyToolsService, services, MinimalToolsService, TracingService } from "../services";
|
|
4
4
|
import { agents } from "../agents";
|
|
5
5
|
import { ModulesService } from "../services/modules";
|
|
6
|
-
import { getConfig } from "../config";
|
|
6
|
+
import { getConfig, getConfigSync } from "../config";
|
|
7
|
+
import { authenticateWithKey } from "../auth/keyAuth";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Shared service setup used by commands that need full services (chat, agent, worker, etc.)
|
|
@@ -20,6 +21,7 @@ export async function setupServices() {
|
|
|
20
21
|
MediaProcessor,
|
|
21
22
|
Behaviors,
|
|
22
23
|
RuntimeReload,
|
|
24
|
+
Extensions,
|
|
23
25
|
} = services();
|
|
24
26
|
|
|
25
27
|
await Plugins.refreshConfiguredState();
|
|
@@ -76,6 +78,25 @@ export async function setupServices() {
|
|
|
76
78
|
`⚠ Some MCP servers failed to connect (continuing without them): ${msg}`
|
|
77
79
|
);
|
|
78
80
|
}
|
|
81
|
+
// Attempt silent JWT refresh via SSH identity before registering models.
|
|
82
|
+
// This handles the case where the JWT has expired but the user has a valid
|
|
83
|
+
// SSH identity registered — they shouldn't need to run `knowhow login` again
|
|
84
|
+
// unless they want to switch organizations.
|
|
85
|
+
try {
|
|
86
|
+
const startupConfig = getConfigSync();
|
|
87
|
+
if (startupConfig.orgId && !process.env.KNOWHOW_JWT) {
|
|
88
|
+
await authenticateWithKey(
|
|
89
|
+
startupConfig.orgId,
|
|
90
|
+
process.env.KNOWHOW_API_URL || "https://api.knowhow.tyvm.ai",
|
|
91
|
+
startupConfig.cliIdentityPath
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
} catch (_err) {
|
|
95
|
+
// Non-fatal: if SSH refresh fails (no key, revoked, network issue), we
|
|
96
|
+
// continue with the existing JWT (or no JWT). The user will see model
|
|
97
|
+
// warnings if no valid JWT is available.
|
|
98
|
+
}
|
|
99
|
+
|
|
79
100
|
console.log("Connecting to clients...");
|
|
80
101
|
await Clients.registerConfiguredModels();
|
|
81
102
|
console.log("✓ Services are set up and ready to go!");
|
|
@@ -92,6 +113,7 @@ export async function setupServices() {
|
|
|
92
113
|
Behaviors,
|
|
93
114
|
Events,
|
|
94
115
|
Tracing: TracingService,
|
|
116
|
+
Extensions,
|
|
95
117
|
};
|
|
96
118
|
|
|
97
119
|
// Call destroy() on all modules when the process is shutting down so they
|
package/src/index.ts
CHANGED
|
@@ -65,6 +65,7 @@ export * as embeddings from "./embeddings";
|
|
|
65
65
|
export * as types from "./types";
|
|
66
66
|
export * as processors from "./processors";
|
|
67
67
|
export * as ai from "./ai";
|
|
68
|
+
export * from "./processes/ProcessManager";
|
|
68
69
|
|
|
69
70
|
// Export module system types for external modules
|
|
70
71
|
export * from "./services/modules/types";
|