sisyphi 1.1.31 → 1.1.33
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/README.md +1 -0
- package/deploy/shared/cloud-init.yaml.tpl +3 -0
- package/dist/cli.js +164 -77
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +692 -322
- package/dist/daemon.js.map +1 -1
- package/dist/deploy/shared/cloud-init.yaml.tpl +3 -0
- package/dist/templates/agent-plugin/hooks/hooks.json +51 -0
- package/dist/tui.js +103 -41
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/templates/agent-plugin/hooks/hooks.json +51 -0
package/README.md
CHANGED
|
@@ -160,6 +160,7 @@ Key bindings — navigate mode:
|
|
|
160
160
|
| `e` | Edit context file |
|
|
161
161
|
| `S` | Session info |
|
|
162
162
|
| `F` | Filter / search |
|
|
163
|
+
| `D` | Toggle DANGEROUS mode for the selected session (auto-accepts the default for every ask) |
|
|
163
164
|
| `c` | Open companion overlay |
|
|
164
165
|
| `/` | Search sessions |
|
|
165
166
|
| `q` | Quit |
|
|
@@ -96,6 +96,9 @@ runcmd:
|
|
|
96
96
|
# 6. Sisyphus install (root → /usr/bin/sisyphusd symlink).
|
|
97
97
|
- npm i -g sisyphi@${sisyphus_version}
|
|
98
98
|
|
|
99
|
+
# 6b. Claude Code CLI (sisyphus drives it for agent sessions).
|
|
100
|
+
- npm i -g @anthropic-ai/claude-code
|
|
101
|
+
|
|
99
102
|
# 7. Daemon as systemd user service.
|
|
100
103
|
- sudo -u sisyphus XDG_RUNTIME_DIR=/run/user/$(id -u sisyphus) systemctl --user daemon-reload
|
|
101
104
|
- sudo -u sisyphus XDG_RUNTIME_DIR=/run/user/$(id -u sisyphus) systemctl --user enable --now sisyphusd
|
package/dist/cli.js
CHANGED
|
@@ -36,6 +36,12 @@ function projectDir(cwd) {
|
|
|
36
36
|
function projectConfigPath(cwd) {
|
|
37
37
|
return join(projectDir(cwd), "config.json");
|
|
38
38
|
}
|
|
39
|
+
function projectAgentPluginDir(cwd) {
|
|
40
|
+
return join(projectDir(cwd), "agent-plugin");
|
|
41
|
+
}
|
|
42
|
+
function userAgentPluginDir() {
|
|
43
|
+
return join(globalDir(), "agent-plugin");
|
|
44
|
+
}
|
|
39
45
|
function sessionsDir(cwd) {
|
|
40
46
|
return join(projectDir(cwd), "sessions");
|
|
41
47
|
}
|
|
@@ -2320,6 +2326,11 @@ function getTmuxSessionInfo() {
|
|
|
2320
2326
|
function shellQuote(s) {
|
|
2321
2327
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
2322
2328
|
}
|
|
2329
|
+
function shellQuoteHomePath(path) {
|
|
2330
|
+
if (path === "~") return "~";
|
|
2331
|
+
if (path.startsWith("~/")) return `~/${shellQuote(path.slice(2))}`;
|
|
2332
|
+
return shellQuote(path);
|
|
2333
|
+
}
|
|
2323
2334
|
function validateRepoName(repo) {
|
|
2324
2335
|
return !repo.includes("/") && !repo.includes("\\") && !repo.includes("..");
|
|
2325
2336
|
}
|
|
@@ -3277,13 +3288,79 @@ function emitHistoryEvent(sessionId, event, data) {
|
|
|
3277
3288
|
}
|
|
3278
3289
|
}
|
|
3279
3290
|
|
|
3291
|
+
// src/daemon/state.ts
|
|
3292
|
+
init_atomic();
|
|
3293
|
+
init_paths();
|
|
3294
|
+
import { copyFileSync, cpSync, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync2, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3295
|
+
import { join as join10 } from "path";
|
|
3296
|
+
|
|
3297
|
+
// src/shared/gitignore.ts
|
|
3298
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
3299
|
+
import { join as join9 } from "path";
|
|
3300
|
+
|
|
3301
|
+
// src/shared/types.ts
|
|
3302
|
+
var ORCHESTRATOR_ASKED_BY = "orchestrator";
|
|
3303
|
+
|
|
3304
|
+
// src/daemon/state.ts
|
|
3305
|
+
function withSessionLock(sessionId, fn) {
|
|
3306
|
+
return withLock(sessionId, fn);
|
|
3307
|
+
}
|
|
3308
|
+
function getSession(cwd, sessionId) {
|
|
3309
|
+
const content = readFileSync11(statePath(cwd, sessionId), "utf-8");
|
|
3310
|
+
const session2 = JSON.parse(content);
|
|
3311
|
+
if (session2.activeMs == null) session2.activeMs = 0;
|
|
3312
|
+
if (session2.userBlockedMs == null) session2.userBlockedMs = 0;
|
|
3313
|
+
for (const agent2 of session2.agents) {
|
|
3314
|
+
if (!agent2.repo) agent2.repo = ".";
|
|
3315
|
+
if (agent2.activeMs == null) agent2.activeMs = 0;
|
|
3316
|
+
if (agent2.orphaned == null) agent2.orphaned = false;
|
|
3317
|
+
}
|
|
3318
|
+
if (session2.orphaned == null) session2.orphaned = false;
|
|
3319
|
+
for (const cycle of session2.orchestratorCycles) {
|
|
3320
|
+
if (cycle.activeMs == null) cycle.activeMs = 0;
|
|
3321
|
+
if (cycle.userBlockedMs == null) cycle.userBlockedMs = 0;
|
|
3322
|
+
}
|
|
3323
|
+
return session2;
|
|
3324
|
+
}
|
|
3325
|
+
function saveSession(session2) {
|
|
3326
|
+
atomicWrite(statePath(session2.cwd, session2.id), JSON.stringify(session2, null, 2));
|
|
3327
|
+
}
|
|
3328
|
+
function isSessionDangerous(cwd, sessionId) {
|
|
3329
|
+
try {
|
|
3330
|
+
return getSession(cwd, sessionId).dangerousMode === true;
|
|
3331
|
+
} catch {
|
|
3332
|
+
return false;
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
async function incrementUserBlockedMs(cwd, sessionId, deltaMs, askedAt, askedBy) {
|
|
3336
|
+
if (deltaMs <= 0) return;
|
|
3337
|
+
return withSessionLock(sessionId, () => {
|
|
3338
|
+
const session2 = getSession(cwd, sessionId);
|
|
3339
|
+
session2.userBlockedMs = (session2.userBlockedMs ?? 0) + deltaMs;
|
|
3340
|
+
if (askedAt) {
|
|
3341
|
+
const askedAtMs = new Date(askedAt).getTime();
|
|
3342
|
+
const cycle = session2.orchestratorCycles.find((c2) => {
|
|
3343
|
+
const startMs = new Date(c2.timestamp).getTime();
|
|
3344
|
+
const endMs = c2.completedAt ? new Date(c2.completedAt).getTime() : Infinity;
|
|
3345
|
+
return startMs <= askedAtMs && askedAtMs < endMs;
|
|
3346
|
+
});
|
|
3347
|
+
if (cycle) cycle.userBlockedMs = (cycle.userBlockedMs ?? 0) + deltaMs;
|
|
3348
|
+
}
|
|
3349
|
+
if (askedBy && askedBy !== ORCHESTRATOR_ASKED_BY) {
|
|
3350
|
+
const agent2 = session2.agents.slice().reverse().find((a) => a.id === askedBy);
|
|
3351
|
+
if (agent2) agent2.userBlockedMs = (agent2.userBlockedMs ?? 0) + deltaMs;
|
|
3352
|
+
}
|
|
3353
|
+
saveSession(session2);
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3280
3357
|
// src/daemon/ask-store.ts
|
|
3281
3358
|
init_atomic();
|
|
3282
3359
|
|
|
3283
3360
|
// src/daemon/notify.ts
|
|
3284
3361
|
import { spawn, execFile } from "child_process";
|
|
3285
|
-
import { writeFileSync as
|
|
3286
|
-
import { join as
|
|
3362
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, existsSync as existsSync8 } from "fs";
|
|
3363
|
+
import { join as join11 } from "path";
|
|
3287
3364
|
import { homedir as homedir6 } from "os";
|
|
3288
3365
|
var TMUX_SOCKET = `/tmp/tmux-${process.getuid?.() ?? 0}/default`;
|
|
3289
3366
|
var SWITCH_SCRIPT = [
|
|
@@ -3326,24 +3403,24 @@ var SWITCH_SCRIPT = [
|
|
|
3326
3403
|
""
|
|
3327
3404
|
].join("\n");
|
|
3328
3405
|
function ensureSwitchScript() {
|
|
3329
|
-
const dir =
|
|
3330
|
-
const scriptPath2 =
|
|
3406
|
+
const dir = join11(homedir6(), ".sisyphus");
|
|
3407
|
+
const scriptPath2 = join11(dir, "notify-switch.sh");
|
|
3331
3408
|
try {
|
|
3332
|
-
|
|
3333
|
-
|
|
3409
|
+
mkdirSync5(dir, { recursive: true });
|
|
3410
|
+
writeFileSync7(scriptPath2, SWITCH_SCRIPT, { mode: 493 });
|
|
3334
3411
|
} catch {
|
|
3335
3412
|
}
|
|
3336
3413
|
}
|
|
3337
3414
|
var notifyProcess = null;
|
|
3338
3415
|
function getNotifyBinary() {
|
|
3339
|
-
return
|
|
3416
|
+
return join11(homedir6(), ".sisyphus", "SisyphusNotify.app", "Contents", "MacOS", "sisyphus-notify");
|
|
3340
3417
|
}
|
|
3341
3418
|
function ensureNotifyProcess() {
|
|
3342
3419
|
if (notifyProcess && !notifyProcess.killed && notifyProcess.stdin?.writable) {
|
|
3343
3420
|
return notifyProcess;
|
|
3344
3421
|
}
|
|
3345
3422
|
const binary = getNotifyBinary();
|
|
3346
|
-
if (!
|
|
3423
|
+
if (!existsSync8(binary)) {
|
|
3347
3424
|
return null;
|
|
3348
3425
|
}
|
|
3349
3426
|
notifyProcess = spawn(binary, [], {
|
|
@@ -3399,65 +3476,6 @@ function sendTerminalNotification(titleOrOpts, message, tmuxSession, level) {
|
|
|
3399
3476
|
});
|
|
3400
3477
|
}
|
|
3401
3478
|
|
|
3402
|
-
// src/daemon/state.ts
|
|
3403
|
-
init_atomic();
|
|
3404
|
-
init_paths();
|
|
3405
|
-
import { copyFileSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync11, readdirSync as readdirSync2, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
3406
|
-
import { join as join11 } from "path";
|
|
3407
|
-
|
|
3408
|
-
// src/shared/gitignore.ts
|
|
3409
|
-
import { existsSync as existsSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
3410
|
-
import { join as join10 } from "path";
|
|
3411
|
-
|
|
3412
|
-
// src/shared/types.ts
|
|
3413
|
-
var ORCHESTRATOR_ASKED_BY = "orchestrator";
|
|
3414
|
-
|
|
3415
|
-
// src/daemon/state.ts
|
|
3416
|
-
function withSessionLock(sessionId, fn) {
|
|
3417
|
-
return withLock(sessionId, fn);
|
|
3418
|
-
}
|
|
3419
|
-
function getSession(cwd, sessionId) {
|
|
3420
|
-
const content = readFileSync11(statePath(cwd, sessionId), "utf-8");
|
|
3421
|
-
const session2 = JSON.parse(content);
|
|
3422
|
-
if (session2.activeMs == null) session2.activeMs = 0;
|
|
3423
|
-
if (session2.userBlockedMs == null) session2.userBlockedMs = 0;
|
|
3424
|
-
for (const agent2 of session2.agents) {
|
|
3425
|
-
if (!agent2.repo) agent2.repo = ".";
|
|
3426
|
-
if (agent2.activeMs == null) agent2.activeMs = 0;
|
|
3427
|
-
if (agent2.orphaned == null) agent2.orphaned = false;
|
|
3428
|
-
}
|
|
3429
|
-
if (session2.orphaned == null) session2.orphaned = false;
|
|
3430
|
-
for (const cycle of session2.orchestratorCycles) {
|
|
3431
|
-
if (cycle.activeMs == null) cycle.activeMs = 0;
|
|
3432
|
-
if (cycle.userBlockedMs == null) cycle.userBlockedMs = 0;
|
|
3433
|
-
}
|
|
3434
|
-
return session2;
|
|
3435
|
-
}
|
|
3436
|
-
function saveSession(session2) {
|
|
3437
|
-
atomicWrite(statePath(session2.cwd, session2.id), JSON.stringify(session2, null, 2));
|
|
3438
|
-
}
|
|
3439
|
-
async function incrementUserBlockedMs(cwd, sessionId, deltaMs, askedAt, askedBy) {
|
|
3440
|
-
if (deltaMs <= 0) return;
|
|
3441
|
-
return withSessionLock(sessionId, () => {
|
|
3442
|
-
const session2 = getSession(cwd, sessionId);
|
|
3443
|
-
session2.userBlockedMs = (session2.userBlockedMs ?? 0) + deltaMs;
|
|
3444
|
-
if (askedAt) {
|
|
3445
|
-
const askedAtMs = new Date(askedAt).getTime();
|
|
3446
|
-
const cycle = session2.orchestratorCycles.find((c2) => {
|
|
3447
|
-
const startMs = new Date(c2.timestamp).getTime();
|
|
3448
|
-
const endMs = c2.completedAt ? new Date(c2.completedAt).getTime() : Infinity;
|
|
3449
|
-
return startMs <= askedAtMs && askedAtMs < endMs;
|
|
3450
|
-
});
|
|
3451
|
-
if (cycle) cycle.userBlockedMs = (cycle.userBlockedMs ?? 0) + deltaMs;
|
|
3452
|
-
}
|
|
3453
|
-
if (askedBy && askedBy !== ORCHESTRATOR_ASKED_BY) {
|
|
3454
|
-
const agent2 = session2.agents.slice().reverse().find((a) => a.id === askedBy);
|
|
3455
|
-
if (agent2) agent2.userBlockedMs = (agent2.userBlockedMs ?? 0) + deltaMs;
|
|
3456
|
-
}
|
|
3457
|
-
saveSession(session2);
|
|
3458
|
-
});
|
|
3459
|
-
}
|
|
3460
|
-
|
|
3461
3479
|
// src/daemon/ask-store.ts
|
|
3462
3480
|
var ACTIONABLE_KINDS = /* @__PURE__ */ new Set([
|
|
3463
3481
|
"validation",
|
|
@@ -3511,6 +3529,21 @@ function createAsk(cwd, sessionId, params) {
|
|
|
3511
3529
|
}
|
|
3512
3530
|
function writeDecisions(cwd, sessionId, askId, deck) {
|
|
3513
3531
|
atomicWrite(askDecisionsPath(cwd, sessionId, askId), JSON.stringify(deck, null, 2));
|
|
3532
|
+
void maybeAutoResolveAsk(cwd, sessionId, askId, deck);
|
|
3533
|
+
}
|
|
3534
|
+
function readDecisions(cwd, sessionId, askId) {
|
|
3535
|
+
const p = askDecisionsPath(cwd, sessionId, askId);
|
|
3536
|
+
try {
|
|
3537
|
+
return JSON.parse(readFileSync12(p, { encoding: "utf-8" }));
|
|
3538
|
+
} catch (_e) {
|
|
3539
|
+
return null;
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
function writeOutput(cwd, sessionId, askId, responses, completedAt) {
|
|
3543
|
+
atomicWrite(askOutputPath(cwd, sessionId, askId), JSON.stringify({
|
|
3544
|
+
responses,
|
|
3545
|
+
completedAt: completedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
3546
|
+
}, null, 2));
|
|
3514
3547
|
}
|
|
3515
3548
|
function readMeta(cwd, sessionId, askId) {
|
|
3516
3549
|
const p = askMetaPath(cwd, sessionId, askId);
|
|
@@ -3530,6 +3563,40 @@ async function updateMeta(cwd, sessionId, askId, patch) {
|
|
|
3530
3563
|
return next;
|
|
3531
3564
|
});
|
|
3532
3565
|
}
|
|
3566
|
+
function buildAutoResponses(deck) {
|
|
3567
|
+
const out = [];
|
|
3568
|
+
for (const interaction of deck.interactions) {
|
|
3569
|
+
const first = interaction.options[0];
|
|
3570
|
+
if (!first) continue;
|
|
3571
|
+
out.push({ id: interaction.id, selectedOptionId: first.id });
|
|
3572
|
+
}
|
|
3573
|
+
return out;
|
|
3574
|
+
}
|
|
3575
|
+
async function autoResolveAsk(cwd, sessionId, askId, deck) {
|
|
3576
|
+
try {
|
|
3577
|
+
if (existsSync9(askOutputPath(cwd, sessionId, askId))) return false;
|
|
3578
|
+
const d = deck ?? readDecisions(cwd, sessionId, askId);
|
|
3579
|
+
if (!d) return false;
|
|
3580
|
+
const responses = buildAutoResponses(d);
|
|
3581
|
+
if (responses.length === 0) return false;
|
|
3582
|
+
writeOutput(cwd, sessionId, askId, responses);
|
|
3583
|
+
await updateMeta(cwd, sessionId, askId, {
|
|
3584
|
+
status: "answered",
|
|
3585
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3586
|
+
});
|
|
3587
|
+
return true;
|
|
3588
|
+
} catch (err) {
|
|
3589
|
+
console.warn(`[sisyphus] dangerous-mode auto-resolve failed for ask ${askId}:`, err instanceof Error ? err.message : err);
|
|
3590
|
+
return false;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
async function maybeAutoResolveAsk(cwd, sessionId, askId, deck) {
|
|
3594
|
+
try {
|
|
3595
|
+
if (!isSessionDangerous(cwd, sessionId)) return;
|
|
3596
|
+
await autoResolveAsk(cwd, sessionId, askId, deck);
|
|
3597
|
+
} catch {
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3533
3600
|
|
|
3534
3601
|
// src/cli/commands/ask.ts
|
|
3535
3602
|
init_paths();
|
|
@@ -4230,6 +4297,7 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
4230
4297
|
import { join as join14, resolve as resolve5 } from "path";
|
|
4231
4298
|
|
|
4232
4299
|
// src/daemon/frontmatter.ts
|
|
4300
|
+
init_paths();
|
|
4233
4301
|
import { readFileSync as readFileSync16, existsSync as existsSync11, readdirSync as readdirSync5 } from "fs";
|
|
4234
4302
|
import { homedir as homedir7 } from "os";
|
|
4235
4303
|
import { join as join13, basename as basename4 } from "path";
|
|
@@ -4290,6 +4358,8 @@ function discoverAgentTypes(pluginDir, cwd) {
|
|
|
4290
4358
|
}
|
|
4291
4359
|
}
|
|
4292
4360
|
}
|
|
4361
|
+
scanDir(join13(projectAgentPluginDir(cwd), "agents"), null, "project-sis");
|
|
4362
|
+
scanDir(join13(userAgentPluginDir(), "agents"), null, "user-sis");
|
|
4293
4363
|
scanDir(join13(cwd, ".claude", "agents"), null, "project");
|
|
4294
4364
|
scanDir(join13(homedir7(), ".claude", "agents"), null, "user");
|
|
4295
4365
|
scanDir(join13(pluginDir, "agents"), "sisyphus", "bundled");
|
|
@@ -10296,7 +10366,7 @@ function ensureGroveInstalled(provider) {
|
|
|
10296
10366
|
}
|
|
10297
10367
|
}
|
|
10298
10368
|
function ensureGroveRegistered(provider, repo, instancePath) {
|
|
10299
|
-
const cmd = `grove register --update --name ${shellQuote(repo)} ${
|
|
10369
|
+
const cmd = `grove register --update --name ${shellQuote(repo)} ${shellQuoteHomePath(instancePath)}`;
|
|
10300
10370
|
const result = runOnBox(provider, cmd);
|
|
10301
10371
|
if (result.exitCode !== 0) {
|
|
10302
10372
|
throw new Error(`Failed to register grove project ${repo}: ${result.stderr || result.stdout}`);
|
|
@@ -10382,7 +10452,7 @@ function packageManagerInstallCmd(pm) {
|
|
|
10382
10452
|
init_paths();
|
|
10383
10453
|
function readSidecar(provider, repo) {
|
|
10384
10454
|
const path = boxCloudSidecarPath(repo);
|
|
10385
|
-
const result = runOnBox(provider, `cat ${
|
|
10455
|
+
const result = runOnBox(provider, `cat ${shellQuoteHomePath(path)} 2>/dev/null`);
|
|
10386
10456
|
if (result.exitCode !== 0 || !result.stdout.trim()) return null;
|
|
10387
10457
|
try {
|
|
10388
10458
|
const parsed = JSON.parse(result.stdout);
|
|
@@ -10396,8 +10466,8 @@ function writeSidecar(provider, repo, data) {
|
|
|
10396
10466
|
const path = boxCloudSidecarPath(repo);
|
|
10397
10467
|
const json = JSON.stringify(data, null, 2);
|
|
10398
10468
|
const cmd = [
|
|
10399
|
-
`mkdir -p ${
|
|
10400
|
-
`cat > ${
|
|
10469
|
+
`mkdir -p ${shellQuoteHomePath(dir)}`,
|
|
10470
|
+
`cat > ${shellQuoteHomePath(path)} <<'SISYPHUS_CLOUD_SIDECAR_EOF'`,
|
|
10401
10471
|
json,
|
|
10402
10472
|
"SISYPHUS_CLOUD_SIDECAR_EOF"
|
|
10403
10473
|
].join("\n");
|
|
@@ -10438,14 +10508,14 @@ Pass --name <slug> to disambiguate, or --fresh to overwrite.`
|
|
|
10438
10508
|
}
|
|
10439
10509
|
console.log(`\u2192 wiping ${remoteDir} and cloning ${localOrigin} on box...`);
|
|
10440
10510
|
const cloneCmd = [
|
|
10441
|
-
`rm -rf ${
|
|
10442
|
-
`mkdir -p ${
|
|
10443
|
-
`git clone ${shellQuote(localOrigin)} ${
|
|
10511
|
+
`rm -rf ${shellQuoteHomePath(remoteDir)}`,
|
|
10512
|
+
`mkdir -p ${shellQuoteHomePath("~/projects")}`,
|
|
10513
|
+
`git clone ${shellQuote(localOrigin)} ${shellQuoteHomePath(remoteDir)}`
|
|
10444
10514
|
].join(" && ");
|
|
10445
10515
|
const code = await runOnBoxStreaming(provider, cloneCmd);
|
|
10446
10516
|
if (code !== 0) throw new Error(`fresh clone failed (exit ${code})`);
|
|
10447
10517
|
} else {
|
|
10448
|
-
const mkdir = runOnBox(provider, `mkdir -p ${
|
|
10518
|
+
const mkdir = runOnBox(provider, `mkdir -p ${shellQuoteHomePath(remoteDir)}`);
|
|
10449
10519
|
if (mkdir.exitCode !== 0) {
|
|
10450
10520
|
throw new Error(`Failed to mkdir on box: ${mkdir.stderr}`);
|
|
10451
10521
|
}
|
|
@@ -10483,7 +10553,7 @@ async function cloudInstall(provider, repo) {
|
|
|
10483
10553
|
return;
|
|
10484
10554
|
}
|
|
10485
10555
|
console.log(`\u2192 ${pm} install in ${remoteDir} on box...`);
|
|
10486
|
-
const remoteCmd = `cd ${
|
|
10556
|
+
const remoteCmd = `cd ${shellQuoteHomePath(remoteDir)} && ${cmd}`;
|
|
10487
10557
|
const code = await runOnBoxStreaming(provider, remoteCmd);
|
|
10488
10558
|
if (code !== 0) throw new Error(`${pm} install failed (exit ${code})`);
|
|
10489
10559
|
const existing = readSidecar(provider, repo);
|
|
@@ -10499,7 +10569,7 @@ async function cloudInstall(provider, repo) {
|
|
|
10499
10569
|
}
|
|
10500
10570
|
async function cloudSession(provider, repo) {
|
|
10501
10571
|
const remoteDir = boxRepoPath(repo);
|
|
10502
|
-
const cmd = `sis admin home-init ${shellQuote(repo)} ${
|
|
10572
|
+
const cmd = `sis admin home-init ${shellQuote(repo)} ${shellQuoteHomePath(remoteDir)}`;
|
|
10503
10573
|
console.log(`\u2192 initializing tmux home session "${repo}" on box...`);
|
|
10504
10574
|
const result = runOnBox(provider, cmd);
|
|
10505
10575
|
if (result.exitCode !== 0) {
|
|
@@ -10523,6 +10593,19 @@ Use a fresh terminal, or run from outside tmux:
|
|
|
10523
10593
|
});
|
|
10524
10594
|
child.on("exit", (code) => process.exit(code === null ? 1 : code));
|
|
10525
10595
|
}
|
|
10596
|
+
function cloudClaudeLogin(provider) {
|
|
10597
|
+
const target = effectiveSshTarget(provider);
|
|
10598
|
+
const remote = [
|
|
10599
|
+
"command -v claude >/dev/null 2>&1",
|
|
10600
|
+
"|| sudo npm i -g @anthropic-ai/claude-code",
|
|
10601
|
+
"&& claude auth login"
|
|
10602
|
+
].join(" ");
|
|
10603
|
+
const child = spawn4("ssh", ["-t", target, remote], {
|
|
10604
|
+
stdio: "inherit",
|
|
10605
|
+
env: EXEC_ENV
|
|
10606
|
+
});
|
|
10607
|
+
child.on("exit", (code) => process.exit(code === null ? 1 : code));
|
|
10608
|
+
}
|
|
10526
10609
|
async function cloudStart(provider, repo, opts) {
|
|
10527
10610
|
await cloudSync(provider, repo, { fresh: opts.fresh, yes: opts.yes });
|
|
10528
10611
|
await cloudInstall(provider, repo);
|
|
@@ -10605,6 +10688,10 @@ function registerCloud(program2) {
|
|
|
10605
10688
|
const { provider, repo } = resolve11(raw);
|
|
10606
10689
|
await cloudStart(provider, repo, { fresh: raw.fresh === true, yes: raw.yes === true });
|
|
10607
10690
|
});
|
|
10691
|
+
cloud.command("claude-login").description("Run `claude auth login` on the box (device-code flow; paste the URL into your local browser).").option("--provider <name>", "Cloud provider.").action((raw) => {
|
|
10692
|
+
const provider = pickProvider(raw.provider);
|
|
10693
|
+
cloudClaudeLogin(provider);
|
|
10694
|
+
});
|
|
10608
10695
|
cloud.command("status").description("Print box-side status for this repo (planted, session running, last sync/install).").option("--name <repo>", "Override the repo name.").option("--provider <name>", "Cloud provider.").action((raw) => {
|
|
10609
10696
|
const { provider, repo } = resolve11(raw);
|
|
10610
10697
|
cloudStatus(provider, repo);
|