50c 3.9.2 → 4.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/README.md +1 -1
- package/bin/50c.js +2241 -2212
- package/lib/pre-publish.js +24 -5
- package/lib/subagent.js +369 -366
- package/package.json +3 -2
package/lib/subagent.js
CHANGED
|
@@ -1,366 +1,369 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* 50c Sub-Agent - Pure Node.js execution engine
|
|
4
|
-
* Bypasses IDE shell restrictions, works on ALL MCP-compatible IDEs
|
|
5
|
-
*
|
|
6
|
-
* Capabilities:
|
|
7
|
-
* - SSH remote execution (via ssh2)
|
|
8
|
-
* - HTTP/HTTPS requests (native)
|
|
9
|
-
* - File operations (native)
|
|
10
|
-
* - Process spawning (child_process)
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const { spawn, exec } = require('child_process');
|
|
14
|
-
const https = require('https');
|
|
15
|
-
const http = require('http');
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
'-o', '
|
|
118
|
-
'-o', '
|
|
119
|
-
|
|
120
|
-
'-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
case '
|
|
274
|
-
return
|
|
275
|
-
|
|
276
|
-
case '
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
case '
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 50c Sub-Agent - Pure Node.js execution engine
|
|
4
|
+
* Bypasses IDE shell restrictions, works on ALL MCP-compatible IDEs
|
|
5
|
+
*
|
|
6
|
+
* Capabilities:
|
|
7
|
+
* - SSH remote execution (via ssh2)
|
|
8
|
+
* - HTTP/HTTPS requests (native)
|
|
9
|
+
* - File operations (native)
|
|
10
|
+
* - Process spawning (child_process)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { spawn, exec } = require('child_process');
|
|
14
|
+
const https = require('https');
|
|
15
|
+
const http = require('http');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
// Known servers - loaded from ~/.50c/servers.json (not hardcoded for security)
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const SERVERS_FILE = path.join(os.homedir(), '.50c', 'servers.json');
|
|
22
|
+
|
|
23
|
+
function loadKnownServers() {
|
|
24
|
+
try {
|
|
25
|
+
if (fs.existsSync(SERVERS_FILE)) {
|
|
26
|
+
return JSON.parse(fs.readFileSync(SERVERS_FILE, 'utf8'));
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {}
|
|
29
|
+
// Empty by default - users must configure their own servers
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const KNOWN_SERVERS = loadKnownServers();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* HTTP/HTTPS fetch - no shell, pure Node
|
|
37
|
+
*/
|
|
38
|
+
async function httpFetch(url, options = {}) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const isHttps = url.startsWith('https');
|
|
41
|
+
const lib = isHttps ? https : http;
|
|
42
|
+
|
|
43
|
+
const urlObj = new URL(url);
|
|
44
|
+
const bodyData = options.body || '';
|
|
45
|
+
const reqOptions = {
|
|
46
|
+
hostname: urlObj.hostname,
|
|
47
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
48
|
+
path: urlObj.pathname + urlObj.search,
|
|
49
|
+
method: options.method || 'GET',
|
|
50
|
+
headers: {
|
|
51
|
+
...options.headers,
|
|
52
|
+
...(bodyData ? { 'Content-Length': Buffer.byteLength(bodyData) } : {})
|
|
53
|
+
},
|
|
54
|
+
timeout: options.timeout || 30000,
|
|
55
|
+
rejectUnauthorized: false // Allow self-signed certs
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const req = lib.request(reqOptions, (res) => {
|
|
59
|
+
let data = '';
|
|
60
|
+
res.on('data', chunk => data += chunk);
|
|
61
|
+
res.on('end', () => {
|
|
62
|
+
resolve({
|
|
63
|
+
status: res.statusCode,
|
|
64
|
+
headers: res.headers,
|
|
65
|
+
body: data,
|
|
66
|
+
ok: res.statusCode >= 200 && res.statusCode < 300
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
req.on('error', reject);
|
|
72
|
+
req.on('timeout', () => {
|
|
73
|
+
req.destroy();
|
|
74
|
+
reject(new Error('Request timeout'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (bodyData) {
|
|
78
|
+
req.write(bodyData);
|
|
79
|
+
}
|
|
80
|
+
req.end();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* SSH execution via native ssh command (spawned, not shell)
|
|
86
|
+
* Fixed: Uses explicit identity file for Windows compatibility
|
|
87
|
+
*/
|
|
88
|
+
async function sshExec(server, command, options = {}) {
|
|
89
|
+
const serverConfig = typeof server === 'string' ? KNOWN_SERVERS[server] : server;
|
|
90
|
+
|
|
91
|
+
if (!serverConfig) {
|
|
92
|
+
throw new Error(`Unknown server: ${server}. Known: ${Object.keys(KNOWN_SERVERS).join(', ')}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { host, user = 'root', port = 22 } = serverConfig;
|
|
96
|
+
const timeout = options.timeout || 30000;
|
|
97
|
+
|
|
98
|
+
// Try to find SSH key - common locations
|
|
99
|
+
const os = require('os');
|
|
100
|
+
const homeDir = os.homedir();
|
|
101
|
+
const keyPaths = [
|
|
102
|
+
path.join(homeDir, '.ssh', 'id_rsa'),
|
|
103
|
+
path.join(homeDir, '.ssh', 'id_ed25519'),
|
|
104
|
+
options.keyFile
|
|
105
|
+
].filter(Boolean);
|
|
106
|
+
|
|
107
|
+
let identityArgs = [];
|
|
108
|
+
for (const keyPath of keyPaths) {
|
|
109
|
+
if (fs.existsSync(keyPath)) {
|
|
110
|
+
identityArgs = ['-i', keyPath];
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const args = [
|
|
117
|
+
'-o', 'StrictHostKeyChecking=no',
|
|
118
|
+
'-o', 'UserKnownHostsFile=/dev/null',
|
|
119
|
+
'-o', 'ConnectTimeout=10',
|
|
120
|
+
'-o', 'ServerAliveInterval=5',
|
|
121
|
+
'-o', 'ServerAliveCountMax=2',
|
|
122
|
+
...identityArgs,
|
|
123
|
+
'-p', String(port),
|
|
124
|
+
`${user}@${host}`,
|
|
125
|
+
command
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
const proc = spawn('ssh', args, {
|
|
129
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
130
|
+
timeout,
|
|
131
|
+
windowsHide: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
let stdout = '';
|
|
135
|
+
let stderr = '';
|
|
136
|
+
|
|
137
|
+
proc.stdout.on('data', data => stdout += data.toString());
|
|
138
|
+
proc.stderr.on('data', data => stderr += data.toString());
|
|
139
|
+
|
|
140
|
+
const timer = setTimeout(() => {
|
|
141
|
+
proc.kill('SIGTERM');
|
|
142
|
+
reject(new Error(`SSH timeout after ${timeout}ms`));
|
|
143
|
+
}, timeout);
|
|
144
|
+
|
|
145
|
+
proc.on('close', code => {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
resolve({
|
|
148
|
+
ok: code === 0,
|
|
149
|
+
code,
|
|
150
|
+
stdout: stdout.trim(),
|
|
151
|
+
stderr: stderr.trim(),
|
|
152
|
+
server: serverConfig.name || host
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
proc.on('error', err => {
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
reject(err);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Local command execution (spawned, controlled)
|
|
165
|
+
*/
|
|
166
|
+
async function localExec(command, options = {}) {
|
|
167
|
+
const timeout = options.timeout || 30000;
|
|
168
|
+
const cwd = options.cwd || process.cwd();
|
|
169
|
+
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
exec(command, { timeout, cwd, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
172
|
+
resolve({
|
|
173
|
+
ok: !error,
|
|
174
|
+
code: error ? error.code || 1 : 0,
|
|
175
|
+
stdout: stdout.trim(),
|
|
176
|
+
stderr: stderr.trim(),
|
|
177
|
+
command
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* File operations
|
|
185
|
+
*/
|
|
186
|
+
const fileOps = {
|
|
187
|
+
read: (filePath) => {
|
|
188
|
+
try {
|
|
189
|
+
return { ok: true, content: fs.readFileSync(filePath, 'utf8') };
|
|
190
|
+
} catch (e) {
|
|
191
|
+
return { ok: false, error: e.message };
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
write: (filePath, content) => {
|
|
196
|
+
try {
|
|
197
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
198
|
+
fs.writeFileSync(filePath, content);
|
|
199
|
+
return { ok: true, path: filePath };
|
|
200
|
+
} catch (e) {
|
|
201
|
+
return { ok: false, error: e.message };
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
exists: (filePath) => ({ ok: true, exists: fs.existsSync(filePath) }),
|
|
206
|
+
|
|
207
|
+
list: (dirPath) => {
|
|
208
|
+
try {
|
|
209
|
+
return { ok: true, files: fs.readdirSync(dirPath) };
|
|
210
|
+
} catch (e) {
|
|
211
|
+
return { ok: false, error: e.message };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 50c MCP Tool calling - call any 50c tool via API
|
|
218
|
+
*/
|
|
219
|
+
const API_ENDPOINT = process.env.FIFTYC_ENDPOINT || 'https://api.50c.ai';
|
|
220
|
+
|
|
221
|
+
async function call50cTool(toolName, toolArgs, apiKey) {
|
|
222
|
+
const key = apiKey || process.env.FIFTYC_API_KEY || loadApiKey();
|
|
223
|
+
|
|
224
|
+
if (!key) {
|
|
225
|
+
return { ok: false, error: 'No API key. Set FIFTYC_API_KEY or run: 50c config key <key>' };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const response = await httpFetch(`${API_ENDPOINT}/tools/${toolName}`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
'Authorization': `Bearer ${key}`
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify(toolArgs || {}),
|
|
236
|
+
timeout: 120000 // 2 min for genius+
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
return { ok: false, error: `HTTP ${response.status}`, body: response.body };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const result = JSON.parse(response.body);
|
|
244
|
+
if (result.error) {
|
|
245
|
+
return { ok: false, error: result.error };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return { ok: true, result: result.result || result };
|
|
249
|
+
} catch (e) {
|
|
250
|
+
return { ok: false, error: e.message };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function loadApiKey() {
|
|
255
|
+
try {
|
|
256
|
+
const configPath = path.join(require('os').homedir(), '.50c', 'config.json');
|
|
257
|
+
if (fs.existsSync(configPath)) {
|
|
258
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
259
|
+
return config.api_key;
|
|
260
|
+
}
|
|
261
|
+
} catch {}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Main sub-agent dispatcher
|
|
267
|
+
*/
|
|
268
|
+
async function subagent(task) {
|
|
269
|
+
const { type, ...params } = task;
|
|
270
|
+
|
|
271
|
+
switch (type) {
|
|
272
|
+
case 'http':
|
|
273
|
+
case 'fetch':
|
|
274
|
+
return httpFetch(params.url, params);
|
|
275
|
+
|
|
276
|
+
case 'ssh':
|
|
277
|
+
return sshExec(params.server, params.command, params);
|
|
278
|
+
|
|
279
|
+
case 'local':
|
|
280
|
+
case 'exec':
|
|
281
|
+
return localExec(params.command, params);
|
|
282
|
+
|
|
283
|
+
case 'file':
|
|
284
|
+
const op = fileOps[params.op];
|
|
285
|
+
if (!op) throw new Error(`Unknown file op: ${params.op}`);
|
|
286
|
+
return op(params.path, params.content);
|
|
287
|
+
|
|
288
|
+
case 'multi':
|
|
289
|
+
// Parallel execution of multiple tasks
|
|
290
|
+
const results = await Promise.all(
|
|
291
|
+
params.tasks.map(t => subagent(t).catch(e => ({ ok: false, error: e.message })))
|
|
292
|
+
);
|
|
293
|
+
return { ok: results.every(r => r.ok), results };
|
|
294
|
+
|
|
295
|
+
case 'sequence':
|
|
296
|
+
// Sequential execution, stops on first failure
|
|
297
|
+
const seqResults = [];
|
|
298
|
+
for (const t of params.tasks) {
|
|
299
|
+
const result = await subagent(t).catch(e => ({ ok: false, error: e.message }));
|
|
300
|
+
seqResults.push(result);
|
|
301
|
+
if (!result.ok && params.stopOnError !== false) break;
|
|
302
|
+
}
|
|
303
|
+
return { ok: seqResults.every(r => r.ok), results: seqResults };
|
|
304
|
+
|
|
305
|
+
case 'servers':
|
|
306
|
+
// List known servers
|
|
307
|
+
return { ok: true, servers: KNOWN_SERVERS };
|
|
308
|
+
|
|
309
|
+
case '50c':
|
|
310
|
+
case 'tool':
|
|
311
|
+
// Call 50c MCP tool
|
|
312
|
+
return call50cTool(params.tool, params.args || {}, params.api_key);
|
|
313
|
+
|
|
314
|
+
case 'chain':
|
|
315
|
+
// Chain multiple 50c tools (output of one feeds into next)
|
|
316
|
+
let chainResult = params.input || {};
|
|
317
|
+
const chainSteps = [];
|
|
318
|
+
for (const step of params.steps) {
|
|
319
|
+
const args = { ...step.args };
|
|
320
|
+
// Inject previous result if placeholder exists
|
|
321
|
+
for (const [k, v] of Object.entries(args)) {
|
|
322
|
+
if (v === '$prev' || v === '$result') {
|
|
323
|
+
args[k] = typeof chainResult === 'string' ? chainResult : JSON.stringify(chainResult);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const stepResult = await call50cTool(step.tool, args, params.api_key);
|
|
327
|
+
chainSteps.push({ tool: step.tool, result: stepResult });
|
|
328
|
+
if (!stepResult.ok) {
|
|
329
|
+
return { ok: false, error: `Chain failed at ${step.tool}`, steps: chainSteps };
|
|
330
|
+
}
|
|
331
|
+
chainResult = stepResult.result;
|
|
332
|
+
}
|
|
333
|
+
return { ok: true, result: chainResult, steps: chainSteps };
|
|
334
|
+
|
|
335
|
+
default:
|
|
336
|
+
throw new Error(`Unknown task type: ${type}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Export for MCP integration
|
|
341
|
+
module.exports = {
|
|
342
|
+
subagent,
|
|
343
|
+
httpFetch,
|
|
344
|
+
sshExec,
|
|
345
|
+
localExec,
|
|
346
|
+
fileOps,
|
|
347
|
+
call50cTool,
|
|
348
|
+
KNOWN_SERVERS
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// CLI mode
|
|
352
|
+
if (require.main === module) {
|
|
353
|
+
const args = process.argv.slice(2);
|
|
354
|
+
if (args.length === 0) {
|
|
355
|
+
console.log('50c Sub-Agent - Pure Node execution engine');
|
|
356
|
+
console.log('Usage: node subagent.js <json-task>');
|
|
357
|
+
console.log('Example: node subagent.js \'{"type":"http","url":"https://api.50c.ai/health"}\'');
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const task = JSON.parse(args[0]);
|
|
363
|
+
subagent(task)
|
|
364
|
+
.then(r => console.log(JSON.stringify(r, null, 2)))
|
|
365
|
+
.catch(e => console.error(JSON.stringify({ ok: false, error: e.message })));
|
|
366
|
+
} catch (e) {
|
|
367
|
+
console.error(JSON.stringify({ ok: false, error: `Invalid JSON: ${e.message}` }));
|
|
368
|
+
}
|
|
369
|
+
}
|