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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -181
  3. package/bin/rackmind.js +5 -1
  4. package/dist/ai/models.d.ts +40 -0
  5. package/dist/ai/models.d.ts.map +1 -0
  6. package/dist/ai/models.js +90 -0
  7. package/dist/ai/models.js.map +1 -0
  8. package/dist/ai/providers/anthropic-provider.d.ts +22 -0
  9. package/dist/ai/providers/anthropic-provider.d.ts.map +1 -0
  10. package/dist/ai/{client.js → providers/anthropic-provider.js} +41 -59
  11. package/dist/ai/providers/anthropic-provider.js.map +1 -0
  12. package/dist/ai/providers/base.d.ts +82 -0
  13. package/dist/ai/providers/base.d.ts.map +1 -0
  14. package/dist/ai/providers/base.js +9 -0
  15. package/dist/ai/providers/base.js.map +1 -0
  16. package/dist/ai/providers/claude-code-bridge.d.ts +51 -0
  17. package/dist/ai/providers/claude-code-bridge.d.ts.map +1 -0
  18. package/dist/ai/providers/claude-code-bridge.js +371 -0
  19. package/dist/ai/providers/claude-code-bridge.js.map +1 -0
  20. package/dist/ai/providers/claude-code-provider.d.ts +24 -0
  21. package/dist/ai/providers/claude-code-provider.d.ts.map +1 -0
  22. package/dist/ai/providers/claude-code-provider.js +246 -0
  23. package/dist/ai/providers/claude-code-provider.js.map +1 -0
  24. package/dist/ai/providers/claude-code-runner.d.ts +51 -0
  25. package/dist/ai/providers/claude-code-runner.d.ts.map +1 -0
  26. package/dist/ai/providers/claude-code-runner.js +325 -0
  27. package/dist/ai/providers/claude-code-runner.js.map +1 -0
  28. package/dist/ai/providers/index.d.ts +8 -0
  29. package/dist/ai/providers/index.d.ts.map +1 -0
  30. package/dist/ai/providers/index.js +28 -0
  31. package/dist/ai/providers/index.js.map +1 -0
  32. package/dist/ai/system-prompt.d.ts.map +1 -1
  33. package/dist/ai/system-prompt.js +1 -26
  34. package/dist/ai/system-prompt.js.map +1 -1
  35. package/dist/ai/tool-executor.d.ts +29 -1
  36. package/dist/ai/tool-executor.d.ts.map +1 -1
  37. package/dist/ai/tool-executor.js +85 -1
  38. package/dist/ai/tool-executor.js.map +1 -1
  39. package/dist/commands/auth.d.ts.map +1 -1
  40. package/dist/commands/auth.js +21 -15
  41. package/dist/commands/auth.js.map +1 -1
  42. package/dist/commands/containers.d.ts.map +1 -1
  43. package/dist/commands/containers.js +10 -3
  44. package/dist/commands/containers.js.map +1 -1
  45. package/dist/commands/exec.d.ts.map +1 -1
  46. package/dist/commands/exec.js +19 -7
  47. package/dist/commands/exec.js.map +1 -1
  48. package/dist/commands/lifecycle.d.ts.map +1 -1
  49. package/dist/commands/lifecycle.js +133 -105
  50. package/dist/commands/lifecycle.js.map +1 -1
  51. package/dist/commands/logs.d.ts.map +1 -1
  52. package/dist/commands/logs.js +10 -4
  53. package/dist/commands/logs.js.map +1 -1
  54. package/dist/commands/report.d.ts.map +1 -1
  55. package/dist/commands/report.js +20 -12
  56. package/dist/commands/report.js.map +1 -1
  57. package/dist/commands/servers.d.ts.map +1 -1
  58. package/dist/commands/servers.js +29 -2
  59. package/dist/commands/servers.js.map +1 -1
  60. package/dist/commands/status.d.ts +5 -0
  61. package/dist/commands/status.d.ts.map +1 -1
  62. package/dist/commands/status.js +66 -50
  63. package/dist/commands/status.js.map +1 -1
  64. package/dist/commands/vms.d.ts.map +1 -1
  65. package/dist/commands/vms.js +9 -3
  66. package/dist/commands/vms.js.map +1 -1
  67. package/dist/config/index.d.ts +10 -0
  68. package/dist/config/index.d.ts.map +1 -1
  69. package/dist/config/index.js +21 -0
  70. package/dist/config/index.js.map +1 -1
  71. package/dist/globals.d.ts +21 -3
  72. package/dist/globals.d.ts.map +1 -1
  73. package/dist/globals.js +30 -4
  74. package/dist/globals.js.map +1 -1
  75. package/dist/index.js +22 -6
  76. package/dist/index.js.map +1 -1
  77. package/dist/interactive/App.d.ts.map +1 -1
  78. package/dist/interactive/App.js +11 -5
  79. package/dist/interactive/App.js.map +1 -1
  80. package/dist/interactive/components/ConfirmDialog.d.ts +9 -0
  81. package/dist/interactive/components/ConfirmDialog.d.ts.map +1 -0
  82. package/dist/interactive/components/ConfirmDialog.js +26 -0
  83. package/dist/interactive/components/ConfirmDialog.js.map +1 -0
  84. package/dist/interactive/components/InputBar.d.ts.map +1 -1
  85. package/dist/interactive/components/InputBar.js +10 -1
  86. package/dist/interactive/components/InputBar.js.map +1 -1
  87. package/dist/interactive/components/ToolOutput.d.ts +1 -1
  88. package/dist/interactive/components/ToolOutput.d.ts.map +1 -1
  89. package/dist/interactive/launch.d.ts +5 -0
  90. package/dist/interactive/launch.d.ts.map +1 -1
  91. package/dist/interactive/launch.js +16 -0
  92. package/dist/interactive/launch.js.map +1 -1
  93. package/dist/interactive/slashCommands.d.ts +1 -0
  94. package/dist/interactive/slashCommands.d.ts.map +1 -1
  95. package/dist/interactive/slashCommands.js +13 -0
  96. package/dist/interactive/slashCommands.js.map +1 -1
  97. package/dist/interactive/useRackMind.d.ts +9 -1
  98. package/dist/interactive/useRackMind.d.ts.map +1 -1
  99. package/dist/interactive/useRackMind.js +81 -8
  100. package/dist/interactive/useRackMind.js.map +1 -1
  101. package/dist/oneshot/run.d.ts.map +1 -1
  102. package/dist/oneshot/run.js +65 -14
  103. package/dist/oneshot/run.js.map +1 -1
  104. package/dist/server/client.d.ts +4 -2
  105. package/dist/server/client.d.ts.map +1 -1
  106. package/dist/server/client.js +6 -4
  107. package/dist/server/client.js.map +1 -1
  108. package/dist/server/ssh.d.ts.map +1 -1
  109. package/dist/server/ssh.js +1 -1
  110. package/dist/server/ssh.js.map +1 -1
  111. package/dist/utils/prompt.d.ts.map +1 -1
  112. package/dist/utils/prompt.js +52 -49
  113. package/dist/utils/prompt.js.map +1 -1
  114. package/dist/utils/table.d.ts +3 -0
  115. package/dist/utils/table.d.ts.map +1 -1
  116. package/dist/utils/table.js +4 -1
  117. package/dist/utils/table.js.map +1 -1
  118. package/package.json +44 -12
  119. package/dist/ai/client.d.ts +0 -71
  120. package/dist/ai/client.d.ts.map +0 -1
  121. package/dist/ai/client.js.map +0 -1
