rackmind-cli 0.1.5 → 0.2.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/LICENSE +21 -0
- package/README.md +67 -181
- package/bin/rackmind.js +5 -1
- package/dist/ai/models.d.ts +40 -0
- package/dist/ai/models.d.ts.map +1 -0
- package/dist/ai/models.js +90 -0
- package/dist/ai/models.js.map +1 -0
- package/dist/ai/providers/anthropic-provider.d.ts +22 -0
- package/dist/ai/providers/anthropic-provider.d.ts.map +1 -0
- package/dist/ai/{client.js → providers/anthropic-provider.js} +41 -59
- package/dist/ai/providers/anthropic-provider.js.map +1 -0
- package/dist/ai/providers/base.d.ts +82 -0
- package/dist/ai/providers/base.d.ts.map +1 -0
- package/dist/ai/providers/base.js +9 -0
- package/dist/ai/providers/base.js.map +1 -0
- package/dist/ai/providers/claude-code-bridge.d.ts +51 -0
- package/dist/ai/providers/claude-code-bridge.d.ts.map +1 -0
- package/dist/ai/providers/claude-code-bridge.js +371 -0
- package/dist/ai/providers/claude-code-bridge.js.map +1 -0
- package/dist/ai/providers/claude-code-provider.d.ts +24 -0
- package/dist/ai/providers/claude-code-provider.d.ts.map +1 -0
- package/dist/ai/providers/claude-code-provider.js +246 -0
- package/dist/ai/providers/claude-code-provider.js.map +1 -0
- package/dist/ai/providers/claude-code-runner.d.ts +51 -0
- package/dist/ai/providers/claude-code-runner.d.ts.map +1 -0
- package/dist/ai/providers/claude-code-runner.js +325 -0
- package/dist/ai/providers/claude-code-runner.js.map +1 -0
- package/dist/ai/providers/index.d.ts +8 -0
- package/dist/ai/providers/index.d.ts.map +1 -0
- package/dist/ai/providers/index.js +28 -0
- package/dist/ai/providers/index.js.map +1 -0
- package/dist/ai/system-prompt.d.ts.map +1 -1
- package/dist/ai/system-prompt.js +1 -26
- package/dist/ai/system-prompt.js.map +1 -1
- package/dist/ai/tool-executor.d.ts +29 -1
- package/dist/ai/tool-executor.d.ts.map +1 -1
- package/dist/ai/tool-executor.js +85 -1
- package/dist/ai/tool-executor.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +21 -15
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/containers.d.ts.map +1 -1
- package/dist/commands/containers.js +10 -3
- package/dist/commands/containers.js.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +19 -7
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/lifecycle.d.ts.map +1 -1
- package/dist/commands/lifecycle.js +133 -105
- package/dist/commands/lifecycle.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +10 -4
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/report.d.ts.map +1 -1
- package/dist/commands/report.js +20 -12
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +29 -2
- package/dist/commands/servers.js.map +1 -1
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +66 -50
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/vms.d.ts.map +1 -1
- package/dist/commands/vms.js +9 -3
- package/dist/commands/vms.js.map +1 -1
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +21 -0
- package/dist/config/index.js.map +1 -1
- package/dist/globals.d.ts +21 -3
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +30 -4
- package/dist/globals.js.map +1 -1
- package/dist/index.js +22 -6
- package/dist/index.js.map +1 -1
- package/dist/interactive/App.d.ts.map +1 -1
- package/dist/interactive/App.js +11 -5
- package/dist/interactive/App.js.map +1 -1
- package/dist/interactive/components/ConfirmDialog.d.ts +9 -0
- package/dist/interactive/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/interactive/components/ConfirmDialog.js +26 -0
- package/dist/interactive/components/ConfirmDialog.js.map +1 -0
- package/dist/interactive/components/InputBar.d.ts.map +1 -1
- package/dist/interactive/components/InputBar.js +10 -1
- package/dist/interactive/components/InputBar.js.map +1 -1
- package/dist/interactive/components/ToolOutput.d.ts +1 -1
- package/dist/interactive/components/ToolOutput.d.ts.map +1 -1
- package/dist/interactive/launch.d.ts +5 -0
- package/dist/interactive/launch.d.ts.map +1 -1
- package/dist/interactive/launch.js +16 -0
- package/dist/interactive/launch.js.map +1 -1
- package/dist/interactive/slashCommands.d.ts +1 -0
- package/dist/interactive/slashCommands.d.ts.map +1 -1
- package/dist/interactive/slashCommands.js +13 -0
- package/dist/interactive/slashCommands.js.map +1 -1
- package/dist/interactive/useRackMind.d.ts +9 -1
- package/dist/interactive/useRackMind.d.ts.map +1 -1
- package/dist/interactive/useRackMind.js +81 -8
- package/dist/interactive/useRackMind.js.map +1 -1
- package/dist/oneshot/run.d.ts.map +1 -1
- package/dist/oneshot/run.js +65 -14
- package/dist/oneshot/run.js.map +1 -1
- package/dist/server/client.d.ts +4 -2
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/client.js +6 -4
- package/dist/server/client.js.map +1 -1
- package/dist/server/ssh.d.ts.map +1 -1
- package/dist/server/ssh.js +1 -1
- package/dist/server/ssh.js.map +1 -1
- package/dist/utils/prompt.d.ts.map +1 -1
- package/dist/utils/prompt.js +52 -49
- package/dist/utils/prompt.js.map +1 -1
- package/dist/utils/table.d.ts +3 -0
- package/dist/utils/table.d.ts.map +1 -1
- package/dist/utils/table.js +4 -1
- package/dist/utils/table.js.map +1 -1
- package/package.json +44 -12
- package/dist/ai/client.d.ts +0 -71
- package/dist/ai/client.d.ts.map +0 -1
- package/dist/ai/client.js.map +0 -1
|
@@ -1,56 +1,55 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
//
|
|
2
|
+
// Anthropic provider — original AIClient logic, now behind AIProvider
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
|
-
// Wraps @anthropic-ai/sdk
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// 3. Executing tool calls and feeding results back until a final text response
|
|
4
|
+
// Wraps @anthropic-ai/sdk with the CLI's tool-use loop. This is the default
|
|
5
|
+
// provider — every unknown/missing model id falls back here via
|
|
6
|
+
// providers/index.ts::getAIProvider().
|
|
8
7
|
// ---------------------------------------------------------------------------
|
|
9
8
|
import Anthropic from '@anthropic-ai/sdk';
|
|
10
|
-
import { getToolDefinitions } from '
|
|
11
|
-
import { fetchServerContext, buildSystemPrompt } from '
|
|
12
|
-
import { executeTool } from '
|
|
13
|
-
import { logger } from '
|
|
14
|
-
import { withRetry, isTransientError, isRateLimitError } from '
|
|
15
|
-
|
|
9
|
+
import { getToolDefinitions } from '../tools.js';
|
|
10
|
+
import { fetchServerContext, buildSystemPrompt } from '../system-prompt.js';
|
|
11
|
+
import { executeTool } from '../tool-executor.js';
|
|
12
|
+
import { logger } from '../../utils/logger.js';
|
|
13
|
+
import { withRetry, isTransientError, isRateLimitError } from '../../utils/retry.js';
|
|
14
|
+
import { DEFAULT_ANTHROPIC_MODEL_ID } from '../models.js';
|
|
15
|
+
/** Max iterations of the tool use loop — hard guard against runaway chains. */
|
|
16
16
|
const MAX_TOOL_ITERATIONS = 20;
|
|
17
|
-
|
|
18
|
-
// AI Client
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
export class AIClient {
|
|
17
|
+
export class AnthropicProvider {
|
|
21
18
|
anthropic;
|
|
22
19
|
model;
|
|
23
20
|
maxTokens;
|
|
21
|
+
/** Cached server context — fetched once per session, cleared by /refresh */
|
|
22
|
+
cachedContext = null;
|
|
24
23
|
constructor(options) {
|
|
24
|
+
if (!options.apiKey) {
|
|
25
|
+
throw new Error('Anthropic provider requires an API key.');
|
|
26
|
+
}
|
|
25
27
|
this.anthropic = new Anthropic({
|
|
26
28
|
apiKey: options.apiKey,
|
|
27
|
-
timeout: options.timeoutMs ?? 120_000,
|
|
29
|
+
timeout: options.timeoutMs ?? 120_000,
|
|
28
30
|
});
|
|
29
|
-
this.model = options.model
|
|
31
|
+
this.model = options.model || DEFAULT_ANTHROPIC_MODEL_ID;
|
|
30
32
|
this.maxTokens = options.maxTokens ?? 4096;
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* 5. Returns when Claude produces a final text response (stop_reason = "end_turn")
|
|
41
|
-
*/
|
|
34
|
+
getModel() {
|
|
35
|
+
return this.model;
|
|
36
|
+
}
|
|
37
|
+
/** Clear cached server context so next turn re-fetches from Proxmox API. */
|
|
38
|
+
invalidateContextCache() {
|
|
39
|
+
this.cachedContext = null;
|
|
40
|
+
logger.info('Server context cache cleared — will re-fetch on next turn');
|
|
41
|
+
}
|
|
42
42
|
async chat(query, serverClient, serverAlias, callbacks) {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (!this.cachedContext) {
|
|
44
|
+
this.cachedContext = await fetchServerContext(serverClient);
|
|
45
|
+
}
|
|
46
|
+
const context = this.cachedContext;
|
|
45
47
|
const systemPrompt = buildSystemPrompt(context, serverAlias);
|
|
46
48
|
const tools = getToolDefinitions();
|
|
47
|
-
// Build initial messages
|
|
48
49
|
const messages = [{ role: 'user', content: query }];
|
|
49
|
-
// Tool use loop — keep going until Claude gives a final text response
|
|
50
50
|
for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
|
|
51
51
|
logger.debug('AI request iteration', { iteration, messageCount: messages.length });
|
|
52
52
|
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks);
|
|
53
|
-
// Process the response content blocks
|
|
54
53
|
const assistantContent = [];
|
|
55
54
|
const toolUseBlocks = [];
|
|
56
55
|
for (const block of response.content) {
|
|
@@ -62,9 +61,7 @@ export class AIClient {
|
|
|
62
61
|
toolUseBlocks.push(block);
|
|
63
62
|
}
|
|
64
63
|
}
|
|
65
|
-
// Add the assistant's response to the conversation
|
|
66
64
|
messages.push({ role: 'assistant', content: assistantContent });
|
|
67
|
-
// If Claude is done (no more tool calls), we're finished
|
|
68
65
|
if (response.stop_reason === 'end_turn' || toolUseBlocks.length === 0) {
|
|
69
66
|
logger.debug('AI conversation complete', {
|
|
70
67
|
iterations: iteration + 1,
|
|
@@ -72,12 +69,11 @@ export class AIClient {
|
|
|
72
69
|
});
|
|
73
70
|
return;
|
|
74
71
|
}
|
|
75
|
-
// Execute all tool calls and collect results
|
|
76
72
|
const toolResults = [];
|
|
77
73
|
for (const toolBlock of toolUseBlocks) {
|
|
78
74
|
const toolInput = toolBlock.input;
|
|
79
75
|
callbacks.onToolStart(toolBlock.name, toolBlock.name);
|
|
80
|
-
const result = await executeTool(toolBlock.name, toolInput, serverClient);
|
|
76
|
+
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive });
|
|
81
77
|
callbacks.onToolEnd(toolBlock.name, result.label, result.isError);
|
|
82
78
|
toolResults.push({
|
|
83
79
|
type: 'tool_result',
|
|
@@ -86,38 +82,27 @@ export class AIClient {
|
|
|
86
82
|
is_error: result.isError,
|
|
87
83
|
});
|
|
88
84
|
}
|
|
89
|
-
// Add tool results to the conversation and loop back
|
|
90
85
|
messages.push({ role: 'user', content: toolResults });
|
|
91
86
|
}
|
|
92
87
|
callbacks.onError('Reached maximum tool iterations. The AI may be stuck in a loop.');
|
|
93
88
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Multi-turn conversation method for the interactive REPL.
|
|
96
|
-
*
|
|
97
|
-
* Unlike `chat()`, this accepts the full conversation history so far
|
|
98
|
-
* and appends the new user query. It returns the updated history
|
|
99
|
-
* (including the assistant's response) for the caller to persist.
|
|
100
|
-
*
|
|
101
|
-
* The caller is responsible for building up `history` across turns.
|
|
102
|
-
*/
|
|
103
89
|
async chatMultiTurn(query, history, serverClient, serverAlias, callbacks) {
|
|
104
|
-
|
|
105
|
-
|
|
90
|
+
if (!this.cachedContext) {
|
|
91
|
+
this.cachedContext = await fetchServerContext(serverClient);
|
|
92
|
+
}
|
|
93
|
+
const context = this.cachedContext;
|
|
106
94
|
const systemPrompt = buildSystemPrompt(context, serverAlias);
|
|
107
95
|
const tools = getToolDefinitions();
|
|
108
|
-
// Clone history and append the new user message
|
|
109
96
|
const messages = [
|
|
110
97
|
...history,
|
|
111
98
|
{ role: 'user', content: query },
|
|
112
99
|
];
|
|
113
|
-
// Tool use loop
|
|
114
100
|
for (let iteration = 0; iteration < MAX_TOOL_ITERATIONS; iteration++) {
|
|
115
101
|
logger.debug('AI multi-turn request iteration', {
|
|
116
102
|
iteration,
|
|
117
103
|
messageCount: messages.length,
|
|
118
104
|
});
|
|
119
105
|
const response = await this.createWithRetry(systemPrompt, tools, messages, callbacks);
|
|
120
|
-
// Process content blocks
|
|
121
106
|
const assistantContent = [];
|
|
122
107
|
const toolUseBlocks = [];
|
|
123
108
|
for (const block of response.content) {
|
|
@@ -130,7 +115,6 @@ export class AIClient {
|
|
|
130
115
|
}
|
|
131
116
|
}
|
|
132
117
|
messages.push({ role: 'assistant', content: assistantContent });
|
|
133
|
-
// If done, return the full updated history
|
|
134
118
|
if (response.stop_reason === 'end_turn' || toolUseBlocks.length === 0) {
|
|
135
119
|
logger.debug('AI multi-turn conversation turn complete', {
|
|
136
120
|
iterations: iteration + 1,
|
|
@@ -138,12 +122,11 @@ export class AIClient {
|
|
|
138
122
|
});
|
|
139
123
|
return messages;
|
|
140
124
|
}
|
|
141
|
-
// Execute tool calls
|
|
142
125
|
const toolResults = [];
|
|
143
126
|
for (const toolBlock of toolUseBlocks) {
|
|
144
127
|
const toolInput = toolBlock.input;
|
|
145
128
|
callbacks.onToolStart(toolBlock.name, toolBlock.name);
|
|
146
|
-
const result = await executeTool(toolBlock.name, toolInput, serverClient);
|
|
129
|
+
const result = await executeTool(toolBlock.name, toolInput, serverClient, { confirm: callbacks.confirmDestructive });
|
|
147
130
|
callbacks.onToolEnd(toolBlock.name, result.label, result.isError);
|
|
148
131
|
if (callbacks.onToolResult) {
|
|
149
132
|
callbacks.onToolResult({
|
|
@@ -165,6 +148,9 @@ export class AIClient {
|
|
|
165
148
|
callbacks.onError('Reached maximum tool iterations. The AI may be stuck in a loop.');
|
|
166
149
|
return messages;
|
|
167
150
|
}
|
|
151
|
+
async dispose() {
|
|
152
|
+
// Anthropic SDK has no persistent resources to release.
|
|
153
|
+
}
|
|
168
154
|
/**
|
|
169
155
|
* Create a message with retry logic for transient errors.
|
|
170
156
|
* Rate limits and overload errors trigger exponential backoff.
|
|
@@ -190,9 +176,5 @@ export class AIClient {
|
|
|
190
176
|
},
|
|
191
177
|
});
|
|
192
178
|
}
|
|
193
|
-
/** Get the current model name */
|
|
194
|
-
getModel() {
|
|
195
|
-
return this.model;
|
|
196
|
-
}
|
|
197
179
|
}
|
|
198
|
-
//# sourceMappingURL=
|
|
180
|
+
//# sourceMappingURL=anthropic-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../../../src/ai/providers/anthropic-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { ServerClient } from '../../server/client.js';
|
|
3
|
+
import type { ConfirmDestructiveFn } from '../tool-executor.js';
|
|
4
|
+
export type { ConfirmDestructiveFn, PendingDestructiveAction } from '../tool-executor.js';
|
|
5
|
+
/** Callbacks the provider invokes while a chat turn is in flight. */
|
|
6
|
+
export interface StreamCallbacks {
|
|
7
|
+
/** Streaming text chunk from the assistant. */
|
|
8
|
+
onText: (text: string) => void;
|
|
9
|
+
/** A tool call is about to run (or has started). */
|
|
10
|
+
onToolStart: (toolName: string, label: string) => void;
|
|
11
|
+
/** A tool call finished (success or error). */
|
|
12
|
+
onToolEnd: (toolName: string, label: string, isError: boolean) => void;
|
|
13
|
+
/** A non-fatal error occurred (rate limit retries, warnings, etc.). */
|
|
14
|
+
onError: (error: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Optional: asked before a destructive tool runs. Return true to proceed,
|
|
17
|
+
* false to cancel. If omitted, the provider may execute without prompting
|
|
18
|
+
* (only intended for non-interactive surfaces that have already validated
|
|
19
|
+
* the user opted into auto-confirm).
|
|
20
|
+
*/
|
|
21
|
+
confirmDestructive?: ConfirmDestructiveFn;
|
|
22
|
+
}
|
|
23
|
+
/** A single tool execution observed during a turn (shown in REPL history). */
|
|
24
|
+
export interface ToolExecution {
|
|
25
|
+
toolName: string;
|
|
26
|
+
label: string;
|
|
27
|
+
isError: boolean;
|
|
28
|
+
content: string;
|
|
29
|
+
}
|
|
30
|
+
/** Extended callbacks for multi-turn mode (captures full tool result for UI). */
|
|
31
|
+
export interface MultiTurnCallbacks extends StreamCallbacks {
|
|
32
|
+
onToolResult?: (execution: ToolExecution) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Conversation history shape used by multi-turn mode. We reuse Anthropic's
|
|
36
|
+
* MessageParam for wire compatibility with the anthropic provider and for
|
|
37
|
+
* easy mirroring when the user switches providers mid-conversation.
|
|
38
|
+
*/
|
|
39
|
+
export type ChatHistory = Anthropic.Messages.MessageParam[];
|
|
40
|
+
/**
|
|
41
|
+
* Every provider implements this. `chat` is one-shot (no history);
|
|
42
|
+
* `chatMultiTurn` takes the prior history, appends the new turn, and
|
|
43
|
+
* returns the updated history for the caller to persist.
|
|
44
|
+
*/
|
|
45
|
+
export interface AIProvider {
|
|
46
|
+
/** The model id this provider instance was constructed for. */
|
|
47
|
+
getModel(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Send a single query with no prior history. Streams results via
|
|
50
|
+
* callbacks. Throws on fatal errors.
|
|
51
|
+
*/
|
|
52
|
+
chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Send a new user message given the prior conversation history.
|
|
55
|
+
* Returns the updated history (with the new user turn and the
|
|
56
|
+
* assistant's response appended).
|
|
57
|
+
*/
|
|
58
|
+
chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks): Promise<ChatHistory>;
|
|
59
|
+
/**
|
|
60
|
+
* Release any resources this provider holds (HTTP bridge, child
|
|
61
|
+
* processes, etc.). Safe to call multiple times.
|
|
62
|
+
*/
|
|
63
|
+
dispose(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Optional: clear any cached server context so the next chat turn
|
|
66
|
+
* re-fetches fresh data from the Proxmox API.
|
|
67
|
+
* Not all providers cache context — callers should treat as a no-op if absent.
|
|
68
|
+
*/
|
|
69
|
+
invalidateContextCache?(): void;
|
|
70
|
+
}
|
|
71
|
+
/** Construction options common to every provider. */
|
|
72
|
+
export interface ProviderOptions {
|
|
73
|
+
/** Anthropic API key (required for the anthropic provider, ignored otherwise). */
|
|
74
|
+
apiKey?: string;
|
|
75
|
+
/** Model id to route with. */
|
|
76
|
+
model: string;
|
|
77
|
+
/** Max tokens per response (anthropic only). */
|
|
78
|
+
maxTokens?: number;
|
|
79
|
+
/** Request timeout (anthropic only), ms. */
|
|
80
|
+
timeoutMs?: number;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +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,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,aAAa,CACT,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,kBAAkB,GAC9B,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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// AIProvider interface — minimum surface every provider must implement
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Callers (oneshot/run.ts, interactive/useRackMind.ts) depend ONLY on this
|
|
5
|
+
// interface. Concrete providers (anthropic, claude-code) live in sibling
|
|
6
|
+
// files and are wired up by providers/index.ts via getAIProvider(modelId).
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/ai/providers/base.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uEAAuE;AACvE,8EAA8E;AAC9E,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeCodeBridge — loopback HTTP tool bridge for the spawned `claude`
|
|
3
|
+
* CLI child. Ported from the desktop app's bridge with one adaptation:
|
|
4
|
+
* the executor here runs the CLI's in-process tools (src/ai/tool-executor.ts)
|
|
5
|
+
* instead of delegating to Electron IPC. The security model (loopback bind,
|
|
6
|
+
* random shared secret, Host header check, body cap, request timeout) is
|
|
7
|
+
* identical to the desktop version.
|
|
8
|
+
*/
|
|
9
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
10
|
+
import type { ServerClient } from '../../server/client.js';
|
|
11
|
+
import { type ConfirmDestructiveFn } from '../tool-executor.js';
|
|
12
|
+
export interface BridgeToolCallback {
|
|
13
|
+
(toolName: string, label: string, isError: boolean, content: string): void;
|
|
14
|
+
}
|
|
15
|
+
export interface BridgeHandle {
|
|
16
|
+
bridgeDir: string;
|
|
17
|
+
port: number;
|
|
18
|
+
secret: string;
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a concise manifest of RackMind tools for injection into the Claude
|
|
23
|
+
* Code system prompt. Lists every available tool with its description,
|
|
24
|
+
* input schema, and an example invocation so the model knows what to call
|
|
25
|
+
* via `rackmind-bridge`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildToolManifest(tools: Anthropic.Messages.Tool[]): string;
|
|
28
|
+
export interface StartBridgeOptions {
|
|
29
|
+
/** Server client the in-process tool executor dispatches to. */
|
|
30
|
+
serverClient: ServerClient;
|
|
31
|
+
/**
|
|
32
|
+
* Optional observer called for every bridge tool invocation — lets the
|
|
33
|
+
* caller forward a display label into the REPL's tool-event stream.
|
|
34
|
+
*/
|
|
35
|
+
onToolCall?: BridgeToolCallback;
|
|
36
|
+
/** Signal that aborts pending/future tool calls (returns `cancelled`). */
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
/**
|
|
39
|
+
* Confirmation callback for destructive tools (stop/restart/start of guests).
|
|
40
|
+
* Returning false from this callback cancels the action — the bridge returns
|
|
41
|
+
* a non-error result describing the cancellation so the model can react.
|
|
42
|
+
*/
|
|
43
|
+
confirmDestructive?: ConfirmDestructiveFn;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Start the local HTTP bridge and write the `rackmind-bridge` helper
|
|
47
|
+
* script. Returns a handle with the PATH dir and port the child should
|
|
48
|
+
* see in its env.
|
|
49
|
+
*/
|
|
50
|
+
export declare function startClaudeCodeBridge(opts: StartBridgeOptions): Promise<BridgeHandle>;
|
|
51
|
+
//# sourceMappingURL=claude-code-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-bridge.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/claude-code-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAe,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAa7E,MAAM,WAAW,kBAAkB;IAC/B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9E;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAwCD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,MAAM,CA+C1E;AA6CD,MAAM,WAAW,kBAAkB;IAC/B,gEAAgE;IAChE,YAAY,EAAE,YAAY,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmP3F"}
|