rackmind-cli 0.3.0 → 0.4.0
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/dist/ai/providers/anthropic-provider.d.ts +9 -2
- package/dist/ai/providers/anthropic-provider.d.ts.map +1 -1
- package/dist/ai/providers/anthropic-provider.js +42 -9
- package/dist/ai/providers/anthropic-provider.js.map +1 -1
- package/dist/ai/providers/base.d.ts +6 -2
- package/dist/ai/providers/base.d.ts.map +1 -1
- package/dist/ai/tool-executor.d.ts +11 -13
- package/dist/ai/tool-executor.d.ts.map +1 -1
- package/dist/ai/tool-executor.js +183 -27
- package/dist/ai/tool-executor.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +8 -6
- package/dist/commands/logs.js.map +1 -1
- package/dist/config/types.d.ts +5 -5
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +5 -1
- package/dist/config/types.js.map +1 -1
- package/dist/interactive/App.d.ts.map +1 -1
- package/dist/interactive/App.js +13 -4
- package/dist/interactive/App.js.map +1 -1
- package/dist/interactive/useRackMind.d.ts +37 -0
- package/dist/interactive/useRackMind.d.ts.map +1 -1
- package/dist/interactive/useRackMind.js +162 -24
- package/dist/interactive/useRackMind.js.map +1 -1
- package/dist/utils/command-safety.d.ts.map +1 -1
- package/dist/utils/command-safety.js +14 -0
- package/dist/utils/command-safety.js.map +1 -1
- package/dist/utils/logger.d.ts +21 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +80 -0
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ServerClient } from '../../server/client.js';
|
|
2
2
|
import type { AIProvider, ChatHistory, MultiTurnCallbacks, ProviderOptions, StreamCallbacks } from './base.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns true for browser AbortError and the Anthropic SDK's
|
|
5
|
+
* APIUserAbortError so callers can treat them uniformly.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isAbortError(err: unknown): boolean;
|
|
3
8
|
export declare class AnthropicProvider implements AIProvider {
|
|
4
9
|
private readonly anthropic;
|
|
5
10
|
private readonly model;
|
|
@@ -10,12 +15,14 @@ export declare class AnthropicProvider implements AIProvider {
|
|
|
10
15
|
getModel(): string;
|
|
11
16
|
/** Clear cached server context so next turn re-fetches from Proxmox API. */
|
|
12
17
|
invalidateContextCache(): void;
|
|
13
|
-
chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks): Promise<void>;
|
|
14
|
-
chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks): Promise<ChatHistory>;
|
|
18
|
+
chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<void>;
|
|
19
|
+
chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks, signal?: AbortSignal): Promise<ChatHistory>;
|
|
15
20
|
dispose(): Promise<void>;
|
|
16
21
|
/**
|
|
17
22
|
* Create a message with retry logic for transient errors.
|
|
18
23
|
* Rate limits and overload errors trigger exponential backoff.
|
|
24
|
+
* The optional AbortSignal is forwarded to the SDK so the in-flight HTTP
|
|
25
|
+
* request can be cancelled immediately when the user hits Ctrl+C.
|
|
19
26
|
*/
|
|
20
27
|
private createWithRetry;
|
|
21
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/anthropic-provider.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAO3D,OAAO,KAAK,EACR,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EAClB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/anthropic-provider.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAO3D,OAAO,KAAK,EACR,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EAClB,MAAM,WAAW,CAAC;AASnB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAElD;AAYD,qBAAa,iBAAkB,YAAW,UAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAA8B;gBAEvC,OAAO,EAAE,eAAe;IAYpC,QAAQ,IAAI,MAAM;IAIlB,4EAA4E;IAC5E,sBAAsB,IAAI,IAAI;IAKxB,IAAI,CACN,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,IAAI,CAAC;IA0EV,aAAa,CACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,kBAAkB,EAC7B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC;IAyFjB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAoC1B"}
|
|
@@ -14,6 +14,25 @@ import { withRetry, isTransientError, isRateLimitError } from '../../utils/retry
|
|
|
14
14
|
import { DEFAULT_ANTHROPIC_MODEL_ID } from '../models.js';
|
|
15
15
|
/** Max iterations of the tool use loop — hard guard against runaway chains. */
|
|
16
16
|
const MAX_TOOL_ITERATIONS = 20;
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Abort helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Returns true for browser AbortError and the Anthropic SDK's
|
|
22
|
+
* APIUserAbortError so callers can treat them uniformly.
|
|
23
|
+
*/
|
|
24
|
+
export function isAbortError(err) {
|
|
25
|
+
return err instanceof Error && (err.name === 'AbortError' || err.name === 'APIUserAbortError');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Throw an AbortError. Centralised so callers in the tool loop all use
|
|
29
|
+
* the same error shape, matching what the Anthropic SDK itself throws.
|
|
30
|
+
*/
|
|
31
|
+
function throwAbortError() {
|
|
32
|
+
const err = new Error('The operation was aborted.');
|
|
33
|
+
err.name = 'AbortError';
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
17
36
|
export class AnthropicProvider {
|
|
18
37
|
anthropic;
|
|
19
38
|
model;
|
|
@@ -39,7 +58,7 @@ export class AnthropicProvider {
|
|
|
39
58
|
this.cachedContext = null;
|
|
40
59
|
logger.info('Server context cache cleared — will re-fetch on next turn');
|
|
41
60
|
}
|
|
42
|
-
async chat(query, serverClient, serverAlias, callbacks) {
|
|
61
|
+
async chat(query, serverClient, serverAlias, callbacks, signal) {
|
|
43
62
|
if (!this.cachedContext) {
|
|
44
63
|
this.cachedContext = await fetchServerContext(serverClient);
|
|
45
64
|
}
|
|
@@ -48,8 +67,11 @@ export class AnthropicProvider {
|
|
|
48
67
|
const tools = getToolDefinitions();
|
|
49
68
|
const messages = [{ role: 'user', content: query }];
|
|
50
69
|
for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
|
|
70
|
+
if (signal?.aborted) {
|
|
71
|
+
throwAbortError();
|
|
72
|
+
}
|
|
51
73
|
logger.debug('AI request iteration', { iteration, messageCount: messages.length });
|
|
52
|
-
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks);
|
|
74
|
+
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks, signal);
|
|
53
75
|
const assistantContent = [];
|
|
54
76
|
const toolUseBlocks = [];
|
|
55
77
|
for (const block of response.content) {
|
|
@@ -71,9 +93,12 @@ export class AnthropicProvider {
|
|
|
71
93
|
}
|
|
72
94
|
const toolResults = [];
|
|
73
95
|
for (const toolBlock of toolUseBlocks) {
|
|
96
|
+
if (signal?.aborted) {
|
|
97
|
+
throwAbortError();
|
|
98
|
+
}
|
|
74
99
|
const toolInput = toolBlock.input;
|
|
75
100
|
callbacks.onToolStart(toolBlock.name, toolBlock.name);
|
|
76
|
-
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive });
|
|
101
|
+
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive, signal });
|
|
77
102
|
callbacks.onToolEnd(toolBlock.name, result.label, result.isError);
|
|
78
103
|
toolResults.push({
|
|
79
104
|
type: 'tool_result',
|
|
@@ -86,7 +111,7 @@ export class AnthropicProvider {
|
|
|
86
111
|
}
|
|
87
112
|
callbacks.onError('Reached maximum tool iterations. The AI may be stuck in a loop.');
|
|
88
113
|
}
|
|
89
|
-
async chatMultiTurn(query, history, serverClient, serverAlias, callbacks) {
|
|
114
|
+
async chatMultiTurn(query, history, serverClient, serverAlias, callbacks, signal) {
|
|
90
115
|
if (!this.cachedContext) {
|
|
91
116
|
this.cachedContext = await fetchServerContext(serverClient);
|
|
92
117
|
}
|
|
@@ -98,11 +123,14 @@ export class AnthropicProvider {
|
|
|
98
123
|
{ role: 'user', content: query },
|
|
99
124
|
];
|
|
100
125
|
for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
|
|
126
|
+
if (signal?.aborted) {
|
|
127
|
+
throwAbortError();
|
|
128
|
+
}
|
|
101
129
|
logger.debug('AI multi-turn request iteration', {
|
|
102
130
|
iteration,
|
|
103
131
|
messageCount: messages.length,
|
|
104
132
|
});
|
|
105
|
-
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks);
|
|
133
|
+
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks, signal);
|
|
106
134
|
const assistantContent = [];
|
|
107
135
|
const toolUseBlocks = [];
|
|
108
136
|
for (const block of response.content) {
|
|
@@ -124,9 +152,12 @@ export class AnthropicProvider {
|
|
|
124
152
|
}
|
|
125
153
|
const toolResults = [];
|
|
126
154
|
for (const toolBlock of toolUseBlocks) {
|
|
155
|
+
if (signal?.aborted) {
|
|
156
|
+
throwAbortError();
|
|
157
|
+
}
|
|
127
158
|
const toolInput = toolBlock.input;
|
|
128
159
|
callbacks.onToolStart(toolBlock.name, toolBlock.name);
|
|
129
|
-
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive });
|
|
160
|
+
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive, signal });
|
|
130
161
|
callbacks.onToolEnd(toolBlock.name, result.label, result.isError);
|
|
131
162
|
if (callbacks.onToolResult) {
|
|
132
163
|
callbacks.onToolResult({
|
|
@@ -154,19 +185,21 @@ export class AnthropicProvider {
|
|
|
154
185
|
/**
|
|
155
186
|
* Create a message with retry logic for transient errors.
|
|
156
187
|
* Rate limits and overload errors trigger exponential backoff.
|
|
188
|
+
* The optional AbortSignal is forwarded to the SDK so the in-flight HTTP
|
|
189
|
+
* request can be cancelled immediately when the user hits Ctrl+C.
|
|
157
190
|
*/
|
|
158
|
-
createWithRetry(system, tools, messages, callbacks) {
|
|
191
|
+
createWithRetry(system, tools, messages, callbacks, signal) {
|
|
159
192
|
return withRetry(() => this.anthropic.messages.create({
|
|
160
193
|
model: this.model,
|
|
161
194
|
max_tokens: this.maxTokens,
|
|
162
195
|
system,
|
|
163
196
|
tools,
|
|
164
197
|
messages,
|
|
165
|
-
}), {
|
|
198
|
+
}, { signal }), {
|
|
166
199
|
maxAttempts: 3,
|
|
167
200
|
initialDelayMs: 2000,
|
|
168
201
|
maxDelayMs: 30_000,
|
|
169
|
-
isRetryable: isTransientError,
|
|
202
|
+
isRetryable: (err) => !isAbortError(err) && isTransientError(err),
|
|
170
203
|
onRetry: (attempt, error, delayMs) => {
|
|
171
204
|
const msg = error instanceof Error ? error.message : String(error);
|
|
172
205
|
const isRateLimit = isRateLimitError(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../../../src/ai/providers/anthropic-provider.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAC9E,4EAA4E;AAC5E,gEAAgE;AAChE,uCAAuC;AACvC,8EAA8E;AAE9E,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAsB,MAAM,qBAAqB,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAS1D,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,MAAM,OAAO,iBAAiB;IACT,SAAS,CAAY;IACrB,KAAK,CAAS;IACd,SAAS,CAAS;IACnC,4EAA4E;IACpE,aAAa,GAAyB,IAAI,CAAC;IAEnD,YAAY,OAAwB;QAChC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC;YAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,0BAA0B,CAAC;QACzD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC/C,CAAC;IAED,QAAQ;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,4EAA4E;IAC5E,sBAAsB;QAClB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,IAAI,CACN,KAAa,EACb,YAA0B,EAC1B,WAAmB,EACnB,SAA0B;
|
|
1
|
+
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../../../src/ai/providers/anthropic-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,13 +49,17 @@ export interface AIProvider {
|
|
|
49
49
|
* Send a single query with no prior history. Streams results via
|
|
50
50
|
* callbacks. Throws on fatal errors.
|
|
51
51
|
*/
|
|
52
|
-
chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks): Promise<void>;
|
|
52
|
+
chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<void>;
|
|
53
53
|
/**
|
|
54
54
|
* Send a new user message given the prior conversation history.
|
|
55
55
|
* Returns the updated history (with the new user turn and the
|
|
56
56
|
* assistant's response appended).
|
|
57
|
+
*
|
|
58
|
+
* When `signal` is aborted mid-turn the call rejects with an AbortError
|
|
59
|
+
* (or the SDK's APIUserAbortError). The caller is responsible for not
|
|
60
|
+
* persisting any partial history returned before the abort.
|
|
57
61
|
*/
|
|
58
|
-
chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks): Promise<ChatHistory>;
|
|
62
|
+
chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks, signal?: AbortSignal): Promise<ChatHistory>;
|
|
59
63
|
/**
|
|
60
64
|
* Release any resources this provider holds (HTTP bridge, child
|
|
61
65
|
* processes, etc.). Safe to call multiple times.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/base.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE1F,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC5B,+CAA+C;IAC/C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,+CAA+C;IAC/C,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvE,uEAAuE;IACvE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;CACrD;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACvB,+DAA+D;IAC/D,QAAQ,IAAI,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,CACA,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/base.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE1F,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC5B,+CAA+C;IAC/C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,oDAAoD;IACpD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,+CAA+C;IAC/C,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvE,uEAAuE;IACvE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;CACrD;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACvB,+DAA+D;IAC/D,QAAQ,IAAI,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,CACA,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;OAQG;IACH,aAAa,CACT,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,kBAAkB,EAC7B,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB;;;;OAIG;IACH,sBAAsB,CAAC,IAAI,IAAI,CAAC;CACnC;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC5B,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import type { ServerClient } from '../server/client.js';
|
|
2
3
|
/**
|
|
3
4
|
* Deep-clone `input` and redact any value that either lives under a
|
|
@@ -9,17 +10,7 @@ import type { ServerClient } from '../server/client.js';
|
|
|
9
10
|
* the wrapper helpers below.
|
|
10
11
|
*/
|
|
11
12
|
export declare function redactSecrets(input: unknown): unknown;
|
|
12
|
-
|
|
13
|
-
command: string;
|
|
14
|
-
}
|
|
15
|
-
interface ExecuteCommandInContainerInput {
|
|
16
|
-
vmid: number;
|
|
17
|
-
command: string;
|
|
18
|
-
}
|
|
19
|
-
interface VmidInput {
|
|
20
|
-
vmid: number;
|
|
21
|
-
}
|
|
22
|
-
type ToolInput = ExecuteCommandInput | ExecuteCommandInContainerInput | VmidInput | Record<string, never>;
|
|
13
|
+
export declare const _toolSchemas: Map<string, z.ZodTypeAny>;
|
|
23
14
|
export interface ToolResult {
|
|
24
15
|
/** The string content to send back to Claude */
|
|
25
16
|
content: string;
|
|
@@ -55,11 +46,18 @@ export interface ExecuteToolOptions {
|
|
|
55
46
|
* should always supply this.
|
|
56
47
|
*/
|
|
57
48
|
confirm?: ConfirmDestructiveFn;
|
|
49
|
+
/**
|
|
50
|
+
* AbortSignal for the current turn. When already aborted at call time,
|
|
51
|
+
* the tool short-circuits and returns an aborted result without calling
|
|
52
|
+
* the underlying handler. Handlers themselves do not receive the signal —
|
|
53
|
+
* they return quickly enough that mid-execution cancellation is handled at
|
|
54
|
+
* the loop level in the provider.
|
|
55
|
+
*/
|
|
56
|
+
signal?: AbortSignal;
|
|
58
57
|
}
|
|
59
58
|
/**
|
|
60
59
|
* Execute a tool call and return the result.
|
|
61
60
|
* Catches all errors and returns them as error results rather than throwing.
|
|
62
61
|
*/
|
|
63
|
-
export declare function executeTool(toolName: string, input:
|
|
64
|
-
export {};
|
|
62
|
+
export declare function executeTool(toolName: string, input: Record<string, unknown>, client: ServerClient, options?: ExecuteToolOptions): Promise<ToolResult>;
|
|
65
63
|
//# sourceMappingURL=tool-executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-executor.d.ts","sourceRoot":"","sources":["../../src/ai/tool-executor.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-executor.d.ts","sourceRoot":"","sources":["../../src/ai/tool-executor.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAmFxD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAErD;AAyFD,eAAO,MAAM,YAAY,2BAcvB,CAAC;AASH,MAAM,WAAW,UAAU;IACvB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACjB;AAMD;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,wBAAwB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAwB1F,yDAAyD;AACzD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3D;AAmCD,MAAM,WAAW,kBAAkB;IAC/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAMD;;;GAGG;AACH,wBAAsB,WAAW,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,UAAU,CAAC,CA4KrB"}
|
package/dist/ai/tool-executor.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// (e.g. in tests) destructive tools execute without prompting — callers
|
|
12
12
|
// are responsible for plumbing the gate through.
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
+
import { z } from 'zod';
|
|
14
15
|
import { ProxmoxApiError } from '../server/proxmox.js';
|
|
15
16
|
import { logger } from '../utils/logger.js';
|
|
16
17
|
import { matchDangerousPattern } from '../utils/command-safety.js';
|
|
@@ -32,12 +33,59 @@ import { matchDangerousPattern } from '../utils/command-safety.js';
|
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
33
34
|
const SECRET_KEY_PATTERN = /api[_-]?key|token|password|secret|credential|authorization|bearer/i;
|
|
34
35
|
/**
|
|
35
|
-
* Heuristic: a value "looks like a secret"
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Heuristic: returns true if a standalone string value "looks like a secret"
|
|
37
|
+
* (API key, JWT, session token, etc.).
|
|
38
|
+
*
|
|
39
|
+
* All five conditions must hold:
|
|
40
|
+
* 1. Length ≥ 33 — keeps SHA-1 (40 chars) and most tokens in scope.
|
|
41
|
+
* 2. Contains at least one digit.
|
|
42
|
+
* 3. Contains at least one uppercase letter.
|
|
43
|
+
* 4. Contains at least one lowercase letter.
|
|
44
|
+
* 5. Contains NO forward-slash (`/`).
|
|
45
|
+
*
|
|
46
|
+
* Real keys satisfy all five: `sk-ant-…`, JWTs (base64url uses `-`/`_`, not
|
|
47
|
+
* `/`), AWS access keys, GitHub PATs. Absolute paths fail condition 5.
|
|
48
|
+
*
|
|
49
|
+
* Trade-off: pure-lowercase tokens (git commit SHAs, some hex digests) are no
|
|
50
|
+
* longer redacted — they fail condition 3. We prefer high precision (no false
|
|
51
|
+
* positives on file paths and hashes auditors need to read) over perfect
|
|
52
|
+
* recall on the rare pure-lowercase token.
|
|
53
|
+
*/
|
|
54
|
+
function looksLikeSecret(value) {
|
|
55
|
+
return (value.length >= 33 &&
|
|
56
|
+
/[0-9]/.test(value) &&
|
|
57
|
+
/[A-Z]/.test(value) &&
|
|
58
|
+
/[a-z]/.test(value) &&
|
|
59
|
+
!value.includes('/'));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Inline-secret patterns — scan an arbitrary string value for credential
|
|
63
|
+
* fragments embedded in commands or HTTP headers. Applied to EVERY string
|
|
64
|
+
* value before the whole-string heuristic.
|
|
65
|
+
*
|
|
66
|
+
* The simpler `[REDACTED]` (no length) is used here because the surrounding
|
|
67
|
+
* command/header context already identifies what was redacted.
|
|
39
68
|
*/
|
|
40
|
-
const
|
|
69
|
+
const INLINE_SECRET_PATTERNS = [
|
|
70
|
+
// shell: password <value>
|
|
71
|
+
[/(?<=\bpassword\s+)\S+/gi, '[REDACTED]'],
|
|
72
|
+
// shell: --password=<value> or --password <value>
|
|
73
|
+
[/(?<=--password[=\s]+)\S+/gi, '[REDACTED]'],
|
|
74
|
+
// HTTP: Bearer <token>
|
|
75
|
+
[/(?<=\bBearer\s+)\S+/gi, '[REDACTED]'],
|
|
76
|
+
// HTTP: Authorization: <scheme> <credential> (full header value)
|
|
77
|
+
[/(?<=^Authorization:\s+).+/gim, '[REDACTED]'],
|
|
78
|
+
// CLI flags
|
|
79
|
+
[/(?<=--token[=\s]+)\S+/gi, '[REDACTED]'],
|
|
80
|
+
[/(?<=--api-key[=\s]+)\S+/gi, '[REDACTED]'],
|
|
81
|
+
];
|
|
82
|
+
function redactInlineSecrets(value) {
|
|
83
|
+
let result = value;
|
|
84
|
+
for (const [pattern, replacement] of INLINE_SECRET_PATTERNS) {
|
|
85
|
+
result = result.replace(pattern, replacement);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
41
89
|
/**
|
|
42
90
|
* Deep-clone `input` and redact any value that either lives under a
|
|
43
91
|
* secret-shaped key (api_key, token, password, …) or whose value matches
|
|
@@ -63,10 +111,15 @@ function redactInternal(value, parentKeyIsSecret, seen) {
|
|
|
63
111
|
if (typeof value === 'string') {
|
|
64
112
|
if (parentKeyIsSecret)
|
|
65
113
|
return '[REDACTED]';
|
|
66
|
-
|
|
114
|
+
// Apply inline patterns first — catches credentials embedded in
|
|
115
|
+
// command strings (e.g. `mysqladmin ... password hunter2`).
|
|
116
|
+
const afterInline = redactInlineSecrets(value);
|
|
117
|
+
// Whole-string heuristic backstop — catches opaque tokens not under
|
|
118
|
+
// a secret-shaped key and not caught by inline patterns.
|
|
119
|
+
if (looksLikeSecret(afterInline)) {
|
|
67
120
|
return `[REDACTED:length=${value.length}]`;
|
|
68
121
|
}
|
|
69
|
-
return
|
|
122
|
+
return afterInline;
|
|
70
123
|
}
|
|
71
124
|
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
72
125
|
return value;
|
|
@@ -85,14 +138,46 @@ function redactInternal(value, parentKeyIsSecret, seen) {
|
|
|
85
138
|
// Functions / symbols — never log them at all.
|
|
86
139
|
return '[UNLOGGABLE]';
|
|
87
140
|
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Zod schemas — authoritative runtime validation at the tool boundary
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// These mirror the JSON Schema input_schema fields in tools.ts. Every tool
|
|
145
|
+
// call is validated here before dispatch. On failure we return a tool-error
|
|
146
|
+
// so the AI can self-correct; we never throw into the AI loop.
|
|
147
|
+
//
|
|
148
|
+
// Exported as _toolSchemas (underscore = test-only) so unit tests can
|
|
149
|
+
// temporarily substitute schemas (e.g. to test .default() propagation).
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
const _ExecuteCommandSchema = z.object({ command: z.string() });
|
|
152
|
+
const _CommandInContainerSchema = z.object({ vmid: z.number(), command: z.string() });
|
|
153
|
+
const _VmidSchema = z.object({ vmid: z.number() });
|
|
154
|
+
const _EmptySchema = z.object({});
|
|
155
|
+
export const _toolSchemas = new Map([
|
|
156
|
+
['execute_command', _ExecuteCommandSchema],
|
|
157
|
+
['execute_command_in_container', _CommandInContainerSchema],
|
|
158
|
+
['get_container_status', _VmidSchema],
|
|
159
|
+
['get_vm_status', _VmidSchema],
|
|
160
|
+
['get_node_status', _EmptySchema],
|
|
161
|
+
['list_containers', _EmptySchema],
|
|
162
|
+
['list_vms', _EmptySchema],
|
|
163
|
+
['start_container', _VmidSchema],
|
|
164
|
+
['stop_container', _VmidSchema],
|
|
165
|
+
['restart_container', _VmidSchema],
|
|
166
|
+
['start_vm', _VmidSchema],
|
|
167
|
+
['stop_vm', _VmidSchema],
|
|
168
|
+
['restart_vm', _VmidSchema],
|
|
169
|
+
]);
|
|
170
|
+
/** Tool names for which we have already emitted the "no schema" warning (once per session). */
|
|
171
|
+
const _noSchemaWarned = new Set();
|
|
88
172
|
/**
|
|
89
|
-
* Tool names that mutate server state
|
|
90
|
-
* through the confirmation gate before executing.
|
|
173
|
+
* Tool names that mutate server state or execute arbitrary shell commands.
|
|
174
|
+
* Anything in this set MUST go through the confirmation gate before executing.
|
|
91
175
|
*
|
|
92
|
-
* `execute_command*` are
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
176
|
+
* Shell-execution tools (`execute_command*`) are gated here AND additionally
|
|
177
|
+
* pre-screened by [matchDangerousPattern] before the gate is reached — so a
|
|
178
|
+
* blocklisted command is refused outright without prompting the user to confirm.
|
|
179
|
+
* This closes the shell-escape bypass class: a user can never be coaxed into
|
|
180
|
+
* approving a regex-evading variant of a known-dangerous command.
|
|
96
181
|
*/
|
|
97
182
|
const DESTRUCTIVE_TOOLS = new Set([
|
|
98
183
|
'start_container',
|
|
@@ -101,6 +186,9 @@ const DESTRUCTIVE_TOOLS = new Set([
|
|
|
101
186
|
'start_vm',
|
|
102
187
|
'stop_vm',
|
|
103
188
|
'restart_vm',
|
|
189
|
+
// Shell execution: every command requires explicit user confirmation.
|
|
190
|
+
'execute_command',
|
|
191
|
+
'execute_command_in_container',
|
|
104
192
|
]);
|
|
105
193
|
/** Returns true if the tool will mutate server state. */
|
|
106
194
|
export function isDestructiveTool(toolName) {
|
|
@@ -122,6 +210,14 @@ function describeDestructiveAction(toolName, input) {
|
|
|
122
210
|
return `Stop VM ${vmid}`;
|
|
123
211
|
case 'restart_vm':
|
|
124
212
|
return `Restart VM ${vmid}`;
|
|
213
|
+
case 'execute_command': {
|
|
214
|
+
const cmd = input.command;
|
|
215
|
+
return `Execute on host: ${cmd}`;
|
|
216
|
+
}
|
|
217
|
+
case 'execute_command_in_container': {
|
|
218
|
+
const ci = input;
|
|
219
|
+
return `Execute in container ${ci.vmid}: ${ci.command}`;
|
|
220
|
+
}
|
|
125
221
|
default:
|
|
126
222
|
return toolName;
|
|
127
223
|
}
|
|
@@ -134,14 +230,74 @@ function describeDestructiveAction(toolName, input) {
|
|
|
134
230
|
* Catches all errors and returns them as error results rather than throwing.
|
|
135
231
|
*/
|
|
136
232
|
export async function executeTool(toolName, input, client, options = {}) {
|
|
233
|
+
// Short-circuit immediately if the caller's turn has already been aborted.
|
|
234
|
+
if (options.signal?.aborted) {
|
|
235
|
+
logger.info('Tool short-circuited: signal already aborted', { toolName });
|
|
236
|
+
return {
|
|
237
|
+
content: 'Tool execution aborted.',
|
|
238
|
+
isError: false,
|
|
239
|
+
label: `${toolName} (aborted)`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
137
242
|
logger.info('Executing tool', { toolName, input: redactSecrets(input) });
|
|
138
|
-
//
|
|
243
|
+
// Validate input against the declared Zod schema before touching any values.
|
|
244
|
+
// On failure, return a tool-error so the AI can self-correct without crashing
|
|
245
|
+
// the loop. On success, use result.data (with coercions and defaults applied).
|
|
246
|
+
const schema = _toolSchemas.get(toolName);
|
|
247
|
+
let parsedInput;
|
|
248
|
+
if (schema !== undefined) {
|
|
249
|
+
const result = schema.safeParse(input);
|
|
250
|
+
if (!result.success) {
|
|
251
|
+
const issues = result.error.issues
|
|
252
|
+
.map((iss) => `${iss.path.length ? iss.path.join('.') : '(root)'}: ${iss.message}`)
|
|
253
|
+
.join('; ');
|
|
254
|
+
logger.warn('Tool input validation failed', { toolName, issues });
|
|
255
|
+
return {
|
|
256
|
+
content: `Invalid input for tool "${toolName}": ${issues}. ` +
|
|
257
|
+
'Please retry with corrected input.',
|
|
258
|
+
isError: true,
|
|
259
|
+
label: `${toolName} (invalid input)`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
parsedInput = result.data;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
if (!_noSchemaWarned.has(toolName)) {
|
|
266
|
+
logger.warn(`[warn] No schema declared for tool "${toolName}" — skipping validation`);
|
|
267
|
+
_noSchemaWarned.add(toolName);
|
|
268
|
+
}
|
|
269
|
+
parsedInput = input;
|
|
270
|
+
}
|
|
271
|
+
// For shell-execution tools: run the blocklist BEFORE the confirmation
|
|
272
|
+
// gate. A blocklisted command is refused outright — the AI must not be
|
|
273
|
+
// able to coax the user into confirming a regex-evading dangerous variant
|
|
274
|
+
// (IFS substitution, $(), base64, backslash-quoting, etc. are all
|
|
275
|
+
// confirmed-command bypass classes that this structural check closes).
|
|
276
|
+
if (toolName === 'execute_command' || toolName === 'execute_command_in_container') {
|
|
277
|
+
const cmd = 'command' in parsedInput ? parsedInput.command : '';
|
|
278
|
+
const dangerMatch = matchDangerousPattern(cmd);
|
|
279
|
+
if (dangerMatch) {
|
|
280
|
+
logger.warn('AI attempted blocklisted command (refused before confirmation gate)', {
|
|
281
|
+
toolName,
|
|
282
|
+
command: cmd,
|
|
283
|
+
pattern: dangerMatch,
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
content: `Command blocked for safety: ${dangerMatch}. This command is not permitted. ` +
|
|
287
|
+
'Please inform the user about the command you want to run and ask them ' +
|
|
288
|
+
'to execute it manually if truly needed.',
|
|
289
|
+
isError: true,
|
|
290
|
+
label: `${toolName}: ${cmd} (blocked)`,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Confirmation gate for destructive tools (lifecycle + shell execution).
|
|
139
295
|
if (isDestructiveTool(toolName)) {
|
|
140
|
-
const label = describeDestructiveAction(toolName,
|
|
296
|
+
const label = describeDestructiveAction(toolName, parsedInput);
|
|
141
297
|
logger.warn('Destructive tool requested', {
|
|
142
298
|
toolName,
|
|
143
299
|
label,
|
|
144
|
-
input: redactSecrets(
|
|
300
|
+
input: redactSecrets(parsedInput),
|
|
145
301
|
hasConfirm: Boolean(options.confirm),
|
|
146
302
|
});
|
|
147
303
|
if (options.confirm) {
|
|
@@ -150,7 +306,7 @@ export async function executeTool(toolName, input, client, options = {}) {
|
|
|
150
306
|
confirmed = await options.confirm({
|
|
151
307
|
tool: toolName,
|
|
152
308
|
label,
|
|
153
|
-
details:
|
|
309
|
+
details: parsedInput,
|
|
154
310
|
});
|
|
155
311
|
}
|
|
156
312
|
catch (err) {
|
|
@@ -175,13 +331,13 @@ export async function executeTool(toolName, input, client, options = {}) {
|
|
|
175
331
|
try {
|
|
176
332
|
switch (toolName) {
|
|
177
333
|
case 'execute_command':
|
|
178
|
-
return await executeCommand(
|
|
334
|
+
return await executeCommand(parsedInput, client);
|
|
179
335
|
case 'execute_command_in_container':
|
|
180
|
-
return await executeCommandInContainer(
|
|
336
|
+
return await executeCommandInContainer(parsedInput, client);
|
|
181
337
|
case 'get_container_status':
|
|
182
|
-
return await getContainerStatus(
|
|
338
|
+
return await getContainerStatus(parsedInput, client);
|
|
183
339
|
case 'get_vm_status':
|
|
184
|
-
return await getVmStatus(
|
|
340
|
+
return await getVmStatus(parsedInput, client);
|
|
185
341
|
case 'get_node_status':
|
|
186
342
|
return await getNodeStatus(client);
|
|
187
343
|
case 'list_containers':
|
|
@@ -189,17 +345,17 @@ export async function executeTool(toolName, input, client, options = {}) {
|
|
|
189
345
|
case 'list_vms':
|
|
190
346
|
return await listVms(client);
|
|
191
347
|
case 'start_container':
|
|
192
|
-
return await startContainer(
|
|
348
|
+
return await startContainer(parsedInput, client);
|
|
193
349
|
case 'stop_container':
|
|
194
|
-
return await stopContainer(
|
|
350
|
+
return await stopContainer(parsedInput, client);
|
|
195
351
|
case 'restart_container':
|
|
196
|
-
return await restartContainer(
|
|
352
|
+
return await restartContainer(parsedInput, client);
|
|
197
353
|
case 'start_vm':
|
|
198
|
-
return await startVm(
|
|
354
|
+
return await startVm(parsedInput, client);
|
|
199
355
|
case 'stop_vm':
|
|
200
|
-
return await stopVm(
|
|
356
|
+
return await stopVm(parsedInput, client);
|
|
201
357
|
case 'restart_vm':
|
|
202
|
-
return await restartVm(
|
|
358
|
+
return await restartVm(parsedInput, client);
|
|
203
359
|
default:
|
|
204
360
|
return {
|
|
205
361
|
content: `Unknown tool: ${toolName}`,
|