sisyphi 1.0.5 → 1.0.6
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/dist/chunk-6G226ZK7.js +11 -0
- package/dist/chunk-6G226ZK7.js.map +1 -0
- package/dist/{chunk-YGBGKMTF.js → chunk-JXKUI4P6.js} +28 -1
- package/dist/{chunk-YGBGKMTF.js.map → chunk-JXKUI4P6.js.map} +1 -1
- package/dist/chunk-LWWRGQWM.js +87 -0
- package/dist/chunk-LWWRGQWM.js.map +1 -0
- package/dist/{chunk-DBR33QHM.js → chunk-T7ETTIQK.js} +81 -2
- package/dist/chunk-T7ETTIQK.js.map +1 -0
- package/dist/cli.js +241 -133
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +322 -330
- package/dist/daemon.js.map +1 -1
- package/dist/{paths-FYYSBD27.js → paths-NUUALUVP.js} +2 -2
- package/dist/tui.js +828 -836
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DBR33QHM.js.map +0 -1
- package/dist/chunk-KQBSC5KY.js +0 -31
- package/dist/chunk-KQBSC5KY.js.map +0 -1
- /package/dist/{paths-FYYSBD27.js.map → paths-NUUALUVP.js.map} +0 -0
package/dist/daemon.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
EXEC_ENV,
|
|
4
|
+
exec,
|
|
5
|
+
execEnv,
|
|
6
|
+
execSafe,
|
|
3
7
|
loadConfig
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LWWRGQWM.js";
|
|
9
|
+
import {
|
|
10
|
+
shellQuote
|
|
11
|
+
} from "./chunk-6G226ZK7.js";
|
|
5
12
|
import {
|
|
6
13
|
contextDir,
|
|
7
14
|
cycleLogPath,
|
|
@@ -25,21 +32,21 @@ import {
|
|
|
25
32
|
statePath,
|
|
26
33
|
worktreeBaseDir,
|
|
27
34
|
worktreeConfigPath
|
|
28
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-JXKUI4P6.js";
|
|
29
36
|
|
|
30
37
|
// src/daemon/index.ts
|
|
31
|
-
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as
|
|
38
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3, existsSync as existsSync9 } from "fs";
|
|
32
39
|
import { execSync as execSync4 } from "child_process";
|
|
33
40
|
import { setTimeout as sleep } from "timers/promises";
|
|
34
41
|
|
|
35
42
|
// src/daemon/server.ts
|
|
36
43
|
import { createServer } from "net";
|
|
37
|
-
import { unlinkSync, existsSync as
|
|
44
|
+
import { unlinkSync, existsSync as existsSync8, writeFileSync as writeFileSync5, readFileSync as readFileSync6, mkdirSync as mkdirSync4, rmSync as rmSync4 } from "fs";
|
|
38
45
|
import { join as join5 } from "path";
|
|
39
46
|
|
|
40
47
|
// src/daemon/session-manager.ts
|
|
41
48
|
import { v4 as uuidv4 } from "uuid";
|
|
42
|
-
import { existsSync as
|
|
49
|
+
import { existsSync as existsSync7, readdirSync as readdirSync6, rmSync as rmSync3 } from "fs";
|
|
43
50
|
|
|
44
51
|
// src/daemon/state.ts
|
|
45
52
|
import { randomUUID } from "crypto";
|
|
@@ -57,16 +64,16 @@ Agents save exploration findings, architectural notes, and reference material he
|
|
|
57
64
|
var sessionLocks = /* @__PURE__ */ new Map();
|
|
58
65
|
async function withSessionLock(sessionId, fn) {
|
|
59
66
|
const prev = sessionLocks.get(sessionId) ?? Promise.resolve();
|
|
60
|
-
let
|
|
67
|
+
let resolve5;
|
|
61
68
|
const next = new Promise((r) => {
|
|
62
|
-
|
|
69
|
+
resolve5 = r;
|
|
63
70
|
});
|
|
64
71
|
sessionLocks.set(sessionId, next);
|
|
65
72
|
await prev;
|
|
66
73
|
try {
|
|
67
74
|
return fn();
|
|
68
75
|
} finally {
|
|
69
|
-
|
|
76
|
+
resolve5();
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
function atomicWrite(filePath, data) {
|
|
@@ -293,8 +300,35 @@ function deleteSnapshotsAfter(cwd, sessionId, afterCycle) {
|
|
|
293
300
|
}
|
|
294
301
|
|
|
295
302
|
// src/daemon/orchestrator.ts
|
|
296
|
-
import { existsSync as
|
|
297
|
-
import {
|
|
303
|
+
import { existsSync as existsSync4, readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
304
|
+
import { execSync } from "child_process";
|
|
305
|
+
import { resolve as resolve2, join as join3 } from "path";
|
|
306
|
+
|
|
307
|
+
// src/daemon/spawn-helpers.ts
|
|
308
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
309
|
+
import { resolve } from "path";
|
|
310
|
+
function resolveCliBin() {
|
|
311
|
+
return resolve(import.meta.dirname, "cli.js");
|
|
312
|
+
}
|
|
313
|
+
function resolveNpmBinDir() {
|
|
314
|
+
return resolve(import.meta.dirname, "../../.bin");
|
|
315
|
+
}
|
|
316
|
+
function resolveBannerCmd() {
|
|
317
|
+
const bannerPath = resolve(import.meta.dirname, "../templates/banner.txt");
|
|
318
|
+
return existsSync2(bannerPath) ? `cat '${bannerPath}'` : null;
|
|
319
|
+
}
|
|
320
|
+
function buildEnvExports(statements) {
|
|
321
|
+
return statements.join(" && ");
|
|
322
|
+
}
|
|
323
|
+
function buildNotifyCmd(paneId) {
|
|
324
|
+
const cliBin = resolveCliBin();
|
|
325
|
+
return `node "${cliBin}" notify pane-exited --pane-id ${paneId}`;
|
|
326
|
+
}
|
|
327
|
+
function writeRunScript(dir, name, lines) {
|
|
328
|
+
const scriptPath = `${dir}/${name}.sh`;
|
|
329
|
+
writeFileSync2(scriptPath, lines.join("\n"), { mode: 493 });
|
|
330
|
+
return scriptPath;
|
|
331
|
+
}
|
|
298
332
|
|
|
299
333
|
// src/daemon/colors.ts
|
|
300
334
|
var ORCHESTRATOR_COLOR = "yellow";
|
|
@@ -318,7 +352,7 @@ function resetColors(sessionId) {
|
|
|
318
352
|
}
|
|
319
353
|
|
|
320
354
|
// src/daemon/frontmatter.ts
|
|
321
|
-
import { readFileSync as readFileSync2, existsSync as
|
|
355
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
|
|
322
356
|
import { homedir } from "os";
|
|
323
357
|
import { join as join2, basename } from "path";
|
|
324
358
|
function detectProvider(model) {
|
|
@@ -386,7 +420,7 @@ function resolveAgentTypePath(agentType, pluginDir, cwd) {
|
|
|
386
420
|
searchPaths.push(join2(pluginDir, "agents", `${name}.md`));
|
|
387
421
|
}
|
|
388
422
|
for (const path of searchPaths) {
|
|
389
|
-
if (
|
|
423
|
+
if (existsSync3(path)) return path;
|
|
390
424
|
}
|
|
391
425
|
return null;
|
|
392
426
|
}
|
|
@@ -452,21 +486,6 @@ function resolveAgentConfig(agentType, pluginDir, cwd) {
|
|
|
452
486
|
}
|
|
453
487
|
|
|
454
488
|
// src/daemon/tmux.ts
|
|
455
|
-
import { execSync } from "child_process";
|
|
456
|
-
var EXEC_ENV = {
|
|
457
|
-
...process.env,
|
|
458
|
-
PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`
|
|
459
|
-
};
|
|
460
|
-
function exec(cmd) {
|
|
461
|
-
return execSync(cmd, { encoding: "utf-8", env: EXEC_ENV }).trim();
|
|
462
|
-
}
|
|
463
|
-
function execSafe(cmd) {
|
|
464
|
-
try {
|
|
465
|
-
return execSync(cmd, { encoding: "utf-8", env: EXEC_ENV, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
466
|
-
} catch {
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
489
|
function createPane(windowTarget, cwd, position = "right") {
|
|
471
490
|
const cwdFlag = cwd ? ` -c ${shellQuote(cwd)}` : "";
|
|
472
491
|
const panes = listPanes(windowTarget);
|
|
@@ -525,9 +544,6 @@ function setPaneStyle(paneTarget, color) {
|
|
|
525
544
|
function selectLayout(windowTarget, layout = "even-horizontal") {
|
|
526
545
|
execSafe(`tmux select-layout -t "${windowTarget}" ${layout}`);
|
|
527
546
|
}
|
|
528
|
-
function shellQuote(s) {
|
|
529
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
530
|
-
}
|
|
531
547
|
|
|
532
548
|
// src/daemon/pane-registry.ts
|
|
533
549
|
var paneMap = /* @__PURE__ */ new Map();
|
|
@@ -573,16 +589,16 @@ function setOrchestratorPaneId(sessionId, paneId) {
|
|
|
573
589
|
}
|
|
574
590
|
function loadOrchestratorPrompt(cwd, mode) {
|
|
575
591
|
const projectPath = projectOrchestratorPromptPath(cwd);
|
|
576
|
-
if (
|
|
592
|
+
if (existsSync4(projectPath)) {
|
|
577
593
|
return readFileSync3(projectPath, "utf-8");
|
|
578
594
|
}
|
|
579
|
-
const basePath =
|
|
595
|
+
const basePath = resolve2(import.meta.dirname, "../templates/orchestrator-base.md");
|
|
580
596
|
const base = readFileSync3(basePath, "utf-8");
|
|
581
597
|
if (mode === "implementation") {
|
|
582
|
-
const implPath =
|
|
598
|
+
const implPath = resolve2(import.meta.dirname, "../templates/orchestrator-impl.md");
|
|
583
599
|
return base + "\n\n" + readFileSync3(implPath, "utf-8");
|
|
584
600
|
}
|
|
585
|
-
const planningPath =
|
|
601
|
+
const planningPath = resolve2(import.meta.dirname, "../templates/orchestrator-planning.md");
|
|
586
602
|
return base + "\n\n" + readFileSync3(planningPath, "utf-8");
|
|
587
603
|
}
|
|
588
604
|
function formatStateForOrchestrator(session) {
|
|
@@ -601,7 +617,7 @@ ${session.context}
|
|
|
601
617
|
}
|
|
602
618
|
} else {
|
|
603
619
|
let ctxFiles = [];
|
|
604
|
-
if (
|
|
620
|
+
if (existsSync4(ctxDir)) {
|
|
605
621
|
ctxFiles = readdirSync3(ctxDir).filter((f) => f !== "CLAUDE.md");
|
|
606
622
|
}
|
|
607
623
|
if (ctxFiles.length > 0) {
|
|
@@ -667,10 +683,10 @@ ${agentBlocks}
|
|
|
667
683
|
</last-cycle>
|
|
668
684
|
`;
|
|
669
685
|
}
|
|
670
|
-
const roadmapRef =
|
|
686
|
+
const roadmapRef = existsSync4(roadmapFile) ? `@${roadmapFile}` : "(empty)";
|
|
671
687
|
const worktreeAgents = session.agents.filter((a) => a.worktreePath);
|
|
672
688
|
let worktreeSection = "";
|
|
673
|
-
if (worktreeAgents.length > 0 ||
|
|
689
|
+
if (worktreeAgents.length > 0 || existsSync4(worktreeConfigPath(session.cwd))) {
|
|
674
690
|
let wtLines = "";
|
|
675
691
|
if (worktreeAgents.length > 0) {
|
|
676
692
|
wtLines = "\n" + worktreeAgents.map((a) => {
|
|
@@ -686,7 +702,7 @@ ${agentBlocks}
|
|
|
686
702
|
return `- ${a.id}: ${status} (branch ${a.branchName})`;
|
|
687
703
|
}).join("\n");
|
|
688
704
|
}
|
|
689
|
-
const worktreeHint =
|
|
705
|
+
const worktreeHint = existsSync4(worktreeConfigPath(session.cwd)) ? "Worktree config active (`.sisyphus/worktree.json`). Use `--worktree` flag with `sisyphus spawn` to isolate agents in their own worktrees. Recommended for feature work, especially with potential file overlap." : "No worktree configuration found. If this session involves parallel work where agents may edit overlapping files, use the `git-management` skill to set up `.sisyphus/worktree.json` and enable worktree isolation.";
|
|
690
706
|
worktreeSection = `
|
|
691
707
|
|
|
692
708
|
## Git Worktrees
|
|
@@ -694,7 +710,7 @@ ${agentBlocks}
|
|
|
694
710
|
${worktreeHint}${wtLines}`;
|
|
695
711
|
}
|
|
696
712
|
const goalFile = goalPath(session.cwd, session.id);
|
|
697
|
-
const goalContent =
|
|
713
|
+
const goalContent = existsSync4(goalFile) ? readFileSync3(goalFile, "utf-8").trim() : session.task;
|
|
698
714
|
return `## Goal
|
|
699
715
|
|
|
700
716
|
${goalContent}
|
|
@@ -709,12 +725,17 @@ ${roadmapRef}
|
|
|
709
725
|
${worktreeSection}`;
|
|
710
726
|
}
|
|
711
727
|
async function spawnOrchestrator(sessionId, cwd, windowId, message) {
|
|
728
|
+
try {
|
|
729
|
+
execSync("which claude", { stdio: "pipe", env: EXEC_ENV });
|
|
730
|
+
} catch {
|
|
731
|
+
throw new Error("Claude CLI not found on PATH. Run `sisyphus doctor` to diagnose.");
|
|
732
|
+
}
|
|
712
733
|
const session = getSession(cwd, sessionId);
|
|
713
734
|
const lastCycle = [...session.orchestratorCycles].reverse().find((c) => c.completedAt);
|
|
714
735
|
const mode = lastCycle?.mode ?? "planning";
|
|
715
736
|
const basePrompt = loadOrchestratorPrompt(cwd, mode);
|
|
716
737
|
const formattedState = formatStateForOrchestrator(session);
|
|
717
|
-
const agentPluginPath =
|
|
738
|
+
const agentPluginPath = resolve2(import.meta.dirname, "../templates/agent-plugin");
|
|
718
739
|
const agentTypes = discoverAgentTypes(agentPluginPath, session.cwd);
|
|
719
740
|
agentTypes.push(
|
|
720
741
|
{ qualifiedName: "Explore", source: "bundled", model: "haiku", description: "Fast codebase exploration \u2014 find files, search code, answer questions about architecture. Use for research and context gathering." }
|
|
@@ -727,16 +748,15 @@ async function spawnOrchestrator(sessionId, cwd, windowId, message) {
|
|
|
727
748
|
const systemPrompt = basePrompt.replace("{{AGENT_TYPES}}", agentTypeLines);
|
|
728
749
|
const cycleNum = session.orchestratorCycles.length + 1;
|
|
729
750
|
const promptFilePath = `${promptsDir(cwd, sessionId)}/orchestrator-system-${cycleNum}.md`;
|
|
730
|
-
|
|
751
|
+
writeFileSync3(promptFilePath, systemPrompt, "utf-8");
|
|
731
752
|
sessionWindowMap.set(sessionId, windowId);
|
|
732
|
-
const
|
|
733
|
-
const
|
|
734
|
-
const envExports = [
|
|
753
|
+
const npmBinDir = resolveNpmBinDir();
|
|
754
|
+
const envExports = buildEnvExports([
|
|
735
755
|
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
736
756
|
`export SISYPHUS_AGENT_ID='orchestrator'`,
|
|
737
757
|
`export SISYPHUS_CWD='${cwd}'`,
|
|
738
758
|
`export PATH="${npmBinDir}:$PATH"`
|
|
739
|
-
]
|
|
759
|
+
]);
|
|
740
760
|
let userPrompt = formattedState;
|
|
741
761
|
if (message) {
|
|
742
762
|
userPrompt += `
|
|
@@ -754,12 +774,12 @@ The user resumed this session with new instructions: ${message}`;
|
|
|
754
774
|
${continuationText}`;
|
|
755
775
|
}
|
|
756
776
|
const userPromptFilePath = `${promptsDir(cwd, sessionId)}/orchestrator-user-${cycleNum}.md`;
|
|
757
|
-
|
|
777
|
+
writeFileSync3(userPromptFilePath, userPrompt, "utf-8");
|
|
758
778
|
if (session.messages && session.messages.length > 0) {
|
|
759
779
|
await drainMessages(cwd, sessionId, session.messages.length);
|
|
760
780
|
}
|
|
761
|
-
const pluginPath =
|
|
762
|
-
const settingsPath =
|
|
781
|
+
const pluginPath = resolve2(import.meta.dirname, "../templates/orchestrator-plugin");
|
|
782
|
+
const settingsPath = resolve2(import.meta.dirname, "../templates/orchestrator-settings.json");
|
|
763
783
|
const config = loadConfig(cwd);
|
|
764
784
|
const effort = config.orchestratorEffort ?? "high";
|
|
765
785
|
const claudeCmd = `claude --dangerously-skip-permissions --disallowed-tools "Task,Agent" --effort ${effort} --settings "${settingsPath}" --plugin-dir "${pluginPath}" --name "sisyphus:orch-${sessionId.slice(0, 8)}-cycle-${cycleNum}" --system-prompt "$(cat '${promptFilePath}')" "$(cat '${userPromptFilePath}')"`;
|
|
@@ -768,18 +788,15 @@ ${continuationText}`;
|
|
|
768
788
|
registerPane(paneId, sessionId, "orchestrator");
|
|
769
789
|
setPaneTitle(paneId, `Sisyphus`);
|
|
770
790
|
setPaneStyle(paneId, ORCHESTRATOR_COLOR);
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
const
|
|
774
|
-
const scriptLines = [
|
|
791
|
+
const bannerCmd = resolveBannerCmd();
|
|
792
|
+
const notifyCmd = buildNotifyCmd(paneId);
|
|
793
|
+
const scriptPath = writeRunScript(promptsDir(cwd, sessionId), `orchestrator-run-${cycleNum}`, [
|
|
775
794
|
"#!/usr/bin/env bash",
|
|
776
795
|
...bannerCmd ? [bannerCmd] : [],
|
|
777
796
|
envExports,
|
|
778
|
-
|
|
797
|
+
claudeCmd,
|
|
779
798
|
notifyCmd
|
|
780
|
-
];
|
|
781
|
-
const scriptPath = `${promptsDir(cwd, sessionId)}/orchestrator-run-${cycleNum}.sh`;
|
|
782
|
-
writeFileSync2(scriptPath, scriptLines.join("\n"), { mode: 493 });
|
|
799
|
+
]);
|
|
783
800
|
sendKeys(paneId, `bash '${scriptPath}'`);
|
|
784
801
|
await addOrchestratorCycle(cwd, sessionId, {
|
|
785
802
|
cycle: cycleNum,
|
|
@@ -823,30 +840,13 @@ function cleanupSessionMaps(sessionId) {
|
|
|
823
840
|
}
|
|
824
841
|
|
|
825
842
|
// src/daemon/agent.ts
|
|
826
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
827
|
-
import {
|
|
843
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync as copyFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync5 } from "fs";
|
|
844
|
+
import { execSync as execSync2 } from "child_process";
|
|
845
|
+
import { resolve as resolve3 } from "path";
|
|
828
846
|
|
|
829
847
|
// src/daemon/worktree.ts
|
|
830
|
-
import {
|
|
831
|
-
import {
|
|
832
|
-
import { dirname as dirname3, join as join4 } from "path";
|
|
833
|
-
var EXEC_ENV2 = {
|
|
834
|
-
...process.env,
|
|
835
|
-
PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`
|
|
836
|
-
};
|
|
837
|
-
function exec2(cmd, cwd) {
|
|
838
|
-
return execSync2(cmd, { encoding: "utf-8", env: EXEC_ENV2, cwd }).trim();
|
|
839
|
-
}
|
|
840
|
-
function execSafe2(cmd, cwd) {
|
|
841
|
-
try {
|
|
842
|
-
return exec2(cmd, cwd);
|
|
843
|
-
} catch {
|
|
844
|
-
return null;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
function shellQuote2(s) {
|
|
848
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
849
|
-
}
|
|
848
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, readdirSync as readdirSync4, rmSync as rmSync2 } from "fs";
|
|
849
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
850
850
|
function loadWorktreeConfig(cwd) {
|
|
851
851
|
try {
|
|
852
852
|
const content = readFileSync4(worktreeConfigPath(cwd), "utf-8");
|
|
@@ -858,52 +858,52 @@ function loadWorktreeConfig(cwd) {
|
|
|
858
858
|
function createWorktreeShell(cwd, sessionId, agentId) {
|
|
859
859
|
const branchName = `sisyphus/${sessionId.slice(0, 8)}/${agentId}`;
|
|
860
860
|
const worktreePath = join4(worktreeBaseDir(cwd), sessionId.slice(0, 8), agentId);
|
|
861
|
-
mkdirSync2(
|
|
862
|
-
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
861
|
+
mkdirSync2(dirname2(worktreePath), { recursive: true });
|
|
862
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree prune`);
|
|
863
|
+
if (existsSync5(worktreePath)) {
|
|
864
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove --force ${shellQuote(worktreePath)}`);
|
|
865
|
+
}
|
|
866
|
+
execSafe(`git -C ${shellQuote(cwd)} branch -D ${shellQuote(branchName)}`);
|
|
867
|
+
exec(`git -C ${shellQuote(cwd)} branch ${shellQuote(branchName)} HEAD`);
|
|
868
|
+
exec(`git -C ${shellQuote(cwd)} worktree add ${shellQuote(worktreePath)} ${shellQuote(branchName)}`);
|
|
869
869
|
return { worktreePath, branchName };
|
|
870
870
|
}
|
|
871
871
|
function bootstrapWorktree(cwd, worktreePath, config) {
|
|
872
872
|
if (config.copy) {
|
|
873
873
|
for (const entry of config.copy) {
|
|
874
874
|
const dest = join4(worktreePath, entry);
|
|
875
|
-
mkdirSync2(
|
|
876
|
-
|
|
875
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
876
|
+
execSafe(`cp -r ${shellQuote(join4(cwd, entry))} ${shellQuote(dest)}`);
|
|
877
877
|
}
|
|
878
878
|
}
|
|
879
879
|
if (config.clone) {
|
|
880
880
|
for (const entry of config.clone) {
|
|
881
881
|
const dest = join4(worktreePath, entry);
|
|
882
|
-
mkdirSync2(
|
|
883
|
-
const src =
|
|
884
|
-
const dstQ =
|
|
885
|
-
if (
|
|
886
|
-
|
|
882
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
883
|
+
const src = shellQuote(join4(cwd, entry));
|
|
884
|
+
const dstQ = shellQuote(dest);
|
|
885
|
+
if (execSafe(`cp -Rc ${src} ${dstQ}`) === null) {
|
|
886
|
+
execSafe(`cp -r ${src} ${dstQ}`);
|
|
887
887
|
}
|
|
888
888
|
}
|
|
889
889
|
}
|
|
890
890
|
if (config.symlink) {
|
|
891
891
|
for (const entry of config.symlink) {
|
|
892
892
|
const dest = join4(worktreePath, entry);
|
|
893
|
-
mkdirSync2(
|
|
894
|
-
|
|
893
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
894
|
+
execSafe(`ln -s ${shellQuote(join4(cwd, entry))} ${shellQuote(dest)}`);
|
|
895
895
|
}
|
|
896
896
|
}
|
|
897
897
|
if (config.init) {
|
|
898
898
|
try {
|
|
899
|
-
|
|
899
|
+
exec(config.init, worktreePath);
|
|
900
900
|
} catch (err) {
|
|
901
901
|
console.error(`[sisyphus] worktree init command failed: ${err instanceof Error ? err.message : err}`);
|
|
902
902
|
}
|
|
903
903
|
}
|
|
904
904
|
}
|
|
905
905
|
function resolveWorktreeBranch(cwd, worktreePath) {
|
|
906
|
-
const output =
|
|
906
|
+
const output = execSafe(`git -C ${shellQuote(cwd)} worktree list --porcelain`);
|
|
907
907
|
if (!output) return null;
|
|
908
908
|
const lines = output.split("\n");
|
|
909
909
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -925,30 +925,30 @@ function mergeWorktrees(cwd, agents) {
|
|
|
925
925
|
(a) => a.worktreePath && a.mergeStatus === "pending"
|
|
926
926
|
);
|
|
927
927
|
const results = [];
|
|
928
|
-
|
|
929
|
-
|
|
928
|
+
execSafe(`git -C ${shellQuote(cwd)} add .sisyphus`);
|
|
929
|
+
execSafe(`git -C ${shellQuote(cwd)} commit -m 'sisyphus: snapshot session state before merge'`);
|
|
930
930
|
for (const agent of pending) {
|
|
931
931
|
const branch = resolveWorktreeBranch(cwd, agent.worktreePath);
|
|
932
932
|
if (!branch) {
|
|
933
933
|
results.push({ agentId: agent.id, name: agent.name, status: "no-changes" });
|
|
934
|
-
|
|
934
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove ${shellQuote(agent.worktreePath)} --force`);
|
|
935
935
|
continue;
|
|
936
936
|
}
|
|
937
|
-
const aheadLog =
|
|
937
|
+
const aheadLog = execSafe(`git -C ${shellQuote(cwd)} log HEAD..${shellQuote(branch)} --oneline`);
|
|
938
938
|
if (!aheadLog) {
|
|
939
939
|
results.push({ agentId: agent.id, name: agent.name, status: "no-changes" });
|
|
940
940
|
cleanupWorktree(cwd, agent.worktreePath, branch);
|
|
941
941
|
continue;
|
|
942
942
|
}
|
|
943
943
|
const mergeMsg = `sisyphus: merge ${agent.id} (${agent.name})`;
|
|
944
|
-
const mergeCmd = `git -C ${
|
|
944
|
+
const mergeCmd = `git -C ${shellQuote(cwd)} merge --no-ff ${shellQuote(branch)} -m ${shellQuote(mergeMsg)}`;
|
|
945
945
|
try {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
946
|
+
exec(mergeCmd);
|
|
947
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove ${shellQuote(agent.worktreePath)}`);
|
|
948
|
+
execSafe(`git -C ${shellQuote(cwd)} branch -d ${shellQuote(branch)}`);
|
|
949
949
|
results.push({ agentId: agent.id, name: agent.name, status: "merged" });
|
|
950
950
|
} catch (err) {
|
|
951
|
-
|
|
951
|
+
execSafe(`git -C ${shellQuote(cwd)} merge --abort`);
|
|
952
952
|
const errObj = err;
|
|
953
953
|
const stdout = errObj.stdout ? (typeof errObj.stdout === "string" ? errObj.stdout : errObj.stdout.toString("utf-8")).trim() : "";
|
|
954
954
|
const stderr = errObj.stderr ? (typeof errObj.stderr === "string" ? errObj.stderr : errObj.stderr.toString("utf-8")).trim() : "";
|
|
@@ -959,9 +959,9 @@ function mergeWorktrees(cwd, agents) {
|
|
|
959
959
|
return results;
|
|
960
960
|
}
|
|
961
961
|
function cleanupWorktree(cwd, worktreePath, branchName) {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
const baseDir =
|
|
962
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove ${shellQuote(worktreePath)} --force`);
|
|
963
|
+
execSafe(`git -C ${shellQuote(cwd)} branch -D ${shellQuote(branchName)}`);
|
|
964
|
+
const baseDir = dirname2(worktreePath);
|
|
965
965
|
try {
|
|
966
966
|
const entries = readdirSync4(baseDir);
|
|
967
967
|
if (entries.length === 0) {
|
|
@@ -1023,7 +1023,7 @@ function clearAgentCounter(sessionId) {
|
|
|
1023
1023
|
agentCounters.delete(sessionId);
|
|
1024
1024
|
}
|
|
1025
1025
|
function renderAgentSuffix(sessionId, instruction, worktreeContext) {
|
|
1026
|
-
const templatePath =
|
|
1026
|
+
const templatePath = resolve3(import.meta.dirname, "../templates/agent-suffix.md");
|
|
1027
1027
|
let template;
|
|
1028
1028
|
try {
|
|
1029
1029
|
template = readFileSync5(templatePath, "utf-8");
|
|
@@ -1047,7 +1047,7 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
1047
1047
|
mkdirSync3(`${base}/.claude-plugin`, { recursive: true });
|
|
1048
1048
|
mkdirSync3(`${base}/agents`, { recursive: true });
|
|
1049
1049
|
mkdirSync3(`${base}/hooks`, { recursive: true });
|
|
1050
|
-
|
|
1050
|
+
writeFileSync4(
|
|
1051
1051
|
`${base}/.claude-plugin/plugin.json`,
|
|
1052
1052
|
JSON.stringify({ name: `sisyphus-agent-${agentId}`, version: "1.0.0" }),
|
|
1053
1053
|
"utf-8"
|
|
@@ -1056,34 +1056,14 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
1056
1056
|
const shortName = agentType.replace(/^sisyphus:/, "");
|
|
1057
1057
|
copyFileSync2(agentConfig.filePath, `${base}/agents/${shortName}.md`);
|
|
1058
1058
|
}
|
|
1059
|
-
const srcHooks =
|
|
1059
|
+
const srcHooks = resolve3(import.meta.dirname, "../templates/agent-plugin/hooks");
|
|
1060
1060
|
for (const f of ["hooks.json", "require-submit.sh", "intercept-send-message.sh"]) {
|
|
1061
1061
|
copyFileSync2(`${srcHooks}/${f}`, `${base}/hooks/${f}`);
|
|
1062
1062
|
}
|
|
1063
1063
|
return base;
|
|
1064
1064
|
}
|
|
1065
|
-
|
|
1066
|
-
const { sessionId, cwd, agentType, name, instruction, windowId } = opts;
|
|
1067
|
-
const count = (agentCounters.get(sessionId) ?? 0) + 1;
|
|
1068
|
-
agentCounters.set(sessionId, count);
|
|
1069
|
-
const agentId = `agent-${String(count).padStart(3, "0")}`;
|
|
1070
|
-
const bundledPluginPath = resolve2(import.meta.dirname, "../templates/agent-plugin");
|
|
1071
|
-
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
1072
|
-
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
1073
|
-
const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
|
|
1074
|
-
let paneCwd = cwd;
|
|
1075
|
-
let worktreePath;
|
|
1076
|
-
let branchName;
|
|
1077
|
-
let worktreeContext;
|
|
1078
|
-
if (opts.worktree) {
|
|
1079
|
-
const wt = createWorktreeShell(cwd, sessionId, agentId);
|
|
1080
|
-
worktreePath = wt.worktreePath;
|
|
1081
|
-
branchName = wt.branchName;
|
|
1082
|
-
paneCwd = worktreePath;
|
|
1083
|
-
const session = getSession(cwd, sessionId);
|
|
1084
|
-
const portOffset = countWorktreeAgents(session.agents) + 1;
|
|
1085
|
-
worktreeContext = { offset: portOffset, total: portOffset, branchName };
|
|
1086
|
-
}
|
|
1065
|
+
function setupAgentPane(opts) {
|
|
1066
|
+
const { sessionId, cwd, agentId, agentType, name, instruction, windowId, color, provider, agentConfig, worktreeContext, paneCwd } = opts;
|
|
1087
1067
|
const paneId = createPane(windowId, paneCwd);
|
|
1088
1068
|
registerPane(paneId, sessionId, "agent", agentId);
|
|
1089
1069
|
const shortType = agentType && agentType !== "worker" ? agentType.replace(/^sisyphus:/, "") : "";
|
|
@@ -1092,50 +1072,88 @@ async function spawnAgent(opts) {
|
|
|
1092
1072
|
setPaneStyle(paneId, color);
|
|
1093
1073
|
const suffix = renderAgentSuffix(sessionId, instruction, worktreeContext);
|
|
1094
1074
|
const suffixFilePath = `${promptsDir(cwd, sessionId)}/${agentId}-system.md`;
|
|
1095
|
-
|
|
1096
|
-
const
|
|
1097
|
-
const
|
|
1098
|
-
const
|
|
1099
|
-
const npmBinDir = resolve2(import.meta.dirname, "../../.bin");
|
|
1100
|
-
const envExports = [
|
|
1075
|
+
writeFileSync4(suffixFilePath, suffix, "utf-8");
|
|
1076
|
+
const bannerCmd = resolveBannerCmd();
|
|
1077
|
+
const npmBinDir = resolveNpmBinDir();
|
|
1078
|
+
const envExports = buildEnvExports([
|
|
1101
1079
|
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
1102
1080
|
`export SISYPHUS_AGENT_ID='${agentId}'`,
|
|
1103
1081
|
`export SISYPHUS_CWD='${cwd}'`,
|
|
1104
1082
|
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : [],
|
|
1105
1083
|
`export PATH="${npmBinDir}:$PATH"`
|
|
1106
|
-
]
|
|
1107
|
-
const notifyCmd =
|
|
1084
|
+
]);
|
|
1085
|
+
const notifyCmd = buildNotifyCmd(paneId);
|
|
1108
1086
|
let mainCmd;
|
|
1109
1087
|
if (provider === "openai") {
|
|
1110
1088
|
const codexPromptPath = `${promptsDir(cwd, sessionId)}/${agentId}-codex-prompt.md`;
|
|
1111
1089
|
const parts = [];
|
|
1112
|
-
if (agentConfig?.body)
|
|
1113
|
-
parts.push(agentConfig.body);
|
|
1114
|
-
}
|
|
1090
|
+
if (agentConfig?.body) parts.push(agentConfig.body);
|
|
1115
1091
|
parts.push(suffix);
|
|
1116
1092
|
parts.push(`## Task
|
|
1117
1093
|
|
|
1118
1094
|
${instruction}`);
|
|
1119
|
-
|
|
1095
|
+
writeFileSync4(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
1120
1096
|
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
1121
|
-
mainCmd = `codex -m ${
|
|
1097
|
+
mainCmd = `codex -m ${shellQuote(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
1122
1098
|
} else {
|
|
1123
|
-
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${
|
|
1099
|
+
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${shellQuote(agentType)}` : "";
|
|
1124
1100
|
const config = loadConfig(cwd);
|
|
1125
1101
|
const effort = agentConfig?.frontmatter.effort ?? config.agentEffort ?? "medium";
|
|
1126
1102
|
const pluginPath = createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig);
|
|
1127
|
-
mainCmd = `claude --dangerously-skip-permissions --effort ${effort} --plugin-dir "${pluginPath}"${agentFlag} --name ${
|
|
1103
|
+
mainCmd = `claude --dangerously-skip-permissions --effort ${effort} --plugin-dir "${pluginPath}"${agentFlag} --name ${shellQuote(`sisyphus:${name}`)} --append-system-prompt "$(cat '${suffixFilePath}')" ${shellQuote(instruction)}`;
|
|
1128
1104
|
}
|
|
1129
|
-
const
|
|
1105
|
+
const scriptPath = writeRunScript(promptsDir(cwd, sessionId), `${agentId}-run`, [
|
|
1130
1106
|
"#!/usr/bin/env bash",
|
|
1131
|
-
...bannerCmd ? [bannerCmd
|
|
1107
|
+
...bannerCmd ? [bannerCmd] : [],
|
|
1132
1108
|
envExports,
|
|
1133
1109
|
mainCmd,
|
|
1134
1110
|
notifyCmd
|
|
1135
|
-
];
|
|
1136
|
-
const scriptPath = `${promptsDir(cwd, sessionId)}/${agentId}-run.sh`;
|
|
1137
|
-
writeFileSync3(scriptPath, scriptLines.join("\n"), { mode: 493 });
|
|
1111
|
+
]);
|
|
1138
1112
|
const fullCmd = `bash '${scriptPath}'`;
|
|
1113
|
+
return { paneId, fullCmd };
|
|
1114
|
+
}
|
|
1115
|
+
async function spawnAgent(opts) {
|
|
1116
|
+
const { sessionId, cwd, agentType, name, instruction, windowId } = opts;
|
|
1117
|
+
const count = (agentCounters.get(sessionId) ?? 0) + 1;
|
|
1118
|
+
agentCounters.set(sessionId, count);
|
|
1119
|
+
const agentId = `agent-${String(count).padStart(3, "0")}`;
|
|
1120
|
+
const bundledPluginPath = resolve3(import.meta.dirname, "../templates/agent-plugin");
|
|
1121
|
+
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
1122
|
+
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
1123
|
+
const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
|
|
1124
|
+
const cliToCheck = provider === "openai" ? "codex" : "claude";
|
|
1125
|
+
try {
|
|
1126
|
+
execSync2(`which ${cliToCheck}`, { stdio: "pipe", env: execEnv() });
|
|
1127
|
+
} catch {
|
|
1128
|
+
throw new Error(`${cliToCheck} CLI not found on PATH. Run \`sisyphus doctor\` to diagnose.`);
|
|
1129
|
+
}
|
|
1130
|
+
let paneCwd = cwd;
|
|
1131
|
+
let worktreePath;
|
|
1132
|
+
let branchName;
|
|
1133
|
+
let worktreeContext;
|
|
1134
|
+
if (opts.worktree) {
|
|
1135
|
+
const wt = createWorktreeShell(cwd, sessionId, agentId);
|
|
1136
|
+
worktreePath = wt.worktreePath;
|
|
1137
|
+
branchName = wt.branchName;
|
|
1138
|
+
paneCwd = worktreePath;
|
|
1139
|
+
const session = getSession(cwd, sessionId);
|
|
1140
|
+
const portOffset = countWorktreeAgents(session.agents) + 1;
|
|
1141
|
+
worktreeContext = { offset: portOffset, total: portOffset, branchName };
|
|
1142
|
+
}
|
|
1143
|
+
const { paneId, fullCmd } = setupAgentPane({
|
|
1144
|
+
sessionId,
|
|
1145
|
+
cwd,
|
|
1146
|
+
agentId,
|
|
1147
|
+
agentType,
|
|
1148
|
+
name,
|
|
1149
|
+
instruction,
|
|
1150
|
+
windowId,
|
|
1151
|
+
color,
|
|
1152
|
+
provider,
|
|
1153
|
+
agentConfig,
|
|
1154
|
+
worktreeContext,
|
|
1155
|
+
paneCwd
|
|
1156
|
+
});
|
|
1139
1157
|
const agent = {
|
|
1140
1158
|
id: agentId,
|
|
1141
1159
|
name,
|
|
@@ -1187,7 +1205,7 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
1187
1205
|
});
|
|
1188
1206
|
}
|
|
1189
1207
|
const { instruction, agentType, name, color } = agent;
|
|
1190
|
-
const bundledPluginPath =
|
|
1208
|
+
const bundledPluginPath = resolve3(import.meta.dirname, "../templates/agent-plugin");
|
|
1191
1209
|
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
1192
1210
|
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
1193
1211
|
let paneCwd = cwd;
|
|
@@ -1208,56 +1226,20 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
1208
1226
|
}
|
|
1209
1227
|
unregisterAgentPane(sessionId, agentId);
|
|
1210
1228
|
}
|
|
1211
|
-
const paneId =
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
1226
|
-
`export SISYPHUS_AGENT_ID='${agentId}'`,
|
|
1227
|
-
`export SISYPHUS_CWD='${cwd}'`,
|
|
1228
|
-
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : [],
|
|
1229
|
-
`export PATH="${npmBinDir}:$PATH"`
|
|
1230
|
-
].join(" && ");
|
|
1231
|
-
const notifyCmd = `node "${cliBin}" notify pane-exited --pane-id ${paneId}`;
|
|
1232
|
-
let mainCmd;
|
|
1233
|
-
if (provider === "openai") {
|
|
1234
|
-
const codexPromptPath = `${promptsDir(cwd, sessionId)}/${agentId}-codex-prompt.md`;
|
|
1235
|
-
const parts = [];
|
|
1236
|
-
if (agentConfig?.body) parts.push(agentConfig.body);
|
|
1237
|
-
parts.push(suffix);
|
|
1238
|
-
parts.push(`## Task
|
|
1239
|
-
|
|
1240
|
-
${instruction}`);
|
|
1241
|
-
writeFileSync3(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
1242
|
-
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
1243
|
-
mainCmd = `codex -m ${shellQuote3(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
1244
|
-
} else {
|
|
1245
|
-
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${shellQuote3(agentType)}` : "";
|
|
1246
|
-
const config = loadConfig(cwd);
|
|
1247
|
-
const effort = agentConfig?.frontmatter.effort ?? config.agentEffort ?? "medium";
|
|
1248
|
-
const pluginPath = createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig);
|
|
1249
|
-
mainCmd = `claude --dangerously-skip-permissions --effort ${effort} --plugin-dir "${pluginPath}"${agentFlag} --name ${shellQuote3(`sisyphus:${name}`)} --append-system-prompt "$(cat '${suffixFilePath}')" ${shellQuote3(instruction)}`;
|
|
1250
|
-
}
|
|
1251
|
-
const scriptLines = [
|
|
1252
|
-
"#!/usr/bin/env bash",
|
|
1253
|
-
...bannerCmd ? [bannerCmd.replace(/ &&$/, "")] : [],
|
|
1254
|
-
envExports,
|
|
1255
|
-
mainCmd,
|
|
1256
|
-
notifyCmd
|
|
1257
|
-
];
|
|
1258
|
-
const scriptPath = `${promptsDir(cwd, sessionId)}/${agentId}-run.sh`;
|
|
1259
|
-
writeFileSync3(scriptPath, scriptLines.join("\n"), { mode: 493 });
|
|
1260
|
-
const fullCmd = `bash '${scriptPath}'`;
|
|
1229
|
+
const { paneId, fullCmd } = setupAgentPane({
|
|
1230
|
+
sessionId,
|
|
1231
|
+
cwd,
|
|
1232
|
+
agentId,
|
|
1233
|
+
agentType,
|
|
1234
|
+
name,
|
|
1235
|
+
instruction,
|
|
1236
|
+
windowId,
|
|
1237
|
+
color,
|
|
1238
|
+
provider,
|
|
1239
|
+
agentConfig,
|
|
1240
|
+
worktreeContext,
|
|
1241
|
+
paneCwd
|
|
1242
|
+
});
|
|
1261
1243
|
await updateAgent(cwd, sessionId, agentId, {
|
|
1262
1244
|
status: "running",
|
|
1263
1245
|
paneId,
|
|
@@ -1282,7 +1264,7 @@ async function handleAgentReport(cwd, sessionId, agentId, content) {
|
|
|
1282
1264
|
mkdirSync3(dir, { recursive: true });
|
|
1283
1265
|
const num = nextReportNumber(cwd, sessionId, agentId);
|
|
1284
1266
|
const filePath = reportFilePath(cwd, sessionId, agentId, num);
|
|
1285
|
-
|
|
1267
|
+
writeFileSync4(filePath, content, "utf-8");
|
|
1286
1268
|
const entry = {
|
|
1287
1269
|
type: "update",
|
|
1288
1270
|
filePath,
|
|
@@ -1301,7 +1283,7 @@ async function handleAgentSubmit(cwd, sessionId, agentId, report) {
|
|
|
1301
1283
|
const dir = reportsDir(cwd, sessionId);
|
|
1302
1284
|
mkdirSync3(dir, { recursive: true });
|
|
1303
1285
|
const filePath = reportFilePath(cwd, sessionId, agentId, "final");
|
|
1304
|
-
|
|
1286
|
+
writeFileSync4(filePath, report, "utf-8");
|
|
1305
1287
|
const entry = {
|
|
1306
1288
|
type: "final",
|
|
1307
1289
|
filePath,
|
|
@@ -1344,9 +1326,6 @@ function allAgentsDone(session) {
|
|
|
1344
1326
|
const running = session.agents.filter((a) => a.status === "running");
|
|
1345
1327
|
return running.length === 0 && session.agents.length > 0;
|
|
1346
1328
|
}
|
|
1347
|
-
function shellQuote3(s) {
|
|
1348
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
1349
|
-
}
|
|
1350
1329
|
|
|
1351
1330
|
// src/daemon/pane-monitor.ts
|
|
1352
1331
|
var monitorInterval = null;
|
|
@@ -1491,7 +1470,7 @@ var PRUNE_KEEP_DAYS = 7;
|
|
|
1491
1470
|
function pruneOldSessions(cwd) {
|
|
1492
1471
|
try {
|
|
1493
1472
|
const dir = sessionsDir(cwd);
|
|
1494
|
-
if (!
|
|
1473
|
+
if (!existsSync7(dir)) return;
|
|
1495
1474
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
1496
1475
|
const candidates = [];
|
|
1497
1476
|
for (const entry of entries) {
|
|
@@ -1573,7 +1552,7 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
1573
1552
|
}
|
|
1574
1553
|
function listSessions(cwd) {
|
|
1575
1554
|
const dir = sessionsDir(cwd);
|
|
1576
|
-
if (!
|
|
1555
|
+
if (!existsSync7(dir)) return [];
|
|
1577
1556
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
1578
1557
|
const sessions = [];
|
|
1579
1558
|
for (const entry of entries) {
|
|
@@ -1853,10 +1832,7 @@ async function handlePaneExited(paneId, cwd, sessionId, role, agentId) {
|
|
|
1853
1832
|
|
|
1854
1833
|
// src/daemon/server.ts
|
|
1855
1834
|
var server = null;
|
|
1856
|
-
var
|
|
1857
|
-
var sessionMessageCounters = /* @__PURE__ */ new Map();
|
|
1858
|
-
var sessionTmuxMap = /* @__PURE__ */ new Map();
|
|
1859
|
-
var sessionWindowMap2 = /* @__PURE__ */ new Map();
|
|
1835
|
+
var sessionTrackingMap = /* @__PURE__ */ new Map();
|
|
1860
1836
|
function registryPath() {
|
|
1861
1837
|
return join5(globalDir(), "session-registry.json");
|
|
1862
1838
|
}
|
|
@@ -1864,14 +1840,14 @@ function persistSessionRegistry() {
|
|
|
1864
1840
|
const dir = globalDir();
|
|
1865
1841
|
mkdirSync4(dir, { recursive: true });
|
|
1866
1842
|
const registry = {};
|
|
1867
|
-
for (const [id,
|
|
1868
|
-
registry[id] = cwd;
|
|
1843
|
+
for (const [id, tracking] of sessionTrackingMap) {
|
|
1844
|
+
registry[id] = tracking.cwd;
|
|
1869
1845
|
}
|
|
1870
|
-
|
|
1846
|
+
writeFileSync5(registryPath(), JSON.stringify(registry, null, 2), "utf-8");
|
|
1871
1847
|
}
|
|
1872
1848
|
function loadSessionRegistry() {
|
|
1873
1849
|
const p = registryPath();
|
|
1874
|
-
if (!
|
|
1850
|
+
if (!existsSync8(p)) return {};
|
|
1875
1851
|
try {
|
|
1876
1852
|
return JSON.parse(readFileSync6(p, "utf-8"));
|
|
1877
1853
|
} catch {
|
|
@@ -1879,65 +1855,81 @@ function loadSessionRegistry() {
|
|
|
1879
1855
|
}
|
|
1880
1856
|
}
|
|
1881
1857
|
function registerSessionCwd(sessionId, cwd) {
|
|
1882
|
-
|
|
1858
|
+
const existing = sessionTrackingMap.get(sessionId);
|
|
1859
|
+
if (existing) {
|
|
1860
|
+
existing.cwd = cwd;
|
|
1861
|
+
} else {
|
|
1862
|
+
sessionTrackingMap.set(sessionId, { cwd, messageCounter: 0 });
|
|
1863
|
+
}
|
|
1883
1864
|
persistSessionRegistry();
|
|
1884
1865
|
}
|
|
1885
1866
|
function registerSessionTmux(sessionId, tmuxSession, windowId) {
|
|
1886
|
-
|
|
1887
|
-
|
|
1867
|
+
const existing = sessionTrackingMap.get(sessionId);
|
|
1868
|
+
if (existing) {
|
|
1869
|
+
existing.tmuxSession = tmuxSession;
|
|
1870
|
+
existing.windowId = windowId;
|
|
1871
|
+
} else {
|
|
1872
|
+
sessionTrackingMap.set(sessionId, { cwd: "", tmuxSession, windowId, messageCounter: 0 });
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
function unknownSessionError(sessionId) {
|
|
1876
|
+
return { ok: false, error: `Unknown session: ${sessionId}. Run \`sisyphus list --all\` to see available sessions.` };
|
|
1888
1877
|
}
|
|
1889
1878
|
async function handleRequest(req) {
|
|
1890
1879
|
try {
|
|
1891
1880
|
switch (req.type) {
|
|
1892
1881
|
case "start": {
|
|
1893
1882
|
const session = await startSession(req.task, req.cwd, req.context, req.name);
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1883
|
+
sessionTrackingMap.set(session.id, {
|
|
1884
|
+
cwd: req.cwd,
|
|
1885
|
+
tmuxSession: session.tmuxSessionName,
|
|
1886
|
+
windowId: session.tmuxWindowId,
|
|
1887
|
+
messageCounter: 0
|
|
1888
|
+
});
|
|
1889
|
+
persistSessionRegistry();
|
|
1897
1890
|
return { ok: true, data: { sessionId: session.id, tmuxSessionName: session.tmuxSessionName } };
|
|
1898
1891
|
}
|
|
1899
1892
|
case "spawn": {
|
|
1900
|
-
const
|
|
1901
|
-
if (!
|
|
1902
|
-
const result = await handleSpawn(req.sessionId, cwd, req.agentType, req.name, req.instruction, req.worktree);
|
|
1893
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1894
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1895
|
+
const result = await handleSpawn(req.sessionId, tracking.cwd, req.agentType, req.name, req.instruction, req.worktree);
|
|
1903
1896
|
return { ok: true, data: { agentId: result.agentId } };
|
|
1904
1897
|
}
|
|
1905
1898
|
case "submit": {
|
|
1906
|
-
const
|
|
1907
|
-
if (!
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
await handleSubmit(cwd, req.sessionId, req.agentId, req.report, windowId);
|
|
1899
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1900
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1901
|
+
if (!tracking.windowId) return { ok: false, error: `No tmux window found for session: ${req.sessionId}` };
|
|
1902
|
+
await handleSubmit(tracking.cwd, req.sessionId, req.agentId, req.report, tracking.windowId);
|
|
1911
1903
|
return { ok: true };
|
|
1912
1904
|
}
|
|
1913
1905
|
case "report": {
|
|
1914
|
-
const
|
|
1915
|
-
if (!
|
|
1916
|
-
await handleReport(cwd, req.sessionId, req.agentId, req.content);
|
|
1906
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1907
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1908
|
+
await handleReport(tracking.cwd, req.sessionId, req.agentId, req.content);
|
|
1917
1909
|
return { ok: true };
|
|
1918
1910
|
}
|
|
1919
1911
|
case "yield": {
|
|
1920
|
-
const
|
|
1921
|
-
if (!
|
|
1922
|
-
await handleYield(req.sessionId, cwd, req.nextPrompt, req.mode);
|
|
1912
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1913
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1914
|
+
await handleYield(req.sessionId, tracking.cwd, req.nextPrompt, req.mode);
|
|
1923
1915
|
return { ok: true };
|
|
1924
1916
|
}
|
|
1925
1917
|
case "complete": {
|
|
1926
|
-
const
|
|
1927
|
-
if (!
|
|
1928
|
-
await handleComplete(req.sessionId, cwd, req.report);
|
|
1918
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1919
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1920
|
+
await handleComplete(req.sessionId, tracking.cwd, req.report);
|
|
1929
1921
|
return { ok: true };
|
|
1930
1922
|
}
|
|
1931
1923
|
case "continue": {
|
|
1932
|
-
const
|
|
1933
|
-
if (!
|
|
1934
|
-
await handleContinue(req.sessionId, cwd);
|
|
1924
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1925
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1926
|
+
await handleContinue(req.sessionId, tracking.cwd);
|
|
1935
1927
|
return { ok: true };
|
|
1936
1928
|
}
|
|
1937
1929
|
case "status": {
|
|
1938
1930
|
if (req.sessionId) {
|
|
1939
|
-
const cwd =
|
|
1940
|
-
if (!cwd) return
|
|
1931
|
+
const cwd = sessionTrackingMap.get(req.sessionId)?.cwd ?? req.cwd;
|
|
1932
|
+
if (!cwd) return unknownSessionError(req.sessionId);
|
|
1941
1933
|
const session = getSessionStatus(cwd, req.sessionId);
|
|
1942
1934
|
return { ok: true, data: { session } };
|
|
1943
1935
|
}
|
|
@@ -1947,21 +1939,21 @@ async function handleRequest(req) {
|
|
|
1947
1939
|
const allSessions = [];
|
|
1948
1940
|
if (req.all) {
|
|
1949
1941
|
const seenCwds = /* @__PURE__ */ new Set();
|
|
1950
|
-
for (const
|
|
1951
|
-
if (seenCwds.has(cwd)) continue;
|
|
1952
|
-
seenCwds.add(cwd);
|
|
1953
|
-
const sessions = listSessions(cwd);
|
|
1954
|
-
allSessions.push(...sessions.map((s) => ({ ...s, cwd })));
|
|
1942
|
+
for (const tracking of sessionTrackingMap.values()) {
|
|
1943
|
+
if (seenCwds.has(tracking.cwd)) continue;
|
|
1944
|
+
seenCwds.add(tracking.cwd);
|
|
1945
|
+
const sessions = listSessions(tracking.cwd);
|
|
1946
|
+
allSessions.push(...sessions.map((s) => ({ ...s, cwd: tracking.cwd })));
|
|
1955
1947
|
}
|
|
1956
1948
|
} else {
|
|
1957
1949
|
const sessions = listSessions(req.cwd);
|
|
1958
1950
|
allSessions.push(...sessions.map((s) => ({ ...s, cwd: req.cwd })));
|
|
1959
1951
|
let totalCount = allSessions.length;
|
|
1960
1952
|
const seenCwds = /* @__PURE__ */ new Set([req.cwd]);
|
|
1961
|
-
for (const
|
|
1962
|
-
if (seenCwds.has(cwd)) continue;
|
|
1963
|
-
seenCwds.add(cwd);
|
|
1964
|
-
totalCount += listSessions(cwd).length;
|
|
1953
|
+
for (const tracking of sessionTrackingMap.values()) {
|
|
1954
|
+
if (seenCwds.has(tracking.cwd)) continue;
|
|
1955
|
+
seenCwds.add(tracking.cwd);
|
|
1956
|
+
totalCount += listSessions(tracking.cwd).length;
|
|
1965
1957
|
}
|
|
1966
1958
|
if (totalCount > allSessions.length) {
|
|
1967
1959
|
return { ok: true, data: { sessions: allSessions, totalCount, filtered: true } };
|
|
@@ -1970,114 +1962,109 @@ async function handleRequest(req) {
|
|
|
1970
1962
|
return { ok: true, data: { sessions: allSessions } };
|
|
1971
1963
|
}
|
|
1972
1964
|
case "resume": {
|
|
1973
|
-
let
|
|
1974
|
-
if (!
|
|
1965
|
+
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
1966
|
+
if (!tracking) {
|
|
1975
1967
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
1976
|
-
if (
|
|
1977
|
-
|
|
1978
|
-
|
|
1968
|
+
if (existsSync8(stateFile)) {
|
|
1969
|
+
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
1970
|
+
sessionTrackingMap.set(req.sessionId, tracking);
|
|
1971
|
+
persistSessionRegistry();
|
|
1979
1972
|
} else {
|
|
1980
|
-
return { ok: false, error: `Unknown session: ${req.sessionId}. No state.json found at ${stateFile}
|
|
1973
|
+
return { ok: false, error: `Unknown session: ${req.sessionId}. No state.json found at ${stateFile}. Run \`sisyphus list --all\` to see available sessions.` };
|
|
1981
1974
|
}
|
|
1982
1975
|
}
|
|
1983
|
-
const session = await resumeSession(req.sessionId, cwd, req.message);
|
|
1984
|
-
if (session.tmuxSessionName)
|
|
1985
|
-
if (session.tmuxWindowId)
|
|
1976
|
+
const session = await resumeSession(req.sessionId, tracking.cwd, req.message);
|
|
1977
|
+
if (session.tmuxSessionName) tracking.tmuxSession = session.tmuxSessionName;
|
|
1978
|
+
if (session.tmuxWindowId) tracking.windowId = session.tmuxWindowId;
|
|
1986
1979
|
return { ok: true, data: { sessionId: session.id, status: session.status, tmuxSessionName: session.tmuxSessionName } };
|
|
1987
1980
|
}
|
|
1988
1981
|
case "register_claude_session": {
|
|
1989
|
-
const
|
|
1990
|
-
if (!
|
|
1991
|
-
await handleRegisterClaudeSession(cwd, req.sessionId, req.agentId, req.claudeSessionId);
|
|
1982
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1983
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1984
|
+
await handleRegisterClaudeSession(tracking.cwd, req.sessionId, req.agentId, req.claudeSessionId);
|
|
1992
1985
|
return { ok: true };
|
|
1993
1986
|
}
|
|
1994
1987
|
case "kill": {
|
|
1995
|
-
const
|
|
1996
|
-
if (!
|
|
1997
|
-
const killedAgents = await handleKill(req.sessionId, cwd);
|
|
1998
|
-
|
|
1999
|
-
sessionTmuxMap.delete(req.sessionId);
|
|
2000
|
-
sessionWindowMap2.delete(req.sessionId);
|
|
1988
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1989
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1990
|
+
const killedAgents = await handleKill(req.sessionId, tracking.cwd);
|
|
1991
|
+
sessionTrackingMap.delete(req.sessionId);
|
|
2001
1992
|
persistSessionRegistry();
|
|
2002
1993
|
return { ok: true, data: { killedAgents, sessionId: req.sessionId } };
|
|
2003
1994
|
}
|
|
2004
1995
|
case "kill-agent": {
|
|
2005
|
-
const
|
|
2006
|
-
if (!
|
|
2007
|
-
await handleKillAgent(req.sessionId, cwd, req.agentId);
|
|
1996
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
1997
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
1998
|
+
await handleKillAgent(req.sessionId, tracking.cwd, req.agentId);
|
|
2008
1999
|
return { ok: true, data: { agentId: req.agentId } };
|
|
2009
2000
|
}
|
|
2010
2001
|
case "restart-agent": {
|
|
2011
|
-
const
|
|
2012
|
-
if (!
|
|
2013
|
-
await handleRestartAgent(req.sessionId, cwd, req.agentId);
|
|
2002
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
2003
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
2004
|
+
await handleRestartAgent(req.sessionId, tracking.cwd, req.agentId);
|
|
2014
2005
|
return { ok: true, data: { agentId: req.agentId } };
|
|
2015
2006
|
}
|
|
2016
2007
|
case "rollback": {
|
|
2017
|
-
let
|
|
2018
|
-
if (!
|
|
2008
|
+
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
2009
|
+
if (!tracking) {
|
|
2019
2010
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
2020
|
-
if (
|
|
2021
|
-
|
|
2022
|
-
|
|
2011
|
+
if (existsSync8(stateFile)) {
|
|
2012
|
+
registerSessionCwd(req.sessionId, req.cwd);
|
|
2013
|
+
tracking = sessionTrackingMap.get(req.sessionId);
|
|
2023
2014
|
} else {
|
|
2024
|
-
return
|
|
2015
|
+
return unknownSessionError(req.sessionId);
|
|
2025
2016
|
}
|
|
2026
2017
|
}
|
|
2027
|
-
const result = await handleRollback(req.sessionId, cwd, req.toCycle);
|
|
2018
|
+
const result = await handleRollback(req.sessionId, tracking.cwd, req.toCycle);
|
|
2028
2019
|
return { ok: true, data: result };
|
|
2029
2020
|
}
|
|
2030
2021
|
case "delete": {
|
|
2031
|
-
const
|
|
2032
|
-
if (
|
|
2022
|
+
const activeTracking = sessionTrackingMap.get(req.sessionId);
|
|
2023
|
+
if (activeTracking) {
|
|
2033
2024
|
try {
|
|
2034
|
-
await handleKill(req.sessionId,
|
|
2025
|
+
await handleKill(req.sessionId, activeTracking.cwd);
|
|
2035
2026
|
} catch {
|
|
2036
2027
|
}
|
|
2037
|
-
|
|
2038
|
-
sessionTmuxMap.delete(req.sessionId);
|
|
2039
|
-
sessionWindowMap2.delete(req.sessionId);
|
|
2040
|
-
sessionMessageCounters.delete(req.sessionId);
|
|
2028
|
+
sessionTrackingMap.delete(req.sessionId);
|
|
2041
2029
|
persistSessionRegistry();
|
|
2042
2030
|
}
|
|
2043
|
-
const { sessionDir: sessionDir2 } = await import("./paths-
|
|
2031
|
+
const { sessionDir: sessionDir2 } = await import("./paths-NUUALUVP.js");
|
|
2044
2032
|
rmSync4(sessionDir2(req.cwd, req.sessionId), { recursive: true, force: true });
|
|
2045
2033
|
return { ok: true };
|
|
2046
2034
|
}
|
|
2047
2035
|
case "pane-exited": {
|
|
2048
2036
|
const entry = lookupPane(req.paneId);
|
|
2049
2037
|
if (!entry) return { ok: true };
|
|
2050
|
-
const
|
|
2051
|
-
if (!
|
|
2038
|
+
const tracking = sessionTrackingMap.get(entry.sessionId);
|
|
2039
|
+
if (!tracking) {
|
|
2052
2040
|
unregisterPane(req.paneId);
|
|
2053
2041
|
return { ok: true };
|
|
2054
2042
|
}
|
|
2055
2043
|
unregisterPane(req.paneId);
|
|
2056
|
-
await handlePaneExited(req.paneId, cwd, entry.sessionId, entry.role, entry.agentId);
|
|
2044
|
+
await handlePaneExited(req.paneId, tracking.cwd, entry.sessionId, entry.role, entry.agentId);
|
|
2057
2045
|
return { ok: true };
|
|
2058
2046
|
}
|
|
2059
2047
|
case "update-task": {
|
|
2060
|
-
const
|
|
2061
|
-
if (!
|
|
2062
|
-
await updateTask(cwd, req.sessionId, req.task);
|
|
2048
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
2049
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
2050
|
+
await updateTask(tracking.cwd, req.sessionId, req.task);
|
|
2063
2051
|
return { ok: true };
|
|
2064
2052
|
}
|
|
2065
2053
|
case "message": {
|
|
2066
|
-
const
|
|
2067
|
-
if (!
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
const id = `msg-${String(counter).padStart(3, "0")}`;
|
|
2054
|
+
const tracking = sessionTrackingMap.get(req.sessionId);
|
|
2055
|
+
if (!tracking) return unknownSessionError(req.sessionId);
|
|
2056
|
+
tracking.messageCounter += 1;
|
|
2057
|
+
const id = `msg-${String(tracking.messageCounter).padStart(3, "0")}`;
|
|
2071
2058
|
const source = req.source ?? { type: "user" };
|
|
2072
2059
|
const summary = req.content.length > 200 ? req.content.slice(0, 200) + "..." : req.content;
|
|
2073
2060
|
let filePath;
|
|
2074
2061
|
if (req.content.length > 200) {
|
|
2075
|
-
const dir = messagesDir(cwd, req.sessionId);
|
|
2062
|
+
const dir = messagesDir(tracking.cwd, req.sessionId);
|
|
2076
2063
|
mkdirSync4(dir, { recursive: true });
|
|
2077
2064
|
filePath = join5(dir, `${id}.md`);
|
|
2078
|
-
|
|
2065
|
+
writeFileSync5(filePath, req.content, "utf-8");
|
|
2079
2066
|
}
|
|
2080
|
-
await appendMessage(cwd, req.sessionId, {
|
|
2067
|
+
await appendMessage(tracking.cwd, req.sessionId, {
|
|
2081
2068
|
id,
|
|
2082
2069
|
source,
|
|
2083
2070
|
content: req.content,
|
|
@@ -2096,9 +2083,9 @@ async function handleRequest(req) {
|
|
|
2096
2083
|
}
|
|
2097
2084
|
}
|
|
2098
2085
|
function startServer() {
|
|
2099
|
-
return new Promise((
|
|
2086
|
+
return new Promise((resolve5, reject) => {
|
|
2100
2087
|
const sock = socketPath();
|
|
2101
|
-
if (
|
|
2088
|
+
if (existsSync8(sock)) {
|
|
2102
2089
|
unlinkSync(sock);
|
|
2103
2090
|
}
|
|
2104
2091
|
server = createServer((conn) => {
|
|
@@ -2128,31 +2115,31 @@ function startServer() {
|
|
|
2128
2115
|
server.on("error", reject);
|
|
2129
2116
|
server.listen(sock, () => {
|
|
2130
2117
|
console.log(`[sisyphus] Daemon listening on ${sock}`);
|
|
2131
|
-
|
|
2118
|
+
resolve5(server);
|
|
2132
2119
|
});
|
|
2133
2120
|
});
|
|
2134
2121
|
}
|
|
2135
2122
|
function stopServer() {
|
|
2136
|
-
return new Promise((
|
|
2123
|
+
return new Promise((resolve5) => {
|
|
2137
2124
|
if (!server) {
|
|
2138
|
-
|
|
2125
|
+
resolve5();
|
|
2139
2126
|
return;
|
|
2140
2127
|
}
|
|
2141
2128
|
server.close(() => {
|
|
2142
2129
|
const sock = socketPath();
|
|
2143
|
-
if (
|
|
2130
|
+
if (existsSync8(sock)) {
|
|
2144
2131
|
unlinkSync(sock);
|
|
2145
2132
|
}
|
|
2146
2133
|
server = null;
|
|
2147
|
-
|
|
2134
|
+
resolve5();
|
|
2148
2135
|
});
|
|
2149
2136
|
});
|
|
2150
2137
|
}
|
|
2151
2138
|
|
|
2152
2139
|
// src/daemon/updater.ts
|
|
2153
2140
|
import { execSync as execSync3 } from "child_process";
|
|
2154
|
-
import { readFileSync as readFileSync7, writeFileSync as
|
|
2155
|
-
import { resolve as
|
|
2141
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
2142
|
+
import { resolve as resolve4 } from "path";
|
|
2156
2143
|
import { get } from "https";
|
|
2157
2144
|
function isNewer(latest, current) {
|
|
2158
2145
|
const a = latest.split(".").map(Number);
|
|
@@ -2168,7 +2155,7 @@ function isNewer(latest, current) {
|
|
|
2168
2155
|
function readPackageVersion() {
|
|
2169
2156
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
2170
2157
|
try {
|
|
2171
|
-
const raw = readFileSync7(
|
|
2158
|
+
const raw = readFileSync7(resolve4(import.meta.dirname, rel), "utf-8");
|
|
2172
2159
|
const pkg = JSON.parse(raw);
|
|
2173
2160
|
if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
|
|
2174
2161
|
} catch {
|
|
@@ -2178,9 +2165,9 @@ function readPackageVersion() {
|
|
|
2178
2165
|
}
|
|
2179
2166
|
var currentVersion = readPackageVersion();
|
|
2180
2167
|
function checkForUpdate() {
|
|
2181
|
-
return new Promise((
|
|
2168
|
+
return new Promise((resolve5) => {
|
|
2182
2169
|
const timeout = setTimeout(() => {
|
|
2183
|
-
|
|
2170
|
+
resolve5(null);
|
|
2184
2171
|
}, 5e3);
|
|
2185
2172
|
const req = get("https://registry.npmjs.org/sisyphi/latest", (res) => {
|
|
2186
2173
|
let data = "";
|
|
@@ -2192,24 +2179,24 @@ function checkForUpdate() {
|
|
|
2192
2179
|
try {
|
|
2193
2180
|
const { version: latest } = JSON.parse(data);
|
|
2194
2181
|
if (latest && isNewer(latest, currentVersion)) {
|
|
2195
|
-
|
|
2182
|
+
resolve5({ current: currentVersion, latest });
|
|
2196
2183
|
} else {
|
|
2197
|
-
|
|
2184
|
+
resolve5(null);
|
|
2198
2185
|
}
|
|
2199
2186
|
} catch {
|
|
2200
|
-
|
|
2187
|
+
resolve5(null);
|
|
2201
2188
|
}
|
|
2202
2189
|
});
|
|
2203
2190
|
});
|
|
2204
2191
|
req.on("error", () => {
|
|
2205
2192
|
clearTimeout(timeout);
|
|
2206
|
-
|
|
2193
|
+
resolve5(null);
|
|
2207
2194
|
});
|
|
2208
2195
|
});
|
|
2209
2196
|
}
|
|
2210
2197
|
function applyUpdate(expectedVersion) {
|
|
2211
2198
|
try {
|
|
2212
|
-
const nodeDir =
|
|
2199
|
+
const nodeDir = resolve4(process.execPath, "..");
|
|
2213
2200
|
const env = { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` };
|
|
2214
2201
|
execSync3("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
|
|
2215
2202
|
const result = execSync3("npm ls -g sisyphi --json --depth=0", {
|
|
@@ -2231,7 +2218,7 @@ function applyUpdate(expectedVersion) {
|
|
|
2231
2218
|
}
|
|
2232
2219
|
function markUpdating(version) {
|
|
2233
2220
|
try {
|
|
2234
|
-
|
|
2221
|
+
writeFileSync6(daemonUpdatingPath(), version, "utf-8");
|
|
2235
2222
|
} catch {
|
|
2236
2223
|
}
|
|
2237
2224
|
}
|
|
@@ -2261,6 +2248,11 @@ async function checkAndApply() {
|
|
|
2261
2248
|
}
|
|
2262
2249
|
|
|
2263
2250
|
// src/daemon/index.ts
|
|
2251
|
+
var nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
2252
|
+
if (nodeVersion < 22) {
|
|
2253
|
+
console.error(`[sisyphus] Node.js v22+ required (current: v${process.versions.node})`);
|
|
2254
|
+
process.exit(1);
|
|
2255
|
+
}
|
|
2264
2256
|
var ts = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
2265
2257
|
var origLog = console.log.bind(console);
|
|
2266
2258
|
var origError = console.error.bind(console);
|
|
@@ -2292,7 +2284,7 @@ function acquirePidLock() {
|
|
|
2292
2284
|
console.error(`[sisyphus] Daemon already running (pid ${pid}). Use 'sisyphusd restart' or 'sisyphusd stop' first.`);
|
|
2293
2285
|
process.exit(0);
|
|
2294
2286
|
}
|
|
2295
|
-
|
|
2287
|
+
writeFileSync7(daemonPidPath(), String(process.pid), "utf-8");
|
|
2296
2288
|
}
|
|
2297
2289
|
function isLaunchdManaged() {
|
|
2298
2290
|
try {
|
|
@@ -2351,7 +2343,7 @@ async function recoverSessions() {
|
|
|
2351
2343
|
let recovered = 0;
|
|
2352
2344
|
for (const [sessionId, cwd] of entries) {
|
|
2353
2345
|
const stateFile = statePath(cwd, sessionId);
|
|
2354
|
-
if (!
|
|
2346
|
+
if (!existsSync9(stateFile)) {
|
|
2355
2347
|
continue;
|
|
2356
2348
|
}
|
|
2357
2349
|
try {
|