svamp-cli 0.2.322 → 0.2.324
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/{adminCommands-CnJSw_fJ.mjs → adminCommands-CHE1wq5m.mjs} +1 -1
- package/dist/{agentCommands-ri042RB5.mjs → agentCommands-D7CV6DKL.mjs} +5 -5
- package/dist/{auth-CvQD_JbA.mjs → auth-BfimMIpL.mjs} +1 -1
- package/dist/{cli-BPJoSvJ0.mjs → cli-Dv0vM2P7.mjs} +82 -77
- package/dist/cli.mjs +2 -2
- package/dist/{commands-BWWAxC-g.mjs → commands-4hjEGB-I.mjs} +11 -11
- package/dist/{commands-yuhNv9AR.mjs → commands-BA4LVlQW.mjs} +2 -2
- package/dist/{commands-BxG6r6us.mjs → commands-BQ7UFowe.mjs} +3 -3
- package/dist/{commands-Db0PpuwO.mjs → commands-CQK2nD3o.mjs} +7 -4
- package/dist/{commands-BW_5joei.mjs → commands-CdbvHh2p.mjs} +2 -2
- package/dist/{commands-DR8N0cmJ.mjs → commands-CuyCq5eZ.mjs} +3 -3
- package/dist/{commands-AOno1gcV.mjs → commands-GRfwAHgR.mjs} +17 -5
- package/dist/{commands-D7iQqb-i.mjs → commands-jaaQooYW.mjs} +1 -1
- package/dist/{fleet--8tb1jX3.mjs → fleet-CSh3MUcM.mjs} +3 -3
- package/dist/{headlessCli-COD8n9AD.mjs → headlessCli-Zl5Yu4CL.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notifyCommands-DLo0ANDt.mjs → notifyCommands-BQZXhfLt.mjs} +1 -1
- package/dist/{package-DK43B5gu.mjs → package-DgboZg2o.mjs} +2 -2
- package/dist/{pinnedClaudeCode-C8CmQv3y.mjs → pinnedClaudeCode-8SZDu7O-.mjs} +1 -1
- package/dist/{rpc-JWRwb6oY.mjs → rpc-BIzMNjqC.mjs} +1 -1
- package/dist/{rpc-DMlurOlf.mjs → rpc-C1KIoUyz.mjs} +1 -1
- package/dist/{run-BXDA0YFP.mjs → run-CEp9YpUZ.mjs} +350 -55
- package/dist/{run-RYTtTBDy.mjs → run-DKAROgSg.mjs} +1 -1
- package/dist/{scheduler-CkYMjhum.mjs → scheduler-DIYulmdV.mjs} +1 -1
- package/dist/{serveCommands-BX3D9l8T.mjs → serveCommands-Bq5Cw3mE.mjs} +10 -10
- package/dist/{sideband-CdXFW-wu.mjs → sideband-C83128hf.mjs} +1 -1
- package/package.json +2 -2
- package/dist/supervisorLock-DmfzJx7B.mjs +0 -159
|
@@ -9,7 +9,7 @@ import { execFile, execSync, spawn, exec as exec$1 } from 'child_process';
|
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
10
|
import { randomUUID, createHash } from 'crypto';
|
|
11
11
|
import { randomUUID as randomUUID$1, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
|
|
12
|
-
import { existsSync, readFileSync, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, chmodSync as chmodSync$1, rmSync as rmSync$1, cpSync, statSync, realpathSync, readdirSync, renameSync, appendFileSync, openSync, readSync, closeSync, unlinkSync as unlinkSync$1 } from 'node:fs';
|
|
12
|
+
import { existsSync, readFileSync, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, chmodSync as chmodSync$1, rmSync as rmSync$1, cpSync, statSync, realpathSync, readdirSync, renameSync, appendFileSync, openSync, readSync, closeSync, unlinkSync as unlinkSync$1, writeSync } from 'node:fs';
|
|
13
13
|
import { exec, spawn as spawn$1, execSync as execSync$1, execFile as execFile$1, execFileSync } from 'node:child_process';
|
|
14
14
|
import { promisify } from 'util';
|
|
15
15
|
import * as http from 'http';
|
|
@@ -2408,6 +2408,7 @@ function serveStaticMount(req, res, opts) {
|
|
|
2408
2408
|
const { rootDir, relPath, mountUrlPrefix } = opts;
|
|
2409
2409
|
const browse = opts.browse !== false;
|
|
2410
2410
|
const attachment = opts.attachment === true;
|
|
2411
|
+
const allowHidden = opts.allowHidden === true;
|
|
2411
2412
|
const method = (req.method || "GET").toUpperCase();
|
|
2412
2413
|
if (method === "OPTIONS") {
|
|
2413
2414
|
res.writeHead(204, CORS);
|
|
@@ -2431,7 +2432,7 @@ function serveStaticMount(req, res, opts) {
|
|
|
2431
2432
|
sendFile(req, res, rootDir, rootStat, attachment);
|
|
2432
2433
|
return;
|
|
2433
2434
|
}
|
|
2434
|
-
if (hasBlockedDotSegment(relPath)) {
|
|
2435
|
+
if (!allowHidden && hasBlockedDotSegment(relPath)) {
|
|
2435
2436
|
res.writeHead(404, CORS);
|
|
2436
2437
|
res.end("Not Found");
|
|
2437
2438
|
return;
|
|
@@ -3810,7 +3811,7 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
|
|
|
3810
3811
|
const normRel = this.toSessionRelative(rootDir, m[2] || "");
|
|
3811
3812
|
if (method === "GET" || method === "HEAD") {
|
|
3812
3813
|
if (url.searchParams.get("list") === "1") {
|
|
3813
|
-
const lex =
|
|
3814
|
+
const lex = this.resolveContainedPath(rootDir, normRel);
|
|
3814
3815
|
if (!lex) {
|
|
3815
3816
|
deny(403, "Forbidden");
|
|
3816
3817
|
return;
|
|
@@ -3838,7 +3839,7 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
|
|
|
3838
3839
|
return;
|
|
3839
3840
|
}
|
|
3840
3841
|
const asAttachment = url.searchParams.get("download") === "1";
|
|
3841
|
-
serveStaticMount(req, res, { rootDir, relPath: normRel ? `/${normRel}` : "/", mountUrlPrefix: "", browse: false, attachment: asAttachment});
|
|
3842
|
+
serveStaticMount(req, res, { rootDir, relPath: normRel ? `/${normRel}` : "/", mountUrlPrefix: "", browse: false, attachment: asAttachment, allowHidden: true});
|
|
3842
3843
|
return;
|
|
3843
3844
|
}
|
|
3844
3845
|
if (method === "PUT" || method === "DELETE") {
|
|
@@ -4413,6 +4414,29 @@ var serveManager = /*#__PURE__*/Object.freeze({
|
|
|
4413
4414
|
sanitizeMountForRole: sanitizeMountForRole
|
|
4414
4415
|
});
|
|
4415
4416
|
|
|
4417
|
+
const EXEC_MAX_BUFFER = 16 * 1024 * 1024;
|
|
4418
|
+
function execFailureOutcome(err, stdout, stderr) {
|
|
4419
|
+
const e = err || {};
|
|
4420
|
+
const rawCode = e.code;
|
|
4421
|
+
const enobufs = rawCode === "ENOBUFS";
|
|
4422
|
+
const timedOut = rawCode === "ETIMEDOUT" || e.killed === true && !!e.signal;
|
|
4423
|
+
let exitCode;
|
|
4424
|
+
if (typeof rawCode === "number") exitCode = rawCode;
|
|
4425
|
+
else if (timedOut) exitCode = 124;
|
|
4426
|
+
else exitCode = 1;
|
|
4427
|
+
const base = stderr || e.message || "";
|
|
4428
|
+
let text = base;
|
|
4429
|
+
if (enobufs) {
|
|
4430
|
+
text = `output exceeded the ${Math.round(EXEC_MAX_BUFFER / (1024 * 1024))} MB buffer and was truncated (the command may have completed): ${base}`;
|
|
4431
|
+
} else if (timedOut) {
|
|
4432
|
+
text = `command timed out and was killed (it may have partially completed): ${base}`;
|
|
4433
|
+
}
|
|
4434
|
+
return { success: false, stdout: stdout || "", stderr: text, exitCode };
|
|
4435
|
+
}
|
|
4436
|
+
function execSuccessOutcome(stdout, stderr) {
|
|
4437
|
+
return { success: true, stdout: stdout || "", stderr: stderr || "", exitCode: 0 };
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4416
4440
|
const EXAMPLE_HYPHA_PROXY_URL = "https://proxy.hypha.aicell.io";
|
|
4417
4441
|
const MODE_KEY = "SVAMP_CLAUDE_PROXY";
|
|
4418
4442
|
const HYPHA_PROXY_URL_KEY = "SVAMP_HYPHA_PROXY_URL";
|
|
@@ -4843,6 +4867,13 @@ function stripV1(url) {
|
|
|
4843
4867
|
}
|
|
4844
4868
|
return trimmed;
|
|
4845
4869
|
}
|
|
4870
|
+
function promptCacheTtlEnv(env, subscription) {
|
|
4871
|
+
if (env.FORCE_PROMPT_CACHING_5M) return void 0;
|
|
4872
|
+
if (!subscription) {
|
|
4873
|
+
return env.ENABLE_PROMPT_CACHING_1H === "1" ? "1" : void 0;
|
|
4874
|
+
}
|
|
4875
|
+
return env.ENABLE_PROMPT_CACHING_1H === "0" ? void 0 : "1";
|
|
4876
|
+
}
|
|
4846
4877
|
function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
4847
4878
|
switch (account.method) {
|
|
4848
4879
|
case "anthropic-api-key":
|
|
@@ -4851,7 +4882,9 @@ function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
|
4851
4882
|
claudeEnv: {
|
|
4852
4883
|
ANTHROPIC_API_KEY: account.secret,
|
|
4853
4884
|
ANTHROPIC_BASE_URL: account.baseUrl ? stripV1(account.baseUrl) : void 0,
|
|
4854
|
-
CLAUDE_CODE_OAUTH_TOKEN: void 0
|
|
4885
|
+
CLAUDE_CODE_OAUTH_TOKEN: void 0,
|
|
4886
|
+
// #0986: metered first-party key — the 1h TTL costs 2.0x per cache write.
|
|
4887
|
+
ENABLE_PROMPT_CACHING_1H: promptCacheTtlEnv(env, false)
|
|
4855
4888
|
},
|
|
4856
4889
|
describe: `anthropic API key (${account.label})`
|
|
4857
4890
|
};
|
|
@@ -4862,7 +4895,9 @@ function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
|
4862
4895
|
claudeEnv: {
|
|
4863
4896
|
ANTHROPIC_BASE_URL: url,
|
|
4864
4897
|
ANTHROPIC_API_KEY: account.secret,
|
|
4865
|
-
CLAUDE_CODE_OAUTH_TOKEN: void 0
|
|
4898
|
+
CLAUDE_CODE_OAUTH_TOKEN: void 0,
|
|
4899
|
+
// #0986: an arbitrary gateway's billing model is unknown — opt-in only.
|
|
4900
|
+
ENABLE_PROMPT_CACHING_1H: promptCacheTtlEnv(env, false)
|
|
4866
4901
|
},
|
|
4867
4902
|
describe: `anthropic gateway ${url} (${account.label})`
|
|
4868
4903
|
};
|
|
@@ -4879,7 +4914,7 @@ function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
|
4879
4914
|
ANTHROPIC_API_KEY: token,
|
|
4880
4915
|
CLAUDE_CODE_OAUTH_TOKEN: void 0,
|
|
4881
4916
|
// #0203: subscription backend qualifies for the free 1h prompt-cache TTL.
|
|
4882
|
-
ENABLE_PROMPT_CACHING_1H: env
|
|
4917
|
+
ENABLE_PROMPT_CACHING_1H: promptCacheTtlEnv(env, true)
|
|
4883
4918
|
},
|
|
4884
4919
|
describe: `hypha proxy ${url} (${account.label})`
|
|
4885
4920
|
};
|
|
@@ -4890,7 +4925,9 @@ function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
|
4890
4925
|
claudeEnv: {
|
|
4891
4926
|
CLAUDE_CODE_OAUTH_TOKEN: account.secret,
|
|
4892
4927
|
ANTHROPIC_BASE_URL: void 0,
|
|
4893
|
-
ANTHROPIC_API_KEY: void 0
|
|
4928
|
+
ANTHROPIC_API_KEY: void 0,
|
|
4929
|
+
// #0986: subscription token — free 1h TTL.
|
|
4930
|
+
ENABLE_PROMPT_CACHING_1H: promptCacheTtlEnv(env, true)
|
|
4894
4931
|
},
|
|
4895
4932
|
describe: `claude subscription token (${account.label})`
|
|
4896
4933
|
};
|
|
@@ -4900,7 +4937,9 @@ function resolveAccountEnv(account, env = process.env, svampHome) {
|
|
|
4900
4937
|
claudeEnv: {
|
|
4901
4938
|
CLAUDE_CODE_OAUTH_TOKEN: account.accessToken,
|
|
4902
4939
|
ANTHROPIC_BASE_URL: void 0,
|
|
4903
|
-
ANTHROPIC_API_KEY: void 0
|
|
4940
|
+
ANTHROPIC_API_KEY: void 0,
|
|
4941
|
+
// #0986: subscription OAuth — free 1h TTL.
|
|
4942
|
+
ENABLE_PROMPT_CACHING_1H: promptCacheTtlEnv(env, true)
|
|
4904
4943
|
},
|
|
4905
4944
|
describe: `claude subscription oauth (${account.label})`
|
|
4906
4945
|
};
|
|
@@ -5520,10 +5559,16 @@ const genKey = () => "ck_" + randomBytes(18).toString("base64url");
|
|
|
5520
5559
|
const DEFAULT_TEMPLATE = `<inbound-message from="\${sender.name}" sender-type="\${sender.kind}" verified="\${sender.verified}" channel="\${channel.name}" call-id="\${call.id}" at="\${now}">
|
|
5521
5560
|
\${body.message}
|
|
5522
5561
|
</inbound-message>`;
|
|
5562
|
+
const CHANNEL_ID_RE = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
5563
|
+
function isValidChannelId(id) {
|
|
5564
|
+
return typeof id === "string" && CHANNEL_ID_RE.test(id) && id !== "." && id !== "..";
|
|
5565
|
+
}
|
|
5523
5566
|
function validateChannel(c) {
|
|
5524
5567
|
const errs = [];
|
|
5525
5568
|
if (!c || typeof c !== "object") return ["channel must be an object"];
|
|
5526
5569
|
if (!c.name) errs.push("name required");
|
|
5570
|
+
if (c.id !== void 0 && !isValidChannelId(c.id))
|
|
5571
|
+
errs.push("id must match [A-Za-z0-9_.-]{1,64} (no path separators)");
|
|
5527
5572
|
const m = c.identity?.mode;
|
|
5528
5573
|
if (!["per-key", "caller-supplied", "fixed"].includes(m)) errs.push("identity.mode must be per-key|caller-supplied|fixed");
|
|
5529
5574
|
if (m === "fixed" && !c.identity.fixed?.name) errs.push("identity.fixed.name required for fixed mode");
|
|
@@ -5590,10 +5635,14 @@ class ChannelStore {
|
|
|
5590
5635
|
} catch {
|
|
5591
5636
|
}
|
|
5592
5637
|
}
|
|
5638
|
+
// #0982: reject a traversing/oversized id at the path boundary itself, so no
|
|
5639
|
+
// caller (RPC, HTTP, CLI) can reach outside this.dir regardless of its own checks.
|
|
5593
5640
|
_path(id) {
|
|
5641
|
+
if (!isValidChannelId(id)) throw new Error(`invalid channel id: ${String(id).slice(0, 64)}`);
|
|
5594
5642
|
return join$1(this.dir, `${id}.json`);
|
|
5595
5643
|
}
|
|
5596
5644
|
_lock(id) {
|
|
5645
|
+
if (!isValidChannelId(id)) throw new Error(`invalid channel id: ${String(id).slice(0, 64)}`);
|
|
5597
5646
|
return join$1(this.dir, `${id}.json.lock`);
|
|
5598
5647
|
}
|
|
5599
5648
|
// #0679: the actual validate + atomic write, WITHOUT the lock, so a locked RMW mutator can
|
|
@@ -6462,6 +6511,15 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
6462
6511
|
serveCallerTrusted(context),
|
|
6463
6512
|
process.env.SVAMP_OWNER_EMAIL
|
|
6464
6513
|
);
|
|
6514
|
+
const assertMayReplaceMount = (sm, name, context) => {
|
|
6515
|
+
const existingMount = sm.getMount(name);
|
|
6516
|
+
if (!existingMount || serveCallerTrusted(context)) return;
|
|
6517
|
+
const callerEmail = (context?.user?.email || "").toLowerCase();
|
|
6518
|
+
const mountOwner = (existingMount.ownerEmail || "").toLowerCase();
|
|
6519
|
+
if (!callerEmail || !mountOwner || callerEmail !== mountOwner) {
|
|
6520
|
+
throw new Error(`Not authorized to replace mount '${name}' (owned by another user)`);
|
|
6521
|
+
}
|
|
6522
|
+
};
|
|
6465
6523
|
let lastInboundRpcAt = Date.now();
|
|
6466
6524
|
const trackInbound = () => {
|
|
6467
6525
|
lastInboundRpcAt = Date.now();
|
|
@@ -6602,6 +6660,21 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
6602
6660
|
}
|
|
6603
6661
|
return result;
|
|
6604
6662
|
},
|
|
6663
|
+
/**
|
|
6664
|
+
* #0970: list ARCHIVED (stopped-but-persisted) sessions.
|
|
6665
|
+
*
|
|
6666
|
+
* Kept as its own method rather than a flag on listSessions so no existing caller's
|
|
6667
|
+
* view silently gains stopped rows. Machine-level `view` only: an archived session has
|
|
6668
|
+
* no live RPC handler, so the per-session sharing check listSessions falls back to
|
|
6669
|
+
* cannot run — rather than guess, this returns nothing to a caller without machine
|
|
6670
|
+
* access. That is the conservative direction (a session-shared user simply keeps
|
|
6671
|
+
* today's behaviour).
|
|
6672
|
+
*/
|
|
6673
|
+
listArchivedSessions: async (context) => {
|
|
6674
|
+
trackInbound();
|
|
6675
|
+
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
6676
|
+
return handlers.getArchivedSessions?.() || [];
|
|
6677
|
+
},
|
|
6605
6678
|
/**
|
|
6606
6679
|
* Get summary info for all sessions (metadata, agent state, activity).
|
|
6607
6680
|
* Replaces the need to discover and query N individual session services.
|
|
@@ -7079,14 +7152,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7079
7152
|
const { exec } = await import('child_process');
|
|
7080
7153
|
const { homedir } = await import('os');
|
|
7081
7154
|
return new Promise((resolve) => {
|
|
7082
|
-
exec(command, { cwd: cwd || homedir(), timeout: 3e4, maxBuffer:
|
|
7083
|
-
if (err)
|
|
7084
|
-
|
|
7085
|
-
const errText = enobufs ? `output exceeded the 16 MB buffer and was truncated (the command may have completed): ${stderr || err.message}` : stderr || err.message;
|
|
7086
|
-
resolve({ success: false, stdout: stdout || "", stderr: errText, exitCode: err.code ?? 1 });
|
|
7087
|
-
} else {
|
|
7088
|
-
resolve({ success: true, stdout, stderr: stderr || "", exitCode: 0 });
|
|
7089
|
-
}
|
|
7155
|
+
exec(command, { cwd: cwd || homedir(), timeout: 3e4, maxBuffer: EXEC_MAX_BUFFER }, (err, stdout, stderr) => {
|
|
7156
|
+
if (err) resolve(execFailureOutcome(err, stdout, stderr));
|
|
7157
|
+
else resolve(execSuccessOutcome(stdout, stderr));
|
|
7090
7158
|
});
|
|
7091
7159
|
});
|
|
7092
7160
|
},
|
|
@@ -7631,6 +7699,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7631
7699
|
if (params.sessionId && !serveCallerTrusted(context)) {
|
|
7632
7700
|
await authorizeSessionAccess(params.sessionId, "admin", context);
|
|
7633
7701
|
}
|
|
7702
|
+
assertMayReplaceMount(sm, params.name, context);
|
|
7634
7703
|
const ownerEmail2 = resolveMountOwnerEmailFor(params.ownerEmail, context);
|
|
7635
7704
|
const access = params.access || "owner";
|
|
7636
7705
|
return sm.addMount(params.name, params.directory, params.sessionId, access, ownerEmail2);
|
|
@@ -7651,14 +7720,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7651
7720
|
if (params.sessionId && !serveCallerTrusted(context)) {
|
|
7652
7721
|
await authorizeSessionAccess(params.sessionId, "admin", context);
|
|
7653
7722
|
}
|
|
7654
|
-
|
|
7655
|
-
if (existingMount && !serveCallerTrusted(context)) {
|
|
7656
|
-
const callerEmail = (context?.user?.email || "").toLowerCase();
|
|
7657
|
-
const mountOwner = (existingMount.ownerEmail || "").toLowerCase();
|
|
7658
|
-
if (!callerEmail || !mountOwner || callerEmail !== mountOwner) {
|
|
7659
|
-
throw new Error(`Not authorized to replace mount '${params.name}' (owned by another user)`);
|
|
7660
|
-
}
|
|
7661
|
-
}
|
|
7723
|
+
assertMayReplaceMount(sm, params.name, context);
|
|
7662
7724
|
const ownerEmail2 = resolveMountOwnerEmailFor(params.ownerEmail, context);
|
|
7663
7725
|
const access = params.access ?? "owner";
|
|
7664
7726
|
return sm.applyMount({
|
|
@@ -8029,7 +8091,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8029
8091
|
}
|
|
8030
8092
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
8031
8093
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
8032
|
-
const { toolsForRole } = await import('./sideband-
|
|
8094
|
+
const { toolsForRole } = await import('./sideband-C83128hf.mjs');
|
|
8033
8095
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
8034
8096
|
return fmt(r2);
|
|
8035
8097
|
}
|
|
@@ -8134,7 +8196,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8134
8196
|
return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
|
|
8135
8197
|
}
|
|
8136
8198
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
8137
|
-
const { queryCore } = await import('./commands-
|
|
8199
|
+
const { queryCore } = await import('./commands-GRfwAHgR.mjs');
|
|
8138
8200
|
const timeout = c.reply?.timeout_sec || 120;
|
|
8139
8201
|
let result;
|
|
8140
8202
|
try {
|
|
@@ -10241,7 +10303,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
10241
10303
|
return { ok: result.status === "completed", call_id: callId, correlationId: callId, status: result.status, reply: result.reply, tool_calls: result.toolCalls, error: result.error };
|
|
10242
10304
|
}
|
|
10243
10305
|
if (c.action?.kind === "loop") return { error: "loop channels are served by the channel server, not channelSend" };
|
|
10244
|
-
if (params.no_reply && c.action?.kind === "message") {
|
|
10306
|
+
if (params.no_reply && c.action?.kind === "message" && r.sender.verified) {
|
|
10245
10307
|
const envelope = renderMessage(c, { sender: r.sender, body: { message: params.message }, callId });
|
|
10246
10308
|
const wire = JSON.stringify({ role: "user", content: { type: "text", text: envelope } });
|
|
10247
10309
|
await deliverUserMessageCore(wire, params.localId || callId, void 0, true);
|
|
@@ -10448,17 +10510,29 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
10448
10510
|
return { ...lastActivity, sessionId, pendingPermissions };
|
|
10449
10511
|
},
|
|
10450
10512
|
// ── File Operations (optional, admin-only) ──
|
|
10513
|
+
// #0965: file BYTES now travel over the HTTP files-gateway (the always-on frp tunnel,
|
|
10514
|
+
// GET/HEAD/PUT/DELETE + ?list=1 in serveManager), NOT this hypha-rpc control WebSocket. A
|
|
10515
|
+
// large base64 body over RPC died on any WS blip and starved the session heartbeat. So
|
|
10516
|
+
// `writeFile`, `writeFileChunk` and `listDirectory` below are DEPRECATED — no current
|
|
10517
|
+
// frontend calls them; they're retained only so a cached/stale PWA build still has a
|
|
10518
|
+
// working handler (there is no client-version guard). EXCEPTIONS that stay first-class:
|
|
10519
|
+
// • `readFile` — control-plane, NOT file transfer: the CLI `loop status` and the
|
|
10520
|
+
// frontend `getSessionLoopStatus` read small `.svamp/<id>/loop/*.json` files through
|
|
10521
|
+
// it. Keeping loop status off the file tunnel avoids coupling the loop UI to it.
|
|
10522
|
+
// • `filesEndpoint` — resolves the gateway URL the frontend uses for all of the above.
|
|
10451
10523
|
readFile: async (path, context) => {
|
|
10452
10524
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
10453
10525
|
if (!callbacks.onReadFile) throw new Error("readFile not supported");
|
|
10454
10526
|
return await callbacks.onReadFile(path);
|
|
10455
10527
|
},
|
|
10528
|
+
/** @deprecated #0965 — use the HTTP files-gateway (PUT). Retained for cached-PWA skew only. */
|
|
10456
10529
|
writeFile: async (path, content, context) => {
|
|
10457
10530
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
10458
10531
|
if (!callbacks.onWriteFile) throw new Error("writeFile not supported");
|
|
10459
10532
|
await callbacks.onWriteFile(path, content);
|
|
10460
10533
|
return { success: true };
|
|
10461
10534
|
},
|
|
10535
|
+
/** @deprecated #0965 — superseded by the HTTP files-gateway (PUT). Retained for cached-PWA skew only. */
|
|
10462
10536
|
writeFileChunk: async (path, content, uploadId, chunkIndex, totalChunks, isLast, context) => {
|
|
10463
10537
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
10464
10538
|
if (!callbacks.onWriteFileChunk) throw new Error("writeFileChunk not supported");
|
|
@@ -10472,6 +10546,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
10472
10546
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
10473
10547
|
return { url: callbacks.onFilesEndpoint?.() ?? null };
|
|
10474
10548
|
},
|
|
10549
|
+
/** @deprecated #0965 — use the HTTP files-gateway (GET ?list=1). Retained for cached-PWA skew only. */
|
|
10475
10550
|
listDirectory: async (path, context) => {
|
|
10476
10551
|
authorizeRequest(context, metadata.sharing, "admin");
|
|
10477
10552
|
if (!callbacks.onListDirectory) throw new Error("listDirectory not supported");
|
|
@@ -17837,6 +17912,197 @@ function shouldForceReconnect(consecutiveHeartbeatFailures) {
|
|
|
17837
17912
|
return consecutiveHeartbeatFailures === 2 || consecutiveHeartbeatFailures % 3 === 0;
|
|
17838
17913
|
}
|
|
17839
17914
|
|
|
17915
|
+
function acquireSupervisorLock(pidFile, pid = process.pid) {
|
|
17916
|
+
try {
|
|
17917
|
+
const fd = openSync(pidFile, "wx");
|
|
17918
|
+
try {
|
|
17919
|
+
writeSync(fd, String(pid));
|
|
17920
|
+
} finally {
|
|
17921
|
+
closeSync(fd);
|
|
17922
|
+
}
|
|
17923
|
+
return { acquired: true };
|
|
17924
|
+
} catch (err) {
|
|
17925
|
+
if (err?.code !== "EEXIST") throw err;
|
|
17926
|
+
}
|
|
17927
|
+
let existingPid = 0;
|
|
17928
|
+
try {
|
|
17929
|
+
existingPid = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
17930
|
+
} catch {
|
|
17931
|
+
}
|
|
17932
|
+
if (!existingPid || Number.isNaN(existingPid) || existingPid <= 0) {
|
|
17933
|
+
try {
|
|
17934
|
+
unlinkSync$1(pidFile);
|
|
17935
|
+
} catch {
|
|
17936
|
+
}
|
|
17937
|
+
return { acquired: false, staleCleaned: true };
|
|
17938
|
+
}
|
|
17939
|
+
if (isPidAlive(existingPid)) {
|
|
17940
|
+
return { acquired: false, heldBy: existingPid };
|
|
17941
|
+
}
|
|
17942
|
+
try {
|
|
17943
|
+
unlinkSync$1(pidFile);
|
|
17944
|
+
} catch {
|
|
17945
|
+
}
|
|
17946
|
+
return { acquired: false, staleCleaned: true };
|
|
17947
|
+
}
|
|
17948
|
+
function acquireSupervisorLockWithRetry(pidFile, pid = process.pid) {
|
|
17949
|
+
let result = acquireSupervisorLock(pidFile, pid);
|
|
17950
|
+
if (!result.acquired && result.staleCleaned) {
|
|
17951
|
+
result = acquireSupervisorLock(pidFile, pid);
|
|
17952
|
+
}
|
|
17953
|
+
return { acquired: result.acquired, heldBy: result.heldBy };
|
|
17954
|
+
}
|
|
17955
|
+
function releaseSupervisorLock(pidFile, pid = process.pid) {
|
|
17956
|
+
try {
|
|
17957
|
+
if (!existsSync(pidFile)) return;
|
|
17958
|
+
const content = parseInt(readFileSync(pidFile, "utf-8").trim(), 10);
|
|
17959
|
+
if (content === pid) unlinkSync$1(pidFile);
|
|
17960
|
+
} catch {
|
|
17961
|
+
}
|
|
17962
|
+
}
|
|
17963
|
+
function isPidAlive(pid) {
|
|
17964
|
+
if (!pid || Number.isNaN(pid) || pid <= 0) return false;
|
|
17965
|
+
try {
|
|
17966
|
+
process.kill(pid, 0);
|
|
17967
|
+
return true;
|
|
17968
|
+
} catch (err) {
|
|
17969
|
+
return err?.code === "EPERM";
|
|
17970
|
+
}
|
|
17971
|
+
}
|
|
17972
|
+
function isSupervisorPid(pid) {
|
|
17973
|
+
if (!pid || Number.isNaN(pid) || pid <= 0) return false;
|
|
17974
|
+
if (process.platform === "win32") return null;
|
|
17975
|
+
try {
|
|
17976
|
+
const out = execFileSync("ps", ["-o", "command=", "-p", String(pid)], {
|
|
17977
|
+
encoding: "utf-8",
|
|
17978
|
+
timeout: 5e3,
|
|
17979
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
17980
|
+
}).trim();
|
|
17981
|
+
if (!out) return false;
|
|
17982
|
+
return /daemon\s+start-supervised/.test(out);
|
|
17983
|
+
} catch {
|
|
17984
|
+
return null;
|
|
17985
|
+
}
|
|
17986
|
+
}
|
|
17987
|
+
function shouldSignalSupervisor(opts) {
|
|
17988
|
+
const { pid, alive, isSupervisor } = opts;
|
|
17989
|
+
if (!pid || Number.isNaN(pid) || pid <= 0) return false;
|
|
17990
|
+
if (!alive) return false;
|
|
17991
|
+
if (isSupervisor === false) return false;
|
|
17992
|
+
return true;
|
|
17993
|
+
}
|
|
17994
|
+
function findOrphanedSyncPids(currentSupervisorPid = process.pid) {
|
|
17995
|
+
if (process.platform === "win32") return [];
|
|
17996
|
+
const pids = [];
|
|
17997
|
+
try {
|
|
17998
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
17999
|
+
const pgrepArgs = uid !== void 0 ? ["-u", String(uid), "-af", "svamp daemon start-sync"] : ["-af", "svamp daemon start-sync"];
|
|
18000
|
+
const output = execFileSync("pgrep", pgrepArgs, {
|
|
18001
|
+
encoding: "utf-8",
|
|
18002
|
+
timeout: 5e3
|
|
18003
|
+
});
|
|
18004
|
+
for (const line of output.split("\n")) {
|
|
18005
|
+
const trimmed = line.trim();
|
|
18006
|
+
if (!trimmed) continue;
|
|
18007
|
+
const match = trimmed.match(/^(\d+)\s/);
|
|
18008
|
+
if (!match) continue;
|
|
18009
|
+
const pid = parseInt(match[1], 10);
|
|
18010
|
+
if (Number.isNaN(pid) || pid <= 0) continue;
|
|
18011
|
+
if (pid === process.pid) continue;
|
|
18012
|
+
const ppid = getPpid(pid);
|
|
18013
|
+
if (ppid === currentSupervisorPid) continue;
|
|
18014
|
+
pids.push(pid);
|
|
18015
|
+
}
|
|
18016
|
+
} catch (err) {
|
|
18017
|
+
if (err?.status !== 1) ;
|
|
18018
|
+
}
|
|
18019
|
+
return pids;
|
|
18020
|
+
}
|
|
18021
|
+
function getPpid(pid) {
|
|
18022
|
+
if (process.platform === "win32") return 0;
|
|
18023
|
+
try {
|
|
18024
|
+
const out = execFileSync("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
18025
|
+
encoding: "utf-8",
|
|
18026
|
+
timeout: 2e3
|
|
18027
|
+
}).trim();
|
|
18028
|
+
const ppid = parseInt(out, 10);
|
|
18029
|
+
return Number.isNaN(ppid) ? 0 : ppid;
|
|
18030
|
+
} catch {
|
|
18031
|
+
return 0;
|
|
18032
|
+
}
|
|
18033
|
+
}
|
|
18034
|
+
async function killOrphanedSyncs(pids, opts = {}) {
|
|
18035
|
+
if (pids.length === 0) return;
|
|
18036
|
+
const log = opts.log || (() => {
|
|
18037
|
+
});
|
|
18038
|
+
const gracePeriodMs = opts.gracePeriodMs ?? 3e3;
|
|
18039
|
+
log(`Killing ${pids.length} orphan sync daemon(s): ${pids.join(", ")}`);
|
|
18040
|
+
for (const pid of pids) {
|
|
18041
|
+
try {
|
|
18042
|
+
process.kill(pid, "SIGTERM");
|
|
18043
|
+
} catch {
|
|
18044
|
+
}
|
|
18045
|
+
}
|
|
18046
|
+
const pollStep = 100;
|
|
18047
|
+
const steps = Math.max(1, Math.ceil(gracePeriodMs / pollStep));
|
|
18048
|
+
for (let i = 0; i < steps; i++) {
|
|
18049
|
+
await new Promise((r) => setTimeout(r, pollStep));
|
|
18050
|
+
if (pids.every((p) => !isPidAlive(p))) {
|
|
18051
|
+
log("All orphan daemons exited cleanly");
|
|
18052
|
+
return;
|
|
18053
|
+
}
|
|
18054
|
+
}
|
|
18055
|
+
const survivors = pids.filter(isPidAlive);
|
|
18056
|
+
if (survivors.length > 0) {
|
|
18057
|
+
log(`Force-killing survivors: ${survivors.join(", ")}`);
|
|
18058
|
+
for (const pid of survivors) {
|
|
18059
|
+
try {
|
|
18060
|
+
process.kill(pid, "SIGKILL");
|
|
18061
|
+
} catch {
|
|
18062
|
+
}
|
|
18063
|
+
}
|
|
18064
|
+
}
|
|
18065
|
+
}
|
|
18066
|
+
function watchParentLiveness(opts) {
|
|
18067
|
+
if (process.env.SVAMP_SUPERVISED !== "1") {
|
|
18068
|
+
return () => {
|
|
18069
|
+
};
|
|
18070
|
+
}
|
|
18071
|
+
const supervisorPid = process.ppid;
|
|
18072
|
+
if (!supervisorPid || supervisorPid === 1) {
|
|
18073
|
+
return () => {
|
|
18074
|
+
};
|
|
18075
|
+
}
|
|
18076
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
18077
|
+
let fired = false;
|
|
18078
|
+
const timer = setInterval(() => {
|
|
18079
|
+
if (fired) return;
|
|
18080
|
+
if (!isPidAlive(supervisorPid)) {
|
|
18081
|
+
fired = true;
|
|
18082
|
+
clearInterval(timer);
|
|
18083
|
+
opts.onParentDeath();
|
|
18084
|
+
}
|
|
18085
|
+
}, intervalMs);
|
|
18086
|
+
timer.unref?.();
|
|
18087
|
+
return () => {
|
|
18088
|
+
clearInterval(timer);
|
|
18089
|
+
};
|
|
18090
|
+
}
|
|
18091
|
+
|
|
18092
|
+
var supervisorLock = /*#__PURE__*/Object.freeze({
|
|
18093
|
+
__proto__: null,
|
|
18094
|
+
acquireSupervisorLock: acquireSupervisorLock,
|
|
18095
|
+
acquireSupervisorLockWithRetry: acquireSupervisorLockWithRetry,
|
|
18096
|
+
findOrphanedSyncPids: findOrphanedSyncPids,
|
|
18097
|
+
getPpid: getPpid,
|
|
18098
|
+
isPidAlive: isPidAlive,
|
|
18099
|
+
isSupervisorPid: isSupervisorPid,
|
|
18100
|
+
killOrphanedSyncs: killOrphanedSyncs,
|
|
18101
|
+
releaseSupervisorLock: releaseSupervisorLock,
|
|
18102
|
+
shouldSignalSupervisor: shouldSignalSupervisor,
|
|
18103
|
+
watchParentLiveness: watchParentLiveness
|
|
18104
|
+
});
|
|
18105
|
+
|
|
17840
18106
|
const SVAMP_HOME$1 = process.env.SVAMP_HOME || join$1(os.homedir(), ".svamp");
|
|
17841
18107
|
function generateHookSettings(portOrOptions = {}) {
|
|
17842
18108
|
const opts = typeof portOrOptions === "number" ? { sessionStartPort: portOrOptions } : portOrOptions;
|
|
@@ -18797,7 +19063,9 @@ function spawnHeadlessEvaluator(prompt, opts) {
|
|
|
18797
19063
|
const evalArgs = [
|
|
18798
19064
|
"--print",
|
|
18799
19065
|
prompt,
|
|
18800
|
-
// Read-only reviewer (
|
|
19066
|
+
// Read-only reviewer (#0995: NOT the retired .claude/agents/loop-evaluator.md subagent —
|
|
19067
|
+
// this is a separate `claude --print` process whose prompt buildEvaluatorPrompt owns,
|
|
19068
|
+
// which is what keeps the #0189 independence invariant): it inspects real repo
|
|
18801
19069
|
// state but must not mutate the working tree it's judging. The allowedTools whitelist
|
|
18802
19070
|
// already excludes mutation tools; --disallowedTools is belt-and-suspenders. Kept to
|
|
18803
19071
|
// Write/Edit only — MultiEdit/NotebookEdit aren't known tool names on some claude builds
|
|
@@ -19323,6 +19591,26 @@ function clearSessionArchivedFlag(sessionId) {
|
|
|
19323
19591
|
return null;
|
|
19324
19592
|
}
|
|
19325
19593
|
}
|
|
19594
|
+
function loadArchivedSessionInfos(isLive) {
|
|
19595
|
+
const out = [];
|
|
19596
|
+
const index = loadSessionIndex();
|
|
19597
|
+
for (const [sessionId, entry] of Object.entries(index)) {
|
|
19598
|
+
if (isLive(sessionId)) continue;
|
|
19599
|
+
try {
|
|
19600
|
+
const data = JSON.parse(readFileSync$1(getSessionFilePath(entry.directory, sessionId), "utf-8"));
|
|
19601
|
+
if (!data?.sessionId) continue;
|
|
19602
|
+
out.push({
|
|
19603
|
+
sessionId: data.sessionId,
|
|
19604
|
+
startedBy: data.startedBy || "unknown",
|
|
19605
|
+
directory: data.directory || entry.directory,
|
|
19606
|
+
active: false,
|
|
19607
|
+
metadata: data.metadata
|
|
19608
|
+
});
|
|
19609
|
+
} catch {
|
|
19610
|
+
}
|
|
19611
|
+
}
|
|
19612
|
+
return out;
|
|
19613
|
+
}
|
|
19326
19614
|
function loadPersistedSessions() {
|
|
19327
19615
|
const sessions = [];
|
|
19328
19616
|
const index = loadSessionIndex();
|
|
@@ -19473,7 +19761,7 @@ async function startDaemon(options) {
|
|
|
19473
19761
|
process.on("SIGINT", () => requestShutdown("os-signal"));
|
|
19474
19762
|
process.on("SIGTERM", () => requestShutdown("os-signal"));
|
|
19475
19763
|
process.on("SIGUSR1", () => requestShutdown("os-signal-cleanup"));
|
|
19476
|
-
const { watchParentLiveness } = await
|
|
19764
|
+
const { watchParentLiveness } = await Promise.resolve().then(function () { return supervisorLock; });
|
|
19477
19765
|
const cancelParentWatchdog = watchParentLiveness({
|
|
19478
19766
|
intervalMs: 5e3,
|
|
19479
19767
|
onParentDeath: () => {
|
|
@@ -19666,7 +19954,7 @@ async function startDaemon(options) {
|
|
|
19666
19954
|
try {
|
|
19667
19955
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
19668
19956
|
if (!dir) return;
|
|
19669
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
19957
|
+
const { reconcileServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
|
|
19670
19958
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
19671
19959
|
const config = readSvampConfig(configPath);
|
|
19672
19960
|
const entries = Array.from(urls.entries());
|
|
@@ -19688,7 +19976,7 @@ async function startDaemon(options) {
|
|
|
19688
19976
|
try {
|
|
19689
19977
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
19690
19978
|
if (!dir) return;
|
|
19691
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
19979
|
+
const { reconcileServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
|
|
19692
19980
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
19693
19981
|
const config = readSvampConfig(configPath);
|
|
19694
19982
|
const incoming = [{
|
|
@@ -19709,7 +19997,7 @@ async function startDaemon(options) {
|
|
|
19709
19997
|
try {
|
|
19710
19998
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
19711
19999
|
if (!dir) return;
|
|
19712
|
-
const { dropServiceLinks } = await import('./agentCommands-
|
|
20000
|
+
const { dropServiceLinks } = await import('./agentCommands-D7CV6DKL.mjs');
|
|
19713
20001
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
19714
20002
|
const config = readSvampConfig(configPath);
|
|
19715
20003
|
if (dropServiceLinks(config, "serve", mountName)) {
|
|
@@ -19752,7 +20040,7 @@ async function startDaemon(options) {
|
|
|
19752
20040
|
ensureAutoInstalledCommands(logger);
|
|
19753
20041
|
(async () => {
|
|
19754
20042
|
try {
|
|
19755
|
-
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-
|
|
20043
|
+
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
|
|
19756
20044
|
beginClaudeVersionReconcile((msg) => logger.log(msg));
|
|
19757
20045
|
} catch (e) {
|
|
19758
20046
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
@@ -22072,21 +22360,18 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
22072
22360
|
logger.log(`[Session ${sessionId}] Bash: ${command} (cwd: ${cwd || directory}, timeout: ${execTimeout}ms)`);
|
|
22073
22361
|
const { exec: exec2 } = await import('child_process');
|
|
22074
22362
|
return new Promise((resolve2) => {
|
|
22075
|
-
exec2(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer:
|
|
22076
|
-
if (err)
|
|
22077
|
-
|
|
22078
|
-
} else {
|
|
22079
|
-
resolve2({ success: true, stdout, stderr: stderr || "", exitCode: 0 });
|
|
22080
|
-
}
|
|
22363
|
+
exec2(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer: EXEC_MAX_BUFFER }, (err, stdout, stderr) => {
|
|
22364
|
+
if (err) resolve2(execFailureOutcome(err, stdout, stderr));
|
|
22365
|
+
else resolve2(execSuccessOutcome(stdout, stderr));
|
|
22081
22366
|
});
|
|
22082
22367
|
});
|
|
22083
22368
|
},
|
|
22084
22369
|
onIssue: async (params) => {
|
|
22085
|
-
const { issueRpc } = await import('./rpc-
|
|
22370
|
+
const { issueRpc } = await import('./rpc-BIzMNjqC.mjs');
|
|
22086
22371
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
22087
22372
|
},
|
|
22088
22373
|
onWorkflow: async (params) => {
|
|
22089
|
-
const { workflowRpc } = await import('./rpc-
|
|
22374
|
+
const { workflowRpc } = await import('./rpc-C1KIoUyz.mjs');
|
|
22090
22375
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
22091
22376
|
},
|
|
22092
22377
|
onRipgrep: async (args, cwd) => {
|
|
@@ -22783,21 +23068,18 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
22783
23068
|
const execTimeout = timeout || 12e4;
|
|
22784
23069
|
const { exec: exec2 } = await import('child_process');
|
|
22785
23070
|
return new Promise((resolve2) => {
|
|
22786
|
-
exec2(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer:
|
|
22787
|
-
if (err)
|
|
22788
|
-
|
|
22789
|
-
} else {
|
|
22790
|
-
resolve2({ success: true, stdout, stderr: stderr || "", exitCode: 0 });
|
|
22791
|
-
}
|
|
23071
|
+
exec2(command, { cwd: cwd || directory, timeout: execTimeout, maxBuffer: EXEC_MAX_BUFFER }, (err, stdout, stderr) => {
|
|
23072
|
+
if (err) resolve2(execFailureOutcome(err, stdout, stderr));
|
|
23073
|
+
else resolve2(execSuccessOutcome(stdout, stderr));
|
|
22792
23074
|
});
|
|
22793
23075
|
});
|
|
22794
23076
|
},
|
|
22795
23077
|
onIssue: async (params) => {
|
|
22796
|
-
const { issueRpc } = await import('./rpc-
|
|
23078
|
+
const { issueRpc } = await import('./rpc-BIzMNjqC.mjs');
|
|
22797
23079
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
22798
23080
|
},
|
|
22799
23081
|
onWorkflow: async (params) => {
|
|
22800
|
-
const { workflowRpc } = await import('./rpc-
|
|
23082
|
+
const { workflowRpc } = await import('./rpc-C1KIoUyz.mjs');
|
|
22801
23083
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
22802
23084
|
},
|
|
22803
23085
|
onRipgrep: async (args, cwd) => {
|
|
@@ -23626,6 +23908,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
23626
23908
|
logger.log("Shutdown requested via hypha-app (ignored \u2014 daemon never self-terminates)");
|
|
23627
23909
|
},
|
|
23628
23910
|
getTrackedSessions: getCurrentChildren,
|
|
23911
|
+
// #0970: archived (stopped-but-persisted) sessions, so CLI id resolution can find them.
|
|
23912
|
+
getArchivedSessions: () => {
|
|
23913
|
+
const live = new Set(getCurrentChildren().map((c) => c.sessionId));
|
|
23914
|
+
return loadArchivedSessionInfos((id) => live.has(id));
|
|
23915
|
+
},
|
|
23629
23916
|
getSessionRPCHandlers: (sessionId) => {
|
|
23630
23917
|
for (const [, session] of pidToTrackedSession) {
|
|
23631
23918
|
if (session.svampSessionId === sessionId && !session.stopped && session.sessionRPCHandlers) {
|
|
@@ -23849,7 +24136,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
23849
24136
|
}
|
|
23850
24137
|
if (persistedSessions.length > 0) {
|
|
23851
24138
|
try {
|
|
23852
|
-
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-
|
|
24139
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-8SZDu7O-.mjs');
|
|
23853
24140
|
await awaitClaudeVersionReady();
|
|
23854
24141
|
} catch {
|
|
23855
24142
|
}
|
|
@@ -23950,8 +24237,16 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
23950
24237
|
const supPidFile = join(SVAMP_HOME, "supervisor.pid");
|
|
23951
24238
|
const supPid = existsSync$1(supPidFile) ? parseInt(readFileSync$1(supPidFile, "utf-8").trim(), 10) : NaN;
|
|
23952
24239
|
if (supPid && !isNaN(supPid)) {
|
|
23953
|
-
|
|
23954
|
-
|
|
24240
|
+
const ok = shouldSignalSupervisor({
|
|
24241
|
+
pid: supPid,
|
|
24242
|
+
alive: isPidAlive(supPid),
|
|
24243
|
+
isSupervisor: isSupervisorPid(supPid)
|
|
24244
|
+
});
|
|
24245
|
+
if (ok) {
|
|
24246
|
+
process.kill(supPid, "SIGUSR2");
|
|
24247
|
+
return;
|
|
24248
|
+
}
|
|
24249
|
+
logger.log(`[graceful-restart] supervisor.pid ${supPid} is stale or not a supervisor \u2014 not signalling`);
|
|
23955
24250
|
}
|
|
23956
24251
|
} catch {
|
|
23957
24252
|
}
|
|
@@ -24098,7 +24393,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24098
24393
|
const PING_TIMEOUT_MS = 15e3;
|
|
24099
24394
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
24100
24395
|
const RECONNECT_JITTER_MS = 2500;
|
|
24101
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
24396
|
+
const { WorkflowScheduler } = await import('./scheduler-DIYulmdV.mjs');
|
|
24102
24397
|
const workflowProjectRoots = () => {
|
|
24103
24398
|
const dirs = /* @__PURE__ */ new Set();
|
|
24104
24399
|
for (const s of pidToTrackedSession.values()) {
|
|
@@ -24731,4 +25026,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
24731
25026
|
writeStopMarker: writeStopMarker
|
|
24732
25027
|
});
|
|
24733
25028
|
|
|
24734
|
-
export { listSkillFiles as $, saveWorkflow as A, rawWorkflow as B, listWorkflows as C, isWorkflowEnabled as D, workflowSchedules as E, inZone as F, cronMatches as G, summarize as H, workflowSteps as I, parseJwtEmail as J, computeCollectionConfigUpdate as K, SYSTEM_COLLECTION_CONFIG as L, loadMachineContext as M, buildMachineInstructions as N, machineToolsForRole as O, buildMachineTools as P, parseFrontmatter as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, getSkillsServer as T, getSkillsWorkspaceName as U, getSkillsCollectionName as V, fetchWithTimeout as W, searchSkills as X, SKILLS_DIR as Y, getSkillInfo as Z, downloadSkillFile as _, createSessionStore as a, resolveModel as a0, formatHandle as a1, normalizeAllowedUser as a2, loadSecurityContextConfig as a3, resolveSecurityContext as a4, buildSecurityContextFromFlags as a5, mergeSecurityContexts as a6, buildSessionShareUrl as a7, computeOutboundHop as a8, registerAwaitingReply as a9, kimiSetup as aA, api as aB,
|
|
25029
|
+
export { listSkillFiles as $, saveWorkflow as A, rawWorkflow as B, listWorkflows as C, isWorkflowEnabled as D, workflowSchedules as E, inZone as F, cronMatches as G, summarize as H, workflowSteps as I, parseJwtEmail as J, computeCollectionConfigUpdate as K, SYSTEM_COLLECTION_CONFIG as L, loadMachineContext as M, buildMachineInstructions as N, machineToolsForRole as O, buildMachineTools as P, parseFrontmatter as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, getSkillsServer as T, getSkillsWorkspaceName as U, getSkillsCollectionName as V, fetchWithTimeout as W, searchSkills as X, SKILLS_DIR as Y, getSkillInfo as Z, downloadSkillFile as _, createSessionStore as a, resolveModel as a0, formatHandle as a1, normalizeAllowedUser as a2, loadSecurityContextConfig as a3, resolveSecurityContext as a4, buildSecurityContextFromFlags as a5, mergeSecurityContexts as a6, buildSessionShareUrl as a7, computeOutboundHop as a8, registerAwaitingReply as a9, kimiSetup as aA, api as aB, supervisorLock as aC, run as aD, buildMachineShareUrl as aa, parseHandle as ab, handleMatchesMetadata as ac, PINNED_CODEX_VERSION as ad, clearStopMarker as ae, stopMarkerExists as af, withFileLock as ag, describeMisconfiguration as ah, buildMachineDeps as ai, applyClaudeProxyEnv as aj, composeSessionId as ak, generateFriendlyName as al, generateHookSettings as am, instanceConfig as an, frpc as ao, staticFileServer as ap, claudeAuth as aq, codexProvider as ar, projectInfo as as, DefaultTransport$1 as at, acpBackend as au, acpAgentConfig as av, codexAppServerBackend as aw, GeminiTransport$1 as ax, KimiTransport$1 as ay, kimiProvider as az, stopDaemon as b, connectToHypha as c, daemonStatus as d, shortId as e, resolveProjectRoot as f, getHyphaServerUrl$1 as g, getIssue as h, resumeIssue as i, addComment as j, addIssue as k, listIssues as l, isPendingIssue as m, searchIssues as n, isVisibleTo as o, pauseIssue as p, getRun as q, registerMachineService as r, startDaemon as s, listRuns as t, updateIssue as u, getWorkflow as v, runWorkflow as w, setWorkflowEnabled as x, removeWorkflow as y, validateWorkflowName as z };
|