svamp-cli 0.2.331 → 0.2.333
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-DQ6TQAXa.mjs → adminCommands-BMzwjOct.mjs} +1 -1
- package/dist/{agentCommands-CovUtOYv.mjs → agentCommands-BVz1k2R2.mjs} +35 -6
- package/dist/{auth-CMWkQmps.mjs → auth-D0v5RwMJ.mjs} +1 -1
- package/dist/{cli-Cd_6Xp2x.mjs → cli-DmjtuskT.mjs} +93 -77
- package/dist/cli.mjs +2 -2
- package/dist/{commands-B-PLeiIB.mjs → commands-BOGd7CmP.mjs} +14 -4
- package/dist/{commands-CpXKePYP.mjs → commands-C3r_GhOb.mjs} +3 -3
- package/dist/{commands-D2he7eNq.mjs → commands-CPTWYXJD.mjs} +3 -3
- package/dist/{commands-DCTCLAPA.mjs → commands-CYgkxHsf.mjs} +3 -3
- package/dist/{commands-OwmAlnGb.mjs → commands-CwoMu1mu.mjs} +1 -1
- package/dist/{commands-CWkWkrZF.mjs → commands-DKouDroK.mjs} +1 -1
- package/dist/{commands-BCWHfbuw.mjs → commands-DcQMh7oz.mjs} +29 -18
- package/dist/{commands-Bv52WaQ0.mjs → commands-DmPwUOqX.mjs} +11 -11
- package/dist/{fleet-DrLOwe6r.mjs → fleet-5foIrEnM.mjs} +3 -3
- package/dist/{headlessCli-CDjGYUpj.mjs → headlessCli-BLZSVVlt.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notifyCommands-DSRd8YaE.mjs → notifyCommands-DADOx-j_.mjs} +1 -1
- package/dist/package-By6uMuic.mjs +64 -0
- package/dist/{pinnedClaudeCode-BO0Qs_kE.mjs → pinnedClaudeCode-C43KnZ-Q.mjs} +27 -6
- package/dist/{rpc-OmhzsDGP.mjs → rpc-CoPRGZkJ.mjs} +2 -1
- package/dist/{rpc-CVBhR9Cj.mjs → rpc-UomHF39a.mjs} +13 -1
- package/dist/{run-DcwSfoyP.mjs → run-CkEjIO1m.mjs} +1 -1
- package/dist/{run-CSNL62FQ.mjs → run-PXdghhjy.mjs} +592 -147
- package/dist/{scheduler-DDw3YLzH.mjs → scheduler-gMWmsAgp.mjs} +1 -1
- package/dist/{serveCommands-BQ0jhFUR.mjs → serveCommands-Cklx2iuZ.mjs} +10 -10
- package/dist/{sideband-S3ycJa01.mjs → sideband-DXyAkIaB.mjs} +1 -1
- package/package.json +4 -4
- package/dist/package-C23qFmVz.mjs +0 -64
|
@@ -7,8 +7,8 @@ import path__default, { join, delimiter, resolve as resolve$1, dirname as dirnam
|
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { execFile, execSync, spawn, exec as exec$1 } from 'child_process';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
|
-
import { randomUUID, createHash } from 'crypto';
|
|
11
|
-
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';
|
|
10
|
+
import { randomUUID, createHash, randomBytes as randomBytes$1 } from 'crypto';
|
|
11
|
+
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, fstatSync, writeSync } from 'node:fs';
|
|
12
12
|
import { join as join$1, extname, resolve, sep, basename, dirname } from 'node:path';
|
|
13
13
|
import { randomUUID as randomUUID$1, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
|
|
14
14
|
import { exec, spawn as spawn$1, execSync as execSync$1, execFile as execFile$1, execFileSync } from 'node:child_process';
|
|
@@ -3738,7 +3738,7 @@ class ServeManager {
|
|
|
3738
3738
|
/**
|
|
3739
3739
|
* Get the public URL for a mount (mount-specific subdomain).
|
|
3740
3740
|
* For `access: 'link'` mounts, the subdomain itself carries the capability —
|
|
3741
|
-
* the tunnel registers a long random suffix (~
|
|
3741
|
+
* the tunnel registers a long random suffix (~88 bits of entropy (randomBytes(11), 22 hex chars)), so the
|
|
3742
3742
|
* URL is identical in shape to other tiers, just longer. Content serves
|
|
3743
3743
|
* from the root, so HTML with absolute paths (`/style.css`, `/app.js`)
|
|
3744
3744
|
* works unchanged.
|
|
@@ -5446,10 +5446,11 @@ function cronMatches(expr, date) {
|
|
|
5446
5446
|
if (c.domRestricted && c.dowRestricted) return domOk || dowOk;
|
|
5447
5447
|
return domOk && dowOk;
|
|
5448
5448
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5449
|
+
const _zoneFormatters = /* @__PURE__ */ new Map();
|
|
5450
|
+
function zoneFormatter(tz) {
|
|
5451
|
+
let f = _zoneFormatters.get(tz);
|
|
5452
|
+
if (!f) {
|
|
5453
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
5453
5454
|
timeZone: tz,
|
|
5454
5455
|
hour12: false,
|
|
5455
5456
|
year: "numeric",
|
|
@@ -5457,7 +5458,15 @@ function inZone(date, tz) {
|
|
|
5457
5458
|
day: "2-digit",
|
|
5458
5459
|
hour: "2-digit",
|
|
5459
5460
|
minute: "2-digit"
|
|
5460
|
-
})
|
|
5461
|
+
});
|
|
5462
|
+
_zoneFormatters.set(tz, f);
|
|
5463
|
+
}
|
|
5464
|
+
return f;
|
|
5465
|
+
}
|
|
5466
|
+
function inZone(date, tz) {
|
|
5467
|
+
if (!tz) return date;
|
|
5468
|
+
try {
|
|
5469
|
+
const p = zoneFormatter(tz).formatToParts(date).reduce((o, x) => {
|
|
5461
5470
|
o[x.type] = x.value;
|
|
5462
5471
|
return o;
|
|
5463
5472
|
}, {});
|
|
@@ -5467,6 +5476,34 @@ function inZone(date, tz) {
|
|
|
5467
5476
|
return date;
|
|
5468
5477
|
}
|
|
5469
5478
|
}
|
|
5479
|
+
function nextFire(expr, from, tz) {
|
|
5480
|
+
const c = parseCron(expr);
|
|
5481
|
+
if (tz && scanForMatch(c, from, void 0) === null) return null;
|
|
5482
|
+
return scanForMatch(c, from, tz);
|
|
5483
|
+
}
|
|
5484
|
+
function scanForMatch(c, from, tz) {
|
|
5485
|
+
const d = new Date(from.getTime());
|
|
5486
|
+
d.setSeconds(0, 0);
|
|
5487
|
+
d.setMinutes(d.getMinutes() + 1);
|
|
5488
|
+
for (let i = 0; i < 366 * 24 * 60; i++) {
|
|
5489
|
+
if (cronMatches(c, tz ? inZone(d, tz) : d)) return new Date(d.getTime());
|
|
5490
|
+
d.setMinutes(d.getMinutes() + 1);
|
|
5491
|
+
}
|
|
5492
|
+
return null;
|
|
5493
|
+
}
|
|
5494
|
+
function describeNextFire(expr, tz, from = /* @__PURE__ */ new Date()) {
|
|
5495
|
+
let next;
|
|
5496
|
+
try {
|
|
5497
|
+
next = nextFire(expr, from, tz);
|
|
5498
|
+
} catch {
|
|
5499
|
+
return null;
|
|
5500
|
+
}
|
|
5501
|
+
if (!next) return "never fires";
|
|
5502
|
+
const shown = tz ? inZone(next, tz) : next;
|
|
5503
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
5504
|
+
const stamp = `${shown.getFullYear()}-${pad(shown.getMonth() + 1)}-${pad(shown.getDate())} ${pad(shown.getHours())}:${pad(shown.getMinutes())}`;
|
|
5505
|
+
return `next ${stamp} ${tz || "local"}`;
|
|
5506
|
+
}
|
|
5470
5507
|
function resolvePath(ctx, path) {
|
|
5471
5508
|
return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], ctx);
|
|
5472
5509
|
}
|
|
@@ -7823,9 +7860,12 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7823
7860
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
7824
7861
|
const client = tunnels.get(params.name);
|
|
7825
7862
|
if (!client) throw new Error(`Tunnel '${params.name}' not found`);
|
|
7863
|
+
const owner = handlers.listExposedTunnels?.().find((t) => t.name === params.name)?.sessionId;
|
|
7826
7864
|
client.destroy();
|
|
7827
7865
|
tunnels.delete(params.name);
|
|
7828
7866
|
handlers.forgetExposedTunnel?.(params.name);
|
|
7867
|
+
if (owner) handlers.dropTunnelSessionLink?.(owner, params.name);
|
|
7868
|
+
handlers.scheduleServiceLinkReconcile?.();
|
|
7829
7869
|
return { name: params.name, stopped: true };
|
|
7830
7870
|
},
|
|
7831
7871
|
/**
|
|
@@ -7868,6 +7908,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7868
7908
|
sessionId: params.sessionId,
|
|
7869
7909
|
addedAt: Date.now()
|
|
7870
7910
|
});
|
|
7911
|
+
if (params.sessionId) handlers.scheduleServiceLinkReconcile?.();
|
|
7871
7912
|
const urls = {};
|
|
7872
7913
|
for (const [port, url] of tunnel.getUrls()) urls[port] = url;
|
|
7873
7914
|
return { ...tunnel.status, urls };
|
|
@@ -7879,9 +7920,12 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7879
7920
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
7880
7921
|
const client = tunnels.get(params.name);
|
|
7881
7922
|
if (!client) throw new Error(`Tunnel '${params.name}' not found`);
|
|
7923
|
+
const owner = handlers.listExposedTunnels?.().find((t) => t.name === params.name)?.sessionId;
|
|
7882
7924
|
client.destroy();
|
|
7883
7925
|
tunnels.delete(params.name);
|
|
7884
7926
|
handlers.forgetExposedTunnel?.(params.name);
|
|
7927
|
+
if (owner) handlers.dropTunnelSessionLink?.(owner, params.name);
|
|
7928
|
+
handlers.scheduleServiceLinkReconcile?.();
|
|
7885
7929
|
return { name: params.name, stopped: true };
|
|
7886
7930
|
},
|
|
7887
7931
|
/**
|
|
@@ -7942,7 +7986,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7942
7986
|
assertMayReplaceMount(sm, params.name, context);
|
|
7943
7987
|
const ownerEmail2 = resolveMountOwnerEmailFor(params.ownerEmail, context);
|
|
7944
7988
|
const access = params.access || "owner";
|
|
7945
|
-
|
|
7989
|
+
const result = sm.addMount(params.name, params.directory, params.sessionId, access, ownerEmail2);
|
|
7990
|
+
if (params.sessionId) handlers.scheduleServiceLinkReconcile?.();
|
|
7991
|
+
return result;
|
|
7946
7992
|
},
|
|
7947
7993
|
/**
|
|
7948
7994
|
* Apply a mount declaratively. Replaces existing mount with same name.
|
|
@@ -8332,7 +8378,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8332
8378
|
}
|
|
8333
8379
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
8334
8380
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
8335
|
-
const { toolsForRole } = await import('./sideband-
|
|
8381
|
+
const { toolsForRole } = await import('./sideband-DXyAkIaB.mjs');
|
|
8336
8382
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
8337
8383
|
return fmt(r2);
|
|
8338
8384
|
}
|
|
@@ -8421,6 +8467,33 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8421
8467
|
};
|
|
8422
8468
|
const _caps = statelessDispatchCaps();
|
|
8423
8469
|
const statelessLimiter = new StatelessDispatchLimiter(_caps.concurrent, _caps.perSender);
|
|
8470
|
+
const MAX_RECEIVE_POLLS_PER_CHANNEL = Math.max(
|
|
8471
|
+
1,
|
|
8472
|
+
Number(process.env.SVAMP_MAX_CHANNEL_RECEIVE_POLLS) || 16
|
|
8473
|
+
);
|
|
8474
|
+
const _receivePolls = /* @__PURE__ */ new Map();
|
|
8475
|
+
const acquireReceiveSlot = (channelId) => {
|
|
8476
|
+
const n = _receivePolls.get(channelId) || 0;
|
|
8477
|
+
if (n >= MAX_RECEIVE_POLLS_PER_CHANNEL) return false;
|
|
8478
|
+
_receivePolls.set(channelId, n + 1);
|
|
8479
|
+
return true;
|
|
8480
|
+
};
|
|
8481
|
+
const releaseReceiveSlot = (channelId) => {
|
|
8482
|
+
const n = (_receivePolls.get(channelId) || 1) - 1;
|
|
8483
|
+
if (n <= 0) _receivePolls.delete(channelId);
|
|
8484
|
+
else _receivePolls.set(channelId, n);
|
|
8485
|
+
};
|
|
8486
|
+
const _outboxCache = /* @__PURE__ */ new Map();
|
|
8487
|
+
const getOutbox = (dir) => {
|
|
8488
|
+
let ob = _outboxCache.get(dir);
|
|
8489
|
+
if (!ob) {
|
|
8490
|
+
ob = new ChannelOutbox(dir);
|
|
8491
|
+
_outboxCache.set(dir, ob);
|
|
8492
|
+
} else {
|
|
8493
|
+
ob.reload();
|
|
8494
|
+
}
|
|
8495
|
+
return ob;
|
|
8496
|
+
};
|
|
8424
8497
|
const dispatchStateless = async (c, dir, kwargs, context) => {
|
|
8425
8498
|
const u = context?.user;
|
|
8426
8499
|
const r = resolveSender(c, {
|
|
@@ -8437,7 +8510,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8437
8510
|
return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
|
|
8438
8511
|
}
|
|
8439
8512
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
8440
|
-
const { queryCore } = await import('./commands-
|
|
8513
|
+
const { queryCore } = await import('./commands-DKouDroK.mjs');
|
|
8441
8514
|
const timeout = c.reply?.timeout_sec || 120;
|
|
8442
8515
|
let result;
|
|
8443
8516
|
let thrownSessionId;
|
|
@@ -8539,15 +8612,22 @@ ${d?.error || "not found"}`;
|
|
|
8539
8612
|
const waitRaw = Number(kwargs.wait ?? 25);
|
|
8540
8613
|
const waitSec = Number.isFinite(waitRaw) ? waitRaw : 25;
|
|
8541
8614
|
const waitMs = Math.min(Math.max(0, waitSec * 1e3), 6e4);
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8615
|
+
if (!acquireReceiveSlot(c.id)) {
|
|
8616
|
+
return { error: "busy: too many concurrent receive polls on this channel \u2014 retry shortly", retryable: true };
|
|
8617
|
+
}
|
|
8618
|
+
try {
|
|
8619
|
+
const outbox = getOutbox(dir);
|
|
8620
|
+
const deadline = Date.now() + waitMs;
|
|
8621
|
+
for (; ; ) {
|
|
8622
|
+
const replies = outbox.since(c.id, cursor, r.sender.name, kwargs.correlationId);
|
|
8623
|
+
if (replies.length || Date.now() >= deadline) {
|
|
8624
|
+
return { ok: true, replies, cursor: outbox.cursor(c.id) };
|
|
8625
|
+
}
|
|
8626
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
8627
|
+
outbox.reload();
|
|
8548
8628
|
}
|
|
8549
|
-
|
|
8550
|
-
|
|
8629
|
+
} finally {
|
|
8630
|
+
releaseReceiveSlot(c.id);
|
|
8551
8631
|
}
|
|
8552
8632
|
},
|
|
8553
8633
|
// Safety-restricted file upload (#0093). Deny-by-default: only channels whose
|
|
@@ -9244,10 +9324,15 @@ const OUTPOST_EXEC_TIMEOUT_MS = 12 * 6e4;
|
|
|
9244
9324
|
const OUTPOST_MAX_OUTPUT_BYTES = 1 << 20;
|
|
9245
9325
|
function capOutpostOutput(s) {
|
|
9246
9326
|
if (!s) return "";
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9327
|
+
const bytes = Buffer.byteLength(s, "utf-8");
|
|
9328
|
+
if (bytes <= OUTPOST_MAX_OUTPUT_BYTES) return s;
|
|
9329
|
+
const buf = Buffer.from(s, "utf-8");
|
|
9330
|
+
let start = buf.length - OUTPOST_MAX_OUTPUT_BYTES;
|
|
9331
|
+
while (start < buf.length && (buf[start] & 192) === 128) start++;
|
|
9332
|
+
const tail = buf.subarray(start).toString("utf-8");
|
|
9333
|
+
const dropped = bytes - Buffer.byteLength(tail, "utf-8");
|
|
9334
|
+
return `[svamp-daemon] output truncated: ${dropped} earlier bytes dropped
|
|
9335
|
+
${tail}`;
|
|
9251
9336
|
}
|
|
9252
9337
|
class OutpostCoordinator {
|
|
9253
9338
|
conns = /* @__PURE__ */ new Map();
|
|
@@ -9365,9 +9450,14 @@ class OutpostCoordinator {
|
|
|
9365
9450
|
resolveTarget(target) {
|
|
9366
9451
|
const live = this.listConnections();
|
|
9367
9452
|
if (target) {
|
|
9368
|
-
const
|
|
9369
|
-
if (
|
|
9370
|
-
|
|
9453
|
+
const byId = live.find((c) => c.machine === target);
|
|
9454
|
+
if (byId) return { machine: byId.machine };
|
|
9455
|
+
const byLabel = live.filter((c) => c.label === target);
|
|
9456
|
+
if (byLabel.length === 1) return { machine: byLabel[0].machine };
|
|
9457
|
+
if (byLabel.length > 1) {
|
|
9458
|
+
return { error: `outpost label "${target}" is ambiguous \u2014 ${byLabel.length} connected outposts share it (${byLabel.map((c) => c.machine).join(", ")}); pass the machine id instead` };
|
|
9459
|
+
}
|
|
9460
|
+
return { error: `no connected outpost named "${target}" (connected: ${live.map((c) => c.machine).join(", ") || "none"})` };
|
|
9371
9461
|
}
|
|
9372
9462
|
if (live.length === 0) return { error: "no remote outpost is connected \u2014 run the install one-liner on the remote machine first" };
|
|
9373
9463
|
if (live.length > 1) return { error: `multiple outposts connected (${live.map((c) => c.machine).join(", ")}) \u2014 pass a target` };
|
|
@@ -9981,7 +10071,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
9981
10071
|
ensureParticipantsChannel(initialMetadata.sharing);
|
|
9982
10072
|
const forkClaudeOnce = async (prompt, claudeSessionId, cwd, maxTurns, timeoutMs) => {
|
|
9983
10073
|
const { spawn } = await import('child_process');
|
|
9984
|
-
const forkEnv = { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
10074
|
+
const forkEnv = { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1", DISABLE_AUTOUPDATER: "1" };
|
|
9985
10075
|
applyClaudeProxyEnv(forkEnv);
|
|
9986
10076
|
return new Promise((resolve) => {
|
|
9987
10077
|
const child = spawn("claude", [
|
|
@@ -11575,6 +11665,53 @@ function buildSessionIdentityEnv(identity) {
|
|
|
11575
11665
|
return env;
|
|
11576
11666
|
}
|
|
11577
11667
|
|
|
11668
|
+
async function downloadSkillAtomically(targetDir, files, fetchFile) {
|
|
11669
|
+
const stagingDir = `${targetDir}.tmp-${process.pid}-${Date.now().toString(36)}`;
|
|
11670
|
+
try {
|
|
11671
|
+
mkdirSync$1(stagingDir, { recursive: true });
|
|
11672
|
+
for (const filePath of files) {
|
|
11673
|
+
if (!filePath) continue;
|
|
11674
|
+
const content = await fetchFile(filePath);
|
|
11675
|
+
const localPath = join$1(stagingDir, filePath);
|
|
11676
|
+
if (!localPath.startsWith(stagingDir + "/")) continue;
|
|
11677
|
+
mkdirSync$1(dirname(localPath), { recursive: true });
|
|
11678
|
+
writeFileSync$1(localPath, content, "utf-8");
|
|
11679
|
+
}
|
|
11680
|
+
} catch (err) {
|
|
11681
|
+
rmSync$1(stagingDir, { recursive: true, force: true });
|
|
11682
|
+
throw err;
|
|
11683
|
+
}
|
|
11684
|
+
const backupDir = `${targetDir}.old-${process.pid}-${Date.now().toString(36)}`;
|
|
11685
|
+
let backedUp = false;
|
|
11686
|
+
try {
|
|
11687
|
+
if (existsSync(targetDir)) {
|
|
11688
|
+
renameSync(targetDir, backupDir);
|
|
11689
|
+
backedUp = true;
|
|
11690
|
+
}
|
|
11691
|
+
renameSync(stagingDir, targetDir);
|
|
11692
|
+
} catch (err) {
|
|
11693
|
+
if (backedUp && !existsSync(targetDir)) {
|
|
11694
|
+
try {
|
|
11695
|
+
renameSync(backupDir, targetDir);
|
|
11696
|
+
backedUp = false;
|
|
11697
|
+
} catch {
|
|
11698
|
+
}
|
|
11699
|
+
}
|
|
11700
|
+
try {
|
|
11701
|
+
rmSync$1(stagingDir, { recursive: true, force: true });
|
|
11702
|
+
} catch {
|
|
11703
|
+
}
|
|
11704
|
+
if (backedUp) {
|
|
11705
|
+
try {
|
|
11706
|
+
rmSync$1(backupDir, { recursive: true, force: true });
|
|
11707
|
+
} catch {
|
|
11708
|
+
}
|
|
11709
|
+
}
|
|
11710
|
+
throw err;
|
|
11711
|
+
}
|
|
11712
|
+
if (backedUp) rmSync$1(backupDir, { recursive: true, force: true });
|
|
11713
|
+
}
|
|
11714
|
+
|
|
11578
11715
|
const HOTRELOAD_ENV = "SVAMP_DEV_HOTRELOAD";
|
|
11579
11716
|
function isHotReloadEnabled(env = process.env) {
|
|
11580
11717
|
const v = env[HOTRELOAD_ENV];
|
|
@@ -14185,7 +14322,7 @@ function codexCumulativeTokens(payload) {
|
|
|
14185
14322
|
return nonCachedInput + output + Math.round(cached * 0.1);
|
|
14186
14323
|
}
|
|
14187
14324
|
|
|
14188
|
-
const PINNED_CODEX_VERSION = "0.
|
|
14325
|
+
const PINNED_CODEX_VERSION = "0.149.0";
|
|
14189
14326
|
const MIN_CODEX_STEER_VERSION = { major: 0, minor: 129, patch: 0 };
|
|
14190
14327
|
function cmpVer(a, b) {
|
|
14191
14328
|
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
@@ -15216,6 +15353,15 @@ function kimiProviderConfigured(configTomlText, provider) {
|
|
|
15216
15353
|
const block = nextTable < 0 ? rest : rest.slice(0, nextTable);
|
|
15217
15354
|
return /(^|\n)\s*api_key\s*=/.test(block);
|
|
15218
15355
|
}
|
|
15356
|
+
function kimiHasGlobalDefaultModel(configTomlText) {
|
|
15357
|
+
if (!configTomlText) return false;
|
|
15358
|
+
const firstTable = configTomlText.search(/(^|\n)\s*\[/);
|
|
15359
|
+
const preamble = firstTable < 0 ? configTomlText : configTomlText.slice(0, firstTable);
|
|
15360
|
+
return /(^|\n)\s*default_model\s*=/.test(preamble);
|
|
15361
|
+
}
|
|
15362
|
+
function kimiConfigComplete(configTomlText, provider) {
|
|
15363
|
+
return kimiProviderConfigured(configTomlText, provider) && kimiHasGlobalDefaultModel(configTomlText);
|
|
15364
|
+
}
|
|
15219
15365
|
function kimiModelMenu(env = process.env) {
|
|
15220
15366
|
return (env.SVAMP_KIMI_MODELS || "").split(",").map((s) => s.trim()).filter(Boolean).filter((m, i, a) => a.indexOf(m) === i);
|
|
15221
15367
|
}
|
|
@@ -15237,8 +15383,10 @@ var kimiProvider = /*#__PURE__*/Object.freeze({
|
|
|
15237
15383
|
buildKimiCatalogAddArgs: buildKimiCatalogAddArgs,
|
|
15238
15384
|
hasKimiProvider: hasKimiProvider,
|
|
15239
15385
|
kimiClampModel: kimiClampModel,
|
|
15386
|
+
kimiConfigComplete: kimiConfigComplete,
|
|
15240
15387
|
kimiDefaultModel: kimiDefaultModel,
|
|
15241
15388
|
kimiDefaultModelId: kimiDefaultModelId,
|
|
15389
|
+
kimiHasGlobalDefaultModel: kimiHasGlobalDefaultModel,
|
|
15242
15390
|
kimiModelMenu: kimiModelMenu,
|
|
15243
15391
|
kimiProviderConfigured: kimiProviderConfigured,
|
|
15244
15392
|
kimiSpawnEnv: kimiSpawnEnv,
|
|
@@ -15257,7 +15405,7 @@ async function ensureKimiProviderConfigured(env = process.env, command = "kimi",
|
|
|
15257
15405
|
}) {
|
|
15258
15406
|
const cfg = resolveKimiProvider(env);
|
|
15259
15407
|
if (!hasKimiProvider(cfg)) return false;
|
|
15260
|
-
if (
|
|
15408
|
+
if (kimiConfigComplete(readKimiConfig(), cfg.provider)) return true;
|
|
15261
15409
|
const args = buildKimiCatalogAddArgs(cfg);
|
|
15262
15410
|
if (!args) return false;
|
|
15263
15411
|
try {
|
|
@@ -15275,7 +15423,11 @@ async function ensureKimiProviderConfigured(env = process.env, command = "kimi",
|
|
|
15275
15423
|
);
|
|
15276
15424
|
});
|
|
15277
15425
|
log(`[kimi] configured provider '${cfg.provider}' in ${KIMI_CONFIG_PATH}`);
|
|
15278
|
-
|
|
15426
|
+
const complete = kimiConfigComplete(readKimiConfig(), cfg.provider);
|
|
15427
|
+
if (!complete) {
|
|
15428
|
+
log(`[kimi] catalog add left ${KIMI_CONFIG_PATH} without a global default_model \u2014 \`kimi acp\` will refuse session/new ("no default model configured")`);
|
|
15429
|
+
}
|
|
15430
|
+
return complete;
|
|
15279
15431
|
} catch (err) {
|
|
15280
15432
|
log(`[kimi] provider auto-config failed (${err?.message || err}); session may need kimi-auth`);
|
|
15281
15433
|
return false;
|
|
@@ -15783,8 +15935,28 @@ const SENSITIVE_ENV_VARS = [
|
|
|
15783
15935
|
"GH_TOKEN",
|
|
15784
15936
|
"NPM_TOKEN",
|
|
15785
15937
|
"DOCKER_PASSWORD",
|
|
15786
|
-
"DOCKER_CONFIG"
|
|
15938
|
+
"DOCKER_CONFIG",
|
|
15939
|
+
// #1110: svamp's own persisted credentials (written by `svamp daemon codex-auth` /
|
|
15940
|
+
// `kimi-auth` / `wise-agent auth`, and by the generic LLM_* aliases).
|
|
15941
|
+
"OPENAI_API_KEY",
|
|
15942
|
+
"GEMINI_API_KEY",
|
|
15943
|
+
"XAI_API_KEY",
|
|
15944
|
+
"ANTHROPIC_ADMIN_KEY",
|
|
15945
|
+
"LLM_API_KEY",
|
|
15946
|
+
"CLOUDFLARE_API_TOKEN",
|
|
15947
|
+
"R2_ACCESS_KEY_ID",
|
|
15948
|
+
"R2_SECRET_ACCESS_KEY",
|
|
15949
|
+
"X_API_KEY"
|
|
15787
15950
|
];
|
|
15951
|
+
const CREDENTIAL_NAME_RE = /(^|_)(KEY|APIKEY|TOKEN|SECRET|PASSWORD|CREDENTIALS)$/;
|
|
15952
|
+
const CREDENTIAL_SWEEP_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
15953
|
+
"ANTHROPIC_API_KEY",
|
|
15954
|
+
"ANTHROPIC_AUTH_TOKEN"
|
|
15955
|
+
]);
|
|
15956
|
+
function isCredentialEnvName(key) {
|
|
15957
|
+
if (CREDENTIAL_SWEEP_ALLOWLIST.has(key)) return false;
|
|
15958
|
+
return CREDENTIAL_NAME_RE.test(key);
|
|
15959
|
+
}
|
|
15788
15960
|
async function stageCredentialsForSharing(sessionId) {
|
|
15789
15961
|
const realHome = homedir$1();
|
|
15790
15962
|
const realClaudeDir = join$1(realHome, ".claude");
|
|
@@ -15858,6 +16030,9 @@ function sanitizeEnvForSharing(env) {
|
|
|
15858
16030
|
for (const key of SENSITIVE_ENV_VARS) {
|
|
15859
16031
|
delete sanitized[key];
|
|
15860
16032
|
}
|
|
16033
|
+
for (const key of Object.keys(sanitized)) {
|
|
16034
|
+
if (isCredentialEnvName(key)) delete sanitized[key];
|
|
16035
|
+
}
|
|
15861
16036
|
return sanitized;
|
|
15862
16037
|
}
|
|
15863
16038
|
async function sweepOrphanedStagedHomes(activeSessionIds) {
|
|
@@ -15900,6 +16075,7 @@ async function copyDirRecursive(src, dest) {
|
|
|
15900
16075
|
|
|
15901
16076
|
var credentialStaging = /*#__PURE__*/Object.freeze({
|
|
15902
16077
|
__proto__: null,
|
|
16078
|
+
isCredentialEnvName: isCredentialEnvName,
|
|
15903
16079
|
sanitizeEnvForSharing: sanitizeEnvForSharing,
|
|
15904
16080
|
stageCredentialsForSharing: stageCredentialsForSharing,
|
|
15905
16081
|
sweepOrphanedStagedHomes: sweepOrphanedStagedHomes
|
|
@@ -16239,14 +16415,21 @@ class ProcessSupervisor {
|
|
|
16239
16415
|
if (entry.stopping) throw new Error(`Process '${entry.spec.name}' is being stopped, try again shortly`);
|
|
16240
16416
|
throw new Error(`Process '${entry.spec.name}' is already running`);
|
|
16241
16417
|
}
|
|
16242
|
-
entry
|
|
16243
|
-
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16418
|
+
return this.runExclusive(entry, async () => {
|
|
16419
|
+
if (entry.deleted) throw new Error(`Process '${idOrName}' has been removed`);
|
|
16420
|
+
if (entry.child) {
|
|
16421
|
+
if (entry.stopping) throw new Error(`Process '${entry.spec.name}' is being stopped, try again shortly`);
|
|
16422
|
+
throw new Error(`Process '${entry.spec.name}' is already running`);
|
|
16423
|
+
}
|
|
16424
|
+
entry.stopping = false;
|
|
16425
|
+
this.clearTimers(entry);
|
|
16426
|
+
if (entry.spec.desiredStopped) {
|
|
16427
|
+
entry.spec.desiredStopped = false;
|
|
16428
|
+
await this.persistSpec(entry.spec);
|
|
16429
|
+
}
|
|
16430
|
+
this.resetFailureTracking(entry);
|
|
16431
|
+
await this.startEntry(entry, false);
|
|
16432
|
+
});
|
|
16250
16433
|
}
|
|
16251
16434
|
/**
|
|
16252
16435
|
* Stop a running process. Does NOT remove it from supervision.
|
|
@@ -16256,7 +16439,7 @@ class ProcessSupervisor {
|
|
|
16256
16439
|
*/
|
|
16257
16440
|
/**
|
|
16258
16441
|
* #0347: run `fn` in the entry's lifecycle critical section. Each call awaits the previous
|
|
16259
|
-
* lifecycle op's SETTLEMENT before running, so stop/restart/apply/update/remove are strictly
|
|
16442
|
+
* lifecycle op's SETTLEMENT before running, so start/stop/restart/apply/update/remove are strictly
|
|
16260
16443
|
* serialized per entry and can never interleave across an `await killChild` (which is what let
|
|
16261
16444
|
* two ops double-spawn an orphan, or resurrect a just-stopped process). The chain tail never
|
|
16262
16445
|
* rejects (a failed op must not wedge the queue); the caller still gets the real result/error.
|
|
@@ -16473,7 +16656,7 @@ class ProcessSupervisor {
|
|
|
16473
16656
|
}
|
|
16474
16657
|
async persistSpec(spec) {
|
|
16475
16658
|
const filePath = path__default.join(this.persistDir, `${spec.id}.json`);
|
|
16476
|
-
const tmpPath = filePath
|
|
16659
|
+
const tmpPath = `${filePath}.tmp-${process.pid}-${randomBytes$1(4).toString("hex")}`;
|
|
16477
16660
|
await writeFile$1(tmpPath, JSON.stringify(spec, null, 2), "utf-8");
|
|
16478
16661
|
await rename(tmpPath, filePath);
|
|
16479
16662
|
}
|
|
@@ -17052,7 +17235,7 @@ function searchIssues(projectRoot, query) {
|
|
|
17052
17235
|
const q = query.trim();
|
|
17053
17236
|
if (!q) return [];
|
|
17054
17237
|
try {
|
|
17055
|
-
const out = execFileSync("rg", ["-l", "-i", "--no-messages", q, issuesDir(projectRoot)], { encoding: "utf-8" });
|
|
17238
|
+
const out = execFileSync("rg", ["-l", "-i", "-F", "--no-messages", "-e", q, "--", issuesDir(projectRoot)], { encoding: "utf-8" });
|
|
17056
17239
|
const files = out.split("\n").filter(Boolean);
|
|
17057
17240
|
const issues = [];
|
|
17058
17241
|
for (const f of files) {
|
|
@@ -17063,9 +17246,24 @@ function searchIssues(projectRoot, query) {
|
|
|
17063
17246
|
}
|
|
17064
17247
|
}
|
|
17065
17248
|
return issues.sort((a, b) => Number(a.id) - Number(b.id));
|
|
17066
|
-
} catch {
|
|
17067
|
-
|
|
17068
|
-
return
|
|
17249
|
+
} catch (e) {
|
|
17250
|
+
if (e?.status === 1) return [];
|
|
17251
|
+
return searchIssuesInMemory(projectRoot, q);
|
|
17252
|
+
}
|
|
17253
|
+
}
|
|
17254
|
+
function searchIssuesInMemory(projectRoot, q) {
|
|
17255
|
+
const ql = q.toLowerCase();
|
|
17256
|
+
return listIssues(projectRoot, { includeArchived: true }).filter((i) => i.title.toLowerCase().includes(ql) || (i.body || "").toLowerCase().includes(ql) || i.labels.some((l) => l.toLowerCase().includes(ql)));
|
|
17257
|
+
}
|
|
17258
|
+
function checkVerifyCommand(projectRoot, issue) {
|
|
17259
|
+
const cmd = issue?.verify?.type === "command" ? (issue.verify.text || "").trim() : "";
|
|
17260
|
+
if (!cmd) return { ok: true };
|
|
17261
|
+
try {
|
|
17262
|
+
execSync$1(cmd, { cwd: projectRoot, stdio: "pipe", timeout: 6e5, maxBuffer: 64 * 1024 * 1024 });
|
|
17263
|
+
return { ok: true, cmd };
|
|
17264
|
+
} catch (e) {
|
|
17265
|
+
const output = (String(e?.stdout || "") + String(e?.stderr || "")).split("\n").slice(-15).join("\n").trim();
|
|
17266
|
+
return { ok: false, cmd, output };
|
|
17069
17267
|
}
|
|
17070
17268
|
}
|
|
17071
17269
|
function summarize(issues) {
|
|
@@ -17092,7 +17290,8 @@ function planLogPrune(files, policy = DEFAULT_LOG_PRUNE_POLICY, nowMs = Date.now
|
|
|
17092
17290
|
const overCount = keepFiles > 0 ? perStart.slice(keepFiles) : perStart;
|
|
17093
17291
|
const tooOld = maxAgeMs > 0 ? perStart.filter((f) => nowMs - f.mtimeMs > maxAgeMs) : [];
|
|
17094
17292
|
const deleteFiles = [...new Set([...overCount, ...tooOld].map((f) => f.name))];
|
|
17095
|
-
const
|
|
17293
|
+
const doomed = new Set(deleteFiles);
|
|
17294
|
+
const truncateFiles = files.filter((f) => (APPEND_ONLY_LOGS.includes(f.name) || isPerStartLog(f.name)) && !doomed.has(f.name)).filter((f) => policy.maxBytes > 0 && f.size > policy.maxBytes).map((f) => ({ name: f.name, size: f.size }));
|
|
17096
17295
|
return { deleteFiles, truncateFiles };
|
|
17097
17296
|
}
|
|
17098
17297
|
function resolveLogPrunePolicy(env = process.env) {
|
|
@@ -17107,11 +17306,63 @@ function resolveLogPrunePolicy(env = process.env) {
|
|
|
17107
17306
|
maxBytes: num(env.SVAMP_LOG_MAX_BYTES, DEFAULT_LOG_PRUNE_POLICY.maxBytes)
|
|
17108
17307
|
};
|
|
17109
17308
|
}
|
|
17110
|
-
function
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17309
|
+
function trimToLineStart(buf) {
|
|
17310
|
+
const nl = buf.indexOf(10);
|
|
17311
|
+
return nl >= 0 && nl + 1 < buf.length ? buf.subarray(nl + 1) : buf;
|
|
17312
|
+
}
|
|
17313
|
+
|
|
17314
|
+
function readLogTail(path, tailBytes) {
|
|
17315
|
+
const fd = openSync(path, "r");
|
|
17316
|
+
try {
|
|
17317
|
+
const size = fstatSync(fd).size;
|
|
17318
|
+
const want = Math.min(Math.max(0, Math.floor(tailBytes)), size);
|
|
17319
|
+
if (want === 0) return { data: Buffer.alloc(0), truncated: size > 0 };
|
|
17320
|
+
const buf = Buffer.allocUnsafe(want);
|
|
17321
|
+
const start = size - want;
|
|
17322
|
+
let read = 0;
|
|
17323
|
+
while (read < want) {
|
|
17324
|
+
const n = readSync(fd, buf, read, want - read, start + read);
|
|
17325
|
+
if (n <= 0) break;
|
|
17326
|
+
read += n;
|
|
17327
|
+
}
|
|
17328
|
+
return { data: read === want ? buf : buf.subarray(0, read), truncated: size > want };
|
|
17329
|
+
} finally {
|
|
17330
|
+
closeSync(fd);
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
|
|
17334
|
+
const CARRIER_FIELDS = ["message", "error", "detail", "reason", "description"];
|
|
17335
|
+
function formatLogArg(a) {
|
|
17336
|
+
if (typeof a === "string") return a;
|
|
17337
|
+
if (a instanceof Error) return a.stack || `${a.name}: ${a.message}`;
|
|
17338
|
+
if (a === void 0) return "undefined";
|
|
17339
|
+
if (a === null) return "null";
|
|
17340
|
+
try {
|
|
17341
|
+
const s = JSON.stringify(a);
|
|
17342
|
+
if (s === void 0 || s === "{}") return String(a);
|
|
17343
|
+
return s;
|
|
17344
|
+
} catch {
|
|
17345
|
+
try {
|
|
17346
|
+
return String(a);
|
|
17347
|
+
} catch {
|
|
17348
|
+
return "[unserializable]";
|
|
17349
|
+
}
|
|
17350
|
+
}
|
|
17351
|
+
}
|
|
17352
|
+
function formatLogArgs(args) {
|
|
17353
|
+
return args.map(formatLogArg).join(" ");
|
|
17354
|
+
}
|
|
17355
|
+
function describeRejectionReason(reason) {
|
|
17356
|
+
if (typeof reason === "string") return reason;
|
|
17357
|
+
if (reason instanceof Error) return `${reason.name}: ${reason.message}`;
|
|
17358
|
+
if (reason && typeof reason === "object") {
|
|
17359
|
+
for (const f of CARRIER_FIELDS) {
|
|
17360
|
+
const v = reason[f];
|
|
17361
|
+
if (typeof v === "string" && v.trim() !== "") return v;
|
|
17362
|
+
if (v instanceof Error) return `${v.name}: ${v.message}`;
|
|
17363
|
+
}
|
|
17364
|
+
}
|
|
17365
|
+
return formatLogArg(reason);
|
|
17115
17366
|
}
|
|
17116
17367
|
|
|
17117
17368
|
function isWorkflowEnabled(wf) {
|
|
@@ -17135,6 +17386,12 @@ function validateWorkflowName(name) {
|
|
|
17135
17386
|
function workflowPath(projectRoot, name) {
|
|
17136
17387
|
return join$1(workflowsDir(projectRoot), `${validateWorkflowName(name)}.yaml`);
|
|
17137
17388
|
}
|
|
17389
|
+
function resolveWorkflowPath(projectRoot, name) {
|
|
17390
|
+
const canonical = workflowPath(projectRoot, name);
|
|
17391
|
+
if (existsSync(canonical)) return canonical;
|
|
17392
|
+
const alt = join$1(workflowsDir(projectRoot), `${validateWorkflowName(name)}.yml`);
|
|
17393
|
+
return existsSync(alt) ? alt : canonical;
|
|
17394
|
+
}
|
|
17138
17395
|
function normalizeOn(on) {
|
|
17139
17396
|
if (!on || typeof on !== "object") {
|
|
17140
17397
|
return void 0;
|
|
@@ -17231,7 +17488,7 @@ function listWorkflows(projectRoot) {
|
|
|
17231
17488
|
function getWorkflow(projectRoot, name) {
|
|
17232
17489
|
let p;
|
|
17233
17490
|
try {
|
|
17234
|
-
p =
|
|
17491
|
+
p = resolveWorkflowPath(projectRoot, name);
|
|
17235
17492
|
} catch {
|
|
17236
17493
|
return null;
|
|
17237
17494
|
}
|
|
@@ -17245,7 +17502,7 @@ function getWorkflow(projectRoot, name) {
|
|
|
17245
17502
|
function rawWorkflow(projectRoot, name) {
|
|
17246
17503
|
let p;
|
|
17247
17504
|
try {
|
|
17248
|
-
p =
|
|
17505
|
+
p = resolveWorkflowPath(projectRoot, name);
|
|
17249
17506
|
} catch {
|
|
17250
17507
|
return null;
|
|
17251
17508
|
}
|
|
@@ -17258,6 +17515,13 @@ function saveWorkflow(projectRoot, wf) {
|
|
|
17258
17515
|
const tmp = `${path}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
17259
17516
|
writeFileSync$1(tmp, serializeWorkflow(wf));
|
|
17260
17517
|
renameSync(tmp, path);
|
|
17518
|
+
const alt = join$1(dir, `${validateWorkflowName(wf.name)}.yml`);
|
|
17519
|
+
if (alt !== path && existsSync(alt)) {
|
|
17520
|
+
try {
|
|
17521
|
+
unlinkSync$1(alt);
|
|
17522
|
+
} catch {
|
|
17523
|
+
}
|
|
17524
|
+
}
|
|
17261
17525
|
}
|
|
17262
17526
|
function setWorkflowEnabled(projectRoot, name, enabled) {
|
|
17263
17527
|
const wf = getWorkflow(projectRoot, name);
|
|
@@ -17269,7 +17533,7 @@ function setWorkflowEnabled(projectRoot, name, enabled) {
|
|
|
17269
17533
|
return next;
|
|
17270
17534
|
}
|
|
17271
17535
|
function removeWorkflow(projectRoot, name) {
|
|
17272
|
-
const p =
|
|
17536
|
+
const p = resolveWorkflowPath(projectRoot, name);
|
|
17273
17537
|
if (!existsSync(p)) return false;
|
|
17274
17538
|
try {
|
|
17275
17539
|
unlinkSync$1(p);
|
|
@@ -17783,6 +18047,15 @@ function isCostCeiling(kind) {
|
|
|
17783
18047
|
function isHardBudgetStop(kind) {
|
|
17784
18048
|
return isCostCeiling(kind) || kind === "iteration";
|
|
17785
18049
|
}
|
|
18050
|
+
function rearmStartedAt(startedAt, completedAt, now) {
|
|
18051
|
+
const fin = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
18052
|
+
if (!fin(startedAt)) return startedAt;
|
|
18053
|
+
if (!fin(completedAt)) return startedAt;
|
|
18054
|
+
if (completedAt < startedAt) return startedAt;
|
|
18055
|
+
const dormantMs = now - completedAt;
|
|
18056
|
+
if (dormantMs <= 0) return startedAt;
|
|
18057
|
+
return startedAt + dormantMs;
|
|
18058
|
+
}
|
|
17786
18059
|
function checkLoopBudget(ledger, limits, now, startedAt) {
|
|
17787
18060
|
const L = limits || {};
|
|
17788
18061
|
const led = ledger || { turns: 0, tokens: 0, window: [] };
|
|
@@ -18262,6 +18535,47 @@ function isContextTooLongError(text) {
|
|
|
18262
18535
|
if (t.includes("context") && t.includes("exceed") && t.includes("limit") && !t.includes("subscription")) return true;
|
|
18263
18536
|
return false;
|
|
18264
18537
|
}
|
|
18538
|
+
function shouldEvaluateProactiveCompaction(state) {
|
|
18539
|
+
if (!state.enabled) return false;
|
|
18540
|
+
if (state.disabled) return false;
|
|
18541
|
+
if (state.turnErrored) return false;
|
|
18542
|
+
if (state.compactPending) return false;
|
|
18543
|
+
if (state.justCompacted) return false;
|
|
18544
|
+
return true;
|
|
18545
|
+
}
|
|
18546
|
+
function compactSupportedFromSlashCommands(slashCommands) {
|
|
18547
|
+
if (!Array.isArray(slashCommands) || slashCommands.length === 0) return void 0;
|
|
18548
|
+
return slashCommands.some(
|
|
18549
|
+
(c) => typeof c === "string" && c.trim().replace(/^\//, "").toLowerCase() === "compact"
|
|
18550
|
+
);
|
|
18551
|
+
}
|
|
18552
|
+
const DEFAULT_COMPACT_NO_BOUNDARY_STRIKES = 3;
|
|
18553
|
+
function decideCompactUnsupported(input) {
|
|
18554
|
+
const prior = Math.max(0, input.priorStrikes || 0);
|
|
18555
|
+
const limit = Math.max(1, input.maxStrikes);
|
|
18556
|
+
if (input.turnErrored) {
|
|
18557
|
+
return { disable: false, warnUnsupported: false, strikes: prior, reason: "errored-turn" };
|
|
18558
|
+
}
|
|
18559
|
+
if (!input.wasAutoInjected) {
|
|
18560
|
+
return {
|
|
18561
|
+
disable: false,
|
|
18562
|
+
warnUnsupported: input.compactSupported === false,
|
|
18563
|
+
strikes: prior,
|
|
18564
|
+
reason: "user-typed"
|
|
18565
|
+
};
|
|
18566
|
+
}
|
|
18567
|
+
if (input.compactSupported === false) {
|
|
18568
|
+
return { disable: true, warnUnsupported: true, strikes: prior + 1, reason: "unsupported" };
|
|
18569
|
+
}
|
|
18570
|
+
const strikes = prior + 1;
|
|
18571
|
+
if (strikes >= limit) {
|
|
18572
|
+
return { disable: true, warnUnsupported: false, strikes, reason: "strikes-exhausted" };
|
|
18573
|
+
}
|
|
18574
|
+
return { disable: false, warnUnsupported: false, strikes, reason: "strike" };
|
|
18575
|
+
}
|
|
18576
|
+
function describeNoBoundary(strikes, limit) {
|
|
18577
|
+
return `Automatic compaction ran but reported no compaction boundary (attempt ${strikes} of ${limit}) \u2014 usually because there was nothing left to compact. Keeping automatic compaction enabled.`;
|
|
18578
|
+
}
|
|
18265
18579
|
|
|
18266
18580
|
function fmtTokens(n) {
|
|
18267
18581
|
return Math.max(0, Math.round(n)).toLocaleString("en-US");
|
|
@@ -18835,23 +19149,27 @@ function compareVersions(a, b) {
|
|
|
18835
19149
|
return 0;
|
|
18836
19150
|
}
|
|
18837
19151
|
async function installSkillFromEndpoint(name, baseUrl) {
|
|
18838
|
-
const
|
|
18839
|
-
if (!resp.ok) throw new Error(`HTTP ${resp.status} from ${baseUrl}`);
|
|
18840
|
-
const index = await resp.json();
|
|
19152
|
+
const index = await fetchEndpointSkillIndex(baseUrl);
|
|
18841
19153
|
const files = index.files || [];
|
|
18842
19154
|
if (files.length === 0) throw new Error(`Skill index at ${baseUrl} has no files`);
|
|
18843
|
-
|
|
18844
|
-
|
|
18845
|
-
for (const filePath of files) {
|
|
18846
|
-
if (!filePath) continue;
|
|
18847
|
-
const url = `${baseUrl}${filePath}`;
|
|
18848
|
-
const fileResp = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
19155
|
+
await downloadSkillAtomically(join(CLAUDE_SKILLS_DIR, name), files, async (filePath) => {
|
|
19156
|
+
const fileResp = await fetch(`${baseUrl}${filePath}`, { signal: AbortSignal.timeout(3e4) });
|
|
18849
19157
|
if (!fileResp.ok) throw new Error(`Failed to download ${filePath}: HTTP ${fileResp.status}`);
|
|
18850
|
-
|
|
18851
|
-
|
|
18852
|
-
|
|
18853
|
-
|
|
18854
|
-
|
|
19158
|
+
return fileResp.text();
|
|
19159
|
+
});
|
|
19160
|
+
}
|
|
19161
|
+
async function fetchEndpointSkillIndex(baseUrl) {
|
|
19162
|
+
const resp = await fetch(baseUrl, { signal: AbortSignal.timeout(15e3) });
|
|
19163
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status} from ${baseUrl}`);
|
|
19164
|
+
return await resp.json();
|
|
19165
|
+
}
|
|
19166
|
+
async function fetchEndpointSkillVersion(baseUrl) {
|
|
19167
|
+
try {
|
|
19168
|
+
const index = await fetchEndpointSkillIndex(baseUrl);
|
|
19169
|
+
const v = index?.version;
|
|
19170
|
+
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
19171
|
+
} catch {
|
|
19172
|
+
return null;
|
|
18855
19173
|
}
|
|
18856
19174
|
}
|
|
18857
19175
|
async function installSkillFromMarketplace(name) {
|
|
@@ -18875,26 +19193,11 @@ async function installSkillFromMarketplace(name) {
|
|
|
18875
19193
|
}
|
|
18876
19194
|
const files = await collectFiles();
|
|
18877
19195
|
if (files.length === 0) throw new Error(`Skill ${name} has no files in marketplace`);
|
|
18878
|
-
|
|
18879
|
-
|
|
18880
|
-
|
|
18881
|
-
|
|
18882
|
-
|
|
18883
|
-
const url = `${BASE}/files/${filePath}`;
|
|
18884
|
-
const resp = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
18885
|
-
if (!resp.ok) throw new Error(`Failed to download ${filePath}: HTTP ${resp.status}`);
|
|
18886
|
-
const content = await resp.text();
|
|
18887
|
-
const localPath = join(stagingDir, filePath);
|
|
18888
|
-
if (!localPath.startsWith(stagingDir + "/")) continue;
|
|
18889
|
-
mkdirSync(dirname$1(localPath), { recursive: true });
|
|
18890
|
-
writeFileSync(localPath, content, "utf-8");
|
|
18891
|
-
}
|
|
18892
|
-
rmSync(targetDir, { recursive: true, force: true });
|
|
18893
|
-
renameSync$1(stagingDir, targetDir);
|
|
18894
|
-
} catch (err) {
|
|
18895
|
-
rmSync(stagingDir, { recursive: true, force: true });
|
|
18896
|
-
throw err;
|
|
18897
|
-
}
|
|
19196
|
+
await downloadSkillAtomically(join(CLAUDE_SKILLS_DIR, name), files, async (filePath) => {
|
|
19197
|
+
const resp = await fetch(`${BASE}/files/${filePath}`, { signal: AbortSignal.timeout(3e4) });
|
|
19198
|
+
if (!resp.ok) throw new Error(`Failed to download ${filePath}: HTTP ${resp.status}`);
|
|
19199
|
+
return resp.text();
|
|
19200
|
+
});
|
|
18898
19201
|
}
|
|
18899
19202
|
function getBundledSkillsDir() {
|
|
18900
19203
|
try {
|
|
@@ -19113,9 +19416,16 @@ async function ensureAutoInstalledSkills(logger) {
|
|
|
19113
19416
|
marketplaceVersion: () => fetchMarketplaceSkillVersion("svamp")
|
|
19114
19417
|
},
|
|
19115
19418
|
{
|
|
19419
|
+
// #1192: the agent-skills endpoint's index already carries a `version` (verified live:
|
|
19420
|
+
// `{"name":"hypha","version":"1.0.0","files":[...]}`), and installSkillFromEndpoint
|
|
19421
|
+
// fetches that exact document anyway — it just used to discard the field. Supplying the
|
|
19422
|
+
// probe moves `hypha` off the "install once, never refresh" branch, so a machine is no
|
|
19423
|
+
// longer pinned forever to whatever version it first installed. Safe now that #1191
|
|
19424
|
+
// gave this downloader atomic staging: a refresh that fails mid-download leaves the
|
|
19425
|
+
// previous skill intact instead of half-overwriting it.
|
|
19116
19426
|
name: "hypha",
|
|
19117
|
-
install: () => installSkillFromEndpoint("hypha", `${getSkillsServer()}/ws/agent-skills/`)
|
|
19118
|
-
|
|
19427
|
+
install: () => installSkillFromEndpoint("hypha", `${getSkillsServer()}/ws/agent-skills/`),
|
|
19428
|
+
marketplaceVersion: () => fetchEndpointSkillVersion(`${getSkillsServer()}/ws/agent-skills/`)
|
|
19119
19429
|
},
|
|
19120
19430
|
{
|
|
19121
19431
|
// `<artifact>` block rendering — see packages/svamp-app/.../Artifact.tsx.
|
|
@@ -19328,17 +19638,23 @@ function isLoopActive(directory, sessionId) {
|
|
|
19328
19638
|
const s = readLoopState(directory, sessionId);
|
|
19329
19639
|
return !!s && s.active !== false && s.phase !== "dormant" && !isTerminalLoopPhase(s.phase);
|
|
19330
19640
|
}
|
|
19331
|
-
function loopOwnerSession(directory, sessionId) {
|
|
19332
|
-
const s = readLoopState(directory, sessionId);
|
|
19333
|
-
if (!s || s.active === false || s.phase === "dormant" || isTerminalLoopPhase(s.phase)) return null;
|
|
19334
|
-
return typeof s.session_id === "string" ? s.session_id : null;
|
|
19335
|
-
}
|
|
19336
19641
|
function isLoopActiveForSession(directory, sessionId) {
|
|
19337
19642
|
const s = readLoopState(directory, sessionId);
|
|
19338
19643
|
if (!s || s.active === false || s.phase === "dormant" || isTerminalLoopPhase(s.phase)) return false;
|
|
19339
19644
|
if (typeof s.session_id !== "string") return true;
|
|
19340
19645
|
return s.session_id === sessionId;
|
|
19341
19646
|
}
|
|
19647
|
+
function loopExistsForSession(directory, sessionId) {
|
|
19648
|
+
const s = readLoopState(directory, sessionId);
|
|
19649
|
+
if (!s || s.active === false || isTerminalLoopPhase(s.phase)) return false;
|
|
19650
|
+
if (typeof s.session_id !== "string") return true;
|
|
19651
|
+
return s.session_id === sessionId;
|
|
19652
|
+
}
|
|
19653
|
+
function loopOwnerSessionIncludingDormant(directory, sessionId) {
|
|
19654
|
+
const s = readLoopState(directory, sessionId);
|
|
19655
|
+
if (!s || s.active === false || isTerminalLoopPhase(s.phase)) return null;
|
|
19656
|
+
return typeof s.session_id === "string" ? s.session_id : null;
|
|
19657
|
+
}
|
|
19342
19658
|
function isLoopArmedForSession(directory, sessionId) {
|
|
19343
19659
|
const s = readLoopState(directory, sessionId);
|
|
19344
19660
|
if (!s || s.active === false || s.phase !== "dormant") return false;
|
|
@@ -19675,13 +19991,13 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
19675
19991
|
};
|
|
19676
19992
|
let announcedLoopStart = (() => {
|
|
19677
19993
|
const s = readLoopState(directory, sessionId);
|
|
19678
|
-
return s && (s.phase === "done" || s.phase === "gave_up") ? s.started_at ?? "\u2205" : null;
|
|
19994
|
+
return s && (s.phase === "done" || s.phase === "gave_up") ? String(s.started_at ?? "\u2205") : null;
|
|
19679
19995
|
})();
|
|
19680
19996
|
const loopChecker = () => {
|
|
19681
19997
|
try {
|
|
19682
19998
|
const s = readLoopState(directory, sessionId);
|
|
19683
19999
|
if (!s || s.phase !== "done" && s.phase !== "gave_up") return;
|
|
19684
|
-
const key = s.started_at ?? "\u2205";
|
|
20000
|
+
const key = String(s.started_at ?? "\u2205");
|
|
19685
20001
|
if (key === announcedLoopStart) return;
|
|
19686
20002
|
announcedLoopStart = key;
|
|
19687
20003
|
const iter = s.ledger && typeof s.ledger.turns === "number" && s.ledger.turns > 0 ? s.ledger.turns : typeof s.iteration === "number" ? s.iteration : 0;
|
|
@@ -20052,7 +20368,7 @@ function ensureHomeDir() {
|
|
|
20052
20368
|
mkdirSync(LOGS_DIR, { recursive: true });
|
|
20053
20369
|
}
|
|
20054
20370
|
}
|
|
20055
|
-
function pruneDaemonLogs() {
|
|
20371
|
+
function pruneDaemonLogs(exclude = []) {
|
|
20056
20372
|
try {
|
|
20057
20373
|
const policy = resolveLogPrunePolicy(process.env);
|
|
20058
20374
|
const entries = readdirSync$1(LOGS_DIR).map((name) => {
|
|
@@ -20063,7 +20379,7 @@ function pruneDaemonLogs() {
|
|
|
20063
20379
|
return null;
|
|
20064
20380
|
}
|
|
20065
20381
|
}).filter((e) => e !== null);
|
|
20066
|
-
const plan = planLogPrune(entries, policy);
|
|
20382
|
+
const plan = planLogPrune(entries, policy, Date.now(), exclude);
|
|
20067
20383
|
for (const name of plan.deleteFiles) {
|
|
20068
20384
|
try {
|
|
20069
20385
|
unlinkSync(join(LOGS_DIR, name));
|
|
@@ -20073,7 +20389,9 @@ function pruneDaemonLogs() {
|
|
|
20073
20389
|
for (const { name } of plan.truncateFiles) {
|
|
20074
20390
|
try {
|
|
20075
20391
|
const p = join(LOGS_DIR, name);
|
|
20076
|
-
const
|
|
20392
|
+
const keepBytes = Math.floor(policy.maxBytes / 8);
|
|
20393
|
+
const tail = readLogTail(p, keepBytes);
|
|
20394
|
+
const kept = tail.truncated ? trimToLineStart(tail.data) : tail.data;
|
|
20077
20395
|
writeFileSync(p, kept);
|
|
20078
20396
|
} catch {
|
|
20079
20397
|
}
|
|
@@ -20085,10 +20403,13 @@ function createLogger() {
|
|
|
20085
20403
|
ensureHomeDir();
|
|
20086
20404
|
pruneDaemonLogs();
|
|
20087
20405
|
const logFile = join(LOGS_DIR, `daemon-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.log`);
|
|
20406
|
+
const sweepMs = Math.max(6e4, Number(process.env.SVAMP_LOG_PRUNE_INTERVAL_MS) || 36e5);
|
|
20407
|
+
const sweepTimer = setInterval(() => pruneDaemonLogs([basename$1(logFile)]), sweepMs);
|
|
20408
|
+
if (typeof sweepTimer.unref === "function") sweepTimer.unref();
|
|
20088
20409
|
return {
|
|
20089
20410
|
logFilePath: logFile,
|
|
20090
20411
|
log: (...args) => {
|
|
20091
|
-
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${args
|
|
20412
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${formatLogArgs(args)}
|
|
20092
20413
|
`;
|
|
20093
20414
|
fs$1.appendFile(logFile, line).catch(() => {
|
|
20094
20415
|
});
|
|
@@ -20097,7 +20418,7 @@ function createLogger() {
|
|
|
20097
20418
|
}
|
|
20098
20419
|
},
|
|
20099
20420
|
error: (...args) => {
|
|
20100
|
-
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${args
|
|
20421
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${formatLogArgs(args)}
|
|
20101
20422
|
`;
|
|
20102
20423
|
fs$1.appendFile(logFile, line).catch(() => {
|
|
20103
20424
|
});
|
|
@@ -20254,7 +20575,7 @@ async function startDaemon(options) {
|
|
|
20254
20575
|
const UNHANDLED_REJECTION_WINDOW_MS = 6e4;
|
|
20255
20576
|
process.on("unhandledRejection", (reason) => {
|
|
20256
20577
|
if (shutdownRequested) return;
|
|
20257
|
-
const msg =
|
|
20578
|
+
const msg = describeRejectionReason(reason);
|
|
20258
20579
|
logger.error("Unhandled rejection:", reason);
|
|
20259
20580
|
const isTransient = TRANSIENT_REJECTION_PATTERNS.some((p) => msg.toLowerCase().includes(p.toLowerCase()));
|
|
20260
20581
|
const isSessionLoss = msg.toLowerCase().includes("session does not exist");
|
|
@@ -20383,7 +20704,7 @@ async function startDaemon(options) {
|
|
|
20383
20704
|
try {
|
|
20384
20705
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20385
20706
|
if (!dir) return;
|
|
20386
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20707
|
+
const { reconcileServiceLinks } = await import('./agentCommands-BVz1k2R2.mjs');
|
|
20387
20708
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20388
20709
|
const config = readSvampConfig(configPath);
|
|
20389
20710
|
const entries = Array.from(urls.entries());
|
|
@@ -20405,7 +20726,7 @@ async function startDaemon(options) {
|
|
|
20405
20726
|
try {
|
|
20406
20727
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20407
20728
|
if (!dir) return;
|
|
20408
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20729
|
+
const { reconcileServiceLinks } = await import('./agentCommands-BVz1k2R2.mjs');
|
|
20409
20730
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20410
20731
|
const config = readSvampConfig(configPath);
|
|
20411
20732
|
const incoming = [{
|
|
@@ -20426,7 +20747,7 @@ async function startDaemon(options) {
|
|
|
20426
20747
|
try {
|
|
20427
20748
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20428
20749
|
if (!dir) return;
|
|
20429
|
-
const { dropServiceLinks } = await import('./agentCommands-
|
|
20750
|
+
const { dropServiceLinks } = await import('./agentCommands-BVz1k2R2.mjs');
|
|
20430
20751
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20431
20752
|
const config = readSvampConfig(configPath);
|
|
20432
20753
|
if (dropServiceLinks(config, "serve", mountName)) {
|
|
@@ -20437,6 +20758,73 @@ async function startDaemon(options) {
|
|
|
20437
20758
|
logger.log(`[serve] Link drop failed for mount ${mountName}: ${err?.message || err}`);
|
|
20438
20759
|
}
|
|
20439
20760
|
}
|
|
20761
|
+
async function dropTunnelSessionLink(sessionId, tunnelName) {
|
|
20762
|
+
if (!sessionId) return;
|
|
20763
|
+
try {
|
|
20764
|
+
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20765
|
+
if (!dir) return;
|
|
20766
|
+
const { dropServiceLinks } = await import('./agentCommands-BVz1k2R2.mjs');
|
|
20767
|
+
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20768
|
+
const config = readSvampConfig(configPath);
|
|
20769
|
+
if (dropServiceLinks(config, "tunnel", tunnelName)) {
|
|
20770
|
+
writeSvampConfig(configPath, config);
|
|
20771
|
+
logger.log(`[exposed-tunnels] Dropped Launch Pad link for tunnel ${tunnelName} \u2190 session ${sessionId}`);
|
|
20772
|
+
}
|
|
20773
|
+
} catch (err) {
|
|
20774
|
+
logger.log(`[exposed-tunnels] Link drop failed for tunnel ${tunnelName}: ${err?.message || err}`);
|
|
20775
|
+
}
|
|
20776
|
+
}
|
|
20777
|
+
async function reconcileAllServiceLinks() {
|
|
20778
|
+
try {
|
|
20779
|
+
const { reconcileServiceLinksExact } = await import('./agentCommands-BVz1k2R2.mjs');
|
|
20780
|
+
const desiredBySession = /* @__PURE__ */ new Map();
|
|
20781
|
+
const push = (sessionId, entry) => {
|
|
20782
|
+
if (!sessionId || !entry.url) return;
|
|
20783
|
+
const list = desiredBySession.get(sessionId) ?? [];
|
|
20784
|
+
list.push(entry);
|
|
20785
|
+
desiredBySession.set(sessionId, list);
|
|
20786
|
+
};
|
|
20787
|
+
for (const m of serveManager$1.listMounts()) {
|
|
20788
|
+
if (m?.sessionId && m?.url) push(m.sessionId, { url: String(m.url), label: m.name, service: { kind: "serve", name: m.name } });
|
|
20789
|
+
}
|
|
20790
|
+
const specBySession = /* @__PURE__ */ new Map();
|
|
20791
|
+
for (const spec of loadExposedTunnels()) if (spec.sessionId) specBySession.set(spec.name, spec.sessionId);
|
|
20792
|
+
for (const [name, tunnel] of tunnels) {
|
|
20793
|
+
const sessionId = specBySession.get(name);
|
|
20794
|
+
if (!sessionId) continue;
|
|
20795
|
+
const urls = Array.from(tunnel.getUrls().entries());
|
|
20796
|
+
for (const [port, url] of urls) {
|
|
20797
|
+
push(sessionId, { url: String(url), label: urls.length > 1 ? `${name}:${port}` : name, service: { kind: "tunnel", name } });
|
|
20798
|
+
}
|
|
20799
|
+
}
|
|
20800
|
+
const sessionIds = /* @__PURE__ */ new Set([...Object.keys(loadSessionIndex()), ...desiredBySession.keys()]);
|
|
20801
|
+
for (const sessionId of sessionIds) {
|
|
20802
|
+
try {
|
|
20803
|
+
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20804
|
+
if (!dir) continue;
|
|
20805
|
+
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20806
|
+
const config = readSvampConfig(configPath);
|
|
20807
|
+
const desired = desiredBySession.get(sessionId) ?? [];
|
|
20808
|
+
if (reconcileServiceLinksExact(config, ["serve", "tunnel"], desired)) {
|
|
20809
|
+
writeSvampConfig(configPath, config);
|
|
20810
|
+
logger.log(`[launchpad] Reconciled service links for session ${sessionId} (${desired.length} live)`);
|
|
20811
|
+
}
|
|
20812
|
+
} catch (err) {
|
|
20813
|
+
logger.log(`[launchpad] Reconcile failed for session ${sessionId}: ${err?.message || err}`);
|
|
20814
|
+
}
|
|
20815
|
+
}
|
|
20816
|
+
} catch (err) {
|
|
20817
|
+
logger.log(`[launchpad] Service-link sweep failed: ${err?.message || err}`);
|
|
20818
|
+
}
|
|
20819
|
+
}
|
|
20820
|
+
let serviceLinkReconcileTimer;
|
|
20821
|
+
function scheduleServiceLinkReconcile(delayMs = 1200) {
|
|
20822
|
+
if (serviceLinkReconcileTimer) clearTimeout(serviceLinkReconcileTimer);
|
|
20823
|
+
serviceLinkReconcileTimer = setTimeout(() => {
|
|
20824
|
+
serviceLinkReconcileTimer = void 0;
|
|
20825
|
+
void reconcileAllServiceLinks();
|
|
20826
|
+
}, delayMs);
|
|
20827
|
+
}
|
|
20440
20828
|
async function createExposedTunnel(spec) {
|
|
20441
20829
|
const { FrpcTunnel } = await Promise.resolve().then(function () { return frpc; });
|
|
20442
20830
|
const tunnel = new FrpcTunnel({
|
|
@@ -20469,8 +20857,18 @@ async function startDaemon(options) {
|
|
|
20469
20857
|
ensureAutoInstalledCommands(logger);
|
|
20470
20858
|
(async () => {
|
|
20471
20859
|
try {
|
|
20472
|
-
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-
|
|
20473
|
-
|
|
20860
|
+
const { beginClaudeVersionReconcile, CLAUDE_VERSION_RECHECK_MS } = await import('./pinnedClaudeCode-C43KnZ-Q.mjs');
|
|
20861
|
+
const onResult = (result) => {
|
|
20862
|
+
if (result?.kind === "install-failed" || result?.kind === "skipped" || result?.kind === "not-installed") {
|
|
20863
|
+
logger.log(`[claude-version] WARNING \u2014 machine is NOT on the pinned Claude Code (${result.kind}). Sessions are running an untested build.`);
|
|
20864
|
+
}
|
|
20865
|
+
};
|
|
20866
|
+
beginClaudeVersionReconcile((msg) => logger.log(msg), { onResult });
|
|
20867
|
+
const jitter = Math.floor(Math.random() * 5 * 6e4);
|
|
20868
|
+
const timer = setInterval(() => {
|
|
20869
|
+
beginClaudeVersionReconcile((msg) => logger.log(msg), { onResult });
|
|
20870
|
+
}, CLAUDE_VERSION_RECHECK_MS + jitter);
|
|
20871
|
+
timer.unref?.();
|
|
20474
20872
|
} catch (e) {
|
|
20475
20873
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
20476
20874
|
}
|
|
@@ -20963,7 +21361,7 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
20963
21361
|
const cwd = sessionMetadata.path || directory;
|
|
20964
21362
|
const model = process.env.SVAMP_TOPIC_MODEL;
|
|
20965
21363
|
try {
|
|
20966
|
-
const forkEnv = { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
|
21364
|
+
const forkEnv = { ...process.env, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1", DISABLE_AUTOUPDATER: "1" };
|
|
20967
21365
|
applyClaudeProxyEnv(forkEnv);
|
|
20968
21366
|
const child = spawn("claude", [
|
|
20969
21367
|
"--print",
|
|
@@ -21402,6 +21800,9 @@ ${parts.join("\n")}`);
|
|
|
21402
21800
|
let autoCompactWasAutoInjected = false;
|
|
21403
21801
|
let autoCompactDisabled = false;
|
|
21404
21802
|
let autoCompactRecoveryTried = false;
|
|
21803
|
+
let justCompactedTurn = false;
|
|
21804
|
+
let compactNoBoundaryStrikes = 0;
|
|
21805
|
+
let compactSupported = void 0;
|
|
21405
21806
|
let noIsolationWarned = false;
|
|
21406
21807
|
const RATELIMIT_CFG = getRateLimitRetryConfig();
|
|
21407
21808
|
let currentTurnMessage;
|
|
@@ -21847,15 +22248,30 @@ ${parts.join("\n")}`);
|
|
|
21847
22248
|
pendingCompactTurn = false;
|
|
21848
22249
|
const wasAuto = autoCompactWasAutoInjected;
|
|
21849
22250
|
autoCompactWasAutoInjected = false;
|
|
21850
|
-
|
|
22251
|
+
const d = decideCompactUnsupported({
|
|
22252
|
+
wasAutoInjected: wasAuto,
|
|
22253
|
+
turnErrored: !!msg.is_error,
|
|
22254
|
+
compactSupported,
|
|
22255
|
+
priorStrikes: compactNoBoundaryStrikes,
|
|
22256
|
+
maxStrikes: DEFAULT_COMPACT_NO_BOUNDARY_STRIKES
|
|
22257
|
+
});
|
|
22258
|
+
compactNoBoundaryStrikes = d.strikes;
|
|
22259
|
+
if (d.disable) {
|
|
21851
22260
|
autoCompactDisabled = true;
|
|
21852
|
-
logger.log(`[Session ${sessionId}]
|
|
22261
|
+
logger.log(`[Session ${sessionId}] Disabling the auto-compaction backstop for this session (${d.reason})`);
|
|
22262
|
+
}
|
|
22263
|
+
logger.log(`[Session ${sessionId}] /compact produced no compaction boundary (reason=${d.reason}, strikes=${d.strikes}, supported=${String(compactSupported)}, errored=${!!msg.is_error})`);
|
|
22264
|
+
if (d.warnUnsupported) {
|
|
22265
|
+
sessionService.pushMessage(
|
|
22266
|
+
{ type: "message", message: compactUnsupportedWarning(), level: "warning" },
|
|
22267
|
+
"event"
|
|
22268
|
+
);
|
|
22269
|
+
} else if (wasAuto && d.reason !== "errored-turn") {
|
|
22270
|
+
sessionService.pushMessage(
|
|
22271
|
+
{ type: "message", message: describeNoBoundary(d.strikes, DEFAULT_COMPACT_NO_BOUNDARY_STRIKES), level: "warning" },
|
|
22272
|
+
"event"
|
|
22273
|
+
);
|
|
21853
22274
|
}
|
|
21854
|
-
logger.log(`[Session ${sessionId}] /compact produced no compaction boundary \u2014 Claude Code likely too old to handle it in stream-json mode`);
|
|
21855
|
-
sessionService.pushMessage(
|
|
21856
|
-
{ type: "message", message: compactUnsupportedWarning(), level: "warning" },
|
|
21857
|
-
"event"
|
|
21858
|
-
);
|
|
21859
22275
|
}
|
|
21860
22276
|
if (!msg.is_error) {
|
|
21861
22277
|
resetRateLimitStreak();
|
|
@@ -21946,7 +22362,15 @@ ${parts.join("\n")}`);
|
|
|
21946
22362
|
logger.log(`[Session ${sessionId}] ${taskInfo}`);
|
|
21947
22363
|
sessionService.pushMessage({ type: "session_event", message: taskInfo }, "session");
|
|
21948
22364
|
}
|
|
21949
|
-
|
|
22365
|
+
const mayEvaluateLayer1 = shouldEvaluateProactiveCompaction({
|
|
22366
|
+
justCompacted: justCompactedTurn,
|
|
22367
|
+
compactPending: pendingCompactTurn,
|
|
22368
|
+
turnErrored: !!msg.is_error,
|
|
22369
|
+
disabled: autoCompactDisabled,
|
|
22370
|
+
enabled: autoCompactEnabled
|
|
22371
|
+
});
|
|
22372
|
+
justCompactedTurn = false;
|
|
22373
|
+
if (mayEvaluateLayer1) {
|
|
21950
22374
|
try {
|
|
21951
22375
|
const observed = computeObservedContext(msg.usage);
|
|
21952
22376
|
const decision = decideProactiveCompaction({
|
|
@@ -22010,6 +22434,7 @@ ${parts.join("\n")}`);
|
|
|
22010
22434
|
userMessagePending = false;
|
|
22011
22435
|
if (msg.slash_commands && Array.isArray(msg.slash_commands)) {
|
|
22012
22436
|
sessionMetadata = { ...sessionMetadata, slashCommands: msg.slash_commands };
|
|
22437
|
+
compactSupported = compactSupportedFromSlashCommands(msg.slash_commands);
|
|
22013
22438
|
}
|
|
22014
22439
|
if (typeof msg.model === "string" && msg.model.length > 0) {
|
|
22015
22440
|
lastMainModel = msg.model;
|
|
@@ -22063,6 +22488,8 @@ ${parts.join("\n")}`);
|
|
|
22063
22488
|
pendingCompactTurn = false;
|
|
22064
22489
|
autoCompactWasAutoInjected = false;
|
|
22065
22490
|
autoCompactRecoveryTried = false;
|
|
22491
|
+
justCompactedTurn = true;
|
|
22492
|
+
compactNoBoundaryStrikes = 0;
|
|
22066
22493
|
const compactLine = describeCompaction(msg.compact_metadata);
|
|
22067
22494
|
logger.log(`[Session ${sessionId}] ${compactLine}`);
|
|
22068
22495
|
sessionService.pushMessage(
|
|
@@ -22879,11 +23306,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
22879
23306
|
});
|
|
22880
23307
|
},
|
|
22881
23308
|
onIssue: async (params) => {
|
|
22882
|
-
const { issueRpc } = await import('./rpc-
|
|
23309
|
+
const { issueRpc } = await import('./rpc-UomHF39a.mjs');
|
|
22883
23310
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
22884
23311
|
},
|
|
22885
23312
|
onWorkflow: async (params) => {
|
|
22886
|
-
const { workflowRpc } = await import('./rpc-
|
|
23313
|
+
const { workflowRpc } = await import('./rpc-CoPRGZkJ.mjs');
|
|
22887
23314
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
22888
23315
|
},
|
|
22889
23316
|
onRipgrep: async (args, cwd) => {
|
|
@@ -23103,13 +23530,28 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23103
23530
|
active: true,
|
|
23104
23531
|
phase: "continue",
|
|
23105
23532
|
iteration: 0,
|
|
23533
|
+
started_at: rearmStartedAt(s.started_at, s.completed_at, Date.now()),
|
|
23534
|
+
// #1170
|
|
23106
23535
|
completed_at: void 0,
|
|
23107
23536
|
gave_up_reason: void 0,
|
|
23108
23537
|
verify_errors: 0,
|
|
23109
23538
|
// fresh verification budget on re-arm
|
|
23110
23539
|
stall_hinted: false,
|
|
23111
|
-
resumed_at: Date.now()
|
|
23540
|
+
resumed_at: Date.now(),
|
|
23112
23541
|
// #0156: re-arm the stall hint
|
|
23542
|
+
// A re-arm starts a NEW work batch, so the previous batch's progress
|
|
23543
|
+
// baseline must not carry over. assessLoopProgress tracks a MONOTONIC
|
|
23544
|
+
// MINIMUM over the retained history, and a durable loop only parks
|
|
23545
|
+
// dormant once the oracle reads 0 pending — so the old tail is
|
|
23546
|
+
// `pending: 0` and best === 0 forever. Without this reset the first
|
|
23547
|
+
// new marker (pending >= 1) can never beat it, the loop reads STUCK
|
|
23548
|
+
// from checkpoint 3 onward while it is genuinely resolving issues,
|
|
23549
|
+
// `auto_resumes` never resets (updateLoopProgress keys that off the
|
|
23550
|
+
// same signal), and the finite #0958 cap burns down to a terminal
|
|
23551
|
+
// gave_up. That contradicts the invariant that a slow-but-PRODUCTIVE
|
|
23552
|
+
// loop is never killed by the cap.
|
|
23553
|
+
progress_history: [],
|
|
23554
|
+
auto_resumes: 0
|
|
23113
23555
|
});
|
|
23114
23556
|
} catch {
|
|
23115
23557
|
}
|
|
@@ -23591,11 +24033,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23591
24033
|
});
|
|
23592
24034
|
},
|
|
23593
24035
|
onIssue: async (params) => {
|
|
23594
|
-
const { issueRpc } = await import('./rpc-
|
|
24036
|
+
const { issueRpc } = await import('./rpc-UomHF39a.mjs');
|
|
23595
24037
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
23596
24038
|
},
|
|
23597
24039
|
onWorkflow: async (params) => {
|
|
23598
|
-
const { workflowRpc } = await import('./rpc-
|
|
24040
|
+
const { workflowRpc } = await import('./rpc-CoPRGZkJ.mjs');
|
|
23599
24041
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
23600
24042
|
},
|
|
23601
24043
|
onRipgrep: async (args, cwd) => {
|
|
@@ -23938,7 +24380,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23938
24380
|
};
|
|
23939
24381
|
try {
|
|
23940
24382
|
if (!ls.acp_budget_warned && acpCumTokens === 0 && (ledger?.turns || 0) >= 1 && ls.budget && (ls.budget.max_tokens || ls.budget.max_tokens_per_hour)) {
|
|
23941
|
-
sessionService.pushMessage({ type: "message", message: "\u26A0\uFE0F
|
|
24383
|
+
sessionService.pushMessage({ type: "message", message: "\u26A0\uFE0F The token/rate cost cap (--max-tokens-per-hour) is INERT for this agent \u2014 it exposes no token usage, so this loop is bounded only by --max (iterations) and --max-runtime-sec. Set one of those to cap cost.", level: "warning" }, "event");
|
|
23942
24384
|
ls.acp_budget_warned = true;
|
|
23943
24385
|
}
|
|
23944
24386
|
const acpMaxExt = resolveMaxExtensions(process.env.SVAMP_LOOP_MAX_EXTENSIONS);
|
|
@@ -24259,7 +24701,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24259
24701
|
const wasInMemory = teardownTrackedSession(sessionId);
|
|
24260
24702
|
idleTriggerTracker.forget(sessionId);
|
|
24261
24703
|
const markedArchived = markSessionAsArchived(sessionId);
|
|
24262
|
-
if (loopDir &&
|
|
24704
|
+
if (loopDir && loopExistsForSession(loopDir, sessionId)) {
|
|
24263
24705
|
deactivateLoop(loopDir, sessionId);
|
|
24264
24706
|
logger.log(`Deactivated loop for archived session ${sessionId}`);
|
|
24265
24707
|
}
|
|
@@ -24317,7 +24759,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24317
24759
|
teardownTrackedSession(sessionId);
|
|
24318
24760
|
idleTriggerTracker.forget(sessionId);
|
|
24319
24761
|
deletePersistedSession(sessionId);
|
|
24320
|
-
if (loopDir &&
|
|
24762
|
+
if (loopDir && loopExistsForSession(loopDir, sessionId)) {
|
|
24321
24763
|
deactivateLoop(loopDir, sessionId);
|
|
24322
24764
|
}
|
|
24323
24765
|
logger.log(`Session ${sessionId} deleted`);
|
|
@@ -24460,7 +24902,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24460
24902
|
sharingNotificationSync,
|
|
24461
24903
|
persistExposedTunnel,
|
|
24462
24904
|
forgetExposedTunnel,
|
|
24463
|
-
listExposedTunnels: () => loadExposedTunnels().map((t) => ({ name: t.name, ports: t.ports, group: t.group, sessionId: t.sessionId, addedAt: t.addedAt }))
|
|
24905
|
+
listExposedTunnels: () => loadExposedTunnels().map((t) => ({ name: t.name, ports: t.ports, group: t.group, sessionId: t.sessionId, addedAt: t.addedAt })),
|
|
24906
|
+
dropTunnelSessionLink: (sessionId, tunnelName) => {
|
|
24907
|
+
void dropTunnelSessionLink(sessionId, tunnelName);
|
|
24908
|
+
},
|
|
24909
|
+
scheduleServiceLinkReconcile: () => scheduleServiceLinkReconcile()
|
|
24464
24910
|
});
|
|
24465
24911
|
const machineService = await registerMachineService(
|
|
24466
24912
|
server,
|
|
@@ -24592,6 +25038,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24592
25038
|
} catch (err) {
|
|
24593
25039
|
logger.log(`[serve] mount link reconcile failed: ${err?.message || err}`);
|
|
24594
25040
|
}
|
|
25041
|
+
scheduleServiceLinkReconcile(3e3);
|
|
24595
25042
|
}).catch((err) => logger.error(`[serve] mount restore failed: ${err?.message || err}`));
|
|
24596
25043
|
const daemonOwnerEmail = parseJwtEmail(process.env.HYPHA_TOKEN || "") || null;
|
|
24597
25044
|
serveManager$1.setSessionResolver((sessionId) => {
|
|
@@ -24658,7 +25105,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24658
25105
|
try {
|
|
24659
25106
|
for (const ent of readdirSync$1(join(p.directory, ".svamp"), { withFileTypes: true })) {
|
|
24660
25107
|
if (!ent.isDirectory() || knownSessionIds.has(ent.name)) continue;
|
|
24661
|
-
const owner =
|
|
25108
|
+
const owner = loopOwnerSessionIncludingDormant(p.directory, ent.name);
|
|
24662
25109
|
if (owner && !knownSessionIds.has(owner)) {
|
|
24663
25110
|
deactivateLoop(p.directory, ent.name);
|
|
24664
25111
|
logger.log(`[loop] Deactivated stale loop-state for ${ent.name} in ${p.directory} (owner no longer known)`);
|
|
@@ -24666,7 +25113,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24666
25113
|
}
|
|
24667
25114
|
} catch {
|
|
24668
25115
|
}
|
|
24669
|
-
const legacyOwner =
|
|
25116
|
+
const legacyOwner = loopOwnerSessionIncludingDormant(p.directory);
|
|
24670
25117
|
if (legacyOwner && !knownSessionIds.has(legacyOwner)) {
|
|
24671
25118
|
deactivateLoop(p.directory);
|
|
24672
25119
|
logger.log(`[loop] Deactivated stale legacy loop-state in ${p.directory} (owner session ${legacyOwner} no longer known)`);
|
|
@@ -24675,7 +25122,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24675
25122
|
}
|
|
24676
25123
|
if (persistedSessions.length > 0) {
|
|
24677
25124
|
try {
|
|
24678
|
-
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-
|
|
25125
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-C43KnZ-Q.mjs');
|
|
24679
25126
|
await awaitClaudeVersionReady();
|
|
24680
25127
|
} catch {
|
|
24681
25128
|
}
|
|
@@ -24727,7 +25174,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24727
25174
|
try {
|
|
24728
25175
|
const lp = join(getLoopDir(persisted.directory, persisted.sessionId), "loop-state.json");
|
|
24729
25176
|
const cur = readLoopState(persisted.directory, persisted.sessionId);
|
|
24730
|
-
if (cur) atomicWriteLoopState(lp, { ...cur, active: true, phase: "continue", completed_at: void 0, verify_errors: 0, resumed_at: Date.now() });
|
|
25177
|
+
if (cur) atomicWriteLoopState(lp, { ...cur, active: true, phase: "continue", completed_at: void 0, verify_errors: 0, resumed_at: Date.now(), progress_history: [], auto_resumes: 0 });
|
|
24731
25178
|
} catch {
|
|
24732
25179
|
}
|
|
24733
25180
|
}
|
|
@@ -24865,13 +25312,16 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24865
25312
|
}
|
|
24866
25313
|
if (sessionsToLoopResume.length > 0 && !options?.noAutoContinue) {
|
|
24867
25314
|
logger.log(`Resuming loop for ${sessionsToLoopResume.length} session(s)...`);
|
|
24868
|
-
|
|
25315
|
+
const LOOP_RESUME_STEP_MS = Number(process.env.SVAMP_AUTO_CONTINUE_STEP_MS) || 300;
|
|
25316
|
+
const LOOP_RESUME_MAX_SPREAD_MS = Number(process.env.SVAMP_AUTO_CONTINUE_MAX_SPREAD_MS) || 6e4;
|
|
25317
|
+
sessionsToLoopResume.forEach(({ sessionId, directory: sessDir }, loopResumeIndex) => {
|
|
25318
|
+
const loopResumeDelay = 2e3 + Math.min(loopResumeIndex * LOOP_RESUME_STEP_MS, LOOP_RESUME_MAX_SPREAD_MS) + Math.floor(Math.random() * LOOP_RESUME_STEP_MS);
|
|
24869
25319
|
try {
|
|
24870
25320
|
const tracked = Array.from(pidToTrackedSession.values()).find((s) => s.svampSessionId === sessionId);
|
|
24871
25321
|
const rpc = tracked?.sessionRPCHandlers;
|
|
24872
25322
|
if (!rpc) {
|
|
24873
25323
|
logger.log(`Session ${sessionId} RPC handlers not found for loop resume`);
|
|
24874
|
-
|
|
25324
|
+
return;
|
|
24875
25325
|
}
|
|
24876
25326
|
const loopTurnFingerprint = () => {
|
|
24877
25327
|
try {
|
|
@@ -24897,7 +25347,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24897
25347
|
if (!isLoopActiveForSession(sessDir, sessionId)) return;
|
|
24898
25348
|
const fpBefore = loopTurnFingerprint();
|
|
24899
25349
|
await sendResume("");
|
|
24900
|
-
const graceMs = Math.max(5e3, Number(process.env.SVAMP_LOOP_RESUME_WATCHDOG_MS) || 45e3);
|
|
25350
|
+
const graceMs = Math.max(5e3, Number(process.env.SVAMP_LOOP_RESUME_WATCHDOG_MS) || 45e3) + Math.min(loopResumeIndex * LOOP_RESUME_STEP_MS, LOOP_RESUME_MAX_SPREAD_MS);
|
|
24901
25351
|
setTimeout(() => {
|
|
24902
25352
|
try {
|
|
24903
25353
|
if (!isLoopActiveForSession(sessDir, sessionId)) return;
|
|
@@ -24914,11 +25364,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24914
25364
|
} catch (err) {
|
|
24915
25365
|
logger.log(`Failed to resume loop for session ${sessionId}: ${err.message}`);
|
|
24916
25366
|
}
|
|
24917
|
-
},
|
|
25367
|
+
}, loopResumeDelay);
|
|
24918
25368
|
} catch (err) {
|
|
24919
25369
|
logger.log(`Failed to find session service for loop resume ${sessionId}: ${err.message}`);
|
|
24920
25370
|
}
|
|
24921
|
-
}
|
|
25371
|
+
});
|
|
24922
25372
|
}
|
|
24923
25373
|
(async () => {
|
|
24924
25374
|
try {
|
|
@@ -24928,27 +25378,17 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24928
25378
|
logger.log(`[ARTIFACT SYNC] Background init failed: ${err.message}`);
|
|
24929
25379
|
}
|
|
24930
25380
|
})();
|
|
24931
|
-
let appToken;
|
|
24932
|
-
try {
|
|
24933
|
-
appToken = await server.generateToken({});
|
|
24934
|
-
logger.log(`App connection token generated`);
|
|
24935
|
-
} catch (err) {
|
|
24936
|
-
logger.log("Could not generate token (server may not support it):", err);
|
|
24937
|
-
}
|
|
24938
25381
|
console.log("Svamp daemon started successfully!");
|
|
24939
25382
|
console.log(` Machine ID: ${machineId}`);
|
|
24940
25383
|
console.log(` Hypha server: ${hyphaServerUrl}`);
|
|
24941
25384
|
console.log(` Workspace: ${server.config.workspace}`);
|
|
24942
|
-
if (appToken) {
|
|
24943
|
-
console.log(` App token: ${appToken}`);
|
|
24944
|
-
}
|
|
24945
25385
|
console.log(` Service: svamp-machine-${machineId}`);
|
|
24946
25386
|
console.log(` Log file: ${logger.logFilePath}`);
|
|
24947
25387
|
const HEARTBEAT_INTERVAL_MS = 1e4;
|
|
24948
25388
|
const PING_TIMEOUT_MS = 15e3;
|
|
24949
25389
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
24950
25390
|
const RECONNECT_JITTER_MS = 2500;
|
|
24951
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
25391
|
+
const { WorkflowScheduler } = await import('./scheduler-gMWmsAgp.mjs');
|
|
24952
25392
|
const workflowProjectRoots = () => {
|
|
24953
25393
|
const dirs = /* @__PURE__ */ new Set();
|
|
24954
25394
|
for (const s of pidToTrackedSession.values()) {
|
|
@@ -24987,6 +25427,9 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24987
25427
|
};
|
|
24988
25428
|
runOAuthAccountRefresh();
|
|
24989
25429
|
const backendAccountRefreshInterval = setInterval(runOAuthAccountRefresh, 5 * 6e4);
|
|
25430
|
+
const serviceLinkSweepInterval = setInterval(() => {
|
|
25431
|
+
void reconcileAllServiceLinks();
|
|
25432
|
+
}, 6e4);
|
|
24990
25433
|
let heartbeatRunning = false;
|
|
24991
25434
|
let heartbeatCycle = 0;
|
|
24992
25435
|
const heartbeatInterval = setInterval(async () => {
|
|
@@ -25239,6 +25682,8 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
25239
25682
|
clearInterval(heartbeatInterval);
|
|
25240
25683
|
clearInterval(workflowSchedulerInterval);
|
|
25241
25684
|
clearInterval(backendAccountRefreshInterval);
|
|
25685
|
+
clearInterval(serviceLinkSweepInterval);
|
|
25686
|
+
if (serviceLinkReconcileTimer) clearTimeout(serviceLinkReconcileTimer);
|
|
25242
25687
|
if (proxyTokenRefreshInterval) clearInterval(proxyTokenRefreshInterval);
|
|
25243
25688
|
if (oauthRefreshInterval) clearInterval(oauthRefreshInterval);
|
|
25244
25689
|
if (unhandledRejectionResetTimer) clearTimeout(unhandledRejectionResetTimer);
|
|
@@ -25583,4 +26028,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
25583
26028
|
writeStopMarker: writeStopMarker
|
|
25584
26029
|
});
|
|
25585
26030
|
|
|
25586
|
-
export {
|
|
26031
|
+
export { SKILLS_DIR as $, validateCronExpr as A, validateWorkflowName as B, saveWorkflow as C, rawWorkflow as D, listWorkflows as E, isWorkflowEnabled as F, workflowSchedules as G, inZone as H, cronMatches as I, summarize as J, workflowSteps as K, describeNextFire as L, parseJwtEmail as M, computeCollectionConfigUpdate as N, SYSTEM_COLLECTION_CONFIG as O, loadMachineContext as P, buildMachineInstructions as Q, READ_ONLY_TOOLS as R, SharingNotificationSync as S, machineToolsForRole as T, buildMachineTools as U, parseFrontmatter as V, getSkillsServer as W, getSkillsWorkspaceName as X, getSkillsCollectionName as Y, fetchWithTimeout as Z, searchSkills as _, createSessionStore as a, getSkillInfo as a0, downloadSkillFile as a1, listSkillFiles as a2, resolveModel as a3, formatHandle as a4, normalizeAllowedUser as a5, loadSecurityContextConfig as a6, resolveSecurityContext as a7, buildSecurityContextFromFlags as a8, mergeSecurityContexts as a9, GeminiTransport$1 as aA, KimiTransport$1 as aB, kimiProvider as aC, kimiSetup as aD, api as aE, supervisorLock as aF, run as aG, buildSessionShareUrl as aa, computeOutboundHop as ab, registerAwaitingReply as ac, buildMachineShareUrl as ad, parseHandle as ae, handleMatchesMetadata as af, PINNED_CODEX_VERSION as ag, clearStopMarker as ah, stopMarkerExists as ai, withFileLock as aj, describeMisconfiguration as ak, buildMachineDeps as al, applyClaudeProxyEnv as am, composeSessionId as an, generateFriendlyName as ao, generateHookSettings as ap, instanceConfig as aq, frpc as ar, staticFileServer as as, claudeAuth as at, codexProvider as au, projectInfo as av, DefaultTransport$1 as aw, acpBackend as ax, acpAgentConfig as ay, codexAppServerBackend as az, stopDaemon as b, connectToHypha as c, daemonStatus as d, shortId as e, resolveProjectRoot as f, getHyphaServerUrl$1 as g, getIssue as h, checkVerifyCommand as i, resumeIssue as j, addComment as k, addIssue as l, listIssues as m, isPendingIssue as n, searchIssues as o, pauseIssue as p, isVisibleTo as q, registerMachineService as r, startDaemon as s, getRun as t, updateIssue as u, listRuns as v, getWorkflow as w, runWorkflow as x, setWorkflowEnabled as y, removeWorkflow as z };
|