svamp-cli 0.2.245 → 0.2.247
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-DvLTS2bk.mjs → agentCommands-DOzMhE_G.mjs} +5 -5
- package/dist/{auth-JDjru_js.mjs → auth-B_ZnoKX0.mjs} +1 -1
- package/dist/cli.mjs +82 -68
- package/dist/{commands-QyDnDry4.mjs → commands-BENx03Oz.mjs} +2 -2
- package/dist/{commands-DfTq3rS3.mjs → commands-Bm5q9G35.mjs} +1 -1
- package/dist/{commands-CIa6EEAk.mjs → commands-C2u9U3y6.mjs} +2 -2
- package/dist/{commands-Bt8pK8MS.mjs → commands-CxOPcoWa.mjs} +1 -1
- package/dist/{commands-jFRw1VYy.mjs → commands-F6j9TF4Q.mjs} +6 -6
- package/dist/{commands-CRsGX1t1.mjs → commands-_rMwFmFH.mjs} +1 -1
- package/dist/{commands-CnVo5S8W.mjs → commands-vCeWXvYJ.mjs} +1 -1
- package/dist/{fleet-gkqGOfvU.mjs → fleet-pMXCRilx.mjs} +12 -4
- package/dist/{frpc-Dp9dDwDE.mjs → frpc-Di-y4cWK.mjs} +5 -2
- package/dist/{headlessCli-BIOe8SSK.mjs → headlessCli-D6k9Pnum.mjs} +2 -2
- package/dist/{httpServer-B1KVQJfm.mjs → httpServer-CSMZTpoh.mjs} +25 -3
- package/dist/index.mjs +1 -1
- package/dist/{package-ZEiIG_qH.mjs → package-BJYmQjS2.mjs} +2 -2
- package/dist/{rpc-CdbgL61q.mjs → rpc-BncvIQMO.mjs} +1 -1
- package/dist/{rpc-Wp_ufnPT.mjs → rpc-DbY7NUhj.mjs} +2 -2
- package/dist/{run-BgPz-Shl.mjs → run-1BBi0_ol.mjs} +104 -23
- package/dist/{run-hCQkcg9-.mjs → run-CYx_Rhiu.mjs} +1 -1
- package/dist/{runStore-BKgHQGNO.mjs → runStore-C5IyfbON.mjs} +1 -1
- package/dist/{scheduler-BUo_8paQ.mjs → scheduler-BkXOKjHn.mjs} +30 -4
- package/dist/{serveCommands-bEavErQo.mjs → serveCommands-CESRU6-1.mjs} +5 -5
- package/dist/{serveManager-3zR_v1Xj.mjs → serveManager-BZtpClpC.mjs} +36 -19
- package/dist/{sideband-l39JbmHM.mjs → sideband-BqD4-WWR.mjs} +1 -1
- package/package.json +2 -2
|
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
|
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
60
60
|
if (foreground) {
|
|
61
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
61
|
+
const { runFrpcTunnel } = await import('./frpc-Di-y4cWK.mjs');
|
|
62
62
|
await runFrpcTunnel(name, ports, void 0, {
|
|
63
63
|
group,
|
|
64
64
|
groupKey,
|
|
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
|
|
|
68
68
|
});
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
71
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
72
72
|
const { server, machine } = await connectAndGetMachine();
|
|
73
73
|
try {
|
|
74
74
|
const status = await machine.tunnelStart({
|
|
@@ -90,7 +90,7 @@ async function serviceExpose(args) {
|
|
|
90
90
|
console.log(` port ${port}: ${url}`);
|
|
91
91
|
}
|
|
92
92
|
if (process.env.SVAMP_SESSION_ID) {
|
|
93
|
-
const { autoAddSessionLink } = await import('./agentCommands-
|
|
93
|
+
const { autoAddSessionLink } = await import('./agentCommands-DOzMhE_G.mjs');
|
|
94
94
|
let added = 0;
|
|
95
95
|
for (const [port, url] of urlEntries) {
|
|
96
96
|
const label = urlEntries.length > 1 ? `${name}:${port}` : name;
|
|
@@ -136,7 +136,7 @@ async function serviceServe(args) {
|
|
|
136
136
|
};
|
|
137
137
|
process.on("SIGINT", cleanup);
|
|
138
138
|
process.on("SIGTERM", cleanup);
|
|
139
|
-
const { runFrpcTunnel } = await import('./frpc-
|
|
139
|
+
const { runFrpcTunnel } = await import('./frpc-Di-y4cWK.mjs');
|
|
140
140
|
await runFrpcTunnel(name, [caddyPort]);
|
|
141
141
|
} catch (err) {
|
|
142
142
|
console.error(`Error serving directory: ${err.message}`);
|
|
@@ -145,7 +145,7 @@ async function serviceServe(args) {
|
|
|
145
145
|
}
|
|
146
146
|
async function serviceList(_args) {
|
|
147
147
|
try {
|
|
148
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
148
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
149
149
|
const { server, machine } = await connectAndGetMachine();
|
|
150
150
|
try {
|
|
151
151
|
const tunnels = await machine.tunnelList({});
|
|
@@ -185,7 +185,7 @@ async function serviceDelete(args) {
|
|
|
185
185
|
process.exit(1);
|
|
186
186
|
}
|
|
187
187
|
try {
|
|
188
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
188
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
189
189
|
const { server, machine } = await connectAndGetMachine();
|
|
190
190
|
try {
|
|
191
191
|
await machine.tunnelStop({ name });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { m as resolveProjectRoot, w as searchIssues, v as listIssues, o as resumeIssue, p as pauseIssue, q as addComment, u as updateIssue, n as getIssue, x as isVisibleTo, z as summarize, t as addIssue } from './run-
|
|
4
|
+
import { m as resolveProjectRoot, w as searchIssues, v as listIssues, o as resumeIssue, p as pauseIssue, q as addComment, u as updateIssue, n as getIssue, x as isVisibleTo, z as summarize, t as addIssue } from './run-1BBi0_ol.mjs';
|
|
5
5
|
import 'os';
|
|
6
6
|
import 'fs/promises';
|
|
7
7
|
import 'fs';
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { basename, resolve, join, isAbsolute } from 'node:path';
|
|
4
4
|
import os from 'node:os';
|
|
5
|
-
import { Q as formatHandle, T as normalizeAllowedUser, U as loadSecurityContextConfig, V as resolveSecurityContext, W as buildSecurityContextFromFlags, X as mergeSecurityContexts, c as connectToHypha, Y as buildSessionShareUrl, Z as computeOutboundHop, A as shortId, _ as registerAwaitingReply, $ as buildMachineShareUrl, a0 as parseHandle, a1 as handleMatchesMetadata } from './run-
|
|
5
|
+
import { Q as formatHandle, T as normalizeAllowedUser, U as loadSecurityContextConfig, V as resolveSecurityContext, W as buildSecurityContextFromFlags, X as mergeSecurityContexts, c as connectToHypha, Y as buildSessionShareUrl, Z as computeOutboundHop, A as shortId, _ as registerAwaitingReply, $ as buildMachineShareUrl, a0 as parseHandle, a1 as handleMatchesMetadata } from './run-1BBi0_ol.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { c as connectToHypha } from './run-
|
|
4
|
+
import { c as connectToHypha } from './run-1BBi0_ol.mjs';
|
|
5
5
|
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-ivV7xAp3.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
@@ -259,12 +259,14 @@ async function fleetUpgradeSvamp(opts) {
|
|
|
259
259
|
console.log("\u26A0 --exclude-self: could not determine this machine's ID (no local daemon state) \u2014 nothing excluded.");
|
|
260
260
|
}
|
|
261
261
|
const { targets, skipped } = partitionSelf(machines, selfId);
|
|
262
|
-
|
|
262
|
+
const restartCmd = opts?.force ? "svamp daemon restart" : "svamp daemon restart --when-idle";
|
|
263
|
+
const mode = opts?.force ? "force (immediate)" : "graceful (restart when idle)";
|
|
264
|
+
console.log(`Upgrading svamp-cli to ${version} on ${targets.length} machine(s)${selfId ? ` (excluding self: ${selfId})` : ""} \u2014 ${mode}...
|
|
263
265
|
`);
|
|
264
266
|
try {
|
|
265
267
|
printHeader(["MACHINE ID", "LABEL", "STATE", "DETAIL"], [20, 24, 6, 60]);
|
|
266
268
|
for (const m of skipped) printResultRow(m, "SKIP", 'self \u2014 restart separately with "svamp daemon restart"');
|
|
267
|
-
const cmd = `bash -lc 'npm install -g svamp-cli@${version} 2>&1 | tail -5 &&
|
|
269
|
+
const cmd = `bash -lc 'npm install -g svamp-cli@${version} 2>&1 | tail -5 && ${restartCmd} 2>&1 | tail -5'`;
|
|
268
270
|
const rows = await fanOut(targets, async (m) => await m.rpc.bash(cmd, void 0));
|
|
269
271
|
let failures = 0;
|
|
270
272
|
for (const r of rows) {
|
|
@@ -279,7 +281,13 @@ async function fleetUpgradeSvamp(opts) {
|
|
|
279
281
|
if (!ok) failures += 1;
|
|
280
282
|
}
|
|
281
283
|
console.log(`
|
|
282
|
-
${rows.length - failures}/${rows.length} machines
|
|
284
|
+
${rows.length - failures}/${rows.length} machines updated (new binary installed).`);
|
|
285
|
+
if (opts?.force) {
|
|
286
|
+
console.log("Daemons restarted immediately onto the new binary.");
|
|
287
|
+
} else {
|
|
288
|
+
console.log("Graceful mode: each daemon restarts itself once it has no actively-thinking session");
|
|
289
|
+
console.log("(new binary already installed; takes effect at that restart). Force now with --force.");
|
|
290
|
+
}
|
|
283
291
|
console.log("Note: daemons will auto-converge Claude Code to the pinned version on restart.");
|
|
284
292
|
if (failures > 0) process.exitCode = 1;
|
|
285
293
|
} finally {
|
|
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir, platform, arch } from 'os';
|
|
6
6
|
import { randomUUID, createHash } from 'crypto';
|
|
7
|
-
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-
|
|
7
|
+
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-1BBi0_ol.mjs';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|
|
10
10
|
import 'node:crypto';
|
|
@@ -339,7 +339,6 @@ class FrpcTunnel {
|
|
|
339
339
|
if (!this._connected) {
|
|
340
340
|
this._connected = true;
|
|
341
341
|
this._lastConnectedAt = Date.now();
|
|
342
|
-
this._restartAttempts = 0;
|
|
343
342
|
this._consecutiveErrors = 0;
|
|
344
343
|
this._firstErrorAt = 0;
|
|
345
344
|
this.options.onConnect?.();
|
|
@@ -375,6 +374,10 @@ class FrpcTunnel {
|
|
|
375
374
|
reject(new Error(`frpc exited with code ${code}`));
|
|
376
375
|
}
|
|
377
376
|
if (!this._destroyed && code !== 0) {
|
|
377
|
+
const STABILITY_WINDOW_MS = 3e4;
|
|
378
|
+
if (this._lastConnectedAt > 0 && Date.now() - this._lastConnectedAt >= STABILITY_WINDOW_MS) {
|
|
379
|
+
this._restartAttempts = 0;
|
|
380
|
+
}
|
|
378
381
|
this._restartAttempts++;
|
|
379
382
|
const delay = Math.min(3e3 * Math.pow(2, this._restartAttempts - 1), 6e4);
|
|
380
383
|
this.log(`frpc exited with code ${code}, restarting in ${Math.round(delay / 1e3)}s (attempt ${this._restartAttempts})...`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as resolveModel, a2 as describeMisconfiguration, a3 as buildMachineDeps } from './run-
|
|
2
|
-
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-
|
|
1
|
+
import { P as resolveModel, a2 as describeMisconfiguration, a3 as buildMachineDeps } from './run-1BBi0_ol.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-BqD4-WWR.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
|
@@ -86,7 +86,8 @@ function createChannelHttpServer(deps) {
|
|
|
86
86
|
}
|
|
87
87
|
m = u.pathname.match(/^\/channel\/([\w.-]+)\/events$/);
|
|
88
88
|
if (m) {
|
|
89
|
-
const
|
|
89
|
+
const cid = m[1];
|
|
90
|
+
let rpc2 = await findOwner(deps, cid);
|
|
90
91
|
if (!rpc2?.channelReceive) {
|
|
91
92
|
res.writeHead(404).end("not found");
|
|
92
93
|
return;
|
|
@@ -100,8 +101,25 @@ function createChannelHttpServer(deps) {
|
|
|
100
101
|
req.on("close", () => {
|
|
101
102
|
closed = true;
|
|
102
103
|
});
|
|
104
|
+
const MIN_LOOP_MS = 1e3;
|
|
103
105
|
while (!closed) {
|
|
104
|
-
const
|
|
106
|
+
const started = Date.now();
|
|
107
|
+
let out;
|
|
108
|
+
try {
|
|
109
|
+
out = await rpc2.channelReceive({ channel: cid, key, from, cursor, wait: 25 });
|
|
110
|
+
} catch (e) {
|
|
111
|
+
const re = await findOwner(deps, cid).catch(() => null);
|
|
112
|
+
if (re?.channelReceive && !closed) {
|
|
113
|
+
rpc2 = re;
|
|
114
|
+
await new Promise((r) => setTimeout(r, MIN_LOOP_MS));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
res.write(`event: error
|
|
118
|
+
data: ${JSON.stringify({ error: e?.message || String(e) })}
|
|
119
|
+
|
|
120
|
+
`);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
105
123
|
if (out?.error) {
|
|
106
124
|
res.write(`event: error
|
|
107
125
|
data: ${JSON.stringify({ error: out.error })}
|
|
@@ -113,7 +131,11 @@ data: ${JSON.stringify({ error: out.error })}
|
|
|
113
131
|
|
|
114
132
|
`);
|
|
115
133
|
cursor = out.cursor ?? cursor;
|
|
116
|
-
if (!(out.replies || []).length)
|
|
134
|
+
if (!(out.replies || []).length) {
|
|
135
|
+
res.write(": keepalive\n\n");
|
|
136
|
+
const elapsed = Date.now() - started;
|
|
137
|
+
if (elapsed < MIN_LOOP_MS) await new Promise((r) => setTimeout(r, MIN_LOOP_MS - elapsed));
|
|
138
|
+
}
|
|
117
139
|
}
|
|
118
140
|
try {
|
|
119
141
|
res.end();
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-1BBi0_ol.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var name = "svamp-cli";
|
|
2
|
-
var version = "0.2.
|
|
2
|
+
var version = "0.2.247";
|
|
3
3
|
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
4
|
var author = "Amun AI AB";
|
|
5
5
|
var license = "SEE LICENSE IN LICENSE";
|
|
@@ -19,7 +19,7 @@ var exports$1 = {
|
|
|
19
19
|
var scripts = {
|
|
20
20
|
build: "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
21
21
|
typecheck: "tsc --noEmit",
|
|
22
|
-
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs",
|
|
22
|
+
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-graceful-restart.mjs",
|
|
23
23
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
24
|
dev: "tsx src/cli.ts",
|
|
25
25
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-1BBi0_ol.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { m as resolveProjectRoot } from './run-
|
|
1
|
+
import { m as resolveProjectRoot } from './run-1BBi0_ol.mjs';
|
|
2
2
|
import { g as getWorkflow, s as setWorkflowEnabled, r as removeWorkflow, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-BTs0H_y0.mjs';
|
|
3
|
-
import { g as getRun, l as listRuns, r as runWorkflow } from './runStore-
|
|
3
|
+
import { g as getRun, l as listRuns, r as runWorkflow } from './runStore-C5IyfbON.mjs';
|
|
4
4
|
import 'os';
|
|
5
5
|
import 'fs/promises';
|
|
6
6
|
import 'fs';
|
|
@@ -1201,10 +1201,15 @@ function parseField(token, [min, max]) {
|
|
|
1201
1201
|
for (let i = min; i <= max; i++) set.add(i);
|
|
1202
1202
|
} else if (m = part.match(/^\*\/(\d+)$/)) {
|
|
1203
1203
|
const s = +m[1];
|
|
1204
|
+
if (s <= 0) throw new Error(`invalid cron step (must be >= 1): "${token}"`);
|
|
1204
1205
|
for (let i = min; i <= max; i += s) set.add(i);
|
|
1205
1206
|
} else if (m = part.match(/^(\d+)-(\d+)\/(\d+)$/)) {
|
|
1206
|
-
|
|
1207
|
+
const s = +m[3];
|
|
1208
|
+
if (s <= 0) throw new Error(`invalid cron step (must be >= 1): "${token}"`);
|
|
1209
|
+
if (+m[1] > +m[2]) throw new Error(`invalid cron range (start > end): "${token}"`);
|
|
1210
|
+
for (let i = +m[1]; i <= +m[2]; i += s) set.add(i);
|
|
1207
1211
|
} else if (m = part.match(/^(\d+)-(\d+)$/)) {
|
|
1212
|
+
if (+m[1] > +m[2]) throw new Error(`invalid cron range (start > end): "${token}"`);
|
|
1208
1213
|
for (let i = +m[1]; i <= +m[2]; i++) set.add(i);
|
|
1209
1214
|
} else if (m = part.match(/^(\d+)$/)) {
|
|
1210
1215
|
set.add(+m[1]);
|
|
@@ -2991,7 +2996,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2991
2996
|
const tunnels = handlers.tunnels;
|
|
2992
2997
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2993
2998
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2994
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2999
|
+
const { FrpcTunnel } = await import('./frpc-Di-y4cWK.mjs');
|
|
2995
3000
|
const tunnel = new FrpcTunnel({
|
|
2996
3001
|
name: params.name,
|
|
2997
3002
|
ports: params.ports,
|
|
@@ -3442,7 +3447,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3442
3447
|
}
|
|
3443
3448
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3444
3449
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3445
|
-
const { toolsForRole } = await import('./sideband-
|
|
3450
|
+
const { toolsForRole } = await import('./sideband-BqD4-WWR.mjs');
|
|
3446
3451
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3447
3452
|
return fmt(r2);
|
|
3448
3453
|
}
|
|
@@ -3541,7 +3546,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3541
3546
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3542
3547
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3543
3548
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3544
|
-
const { queryCore } = await import('./commands-
|
|
3549
|
+
const { queryCore } = await import('./commands-vCeWXvYJ.mjs');
|
|
3545
3550
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3546
3551
|
let result;
|
|
3547
3552
|
try {
|
|
@@ -3748,15 +3753,7 @@ function writeInboundContext(sessionId, ctx) {
|
|
|
3748
3753
|
mkdirSync(join(SVAMP_HOME$2, "inbound"), { recursive: true });
|
|
3749
3754
|
const tmp = p + ".tmp";
|
|
3750
3755
|
writeFileSync(tmp, JSON.stringify({ ...ctx, deliveredAt: Date.now() }));
|
|
3751
|
-
|
|
3752
|
-
rmSync(p, { force: true });
|
|
3753
|
-
} catch {
|
|
3754
|
-
}
|
|
3755
|
-
writeFileSync(p, readFileSync(tmp));
|
|
3756
|
-
try {
|
|
3757
|
-
rmSync(tmp, { force: true });
|
|
3758
|
-
} catch {
|
|
3759
|
-
}
|
|
3756
|
+
renameSync(tmp, p);
|
|
3760
3757
|
} catch {
|
|
3761
3758
|
}
|
|
3762
3759
|
}
|
|
@@ -5402,7 +5399,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
5402
5399
|
},
|
|
5403
5400
|
// ── BTW (By The Way) — ephemeral side-channel ──
|
|
5404
5401
|
btw: async (question, context) => {
|
|
5405
|
-
authorizeRequest(context, metadata.sharing, "
|
|
5402
|
+
authorizeRequest(context, metadata.sharing, "admin");
|
|
5406
5403
|
if (!question || typeof question !== "string") {
|
|
5407
5404
|
return { success: false, error: "Question is required" };
|
|
5408
5405
|
}
|
|
@@ -11000,7 +10997,7 @@ const HOUR_MS = 36e5;
|
|
|
11000
10997
|
function sumUsageTokens(usage) {
|
|
11001
10998
|
if (!usage || typeof usage !== "object") return 0;
|
|
11002
10999
|
const n = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
11003
|
-
return n(usage.input_tokens) + n(usage.output_tokens) + n(usage.cache_creation_input_tokens);
|
|
11000
|
+
return n(usage.input_tokens) + n(usage.output_tokens) + n(usage.cache_creation_input_tokens) + Math.round(n(usage.cache_read_input_tokens) * 0.1);
|
|
11004
11001
|
}
|
|
11005
11002
|
function accumulateLedger(prev, sample) {
|
|
11006
11003
|
const base = prev && typeof prev === "object" ? { turns: prev.turns || 0, tokens: prev.tokens || 0, window: Array.isArray(prev.window) ? prev.window : [] } : { turns: 0, tokens: 0, window: [] };
|
|
@@ -11855,6 +11852,30 @@ const SVAMP_HOME = process.env.SVAMP_HOME || join$1(os$1.homedir(), ".svamp");
|
|
|
11855
11852
|
const DAEMON_STATE_FILE = join$1(SVAMP_HOME, "daemon.state.json");
|
|
11856
11853
|
const DAEMON_LOCK_FILE = join$1(SVAMP_HOME, "daemon.lock");
|
|
11857
11854
|
const DAEMON_STOP_MARKER_FILE = join$1(SVAMP_HOME, "daemon.stop");
|
|
11855
|
+
const PENDING_RESTART_FILE = join$1(SVAMP_HOME, "pending-restart.json");
|
|
11856
|
+
function writePendingRestart(p) {
|
|
11857
|
+
try {
|
|
11858
|
+
if (!existsSync$1(SVAMP_HOME)) mkdirSync$1(SVAMP_HOME, { recursive: true });
|
|
11859
|
+
const rec = { scheduledAt: Date.now(), ...p };
|
|
11860
|
+
writeFileSync$1(PENDING_RESTART_FILE, JSON.stringify(rec), "utf-8");
|
|
11861
|
+
} catch {
|
|
11862
|
+
}
|
|
11863
|
+
}
|
|
11864
|
+
function readPendingRestart() {
|
|
11865
|
+
try {
|
|
11866
|
+
if (!existsSync$1(PENDING_RESTART_FILE)) return null;
|
|
11867
|
+
const rec = JSON.parse(readFileSync$1(PENDING_RESTART_FILE, "utf-8"));
|
|
11868
|
+
return rec && typeof rec.scheduledAt === "number" ? rec : null;
|
|
11869
|
+
} catch {
|
|
11870
|
+
return null;
|
|
11871
|
+
}
|
|
11872
|
+
}
|
|
11873
|
+
function clearPendingRestart() {
|
|
11874
|
+
try {
|
|
11875
|
+
if (existsSync$1(PENDING_RESTART_FILE)) unlinkSync$1(PENDING_RESTART_FILE);
|
|
11876
|
+
} catch {
|
|
11877
|
+
}
|
|
11878
|
+
}
|
|
11858
11879
|
function writeStopMarker(reason) {
|
|
11859
11880
|
try {
|
|
11860
11881
|
writeFileSync$1(DAEMON_STOP_MARKER_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${reason}
|
|
@@ -12831,7 +12852,7 @@ async function startDaemon(options) {
|
|
|
12831
12852
|
saveExposedTunnels(list);
|
|
12832
12853
|
}
|
|
12833
12854
|
async function createExposedTunnel(spec) {
|
|
12834
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12855
|
+
const { FrpcTunnel } = await import('./frpc-Di-y4cWK.mjs');
|
|
12835
12856
|
const tunnel = new FrpcTunnel({
|
|
12836
12857
|
name: spec.name,
|
|
12837
12858
|
ports: spec.ports,
|
|
@@ -12851,7 +12872,7 @@ async function startDaemon(options) {
|
|
|
12851
12872
|
return tunnel;
|
|
12852
12873
|
}
|
|
12853
12874
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
12854
|
-
const { ServeManager } = await import('./serveManager-
|
|
12875
|
+
const { ServeManager } = await import('./serveManager-BZtpClpC.mjs');
|
|
12855
12876
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
12856
12877
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
12857
12878
|
});
|
|
@@ -14876,11 +14897,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14876
14897
|
});
|
|
14877
14898
|
},
|
|
14878
14899
|
onIssue: async (params) => {
|
|
14879
|
-
const { issueRpc } = await import('./rpc-
|
|
14900
|
+
const { issueRpc } = await import('./rpc-BncvIQMO.mjs');
|
|
14880
14901
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14881
14902
|
},
|
|
14882
14903
|
onWorkflow: async (params) => {
|
|
14883
|
-
const { workflowRpc } = await import('./rpc-
|
|
14904
|
+
const { workflowRpc } = await import('./rpc-DbY7NUhj.mjs');
|
|
14884
14905
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14885
14906
|
},
|
|
14886
14907
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15413,11 +15434,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15413
15434
|
});
|
|
15414
15435
|
},
|
|
15415
15436
|
onIssue: async (params) => {
|
|
15416
|
-
const { issueRpc } = await import('./rpc-
|
|
15437
|
+
const { issueRpc } = await import('./rpc-BncvIQMO.mjs');
|
|
15417
15438
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
15418
15439
|
},
|
|
15419
15440
|
onWorkflow: async (params) => {
|
|
15420
|
-
const { workflowRpc } = await import('./rpc-
|
|
15441
|
+
const { workflowRpc } = await import('./rpc-DbY7NUhj.mjs');
|
|
15421
15442
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
15422
15443
|
},
|
|
15423
15444
|
onRipgrep: async (args, cwd) => {
|
|
@@ -16034,7 +16055,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16034
16055
|
const channelHttpPort = Number(process.env.SVAMP_CHANNEL_HTTP_PORT) || 0;
|
|
16035
16056
|
if (channelHttpPort > 0) {
|
|
16036
16057
|
try {
|
|
16037
|
-
const { createChannelHttpServer } = await import('./httpServer-
|
|
16058
|
+
const { createChannelHttpServer } = await import('./httpServer-CSMZTpoh.mjs');
|
|
16038
16059
|
const channelHttpServer = createChannelHttpServer({
|
|
16039
16060
|
getSessionIds: () => {
|
|
16040
16061
|
const ids = [];
|
|
@@ -16211,6 +16232,61 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16211
16232
|
);
|
|
16212
16233
|
logger.log(`Restored ${persistedSessions.length} session(s) in ${Date.now() - t0}ms (concurrency ${restoreConcurrency})`);
|
|
16213
16234
|
}
|
|
16235
|
+
{
|
|
16236
|
+
const anySessionBusy = () => {
|
|
16237
|
+
let n = 0;
|
|
16238
|
+
for (const s of pidToTrackedSession.values()) {
|
|
16239
|
+
if (s.stopped || !s.hyphaService) continue;
|
|
16240
|
+
try {
|
|
16241
|
+
if (s.hyphaService.getMetadata()?.lifecycleState === "running") n++;
|
|
16242
|
+
} catch {
|
|
16243
|
+
}
|
|
16244
|
+
}
|
|
16245
|
+
return { busy: n > 0, n };
|
|
16246
|
+
};
|
|
16247
|
+
const triggerSelfRestart = (why) => {
|
|
16248
|
+
logger.log(`[graceful-restart] ${why} \u2014 restarting daemon now`);
|
|
16249
|
+
clearPendingRestart();
|
|
16250
|
+
try {
|
|
16251
|
+
const supPidFile = join$1(SVAMP_HOME, "supervisor.pid");
|
|
16252
|
+
const supPid = existsSync$1(supPidFile) ? parseInt(readFileSync$1(supPidFile, "utf-8").trim(), 10) : NaN;
|
|
16253
|
+
if (supPid && !isNaN(supPid)) {
|
|
16254
|
+
process.kill(supPid, "SIGUSR2");
|
|
16255
|
+
return;
|
|
16256
|
+
}
|
|
16257
|
+
} catch {
|
|
16258
|
+
}
|
|
16259
|
+
logger.log("[graceful-restart] no supervisor to signal \u2014 exiting for KeepAlive respawn");
|
|
16260
|
+
process.exit(0);
|
|
16261
|
+
};
|
|
16262
|
+
let lastDeferLog = 0;
|
|
16263
|
+
const GRACEFUL_RESTART_POLL_MS = 15e3;
|
|
16264
|
+
const DEFAULT_MAX_WAIT_MS = 60 * 60 * 1e3;
|
|
16265
|
+
const gracefulTimer = setInterval(() => {
|
|
16266
|
+
const pending = readPendingRestart();
|
|
16267
|
+
if (!pending) return;
|
|
16268
|
+
if (pending.force) {
|
|
16269
|
+
triggerSelfRestart("force requested");
|
|
16270
|
+
return;
|
|
16271
|
+
}
|
|
16272
|
+
const { busy, n } = anySessionBusy();
|
|
16273
|
+
if (!busy) {
|
|
16274
|
+
triggerSelfRestart("all sessions idle");
|
|
16275
|
+
return;
|
|
16276
|
+
}
|
|
16277
|
+
const maxWait = typeof pending.maxWaitMs === "number" && pending.maxWaitMs > 0 ? pending.maxWaitMs : DEFAULT_MAX_WAIT_MS;
|
|
16278
|
+
if (Date.now() - pending.scheduledAt > maxWait) {
|
|
16279
|
+
triggerSelfRestart(`max-wait ${Math.round(maxWait / 6e4)}m elapsed (${n} still busy)`);
|
|
16280
|
+
return;
|
|
16281
|
+
}
|
|
16282
|
+
const now = Date.now();
|
|
16283
|
+
if (now - lastDeferLog > 6e4) {
|
|
16284
|
+
lastDeferLog = now;
|
|
16285
|
+
logger.log(`[graceful-restart] deferring \u2014 ${n} session(s) actively thinking`);
|
|
16286
|
+
}
|
|
16287
|
+
}, GRACEFUL_RESTART_POLL_MS);
|
|
16288
|
+
gracefulTimer.unref?.();
|
|
16289
|
+
}
|
|
16214
16290
|
if (sessionsToAutoContinue.length > 0 && !options?.noAutoContinue) {
|
|
16215
16291
|
logger.log(`Auto-continuing ${sessionsToAutoContinue.length} interrupted session(s)...`);
|
|
16216
16292
|
for (const sessionId of sessionsToAutoContinue) {
|
|
@@ -16294,7 +16370,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16294
16370
|
const PING_TIMEOUT_MS = 15e3;
|
|
16295
16371
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
16296
16372
|
const RECONNECT_JITTER_MS = 2500;
|
|
16297
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
16373
|
+
const { WorkflowScheduler } = await import('./scheduler-BkXOKjHn.mjs');
|
|
16298
16374
|
const workflowScheduler = new WorkflowScheduler({
|
|
16299
16375
|
projectRoots: () => {
|
|
16300
16376
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -16864,12 +16940,17 @@ function daemonStatus() {
|
|
|
16864
16940
|
var run = /*#__PURE__*/Object.freeze({
|
|
16865
16941
|
__proto__: null,
|
|
16866
16942
|
DAEMON_STOP_MARKER_FILE: DAEMON_STOP_MARKER_FILE,
|
|
16943
|
+
PENDING_RESTART_FILE: PENDING_RESTART_FILE,
|
|
16944
|
+
clearPendingRestart: clearPendingRestart,
|
|
16867
16945
|
clearStopMarker: clearStopMarker,
|
|
16868
16946
|
daemonStatus: daemonStatus,
|
|
16947
|
+
isDaemonAlive: isDaemonAlive,
|
|
16948
|
+
readPendingRestart: readPendingRestart,
|
|
16869
16949
|
restartDaemon: restartDaemon,
|
|
16870
16950
|
startDaemon: startDaemon,
|
|
16871
16951
|
stopDaemon: stopDaemon,
|
|
16872
16952
|
stopMarkerExists: stopMarkerExists,
|
|
16953
|
+
writePendingRestart: writePendingRestart,
|
|
16873
16954
|
writeStopMarker: writeStopMarker
|
|
16874
16955
|
});
|
|
16875
16956
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a4 as applyClaudeProxyEnv, a5 as composeSessionId, a6 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a7 as generateHookSettings } from './run-
|
|
1
|
+
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a4 as applyClaudeProxyEnv, a5 as composeSessionId, a6 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a7 as generateHookSettings } from './run-1BBi0_ol.mjs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from '
|
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { c as workflowSteps, d as workflowsDir } from './store-BTs0H_y0.mjs';
|
|
6
|
-
import { v as listIssues, t as addIssue } from './run-
|
|
6
|
+
import { v as listIssues, t as addIssue } from './run-1BBi0_ol.mjs';
|
|
7
7
|
|
|
8
8
|
const MAX_RUNS_PER_WORKFLOW = 50;
|
|
9
9
|
function maxRunsPerWorkflow() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, y as cronMatches } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, y as cronMatches } from './run-1BBi0_ol.mjs';
|
|
2
2
|
import { l as listWorkflows, i as isWorkflowEnabled, w as workflowCrons } from './store-BTs0H_y0.mjs';
|
|
3
|
-
import { r as runWorkflow } from './runStore-
|
|
3
|
+
import { r as runWorkflow } from './runStore-C5IyfbON.mjs';
|
|
4
4
|
import 'os';
|
|
5
5
|
import 'fs/promises';
|
|
6
6
|
import 'fs';
|
|
@@ -38,8 +38,18 @@ class WorkflowScheduler {
|
|
|
38
38
|
// Wall-clock minute we last processed, so multiple sub-minute ticks fire a workflow at most once
|
|
39
39
|
// per minute (cron granularity is one minute).
|
|
40
40
|
lastMinuteKey = null;
|
|
41
|
+
// Epoch ms of the last tick we processed — used to detect and catch up wall-clock minutes that were
|
|
42
|
+
// SKIPPED between two ticks (host sleep / suspend / a >60s event-loop stall), since setInterval does
|
|
43
|
+
// not fire while the process is suspended (#0243).
|
|
44
|
+
lastTickMs = null;
|
|
41
45
|
// In-flight runs (fire-and-forget). `idle()` awaits them — used by tests; harmless in prod.
|
|
42
46
|
pending = /* @__PURE__ */ new Set();
|
|
47
|
+
// Default cap on how many skipped minutes we look back over on a single tick. Deliberately small: it
|
|
48
|
+
// rescues genuine short gaps (a >60s event-loop stall, a brief suspend) without resurrecting daily
|
|
49
|
+
// crons hours late after a long sleep — multi-hour catch-up needs persisted last-fire state, out of
|
|
50
|
+
// scope here. Per-workflow dedup means even an every-minute cron fires at most once per sweep (no
|
|
51
|
+
// stampede). Override via SVAMP_SCHEDULER_MAX_CATCHUP_MIN (0 disables catch-up entirely).
|
|
52
|
+
static DEFAULT_MAX_CATCHUP_MINUTES = 5;
|
|
43
53
|
/** Await all in-flight scheduled runs (test helper; the daemon never needs to block on these). */
|
|
44
54
|
async idle() {
|
|
45
55
|
await Promise.all([...this.pending]);
|
|
@@ -47,11 +57,27 @@ class WorkflowScheduler {
|
|
|
47
57
|
static minuteKey(d) {
|
|
48
58
|
return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}-${d.getHours()}-${d.getMinutes()}`;
|
|
49
59
|
}
|
|
50
|
-
|
|
60
|
+
static maxCatchupMinutes() {
|
|
61
|
+
const raw = Number(process.env.SVAMP_SCHEDULER_MAX_CATCHUP_MIN);
|
|
62
|
+
if (Number.isFinite(raw) && raw >= 0) return Math.min(Math.floor(raw), 1440);
|
|
63
|
+
return WorkflowScheduler.DEFAULT_MAX_CATCHUP_MINUTES;
|
|
64
|
+
}
|
|
65
|
+
/** Fire all scheduled workflows whose cron matches `now` (or any skipped minute since the last tick).
|
|
66
|
+
* Returns the names fired (for tests/logs). */
|
|
51
67
|
tick(now) {
|
|
52
68
|
const key = WorkflowScheduler.minuteKey(now);
|
|
53
69
|
if (key === this.lastMinuteKey) return [];
|
|
70
|
+
const nowMs = now.getTime();
|
|
71
|
+
const curBoundary = Math.floor(nowMs / 6e4) * 6e4;
|
|
72
|
+
const minutes = [];
|
|
73
|
+
if (this.lastTickMs != null) {
|
|
74
|
+
const earliest = nowMs - WorkflowScheduler.maxCatchupMinutes() * 6e4;
|
|
75
|
+
let t = Math.floor(Math.max(this.lastTickMs + 6e4, earliest) / 6e4) * 6e4;
|
|
76
|
+
for (; t < curBoundary; t += 6e4) minutes.push(new Date(t));
|
|
77
|
+
}
|
|
78
|
+
minutes.push(now);
|
|
54
79
|
this.lastMinuteKey = key;
|
|
80
|
+
this.lastTickMs = nowMs;
|
|
55
81
|
const fired = [];
|
|
56
82
|
const seen = /* @__PURE__ */ new Set();
|
|
57
83
|
for (const dir of this.deps.projectRoots()) {
|
|
@@ -73,7 +99,7 @@ class WorkflowScheduler {
|
|
|
73
99
|
if (!isWorkflowEnabled(wf)) continue;
|
|
74
100
|
const crons = workflowCrons(wf);
|
|
75
101
|
if (!crons.length) continue;
|
|
76
|
-
if (crons.some((c) => cronMatchesSafe(c,
|
|
102
|
+
if (minutes.some((m) => crons.some((c) => cronMatchesSafe(c, m)))) {
|
|
77
103
|
this.fire(root, wf);
|
|
78
104
|
fired.push(wf.name);
|
|
79
105
|
}
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-vCeWXvYJ.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|