teleportation-cli 1.0.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/.claude/hooks/config-loader.mjs +93 -0
- package/.claude/hooks/heartbeat.mjs +331 -0
- package/.claude/hooks/notification.mjs +35 -0
- package/.claude/hooks/permission_request.mjs +307 -0
- package/.claude/hooks/post_tool_use.mjs +137 -0
- package/.claude/hooks/pre_tool_use.mjs +451 -0
- package/.claude/hooks/session-register.mjs +274 -0
- package/.claude/hooks/session_end.mjs +256 -0
- package/.claude/hooks/session_start.mjs +308 -0
- package/.claude/hooks/stop.mjs +277 -0
- package/.claude/hooks/user_prompt_submit.mjs +91 -0
- package/LICENSE +21 -0
- package/README.md +243 -0
- package/lib/auth/api-key.js +110 -0
- package/lib/auth/credentials.js +341 -0
- package/lib/backup/manager.js +461 -0
- package/lib/cli/daemon-commands.js +299 -0
- package/lib/cli/index.js +303 -0
- package/lib/cli/session-commands.js +294 -0
- package/lib/cli/snapshot-commands.js +223 -0
- package/lib/cli/worktree-commands.js +291 -0
- package/lib/config/manager.js +306 -0
- package/lib/daemon/lifecycle.js +336 -0
- package/lib/daemon/pid-manager.js +160 -0
- package/lib/daemon/teleportation-daemon.js +2009 -0
- package/lib/handoff/config.js +102 -0
- package/lib/handoff/example.js +152 -0
- package/lib/handoff/git-handoff.js +351 -0
- package/lib/handoff/handoff.js +277 -0
- package/lib/handoff/index.js +25 -0
- package/lib/handoff/session-state.js +238 -0
- package/lib/install/installer.js +555 -0
- package/lib/machine-coders/claude-code-adapter.js +329 -0
- package/lib/machine-coders/example.js +239 -0
- package/lib/machine-coders/gemini-cli-adapter.js +406 -0
- package/lib/machine-coders/index.js +103 -0
- package/lib/machine-coders/interface.js +168 -0
- package/lib/router/classifier.js +251 -0
- package/lib/router/example.js +92 -0
- package/lib/router/index.js +69 -0
- package/lib/router/mech-llms-client.js +277 -0
- package/lib/router/models.js +188 -0
- package/lib/router/router.js +382 -0
- package/lib/session/cleanup.js +100 -0
- package/lib/session/metadata.js +258 -0
- package/lib/session/mute-checker.js +114 -0
- package/lib/session-registry/manager.js +302 -0
- package/lib/snapshot/manager.js +390 -0
- package/lib/utils/errors.js +166 -0
- package/lib/utils/logger.js +148 -0
- package/lib/utils/retry.js +155 -0
- package/lib/worktree/manager.js +301 -0
- package/package.json +66 -0
- package/teleportation-cli.cjs +2987 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Adapter
|
|
3
|
+
*
|
|
4
|
+
* Adapter for Claude Code CLI. Uses the existing hook system for approvals.
|
|
5
|
+
*
|
|
6
|
+
* Key characteristics:
|
|
7
|
+
* - Hooks handle approval via Teleportation Relay
|
|
8
|
+
* - Supports session resume via --resume
|
|
9
|
+
* - Uses --dangerously-skip-permissions for auto-approve mode
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
import { exec } from 'child_process';
|
|
15
|
+
import { MachineCoder, CODER_NAMES } from './interface.js';
|
|
16
|
+
|
|
17
|
+
const execAsync = promisify(exec);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if a command exists on the system
|
|
21
|
+
* @param {string} command
|
|
22
|
+
* @returns {Promise<boolean>}
|
|
23
|
+
*/
|
|
24
|
+
async function commandExists(command) {
|
|
25
|
+
try {
|
|
26
|
+
await execAsync(`which ${command}`);
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Claude Code Adapter
|
|
35
|
+
*/
|
|
36
|
+
export class ClaudeCodeAdapter extends MachineCoder {
|
|
37
|
+
name = CODER_NAMES.CLAUDE_CODE;
|
|
38
|
+
displayName = 'Claude Code';
|
|
39
|
+
|
|
40
|
+
// Track running processes for stop()
|
|
41
|
+
#runningProcesses = new Map();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if Claude Code CLI is available
|
|
45
|
+
* @returns {Promise<boolean>}
|
|
46
|
+
*/
|
|
47
|
+
async isAvailable() {
|
|
48
|
+
return commandExists('claude');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get Claude Code status
|
|
53
|
+
* @returns {Promise<Object>}
|
|
54
|
+
*/
|
|
55
|
+
async getStatus() {
|
|
56
|
+
const available = await this.isAvailable();
|
|
57
|
+
|
|
58
|
+
if (!available) {
|
|
59
|
+
return { available: false };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const { stdout } = await execAsync('claude --version');
|
|
64
|
+
return {
|
|
65
|
+
available: true,
|
|
66
|
+
version: stdout.trim(),
|
|
67
|
+
};
|
|
68
|
+
} catch {
|
|
69
|
+
return { available: true };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Execute a task with Claude Code
|
|
75
|
+
*
|
|
76
|
+
* Note: Claude Code uses hooks for approval, so we don't handle
|
|
77
|
+
* onToolCall here. The hooks communicate with Relay directly.
|
|
78
|
+
*
|
|
79
|
+
* @param {Object} options
|
|
80
|
+
* @returns {Promise<Object>}
|
|
81
|
+
*/
|
|
82
|
+
async execute(options) {
|
|
83
|
+
const {
|
|
84
|
+
projectPath,
|
|
85
|
+
sessionId,
|
|
86
|
+
prompt,
|
|
87
|
+
onProgress,
|
|
88
|
+
timeoutMs = 600000, // 10 min default
|
|
89
|
+
autoApprove = false,
|
|
90
|
+
} = options;
|
|
91
|
+
|
|
92
|
+
const executionId = `claude-${Date.now()}`;
|
|
93
|
+
const args = [];
|
|
94
|
+
|
|
95
|
+
// Add prompt
|
|
96
|
+
args.push('-p', prompt);
|
|
97
|
+
|
|
98
|
+
// Auto-approve mode (dangerous - skips all permission checks)
|
|
99
|
+
if (autoApprove) {
|
|
100
|
+
args.push('--dangerously-skip-permissions');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Output format for parsing
|
|
104
|
+
args.push('--output-format', 'json');
|
|
105
|
+
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const startTime = Date.now();
|
|
108
|
+
let stdout = '';
|
|
109
|
+
let stderr = '';
|
|
110
|
+
const toolCalls = [];
|
|
111
|
+
|
|
112
|
+
const proc = spawn('claude', args, {
|
|
113
|
+
cwd: projectPath,
|
|
114
|
+
env: {
|
|
115
|
+
...process.env,
|
|
116
|
+
// Pass session ID to hooks
|
|
117
|
+
TELEPORTATION_SESSION_ID: sessionId,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.#runningProcesses.set(executionId, proc);
|
|
122
|
+
|
|
123
|
+
// Timeout handling
|
|
124
|
+
const timeout = setTimeout(() => {
|
|
125
|
+
proc.kill('SIGTERM');
|
|
126
|
+
reject(new Error(`Execution timed out after ${timeoutMs}ms`));
|
|
127
|
+
}, timeoutMs);
|
|
128
|
+
|
|
129
|
+
proc.stdout.on('data', (data) => {
|
|
130
|
+
const chunk = data.toString();
|
|
131
|
+
stdout += chunk;
|
|
132
|
+
|
|
133
|
+
// Try to parse JSON events for progress
|
|
134
|
+
try {
|
|
135
|
+
const lines = chunk.split('\n').filter(l => l.trim());
|
|
136
|
+
for (const line of lines) {
|
|
137
|
+
const event = JSON.parse(line);
|
|
138
|
+
|
|
139
|
+
// Track tool calls
|
|
140
|
+
if (event.type === 'tool_use') {
|
|
141
|
+
toolCalls.push({
|
|
142
|
+
id: event.tool_use_id || `tool-${toolCalls.length}`,
|
|
143
|
+
tool: event.tool_name,
|
|
144
|
+
input: event.tool_input,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onProgress?.({
|
|
150
|
+
type: event.type,
|
|
151
|
+
timestamp: Date.now(),
|
|
152
|
+
data: event,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
// Not JSON, just raw output
|
|
157
|
+
onProgress?.({
|
|
158
|
+
type: 'output',
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
data: { text: chunk },
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
proc.stderr.on('data', (data) => {
|
|
166
|
+
stderr += data.toString();
|
|
167
|
+
onProgress?.({
|
|
168
|
+
type: 'error',
|
|
169
|
+
timestamp: Date.now(),
|
|
170
|
+
data: { text: data.toString() },
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
proc.on('close', (code) => {
|
|
175
|
+
clearTimeout(timeout);
|
|
176
|
+
this.#runningProcesses.delete(executionId);
|
|
177
|
+
|
|
178
|
+
const durationMs = Date.now() - startTime;
|
|
179
|
+
|
|
180
|
+
// Try to parse final JSON output
|
|
181
|
+
let output = stdout;
|
|
182
|
+
let stats = { durationMs };
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const result = JSON.parse(stdout);
|
|
186
|
+
output = result.result || result.response || result.output || stdout;
|
|
187
|
+
if (result.usage) {
|
|
188
|
+
stats.tokensUsed = result.usage.total_tokens;
|
|
189
|
+
}
|
|
190
|
+
if (result.cost) {
|
|
191
|
+
stats.cost = result.cost;
|
|
192
|
+
}
|
|
193
|
+
if (result.model) {
|
|
194
|
+
stats.model = result.model;
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// Not JSON, use raw output
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
resolve({
|
|
201
|
+
success: code === 0,
|
|
202
|
+
output,
|
|
203
|
+
error: code !== 0 ? stderr || `Exit code: ${code}` : undefined,
|
|
204
|
+
toolCalls,
|
|
205
|
+
stats,
|
|
206
|
+
executionId,
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
proc.on('error', (err) => {
|
|
211
|
+
clearTimeout(timeout);
|
|
212
|
+
this.#runningProcesses.delete(executionId);
|
|
213
|
+
reject(err);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Resume a Claude Code session with a new prompt
|
|
220
|
+
* @param {string} sessionId - Claude session ID to resume
|
|
221
|
+
* @param {string} prompt - New prompt
|
|
222
|
+
* @param {Object} options - Additional options
|
|
223
|
+
* @returns {Promise<Object>}
|
|
224
|
+
*/
|
|
225
|
+
async resume(sessionId, prompt, options = {}) {
|
|
226
|
+
const {
|
|
227
|
+
projectPath = process.cwd(),
|
|
228
|
+
onProgress,
|
|
229
|
+
timeoutMs = 600000,
|
|
230
|
+
autoApprove = false,
|
|
231
|
+
} = options;
|
|
232
|
+
|
|
233
|
+
const executionId = `claude-resume-${Date.now()}`;
|
|
234
|
+
const args = ['--resume', sessionId, '-p', prompt];
|
|
235
|
+
|
|
236
|
+
if (autoApprove) {
|
|
237
|
+
args.push('--dangerously-skip-permissions');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
args.push('--output-format', 'json');
|
|
241
|
+
|
|
242
|
+
return new Promise((resolve, reject) => {
|
|
243
|
+
const startTime = Date.now();
|
|
244
|
+
let stdout = '';
|
|
245
|
+
let stderr = '';
|
|
246
|
+
const toolCalls = [];
|
|
247
|
+
|
|
248
|
+
const proc = spawn('claude', args, {
|
|
249
|
+
cwd: projectPath,
|
|
250
|
+
env: {
|
|
251
|
+
...process.env,
|
|
252
|
+
TELEPORTATION_SESSION_ID: sessionId,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
this.#runningProcesses.set(executionId, proc);
|
|
257
|
+
|
|
258
|
+
const timeout = setTimeout(() => {
|
|
259
|
+
proc.kill('SIGTERM');
|
|
260
|
+
reject(new Error(`Resume timed out after ${timeoutMs}ms`));
|
|
261
|
+
}, timeoutMs);
|
|
262
|
+
|
|
263
|
+
proc.stdout.on('data', (data) => {
|
|
264
|
+
stdout += data.toString();
|
|
265
|
+
onProgress?.({
|
|
266
|
+
type: 'output',
|
|
267
|
+
timestamp: Date.now(),
|
|
268
|
+
data: { text: data.toString() },
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
proc.stderr.on('data', (data) => {
|
|
273
|
+
stderr += data.toString();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
proc.on('close', (code) => {
|
|
277
|
+
clearTimeout(timeout);
|
|
278
|
+
this.#runningProcesses.delete(executionId);
|
|
279
|
+
|
|
280
|
+
resolve({
|
|
281
|
+
success: code === 0,
|
|
282
|
+
output: stdout,
|
|
283
|
+
error: code !== 0 ? stderr || `Exit code: ${code}` : undefined,
|
|
284
|
+
toolCalls,
|
|
285
|
+
stats: { durationMs: Date.now() - startTime },
|
|
286
|
+
executionId,
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
proc.on('error', (err) => {
|
|
291
|
+
clearTimeout(timeout);
|
|
292
|
+
this.#runningProcesses.delete(executionId);
|
|
293
|
+
reject(err);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Stop a running execution
|
|
300
|
+
* @param {string} executionId
|
|
301
|
+
* @returns {Promise<boolean>}
|
|
302
|
+
*/
|
|
303
|
+
async stop(executionId) {
|
|
304
|
+
const proc = this.#runningProcesses.get(executionId);
|
|
305
|
+
if (proc) {
|
|
306
|
+
proc.kill('SIGTERM');
|
|
307
|
+
this.#runningProcesses.delete(executionId);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get capabilities
|
|
315
|
+
* @returns {Object}
|
|
316
|
+
*/
|
|
317
|
+
getCapabilities() {
|
|
318
|
+
return {
|
|
319
|
+
supportsResume: true,
|
|
320
|
+
supportsStreaming: true,
|
|
321
|
+
hasNativeApproval: true, // Via hooks
|
|
322
|
+
supportsModelSelection: true,
|
|
323
|
+
maxContextTokens: 200000, // Claude 3.5 Sonnet
|
|
324
|
+
supportedTools: ['Bash', 'Read', 'Edit', 'Write', 'Glob', 'Grep', 'LS', 'MultiEdit'],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export default ClaudeCodeAdapter;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Machine Coders Example / Test Script
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates the unified machine coder interface.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node lib/machine-coders/example.js status
|
|
9
|
+
* node lib/machine-coders/example.js list
|
|
10
|
+
* node lib/machine-coders/example.js run <coder> <prompt>
|
|
11
|
+
* node lib/machine-coders/example.js test
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { getAvailableCoders, getBestCoder } from './index.js';
|
|
15
|
+
import { ClaudeCodeAdapter } from './claude-code-adapter.js';
|
|
16
|
+
import { GeminiCliAdapter } from './gemini-cli-adapter.js';
|
|
17
|
+
|
|
18
|
+
const projectPath = process.cwd();
|
|
19
|
+
const sessionId = process.env.TELEPORT_SESSION_ID || `test-${Date.now()}`;
|
|
20
|
+
|
|
21
|
+
async function showStatus() {
|
|
22
|
+
console.log('=== Machine Coders Status ===\n');
|
|
23
|
+
|
|
24
|
+
const coders = [
|
|
25
|
+
new ClaudeCodeAdapter(),
|
|
26
|
+
new GeminiCliAdapter(),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const coder of coders) {
|
|
30
|
+
const status = await coder.getStatus();
|
|
31
|
+
const caps = coder.getCapabilities();
|
|
32
|
+
|
|
33
|
+
console.log(`${coder.displayName} (${coder.name}):`);
|
|
34
|
+
console.log(` Available: ${status.available ? '✓' : '✗'}`);
|
|
35
|
+
if (status.version) {
|
|
36
|
+
console.log(` Version: ${status.version}`);
|
|
37
|
+
}
|
|
38
|
+
if (status.models) {
|
|
39
|
+
console.log(` Models: ${status.models.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
console.log(` Capabilities:`);
|
|
42
|
+
console.log(` - Resume: ${caps.supportsResume ? '✓' : '✗'}`);
|
|
43
|
+
console.log(` - Streaming: ${caps.supportsStreaming ? '✓' : '✗'}`);
|
|
44
|
+
console.log(` - Native Approval: ${caps.hasNativeApproval ? '✓' : '✗'}`);
|
|
45
|
+
console.log(` - Model Selection: ${caps.supportsModelSelection ? '✓' : '✗'}`);
|
|
46
|
+
console.log(` - Max Context: ${(caps.maxContextTokens / 1000).toFixed(0)}K tokens`);
|
|
47
|
+
console.log(` - Tools: ${caps.supportedTools.join(', ')}`);
|
|
48
|
+
console.log('');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function listAvailable() {
|
|
53
|
+
console.log('=== Available Machine Coders ===\n');
|
|
54
|
+
|
|
55
|
+
const available = await getAvailableCoders();
|
|
56
|
+
|
|
57
|
+
if (available.length === 0) {
|
|
58
|
+
console.log('No machine coders available on this system.');
|
|
59
|
+
console.log('Install one of: claude, gemini, goose');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(`Found ${available.length} available coder(s):\n`);
|
|
64
|
+
|
|
65
|
+
for (const coder of available) {
|
|
66
|
+
const status = await coder.getStatus();
|
|
67
|
+
console.log(` • ${coder.displayName}`);
|
|
68
|
+
console.log(` Name: ${coder.name}`);
|
|
69
|
+
if (status.version) {
|
|
70
|
+
console.log(` Version: ${status.version}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log('');
|
|
75
|
+
|
|
76
|
+
// Show best coder
|
|
77
|
+
const best = await getBestCoder();
|
|
78
|
+
if (best) {
|
|
79
|
+
console.log(`Best coder (default priority): ${best.displayName}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const bestLargeContext = await getBestCoder({ largeContext: true });
|
|
83
|
+
if (bestLargeContext) {
|
|
84
|
+
console.log(`Best for large context: ${bestLargeContext.displayName}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function runCoder(coderName, prompt) {
|
|
89
|
+
console.log(`=== Running ${coderName} ===\n`);
|
|
90
|
+
console.log(`Project: ${projectPath}`);
|
|
91
|
+
console.log(`Session: ${sessionId}`);
|
|
92
|
+
console.log(`Prompt: ${prompt}`);
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
let coder;
|
|
96
|
+
switch (coderName) {
|
|
97
|
+
case 'claude':
|
|
98
|
+
case 'claude-code':
|
|
99
|
+
coder = new ClaudeCodeAdapter();
|
|
100
|
+
break;
|
|
101
|
+
case 'gemini':
|
|
102
|
+
case 'gemini-cli':
|
|
103
|
+
coder = new GeminiCliAdapter();
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
console.error(`Unknown coder: ${coderName}`);
|
|
107
|
+
console.log('Available: claude, gemini');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const available = await coder.isAvailable();
|
|
112
|
+
if (!available) {
|
|
113
|
+
console.error(`${coder.displayName} is not available on this system.`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`Using: ${coder.displayName}`);
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log('--- Output ---');
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const result = await coder.execute({
|
|
123
|
+
projectPath,
|
|
124
|
+
sessionId,
|
|
125
|
+
prompt,
|
|
126
|
+
autoApprove: true, // For testing
|
|
127
|
+
onProgress: (event) => {
|
|
128
|
+
switch (event.type) {
|
|
129
|
+
case 'init':
|
|
130
|
+
console.log(`[init] Session: ${event.data.sessionId}, Model: ${event.data.model}`);
|
|
131
|
+
break;
|
|
132
|
+
case 'message':
|
|
133
|
+
if (event.data.role === 'assistant') {
|
|
134
|
+
console.log(`[assistant] ${event.data.content?.substring(0, 200)}...`);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
case 'tool_use':
|
|
138
|
+
console.log(`[tool] ${event.data.tool}: ${JSON.stringify(event.data.input).substring(0, 100)}`);
|
|
139
|
+
break;
|
|
140
|
+
case 'tool_result':
|
|
141
|
+
console.log(`[result] ${event.data.status}: ${event.data.output?.substring(0, 100)}`);
|
|
142
|
+
break;
|
|
143
|
+
case 'error':
|
|
144
|
+
console.log(`[error] ${event.data.message}`);
|
|
145
|
+
break;
|
|
146
|
+
case 'done':
|
|
147
|
+
console.log(`[done] Tokens: ${event.data.tokensUsed}, Duration: ${event.data.durationMs}ms`);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
onToolCall: async (tool) => {
|
|
152
|
+
// For testing, auto-allow all tools
|
|
153
|
+
console.log(`[approval] Tool ${tool.tool} auto-allowed (test mode)`);
|
|
154
|
+
return 'allow';
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log('--- Result ---');
|
|
160
|
+
console.log(`Success: ${result.success}`);
|
|
161
|
+
console.log(`Tool Calls: ${result.toolCalls.length}`);
|
|
162
|
+
if (result.stats) {
|
|
163
|
+
console.log(`Tokens: ${result.stats.tokensUsed || 'N/A'}`);
|
|
164
|
+
console.log(`Duration: ${result.stats.durationMs}ms`);
|
|
165
|
+
console.log(`Model: ${result.stats.model || 'N/A'}`);
|
|
166
|
+
}
|
|
167
|
+
if (result.error) {
|
|
168
|
+
console.log(`Error: ${result.error}`);
|
|
169
|
+
}
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('Output:');
|
|
172
|
+
console.log(result.output?.substring(0, 500) || '(no output)');
|
|
173
|
+
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.error(`\nExecution failed: ${err.message}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function runTest() {
|
|
181
|
+
console.log('=== Machine Coders Test ===\n');
|
|
182
|
+
|
|
183
|
+
// Test 1: Check availability
|
|
184
|
+
console.log('Test 1: Checking availability...');
|
|
185
|
+
const available = await getAvailableCoders();
|
|
186
|
+
console.log(` Found ${available.length} available coder(s)`);
|
|
187
|
+
|
|
188
|
+
if (available.length === 0) {
|
|
189
|
+
console.log('\n✗ No coders available - cannot run further tests');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Test 2: Get best coder
|
|
194
|
+
console.log('\nTest 2: Getting best coder...');
|
|
195
|
+
const best = await getBestCoder();
|
|
196
|
+
console.log(` Best coder: ${best?.displayName || 'none'}`);
|
|
197
|
+
|
|
198
|
+
// Test 3: Get capabilities
|
|
199
|
+
console.log('\nTest 3: Checking capabilities...');
|
|
200
|
+
for (const coder of available) {
|
|
201
|
+
const caps = coder.getCapabilities();
|
|
202
|
+
console.log(` ${coder.displayName}:`);
|
|
203
|
+
console.log(` Max context: ${(caps.maxContextTokens / 1000).toFixed(0)}K`);
|
|
204
|
+
console.log(` Native approval: ${caps.hasNativeApproval}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log('\n✓ All tests passed');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Main
|
|
211
|
+
const command = process.argv[2] || 'status';
|
|
212
|
+
const arg1 = process.argv[3];
|
|
213
|
+
const arg2 = process.argv.slice(4).join(' ');
|
|
214
|
+
|
|
215
|
+
switch (command) {
|
|
216
|
+
case 'status':
|
|
217
|
+
await showStatus();
|
|
218
|
+
break;
|
|
219
|
+
case 'list':
|
|
220
|
+
await listAvailable();
|
|
221
|
+
break;
|
|
222
|
+
case 'run':
|
|
223
|
+
if (!arg1 || !arg2) {
|
|
224
|
+
console.log('Usage: node example.js run <coder> <prompt>');
|
|
225
|
+
console.log('Example: node example.js run gemini "List files in current directory"');
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
await runCoder(arg1, arg2);
|
|
229
|
+
break;
|
|
230
|
+
case 'test':
|
|
231
|
+
await runTest();
|
|
232
|
+
break;
|
|
233
|
+
default:
|
|
234
|
+
console.log('Usage:');
|
|
235
|
+
console.log(' node lib/machine-coders/example.js status');
|
|
236
|
+
console.log(' node lib/machine-coders/example.js list');
|
|
237
|
+
console.log(' node lib/machine-coders/example.js run <coder> <prompt>');
|
|
238
|
+
console.log(' node lib/machine-coders/example.js test');
|
|
239
|
+
}
|