svamp-cli 0.2.68 → 0.2.71
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/{agentCommands-QnPGJFY-.mjs → agentCommands-COEbsMuw.mjs} +2 -2
- package/dist/cli.mjs +127 -68
- package/dist/{commands-D5kCHCfX.mjs → commands-B0zqVia0.mjs} +374 -46
- package/dist/{commands-Cxb6tsha.mjs → commands-Cc8AE2jl.mjs} +3 -3
- package/dist/{commands-tUjBdy54.mjs → commands-Kztc20Nx.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-DhOTD1Ln.mjs → package-CDSBvCp_.mjs} +2 -2
- package/dist/{run-BvcwFt_g.mjs → run-B_bKTQ8M.mjs} +3 -3
- package/dist/{run-HuBXfVSz.mjs → run-h-QVSVFd.mjs} +211 -49
- package/dist/{serveCommands-DDlHXpPe.mjs → serveCommands-CM17DByZ.mjs} +5 -5
- package/dist/{serveManager-DWQtF8NK.mjs → serveManager-Bzjw2bO6.mjs} +1 -1
- package/package.json +2 -2
|
@@ -754,12 +754,36 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
754
754
|
}
|
|
755
755
|
return result;
|
|
756
756
|
},
|
|
757
|
-
//
|
|
758
|
-
|
|
757
|
+
// Archive a session (non-destructive — preserves claudeResumeId for resume)
|
|
758
|
+
archiveSession: async (sessionId, context) => {
|
|
759
759
|
authorizeRequest(context, currentMetadata.sharing, "admin");
|
|
760
|
-
const result = handlers.
|
|
760
|
+
const result = handlers.archiveSession(sessionId);
|
|
761
761
|
notifyListeners({
|
|
762
|
-
type: "session-
|
|
762
|
+
type: "session-archived",
|
|
763
|
+
sessionId,
|
|
764
|
+
machineId
|
|
765
|
+
});
|
|
766
|
+
return result;
|
|
767
|
+
},
|
|
768
|
+
// Resume an archived session (spawns the agent with --resume <claudeResumeId>)
|
|
769
|
+
resumeSession: async (sessionId, context) => {
|
|
770
|
+
authorizeRequest(context, currentMetadata.sharing, "admin");
|
|
771
|
+
const result = await handlers.resumeSession(sessionId);
|
|
772
|
+
if (result.success) {
|
|
773
|
+
notifyListeners({
|
|
774
|
+
type: "session-resumed",
|
|
775
|
+
sessionId,
|
|
776
|
+
machineId
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
},
|
|
781
|
+
// Permanently delete a session (wipes session.json + messages.jsonl + .svamp/{id}/)
|
|
782
|
+
deleteSession: async (sessionId, context) => {
|
|
783
|
+
authorizeRequest(context, currentMetadata.sharing, "admin");
|
|
784
|
+
const result = handlers.deleteSession(sessionId);
|
|
785
|
+
notifyListeners({
|
|
786
|
+
type: "session-deleted",
|
|
763
787
|
sessionId,
|
|
764
788
|
machineId
|
|
765
789
|
});
|
|
@@ -1116,9 +1140,10 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
1116
1140
|
const { join: join2, resolve } = await import('path');
|
|
1117
1141
|
const { homedir } = await import('os');
|
|
1118
1142
|
const targetPath = resolve(path || homedir());
|
|
1143
|
+
const effectiveRole = getEffectiveRole(context, currentMetadata.sharing);
|
|
1119
1144
|
const home = homedir();
|
|
1120
|
-
const
|
|
1121
|
-
if (
|
|
1145
|
+
const restrictedToHome = effectiveRole === "view";
|
|
1146
|
+
if (restrictedToHome && targetPath !== home && !targetPath.startsWith(home + "/")) {
|
|
1122
1147
|
throw new Error(`Access denied: path must be within ${home}`);
|
|
1123
1148
|
}
|
|
1124
1149
|
const showHidden = options?.showHidden ?? false;
|
|
@@ -1572,7 +1597,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
1572
1597
|
}
|
|
1573
1598
|
|
|
1574
1599
|
function isStructuredMessage(msg) {
|
|
1575
|
-
return !!(msg.from
|
|
1600
|
+
return !!(msg.from || msg.fromSession || msg.subject || msg.replyTo || msg.threadId);
|
|
1576
1601
|
}
|
|
1577
1602
|
function escapeXml(s) {
|
|
1578
1603
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -1960,9 +1985,9 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
1960
1985
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
1961
1986
|
return await callbacks.onRestartClaude();
|
|
1962
1987
|
},
|
|
1963
|
-
|
|
1988
|
+
archiveSession: async (context) => {
|
|
1964
1989
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
1965
|
-
callbacks.
|
|
1990
|
+
callbacks.onArchiveSession();
|
|
1966
1991
|
return { success: true };
|
|
1967
1992
|
},
|
|
1968
1993
|
// ── Activity ──
|
|
@@ -2240,6 +2265,8 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
2240
2265
|
claudeSessionId,
|
|
2241
2266
|
"--fork-session",
|
|
2242
2267
|
"--no-session-persistence",
|
|
2268
|
+
"--permission-mode",
|
|
2269
|
+
"bypassPermissions",
|
|
2243
2270
|
"--output-format",
|
|
2244
2271
|
"json",
|
|
2245
2272
|
"--max-turns",
|
|
@@ -5676,10 +5703,16 @@ class ServeAuth {
|
|
|
5676
5703
|
return access.some((e) => e.toLowerCase() === email.toLowerCase());
|
|
5677
5704
|
}
|
|
5678
5705
|
/**
|
|
5679
|
-
* Generate the login page HTML.
|
|
5680
|
-
*
|
|
5681
|
-
*
|
|
5682
|
-
*
|
|
5706
|
+
* Generate the login page HTML.
|
|
5707
|
+
*
|
|
5708
|
+
* Uses the Hypha-Login HTTP endpoints directly (no SDK / no WebSocket):
|
|
5709
|
+
* 1. GET /public/services/hypha-login/start → { login_url, key }
|
|
5710
|
+
* 2. open popup to login_url so the user signs in
|
|
5711
|
+
* 3. GET /public/services/hypha-login/check?key=… → token (long-polled)
|
|
5712
|
+
*
|
|
5713
|
+
* This matches the proven pattern in svamp-app/.../share/[id].tsx and
|
|
5714
|
+
* removes a class of hangs we previously saw when the JS SDK never
|
|
5715
|
+
* invoked its `login_callback` (button-stuck-after-click symptom).
|
|
5683
5716
|
*/
|
|
5684
5717
|
getLoginPageHtml(redirectUrl) {
|
|
5685
5718
|
return `<!DOCTYPE html>
|
|
@@ -5702,7 +5735,6 @@ button:disabled{background:#94d3a2;cursor:wait}
|
|
|
5702
5735
|
a{color:#0969da;text-decoration:none}
|
|
5703
5736
|
a:hover{text-decoration:underline}
|
|
5704
5737
|
</style>
|
|
5705
|
-
<script src="https://cdn.jsdelivr.net/npm/hypha-rpc@0.21.28/dist/hypha-rpc-websocket.min.js"><\/script>
|
|
5706
5738
|
</head><body>
|
|
5707
5739
|
<div class="card">
|
|
5708
5740
|
<h1>Sign in required</h1>
|
|
@@ -5724,35 +5756,62 @@ function setError(msg) {
|
|
|
5724
5756
|
}
|
|
5725
5757
|
|
|
5726
5758
|
btn.addEventListener('click', async () => {
|
|
5727
|
-
|
|
5728
|
-
|
|
5759
|
+
// Open the popup SYNCHRONOUSLY inside the click handler so the browser's
|
|
5760
|
+
// popup blocker treats it as user-initiated. The HTTP request below is
|
|
5761
|
+
// awaited, by which time the user-gesture flag has cleared and any
|
|
5762
|
+
// window.open() call would be silently blocked.
|
|
5763
|
+
const popup = window.open('about:blank', '_blank');
|
|
5764
|
+
if (!popup) {
|
|
5765
|
+
setError('Popup was blocked by the browser. Please allow popups for this site and retry.');
|
|
5729
5766
|
return;
|
|
5730
5767
|
}
|
|
5768
|
+
try { popup.document.write('<html><body style="font-family:system-ui;padding:24px;color:#656d76">Opening Hypha sign-in\u2026</body></html>'); } catch (e) {}
|
|
5731
5769
|
|
|
5732
5770
|
btn.disabled = true;
|
|
5733
|
-
statusEl.textContent = '
|
|
5771
|
+
statusEl.textContent = 'Contacting Hypha\u2026';
|
|
5734
5772
|
|
|
5735
5773
|
try {
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5774
|
+
// 1. Ask the Hypha-Login service for a login URL + polling key.
|
|
5775
|
+
const startResp = await fetch(hyphaServer + '/public/services/hypha-login/start');
|
|
5776
|
+
if (!startResp.ok) throw new Error('Login start failed: HTTP ' + startResp.status);
|
|
5777
|
+
const ctx = await startResp.json();
|
|
5778
|
+
if (!ctx || !ctx.login_url || !ctx.key) throw new Error('Login start returned no URL/key');
|
|
5779
|
+
|
|
5780
|
+
// 2. Redirect the popup to the actual login UI.
|
|
5781
|
+
statusEl.textContent = 'Waiting for you to sign in\u2026';
|
|
5782
|
+
try { popup.location.href = ctx.login_url; }
|
|
5783
|
+
catch (e) { window.open(ctx.login_url, '_blank'); }
|
|
5784
|
+
|
|
5785
|
+
// 3. Long-poll the check endpoint until the user finishes signing in.
|
|
5786
|
+
// Each request waits up to 3 s server-side; we retry for ~3 minutes total.
|
|
5787
|
+
const checkBase = ctx.check_url || (hyphaServer + '/public/services/hypha-login/check');
|
|
5788
|
+
let token = null;
|
|
5789
|
+
for (let i = 0; i < 60; i++) {
|
|
5790
|
+
if (popup.closed) {
|
|
5791
|
+
// User dismissed the popup before completing \u2014 keep polling briefly
|
|
5792
|
+
// (the report endpoint may still fire even if window is gone).
|
|
5793
|
+
}
|
|
5794
|
+
try {
|
|
5795
|
+
const r = await fetch(checkBase + '?key=' + encodeURIComponent(ctx.key) + '&timeout=3');
|
|
5796
|
+
if (r.ok) {
|
|
5797
|
+
const body = await r.json();
|
|
5798
|
+
const t = typeof body === 'string' ? body : (body && (body.token || body));
|
|
5799
|
+
if (typeof t === 'string' && t.length > 20) { token = t; break; }
|
|
5800
|
+
}
|
|
5801
|
+
} catch (e) { /* transient \u2014 retry */ }
|
|
5802
|
+
}
|
|
5746
5803
|
|
|
5747
|
-
if (!token) throw new Error('
|
|
5804
|
+
if (!token) throw new Error('Timed out waiting for sign-in. Please try again.');
|
|
5748
5805
|
|
|
5749
|
-
// Set the cookie so subsequent requests to this origin are authenticated.
|
|
5806
|
+
// 4. Set the cookie so subsequent requests to this origin are authenticated.
|
|
5750
5807
|
const secure = location.protocol === 'https:' ? '; Secure' : '';
|
|
5751
5808
|
document.cookie = cookieName + '=' + token + '; path=/; SameSite=Lax' + secure;
|
|
5752
5809
|
|
|
5810
|
+
try { popup.close(); } catch (e) {}
|
|
5753
5811
|
statusEl.innerHTML = '<span class="ok">Signed in. Redirecting\u2026</span>';
|
|
5754
5812
|
setTimeout(() => { window.location.replace(redirectUrl); }, 300);
|
|
5755
5813
|
} catch (err) {
|
|
5814
|
+
try { popup.close(); } catch (e) {}
|
|
5756
5815
|
setError('Login failed: ' + (err && err.message ? err.message : err));
|
|
5757
5816
|
}
|
|
5758
5817
|
});
|
|
@@ -7470,6 +7529,43 @@ function deletePersistedSession(sessionId) {
|
|
|
7470
7529
|
saveSessionIndex(index);
|
|
7471
7530
|
}
|
|
7472
7531
|
}
|
|
7532
|
+
function markSessionAsArchived(sessionId) {
|
|
7533
|
+
const index = loadSessionIndex();
|
|
7534
|
+
const entry = index[sessionId];
|
|
7535
|
+
if (!entry) return false;
|
|
7536
|
+
const filePath = getSessionFilePath(entry.directory, sessionId);
|
|
7537
|
+
if (!existsSync$1(filePath)) return false;
|
|
7538
|
+
try {
|
|
7539
|
+
const data = JSON.parse(readFileSync$1(filePath, "utf-8"));
|
|
7540
|
+
if (data.stopped === true) return true;
|
|
7541
|
+
data.stopped = true;
|
|
7542
|
+
const tmpPath = filePath + ".tmp";
|
|
7543
|
+
writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7544
|
+
renameSync(tmpPath, filePath);
|
|
7545
|
+
return true;
|
|
7546
|
+
} catch {
|
|
7547
|
+
return false;
|
|
7548
|
+
}
|
|
7549
|
+
}
|
|
7550
|
+
function clearSessionArchivedFlag(sessionId) {
|
|
7551
|
+
const index = loadSessionIndex();
|
|
7552
|
+
const entry = index[sessionId];
|
|
7553
|
+
if (!entry) return null;
|
|
7554
|
+
const filePath = getSessionFilePath(entry.directory, sessionId);
|
|
7555
|
+
if (!existsSync$1(filePath)) return null;
|
|
7556
|
+
try {
|
|
7557
|
+
const data = JSON.parse(readFileSync$1(filePath, "utf-8"));
|
|
7558
|
+
if (data.stopped) {
|
|
7559
|
+
delete data.stopped;
|
|
7560
|
+
const tmpPath = filePath + ".tmp";
|
|
7561
|
+
writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7562
|
+
renameSync(tmpPath, filePath);
|
|
7563
|
+
}
|
|
7564
|
+
return data;
|
|
7565
|
+
} catch {
|
|
7566
|
+
return null;
|
|
7567
|
+
}
|
|
7568
|
+
}
|
|
7473
7569
|
function loadPersistedSessions() {
|
|
7474
7570
|
const sessions = [];
|
|
7475
7571
|
const index = loadSessionIndex();
|
|
@@ -7771,7 +7867,7 @@ async function startDaemon(options) {
|
|
|
7771
7867
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
7772
7868
|
saveExposedTunnels(list);
|
|
7773
7869
|
}
|
|
7774
|
-
const { ServeManager } = await import('./serveManager-
|
|
7870
|
+
const { ServeManager } = await import('./serveManager-Bzjw2bO6.mjs');
|
|
7775
7871
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
7776
7872
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
7777
7873
|
});
|
|
@@ -7956,6 +8052,7 @@ async function startDaemon(options) {
|
|
|
7956
8052
|
startedFromDaemon: true,
|
|
7957
8053
|
startedBy: "daemon",
|
|
7958
8054
|
lifecycleState: resumeSessionId ? "idle" : "starting",
|
|
8055
|
+
...resumeSessionId && { claudeSessionId: resumeSessionId },
|
|
7959
8056
|
sharing: options2.sharing,
|
|
7960
8057
|
securityContext: options2.securityContext,
|
|
7961
8058
|
tags: options2.tags,
|
|
@@ -7971,7 +8068,7 @@ async function startDaemon(options) {
|
|
|
7971
8068
|
const allPersisted = loadPersistedSessions();
|
|
7972
8069
|
const persisted = allPersisted.find((p) => p.sessionId === sessionId) || (resumeSessionId ? allPersisted.find((p) => p.claudeResumeId === resumeSessionId) : void 0);
|
|
7973
8070
|
let claudeResumeId = persisted?.claudeResumeId || (resumeSessionId || void 0);
|
|
7974
|
-
let currentPermissionMode = options2.permissionMode || persisted?.permissionMode || "
|
|
8071
|
+
let currentPermissionMode = options2.permissionMode || persisted?.permissionMode || "bypassPermissions";
|
|
7975
8072
|
const sessionCreatedAt = persisted?.createdAt || Date.now();
|
|
7976
8073
|
let lastSpawnMeta = persisted?.spawnMeta || {};
|
|
7977
8074
|
let sessionWasProcessing = !!options2.wasProcessing;
|
|
@@ -8679,7 +8776,16 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8679
8776
|
artifactSync.scheduleDebouncedSync(sessionId, getSessionDir(directory, sessionId), sessionMetadata, machineId);
|
|
8680
8777
|
}
|
|
8681
8778
|
if (isResumeFailure) {
|
|
8682
|
-
|
|
8779
|
+
const triedId = persisted?.claudeResumeId ?? "unknown";
|
|
8780
|
+
logger.log(`[Session ${sessionId}] Resume failed \u2014 Claude started fresh session (tried: ${triedId}, got: ${msg.session_id})`);
|
|
8781
|
+
sessionService.pushMessage(
|
|
8782
|
+
{
|
|
8783
|
+
type: "message",
|
|
8784
|
+
message: `Resume incomplete \u2014 Claude could not restore the previous conversation. A new Claude session was started (expected ${triedId.slice(0, 8)}\u2026, got ${msg.session_id.slice(0, 8)}\u2026). Earlier messages remain in the history above for reference, but Claude does not have them in its context.`,
|
|
8785
|
+
level: "warning"
|
|
8786
|
+
},
|
|
8787
|
+
"event"
|
|
8788
|
+
);
|
|
8683
8789
|
} else if (isConversationClear) {
|
|
8684
8790
|
logger.log(`[Session ${sessionId}] Conversation cleared (/clear) \u2014 new Claude session: ${msg.session_id}`);
|
|
8685
8791
|
sessionService.clearMessages();
|
|
@@ -8941,6 +9047,11 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
8941
9047
|
claudeResumeId,
|
|
8942
9048
|
"--fork-session",
|
|
8943
9049
|
"--no-session-persistence",
|
|
9050
|
+
// /btw is non-interactive; without bypass the
|
|
9051
|
+
// forked Claude pauses on tool prompts and the
|
|
9052
|
+
// user just sees a hanging side-channel.
|
|
9053
|
+
"--permission-mode",
|
|
9054
|
+
"bypassPermissions",
|
|
8944
9055
|
"--output-format",
|
|
8945
9056
|
"stream-json",
|
|
8946
9057
|
"--verbose"
|
|
@@ -9075,6 +9186,9 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
9075
9186
|
},
|
|
9076
9187
|
onSwitchMode: async (mode) => {
|
|
9077
9188
|
const normalizedMode = toClaudePermissionMode(mode);
|
|
9189
|
+
if (currentPermissionMode === normalizedMode) {
|
|
9190
|
+
return;
|
|
9191
|
+
}
|
|
9078
9192
|
logger.log(`[Session ${sessionId}] Switch mode: ${mode}${mode !== normalizedMode ? ` \u2192 ${normalizedMode}` : ""}`);
|
|
9079
9193
|
if (isRestartingClaude || isSwitchingMode) {
|
|
9080
9194
|
logger.log(`[Session ${sessionId}] Switch mode deferred \u2014 restart/switch already in progress`);
|
|
@@ -9153,15 +9267,14 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
9153
9267
|
lastSpawnMeta = { ...lastSpawnMeta, appendSystemPrompt: prompt };
|
|
9154
9268
|
return await restartClaudeHandler();
|
|
9155
9269
|
},
|
|
9156
|
-
|
|
9157
|
-
logger.log(`[Session ${sessionId}]
|
|
9158
|
-
|
|
9270
|
+
onArchiveSession: () => {
|
|
9271
|
+
logger.log(`[Session ${sessionId}] Archive session requested`);
|
|
9272
|
+
archiveSession(sessionId);
|
|
9159
9273
|
},
|
|
9160
9274
|
onInboxMessage: (message) => {
|
|
9161
9275
|
if (trackedSession?.stopped) return;
|
|
9162
9276
|
logger.log(`[Session ${sessionId}] Inbox message received (urgency: ${message.urgency || "normal"}, from: ${message.from || "unknown"})`);
|
|
9163
9277
|
const formatted = formatInboxMessageXml(message);
|
|
9164
|
-
sessionService.markInboxRead(message.messageId);
|
|
9165
9278
|
if (message.urgency === "urgent") {
|
|
9166
9279
|
logger.log(`[Session ${sessionId}] Delivering urgent inbox message to agent`);
|
|
9167
9280
|
sessionService.pushMessage(formatted, "user");
|
|
@@ -9511,7 +9624,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
9511
9624
|
tags: options2.tags,
|
|
9512
9625
|
parentSessionId: options2.parentSessionId
|
|
9513
9626
|
};
|
|
9514
|
-
let currentPermissionMode = options2.permissionMode || "
|
|
9627
|
+
let currentPermissionMode = options2.permissionMode || "bypassPermissions";
|
|
9515
9628
|
const allowedTools = /* @__PURE__ */ new Set();
|
|
9516
9629
|
const allowedBashLiterals = /* @__PURE__ */ new Set();
|
|
9517
9630
|
const allowedBashPrefixes = /* @__PURE__ */ new Set();
|
|
@@ -9605,6 +9718,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
9605
9718
|
agentBackend.respondToPermission?.(requestId, params.approved);
|
|
9606
9719
|
},
|
|
9607
9720
|
onSwitchMode: (mode) => {
|
|
9721
|
+
if (currentPermissionMode === mode) return;
|
|
9608
9722
|
logger.log(`[${agentName} Session ${sessionId}] Switch mode: ${mode}`);
|
|
9609
9723
|
currentPermissionMode = mode;
|
|
9610
9724
|
},
|
|
@@ -9624,15 +9738,14 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
9624
9738
|
onApplySystemPrompt: async () => {
|
|
9625
9739
|
return { success: false, message: "System prompt updates with restart are not yet supported for this agent type." };
|
|
9626
9740
|
},
|
|
9627
|
-
|
|
9628
|
-
logger.log(`[${agentName} Session ${sessionId}]
|
|
9629
|
-
|
|
9741
|
+
onArchiveSession: () => {
|
|
9742
|
+
logger.log(`[${agentName} Session ${sessionId}] Archive session requested`);
|
|
9743
|
+
archiveSession(sessionId);
|
|
9630
9744
|
},
|
|
9631
9745
|
onInboxMessage: (message) => {
|
|
9632
9746
|
if (acpStopped) return;
|
|
9633
9747
|
logger.log(`[${agentName} Session ${sessionId}] Inbox message received (urgency: ${message.urgency || "normal"}, from: ${message.from || "unknown"})`);
|
|
9634
9748
|
const formatted = formatInboxMessageXml(message);
|
|
9635
|
-
sessionService.markInboxRead(message.messageId);
|
|
9636
9749
|
if (message.urgency === "urgent" && acpBackendReady) {
|
|
9637
9750
|
logger.log(`[${agentName} Session ${sessionId}] Delivering urgent inbox message to agent`);
|
|
9638
9751
|
sessionService.pushMessage(formatted, "user");
|
|
@@ -10082,7 +10195,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
10082
10195
|
"event"
|
|
10083
10196
|
);
|
|
10084
10197
|
sessionService.sendSessionEnd();
|
|
10085
|
-
|
|
10198
|
+
deleteSession(sessionId);
|
|
10086
10199
|
});
|
|
10087
10200
|
return {
|
|
10088
10201
|
type: "success",
|
|
@@ -10097,8 +10210,7 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
10097
10210
|
};
|
|
10098
10211
|
}
|
|
10099
10212
|
};
|
|
10100
|
-
const
|
|
10101
|
-
logger.log(`Stopping session: ${sessionId}`);
|
|
10213
|
+
const teardownTrackedSession = (sessionId) => {
|
|
10102
10214
|
for (const [pid, session] of pidToTrackedSession) {
|
|
10103
10215
|
if (session.svampSessionId === sessionId) {
|
|
10104
10216
|
session.stopped = true;
|
|
@@ -10117,16 +10229,64 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
10117
10229
|
session.cleanupSvampConfig?.();
|
|
10118
10230
|
artifactSync.cancelSync(sessionId);
|
|
10119
10231
|
pidToTrackedSession.delete(pid);
|
|
10120
|
-
deletePersistedSession(sessionId);
|
|
10121
|
-
logger.log(`Session ${sessionId} stopped`);
|
|
10122
10232
|
return true;
|
|
10123
10233
|
}
|
|
10124
10234
|
}
|
|
10125
10235
|
artifactSync.cancelSync(sessionId);
|
|
10126
|
-
deletePersistedSession(sessionId);
|
|
10127
|
-
logger.log(`Session ${sessionId} not found in memory, cleaned up persisted state`);
|
|
10128
10236
|
return false;
|
|
10129
10237
|
};
|
|
10238
|
+
const archiveSession = (sessionId) => {
|
|
10239
|
+
logger.log(`Archiving session: ${sessionId}`);
|
|
10240
|
+
const wasInMemory = teardownTrackedSession(sessionId);
|
|
10241
|
+
const markedArchived = markSessionAsArchived(sessionId);
|
|
10242
|
+
if (wasInMemory || markedArchived) {
|
|
10243
|
+
logger.log(`Session ${sessionId} archived (inMemory=${wasInMemory}, persisted=${markedArchived})`);
|
|
10244
|
+
return true;
|
|
10245
|
+
}
|
|
10246
|
+
logger.log(`Session ${sessionId} not found in memory or on disk; nothing to archive`);
|
|
10247
|
+
return false;
|
|
10248
|
+
};
|
|
10249
|
+
const resumeSession = async (sessionId) => {
|
|
10250
|
+
logger.log(`Resuming session: ${sessionId}`);
|
|
10251
|
+
for (const session of pidToTrackedSession.values()) {
|
|
10252
|
+
if (session.svampSessionId === sessionId && !session.stopped) {
|
|
10253
|
+
logger.log(`Session ${sessionId} already running \u2014 resume is a no-op`);
|
|
10254
|
+
return { success: true, sessionId, message: "Session is already running" };
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
const persisted = clearSessionArchivedFlag(sessionId);
|
|
10258
|
+
if (!persisted) {
|
|
10259
|
+
return { success: false, message: `Session ${sessionId} has no persisted record to resume` };
|
|
10260
|
+
}
|
|
10261
|
+
try {
|
|
10262
|
+
const result = await spawnSession({
|
|
10263
|
+
directory: persisted.directory,
|
|
10264
|
+
sessionId: persisted.sessionId,
|
|
10265
|
+
resumeSessionId: persisted.claudeResumeId,
|
|
10266
|
+
sharing: persisted.metadata?.sharing,
|
|
10267
|
+
securityContext: persisted.metadata?.securityContext,
|
|
10268
|
+
forceIsolation: !!persisted.metadata?.isolationMethod,
|
|
10269
|
+
parentSessionId: persisted.metadata?.parentSessionId,
|
|
10270
|
+
permissionMode: persisted.permissionMode
|
|
10271
|
+
});
|
|
10272
|
+
if (result.type === "success") {
|
|
10273
|
+
logger.log(`Resumed session ${sessionId} (claudeResumeId=${persisted.claudeResumeId || "none"})`);
|
|
10274
|
+
return { success: true, sessionId: result.sessionId };
|
|
10275
|
+
}
|
|
10276
|
+
markSessionAsArchived(sessionId);
|
|
10277
|
+
return { success: false, message: result.errorMessage || `spawnSession returned ${result.type}` };
|
|
10278
|
+
} catch (err) {
|
|
10279
|
+
markSessionAsArchived(sessionId);
|
|
10280
|
+
return { success: false, message: err.message };
|
|
10281
|
+
}
|
|
10282
|
+
};
|
|
10283
|
+
const deleteSession = (sessionId) => {
|
|
10284
|
+
logger.log(`Deleting session: ${sessionId}`);
|
|
10285
|
+
teardownTrackedSession(sessionId);
|
|
10286
|
+
deletePersistedSession(sessionId);
|
|
10287
|
+
logger.log(`Session ${sessionId} deleted`);
|
|
10288
|
+
return true;
|
|
10289
|
+
};
|
|
10130
10290
|
const restartSession = async (sessionId) => {
|
|
10131
10291
|
for (const session of pidToTrackedSession.values()) {
|
|
10132
10292
|
if (session.svampSessionId === sessionId && !session.stopped) {
|
|
@@ -10204,7 +10364,9 @@ The automated loop has finished. Review the progress above and let me know if yo
|
|
|
10204
10364
|
initialDaemonState,
|
|
10205
10365
|
{
|
|
10206
10366
|
spawnSession,
|
|
10207
|
-
|
|
10367
|
+
archiveSession,
|
|
10368
|
+
resumeSession,
|
|
10369
|
+
deleteSession,
|
|
10208
10370
|
restartSession,
|
|
10209
10371
|
requestShutdown: () => {
|
|
10210
10372
|
logger.log("Shutdown requested via hypha-app (ignored \u2014 daemon never self-terminates)");
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-B0zqVia0.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import { S as ServeAuth, h as hasCookieToken } from './run-
|
|
7
|
+
import { S as ServeAuth, h as hasCookieToken } from './run-h-QVSVFd.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.71",
|
|
4
4
|
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only",
|
|
23
|
+
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-ralph-loop.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-ralph-loop-integration.mjs && npx tsx test/test-ralph-loop-modes.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only",
|
|
24
24
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
25
25
|
"dev": "tsx src/cli.ts",
|
|
26
26
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|