sisyphi 1.0.4 → 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 +332 -313
- 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,10 +788,16 @@ ${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
|
-
|
|
791
|
+
const bannerCmd = resolveBannerCmd();
|
|
792
|
+
const notifyCmd = buildNotifyCmd(paneId);
|
|
793
|
+
const scriptPath = writeRunScript(promptsDir(cwd, sessionId), `orchestrator-run-${cycleNum}`, [
|
|
794
|
+
"#!/usr/bin/env bash",
|
|
795
|
+
...bannerCmd ? [bannerCmd] : [],
|
|
796
|
+
envExports,
|
|
797
|
+
claudeCmd,
|
|
798
|
+
notifyCmd
|
|
799
|
+
]);
|
|
800
|
+
sendKeys(paneId, `bash '${scriptPath}'`);
|
|
775
801
|
await addOrchestratorCycle(cwd, sessionId, {
|
|
776
802
|
cycle: cycleNum,
|
|
777
803
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -814,30 +840,13 @@ function cleanupSessionMaps(sessionId) {
|
|
|
814
840
|
}
|
|
815
841
|
|
|
816
842
|
// src/daemon/agent.ts
|
|
817
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
818
|
-
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";
|
|
819
846
|
|
|
820
847
|
// src/daemon/worktree.ts
|
|
821
|
-
import {
|
|
822
|
-
import {
|
|
823
|
-
import { dirname as dirname3, join as join4 } from "path";
|
|
824
|
-
var EXEC_ENV2 = {
|
|
825
|
-
...process.env,
|
|
826
|
-
PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env["PATH"] ?? "/usr/bin:/bin"}`
|
|
827
|
-
};
|
|
828
|
-
function exec2(cmd, cwd) {
|
|
829
|
-
return execSync2(cmd, { encoding: "utf-8", env: EXEC_ENV2, cwd }).trim();
|
|
830
|
-
}
|
|
831
|
-
function execSafe2(cmd, cwd) {
|
|
832
|
-
try {
|
|
833
|
-
return exec2(cmd, cwd);
|
|
834
|
-
} catch {
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
function shellQuote2(s) {
|
|
839
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
840
|
-
}
|
|
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";
|
|
841
850
|
function loadWorktreeConfig(cwd) {
|
|
842
851
|
try {
|
|
843
852
|
const content = readFileSync4(worktreeConfigPath(cwd), "utf-8");
|
|
@@ -849,52 +858,52 @@ function loadWorktreeConfig(cwd) {
|
|
|
849
858
|
function createWorktreeShell(cwd, sessionId, agentId) {
|
|
850
859
|
const branchName = `sisyphus/${sessionId.slice(0, 8)}/${agentId}`;
|
|
851
860
|
const worktreePath = join4(worktreeBaseDir(cwd), sessionId.slice(0, 8), agentId);
|
|
852
|
-
mkdirSync2(
|
|
853
|
-
|
|
854
|
-
if (
|
|
855
|
-
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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)}`);
|
|
860
869
|
return { worktreePath, branchName };
|
|
861
870
|
}
|
|
862
871
|
function bootstrapWorktree(cwd, worktreePath, config) {
|
|
863
872
|
if (config.copy) {
|
|
864
873
|
for (const entry of config.copy) {
|
|
865
874
|
const dest = join4(worktreePath, entry);
|
|
866
|
-
mkdirSync2(
|
|
867
|
-
|
|
875
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
876
|
+
execSafe(`cp -r ${shellQuote(join4(cwd, entry))} ${shellQuote(dest)}`);
|
|
868
877
|
}
|
|
869
878
|
}
|
|
870
879
|
if (config.clone) {
|
|
871
880
|
for (const entry of config.clone) {
|
|
872
881
|
const dest = join4(worktreePath, entry);
|
|
873
|
-
mkdirSync2(
|
|
874
|
-
const src =
|
|
875
|
-
const dstQ =
|
|
876
|
-
if (
|
|
877
|
-
|
|
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}`);
|
|
878
887
|
}
|
|
879
888
|
}
|
|
880
889
|
}
|
|
881
890
|
if (config.symlink) {
|
|
882
891
|
for (const entry of config.symlink) {
|
|
883
892
|
const dest = join4(worktreePath, entry);
|
|
884
|
-
mkdirSync2(
|
|
885
|
-
|
|
893
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
894
|
+
execSafe(`ln -s ${shellQuote(join4(cwd, entry))} ${shellQuote(dest)}`);
|
|
886
895
|
}
|
|
887
896
|
}
|
|
888
897
|
if (config.init) {
|
|
889
898
|
try {
|
|
890
|
-
|
|
899
|
+
exec(config.init, worktreePath);
|
|
891
900
|
} catch (err) {
|
|
892
901
|
console.error(`[sisyphus] worktree init command failed: ${err instanceof Error ? err.message : err}`);
|
|
893
902
|
}
|
|
894
903
|
}
|
|
895
904
|
}
|
|
896
905
|
function resolveWorktreeBranch(cwd, worktreePath) {
|
|
897
|
-
const output =
|
|
906
|
+
const output = execSafe(`git -C ${shellQuote(cwd)} worktree list --porcelain`);
|
|
898
907
|
if (!output) return null;
|
|
899
908
|
const lines = output.split("\n");
|
|
900
909
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -916,30 +925,30 @@ function mergeWorktrees(cwd, agents) {
|
|
|
916
925
|
(a) => a.worktreePath && a.mergeStatus === "pending"
|
|
917
926
|
);
|
|
918
927
|
const results = [];
|
|
919
|
-
|
|
920
|
-
|
|
928
|
+
execSafe(`git -C ${shellQuote(cwd)} add .sisyphus`);
|
|
929
|
+
execSafe(`git -C ${shellQuote(cwd)} commit -m 'sisyphus: snapshot session state before merge'`);
|
|
921
930
|
for (const agent of pending) {
|
|
922
931
|
const branch = resolveWorktreeBranch(cwd, agent.worktreePath);
|
|
923
932
|
if (!branch) {
|
|
924
933
|
results.push({ agentId: agent.id, name: agent.name, status: "no-changes" });
|
|
925
|
-
|
|
934
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove ${shellQuote(agent.worktreePath)} --force`);
|
|
926
935
|
continue;
|
|
927
936
|
}
|
|
928
|
-
const aheadLog =
|
|
937
|
+
const aheadLog = execSafe(`git -C ${shellQuote(cwd)} log HEAD..${shellQuote(branch)} --oneline`);
|
|
929
938
|
if (!aheadLog) {
|
|
930
939
|
results.push({ agentId: agent.id, name: agent.name, status: "no-changes" });
|
|
931
940
|
cleanupWorktree(cwd, agent.worktreePath, branch);
|
|
932
941
|
continue;
|
|
933
942
|
}
|
|
934
943
|
const mergeMsg = `sisyphus: merge ${agent.id} (${agent.name})`;
|
|
935
|
-
const mergeCmd = `git -C ${
|
|
944
|
+
const mergeCmd = `git -C ${shellQuote(cwd)} merge --no-ff ${shellQuote(branch)} -m ${shellQuote(mergeMsg)}`;
|
|
936
945
|
try {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
946
|
+
exec(mergeCmd);
|
|
947
|
+
execSafe(`git -C ${shellQuote(cwd)} worktree remove ${shellQuote(agent.worktreePath)}`);
|
|
948
|
+
execSafe(`git -C ${shellQuote(cwd)} branch -d ${shellQuote(branch)}`);
|
|
940
949
|
results.push({ agentId: agent.id, name: agent.name, status: "merged" });
|
|
941
950
|
} catch (err) {
|
|
942
|
-
|
|
951
|
+
execSafe(`git -C ${shellQuote(cwd)} merge --abort`);
|
|
943
952
|
const errObj = err;
|
|
944
953
|
const stdout = errObj.stdout ? (typeof errObj.stdout === "string" ? errObj.stdout : errObj.stdout.toString("utf-8")).trim() : "";
|
|
945
954
|
const stderr = errObj.stderr ? (typeof errObj.stderr === "string" ? errObj.stderr : errObj.stderr.toString("utf-8")).trim() : "";
|
|
@@ -950,9 +959,9 @@ function mergeWorktrees(cwd, agents) {
|
|
|
950
959
|
return results;
|
|
951
960
|
}
|
|
952
961
|
function cleanupWorktree(cwd, worktreePath, branchName) {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
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);
|
|
956
965
|
try {
|
|
957
966
|
const entries = readdirSync4(baseDir);
|
|
958
967
|
if (entries.length === 0) {
|
|
@@ -1014,7 +1023,7 @@ function clearAgentCounter(sessionId) {
|
|
|
1014
1023
|
agentCounters.delete(sessionId);
|
|
1015
1024
|
}
|
|
1016
1025
|
function renderAgentSuffix(sessionId, instruction, worktreeContext) {
|
|
1017
|
-
const templatePath =
|
|
1026
|
+
const templatePath = resolve3(import.meta.dirname, "../templates/agent-suffix.md");
|
|
1018
1027
|
let template;
|
|
1019
1028
|
try {
|
|
1020
1029
|
template = readFileSync5(templatePath, "utf-8");
|
|
@@ -1038,7 +1047,7 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
1038
1047
|
mkdirSync3(`${base}/.claude-plugin`, { recursive: true });
|
|
1039
1048
|
mkdirSync3(`${base}/agents`, { recursive: true });
|
|
1040
1049
|
mkdirSync3(`${base}/hooks`, { recursive: true });
|
|
1041
|
-
|
|
1050
|
+
writeFileSync4(
|
|
1042
1051
|
`${base}/.claude-plugin/plugin.json`,
|
|
1043
1052
|
JSON.stringify({ name: `sisyphus-agent-${agentId}`, version: "1.0.0" }),
|
|
1044
1053
|
"utf-8"
|
|
@@ -1047,34 +1056,14 @@ function createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig) {
|
|
|
1047
1056
|
const shortName = agentType.replace(/^sisyphus:/, "");
|
|
1048
1057
|
copyFileSync2(agentConfig.filePath, `${base}/agents/${shortName}.md`);
|
|
1049
1058
|
}
|
|
1050
|
-
const srcHooks =
|
|
1059
|
+
const srcHooks = resolve3(import.meta.dirname, "../templates/agent-plugin/hooks");
|
|
1051
1060
|
for (const f of ["hooks.json", "require-submit.sh", "intercept-send-message.sh"]) {
|
|
1052
1061
|
copyFileSync2(`${srcHooks}/${f}`, `${base}/hooks/${f}`);
|
|
1053
1062
|
}
|
|
1054
1063
|
return base;
|
|
1055
1064
|
}
|
|
1056
|
-
|
|
1057
|
-
const { sessionId, cwd, agentType, name, instruction, windowId } = opts;
|
|
1058
|
-
const count = (agentCounters.get(sessionId) ?? 0) + 1;
|
|
1059
|
-
agentCounters.set(sessionId, count);
|
|
1060
|
-
const agentId = `agent-${String(count).padStart(3, "0")}`;
|
|
1061
|
-
const bundledPluginPath = resolve2(import.meta.dirname, "../templates/agent-plugin");
|
|
1062
|
-
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
1063
|
-
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
1064
|
-
const color = (agentConfig?.frontmatter.color ? normalizeTmuxColor(agentConfig.frontmatter.color) : null) ?? getNextColor(sessionId);
|
|
1065
|
-
let paneCwd = cwd;
|
|
1066
|
-
let worktreePath;
|
|
1067
|
-
let branchName;
|
|
1068
|
-
let worktreeContext;
|
|
1069
|
-
if (opts.worktree) {
|
|
1070
|
-
const wt = createWorktreeShell(cwd, sessionId, agentId);
|
|
1071
|
-
worktreePath = wt.worktreePath;
|
|
1072
|
-
branchName = wt.branchName;
|
|
1073
|
-
paneCwd = worktreePath;
|
|
1074
|
-
const session = getSession(cwd, sessionId);
|
|
1075
|
-
const portOffset = countWorktreeAgents(session.agents) + 1;
|
|
1076
|
-
worktreeContext = { offset: portOffset, total: portOffset, branchName };
|
|
1077
|
-
}
|
|
1065
|
+
function setupAgentPane(opts) {
|
|
1066
|
+
const { sessionId, cwd, agentId, agentType, name, instruction, windowId, color, provider, agentConfig, worktreeContext, paneCwd } = opts;
|
|
1078
1067
|
const paneId = createPane(windowId, paneCwd);
|
|
1079
1068
|
registerPane(paneId, sessionId, "agent", agentId);
|
|
1080
1069
|
const shortType = agentType && agentType !== "worker" ? agentType.replace(/^sisyphus:/, "") : "";
|
|
@@ -1083,41 +1072,88 @@ async function spawnAgent(opts) {
|
|
|
1083
1072
|
setPaneStyle(paneId, color);
|
|
1084
1073
|
const suffix = renderAgentSuffix(sessionId, instruction, worktreeContext);
|
|
1085
1074
|
const suffixFilePath = `${promptsDir(cwd, sessionId)}/${agentId}-system.md`;
|
|
1086
|
-
|
|
1087
|
-
const
|
|
1088
|
-
const
|
|
1089
|
-
const
|
|
1090
|
-
const npmBinDir = resolve2(import.meta.dirname, "../../.bin");
|
|
1091
|
-
const envExports = [
|
|
1075
|
+
writeFileSync4(suffixFilePath, suffix, "utf-8");
|
|
1076
|
+
const bannerCmd = resolveBannerCmd();
|
|
1077
|
+
const npmBinDir = resolveNpmBinDir();
|
|
1078
|
+
const envExports = buildEnvExports([
|
|
1092
1079
|
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
1093
1080
|
`export SISYPHUS_AGENT_ID='${agentId}'`,
|
|
1094
1081
|
`export SISYPHUS_CWD='${cwd}'`,
|
|
1095
1082
|
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : [],
|
|
1096
1083
|
`export PATH="${npmBinDir}:$PATH"`
|
|
1097
|
-
]
|
|
1098
|
-
const notifyCmd =
|
|
1084
|
+
]);
|
|
1085
|
+
const notifyCmd = buildNotifyCmd(paneId);
|
|
1099
1086
|
let mainCmd;
|
|
1100
1087
|
if (provider === "openai") {
|
|
1101
1088
|
const codexPromptPath = `${promptsDir(cwd, sessionId)}/${agentId}-codex-prompt.md`;
|
|
1102
1089
|
const parts = [];
|
|
1103
|
-
if (agentConfig?.body)
|
|
1104
|
-
parts.push(agentConfig.body);
|
|
1105
|
-
}
|
|
1090
|
+
if (agentConfig?.body) parts.push(agentConfig.body);
|
|
1106
1091
|
parts.push(suffix);
|
|
1107
1092
|
parts.push(`## Task
|
|
1108
1093
|
|
|
1109
1094
|
${instruction}`);
|
|
1110
|
-
|
|
1095
|
+
writeFileSync4(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
1111
1096
|
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
1112
|
-
mainCmd = `codex -m ${
|
|
1097
|
+
mainCmd = `codex -m ${shellQuote(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
1113
1098
|
} else {
|
|
1114
|
-
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${
|
|
1099
|
+
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${shellQuote(agentType)}` : "";
|
|
1115
1100
|
const config = loadConfig(cwd);
|
|
1116
1101
|
const effort = agentConfig?.frontmatter.effort ?? config.agentEffort ?? "medium";
|
|
1117
1102
|
const pluginPath = createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig);
|
|
1118
|
-
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)}`;
|
|
1104
|
+
}
|
|
1105
|
+
const scriptPath = writeRunScript(promptsDir(cwd, sessionId), `${agentId}-run`, [
|
|
1106
|
+
"#!/usr/bin/env bash",
|
|
1107
|
+
...bannerCmd ? [bannerCmd] : [],
|
|
1108
|
+
envExports,
|
|
1109
|
+
mainCmd,
|
|
1110
|
+
notifyCmd
|
|
1111
|
+
]);
|
|
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 };
|
|
1119
1142
|
}
|
|
1120
|
-
const
|
|
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
|
+
});
|
|
1121
1157
|
const agent = {
|
|
1122
1158
|
id: agentId,
|
|
1123
1159
|
name,
|
|
@@ -1169,7 +1205,7 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
1169
1205
|
});
|
|
1170
1206
|
}
|
|
1171
1207
|
const { instruction, agentType, name, color } = agent;
|
|
1172
|
-
const bundledPluginPath =
|
|
1208
|
+
const bundledPluginPath = resolve3(import.meta.dirname, "../templates/agent-plugin");
|
|
1173
1209
|
const agentConfig = resolveAgentConfig(agentType, bundledPluginPath, cwd);
|
|
1174
1210
|
const provider = detectProvider(agentConfig?.frontmatter.model);
|
|
1175
1211
|
let paneCwd = cwd;
|
|
@@ -1190,47 +1226,20 @@ async function restartAgent(sessionId, cwd, agentId, windowId) {
|
|
|
1190
1226
|
}
|
|
1191
1227
|
unregisterAgentPane(sessionId, agentId);
|
|
1192
1228
|
}
|
|
1193
|
-
const paneId =
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
`export SISYPHUS_SESSION_ID='${sessionId}'`,
|
|
1208
|
-
`export SISYPHUS_AGENT_ID='${agentId}'`,
|
|
1209
|
-
`export SISYPHUS_CWD='${cwd}'`,
|
|
1210
|
-
...worktreeContext ? [`export SISYPHUS_PORT_OFFSET='${worktreeContext.offset}'`] : [],
|
|
1211
|
-
`export PATH="${npmBinDir}:$PATH"`
|
|
1212
|
-
].join(" && ");
|
|
1213
|
-
const notifyCmd = `node "${cliBin}" notify pane-exited --pane-id ${paneId}`;
|
|
1214
|
-
let mainCmd;
|
|
1215
|
-
if (provider === "openai") {
|
|
1216
|
-
const codexPromptPath = `${promptsDir(cwd, sessionId)}/${agentId}-codex-prompt.md`;
|
|
1217
|
-
const parts = [];
|
|
1218
|
-
if (agentConfig?.body) parts.push(agentConfig.body);
|
|
1219
|
-
parts.push(suffix);
|
|
1220
|
-
parts.push(`## Task
|
|
1221
|
-
|
|
1222
|
-
${instruction}`);
|
|
1223
|
-
writeFileSync3(codexPromptPath, parts.join("\n\n"), "utf-8");
|
|
1224
|
-
const model = agentConfig?.frontmatter.model ?? "codex-mini";
|
|
1225
|
-
mainCmd = `codex -m ${shellQuote3(model)} --dangerously-bypass-approvals-and-sandbox "$(cat '${codexPromptPath}')"`;
|
|
1226
|
-
} else {
|
|
1227
|
-
const agentFlag = agentType && agentType !== "worker" ? ` --agent ${shellQuote3(agentType)}` : "";
|
|
1228
|
-
const config = loadConfig(cwd);
|
|
1229
|
-
const effort = agentConfig?.frontmatter.effort ?? config.agentEffort ?? "medium";
|
|
1230
|
-
const pluginPath = createAgentPlugin(cwd, sessionId, agentId, agentType, agentConfig);
|
|
1231
|
-
mainCmd = `claude --dangerously-skip-permissions --effort ${effort} --plugin-dir "${pluginPath}"${agentFlag} --name ${shellQuote3(`sisyphus:${name}`)} --append-system-prompt "$(cat '${suffixFilePath}')" ${shellQuote3(instruction)}`;
|
|
1232
|
-
}
|
|
1233
|
-
const fullCmd = `${bannerCmd} ${envExports} && ${mainCmd}; ${notifyCmd}`;
|
|
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
|
+
});
|
|
1234
1243
|
await updateAgent(cwd, sessionId, agentId, {
|
|
1235
1244
|
status: "running",
|
|
1236
1245
|
paneId,
|
|
@@ -1255,7 +1264,7 @@ async function handleAgentReport(cwd, sessionId, agentId, content) {
|
|
|
1255
1264
|
mkdirSync3(dir, { recursive: true });
|
|
1256
1265
|
const num = nextReportNumber(cwd, sessionId, agentId);
|
|
1257
1266
|
const filePath = reportFilePath(cwd, sessionId, agentId, num);
|
|
1258
|
-
|
|
1267
|
+
writeFileSync4(filePath, content, "utf-8");
|
|
1259
1268
|
const entry = {
|
|
1260
1269
|
type: "update",
|
|
1261
1270
|
filePath,
|
|
@@ -1274,7 +1283,7 @@ async function handleAgentSubmit(cwd, sessionId, agentId, report) {
|
|
|
1274
1283
|
const dir = reportsDir(cwd, sessionId);
|
|
1275
1284
|
mkdirSync3(dir, { recursive: true });
|
|
1276
1285
|
const filePath = reportFilePath(cwd, sessionId, agentId, "final");
|
|
1277
|
-
|
|
1286
|
+
writeFileSync4(filePath, report, "utf-8");
|
|
1278
1287
|
const entry = {
|
|
1279
1288
|
type: "final",
|
|
1280
1289
|
filePath,
|
|
@@ -1317,9 +1326,6 @@ function allAgentsDone(session) {
|
|
|
1317
1326
|
const running = session.agents.filter((a) => a.status === "running");
|
|
1318
1327
|
return running.length === 0 && session.agents.length > 0;
|
|
1319
1328
|
}
|
|
1320
|
-
function shellQuote3(s) {
|
|
1321
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
1322
|
-
}
|
|
1323
1329
|
|
|
1324
1330
|
// src/daemon/pane-monitor.ts
|
|
1325
1331
|
var monitorInterval = null;
|
|
@@ -1464,7 +1470,7 @@ var PRUNE_KEEP_DAYS = 7;
|
|
|
1464
1470
|
function pruneOldSessions(cwd) {
|
|
1465
1471
|
try {
|
|
1466
1472
|
const dir = sessionsDir(cwd);
|
|
1467
|
-
if (!
|
|
1473
|
+
if (!existsSync7(dir)) return;
|
|
1468
1474
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
1469
1475
|
const candidates = [];
|
|
1470
1476
|
for (const entry of entries) {
|
|
@@ -1546,7 +1552,7 @@ function getSessionStatus(cwd, sessionId) {
|
|
|
1546
1552
|
}
|
|
1547
1553
|
function listSessions(cwd) {
|
|
1548
1554
|
const dir = sessionsDir(cwd);
|
|
1549
|
-
if (!
|
|
1555
|
+
if (!existsSync7(dir)) return [];
|
|
1550
1556
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
1551
1557
|
const sessions = [];
|
|
1552
1558
|
for (const entry of entries) {
|
|
@@ -1826,10 +1832,7 @@ async function handlePaneExited(paneId, cwd, sessionId, role, agentId) {
|
|
|
1826
1832
|
|
|
1827
1833
|
// src/daemon/server.ts
|
|
1828
1834
|
var server = null;
|
|
1829
|
-
var
|
|
1830
|
-
var sessionMessageCounters = /* @__PURE__ */ new Map();
|
|
1831
|
-
var sessionTmuxMap = /* @__PURE__ */ new Map();
|
|
1832
|
-
var sessionWindowMap2 = /* @__PURE__ */ new Map();
|
|
1835
|
+
var sessionTrackingMap = /* @__PURE__ */ new Map();
|
|
1833
1836
|
function registryPath() {
|
|
1834
1837
|
return join5(globalDir(), "session-registry.json");
|
|
1835
1838
|
}
|
|
@@ -1837,14 +1840,14 @@ function persistSessionRegistry() {
|
|
|
1837
1840
|
const dir = globalDir();
|
|
1838
1841
|
mkdirSync4(dir, { recursive: true });
|
|
1839
1842
|
const registry = {};
|
|
1840
|
-
for (const [id,
|
|
1841
|
-
registry[id] = cwd;
|
|
1843
|
+
for (const [id, tracking] of sessionTrackingMap) {
|
|
1844
|
+
registry[id] = tracking.cwd;
|
|
1842
1845
|
}
|
|
1843
|
-
|
|
1846
|
+
writeFileSync5(registryPath(), JSON.stringify(registry, null, 2), "utf-8");
|
|
1844
1847
|
}
|
|
1845
1848
|
function loadSessionRegistry() {
|
|
1846
1849
|
const p = registryPath();
|
|
1847
|
-
if (!
|
|
1850
|
+
if (!existsSync8(p)) return {};
|
|
1848
1851
|
try {
|
|
1849
1852
|
return JSON.parse(readFileSync6(p, "utf-8"));
|
|
1850
1853
|
} catch {
|
|
@@ -1852,65 +1855,81 @@ function loadSessionRegistry() {
|
|
|
1852
1855
|
}
|
|
1853
1856
|
}
|
|
1854
1857
|
function registerSessionCwd(sessionId, cwd) {
|
|
1855
|
-
|
|
1858
|
+
const existing = sessionTrackingMap.get(sessionId);
|
|
1859
|
+
if (existing) {
|
|
1860
|
+
existing.cwd = cwd;
|
|
1861
|
+
} else {
|
|
1862
|
+
sessionTrackingMap.set(sessionId, { cwd, messageCounter: 0 });
|
|
1863
|
+
}
|
|
1856
1864
|
persistSessionRegistry();
|
|
1857
1865
|
}
|
|
1858
1866
|
function registerSessionTmux(sessionId, tmuxSession, windowId) {
|
|
1859
|
-
|
|
1860
|
-
|
|
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.` };
|
|
1861
1877
|
}
|
|
1862
1878
|
async function handleRequest(req) {
|
|
1863
1879
|
try {
|
|
1864
1880
|
switch (req.type) {
|
|
1865
1881
|
case "start": {
|
|
1866
1882
|
const session = await startSession(req.task, req.cwd, req.context, req.name);
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1883
|
+
sessionTrackingMap.set(session.id, {
|
|
1884
|
+
cwd: req.cwd,
|
|
1885
|
+
tmuxSession: session.tmuxSessionName,
|
|
1886
|
+
windowId: session.tmuxWindowId,
|
|
1887
|
+
messageCounter: 0
|
|
1888
|
+
});
|
|
1889
|
+
persistSessionRegistry();
|
|
1870
1890
|
return { ok: true, data: { sessionId: session.id, tmuxSessionName: session.tmuxSessionName } };
|
|
1871
1891
|
}
|
|
1872
1892
|
case "spawn": {
|
|
1873
|
-
const
|
|
1874
|
-
if (!
|
|
1875
|
-
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);
|
|
1876
1896
|
return { ok: true, data: { agentId: result.agentId } };
|
|
1877
1897
|
}
|
|
1878
1898
|
case "submit": {
|
|
1879
|
-
const
|
|
1880
|
-
if (!
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
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);
|
|
1884
1903
|
return { ok: true };
|
|
1885
1904
|
}
|
|
1886
1905
|
case "report": {
|
|
1887
|
-
const
|
|
1888
|
-
if (!
|
|
1889
|
-
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);
|
|
1890
1909
|
return { ok: true };
|
|
1891
1910
|
}
|
|
1892
1911
|
case "yield": {
|
|
1893
|
-
const
|
|
1894
|
-
if (!
|
|
1895
|
-
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);
|
|
1896
1915
|
return { ok: true };
|
|
1897
1916
|
}
|
|
1898
1917
|
case "complete": {
|
|
1899
|
-
const
|
|
1900
|
-
if (!
|
|
1901
|
-
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);
|
|
1902
1921
|
return { ok: true };
|
|
1903
1922
|
}
|
|
1904
1923
|
case "continue": {
|
|
1905
|
-
const
|
|
1906
|
-
if (!
|
|
1907
|
-
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);
|
|
1908
1927
|
return { ok: true };
|
|
1909
1928
|
}
|
|
1910
1929
|
case "status": {
|
|
1911
1930
|
if (req.sessionId) {
|
|
1912
|
-
const cwd =
|
|
1913
|
-
if (!cwd) return
|
|
1931
|
+
const cwd = sessionTrackingMap.get(req.sessionId)?.cwd ?? req.cwd;
|
|
1932
|
+
if (!cwd) return unknownSessionError(req.sessionId);
|
|
1914
1933
|
const session = getSessionStatus(cwd, req.sessionId);
|
|
1915
1934
|
return { ok: true, data: { session } };
|
|
1916
1935
|
}
|
|
@@ -1920,21 +1939,21 @@ async function handleRequest(req) {
|
|
|
1920
1939
|
const allSessions = [];
|
|
1921
1940
|
if (req.all) {
|
|
1922
1941
|
const seenCwds = /* @__PURE__ */ new Set();
|
|
1923
|
-
for (const
|
|
1924
|
-
if (seenCwds.has(cwd)) continue;
|
|
1925
|
-
seenCwds.add(cwd);
|
|
1926
|
-
const sessions = listSessions(cwd);
|
|
1927
|
-
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 })));
|
|
1928
1947
|
}
|
|
1929
1948
|
} else {
|
|
1930
1949
|
const sessions = listSessions(req.cwd);
|
|
1931
1950
|
allSessions.push(...sessions.map((s) => ({ ...s, cwd: req.cwd })));
|
|
1932
1951
|
let totalCount = allSessions.length;
|
|
1933
1952
|
const seenCwds = /* @__PURE__ */ new Set([req.cwd]);
|
|
1934
|
-
for (const
|
|
1935
|
-
if (seenCwds.has(cwd)) continue;
|
|
1936
|
-
seenCwds.add(cwd);
|
|
1937
|
-
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;
|
|
1938
1957
|
}
|
|
1939
1958
|
if (totalCount > allSessions.length) {
|
|
1940
1959
|
return { ok: true, data: { sessions: allSessions, totalCount, filtered: true } };
|
|
@@ -1943,114 +1962,109 @@ async function handleRequest(req) {
|
|
|
1943
1962
|
return { ok: true, data: { sessions: allSessions } };
|
|
1944
1963
|
}
|
|
1945
1964
|
case "resume": {
|
|
1946
|
-
let
|
|
1947
|
-
if (!
|
|
1965
|
+
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
1966
|
+
if (!tracking) {
|
|
1948
1967
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
1949
|
-
if (
|
|
1950
|
-
|
|
1951
|
-
|
|
1968
|
+
if (existsSync8(stateFile)) {
|
|
1969
|
+
tracking = { cwd: req.cwd, messageCounter: 0 };
|
|
1970
|
+
sessionTrackingMap.set(req.sessionId, tracking);
|
|
1971
|
+
persistSessionRegistry();
|
|
1952
1972
|
} else {
|
|
1953
|
-
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.` };
|
|
1954
1974
|
}
|
|
1955
1975
|
}
|
|
1956
|
-
const session = await resumeSession(req.sessionId, cwd, req.message);
|
|
1957
|
-
if (session.tmuxSessionName)
|
|
1958
|
-
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;
|
|
1959
1979
|
return { ok: true, data: { sessionId: session.id, status: session.status, tmuxSessionName: session.tmuxSessionName } };
|
|
1960
1980
|
}
|
|
1961
1981
|
case "register_claude_session": {
|
|
1962
|
-
const
|
|
1963
|
-
if (!
|
|
1964
|
-
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);
|
|
1965
1985
|
return { ok: true };
|
|
1966
1986
|
}
|
|
1967
1987
|
case "kill": {
|
|
1968
|
-
const
|
|
1969
|
-
if (!
|
|
1970
|
-
const killedAgents = await handleKill(req.sessionId, cwd);
|
|
1971
|
-
|
|
1972
|
-
sessionTmuxMap.delete(req.sessionId);
|
|
1973
|
-
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);
|
|
1974
1992
|
persistSessionRegistry();
|
|
1975
1993
|
return { ok: true, data: { killedAgents, sessionId: req.sessionId } };
|
|
1976
1994
|
}
|
|
1977
1995
|
case "kill-agent": {
|
|
1978
|
-
const
|
|
1979
|
-
if (!
|
|
1980
|
-
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);
|
|
1981
1999
|
return { ok: true, data: { agentId: req.agentId } };
|
|
1982
2000
|
}
|
|
1983
2001
|
case "restart-agent": {
|
|
1984
|
-
const
|
|
1985
|
-
if (!
|
|
1986
|
-
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);
|
|
1987
2005
|
return { ok: true, data: { agentId: req.agentId } };
|
|
1988
2006
|
}
|
|
1989
2007
|
case "rollback": {
|
|
1990
|
-
let
|
|
1991
|
-
if (!
|
|
2008
|
+
let tracking = sessionTrackingMap.get(req.sessionId);
|
|
2009
|
+
if (!tracking) {
|
|
1992
2010
|
const stateFile = `${req.cwd}/.sisyphus/sessions/${req.sessionId}/state.json`;
|
|
1993
|
-
if (
|
|
1994
|
-
|
|
1995
|
-
|
|
2011
|
+
if (existsSync8(stateFile)) {
|
|
2012
|
+
registerSessionCwd(req.sessionId, req.cwd);
|
|
2013
|
+
tracking = sessionTrackingMap.get(req.sessionId);
|
|
1996
2014
|
} else {
|
|
1997
|
-
return
|
|
2015
|
+
return unknownSessionError(req.sessionId);
|
|
1998
2016
|
}
|
|
1999
2017
|
}
|
|
2000
|
-
const result = await handleRollback(req.sessionId, cwd, req.toCycle);
|
|
2018
|
+
const result = await handleRollback(req.sessionId, tracking.cwd, req.toCycle);
|
|
2001
2019
|
return { ok: true, data: result };
|
|
2002
2020
|
}
|
|
2003
2021
|
case "delete": {
|
|
2004
|
-
const
|
|
2005
|
-
if (
|
|
2022
|
+
const activeTracking = sessionTrackingMap.get(req.sessionId);
|
|
2023
|
+
if (activeTracking) {
|
|
2006
2024
|
try {
|
|
2007
|
-
await handleKill(req.sessionId,
|
|
2025
|
+
await handleKill(req.sessionId, activeTracking.cwd);
|
|
2008
2026
|
} catch {
|
|
2009
2027
|
}
|
|
2010
|
-
|
|
2011
|
-
sessionTmuxMap.delete(req.sessionId);
|
|
2012
|
-
sessionWindowMap2.delete(req.sessionId);
|
|
2013
|
-
sessionMessageCounters.delete(req.sessionId);
|
|
2028
|
+
sessionTrackingMap.delete(req.sessionId);
|
|
2014
2029
|
persistSessionRegistry();
|
|
2015
2030
|
}
|
|
2016
|
-
const { sessionDir: sessionDir2 } = await import("./paths-
|
|
2031
|
+
const { sessionDir: sessionDir2 } = await import("./paths-NUUALUVP.js");
|
|
2017
2032
|
rmSync4(sessionDir2(req.cwd, req.sessionId), { recursive: true, force: true });
|
|
2018
2033
|
return { ok: true };
|
|
2019
2034
|
}
|
|
2020
2035
|
case "pane-exited": {
|
|
2021
2036
|
const entry = lookupPane(req.paneId);
|
|
2022
2037
|
if (!entry) return { ok: true };
|
|
2023
|
-
const
|
|
2024
|
-
if (!
|
|
2038
|
+
const tracking = sessionTrackingMap.get(entry.sessionId);
|
|
2039
|
+
if (!tracking) {
|
|
2025
2040
|
unregisterPane(req.paneId);
|
|
2026
2041
|
return { ok: true };
|
|
2027
2042
|
}
|
|
2028
2043
|
unregisterPane(req.paneId);
|
|
2029
|
-
await handlePaneExited(req.paneId, cwd, entry.sessionId, entry.role, entry.agentId);
|
|
2044
|
+
await handlePaneExited(req.paneId, tracking.cwd, entry.sessionId, entry.role, entry.agentId);
|
|
2030
2045
|
return { ok: true };
|
|
2031
2046
|
}
|
|
2032
2047
|
case "update-task": {
|
|
2033
|
-
const
|
|
2034
|
-
if (!
|
|
2035
|
-
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);
|
|
2036
2051
|
return { ok: true };
|
|
2037
2052
|
}
|
|
2038
2053
|
case "message": {
|
|
2039
|
-
const
|
|
2040
|
-
if (!
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
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")}`;
|
|
2044
2058
|
const source = req.source ?? { type: "user" };
|
|
2045
2059
|
const summary = req.content.length > 200 ? req.content.slice(0, 200) + "..." : req.content;
|
|
2046
2060
|
let filePath;
|
|
2047
2061
|
if (req.content.length > 200) {
|
|
2048
|
-
const dir = messagesDir(cwd, req.sessionId);
|
|
2062
|
+
const dir = messagesDir(tracking.cwd, req.sessionId);
|
|
2049
2063
|
mkdirSync4(dir, { recursive: true });
|
|
2050
2064
|
filePath = join5(dir, `${id}.md`);
|
|
2051
|
-
|
|
2065
|
+
writeFileSync5(filePath, req.content, "utf-8");
|
|
2052
2066
|
}
|
|
2053
|
-
await appendMessage(cwd, req.sessionId, {
|
|
2067
|
+
await appendMessage(tracking.cwd, req.sessionId, {
|
|
2054
2068
|
id,
|
|
2055
2069
|
source,
|
|
2056
2070
|
content: req.content,
|
|
@@ -2069,9 +2083,9 @@ async function handleRequest(req) {
|
|
|
2069
2083
|
}
|
|
2070
2084
|
}
|
|
2071
2085
|
function startServer() {
|
|
2072
|
-
return new Promise((
|
|
2086
|
+
return new Promise((resolve5, reject) => {
|
|
2073
2087
|
const sock = socketPath();
|
|
2074
|
-
if (
|
|
2088
|
+
if (existsSync8(sock)) {
|
|
2075
2089
|
unlinkSync(sock);
|
|
2076
2090
|
}
|
|
2077
2091
|
server = createServer((conn) => {
|
|
@@ -2101,31 +2115,31 @@ function startServer() {
|
|
|
2101
2115
|
server.on("error", reject);
|
|
2102
2116
|
server.listen(sock, () => {
|
|
2103
2117
|
console.log(`[sisyphus] Daemon listening on ${sock}`);
|
|
2104
|
-
|
|
2118
|
+
resolve5(server);
|
|
2105
2119
|
});
|
|
2106
2120
|
});
|
|
2107
2121
|
}
|
|
2108
2122
|
function stopServer() {
|
|
2109
|
-
return new Promise((
|
|
2123
|
+
return new Promise((resolve5) => {
|
|
2110
2124
|
if (!server) {
|
|
2111
|
-
|
|
2125
|
+
resolve5();
|
|
2112
2126
|
return;
|
|
2113
2127
|
}
|
|
2114
2128
|
server.close(() => {
|
|
2115
2129
|
const sock = socketPath();
|
|
2116
|
-
if (
|
|
2130
|
+
if (existsSync8(sock)) {
|
|
2117
2131
|
unlinkSync(sock);
|
|
2118
2132
|
}
|
|
2119
2133
|
server = null;
|
|
2120
|
-
|
|
2134
|
+
resolve5();
|
|
2121
2135
|
});
|
|
2122
2136
|
});
|
|
2123
2137
|
}
|
|
2124
2138
|
|
|
2125
2139
|
// src/daemon/updater.ts
|
|
2126
2140
|
import { execSync as execSync3 } from "child_process";
|
|
2127
|
-
import { readFileSync as readFileSync7, writeFileSync as
|
|
2128
|
-
import { resolve as
|
|
2141
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
2142
|
+
import { resolve as resolve4 } from "path";
|
|
2129
2143
|
import { get } from "https";
|
|
2130
2144
|
function isNewer(latest, current) {
|
|
2131
2145
|
const a = latest.split(".").map(Number);
|
|
@@ -2141,7 +2155,7 @@ function isNewer(latest, current) {
|
|
|
2141
2155
|
function readPackageVersion() {
|
|
2142
2156
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
2143
2157
|
try {
|
|
2144
|
-
const raw = readFileSync7(
|
|
2158
|
+
const raw = readFileSync7(resolve4(import.meta.dirname, rel), "utf-8");
|
|
2145
2159
|
const pkg = JSON.parse(raw);
|
|
2146
2160
|
if (pkg.name === "sisyphi" && pkg.version) return pkg.version;
|
|
2147
2161
|
} catch {
|
|
@@ -2151,9 +2165,9 @@ function readPackageVersion() {
|
|
|
2151
2165
|
}
|
|
2152
2166
|
var currentVersion = readPackageVersion();
|
|
2153
2167
|
function checkForUpdate() {
|
|
2154
|
-
return new Promise((
|
|
2168
|
+
return new Promise((resolve5) => {
|
|
2155
2169
|
const timeout = setTimeout(() => {
|
|
2156
|
-
|
|
2170
|
+
resolve5(null);
|
|
2157
2171
|
}, 5e3);
|
|
2158
2172
|
const req = get("https://registry.npmjs.org/sisyphi/latest", (res) => {
|
|
2159
2173
|
let data = "";
|
|
@@ -2165,24 +2179,24 @@ function checkForUpdate() {
|
|
|
2165
2179
|
try {
|
|
2166
2180
|
const { version: latest } = JSON.parse(data);
|
|
2167
2181
|
if (latest && isNewer(latest, currentVersion)) {
|
|
2168
|
-
|
|
2182
|
+
resolve5({ current: currentVersion, latest });
|
|
2169
2183
|
} else {
|
|
2170
|
-
|
|
2184
|
+
resolve5(null);
|
|
2171
2185
|
}
|
|
2172
2186
|
} catch {
|
|
2173
|
-
|
|
2187
|
+
resolve5(null);
|
|
2174
2188
|
}
|
|
2175
2189
|
});
|
|
2176
2190
|
});
|
|
2177
2191
|
req.on("error", () => {
|
|
2178
2192
|
clearTimeout(timeout);
|
|
2179
|
-
|
|
2193
|
+
resolve5(null);
|
|
2180
2194
|
});
|
|
2181
2195
|
});
|
|
2182
2196
|
}
|
|
2183
2197
|
function applyUpdate(expectedVersion) {
|
|
2184
2198
|
try {
|
|
2185
|
-
const nodeDir =
|
|
2199
|
+
const nodeDir = resolve4(process.execPath, "..");
|
|
2186
2200
|
const env = { ...process.env, PATH: `${nodeDir}:${process.env.PATH ?? ""}` };
|
|
2187
2201
|
execSync3("npm install -g sisyphi", { timeout: 15e3, stdio: "pipe", env });
|
|
2188
2202
|
const result = execSync3("npm ls -g sisyphi --json --depth=0", {
|
|
@@ -2204,7 +2218,7 @@ function applyUpdate(expectedVersion) {
|
|
|
2204
2218
|
}
|
|
2205
2219
|
function markUpdating(version) {
|
|
2206
2220
|
try {
|
|
2207
|
-
|
|
2221
|
+
writeFileSync6(daemonUpdatingPath(), version, "utf-8");
|
|
2208
2222
|
} catch {
|
|
2209
2223
|
}
|
|
2210
2224
|
}
|
|
@@ -2234,6 +2248,11 @@ async function checkAndApply() {
|
|
|
2234
2248
|
}
|
|
2235
2249
|
|
|
2236
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
|
+
}
|
|
2237
2256
|
var ts = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
2238
2257
|
var origLog = console.log.bind(console);
|
|
2239
2258
|
var origError = console.error.bind(console);
|
|
@@ -2265,7 +2284,7 @@ function acquirePidLock() {
|
|
|
2265
2284
|
console.error(`[sisyphus] Daemon already running (pid ${pid}). Use 'sisyphusd restart' or 'sisyphusd stop' first.`);
|
|
2266
2285
|
process.exit(0);
|
|
2267
2286
|
}
|
|
2268
|
-
|
|
2287
|
+
writeFileSync7(daemonPidPath(), String(process.pid), "utf-8");
|
|
2269
2288
|
}
|
|
2270
2289
|
function isLaunchdManaged() {
|
|
2271
2290
|
try {
|
|
@@ -2324,7 +2343,7 @@ async function recoverSessions() {
|
|
|
2324
2343
|
let recovered = 0;
|
|
2325
2344
|
for (const [sessionId, cwd] of entries) {
|
|
2326
2345
|
const stateFile = statePath(cwd, sessionId);
|
|
2327
|
-
if (!
|
|
2346
|
+
if (!existsSync9(stateFile)) {
|
|
2328
2347
|
continue;
|
|
2329
2348
|
}
|
|
2330
2349
|
try {
|