orchestrating 0.1.2 → 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 +93 -100
- package/package.json +1 -1
package/bin/orch
CHANGED
|
@@ -89,8 +89,8 @@ async function handleLogin() {
|
|
|
89
89
|
refresh_token: refreshToken || "",
|
|
90
90
|
expires_at: expiresAt ? Number(expiresAt) : 0,
|
|
91
91
|
});
|
|
92
|
-
res.writeHead(
|
|
93
|
-
res.end();
|
|
92
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
93
|
+
res.end('<html><head><meta http-equiv="refresh" content="0;url=https://app.orchestrat.ing"></head><body><p>Redirecting to dashboard...</p></body></html>');
|
|
94
94
|
console.log("\x1b[32mLogged in successfully.\x1b[0m");
|
|
95
95
|
} else {
|
|
96
96
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
@@ -98,8 +98,7 @@ async function handleLogin() {
|
|
|
98
98
|
console.error("Authentication failed — no token received.");
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
server.close();
|
|
102
|
-
resolve();
|
|
101
|
+
setTimeout(() => { server.close(); resolve(); }, 500);
|
|
103
102
|
} else {
|
|
104
103
|
res.writeHead(404);
|
|
105
104
|
res.end();
|
|
@@ -152,7 +151,6 @@ const ADAPTERS = {
|
|
|
152
151
|
buildArgs(prompt, flags) {
|
|
153
152
|
const args = [
|
|
154
153
|
"--output-format", "stream-json",
|
|
155
|
-
"--input-format", "stream-json",
|
|
156
154
|
"--verbose",
|
|
157
155
|
];
|
|
158
156
|
if (flags.continue) {
|
|
@@ -308,6 +306,7 @@ if (!adapter) {
|
|
|
308
306
|
|
|
309
307
|
let child;
|
|
310
308
|
let handleServerMessage; // set per-mode
|
|
309
|
+
let exitRequested = false;
|
|
311
310
|
|
|
312
311
|
if (adapter) {
|
|
313
312
|
// ======== STRUCTURED MODE (Claude Code JSON streaming) ========
|
|
@@ -329,84 +328,90 @@ if (adapter) {
|
|
|
329
328
|
process.exit(1);
|
|
330
329
|
}
|
|
331
330
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
child
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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;
|
|
349
352
|
|
|
350
|
-
|
|
351
|
-
|
|
353
|
+
// Parse NDJSON from stdout line-by-line
|
|
354
|
+
const rl = readline.createInterface({ input: proc.stdout });
|
|
355
|
+
let permissionsSent = false;
|
|
352
356
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
+
}
|
|
365
369
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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 });
|
|
371
375
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
+
}
|
|
376
381
|
}
|
|
377
|
-
}
|
|
378
|
-
});
|
|
382
|
+
});
|
|
379
383
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
proc.stderr.on("data", (buf) => {
|
|
385
|
+
process.stderr.write(buf);
|
|
386
|
+
});
|
|
383
387
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
+
});
|
|
389
403
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
404
|
+
proc.on("error", (err) => {
|
|
405
|
+
childRunning = false;
|
|
406
|
+
console.error("Failed to start:", err.message);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
});
|
|
394
409
|
|
|
395
|
-
|
|
396
|
-
|
|
410
|
+
return proc;
|
|
411
|
+
}
|
|
397
412
|
|
|
398
413
|
// Auto-approve a tool permission (used by yolo mode and manual approval)
|
|
399
414
|
function approvePermission(toolName, scope) {
|
|
400
|
-
// For confirmation prompts, just send "yes" — no settings change needed
|
|
401
|
-
if (CONFIRMATION_TOOLS.has(toolName)) {
|
|
402
|
-
const confirmMsg = JSON.stringify({
|
|
403
|
-
type: "user",
|
|
404
|
-
message: { role: "user", content: "yes" },
|
|
405
|
-
});
|
|
406
|
-
child.stdin.write(confirmMsg + "\n");
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
415
|
let permEntry = toolName;
|
|
411
416
|
const mcpMatch = toolName.match(/^(mcp__[^_]+(?:__[^_]+)?)__/);
|
|
412
417
|
if (mcpMatch) {
|
|
@@ -443,41 +448,28 @@ if (adapter) {
|
|
|
443
448
|
}
|
|
444
449
|
|
|
445
450
|
broadcastPermissions();
|
|
446
|
-
|
|
447
|
-
const retryMsg = JSON.stringify({
|
|
448
|
-
type: "user",
|
|
449
|
-
message: { role: "user", content: `Permission granted for ${toolName}. Please retry.` },
|
|
450
|
-
});
|
|
451
|
-
child.stdin.write(retryMsg + "\n");
|
|
452
451
|
}
|
|
453
452
|
|
|
454
|
-
//
|
|
455
|
-
|
|
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);
|
|
456
459
|
|
|
457
460
|
handleServerMessage = (msg) => {
|
|
458
461
|
if (msg.type === "agent_input" && (msg.text || msg.images)) {
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
if (msg.text) {
|
|
471
|
-
content.push({ type: "text", text: msg.text });
|
|
472
|
-
}
|
|
473
|
-
} else {
|
|
474
|
-
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");
|
|
475
471
|
}
|
|
476
|
-
|
|
477
|
-
type: "user",
|
|
478
|
-
message: { role: "user", content },
|
|
479
|
-
});
|
|
480
|
-
child.stdin.write(inputMsg + "\n");
|
|
472
|
+
spawnClaude(followUpArgs);
|
|
481
473
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
|
|
482
474
|
const scope = msg.scope || "session";
|
|
483
475
|
approvePermission(msg.tool, scope);
|
|
@@ -856,6 +848,7 @@ function cleanup() {
|
|
|
856
848
|
|
|
857
849
|
process.on("SIGINT", () => {
|
|
858
850
|
if (adapter) {
|
|
851
|
+
exitRequested = true;
|
|
859
852
|
child.kill("SIGINT");
|
|
860
853
|
} else {
|
|
861
854
|
child.stdin.write("\x03");
|