orchestrating 0.1.3 → 0.1.4
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/bin/orch +90 -96
- package/package.json +1 -1
package/bin/orch
CHANGED
|
@@ -151,7 +151,6 @@ const ADAPTERS = {
|
|
|
151
151
|
buildArgs(prompt, flags) {
|
|
152
152
|
const args = [
|
|
153
153
|
"--output-format", "stream-json",
|
|
154
|
-
"--input-format", "stream-json",
|
|
155
154
|
"--verbose",
|
|
156
155
|
];
|
|
157
156
|
if (flags.continue) {
|
|
@@ -307,6 +306,7 @@ if (!adapter) {
|
|
|
307
306
|
|
|
308
307
|
let child;
|
|
309
308
|
let handleServerMessage; // set per-mode
|
|
309
|
+
let exitRequested = false;
|
|
310
310
|
|
|
311
311
|
if (adapter) {
|
|
312
312
|
// ======== STRUCTURED MODE (Claude Code JSON streaming) ========
|
|
@@ -328,84 +328,90 @@ if (adapter) {
|
|
|
328
328
|
process.exit(1);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
child
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
331
|
+
// Confirmation-type tools — these need "yes" response, not permission grants
|
|
332
|
+
const CONFIRMATION_TOOLS = new Set(["ExitPlanMode", "EnterPlanMode"]);
|
|
333
|
+
let childRunning = false;
|
|
334
|
+
|
|
335
|
+
// Build clean env for child processes (no nesting detection, no leaked keys)
|
|
336
|
+
const childEnv = (() => {
|
|
337
|
+
const e = { ...process.env };
|
|
338
|
+
delete e.CLAUDECODE;
|
|
339
|
+
delete e.ANTHROPIC_API_KEY;
|
|
340
|
+
return e;
|
|
341
|
+
})();
|
|
342
|
+
|
|
343
|
+
// Spawn a claude process and wire up output handling
|
|
344
|
+
function spawnClaude(claudeArgs) {
|
|
345
|
+
const proc = spawn(command, claudeArgs, {
|
|
346
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
347
|
+
cwd: process.cwd(),
|
|
348
|
+
env: childEnv,
|
|
349
|
+
});
|
|
350
|
+
childRunning = true;
|
|
351
|
+
child = proc;
|
|
348
352
|
|
|
349
|
-
|
|
350
|
-
|
|
353
|
+
// Parse NDJSON from stdout line-by-line
|
|
354
|
+
const rl = readline.createInterface({ input: proc.stdout });
|
|
355
|
+
let permissionsSent = false;
|
|
351
356
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
357
|
+
rl.on("line", (line) => {
|
|
358
|
+
if (!permissionsSent) {
|
|
359
|
+
permissionsSent = true;
|
|
360
|
+
broadcastPermissions();
|
|
361
|
+
}
|
|
362
|
+
if (!line.trim()) return;
|
|
363
|
+
let raw;
|
|
364
|
+
try {
|
|
365
|
+
raw = JSON.parse(line);
|
|
366
|
+
} catch {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
364
369
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
+
// Normalize and relay each event
|
|
371
|
+
const events = normalizeClaudeEvent(raw);
|
|
372
|
+
for (const event of events) {
|
|
373
|
+
printLocalEvent(event);
|
|
374
|
+
sendToServer({ type: "agent_event", sessionId, event });
|
|
370
375
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
376
|
+
// Yolo mode: auto-approve permission denials immediately
|
|
377
|
+
if (yoloMode && event.kind === "permission_denied") {
|
|
378
|
+
process.stderr.write(`${GREEN}[yolo] Auto-approving: ${event.toolName}${RESET}\n`);
|
|
379
|
+
approvePermission(event.toolName, "session");
|
|
380
|
+
}
|
|
375
381
|
}
|
|
376
|
-
}
|
|
377
|
-
});
|
|
382
|
+
});
|
|
378
383
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
384
|
+
proc.stderr.on("data", (buf) => {
|
|
385
|
+
process.stderr.write(buf);
|
|
386
|
+
});
|
|
382
387
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
+
proc.on("exit", (code) => {
|
|
389
|
+
childRunning = false;
|
|
390
|
+
const exitCode = code ?? 0;
|
|
391
|
+
if (exitCode !== 0 || exitRequested) {
|
|
392
|
+
// Non-zero exit or user interrupted — shut down
|
|
393
|
+
sendToServer({ type: "exit", sessionId, exitCode });
|
|
394
|
+
setTimeout(() => process.exit(exitCode), 200);
|
|
395
|
+
} else {
|
|
396
|
+
// Successful completion — stay alive for follow-up from dashboard
|
|
397
|
+
sendToServer({
|
|
398
|
+
type: "agent_event", sessionId,
|
|
399
|
+
event: { kind: "status", status: "idle" },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
});
|
|
388
403
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
404
|
+
proc.on("error", (err) => {
|
|
405
|
+
childRunning = false;
|
|
406
|
+
console.error("Failed to start:", err.message);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
});
|
|
393
409
|
|
|
394
|
-
|
|
395
|
-
|
|
410
|
+
return proc;
|
|
411
|
+
}
|
|
396
412
|
|
|
397
413
|
// Auto-approve a tool permission (used by yolo mode and manual approval)
|
|
398
414
|
function approvePermission(toolName, scope) {
|
|
399
|
-
// For confirmation prompts, just send "yes" — no settings change needed
|
|
400
|
-
if (CONFIRMATION_TOOLS.has(toolName)) {
|
|
401
|
-
const confirmMsg = JSON.stringify({
|
|
402
|
-
type: "user",
|
|
403
|
-
message: { role: "user", content: "yes" },
|
|
404
|
-
});
|
|
405
|
-
child.stdin.write(confirmMsg + "\n");
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
415
|
let permEntry = toolName;
|
|
410
416
|
const mcpMatch = toolName.match(/^(mcp__[^_]+(?:__[^_]+)?)__/);
|
|
411
417
|
if (mcpMatch) {
|
|
@@ -442,41 +448,28 @@ if (adapter) {
|
|
|
442
448
|
}
|
|
443
449
|
|
|
444
450
|
broadcastPermissions();
|
|
445
|
-
|
|
446
|
-
const retryMsg = JSON.stringify({
|
|
447
|
-
type: "user",
|
|
448
|
-
message: { role: "user", content: `Permission granted for ${toolName}. Please retry.` },
|
|
449
|
-
});
|
|
450
|
-
child.stdin.write(retryMsg + "\n");
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
//
|
|
454
|
-
|
|
453
|
+
// Start the initial claude process
|
|
454
|
+
const initialArgs = adapter.buildArgs(prompt, adapterFlags);
|
|
455
|
+
if (yoloMode) {
|
|
456
|
+
initialArgs.push("--dangerously-skip-permissions");
|
|
457
|
+
}
|
|
458
|
+
spawnClaude(initialArgs);
|
|
455
459
|
|
|
456
460
|
handleServerMessage = (msg) => {
|
|
457
461
|
if (msg.type === "agent_input" && (msg.text || msg.images)) {
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
if (msg.text) {
|
|
470
|
-
content.push({ type: "text", text: msg.text });
|
|
471
|
-
}
|
|
472
|
-
} else {
|
|
473
|
-
content = msg.text;
|
|
462
|
+
// Follow-up from dashboard — spawn new claude with -c (continue)
|
|
463
|
+
if (childRunning) {
|
|
464
|
+
// Child still running, queue or ignore
|
|
465
|
+
process.stderr.write(`${DIM}[orch] Ignoring input — claude is still running${RESET}\n`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const followUpArgs = ["--output-format", "stream-json", "--verbose", "-c", "-p", msg.text || "continue"];
|
|
469
|
+
if (yoloMode) {
|
|
470
|
+
followUpArgs.push("--dangerously-skip-permissions");
|
|
474
471
|
}
|
|
475
|
-
|
|
476
|
-
type: "user",
|
|
477
|
-
message: { role: "user", content },
|
|
478
|
-
});
|
|
479
|
-
child.stdin.write(inputMsg + "\n");
|
|
472
|
+
spawnClaude(followUpArgs);
|
|
480
473
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
|
|
481
474
|
const scope = msg.scope || "session";
|
|
482
475
|
approvePermission(msg.tool, scope);
|
|
@@ -855,6 +848,7 @@ function cleanup() {
|
|
|
855
848
|
|
|
856
849
|
process.on("SIGINT", () => {
|
|
857
850
|
if (adapter) {
|
|
851
|
+
exitRequested = true;
|
|
858
852
|
child.kill("SIGINT");
|
|
859
853
|
} else {
|
|
860
854
|
child.stdin.write("\x03");
|