@@ -0,0 +1,371 @@
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 { createServer } from 'http';
10
+ import { randomBytes, timingSafeEqual } from 'crypto';
11
+ import { promises as fs } from 'fs';
12
+ import os from 'os';
13
+ import path from 'path';
14
+ import { executeTool } from '../tool-executor.js';
15
+ import { logger } from '../../utils/logger.js';
16
+ const MAX_BODY_BYTES = 1024 * 1024;
17
+ const REQUEST_TIMEOUT_MS = 2 * 60 * 1000;
18
+ /** Render a JSON-schema type fragment into a short human-readable hint. */
19
+ function renderSchemaType(schema) {
20
+ if (!schema || typeof schema !== 'object')
21
+ return 'any';
22
+ const s = schema;
23
+ if (Array.isArray(s.enum) && s.enum.length > 0) {
24
+ return `enum(${s.enum.map((v) => JSON.stringify(v)).join('|')})`;
25
+ }
26
+ if (Array.isArray(s.oneOf) || Array.isArray(s.anyOf)) {
27
+ const variants = (s.oneOf ?? s.anyOf ?? []);
28
+ const rendered = variants.map((v) => renderSchemaType(v)).join(' | ');
29
+ return rendered || 'any';
30
+ }
31
+ if (Array.isArray(s.type)) {
32
+ return s.type.join('|');
33
+ }
34
+ if (s.type === 'array') {
35
+ return `array<${renderSchemaType(s.items)}>`;
36
+ }
37
+ if (s.type === 'object') {
38
+ if (s.properties && Object.keys(s.properties).length > 0) {
39
+ return `object{${Object.keys(s.properties).join(',')}}`;
40
+ }
41
+ return 'object';
42
+ }
43
+ return typeof s.type === 'string' ? s.type : 'any';
44
+ }
45
+ function sanitizeForPrompt(text) {
46
+ return text.replace(/`/g, "'").replace(/\r/g, '');
47
+ }
48
+ /**
49
+ * Build a concise manifest of RackMind tools for injection into the Claude
50
+ * Code system prompt. Lists every available tool with its description,
51
+ * input schema, and an example invocation so the model knows what to call
52
+ * via `rackmind-bridge`.
53
+ */
54
+ export function buildToolManifest(tools) {
55
+ const lines = [
56
+ '## RackMind Tool Bridge',
57
+ '',
58
+ 'You have access to RackMind infrastructure tools via a local helper:',
59
+ '',
60
+ " rackmind-bridge <tool_name> '<json_args>'",
61
+ '',
62
+ 'Run it via your Bash tool. The helper prints the tool result to stdout ' +
63
+ 'and exits 0 on success, non-zero on error. Always pass tool args as a ' +
64
+ 'single-quoted JSON object so the shell does not mangle them.',
65
+ '',
66
+ 'Available tools:',
67
+ '',
68
+ ];
69
+ for (const tool of tools) {
70
+ lines.push(`### ${tool.name}`);
71
+ if (tool.description) {
72
+ lines.push(sanitizeForPrompt(tool.description));
73
+ }
74
+ const schema = (tool.input_schema ?? {});
75
+ const props = schema.properties ?? {};
76
+ const required = new Set(schema.required ?? []);
77
+ const propEntries = Object.entries(props);
78
+ const exampleArgs = {};
79
+ if (propEntries.length > 0) {
80
+ lines.push('Arguments:');
81
+ for (const [key, rawVal] of propEntries) {
82
+ const val = (rawVal ?? {});
83
+ const req = required.has(key) ? ' (required)' : '';
84
+ const type = renderSchemaType(rawVal);
85
+ const desc = val.description ? ` — ${sanitizeForPrompt(val.description)}` : '';
86
+ lines.push(` - ${key}: ${type}${req}${desc}`);
87
+ if (required.has(key)) {
88
+ exampleArgs[key] = 'value';
89
+ }
90
+ }
91
+ }
92
+ const exampleBody = Object.keys(exampleArgs).length > 0 ? JSON.stringify(exampleArgs) : '{}';
93
+ lines.push(`Example: rackmind-bridge ${tool.name} '${exampleBody}'`);
94
+ lines.push('');
95
+ }
96
+ return lines.join('\n');
97
+ }
98
+ function secretsEqual(a, b) {
99
+ const ab = Buffer.from(a, 'utf-8');
100
+ const bb = Buffer.from(b, 'utf-8');
101
+ if (ab.length !== bb.length)
102
+ return false;
103
+ return timingSafeEqual(ab, bb);
104
+ }
105
+ function hostHeaderIsLoopback(host) {
106
+ if (!host)
107
+ return false;
108
+ return host.startsWith('127.0.0.1:') || host.startsWith('localhost:');
109
+ }
110
+ function sendJson(res, statusCode, body) {
111
+ if (res.writableEnded)
112
+ return;
113
+ res.writeHead(statusCode, { 'content-type': 'application/json' });
114
+ res.end(JSON.stringify(body));
115
+ }
116
+ function readBodyCapped(req) {
117
+ return new Promise((resolve, reject) => {
118
+ const chunks = [];
119
+ let total = 0;
120
+ let exceeded = false;
121
+ req.on('data', (chunk) => {
122
+ if (exceeded)
123
+ return;
124
+ total += chunk.length;
125
+ if (total > MAX_BODY_BYTES) {
126
+ exceeded = true;
127
+ reject(new Error('BODY_TOO_LARGE'));
128
+ req.destroy();
129
+ return;
130
+ }
131
+ chunks.push(chunk);
132
+ });
133
+ req.on('end', () => {
134
+ if (!exceeded)
135
+ resolve(Buffer.concat(chunks));
136
+ });
137
+ req.on('error', (err) => {
138
+ if (!exceeded)
139
+ reject(err);
140
+ });
141
+ });
142
+ }
143
+ /**
144
+ * Start the local HTTP bridge and write the `rackmind-bridge` helper
145
+ * script. Returns a handle with the PATH dir and port the child should
146
+ * see in its env.
147
+ */
148
+ export async function startClaudeCodeBridge(opts) {
149
+ const secret = randomBytes(32).toString('hex');
150
+ const server = createServer((req, res) => {
151
+ const timeout = setTimeout(() => {
152
+ sendJson(res, 504, {
153
+ ok: false,
154
+ status: 'error',
155
+ error: 'Tool request timed out',
156
+ });
157
+ req.destroy();
158
+ }, REQUEST_TIMEOUT_MS);
159
+ res.on('close', () => clearTimeout(timeout));
160
+ const handle = async () => {
161
+ if (!hostHeaderIsLoopback(req.headers.host)) {
162
+ sendJson(res, 403, { ok: false, status: 'error', error: 'Forbidden host' });
163
+ return;
164
+ }
165
+ if (req.method !== 'POST' || req.url !== '/tool') {
166
+ sendJson(res, 404, { ok: false, status: 'error', error: 'Not found' });
167
+ return;
168
+ }
169
+ const provided = req.headers['x-rackmind-secret'];
170
+ if (typeof provided !== 'string' || !secretsEqual(provided, secret)) {
171
+ sendJson(res, 401, { ok: false, status: 'error', error: 'Unauthorized' });
172
+ return;
173
+ }
174
+ let bodyBuf;
175
+ try {
176
+ bodyBuf = await readBodyCapped(req);
177
+ }
178
+ catch (err) {
179
+ const message = err instanceof Error ? err.message : String(err);
180
+ if (message === 'BODY_TOO_LARGE') {
181
+ sendJson(res, 413, {
182
+ ok: false,
183
+ status: 'error',
184
+ error: 'Request body exceeds 1 MiB',
185
+ });
186
+ return;
187
+ }
188
+ sendJson(res, 400, {
189
+ ok: false,
190
+ status: 'error',
191
+ error: `Failed to read body: ${message}`,
192
+ });
193
+ return;
194
+ }
195
+ let payload;
196
+ try {
197
+ payload = JSON.parse(bodyBuf.toString('utf-8'));
198
+ }
199
+ catch {
200
+ sendJson(res, 400, { ok: false, status: 'error', error: 'Invalid JSON body' });
201
+ return;
202
+ }
203
+ const name = typeof payload.name === 'string' ? payload.name : '';
204
+ const input = typeof payload.input === 'object' && payload.input !== null
205
+ ? payload.input
206
+ : {};
207
+ if (!name) {
208
+ sendJson(res, 400, { ok: false, status: 'error', error: 'Missing tool name' });
209
+ return;
210
+ }
211
+ if (opts.signal?.aborted) {
212
+ sendJson(res, 200, {
213
+ ok: false,
214
+ status: 'cancelled',
215
+ error: 'Aborted by user',
216
+ });
217
+ return;
218
+ }
219
+ try {
220
+ const result = await executeTool(name, input, opts.serverClient, { confirm: opts.confirmDestructive });
221
+ if (opts.onToolCall) {
222
+ opts.onToolCall(name, result.label, result.isError, result.content);
223
+ }
224
+ sendJson(res, 200, {
225
+ ok: !result.isError,
226
+ status: result.isError ? 'error' : 'done',
227
+ result: result.content,
228
+ });
229
+ }
230
+ catch (err) {
231
+ const message = err instanceof Error ? err.message : String(err);
232
+ logger.error('Bridge tool executor threw', { tool: name, error: message });
233
+ sendJson(res, 500, { ok: false, status: 'error', error: message });
234
+ }
235
+ };
236
+ handle().catch((err) => {
237
+ const message = err instanceof Error ? err.message : String(err);
238
+ sendJson(res, 500, { ok: false, status: 'error', error: message });
239
+ });
240
+ });
241
+ const port = await new Promise((resolve, reject) => {
242
+ const onError = (err) => reject(err);
243
+ server.once('error', onError);
244
+ server.listen(0, '127.0.0.1', () => {
245
+ server.removeListener('error', onError);
246
+ const address = server.address();
247
+ if (address && typeof address === 'object') {
248
+ resolve(address.port);
249
+ }
250
+ else {
251
+ reject(new Error('Failed to bind bridge HTTP server'));
252
+ }
253
+ });
254
+ });
255
+ let bridgeDir = null;
256
+ let stopped = false;
257
+ const closeServer = () => new Promise((resolve) => {
258
+ server.close(() => resolve());
259
+ });
260
+ const stop = async () => {
261
+ if (stopped)
262
+ return;
263
+ stopped = true;
264
+ await closeServer().catch(() => {
265
+ // best-effort
266
+ });
267
+ if (bridgeDir) {
268
+ await fs.rm(bridgeDir, { recursive: true, force: true }).catch(() => {
269
+ // best-effort
270
+ });
271
+ }
272
+ };
273
+ try {
274
+ bridgeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rackmind-bridge-'));
275
+ const scriptPath = path.join(bridgeDir, 'rackmind-bridge');
276
+ // Self-contained Node helper. Node is guaranteed to exist because the
277
+ // `claude` CLI itself is a Node program.
278
+ const script = `#!/usr/bin/env node
279
+ 'use strict';
280
+ // rackmind-bridge: forwards a tool call to the local RackMind HTTP bridge.
281
+ // Usage: rackmind-bridge <tool_name> '<json_args>'
282
+ const http = require('http');
283
+
284
+ const toolName = process.argv[2];
285
+ const rawArgs = process.argv[3] != null ? process.argv[3] : '{}';
286
+
287
+ if (!toolName) {
288
+ process.stderr.write("usage: rackmind-bridge <tool_name> '<json_args>'\\n");
289
+ process.exit(2);
290
+ }
291
+
292
+ let parsedInput;
293
+ try {
294
+ parsedInput = JSON.parse(rawArgs);
295
+ } catch (err) {
296
+ process.stderr.write('rackmind-bridge: invalid JSON args: ' + err.message + '\\n');
297
+ process.exit(2);
298
+ }
299
+ if (parsedInput == null || typeof parsedInput !== 'object' || Array.isArray(parsedInput)) {
300
+ process.stderr.write('rackmind-bridge: args must be a JSON object\\n');
301
+ process.exit(2);
302
+ }
303
+
304
+ const body = Buffer.from(JSON.stringify({ name: toolName, input: parsedInput }), 'utf-8');
305
+
306
+ const req = http.request(
307
+ {
308
+ host: '127.0.0.1',
309
+ port: ${port},
310
+ path: '/tool',
311
+ method: 'POST',
312
+ headers: {
313
+ 'content-type': 'application/json',
314
+ 'content-length': body.length,
315
+ 'x-rackmind-secret': ${JSON.stringify(secret)},
316
+ },
317
+ },
318
+ (res) => {
319
+ const chunks = [];
320
+ res.on('data', (c) => chunks.push(c));
321
+ res.on('end', () => {
322
+ const text = Buffer.concat(chunks).toString('utf-8');
323
+ let parsed;
324
+ try {
325
+ parsed = JSON.parse(text);
326
+ } catch (err) {
327
+ process.stderr.write('rackmind-bridge: bad response from server: ' + err.message + '\\n');
328
+ process.stderr.write(text + '\\n');
329
+ process.exit(1);
330
+ }
331
+ const out = parsed.result != null ? parsed.result : (parsed.error != null ? parsed.error : '');
332
+ if (out) process.stdout.write(String(out) + (String(out).endsWith('\\n') ? '' : '\\n'));
333
+ process.exit(parsed.ok === true ? 0 : 1);
334
+ });
335
+ }
336
+ );
337
+
338
+ req.on('error', (err) => {
339
+ process.stderr.write('rackmind-bridge: request failed: ' + err.message + '\\n');
340
+ process.exit(1);
341
+ });
342
+
343
+ req.write(body);
344
+ req.end();
345
+ `;
346
+ await fs.writeFile(scriptPath, script, { mode: 0o755 });
347
+ }
348
+ catch (err) {
349
+ await stop();
350
+ throw err;
351
+ }
352
+ // Belt-and-suspenders cleanup — if the process exits without calling
353
+ // stop() (e.g. uncaught exception in the caller), at least try to
354
+ // unlink the temp dir synchronously.
355
+ const exitHandler = () => {
356
+ stop().catch(() => undefined);
357
+ };
358
+ process.once('exit', exitHandler);
359
+ const originalStop = stop;
360
+ const wrappedStop = async () => {
361
+ process.removeListener('exit', exitHandler);
362
+ await originalStop();
363
+ };
364
+ return {
365
+ bridgeDir,
366
+ port,
367
+ secret,
368
+ stop: wrappedStop,
369
+ };
370
+ }
371
+ //# sourceMappingURL=claude-code-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-bridge.js","sourceRoot":"","sources":["../../../src/ai/providers/claude-code-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ServerClient } from '../../server/client.js';
2
+ import type { AIProvider, ChatHistory, MultiTurnCallbacks, ProviderOptions, StreamCallbacks } from './base.js';
3
+ export declare class ClaudeCodeProvider implements AIProvider {
4
+ private readonly modelId;
5
+ private readonly cliModel;
6
+ /** One session id per serverAlias (REPL continuity within a process). */
7
+ private readonly sessions;
8
+ /** Deterministic scratch cwd per process — cleaned up on dispose(). */
9
+ private scratchRoot;
10
+ private disposed;
11
+ constructor(options: ProviderOptions);
12
+ getModel(): string;
13
+ private getScratchDir;
14
+ chat(query: string, serverClient: ServerClient, serverAlias: string, callbacks: StreamCallbacks): Promise<void>;
15
+ chatMultiTurn(query: string, history: ChatHistory, serverClient: ServerClient, serverAlias: string, callbacks: MultiTurnCallbacks): Promise<ChatHistory>;
16
+ /**
17
+ * Execute one claude-code turn. Returns the clean assistant text
18
+ * (with [STATUS:] markers stripped).
19
+ */
20
+ private runTurn;
21
+ dispose(): Promise<void>;
22
+ }
23
+ export declare function createClaudeCodeProvider(options: ProviderOptions): ClaudeCodeProvider;
24
+ //# sourceMappingURL=claude-code-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-provider.d.ts","sourceRoot":"","sources":["../../../src/ai/providers/claude-code-provider.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,OAAO,KAAK,EACR,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,eAAe,EAClB,MAAM,WAAW,CAAC;AA2DnB,qBAAa,kBAAmB,YAAW,UAAU;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,uEAAuE;IACvE,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,eAAe;IAKpC,QAAQ,IAAI,MAAM;YAIJ,aAAa;IAOrB,IAAI,CACN,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,GAC3B,OAAO,CAAC,IAAI,CAAC;IAKV,aAAa,CACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,kBAAkB,GAC9B,OAAO,CAAC,WAAW,CAAC;IA0BvB;;;OAGG;YACW,OAAO;IA6Hf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAYjC;AAKD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,kBAAkB,CAErF"}
@@ -0,0 +1,246 @@
1
+ // ---------------------------------------------------------------------------
2
+ // ClaudeCodeProvider — routes chat through the local `claude` CLI
3
+ // ---------------------------------------------------------------------------
4
+ // Private provider, invisible unless a feature flag is set. See models.ts
5
+ // for visibility gating — this file deliberately does NOT reference the
6
+ // env var name so that a user reading error output cannot discover it.
7
+ //
8
+ // The provider spawns the `claude` CLI per turn with a fresh rackmind-bridge
9
+ // tool endpoint bound to 127.0.0.1 on an ephemeral port. RackMind tools
10
+ // (execute_command, list_containers, etc.) are invoked by the model via
11
+ // the bridge; native Claude Code tools (Bash, Read, Edit, …) run in the
12
+ // spawned process the way they normally would.
13
+ //
14
+ // Session continuity: within a single CLI process lifetime (interactive
15
+ // REPL), we keep one session id per serverAlias. The first turn mints a
16
+ // new UUID and passes `--session-id`; subsequent turns pass `--resume` so
17
+ // the CLI retains its own tool history. One-shot mode ignores sessions.
18
+ // ---------------------------------------------------------------------------
19
+ import path from 'path';
20
+ import { promises as fsp } from 'fs';
21
+ import os from 'os';
22
+ import { randomUUID } from 'crypto';
23
+ import { getToolDefinitions } from '../tools.js';
24
+ import { fetchServerContext, buildSystemPrompt } from '../system-prompt.js';
25
+ import { logger } from '../../utils/logger.js';
26
+ import { runClaudeCode, checkClaudeCliAvailable, } from './claude-code-runner.js';
27
+ import { startClaudeCodeBridge, buildToolManifest, } from './claude-code-bridge.js';
28
+ /**
29
+ * Native Claude Code tool names we forward into the REPL's onToolStart/End
30
+ * stream (purely for display). RackMind tools that ride on top of the
31
+ * bridge are surfaced by the bridge executor instead, so they would show
32
+ * up twice otherwise.
33
+ */
34
+ const NATIVE_CLAUDE_CODE_TOOLS = new Set([
35
+ 'Bash',
36
+ 'Read',
37
+ 'Write',
38
+ 'Edit',
39
+ 'MultiEdit',
40
+ 'Glob',
41
+ 'Grep',
42
+ ]);
43
+ /** Map a `claude-code:*` model id to the CLI model alias. */
44
+ function claudeCodeCliModel(modelId) {
45
+ return modelId.toLowerCase().includes('opus') ? 'opus' : 'sonnet';
46
+ }
47
+ /** Best-effort classification of runner failures into a user-readable error. */
48
+ function classifyError(stderr, exitCode) {
49
+ const lower = stderr.toLowerCase();
50
+ if (lower.includes('not authenticated') || lower.includes('please log in')) {
51
+ return 'Claude Code is not authenticated. Run `claude login` in a terminal and try again.';
52
+ }
53
+ if (lower.includes('model not found') ||
54
+ lower.includes('invalid model') ||
55
+ lower.includes('model unavailable')) {
56
+ return 'The requested model is not available through this provider.';
57
+ }
58
+ if (lower.includes('enoent') || lower.includes('command not found')) {
59
+ return 'The `claude` CLI binary is not available on PATH.';
60
+ }
61
+ if (lower.includes('rate limit') || lower.includes('429')) {
62
+ return 'Rate limit reached. Wait a minute and try again.';
63
+ }
64
+ const tail = stderr.trim().split('\n').slice(-6).join('\n').slice(-800);
65
+ const code = exitCode === null ? 'unknown' : exitCode;
66
+ return tail
67
+ ? `Claude Code CLI exited with code ${code}:\n${tail}`
68
+ : `Claude Code CLI exited with code ${code}`;
69
+ }
70
+ export class ClaudeCodeProvider {
71
+ modelId;
72
+ cliModel;
73
+ /** One session id per serverAlias (REPL continuity within a process). */
74
+ sessions = new Map();
75
+ /** Deterministic scratch cwd per process — cleaned up on dispose(). */
76
+ scratchRoot = null;
77
+ disposed = false;
78
+ constructor(options) {
79
+ this.modelId = options.model;
80
+ this.cliModel = claudeCodeCliModel(options.model);
81
+ }
82
+ getModel() {
83
+ return this.modelId;
84
+ }
85
+ async getScratchDir() {
86
+ if (!this.scratchRoot) {
87
+ this.scratchRoot = await fsp.mkdtemp(path.join(os.tmpdir(), 'rackmind-cc-'));
88
+ }
89
+ return this.scratchRoot;
90
+ }
91
+ async chat(query, serverClient, serverAlias, callbacks) {
92
+ // One-shot mode uses a throwaway session id — nothing to resume.
93
+ await this.runTurn(query, serverClient, serverAlias, callbacks, /*useSession*/ false);
94
+ }
95
+ async chatMultiTurn(query, history, serverClient, serverAlias, callbacks) {
96
+ // Mirror the turn into the Anthropic-shaped history so that a future
97
+ // switch to the Anthropic provider in the same session preserves a
98
+ // sensible transcript. The Claude Code CLI owns its own tool log
99
+ // via --resume, so we only persist user + assistant text, not the
100
+ // bridge's tool round-trips.
101
+ const messages = [
102
+ ...history,
103
+ { role: 'user', content: query },
104
+ ];
105
+ const assistantText = await this.runTurn(query, serverClient, serverAlias, callbacks,
106
+ /*useSession*/ true);
107
+ messages.push({
108
+ role: 'assistant',
109
+ content: assistantText ? [{ type: 'text', text: assistantText }] : [],
110
+ });
111
+ return messages;
112
+ }
113
+ /**
114
+ * Execute one claude-code turn. Returns the clean assistant text
115
+ * (with [STATUS:] markers stripped).
116
+ */
117
+ async runTurn(userMessage, serverClient, serverAlias, callbacks, useSession) {
118
+ const available = await checkClaudeCliAvailable();
119
+ if (!available) {
120
+ throw new Error('The `claude` CLI is not available on PATH. Install Claude Code or switch to another model.');
121
+ }
122
+ const context = await fetchServerContext(serverClient);
123
+ const baseSystemPrompt = buildSystemPrompt(context, serverAlias);
124
+ const toolManifest = buildToolManifest(getToolDefinitions());
125
+ const systemPrompt = `${baseSystemPrompt}\n\n${toolManifest}`;
126
+ const cwd = await this.getScratchDir();
127
+ const existingSessionId = useSession ? this.sessions.get(serverAlias) : undefined;
128
+ const plannedSessionId = existingSessionId ?? randomUUID();
129
+ let bridge = null;
130
+ let accumulatedText = '';
131
+ try {
132
+ try {
133
+ bridge = await startClaudeCodeBridge({
134
+ serverClient,
135
+ confirmDestructive: callbacks.confirmDestructive,
136
+ onToolCall: (toolName, label, isError, content) => {
137
+ // Forward RackMind tool results from the bridge
138
+ // executor into the REPL's tool stream. Start +
139
+ // end are emitted together because the bridge
140
+ // only calls back once, after the tool completes.
141
+ callbacks.onToolStart(toolName, label);
142
+ callbacks.onToolEnd(toolName, label, isError);
143
+ const withToolResult = callbacks;
144
+ if (withToolResult.onToolResult) {
145
+ withToolResult.onToolResult({
146
+ toolName,
147
+ label,
148
+ isError,
149
+ content,
150
+ });
151
+ }
152
+ },
153
+ });
154
+ }
155
+ catch (err) {
156
+ const message = err instanceof Error ? err.message : String(err);
157
+ throw new Error(`Failed to start Claude Code tool bridge: ${message}`, {
158
+ cause: err,
159
+ });
160
+ }
161
+ const result = await runClaudeCode({
162
+ userMessage,
163
+ systemPrompt,
164
+ model: this.cliModel,
165
+ cwd,
166
+ extraEnv: {
167
+ PATH: `${bridge.bridgeDir}${path.delimiter}${process.env.PATH ?? ''}`,
168
+ },
169
+ ...(existingSessionId
170
+ ? { resumeSessionId: existingSessionId }
171
+ : { newSessionId: plannedSessionId }),
172
+ onText: (delta) => {
173
+ accumulatedText += delta;
174
+ callbacks.onText(delta);
175
+ },
176
+ onStatus: (status) => {
177
+ // Status lines go to the error/status channel —
178
+ // non-fatal, informational.
179
+ callbacks.onError(status);
180
+ },
181
+ onToolUse: (event) => {
182
+ if (!NATIVE_CLAUDE_CODE_TOOLS.has(event.name))
183
+ return;
184
+ if (event.status === 'running') {
185
+ callbacks.onToolStart(event.name, event.name);
186
+ }
187
+ else {
188
+ callbacks.onToolEnd(event.name, event.name, event.status === 'error');
189
+ }
190
+ },
191
+ onStderr: (chunk) => {
192
+ logger.debug('claude-code stderr', { chunk: chunk.trim() });
193
+ },
194
+ });
195
+ if (useSession) {
196
+ const observed = result.sessionId ?? existingSessionId ?? plannedSessionId;
197
+ this.sessions.set(serverAlias, observed);
198
+ }
199
+ if (!result.success) {
200
+ if (result.aborted)
201
+ return result.finalText;
202
+ if (result.timedOut) {
203
+ throw new Error('Claude Code CLI timed out after 10 minutes.');
204
+ }
205
+ if (result.overflowed) {
206
+ throw new Error('Claude Code CLI emitted a single line larger than 10 MiB — aborting to avoid memory exhaustion.');
207
+ }
208
+ if (result.maxTurnsExceeded) {
209
+ callbacks.onError('Claude Code CLI hit its max-turns ceiling — send another message to resume.');
210
+ return result.finalText;
211
+ }
212
+ throw new Error(classifyError(result.stderr, result.exitCode));
213
+ }
214
+ return result.finalText || accumulatedText;
215
+ }
216
+ finally {
217
+ if (bridge) {
218
+ await bridge.stop().catch((err) => {
219
+ logger.warn('Bridge stop failed', {
220
+ error: err instanceof Error ? err.message : String(err),
221
+ });
222
+ });
223
+ }
224
+ }
225
+ }
226
+ async dispose() {
227
+ if (this.disposed)
228
+ return;
229
+ this.disposed = true;
230
+ this.sessions.clear();
231
+ if (this.scratchRoot) {
232
+ const root = this.scratchRoot;
233
+ this.scratchRoot = null;
234
+ await fsp.rm(root, { recursive: true, force: true }).catch(() => {
235
+ // best-effort
236
+ });
237
+ }
238
+ }
239
+ }
240
+ // Construction helper kept separate from the class so providers/index.ts
241
+ // can wire it up without importing the class directly into code that does
242
+ // the gating check.
243
+ export function createClaudeCodeProvider(options) {
244
+ return new ClaudeCodeProvider(options);
245
+ }
246
+ //# sourceMappingURL=claude-code-provider.js.map