svamp-cli 0.2.275 → 0.2.277
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-DGT-1wjo.mjs → agentCommands-DN7hb_oL.mjs} +5 -5
- package/dist/{auth-CT55Rse4.mjs → auth-Ci70oWRc.mjs} +1 -1
- package/dist/{cli-DYTmO9JL.mjs → cli-Cgzvydbh.mjs} +97 -65
- package/dist/cli.mjs +2 -2
- package/dist/{commands-D7fIWAgz.mjs → commands-BryGjm3r.mjs} +1 -1
- package/dist/{commands-CVPcbtWV.mjs → commands-CT7m_PBV.mjs} +1 -1
- package/dist/{commands-C8Zc5axO.mjs → commands-CWVq7FHR.mjs} +7 -7
- package/dist/{commands-CXHZapLH.mjs → commands-Cc2BZW1I.mjs} +8 -7
- package/dist/{commands-C0rRjhMD.mjs → commands-CgQ3wXOd.mjs} +2 -2
- package/dist/{commands-BvmFXAmu.mjs → commands-Dx-uU1hc.mjs} +2 -2
- package/dist/{commands-BV_9mFkz.mjs → commands-KWZkSbXH.mjs} +2 -2
- package/dist/{fleet-BCel6ZVc.mjs → fleet-w3MI6ljI.mjs} +2 -2
- package/dist/{frpc-D3faR6iR.mjs → frpc-DnxvF_Mx.mjs} +7 -5
- package/dist/{headlessCli-DVBFm7og.mjs → headlessCli-CqxD6lTs.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/notifyCommands-D8tYXxPA.mjs +128 -0
- package/dist/package-D5iaKvt5.mjs +64 -0
- package/dist/{pinnedClaudeCode-CgEtGkX2.mjs → pinnedClaudeCode-BaMR97BE.mjs} +1 -1
- package/dist/{rpc-xlV-4-BG.mjs → rpc-BKDKqllr.mjs} +1 -3
- package/dist/{rpc-BQ8zLSUc.mjs → rpc-BvXiNu-z.mjs} +1 -1
- package/dist/{run-C3Fcbk-H.mjs → run-CzXwL-ig.mjs} +277 -55
- package/dist/{run-DMPVho7i.mjs → run-pAWQPn46.mjs} +4 -3
- package/dist/{scheduler-DaFZY2ob.mjs → scheduler-D-25wsin.mjs} +1 -1
- package/dist/{serveCommands-B9Yhx1fl.mjs → serveCommands-DIckIiqD.mjs} +5 -5
- package/dist/{sideband-CYqmMG_m.mjs → sideband-Bk0GhfIX.mjs} +1 -1
- package/package.json +2 -2
- package/dist/package-DIuZ7Wfp.mjs +0 -64
|
@@ -3,7 +3,7 @@ import fs$1, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as wr
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import fs__default, { existsSync as existsSync$1, rmSync, readFileSync as readFileSync$1, mkdirSync, writeFileSync, renameSync as renameSync$1, realpathSync as realpathSync$1, copyFileSync, unlinkSync as unlinkSync$1, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
-
import path__default, { join, resolve as resolve$1, dirname as dirname$1, basename as basename$1 } from 'path';
|
|
6
|
+
import path__default, { join, delimiter, resolve as resolve$1, dirname as dirname$1, basename as basename$1 } from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { execFile, spawn, execSync, exec as exec$1 } from 'child_process';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
@@ -1506,6 +1506,9 @@ const COOKIE_NAME = "svamp_serve_token";
|
|
|
1506
1506
|
const SVAMP_SERVE_TOKEN_PARAM = "__svamp_serve_token";
|
|
1507
1507
|
const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1508
1508
|
const DEFAULT_CACHE_MAX_SIZE = 1e3;
|
|
1509
|
+
function jsStringLiteral(value) {
|
|
1510
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1511
|
+
}
|
|
1509
1512
|
class TokenCache {
|
|
1510
1513
|
cache = /* @__PURE__ */ new Map();
|
|
1511
1514
|
ttlMs;
|
|
@@ -1710,10 +1713,10 @@ a:hover{text-decoration:underline}
|
|
|
1710
1713
|
<div class="status" id="status"></div>
|
|
1711
1714
|
</div>
|
|
1712
1715
|
<script>
|
|
1713
|
-
const hyphaServer = ${
|
|
1714
|
-
const redirectUrl = ${
|
|
1715
|
-
const cookieName = ${
|
|
1716
|
-
const tokenParam = ${
|
|
1716
|
+
const hyphaServer = ${jsStringLiteral(this.hyphaServerUrl)};
|
|
1717
|
+
const redirectUrl = ${jsStringLiteral(redirectUrl)};
|
|
1718
|
+
const cookieName = ${jsStringLiteral(COOKIE_NAME)};
|
|
1719
|
+
const tokenParam = ${jsStringLiteral(SVAMP_SERVE_TOKEN_PARAM)};
|
|
1717
1720
|
|
|
1718
1721
|
const btn = document.getElementById('login-btn');
|
|
1719
1722
|
const statusEl = document.getElementById('status');
|
|
@@ -2210,7 +2213,7 @@ class ServeManager {
|
|
|
2210
2213
|
throw new Error(`Path does not exist: ${resolvedDir}`);
|
|
2211
2214
|
}
|
|
2212
2215
|
if (this.mounts.has(spec.name)) {
|
|
2213
|
-
await this.removeMount(spec.name);
|
|
2216
|
+
await this.removeMount(spec.name, { replacing: true });
|
|
2214
2217
|
}
|
|
2215
2218
|
const access = spec.access ?? "link";
|
|
2216
2219
|
if (access === "owner" && !spec.ownerEmail) {
|
|
@@ -2249,7 +2252,7 @@ class ServeManager {
|
|
|
2249
2252
|
/**
|
|
2250
2253
|
* Remove a mount. If no mounts remain, stop Caddy + auth proxy.
|
|
2251
2254
|
*/
|
|
2252
|
-
async removeMount(name) {
|
|
2255
|
+
async removeMount(name, opts) {
|
|
2253
2256
|
if (!this.mounts.has(name)) {
|
|
2254
2257
|
throw new Error(`Mount '${name}' not found`);
|
|
2255
2258
|
}
|
|
@@ -2269,7 +2272,7 @@ class ServeManager {
|
|
|
2269
2272
|
}
|
|
2270
2273
|
this.persist();
|
|
2271
2274
|
this.log(`Mount removed: ${name}`);
|
|
2272
|
-
if (this.mounts.size === 0) {
|
|
2275
|
+
if (this.mounts.size === 0 && !opts?.replacing) {
|
|
2273
2276
|
await this.shutdown();
|
|
2274
2277
|
}
|
|
2275
2278
|
}
|
|
@@ -2708,12 +2711,27 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
|
|
|
2708
2711
|
if (req.method === "PUT") {
|
|
2709
2712
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2710
2713
|
const ws = fs.createWriteStream(filePath);
|
|
2714
|
+
let done = false;
|
|
2715
|
+
const cleanupPartial = () => {
|
|
2716
|
+
if (done) return;
|
|
2717
|
+
done = true;
|
|
2718
|
+
ws.destroy();
|
|
2719
|
+
fs.unlink(filePath, () => {
|
|
2720
|
+
});
|
|
2721
|
+
};
|
|
2722
|
+
req.on("aborted", cleanupPartial);
|
|
2723
|
+
req.on("close", () => {
|
|
2724
|
+
if (!ws.writableFinished) cleanupPartial();
|
|
2725
|
+
});
|
|
2711
2726
|
req.pipe(ws);
|
|
2712
2727
|
ws.on("finish", () => {
|
|
2728
|
+
done = true;
|
|
2713
2729
|
res.writeHead(201);
|
|
2714
2730
|
res.end();
|
|
2715
2731
|
});
|
|
2716
2732
|
ws.on("error", (err) => {
|
|
2733
|
+
if (done) return;
|
|
2734
|
+
done = true;
|
|
2717
2735
|
res.writeHead(500);
|
|
2718
2736
|
res.end(err.message);
|
|
2719
2737
|
});
|
|
@@ -2955,7 +2973,7 @@ Connection: close\r
|
|
|
2955
2973
|
const mount = this.mounts.get(mountName);
|
|
2956
2974
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
|
|
2957
2975
|
try {
|
|
2958
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2976
|
+
const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
|
|
2959
2977
|
let tunnel;
|
|
2960
2978
|
tunnel = new FrpcTunnel({
|
|
2961
2979
|
name: tunnelName,
|
|
@@ -4350,6 +4368,14 @@ class ChannelOutbox {
|
|
|
4350
4368
|
file;
|
|
4351
4369
|
byChannel = /* @__PURE__ */ new Map();
|
|
4352
4370
|
seqByChannel = /* @__PURE__ */ new Map();
|
|
4371
|
+
// #0500: per-channel MONOTONIC high-water seq that survives TTL eviction/compaction. seqByChannel
|
|
4372
|
+
// is rebuilt from RETAINED records on every reload, so once a channel's replies all age out (>1h
|
|
4373
|
+
// idle) its seq would reset to 1 — a long-poll receiver holding a pre-eviction cursor N would then
|
|
4374
|
+
// silently drop the new seq=1..N replies (since() filters seq > cursor). highWater is never lowered
|
|
4375
|
+
// and is persisted to a small sidecar (_outbox_seq.json) so seq stays monotonic across eviction AND
|
|
4376
|
+
// daemon restarts. Cross-process safe: re-read + max before each bump (via reload()).
|
|
4377
|
+
highWater = /* @__PURE__ */ new Map();
|
|
4378
|
+
seqFile;
|
|
4353
4379
|
emitter = new EventEmitter();
|
|
4354
4380
|
appendsSinceCompact = 0;
|
|
4355
4381
|
// #0362: change-detection for reload() — a long-poll receiver reuses ONE instance and only
|
|
@@ -4360,12 +4386,14 @@ class ChannelOutbox {
|
|
|
4360
4386
|
constructor(projectDir) {
|
|
4361
4387
|
const dir = join$1(projectDir, ".svamp", "channels");
|
|
4362
4388
|
this.file = join$1(dir, "_outbox.jsonl");
|
|
4389
|
+
this.seqFile = join$1(dir, "_outbox_seq.json");
|
|
4363
4390
|
this.emitter.setMaxListeners(0);
|
|
4364
4391
|
try {
|
|
4365
4392
|
mkdirSync$1(dir, { recursive: true });
|
|
4366
4393
|
} catch {
|
|
4367
4394
|
}
|
|
4368
4395
|
this._load();
|
|
4396
|
+
this._loadHighWater();
|
|
4369
4397
|
}
|
|
4370
4398
|
/** Current on-disk (mtimeMs, size), or (0, -1) if the file does not exist. */
|
|
4371
4399
|
_stat() {
|
|
@@ -4388,6 +4416,7 @@ class ChannelOutbox {
|
|
|
4388
4416
|
this.byChannel.clear();
|
|
4389
4417
|
this.seqByChannel.clear();
|
|
4390
4418
|
this._load();
|
|
4419
|
+
this._loadHighWater();
|
|
4391
4420
|
return true;
|
|
4392
4421
|
}
|
|
4393
4422
|
_load() {
|
|
@@ -4410,6 +4439,7 @@ class ChannelOutbox {
|
|
|
4410
4439
|
arr.push(reply);
|
|
4411
4440
|
this.byChannel.set(channelId, arr);
|
|
4412
4441
|
this.seqByChannel.set(channelId, Math.max(this.seqByChannel.get(channelId) || 0, reply.seq));
|
|
4442
|
+
this.highWater.set(channelId, Math.max(this.highWater.get(channelId) || 0, reply.seq));
|
|
4413
4443
|
}
|
|
4414
4444
|
for (const [ch, arr] of this.byChannel) if (arr.length > MAX_PER_CHANNEL) this.byChannel.set(ch, arr.slice(-MAX_PER_CHANNEL));
|
|
4415
4445
|
} catch {
|
|
@@ -4437,6 +4467,30 @@ class ChannelOutbox {
|
|
|
4437
4467
|
} catch {
|
|
4438
4468
|
}
|
|
4439
4469
|
}
|
|
4470
|
+
/** #0500: merge the durable per-channel high-water sidecar into memory (never lowers a value). */
|
|
4471
|
+
_loadHighWater() {
|
|
4472
|
+
try {
|
|
4473
|
+
if (!existsSync(this.seqFile)) return;
|
|
4474
|
+
const obj = JSON.parse(readFileSync(this.seqFile, "utf8"));
|
|
4475
|
+
for (const [ch, seq] of Object.entries(obj)) {
|
|
4476
|
+
if (typeof seq === "number" && Number.isFinite(seq)) {
|
|
4477
|
+
this.highWater.set(ch, Math.max(this.highWater.get(ch) || 0, seq));
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
} catch {
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
/** #0500: atomically persist the high-water map so seq survives eviction + restart. Best-effort. */
|
|
4484
|
+
_persistHighWater() {
|
|
4485
|
+
try {
|
|
4486
|
+
const obj = {};
|
|
4487
|
+
for (const [ch, seq] of this.highWater) obj[ch] = seq;
|
|
4488
|
+
const tmp = `${this.seqFile}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
4489
|
+
writeFileSync$1(tmp, JSON.stringify(obj));
|
|
4490
|
+
renameSync(tmp, this.seqFile);
|
|
4491
|
+
} catch {
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4440
4494
|
_evict(channelId) {
|
|
4441
4495
|
const cutoff = Date.now() - TTL_MS;
|
|
4442
4496
|
let arr = this.byChannel.get(channelId);
|
|
@@ -4448,8 +4502,10 @@ class ChannelOutbox {
|
|
|
4448
4502
|
/** Append a reply addressed to `to`. Assigns seq + ts, persists, and wakes waiters. */
|
|
4449
4503
|
append(channelId, r) {
|
|
4450
4504
|
this.reload();
|
|
4451
|
-
const seq = (this.seqByChannel.get(channelId) || 0) + 1;
|
|
4505
|
+
const seq = Math.max(this.highWater.get(channelId) || 0, this.seqByChannel.get(channelId) || 0) + 1;
|
|
4452
4506
|
this.seqByChannel.set(channelId, seq);
|
|
4507
|
+
this.highWater.set(channelId, seq);
|
|
4508
|
+
this._persistHighWater();
|
|
4453
4509
|
const reply = { seq, ts: Date.now(), to: r.to, body: r.body, ...r.correlationId ? { correlationId: r.correlationId } : {} };
|
|
4454
4510
|
const arr = this.byChannel.get(channelId) || [];
|
|
4455
4511
|
arr.push(reply);
|
|
@@ -5760,7 +5816,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
5760
5816
|
const tunnels = handlers.tunnels;
|
|
5761
5817
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
5762
5818
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
5763
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
5819
|
+
const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
|
|
5764
5820
|
const tunnel = new FrpcTunnel({
|
|
5765
5821
|
name: params.name,
|
|
5766
5822
|
ports: params.ports,
|
|
@@ -5908,7 +5964,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
5908
5964
|
authorizeRequest(context, currentMetadata.sharing, "view");
|
|
5909
5965
|
const sm = handlers.serveManager;
|
|
5910
5966
|
if (!sm) throw new Error("Serve manager not available");
|
|
5911
|
-
|
|
5967
|
+
const info = sm.getInfo();
|
|
5968
|
+
const isAdmin = serveCallerTrusted(context);
|
|
5969
|
+
return { ...info, mounts: (info.mounts || []).map((m) => sanitizeMountForRole(m, isAdmin)) };
|
|
5912
5970
|
},
|
|
5913
5971
|
/**
|
|
5914
5972
|
* Aggregate frpc tunnel health for all serve mounts.
|
|
@@ -6234,7 +6292,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
6234
6292
|
}
|
|
6235
6293
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
6236
6294
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
6237
|
-
const { toolsForRole } = await import('./sideband-
|
|
6295
|
+
const { toolsForRole } = await import('./sideband-Bk0GhfIX.mjs');
|
|
6238
6296
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
6239
6297
|
return fmt(r2);
|
|
6240
6298
|
}
|
|
@@ -6333,7 +6391,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
6333
6391
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
6334
6392
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
6335
6393
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
6336
|
-
const { queryCore } = await import('./commands-
|
|
6394
|
+
const { queryCore } = await import('./commands-BryGjm3r.mjs');
|
|
6337
6395
|
const timeout = c.reply?.timeout_sec || 120;
|
|
6338
6396
|
let result;
|
|
6339
6397
|
try {
|
|
@@ -6766,11 +6824,10 @@ function computeRetryDelayMs(attempt, cfg = getRateLimitRetryConfig(), rng = Mat
|
|
|
6766
6824
|
}
|
|
6767
6825
|
function isRetryableRateLimit(text, apiErrorStatus) {
|
|
6768
6826
|
const t = (text || "").toLowerCase();
|
|
6769
|
-
const
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
t.includes("spending limit") || t.includes("credit") || t.includes("balance") || t.includes("billing") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
|
|
6827
|
+
const strongHardLimit = t.includes("quota") || t.includes("spending limit") || t.includes("payment") || t.includes("subscription") || t.includes("1m-context") || t.includes("1m context") || t.includes("insufficient") || t.includes("usage limit") && !t.includes("not your usage limit");
|
|
6828
|
+
const serverFaultSignal = typeof apiErrorStatus === "number" && apiErrorStatus >= 500 && apiErrorStatus <= 599 || /\b(500|502|503|504|520|521|522|523|524|529)\b/.test(t);
|
|
6829
|
+
const weakBillingLimit = !serverFaultSignal && (t.includes("credit") || t.includes("balance") || t.includes("billing"));
|
|
6830
|
+
const hardLimit = strongHardLimit || weakBillingLimit;
|
|
6774
6831
|
if (hardLimit) return false;
|
|
6775
6832
|
const authIssue = apiErrorStatus === 401 || apiErrorStatus === 403 || t.includes("unauthorized") || t.includes("invalid api key") || t.includes("authentication") || t.includes("forbidden");
|
|
6776
6833
|
if (authIssue) return false;
|
|
@@ -7142,7 +7199,13 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
7142
7199
|
const skillCtxFor = (c) => ({
|
|
7143
7200
|
channelsServiceId,
|
|
7144
7201
|
baseUrl: channelsBaseUrl,
|
|
7145
|
-
|
|
7202
|
+
// #0512: embed ONLY the channel-wide shared_key. The previous `|| callers?.[0]?.key`
|
|
7203
|
+
// fallback leaked one specific caller's PRIVATE per-key credential into the skill body,
|
|
7204
|
+
// which is served by the UNAUTHENTICATED channelDescribe (and view-tier getChannelSkill) —
|
|
7205
|
+
// an anonymous reader could then impersonate that caller (send as them + read their outbox).
|
|
7206
|
+
// For a per-key channel (no shared_key) key is undefined and generateSkillBody renders the
|
|
7207
|
+
// <your-key> placeholder, which is the correct instruction for a per-key caller.
|
|
7208
|
+
key: c.identity?.shared_key,
|
|
7146
7209
|
// The session this skill is being copied FROM — baked into `dynamic` channel
|
|
7147
7210
|
// instructions as the routing target so the copied link lands in THIS session.
|
|
7148
7211
|
session: sessionId
|
|
@@ -7525,7 +7588,9 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
7525
7588
|
name: c.name,
|
|
7526
7589
|
skillUrl,
|
|
7527
7590
|
sendUrl,
|
|
7528
|
-
|
|
7591
|
+
// #0512: only the channel-wide shared_key — never an individual caller's private key
|
|
7592
|
+
// (this getChannelSkill result is reachable at view tier).
|
|
7593
|
+
key: c.identity?.shared_key,
|
|
7529
7594
|
hyphaKeyless: (c.identity?.hypha_allow || []).length > 0,
|
|
7530
7595
|
bind: c.bind,
|
|
7531
7596
|
bindMode: mode,
|
|
@@ -8771,6 +8836,23 @@ function handleClaudeMessage(ctx, msg) {
|
|
|
8771
8836
|
}
|
|
8772
8837
|
}
|
|
8773
8838
|
|
|
8839
|
+
const KNOWN_AGENT_LAUNCHERS = ["claude", "codex", "gemini"];
|
|
8840
|
+
function agentLauncherExists(cmd, env = process.env) {
|
|
8841
|
+
if (!cmd) return false;
|
|
8842
|
+
if (cmd.includes("/") || cmd.includes("\\")) return existsSync$1(cmd);
|
|
8843
|
+
const dirs = (env.PATH || "").split(delimiter).filter(Boolean);
|
|
8844
|
+
const exts = process.platform === "win32" ? ["", ...(env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean)] : [""];
|
|
8845
|
+
for (const dir of dirs) {
|
|
8846
|
+
for (const ext of exts) {
|
|
8847
|
+
if (existsSync$1(join(dir, cmd + ext))) return true;
|
|
8848
|
+
}
|
|
8849
|
+
}
|
|
8850
|
+
return false;
|
|
8851
|
+
}
|
|
8852
|
+
function detectAvailableAgents(env = process.env) {
|
|
8853
|
+
return KNOWN_AGENT_LAUNCHERS.filter((c) => agentLauncherExists(c, env));
|
|
8854
|
+
}
|
|
8855
|
+
|
|
8774
8856
|
const INLINE_ARTIFACT_MAX_BYTES = 512 * 1024;
|
|
8775
8857
|
const MAX_STORED_MESSAGE_BYTES = 2 * 1024 * 1024;
|
|
8776
8858
|
const MIN_DATA_URI_SCAN_CHARS = INLINE_ARTIFACT_MAX_BYTES;
|
|
@@ -9581,6 +9663,52 @@ class SharingNotificationSync {
|
|
|
9581
9663
|
this.log(`[USER EVENT] Failed to publish ${event.type} for ${event.recipientEmail}: ${err.message}`);
|
|
9582
9664
|
}
|
|
9583
9665
|
}
|
|
9666
|
+
/**
|
|
9667
|
+
* List user events addressed to `recipientEmail` (the bell inbox), newest first, excluding
|
|
9668
|
+
* expired ones. Reuses the global svamp-user-events collection — cross-workspace by design.
|
|
9669
|
+
* Mirrors the webapp's fetchUserEvents so the CLI reads the same source of truth.
|
|
9670
|
+
*/
|
|
9671
|
+
async listUserEvents(recipientEmail) {
|
|
9672
|
+
if (!this.initialized || !this.eventCollectionId) return [];
|
|
9673
|
+
const normalized = recipientEmail.toLowerCase();
|
|
9674
|
+
try {
|
|
9675
|
+
const children = await this.artifactManager.list({
|
|
9676
|
+
parent_id: this.eventCollectionId,
|
|
9677
|
+
_rkwargs: true
|
|
9678
|
+
});
|
|
9679
|
+
if (!Array.isArray(children)) return [];
|
|
9680
|
+
const now = Date.now();
|
|
9681
|
+
return children.map((c) => c?.manifest).filter((m) => !!m && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized && (!m.expiresAt || m.expiresAt > now)).sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
|
|
9682
|
+
} catch (err) {
|
|
9683
|
+
this.log(`[USER EVENT] listUserEvents failed for ${recipientEmail}: ${err.message}`);
|
|
9684
|
+
return [];
|
|
9685
|
+
}
|
|
9686
|
+
}
|
|
9687
|
+
/**
|
|
9688
|
+
* Acknowledge (dismiss) a user event by deleting its artifact. The alias is derived from the
|
|
9689
|
+
* event id + recipient email, so only the recipient's own event is removed. Returns true if a
|
|
9690
|
+
* matching event was deleted.
|
|
9691
|
+
*/
|
|
9692
|
+
async ackUserEvent(eventId, recipientEmail) {
|
|
9693
|
+
if (!this.initialized || !this.eventCollectionId) return false;
|
|
9694
|
+
const normalized = recipientEmail.toLowerCase();
|
|
9695
|
+
try {
|
|
9696
|
+
const children = await this.artifactManager.list({ parent_id: this.eventCollectionId, _rkwargs: true });
|
|
9697
|
+
if (!Array.isArray(children)) return false;
|
|
9698
|
+
const short = eventId.slice(0, 8);
|
|
9699
|
+
const match = children.find((c) => {
|
|
9700
|
+
const m = c?.manifest;
|
|
9701
|
+
return m && typeof m.id === "string" && (m.id === eventId || m.id.slice(0, 8) === short) && typeof m.recipientEmail === "string" && m.recipientEmail.toLowerCase() === normalized;
|
|
9702
|
+
});
|
|
9703
|
+
if (!match) return false;
|
|
9704
|
+
await this.artifactManager.delete({ artifact_id: match.id, _rkwargs: true });
|
|
9705
|
+
this.log(`[USER EVENT] Acked ${eventId.slice(0, 8)} for ${recipientEmail}`);
|
|
9706
|
+
return true;
|
|
9707
|
+
} catch (err) {
|
|
9708
|
+
this.log(`[USER EVENT] ackUserEvent failed for ${recipientEmail}: ${err.message}`);
|
|
9709
|
+
return false;
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9584
9712
|
// ── Sharing config diff + sync ───────────────────────────────────
|
|
9585
9713
|
async syncSharing(sessionId, oldSharing, newSharing, context) {
|
|
9586
9714
|
if (!this.initialized) return;
|
|
@@ -11404,7 +11532,13 @@ class CodexAppServerBackend {
|
|
|
11404
11532
|
if (avail === false) throw new Error("Installed Codex is too old for app-server \u2014 upgrade to >= 0.100 (`npm i -g @openai/codex@latest`).");
|
|
11405
11533
|
await this.client.start();
|
|
11406
11534
|
if (this.opts.resumeThreadId) {
|
|
11407
|
-
|
|
11535
|
+
try {
|
|
11536
|
+
await this.client.resumeThread({ threadId: this.opts.resumeThreadId, model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
|
|
11537
|
+
} catch (err) {
|
|
11538
|
+
this.log(`[Codex] resumeThread(${this.opts.resumeThreadId}) failed (${err?.message ?? err}) \u2014 starting a fresh thread instead (context lost, session preserved)`);
|
|
11539
|
+
const r = await this.client.startThread({ model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
|
|
11540
|
+
this.model = this.model ?? r.model;
|
|
11541
|
+
}
|
|
11408
11542
|
} else {
|
|
11409
11543
|
const r = await this.client.startThread({ model: this.model, approvalPolicy: this.approvalPolicy, sandbox: this.sandbox });
|
|
11410
11544
|
this.model = this.model ?? r.model;
|
|
@@ -12135,7 +12269,31 @@ async function verifyIsolation(method, binaryPath) {
|
|
|
12135
12269
|
return { passed: false, error: `${method} is not supported for external isolation` };
|
|
12136
12270
|
}
|
|
12137
12271
|
}
|
|
12272
|
+
function isolationDisabledByEnv(env = process.env) {
|
|
12273
|
+
const v = (env.SVAMP_ISOLATION || "").trim().toLowerCase();
|
|
12274
|
+
if (v && ["none", "off", "disabled", "0", "false", "no"].includes(v)) return true;
|
|
12275
|
+
const truthy = (s) => !!s && !["", "0", "false", "no", "off"].includes(s.trim().toLowerCase());
|
|
12276
|
+
return truthy(env.SVAMP_NO_ISOLATION) || truthy(env.SVAMP_SKIP_NONO);
|
|
12277
|
+
}
|
|
12138
12278
|
async function detectIsolationCapabilities() {
|
|
12279
|
+
if (isolationDisabledByEnv()) {
|
|
12280
|
+
console.log(
|
|
12281
|
+
"[isolation] Disabled via env (SVAMP_ISOLATION=none) \u2014 skipping nono auto-install + runtime detection. Sessions run WITHOUT per-session sandboxing; session sharing is allowed (rely on the container/host boundary)."
|
|
12282
|
+
);
|
|
12283
|
+
const notFound = () => ({ found: false });
|
|
12284
|
+
return {
|
|
12285
|
+
available: [],
|
|
12286
|
+
preferred: null,
|
|
12287
|
+
disabled: true,
|
|
12288
|
+
details: {
|
|
12289
|
+
nono: notFound(),
|
|
12290
|
+
docker: notFound(),
|
|
12291
|
+
podman: notFound(),
|
|
12292
|
+
srt: notFound(),
|
|
12293
|
+
bwrap: notFound()
|
|
12294
|
+
}
|
|
12295
|
+
};
|
|
12296
|
+
}
|
|
12139
12297
|
const checks = {
|
|
12140
12298
|
nono: checkCommand("nono", ["--version"]),
|
|
12141
12299
|
docker: checkCommand("docker", ["--version"]),
|
|
@@ -12625,6 +12783,7 @@ class ProcessSupervisor {
|
|
|
12625
12783
|
throw new Error(`Process '${entry.spec.name}' is already running`);
|
|
12626
12784
|
}
|
|
12627
12785
|
entry.stopping = false;
|
|
12786
|
+
this.clearTimers(entry);
|
|
12628
12787
|
if (entry.spec.desiredStopped) {
|
|
12629
12788
|
entry.spec.desiredStopped = false;
|
|
12630
12789
|
await this.persistSpec(entry.spec);
|
|
@@ -12683,6 +12842,8 @@ class ProcessSupervisor {
|
|
|
12683
12842
|
this.clearTimers(entry);
|
|
12684
12843
|
await this.killChild(entry.child);
|
|
12685
12844
|
entry.child = void 0;
|
|
12845
|
+
} else {
|
|
12846
|
+
this.clearTimers(entry);
|
|
12686
12847
|
}
|
|
12687
12848
|
if (entry.deleted) return;
|
|
12688
12849
|
entry.stopping = false;
|
|
@@ -12914,6 +13075,7 @@ class ProcessSupervisor {
|
|
|
12914
13075
|
spawnProcess(entry) {
|
|
12915
13076
|
const { spec, state } = entry;
|
|
12916
13077
|
if (entry.deleted) return;
|
|
13078
|
+
state.consecutiveProbeFailures = 0;
|
|
12917
13079
|
try {
|
|
12918
13080
|
const env = { ...process.env, ...spec.env ?? {} };
|
|
12919
13081
|
const child = spawn(spec.command, spec.args, {
|
|
@@ -13018,6 +13180,7 @@ class ProcessSupervisor {
|
|
|
13018
13180
|
);
|
|
13019
13181
|
entry.restartTimer = setTimeout(() => {
|
|
13020
13182
|
if (entry.deleted || entry.stopping) return;
|
|
13183
|
+
if (entry.child) return;
|
|
13021
13184
|
state.restartCount++;
|
|
13022
13185
|
state.status = "starting";
|
|
13023
13186
|
this.spawnProcess(entry);
|
|
@@ -13046,6 +13209,8 @@ class ProcessSupervisor {
|
|
|
13046
13209
|
const timeoutMs = (probe.timeout ?? DEFAULT_PROBE_TIMEOUT_S) * 1e3;
|
|
13047
13210
|
const threshold = probe.failureThreshold ?? DEFAULT_PROBE_FAILURE_THRESHOLD;
|
|
13048
13211
|
const url = `http://localhost:${probe.port}${urlPath}`;
|
|
13212
|
+
const probedChild = entry.child;
|
|
13213
|
+
const stillSameChild = () => !entry.deleted && !entry.stopping && entry.child === probedChild && entry.state.status === "running";
|
|
13049
13214
|
try {
|
|
13050
13215
|
const controller = new AbortController();
|
|
13051
13216
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -13055,6 +13220,7 @@ class ProcessSupervisor {
|
|
|
13055
13220
|
} finally {
|
|
13056
13221
|
clearTimeout(timer);
|
|
13057
13222
|
}
|
|
13223
|
+
if (!stillSameChild()) return;
|
|
13058
13224
|
const ok = resp.ok;
|
|
13059
13225
|
entry.state.lastProbe = { ok, timestamp: Date.now(), statusCode: resp.status };
|
|
13060
13226
|
if (ok) {
|
|
@@ -13069,6 +13235,7 @@ class ProcessSupervisor {
|
|
|
13069
13235
|
}
|
|
13070
13236
|
}
|
|
13071
13237
|
} catch (err) {
|
|
13238
|
+
if (!stillSameChild()) return;
|
|
13072
13239
|
entry.state.lastProbe = { ok: false, timestamp: Date.now(), error: err.message };
|
|
13073
13240
|
entry.state.consecutiveProbeFailures++;
|
|
13074
13241
|
console.warn(
|
|
@@ -13099,7 +13266,12 @@ class ProcessSupervisor {
|
|
|
13099
13266
|
return;
|
|
13100
13267
|
}
|
|
13101
13268
|
console.log(`[SUPERVISOR] Process '${entry.spec.name}' TTL: expires in ${remainingS.toFixed(0)}s`);
|
|
13102
|
-
|
|
13269
|
+
const MAX_DELAY = 2147483647;
|
|
13270
|
+
const delayMs = remainingS * 1e3;
|
|
13271
|
+
entry.ttlTimer = setTimeout(
|
|
13272
|
+
() => delayMs > MAX_DELAY ? this.setupTTL(entry) : this.expireProcess(entry),
|
|
13273
|
+
Math.min(delayMs, MAX_DELAY)
|
|
13274
|
+
);
|
|
13103
13275
|
}
|
|
13104
13276
|
expireProcess(entry) {
|
|
13105
13277
|
if (entry.deleted) return;
|
|
@@ -14031,7 +14203,7 @@ function summarizeExtensions(extensions, maxExtensions = DEFAULT_LOOP_MAX_EXTENS
|
|
|
14031
14203
|
const n = grantedExtensionCount(extensions, maxExtensions);
|
|
14032
14204
|
if (n === 0) return "";
|
|
14033
14205
|
const turns = extensionTurns(extensions, maxExtensions);
|
|
14034
|
-
const reasons = extensions.slice(0, maxExtensions).map((e) => (e
|
|
14206
|
+
const reasons = extensions.slice(0, maxExtensions).map((e) => (e?.reason || "").trim()).filter(Boolean).join("; ").slice(0, 300);
|
|
14035
14207
|
return `${n} extension${n === 1 ? "" : "s"} granted (+${turns} turns)${reasons ? `: ${reasons}` : ""}`;
|
|
14036
14208
|
}
|
|
14037
14209
|
const DEFAULT_LOOP_STUCK_CHECKPOINTS = 3;
|
|
@@ -14039,7 +14211,7 @@ const DEFAULT_LOOP_MAX_AUTORESUMES = Number.POSITIVE_INFINITY;
|
|
|
14039
14211
|
const PROGRESS_HISTORY_CAP = 12;
|
|
14040
14212
|
function resolveStuckLimit(envValue) {
|
|
14041
14213
|
const n = Number(envValue);
|
|
14042
|
-
if (Number.isFinite(n) && n >= 1) return Math.floor(n);
|
|
14214
|
+
if (Number.isFinite(n) && n >= 1) return Math.min(Math.floor(n), PROGRESS_HISTORY_CAP - 1);
|
|
14043
14215
|
return DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
14044
14216
|
}
|
|
14045
14217
|
function resolveMaxAutoResumes(envValue) {
|
|
@@ -14097,7 +14269,8 @@ function resolveEvaluatorModel(envValue) {
|
|
|
14097
14269
|
function parseEvaluatorVerdict(text) {
|
|
14098
14270
|
if (!text) return null;
|
|
14099
14271
|
const candidates = [];
|
|
14100
|
-
|
|
14272
|
+
const openStack = [];
|
|
14273
|
+
let inStr = false, esc = false;
|
|
14101
14274
|
for (let i = 0; i < text.length; i++) {
|
|
14102
14275
|
const c = text[i];
|
|
14103
14276
|
if (inStr) {
|
|
@@ -14113,15 +14286,11 @@ function parseEvaluatorVerdict(text) {
|
|
|
14113
14286
|
if (c === '"') {
|
|
14114
14287
|
inStr = true;
|
|
14115
14288
|
} else if (c === "{") {
|
|
14116
|
-
|
|
14117
|
-
depth++;
|
|
14289
|
+
openStack.push(i);
|
|
14118
14290
|
} else if (c === "}") {
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
candidates.push(text.slice(start, i + 1));
|
|
14123
|
-
start = -1;
|
|
14124
|
-
}
|
|
14291
|
+
const open = openStack.pop();
|
|
14292
|
+
if (open !== void 0) {
|
|
14293
|
+
candidates.push(text.slice(open, i + 1));
|
|
14125
14294
|
}
|
|
14126
14295
|
}
|
|
14127
14296
|
}
|
|
@@ -15387,7 +15556,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
15387
15556
|
const key = s.started_at ?? "\u2205";
|
|
15388
15557
|
if (key === announcedLoopStart) return;
|
|
15389
15558
|
announcedLoopStart = key;
|
|
15390
|
-
const iter = typeof s.iteration === "number" ? s.iteration : 0;
|
|
15559
|
+
const iter = s.ledger && typeof s.ledger.turns === "number" && s.ledger.turns > 0 ? s.ledger.turns : typeof s.iteration === "number" ? s.iteration : 0;
|
|
15391
15560
|
const plural = iter === 1 ? "" : "s";
|
|
15392
15561
|
const gaveUpCfg = readLoopJson(directory, sessionId, "loop.config.json") || {};
|
|
15393
15562
|
const curMax = typeof gaveUpCfg.max_iterations === "number" ? gaveUpCfg.max_iterations : iter;
|
|
@@ -16015,7 +16184,7 @@ async function startDaemon(options) {
|
|
|
16015
16184
|
try {
|
|
16016
16185
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
16017
16186
|
if (!dir) return;
|
|
16018
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
16187
|
+
const { reconcileServiceLinks } = await import('./agentCommands-DN7hb_oL.mjs');
|
|
16019
16188
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
16020
16189
|
const config = readSvampConfig(configPath);
|
|
16021
16190
|
const entries = Array.from(urls.entries());
|
|
@@ -16033,7 +16202,7 @@ async function startDaemon(options) {
|
|
|
16033
16202
|
}
|
|
16034
16203
|
}
|
|
16035
16204
|
async function createExposedTunnel(spec) {
|
|
16036
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
16205
|
+
const { FrpcTunnel } = await import('./frpc-DnxvF_Mx.mjs');
|
|
16037
16206
|
const tunnel = new FrpcTunnel({
|
|
16038
16207
|
name: spec.name,
|
|
16039
16208
|
ports: spec.ports,
|
|
@@ -16061,7 +16230,7 @@ async function startDaemon(options) {
|
|
|
16061
16230
|
ensureAutoInstalledCommands(logger);
|
|
16062
16231
|
(async () => {
|
|
16063
16232
|
try {
|
|
16064
|
-
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-
|
|
16233
|
+
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-BaMR97BE.mjs');
|
|
16065
16234
|
beginClaudeVersionReconcile((msg) => logger.log(msg));
|
|
16066
16235
|
} catch (e) {
|
|
16067
16236
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
@@ -16743,10 +16912,11 @@ ${parts.join("\n")}`);
|
|
|
16743
16912
|
logger.log(`[Session ${sessionId}] loop was cancelled during verification \u2014 not re-kicking (#0308)`);
|
|
16744
16913
|
return;
|
|
16745
16914
|
}
|
|
16746
|
-
const
|
|
16915
|
+
const curLs = readLoopState(directory, sessionId) || ls;
|
|
16916
|
+
const ledger = curLs.ledger ?? ls.ledger;
|
|
16747
16917
|
const budget = ls.budget;
|
|
16748
|
-
const progress_history = ls.progress_history;
|
|
16749
|
-
const extensions = ls.extensions;
|
|
16918
|
+
const progress_history = curLs.progress_history ?? ls.progress_history;
|
|
16919
|
+
const extensions = curLs.extensions ?? ls.extensions;
|
|
16750
16920
|
if (result.action === "rekick") {
|
|
16751
16921
|
const cond = ls.task || "";
|
|
16752
16922
|
writeGoalLoopState(directory, sessionId, {
|
|
@@ -16875,6 +17045,7 @@ ${parts.join("\n")}`);
|
|
|
16875
17045
|
let consecutiveOverloadRetries = 0;
|
|
16876
17046
|
let overloadBailedThisTurn = false;
|
|
16877
17047
|
let pendingCompactTurn = false;
|
|
17048
|
+
let noIsolationWarned = false;
|
|
16878
17049
|
const RATELIMIT_CFG = getRateLimitRetryConfig();
|
|
16879
17050
|
let currentTurnMessage;
|
|
16880
17051
|
let rateLimitRetryCount = 0;
|
|
@@ -16982,6 +17153,14 @@ ${parts.join("\n")}`);
|
|
|
16982
17153
|
} else if (shouldIsolateSession()) {
|
|
16983
17154
|
logger.log(`[Session ${sessionId}] WARNING: No isolation runtime (nono/docker/podman) available. Session is NOT sandboxed.`);
|
|
16984
17155
|
sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
|
|
17156
|
+
if (!isolationCapabilities.disabled && !noIsolationWarned) {
|
|
17157
|
+
noIsolationWarned = true;
|
|
17158
|
+
sessionService.pushMessage({
|
|
17159
|
+
type: "message",
|
|
17160
|
+
level: "warning",
|
|
17161
|
+
message: "This session requested sandboxing (a security context / --isolate) but no isolation runtime (nono, docker, or podman) is available on this machine \u2014 it is running WITHOUT a sandbox. Install a runtime, or set SVAMP_ISOLATION=none to run unsandboxed intentionally (e.g. when the machine is already a container)."
|
|
17162
|
+
}, "event");
|
|
17163
|
+
}
|
|
16985
17164
|
} else {
|
|
16986
17165
|
sessionMetadata = { ...sessionMetadata, isolationMethod: void 0 };
|
|
16987
17166
|
}
|
|
@@ -17030,6 +17209,22 @@ ${parts.join("\n")}`);
|
|
|
17030
17209
|
spawnEnv.SVAMP_SANDBOXED = "1";
|
|
17031
17210
|
logger.log(`[Session ${sessionId}] Credential staging: HOME=${stagedCredentials.homePath}, SVAMP_SANDBOXED=1`);
|
|
17032
17211
|
}
|
|
17212
|
+
if (!agentLauncherExists("claude")) {
|
|
17213
|
+
const others = detectAvailableAgents().filter((a) => a !== "claude");
|
|
17214
|
+
const hint = others.length ? ` This machine has: ${others.join(", ")} \u2014 start a ${others[0]} session instead.` : " No agent launchers were found on this machine.";
|
|
17215
|
+
logger.log(`[Session ${sessionId}] Claude launcher ('claude') not found on PATH \u2014 aborting spawn (available: ${detectAvailableAgents().join(", ") || "none"})`);
|
|
17216
|
+
sessionService.pushMessage({
|
|
17217
|
+
type: "message",
|
|
17218
|
+
level: "error",
|
|
17219
|
+
message: `Claude Code (\`claude\`) isn't installed on this machine (${os$1.hostname()}), so this Claude session can't run.${hint}`
|
|
17220
|
+
}, "event");
|
|
17221
|
+
sessionMetadata = { ...sessionMetadata, lifecycleState: "idle" };
|
|
17222
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
17223
|
+
sessionWasProcessing = false;
|
|
17224
|
+
signalProcessing(false);
|
|
17225
|
+
sessionService.sendSessionEnd();
|
|
17226
|
+
return void 0;
|
|
17227
|
+
}
|
|
17033
17228
|
const child = spawn(spawnCommand, spawnArgs, {
|
|
17034
17229
|
cwd: directory,
|
|
17035
17230
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -17046,7 +17241,7 @@ ${parts.join("\n")}`);
|
|
|
17046
17241
|
child.on("error", (err) => {
|
|
17047
17242
|
logger.log(`[Session ${sessionId}] Claude process error: ${err.message}`);
|
|
17048
17243
|
sessionService.pushMessage(
|
|
17049
|
-
{ type: "message", message: `Agent process
|
|
17244
|
+
{ type: "message", level: "error", message: `Agent process failed to start: ${err.message}. Please ensure the Claude Code CLI is installed on this machine.` },
|
|
17050
17245
|
"event"
|
|
17051
17246
|
);
|
|
17052
17247
|
sessionWasProcessing = false;
|
|
@@ -18223,18 +18418,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
18223
18418
|
});
|
|
18224
18419
|
},
|
|
18225
18420
|
onIssue: async (params) => {
|
|
18226
|
-
const { issueRpc } = await import('./rpc-
|
|
18421
|
+
const { issueRpc } = await import('./rpc-BvXiNu-z.mjs');
|
|
18227
18422
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
18228
18423
|
},
|
|
18229
18424
|
onWorkflow: async (params) => {
|
|
18230
|
-
const { workflowRpc } = await import('./rpc-
|
|
18425
|
+
const { workflowRpc } = await import('./rpc-BKDKqllr.mjs');
|
|
18231
18426
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
18232
18427
|
},
|
|
18233
18428
|
onRipgrep: async (args, cwd) => {
|
|
18234
|
-
const {
|
|
18429
|
+
const { execFile } = await import('child_process');
|
|
18235
18430
|
const rgCwd = cwd || directory;
|
|
18431
|
+
const argv = Array.isArray(args) ? args : String(args).split(/\s+/).filter(Boolean);
|
|
18236
18432
|
return new Promise((resolve2, reject) => {
|
|
18237
|
-
|
|
18433
|
+
execFile("rg", argv, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
|
|
18238
18434
|
if (err && !stdout) {
|
|
18239
18435
|
reject(new Error(err.message));
|
|
18240
18436
|
} else {
|
|
@@ -18838,18 +19034,19 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
18838
19034
|
});
|
|
18839
19035
|
},
|
|
18840
19036
|
onIssue: async (params) => {
|
|
18841
|
-
const { issueRpc } = await import('./rpc-
|
|
19037
|
+
const { issueRpc } = await import('./rpc-BvXiNu-z.mjs');
|
|
18842
19038
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
18843
19039
|
},
|
|
18844
19040
|
onWorkflow: async (params) => {
|
|
18845
|
-
const { workflowRpc } = await import('./rpc-
|
|
19041
|
+
const { workflowRpc } = await import('./rpc-BKDKqllr.mjs');
|
|
18846
19042
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
18847
19043
|
},
|
|
18848
19044
|
onRipgrep: async (args, cwd) => {
|
|
18849
|
-
const {
|
|
19045
|
+
const { execFile } = await import('child_process');
|
|
18850
19046
|
const rgCwd = cwd || directory;
|
|
19047
|
+
const argv = Array.isArray(args) ? args : String(args).split(/\s+/).filter(Boolean);
|
|
18851
19048
|
return new Promise((resolve2, reject) => {
|
|
18852
|
-
|
|
19049
|
+
execFile("rg", argv, { cwd: rgCwd, timeout: 3e4, maxBuffer: 5 * 1024 * 1024 }, (err, stdout) => {
|
|
18853
19050
|
if (err && !stdout) {
|
|
18854
19051
|
reject(new Error(err.message));
|
|
18855
19052
|
} else {
|
|
@@ -19343,7 +19540,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
19343
19540
|
"event"
|
|
19344
19541
|
);
|
|
19345
19542
|
sessionService.sendSessionEnd();
|
|
19346
|
-
|
|
19543
|
+
if (!acpPersisted) {
|
|
19544
|
+
deleteSession(sessionId);
|
|
19545
|
+
} else {
|
|
19546
|
+
logger.log(`[Agent Session ${sessionId}] start failed on restore \u2014 preserving persisted session for a later retry (not deleting)`);
|
|
19547
|
+
}
|
|
19347
19548
|
});
|
|
19348
19549
|
return {
|
|
19349
19550
|
type: "success",
|
|
@@ -19485,13 +19686,16 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
19485
19686
|
logger.log(`Restored machine metadata (sharing=${!!persistedMachineMeta.sharing}, securityContextConfig=${!!persistedMachineMeta.securityContextConfig}, injectPlatformGuidance=${persistedMachineMeta.injectPlatformGuidance})`);
|
|
19486
19687
|
}
|
|
19487
19688
|
let seededSharing = persistedMachineMeta?.sharing;
|
|
19689
|
+
let seedShareNotify = null;
|
|
19488
19690
|
const seedEmails = parseDaemonShareEmails(process.env[DAEMON_SHARE_EMAILS_ENV]);
|
|
19489
19691
|
if (seedEmails.length > 0) {
|
|
19490
19692
|
const ownerEmail = parseJwtEmail(process.env.HYPHA_TOKEN || "") || void 0;
|
|
19693
|
+
const preSeedSharing = seededSharing;
|
|
19491
19694
|
const result = applyDaemonShareSeed(seededSharing, seedEmails, ownerEmail);
|
|
19492
19695
|
seededSharing = result.sharing;
|
|
19493
19696
|
if (result.addedEmails.length > 0) {
|
|
19494
19697
|
logger.log(`Seeded machine sharing with ${result.addedEmails.length} user(s): ${result.addedEmails.join(", ")}`);
|
|
19698
|
+
if (seededSharing) seedShareNotify = { oldSharing: preSeedSharing, newSharing: seededSharing, added: result.addedEmails };
|
|
19495
19699
|
} else {
|
|
19496
19700
|
logger.log(`Daemon --share seed: no new users (all already in allowedUsers).`);
|
|
19497
19701
|
}
|
|
@@ -19528,7 +19732,25 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
19528
19732
|
startedAt: Date.now()
|
|
19529
19733
|
};
|
|
19530
19734
|
const sharingNotificationSync = new SharingNotificationSync(server, logger.log);
|
|
19531
|
-
|
|
19735
|
+
const seedNotifyWorkspace = server.config.workspace;
|
|
19736
|
+
const seedNotifyClientId = server.config.client_id;
|
|
19737
|
+
sharingNotificationSync.init().then(() => {
|
|
19738
|
+
if (seedShareNotify) {
|
|
19739
|
+
sharingNotificationSync.syncSharing(
|
|
19740
|
+
`machine-${machineId}`,
|
|
19741
|
+
seedShareNotify.oldSharing,
|
|
19742
|
+
seedShareNotify.newSharing,
|
|
19743
|
+
{
|
|
19744
|
+
machineId,
|
|
19745
|
+
machineServiceId: `${seedNotifyWorkspace}/${seedNotifyClientId}:default`,
|
|
19746
|
+
ownerWorkspace: seedNotifyWorkspace,
|
|
19747
|
+
ownerEmail: seedShareNotify.newSharing.owner || "",
|
|
19748
|
+
label: process.env.SVAMP_DISPLAY_NAME || machineId,
|
|
19749
|
+
shareType: "machine"
|
|
19750
|
+
}
|
|
19751
|
+
).catch((e) => logger.log(`[share-seed] notify failed: ${e?.message || e}`));
|
|
19752
|
+
}
|
|
19753
|
+
}).catch(() => {
|
|
19532
19754
|
});
|
|
19533
19755
|
const buildMachineHandlers = () => ({
|
|
19534
19756
|
spawnSession,
|
|
@@ -19734,7 +19956,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
19734
19956
|
}
|
|
19735
19957
|
if (persistedSessions.length > 0) {
|
|
19736
19958
|
try {
|
|
19737
|
-
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-
|
|
19959
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-BaMR97BE.mjs');
|
|
19738
19960
|
await awaitClaudeVersionReady();
|
|
19739
19961
|
} catch {
|
|
19740
19962
|
}
|
|
@@ -19931,7 +20153,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
19931
20153
|
const PING_TIMEOUT_MS = 15e3;
|
|
19932
20154
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
19933
20155
|
const RECONNECT_JITTER_MS = 2500;
|
|
19934
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
20156
|
+
const { WorkflowScheduler } = await import('./scheduler-D-25wsin.mjs');
|
|
19935
20157
|
const workflowScheduler = new WorkflowScheduler({
|
|
19936
20158
|
projectRoots: () => {
|
|
19937
20159
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -20554,4 +20776,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
20554
20776
|
writeStopMarker: writeStopMarker
|
|
20555
20777
|
});
|
|
20556
20778
|
|
|
20557
|
-
export {
|
|
20779
|
+
export { clearStopMarker as $, removeWorkflow as A, saveWorkflow as B, rawWorkflow as C, listWorkflows as D, isWorkflowEnabled as E, workflowCrons as F, cronMatches as G, summarize as H, workflowSteps as I, parseJwtEmail as J, loadMachineContext as K, buildMachineInstructions as L, machineToolsForRole as M, buildMachineTools as N, parseFrontmatter as O, getSkillsServer as P, getSkillsWorkspaceName as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, getSkillsCollectionName as T, fetchWithTimeout as U, searchSkills as V, SKILLS_DIR as W, getSkillInfo as X, downloadSkillFile as Y, listSkillFiles as Z, resolveModel as _, createSessionStore as a, stopMarkerExists 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, buildMachineShareUrl as aa, parseHandle as ab, handleMatchesMetadata as ac, describeMisconfiguration as ad, buildMachineDeps as ae, applyClaudeProxyEnv as af, composeSessionId as ag, generateFriendlyName as ah, generateHookSettings as ai, staticFileServer as aj, instanceConfig as ak, claudeAuth as al, codexProvider as am, projectInfo as an, DefaultTransport$1 as ao, acpBackend as ap, acpAgentConfig as aq, codexAppServerBackend as ar, GeminiTransport$1 as as, api as at, run as au, stopDaemon as b, connectToHypha as c, daemonStatus as d, getFrpsSubdomainHost as e, getFrpsServerPort as f, getHyphaServerUrl$1 as g, getFrpsServerAddr as h, shortId as i, resolveProjectRoot as j, getIssue as k, resumeIssue as l, addComment as m, addIssue as n, listIssues as o, pauseIssue as p, searchIssues as q, registerMachineService as r, startDaemon as s, isVisibleTo as t, updateIssue as u, getRun as v, listRuns as w, getWorkflow as x, runWorkflow as y, setWorkflowEnabled as z };
|