orchestrating 0.3.2 → 0.4.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/bin/orch +79 -11
- package/package.json +1 -1
package/bin/orch
CHANGED
|
@@ -676,6 +676,43 @@ async function handleDaemon(projectsDir) {
|
|
|
676
676
|
}
|
|
677
677
|
}
|
|
678
678
|
|
|
679
|
+
if (msg.type === "update_cli") {
|
|
680
|
+
process.stderr.write(`${GREEN}${PREFIX} Update requested — running npm i -g orchestrating${RESET}\n`);
|
|
681
|
+
try {
|
|
682
|
+
const result = execSync("npm i -g orchestrating 2>&1", { encoding: "utf-8", timeout: 60_000 });
|
|
683
|
+
// Get the new version
|
|
684
|
+
let ver = "unknown";
|
|
685
|
+
try {
|
|
686
|
+
ver = execSync("npm list -g orchestrating --depth=0 2>/dev/null", { encoding: "utf-8" })
|
|
687
|
+
.match(/orchestrating@([\d.]+)/)?.[1] || "unknown";
|
|
688
|
+
} catch {}
|
|
689
|
+
process.stderr.write(`${GREEN}${PREFIX} Updated to v${ver}${RESET}\n`);
|
|
690
|
+
if (sock.readyState === WebSocket.OPEN) {
|
|
691
|
+
sock.send(JSON.stringify({
|
|
692
|
+
type: "update_result",
|
|
693
|
+
hostname,
|
|
694
|
+
success: true,
|
|
695
|
+
version: ver,
|
|
696
|
+
}));
|
|
697
|
+
}
|
|
698
|
+
// Restart daemon after a short delay so the result message gets sent
|
|
699
|
+
process.stderr.write(`${GREEN}${PREFIX} Restarting daemon...${RESET}\n`);
|
|
700
|
+
setTimeout(() => {
|
|
701
|
+
process.exit(0); // launchd/systemd will restart us with the new version
|
|
702
|
+
}, 1000);
|
|
703
|
+
} catch (err) {
|
|
704
|
+
process.stderr.write(`${RED}${PREFIX} Update failed: ${err.message}${RESET}\n`);
|
|
705
|
+
if (sock.readyState === WebSocket.OPEN) {
|
|
706
|
+
sock.send(JSON.stringify({
|
|
707
|
+
type: "update_result",
|
|
708
|
+
hostname,
|
|
709
|
+
success: false,
|
|
710
|
+
error: err.message,
|
|
711
|
+
}));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
679
716
|
if (msg.type === "browse_directory") {
|
|
680
717
|
const { requestId: browseReqId, path: dirPath } = msg;
|
|
681
718
|
try {
|
|
@@ -1020,6 +1057,7 @@ if (adapter) {
|
|
|
1020
1057
|
// Spawn claude with bidirectional stdin/stdout (stream-json mode)
|
|
1021
1058
|
// Follow-ups are sent via stdin — no more kill/respawn.
|
|
1022
1059
|
let claudeSessionId = null; // Claude's internal session ID (from init event)
|
|
1060
|
+
let pendingRespawn = null; // { prompt } — set when permission grant needs respawn
|
|
1023
1061
|
|
|
1024
1062
|
function spawnClaude(claudeArgs) {
|
|
1025
1063
|
const proc = spawn(command, claudeArgs, {
|
|
@@ -1067,8 +1105,8 @@ if (adapter) {
|
|
|
1067
1105
|
if (yoloMode && event.kind === "permission_denied") {
|
|
1068
1106
|
process.stderr.write(`${GREEN}[yolo] Auto-approving: ${event.toolName}${RESET}\n`);
|
|
1069
1107
|
approvePermission(event.toolName, "session");
|
|
1070
|
-
//
|
|
1071
|
-
|
|
1108
|
+
// Queue respawn — Claude will exit after this turn, then we respawn with -c
|
|
1109
|
+
pendingRespawn = { prompt: `Permission for ${event.toolName} was granted. Please retry your last action.` };
|
|
1072
1110
|
}
|
|
1073
1111
|
}
|
|
1074
1112
|
});
|
|
@@ -1080,8 +1118,23 @@ if (adapter) {
|
|
|
1080
1118
|
proc.on("exit", (code) => {
|
|
1081
1119
|
childRunning = false;
|
|
1082
1120
|
const exitCode = code ?? 0;
|
|
1083
|
-
|
|
1084
|
-
|
|
1121
|
+
|
|
1122
|
+
if (exitCode !== 0 || exitRequested) {
|
|
1123
|
+
// Non-zero exit or user requested stop — terminate
|
|
1124
|
+
sendToServer({ type: "exit", sessionId, exitCode });
|
|
1125
|
+
setTimeout(() => process.exit(exitCode), 200);
|
|
1126
|
+
} else if (pendingRespawn) {
|
|
1127
|
+
// Permission grant/deny triggered a respawn — do it now
|
|
1128
|
+
const { prompt: respawnPrompt } = pendingRespawn;
|
|
1129
|
+
pendingRespawn = null;
|
|
1130
|
+
respawnWithContinue(respawnPrompt);
|
|
1131
|
+
} else {
|
|
1132
|
+
// Normal exit (turn complete) — stay alive, emit idle, wait for follow-ups
|
|
1133
|
+
sendToServer({
|
|
1134
|
+
type: "agent_event", sessionId,
|
|
1135
|
+
event: { kind: "status", status: "idle" },
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1085
1138
|
});
|
|
1086
1139
|
|
|
1087
1140
|
proc.on("error", (err) => {
|
|
@@ -1246,10 +1299,9 @@ if (adapter) {
|
|
|
1246
1299
|
|
|
1247
1300
|
handleServerMessage = (msg) => {
|
|
1248
1301
|
if (msg.type === "agent_input" && (msg.text || msg.images)) {
|
|
1249
|
-
// Follow-up from dashboard
|
|
1302
|
+
// Follow-up from dashboard
|
|
1250
1303
|
if (childRunning) {
|
|
1251
1304
|
process.stderr.write(`${DIM}[orch] Queuing input — claude is still processing${RESET}\n`);
|
|
1252
|
-
// TODO: queue and send after current turn completes
|
|
1253
1305
|
return;
|
|
1254
1306
|
}
|
|
1255
1307
|
let promptText = msg.text || "continue";
|
|
@@ -1266,17 +1318,33 @@ if (adapter) {
|
|
|
1266
1318
|
const fileList = imagePaths.map((p) => `[Attached image: ${p} — use Read tool to view]`).join("\n");
|
|
1267
1319
|
promptText = `${fileList}\n\n${promptText}`;
|
|
1268
1320
|
}
|
|
1269
|
-
|
|
1321
|
+
// If process is still alive, send via stdin; otherwise respawn
|
|
1322
|
+
if (child && child.exitCode === null) {
|
|
1323
|
+
sendFollowUp(promptText);
|
|
1324
|
+
} else {
|
|
1325
|
+
respawnWithContinue(promptText);
|
|
1326
|
+
}
|
|
1270
1327
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
|
|
1271
1328
|
const scope = msg.scope || "session";
|
|
1272
1329
|
approvePermission(msg.tool, scope);
|
|
1273
1330
|
process.stderr.write(`${GREEN}Permission granted (${scope}): ${msg.tool}${RESET}\n`);
|
|
1274
|
-
// Permissions are file-based
|
|
1275
|
-
|
|
1276
|
-
|
|
1331
|
+
// Permissions are file-based — Claude needs restart to pick up settings.local.json
|
|
1332
|
+
const respawnPrompt = `Permission for ${msg.tool} was granted. Please retry your last action.`;
|
|
1333
|
+
if (child && child.exitCode === null) {
|
|
1334
|
+
// Process still running — queue respawn for when it exits
|
|
1335
|
+
pendingRespawn = { prompt: respawnPrompt };
|
|
1336
|
+
} else {
|
|
1337
|
+
// Process already exited — respawn now
|
|
1338
|
+
respawnWithContinue(respawnPrompt);
|
|
1339
|
+
}
|
|
1277
1340
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "deny") {
|
|
1278
1341
|
process.stderr.write(`${RED}Permission denied: ${msg.tool}${RESET}\n`);
|
|
1279
|
-
|
|
1342
|
+
const denyPrompt = `Permission for ${msg.tool} was denied by the user. Do not retry this tool — find an alternative approach or skip this step.`;
|
|
1343
|
+
if (child && child.exitCode === null) {
|
|
1344
|
+
pendingRespawn = { prompt: denyPrompt };
|
|
1345
|
+
} else {
|
|
1346
|
+
respawnWithContinue(denyPrompt);
|
|
1347
|
+
}
|
|
1280
1348
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "revoke") {
|
|
1281
1349
|
removePermission(msg.tool);
|
|
1282
1350
|
broadcastPermissions();
|