orchestrating 0.1.6 → 0.1.9

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 (2) hide show
  1. package/bin/orch +54 -29
  2. package/package.json +1 -1
package/bin/orch CHANGED
@@ -342,7 +342,6 @@ if (adapter) {
342
342
 
343
343
  // Spawn a claude process and wire up output handling
344
344
  function spawnClaude(claudeArgs) {
345
- process.stderr.write(`${DIM}[orch] Spawning: ${command} ${claudeArgs.join(" ")}${RESET}\n`);
346
345
  const proc = spawn(command, claudeArgs, {
347
346
  stdio: ["ignore", "pipe", "pipe"],
348
347
  cwd: process.cwd(),
@@ -350,16 +349,12 @@ if (adapter) {
350
349
  });
351
350
  childRunning = true;
352
351
  child = proc;
353
- process.stderr.write(`${DIM}[orch] Child PID: ${proc.pid}${RESET}\n`);
354
352
 
355
353
  // Parse NDJSON from stdout line-by-line
356
354
  const rl = readline.createInterface({ input: proc.stdout });
357
355
  let permissionsSent = false;
358
- let lineCount = 0;
359
356
 
360
357
  rl.on("line", (line) => {
361
- lineCount++;
362
- process.stderr.write(`${DIM}[orch] stdout line #${lineCount}: ${line.slice(0, 120)}${RESET}\n`);
363
358
  if (!permissionsSent) {
364
359
  permissionsSent = true;
365
360
  broadcastPermissions();
@@ -369,41 +364,41 @@ if (adapter) {
369
364
  try {
370
365
  raw = JSON.parse(line);
371
366
  } catch {
372
- process.stderr.write(`${DIM}[orch] JSON parse failed for line${RESET}\n`);
373
367
  return;
374
368
  }
375
369
 
376
370
  // Normalize and relay each event
377
371
  const events = normalizeClaudeEvent(raw);
378
- process.stderr.write(`${DIM}[orch] Normalized ${events.length} events (type=${raw.type})${RESET}\n`);
379
372
  for (const event of events) {
380
373
  printLocalEvent(event);
381
374
  sendToServer({ type: "agent_event", sessionId, event });
382
375
 
383
- // Yolo mode: auto-approve permission denials immediately
376
+ // Yolo mode: auto-approve permission denials — save to settings,
377
+ // claude will pick it up on respawn via pendingPermissionGrant
384
378
  if (yoloMode && event.kind === "permission_denied") {
385
379
  process.stderr.write(`${GREEN}[yolo] Auto-approving: ${event.toolName}${RESET}\n`);
386
380
  approvePermission(event.toolName, "session");
381
+ pendingPermissionGrant = event.toolName;
387
382
  }
388
383
  }
389
384
  });
390
385
 
391
- proc.stdout.on("end", () => {
392
- process.stderr.write(`${DIM}[orch] stdout stream ended (${lineCount} lines total)${RESET}\n`);
393
- });
394
-
395
386
  proc.stderr.on("data", (buf) => {
396
- process.stderr.write(`${DIM}[orch] child stderr: ${RESET}`);
397
387
  process.stderr.write(buf);
398
388
  });
399
389
 
400
- proc.on("exit", (code, signal) => {
390
+ proc.on("exit", (code) => {
401
391
  childRunning = false;
402
- process.stderr.write(`${DIM}[orch] Child exited: code=${code} signal=${signal}${RESET}\n`);
403
392
  const exitCode = code ?? 0;
404
393
  if (exitCode !== 0 || exitRequested) {
405
394
  sendToServer({ type: "exit", sessionId, exitCode });
406
395
  setTimeout(() => process.exit(exitCode), 200);
396
+ } else if (pendingPermissionGrant) {
397
+ // Permission was granted while claude was running — respawn to retry
398
+ const tool = pendingPermissionGrant;
399
+ pendingPermissionGrant = null;
400
+ process.stderr.write(`${GREEN}Retrying with new permission: ${tool}${RESET}\n`);
401
+ respawnWithContinue(`Permission for ${tool} was granted. Please retry your last action.`);
407
402
  } else {
408
403
  sendToServer({
409
404
  type: "agent_event", sessionId,
@@ -414,7 +409,7 @@ if (adapter) {
414
409
 
415
410
  proc.on("error", (err) => {
416
411
  childRunning = false;
417
- process.stderr.write(`${RED}[orch] Spawn error: ${err.message}${RESET}\n`);
412
+ console.error("Failed to start:", err.message);
418
413
  process.exit(1);
419
414
  });
420
415
 
@@ -468,23 +463,45 @@ if (adapter) {
468
463
  }
469
464
  spawnClaude(initialArgs);
470
465
 
466
+ // Emit the initial prompt as a user message so the dashboard shows it
467
+ if (prompt) {
468
+ sendToServer({
469
+ type: "agent_event", sessionId,
470
+ event: { kind: "user_message", text: prompt },
471
+ });
472
+ }
473
+
474
+ // Respawn claude with -c to continue after permission grants or follow-up
475
+ function respawnWithContinue(prompt) {
476
+ const args = ["--output-format", "stream-json", "--verbose", "-c"];
477
+ if (prompt) args.push("-p", prompt);
478
+ if (yoloMode) args.push("--dangerously-skip-permissions");
479
+ spawnClaude(args);
480
+ }
481
+
482
+ // Queue of permission grants received while claude is still running
483
+ let pendingPermissionGrant = null;
484
+
471
485
  handleServerMessage = (msg) => {
472
486
  if (msg.type === "agent_input" && (msg.text || msg.images)) {
473
487
  // Follow-up from dashboard — spawn new claude with -c (continue)
474
488
  if (childRunning) {
475
- // Child still running, queue or ignore
476
489
  process.stderr.write(`${DIM}[orch] Ignoring input — claude is still running${RESET}\n`);
477
490
  return;
478
491
  }
479
- const followUpArgs = ["--output-format", "stream-json", "--verbose", "-c", "-p", msg.text || "continue"];
480
- if (yoloMode) {
481
- followUpArgs.push("--dangerously-skip-permissions");
482
- }
483
- spawnClaude(followUpArgs);
492
+ respawnWithContinue(msg.text || "continue");
484
493
  } else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
485
494
  const scope = msg.scope || "session";
486
495
  approvePermission(msg.tool, scope);
487
496
  process.stderr.write(`${GREEN}Permission granted (${scope}): ${msg.tool}${RESET}\n`);
497
+
498
+ if (childRunning) {
499
+ // Claude is still running — remember the grant and respawn when it exits
500
+ pendingPermissionGrant = msg.tool;
501
+ } else {
502
+ // Claude already finished — respawn with -c to retry with new permission
503
+ respawnWithContinue(`Permission for ${msg.tool} was granted. Please retry your last action.`);
504
+ }
488
505
  } else if (msg.type === "agent_permission" && msg.tool && msg.action === "revoke") {
489
506
  removePermission(msg.tool);
490
507
  broadcastPermissions();
@@ -535,7 +552,18 @@ if (adapter) {
535
552
  if (process.stdin.isTTY) {
536
553
  process.stdin.setRawMode(false);
537
554
  }
538
- setTimeout(() => process.exit(exitCode), 200);
555
+ // Wait for WS to connect and flush buffer before exiting
556
+ // (fast commands like `ls` may finish before the WS handshake)
557
+ function tryExit() {
558
+ if (wsReady && sendBuffer.length === 0) {
559
+ setTimeout(() => process.exit(exitCode), 100);
560
+ } else {
561
+ setTimeout(tryExit, 50);
562
+ }
563
+ }
564
+ // Give up after 3 seconds regardless
565
+ setTimeout(() => process.exit(exitCode), 3000);
566
+ tryExit();
539
567
  });
540
568
 
541
569
  child.on("error", (err) => {
@@ -770,8 +798,6 @@ function connectWs() {
770
798
  ws = new WebSocket(serverUrl);
771
799
 
772
800
  ws.on("open", () => {
773
- process.stderr.write(`${DIM}[orch] WS connected to ${serverUrl}${RESET}\n`);
774
- process.stderr.write(`${DIM}[orch] Token: ${authToken ? authToken.slice(0, 20) + "..." : "(none)"}${RESET}\n`);
775
801
  ws.send(JSON.stringify({
776
802
  type: "register",
777
803
  token: authToken,
@@ -816,8 +842,7 @@ function connectWs() {
816
842
  } catch {}
817
843
  });
818
844
 
819
- ws.on("close", (code, reason) => {
820
- process.stderr.write(`${DIM}[orch] WS closed: code=${code} reason=${reason}${RESET}\n`);
845
+ ws.on("close", () => {
821
846
  wsReady = false;
822
847
  ws = null;
823
848
  if (!authFailed) {
@@ -825,8 +850,8 @@ function connectWs() {
825
850
  }
826
851
  });
827
852
 
828
- ws.on("error", (err) => {
829
- process.stderr.write(`${DIM}[orch] WS error: ${err.message}${RESET}\n`);
853
+ ws.on("error", () => {
854
+ // Will trigger close
830
855
  });
831
856
  }
832
857
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrating",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Stream terminal sessions to the orchestrat.ing dashboard",
5
5
  "type": "module",
6
6
  "bin": {