svamp-cli 0.2.247 → 0.2.248
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-DOzMhE_G.mjs → agentCommands-B_ozIVKT.mjs} +28 -11
- package/dist/{auth-B_ZnoKX0.mjs → auth-BLDmT7XN.mjs} +2 -1
- package/dist/cli.mjs +62 -61
- package/dist/{commands-vCeWXvYJ.mjs → commands-C3TQOYUo.mjs} +2 -1
- package/dist/{commands-Bm5q9G35.mjs → commands-CL8YI8k5.mjs} +2 -1
- package/dist/{commands-_rMwFmFH.mjs → commands-CRMQUQZG.mjs} +2 -1
- package/dist/{commands-F6j9TF4Q.mjs → commands-Cs2fNd-_.mjs} +7 -7
- package/dist/{commands-CxOPcoWa.mjs → commands-DS4i8ohA.mjs} +10 -6
- package/dist/{commands-BENx03Oz.mjs → commands-Djvi1cZ2.mjs} +3 -2
- package/dist/{commands-C2u9U3y6.mjs → commands-DwhA-iV3.mjs} +3 -2
- package/dist/{fleet-pMXCRilx.mjs → fleet-CJZOVjOY.mjs} +2 -1
- package/dist/{frpc-Di-y4cWK.mjs → frpc-CsjEXBuS.mjs} +2 -1
- package/dist/{headlessCli-D6k9Pnum.mjs → headlessCli-qBontdlf.mjs} +3 -2
- package/dist/index.mjs +2 -1
- package/dist/{package-BJYmQjS2.mjs → package-B7z6GsNX.mjs} +2 -2
- package/dist/{rpc-DbY7NUhj.mjs → rpc-BGFgegj5.mjs} +1 -3
- package/dist/{rpc-BncvIQMO.mjs → rpc-C1DI7HfZ.mjs} +2 -1
- package/dist/{run-CYx_Rhiu.mjs → run-C61rsl7r.mjs} +2 -1
- package/dist/{run-1BBi0_ol.mjs → run-CHIppB2r.mjs} +655 -35
- package/dist/{scheduler-BkXOKjHn.mjs → scheduler-Csp21zzg.mjs} +1 -3
- package/dist/{serveCommands-CESRU6-1.mjs → serveCommands-BYHk72ZO.mjs} +5 -5
- package/dist/{serveManager-BZtpClpC.mjs → serveManager-Di3xrvG2.mjs} +3 -2
- package/dist/{sideband-BqD4-WWR.mjs → sideband-Cs-kNLye.mjs} +2 -1
- package/package.json +2 -2
- package/dist/runStore-C5IyfbON.mjs +0 -239
- package/dist/store-BTs0H_y0.mjs +0 -148
|
@@ -19,6 +19,7 @@ import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
|
19
19
|
import { z } from 'zod';
|
|
20
20
|
import { mkdir, rm, chmod, access, mkdtemp, copyFile, writeFile, readdir, stat, readFile as readFile$1 } from 'node:fs/promises';
|
|
21
21
|
import { promisify as promisify$1 } from 'node:util';
|
|
22
|
+
import { parse, stringify } from 'yaml';
|
|
22
23
|
|
|
23
24
|
let connectToServerFn = null;
|
|
24
25
|
async function getConnectToServer() {
|
|
@@ -2984,6 +2985,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2984
2985
|
if (!client) throw new Error(`Tunnel '${params.name}' not found`);
|
|
2985
2986
|
client.destroy();
|
|
2986
2987
|
tunnels.delete(params.name);
|
|
2988
|
+
handlers.forgetExposedTunnel?.(params.name);
|
|
2987
2989
|
return { name: params.name, stopped: true };
|
|
2988
2990
|
},
|
|
2989
2991
|
/**
|
|
@@ -2996,7 +2998,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2996
2998
|
const tunnels = handlers.tunnels;
|
|
2997
2999
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2998
3000
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2999
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
3001
|
+
const { FrpcTunnel } = await import('./frpc-CsjEXBuS.mjs');
|
|
3000
3002
|
const tunnel = new FrpcTunnel({
|
|
3001
3003
|
name: params.name,
|
|
3002
3004
|
ports: params.ports,
|
|
@@ -3447,7 +3449,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3447
3449
|
}
|
|
3448
3450
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3449
3451
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3450
|
-
const { toolsForRole } = await import('./sideband-
|
|
3452
|
+
const { toolsForRole } = await import('./sideband-Cs-kNLye.mjs');
|
|
3451
3453
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3452
3454
|
return fmt(r2);
|
|
3453
3455
|
}
|
|
@@ -3546,7 +3548,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3546
3548
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3547
3549
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3548
3550
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3549
|
-
const { queryCore } = await import('./commands-
|
|
3551
|
+
const { queryCore } = await import('./commands-C3TQOYUo.mjs');
|
|
3550
3552
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3551
3553
|
let result;
|
|
3552
3554
|
try {
|
|
@@ -10920,6 +10922,438 @@ function summarize(issues) {
|
|
|
10920
10922
|
return { total: issues.length, ready: by("ready"), in_progress: by("in_progress"), archived: by("archived") };
|
|
10921
10923
|
}
|
|
10922
10924
|
|
|
10925
|
+
function isWorkflowEnabled(wf) {
|
|
10926
|
+
return wf.enabled !== false;
|
|
10927
|
+
}
|
|
10928
|
+
function workflowSteps(wf) {
|
|
10929
|
+
return Object.values(wf.jobs || {}).flatMap((j) => j?.steps || []);
|
|
10930
|
+
}
|
|
10931
|
+
function workflowCrons(wf) {
|
|
10932
|
+
return (wf.on?.schedule || []).map((s) => s?.cron).filter((c) => !!c);
|
|
10933
|
+
}
|
|
10934
|
+
function workflowsDir(projectRoot) {
|
|
10935
|
+
return join(projectRoot, ".svamp", "workflows");
|
|
10936
|
+
}
|
|
10937
|
+
function workflowPath(projectRoot, name) {
|
|
10938
|
+
return join(workflowsDir(projectRoot), `${name}.yaml`);
|
|
10939
|
+
}
|
|
10940
|
+
function normalizeOn(on) {
|
|
10941
|
+
if (!on || typeof on !== "object") {
|
|
10942
|
+
return void 0;
|
|
10943
|
+
}
|
|
10944
|
+
const out = {};
|
|
10945
|
+
if (Array.isArray(on.schedule)) {
|
|
10946
|
+
const sched = on.schedule.map((s) => ({ cron: String(s?.cron ?? s ?? "") })).filter((s) => s.cron);
|
|
10947
|
+
if (sched.length) out.schedule = sched;
|
|
10948
|
+
} else if (typeof on.schedule === "string" && on.schedule.trim()) {
|
|
10949
|
+
out.schedule = [{ cron: on.schedule.trim() }];
|
|
10950
|
+
}
|
|
10951
|
+
if ("workflow_dispatch" in on || on.dispatch === true) out.workflow_dispatch = true;
|
|
10952
|
+
if (typeof on.channel === "string" && on.channel) out.channel = on.channel;
|
|
10953
|
+
if (Array.isArray(on.issue) && on.issue.length) out.issue = on.issue.map(String);
|
|
10954
|
+
if ("idle" in on && on.idle !== false || on.idle === true) out.idle = true;
|
|
10955
|
+
return Object.keys(out).length ? out : void 0;
|
|
10956
|
+
}
|
|
10957
|
+
function normalizeJobs(jobs) {
|
|
10958
|
+
if (Array.isArray(jobs)) {
|
|
10959
|
+
const steps = jobs.map((j) => ({ run: String(j?.run ?? "") })).filter((s) => s.run);
|
|
10960
|
+
return steps.length ? { run: { steps } } : {};
|
|
10961
|
+
}
|
|
10962
|
+
if (jobs && typeof jobs === "object") {
|
|
10963
|
+
const out = {};
|
|
10964
|
+
for (const [id, job] of Object.entries(jobs)) {
|
|
10965
|
+
const rawSteps = Array.isArray(job?.steps) ? job.steps : job?.run ? [{ run: job.run }] : [];
|
|
10966
|
+
const steps = rawSteps.map((s) => ({ run: String(s?.run ?? ""), ...s?.name ? { name: String(s.name) } : {} })).filter((s) => s.run);
|
|
10967
|
+
if (steps.length) out[String(id)] = { steps };
|
|
10968
|
+
}
|
|
10969
|
+
return out;
|
|
10970
|
+
}
|
|
10971
|
+
return {};
|
|
10972
|
+
}
|
|
10973
|
+
function parseWorkflow(content) {
|
|
10974
|
+
try {
|
|
10975
|
+
const o = parse(content);
|
|
10976
|
+
if (!o || typeof o !== "object" || !o.name) return null;
|
|
10977
|
+
return {
|
|
10978
|
+
name: String(o.name),
|
|
10979
|
+
on: normalizeOn(o.on),
|
|
10980
|
+
jobs: normalizeJobs(o.jobs),
|
|
10981
|
+
session: o.session ?? null,
|
|
10982
|
+
// Only `enabled: false` disables; anything else (absent/true) is active.
|
|
10983
|
+
...o.enabled === false ? { enabled: false } : {}
|
|
10984
|
+
};
|
|
10985
|
+
} catch {
|
|
10986
|
+
return null;
|
|
10987
|
+
}
|
|
10988
|
+
}
|
|
10989
|
+
function serializeWorkflow(wf) {
|
|
10990
|
+
const clean = { name: wf.name };
|
|
10991
|
+
if (wf.session) clean.session = wf.session;
|
|
10992
|
+
if (wf.enabled === false) clean.enabled = false;
|
|
10993
|
+
const on = {};
|
|
10994
|
+
if (wf.on?.schedule?.length) on.schedule = wf.on.schedule.map((s) => ({ cron: s.cron }));
|
|
10995
|
+
if (wf.on?.workflow_dispatch) on.workflow_dispatch = {};
|
|
10996
|
+
if (wf.on?.channel) on.channel = wf.on.channel;
|
|
10997
|
+
if (wf.on?.issue?.length) on.issue = wf.on.issue;
|
|
10998
|
+
if (wf.on?.idle) on.idle = {};
|
|
10999
|
+
if (Object.keys(on).length) clean.on = on;
|
|
11000
|
+
const jobs = {};
|
|
11001
|
+
for (const [id, job] of Object.entries(wf.jobs || {})) {
|
|
11002
|
+
jobs[id] = { steps: (job.steps || []).map((s) => s.name ? { name: s.name, run: s.run } : { run: s.run }) };
|
|
11003
|
+
}
|
|
11004
|
+
clean.jobs = Object.keys(jobs).length ? jobs : { run: { steps: [] } };
|
|
11005
|
+
return stringify(clean);
|
|
11006
|
+
}
|
|
11007
|
+
function listWorkflows(projectRoot) {
|
|
11008
|
+
const dir = workflowsDir(projectRoot);
|
|
11009
|
+
if (!existsSync(dir)) return [];
|
|
11010
|
+
const out = [];
|
|
11011
|
+
for (const name of readdirSync(dir)) {
|
|
11012
|
+
if (!name.endsWith(".yaml") && !name.endsWith(".yml")) continue;
|
|
11013
|
+
const p = join(dir, name);
|
|
11014
|
+
try {
|
|
11015
|
+
const wf = parseWorkflow(readFileSync(p, "utf-8"));
|
|
11016
|
+
if (wf) {
|
|
11017
|
+
try {
|
|
11018
|
+
const st = statSync(p);
|
|
11019
|
+
wf.created = new Date(st.birthtimeMs || st.mtimeMs).toISOString();
|
|
11020
|
+
} catch {
|
|
11021
|
+
}
|
|
11022
|
+
out.push(wf);
|
|
11023
|
+
}
|
|
11024
|
+
} catch {
|
|
11025
|
+
}
|
|
11026
|
+
}
|
|
11027
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
11028
|
+
}
|
|
11029
|
+
function getWorkflow(projectRoot, name) {
|
|
11030
|
+
const p = workflowPath(projectRoot, name);
|
|
11031
|
+
if (!existsSync(p)) return null;
|
|
11032
|
+
try {
|
|
11033
|
+
return parseWorkflow(readFileSync(p, "utf-8"));
|
|
11034
|
+
} catch {
|
|
11035
|
+
return null;
|
|
11036
|
+
}
|
|
11037
|
+
}
|
|
11038
|
+
function rawWorkflow(projectRoot, name) {
|
|
11039
|
+
const p = workflowPath(projectRoot, name);
|
|
11040
|
+
return existsSync(p) ? readFileSync(p, "utf-8") : null;
|
|
11041
|
+
}
|
|
11042
|
+
function saveWorkflow(projectRoot, wf) {
|
|
11043
|
+
const dir = workflowsDir(projectRoot);
|
|
11044
|
+
mkdirSync(dir, { recursive: true });
|
|
11045
|
+
const path = workflowPath(projectRoot, wf.name);
|
|
11046
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
11047
|
+
writeFileSync(tmp, serializeWorkflow(wf));
|
|
11048
|
+
renameSync(tmp, path);
|
|
11049
|
+
}
|
|
11050
|
+
function setWorkflowEnabled(projectRoot, name, enabled) {
|
|
11051
|
+
const wf = getWorkflow(projectRoot, name);
|
|
11052
|
+
if (!wf) return null;
|
|
11053
|
+
const next = { ...wf };
|
|
11054
|
+
if (enabled) delete next.enabled;
|
|
11055
|
+
else next.enabled = false;
|
|
11056
|
+
saveWorkflow(projectRoot, next);
|
|
11057
|
+
return next;
|
|
11058
|
+
}
|
|
11059
|
+
function removeWorkflow(projectRoot, name) {
|
|
11060
|
+
const p = workflowPath(projectRoot, name);
|
|
11061
|
+
if (!existsSync(p)) return false;
|
|
11062
|
+
try {
|
|
11063
|
+
unlinkSync(p);
|
|
11064
|
+
return true;
|
|
11065
|
+
} catch {
|
|
11066
|
+
return false;
|
|
11067
|
+
}
|
|
11068
|
+
}
|
|
11069
|
+
|
|
11070
|
+
const MAX_RUNS_PER_WORKFLOW = 50;
|
|
11071
|
+
function maxRunsPerWorkflow() {
|
|
11072
|
+
const raw = Number(process.env.SVAMP_MAX_RUNS_PER_WORKFLOW);
|
|
11073
|
+
if (Number.isFinite(raw) && raw > 0) return Math.min(Math.floor(raw), 5e3);
|
|
11074
|
+
return MAX_RUNS_PER_WORKFLOW;
|
|
11075
|
+
}
|
|
11076
|
+
const MAX_STREAM_CHARS = 8e3;
|
|
11077
|
+
const STEP_TIMEOUT_MS = 12e4;
|
|
11078
|
+
function capStream(s, max = MAX_STREAM_CHARS) {
|
|
11079
|
+
if (s.length <= max) return s;
|
|
11080
|
+
const head = Math.floor(max * 0.6);
|
|
11081
|
+
const tail = max - head;
|
|
11082
|
+
const omitted = s.length - max;
|
|
11083
|
+
return `${s.slice(0, head)}
|
|
11084
|
+
\u2026 [${omitted} chars omitted] \u2026
|
|
11085
|
+
${s.slice(s.length - tail)}`;
|
|
11086
|
+
}
|
|
11087
|
+
function runsDir(projectRoot) {
|
|
11088
|
+
return join(workflowsDir(projectRoot), ".runs");
|
|
11089
|
+
}
|
|
11090
|
+
function runsFile(projectRoot, workflow) {
|
|
11091
|
+
const safe = workflow.replace(/[^\w.-]+/g, "_");
|
|
11092
|
+
return join(runsDir(projectRoot), `${safe}.jsonl`);
|
|
11093
|
+
}
|
|
11094
|
+
function shortRunId(seed) {
|
|
11095
|
+
let h = 2166136261;
|
|
11096
|
+
for (let i = 0; i < seed.length; i++) {
|
|
11097
|
+
h ^= seed.charCodeAt(i);
|
|
11098
|
+
h = Math.imul(h, 16777619);
|
|
11099
|
+
}
|
|
11100
|
+
return (h >>> 0).toString(36).padStart(7, "0").slice(0, 7);
|
|
11101
|
+
}
|
|
11102
|
+
function recordRun(projectRoot, run) {
|
|
11103
|
+
try {
|
|
11104
|
+
const dir = runsDir(projectRoot);
|
|
11105
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
11106
|
+
const file = runsFile(projectRoot, run.workflow);
|
|
11107
|
+
const existing = loadRuns(projectRoot, run.workflow);
|
|
11108
|
+
const idx = existing.findIndex((r) => r.id === run.id);
|
|
11109
|
+
if (idx >= 0) existing[idx] = run;
|
|
11110
|
+
else existing.push(run);
|
|
11111
|
+
const kept = existing.slice(-maxRunsPerWorkflow());
|
|
11112
|
+
const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
11113
|
+
writeFileSync(tmp, kept.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
11114
|
+
renameSync(tmp, file);
|
|
11115
|
+
} catch {
|
|
11116
|
+
}
|
|
11117
|
+
}
|
|
11118
|
+
function loadRuns(projectRoot, workflow) {
|
|
11119
|
+
try {
|
|
11120
|
+
const file = runsFile(projectRoot, workflow);
|
|
11121
|
+
if (!existsSync(file)) return [];
|
|
11122
|
+
return readFileSync(file, "utf-8").split("\n").filter(Boolean).map((l) => {
|
|
11123
|
+
try {
|
|
11124
|
+
return JSON.parse(l);
|
|
11125
|
+
} catch {
|
|
11126
|
+
return null;
|
|
11127
|
+
}
|
|
11128
|
+
}).filter((r) => !!r);
|
|
11129
|
+
} catch {
|
|
11130
|
+
return [];
|
|
11131
|
+
}
|
|
11132
|
+
}
|
|
11133
|
+
function listRuns(projectRoot, workflow, opts = {}) {
|
|
11134
|
+
const o = typeof opts === "number" ? { limit: opts } : opts;
|
|
11135
|
+
const all = loadRuns(projectRoot, workflow);
|
|
11136
|
+
const total = all.length;
|
|
11137
|
+
const newestFirst = all.slice().reverse();
|
|
11138
|
+
const offset = Math.max(0, Math.floor(o.offset ?? 0));
|
|
11139
|
+
const limit = o.limit != null && o.limit > 0 ? Math.floor(o.limit) : total;
|
|
11140
|
+
const page = newestFirst.slice(offset, offset + limit).map((r) => {
|
|
11141
|
+
const failedStep = r.steps.findIndex((s) => s.exitCode !== 0 && s.exitCode !== null || s.timedOut);
|
|
11142
|
+
const { steps, ...rest } = r;
|
|
11143
|
+
return { ...rest, stepCount: steps.length, failedStep: failedStep >= 0 ? failedStep : void 0 };
|
|
11144
|
+
});
|
|
11145
|
+
return { runs: page, total, offset, hasMore: offset + page.length < total };
|
|
11146
|
+
}
|
|
11147
|
+
function getRun(projectRoot, workflow, runId) {
|
|
11148
|
+
return loadRuns(projectRoot, workflow).find((r) => r.id === runId) || null;
|
|
11149
|
+
}
|
|
11150
|
+
function defaultExecStep(root, cmd, env) {
|
|
11151
|
+
return new Promise((resolve) => {
|
|
11152
|
+
let stdout = "", stderr = "", timedOut = false, settled = false;
|
|
11153
|
+
const finish = (exitCode) => {
|
|
11154
|
+
if (settled) return;
|
|
11155
|
+
settled = true;
|
|
11156
|
+
resolve({ exitCode, stdout, stderr, timedOut });
|
|
11157
|
+
};
|
|
11158
|
+
try {
|
|
11159
|
+
const child = spawn("sh", ["-c", cmd], { cwd: root, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
11160
|
+
const timer = setTimeout(() => {
|
|
11161
|
+
timedOut = true;
|
|
11162
|
+
try {
|
|
11163
|
+
child.kill("SIGKILL");
|
|
11164
|
+
} catch {
|
|
11165
|
+
}
|
|
11166
|
+
}, STEP_TIMEOUT_MS);
|
|
11167
|
+
child.stdout?.on("data", (d) => {
|
|
11168
|
+
stdout += d.toString();
|
|
11169
|
+
if (stdout.length > MAX_STREAM_CHARS * 4) stdout = capStream(stdout);
|
|
11170
|
+
});
|
|
11171
|
+
child.stderr?.on("data", (d) => {
|
|
11172
|
+
stderr += d.toString();
|
|
11173
|
+
if (stderr.length > MAX_STREAM_CHARS * 4) stderr = capStream(stderr);
|
|
11174
|
+
});
|
|
11175
|
+
child.on("error", (e) => {
|
|
11176
|
+
clearTimeout(timer);
|
|
11177
|
+
stderr += `
|
|
11178
|
+
[spawn error] ${e.message}`;
|
|
11179
|
+
finish(null);
|
|
11180
|
+
});
|
|
11181
|
+
child.on("close", (code) => {
|
|
11182
|
+
clearTimeout(timer);
|
|
11183
|
+
finish(code);
|
|
11184
|
+
});
|
|
11185
|
+
} catch (e) {
|
|
11186
|
+
stderr += `[spawn threw] ${e?.message || e}`;
|
|
11187
|
+
finish(null);
|
|
11188
|
+
}
|
|
11189
|
+
});
|
|
11190
|
+
}
|
|
11191
|
+
function escalateWorkflowFailure(projectRoot, wf, run) {
|
|
11192
|
+
try {
|
|
11193
|
+
if (process.env.SVAMP_WORKFLOW_ESCALATE === "0") return void 0;
|
|
11194
|
+
const failLabel = `wf-fail:${wf.name}`;
|
|
11195
|
+
const open = listIssues(projectRoot, { label: failLabel }).filter((i) => i.status !== "archived");
|
|
11196
|
+
if (open.length > 0) return void 0;
|
|
11197
|
+
const failed = run.steps.find((s) => s.timedOut || s.exitCode !== 0 && s.exitCode !== null) || run.steps[run.steps.length - 1];
|
|
11198
|
+
const failCause = run.error ? `runner error: ${run.error}` : failed?.timedOut ? `step timed out after ${STEP_TIMEOUT_MS / 1e3}s` : `step exited with code ${failed?.exitCode ?? "null (killed/signal)"}`;
|
|
11199
|
+
const defLines = workflowSteps(wf).map((s, i) => ` ${i}. ${s.run}`).join("\n") || " (no steps)";
|
|
11200
|
+
const stderrTail = failed?.stderr ? capStream(failed.stderr, 3e3) : "(none)";
|
|
11201
|
+
const stdoutTail = failed?.stdout ? capStream(failed.stdout, 1500) : "(none)";
|
|
11202
|
+
const body = [
|
|
11203
|
+
`Workflow **${wf.name}** failed on its last run (${run.trigger} trigger${run.triggerContext ? ` \xB7 ${run.triggerContext}` : ""}). Diagnose the failure and **correct the workflow definition** so future runs succeed \u2014 edit it with \`svamp workflow\` (see \`svamp workflow --help\`), or pause/remove it if it's obsolete.`,
|
|
11204
|
+
"",
|
|
11205
|
+
"## Failure",
|
|
11206
|
+
`- Cause: ${failCause}`,
|
|
11207
|
+
`- Failed step: ${failed ? `#${failed.index} \u2014 \`${failed.cmd}\`` : "(unknown)"}`,
|
|
11208
|
+
`- Run id: ${run.id} \xB7 finished: ${run.finishedAt || "?"}`,
|
|
11209
|
+
"",
|
|
11210
|
+
"## Workflow definition",
|
|
11211
|
+
"```",
|
|
11212
|
+
`name: ${wf.name}`,
|
|
11213
|
+
`steps:`,
|
|
11214
|
+
defLines,
|
|
11215
|
+
"```",
|
|
11216
|
+
"",
|
|
11217
|
+
"## Failed step stderr (tail)",
|
|
11218
|
+
"```",
|
|
11219
|
+
stderrTail,
|
|
11220
|
+
"```",
|
|
11221
|
+
"",
|
|
11222
|
+
"## Failed step stdout (tail)",
|
|
11223
|
+
"```",
|
|
11224
|
+
stdoutTail,
|
|
11225
|
+
"```"
|
|
11226
|
+
].join("\n");
|
|
11227
|
+
const issue = addIssue(projectRoot, {
|
|
11228
|
+
title: `Fix failing workflow "${wf.name}" (${failCause})`,
|
|
11229
|
+
labels: ["workflow", "automation", "auto-escalated", failLabel],
|
|
11230
|
+
verify: { type: "agent", text: `The workflow "${wf.name}" is corrected (or removed/paused) so it no longer fails with: ${failCause}` },
|
|
11231
|
+
scope: wf.session ? "session" : "project",
|
|
11232
|
+
session: wf.session ?? null,
|
|
11233
|
+
triaged: true,
|
|
11234
|
+
body
|
|
11235
|
+
});
|
|
11236
|
+
return issue.id;
|
|
11237
|
+
} catch {
|
|
11238
|
+
return void 0;
|
|
11239
|
+
}
|
|
11240
|
+
}
|
|
11241
|
+
async function runWorkflow(projectRoot, wf, opts) {
|
|
11242
|
+
const now = opts.now || (() => {
|
|
11243
|
+
const d = /* @__PURE__ */ new Date();
|
|
11244
|
+
return { iso: d.toISOString(), ms: d.getTime() };
|
|
11245
|
+
});
|
|
11246
|
+
const exec = opts.execStep || defaultExecStep;
|
|
11247
|
+
const start = now();
|
|
11248
|
+
const id = shortRunId(`${wf.name}:${start.iso}:${start.ms}`);
|
|
11249
|
+
const env = wf.session ? { ...process.env, SVAMP_SESSION_ID: wf.session } : process.env;
|
|
11250
|
+
const run = {
|
|
11251
|
+
id,
|
|
11252
|
+
workflow: wf.name,
|
|
11253
|
+
trigger: opts.trigger,
|
|
11254
|
+
triggerContext: opts.triggerContext,
|
|
11255
|
+
actor: opts.actor ?? wf.session ?? null,
|
|
11256
|
+
startedAt: start.iso,
|
|
11257
|
+
status: "running",
|
|
11258
|
+
steps: []
|
|
11259
|
+
};
|
|
11260
|
+
recordRun(projectRoot, run);
|
|
11261
|
+
const steps = workflowSteps(wf);
|
|
11262
|
+
try {
|
|
11263
|
+
for (let i = 0; i < steps.length; i++) {
|
|
11264
|
+
const s0 = now();
|
|
11265
|
+
const r = await exec(projectRoot, steps[i].run, env);
|
|
11266
|
+
const s1 = now();
|
|
11267
|
+
run.steps.push({
|
|
11268
|
+
index: i,
|
|
11269
|
+
cmd: steps[i].run,
|
|
11270
|
+
exitCode: r.exitCode,
|
|
11271
|
+
stdout: capStream(r.stdout),
|
|
11272
|
+
stderr: capStream(r.stderr),
|
|
11273
|
+
durationMs: s1.ms - s0.ms,
|
|
11274
|
+
timedOut: r.timedOut || void 0
|
|
11275
|
+
});
|
|
11276
|
+
if (r.timedOut || r.exitCode !== 0) {
|
|
11277
|
+
run.status = "failed";
|
|
11278
|
+
break;
|
|
11279
|
+
}
|
|
11280
|
+
}
|
|
11281
|
+
if (run.status === "running") run.status = "success";
|
|
11282
|
+
} catch (e) {
|
|
11283
|
+
run.status = "failed";
|
|
11284
|
+
run.error = e?.message || String(e);
|
|
11285
|
+
}
|
|
11286
|
+
const end = now();
|
|
11287
|
+
run.finishedAt = end.iso;
|
|
11288
|
+
run.durationMs = end.ms - start.ms;
|
|
11289
|
+
if (run.status === "failed") {
|
|
11290
|
+
const escalated = escalateWorkflowFailure(projectRoot, wf, run);
|
|
11291
|
+
if (escalated) {
|
|
11292
|
+
run.escalatedIssue = escalated;
|
|
11293
|
+
opts.log?.(`[workflow] run ${id} "${wf.name}" failed \u2192 escalated to issue #${escalated}`);
|
|
11294
|
+
}
|
|
11295
|
+
}
|
|
11296
|
+
recordRun(projectRoot, run);
|
|
11297
|
+
opts.log?.(`[workflow] run ${id} "${wf.name}" \u2192 ${run.status} (${run.steps.length} step${run.steps.length === 1 ? "" : "s"})`);
|
|
11298
|
+
return run;
|
|
11299
|
+
}
|
|
11300
|
+
|
|
11301
|
+
const DEFAULT_IDLE_COOLDOWN_MS = 15e3;
|
|
11302
|
+
function idleCooldownMs() {
|
|
11303
|
+
const raw = Number(process.env.SVAMP_WORKFLOW_IDLE_COOLDOWN_MS);
|
|
11304
|
+
if (Number.isFinite(raw) && raw >= 0) return Math.floor(raw);
|
|
11305
|
+
return DEFAULT_IDLE_COOLDOWN_MS;
|
|
11306
|
+
}
|
|
11307
|
+
function shouldFireIdle(prevActive, event, lastFireMs, now, cooldownMs) {
|
|
11308
|
+
if (prevActive !== true) return false;
|
|
11309
|
+
if (event.active !== false) return false;
|
|
11310
|
+
if (lastFireMs != null && now - lastFireMs < cooldownMs) return false;
|
|
11311
|
+
return true;
|
|
11312
|
+
}
|
|
11313
|
+
class IdleTriggerTracker {
|
|
11314
|
+
constructor(cooldownMs = idleCooldownMs()) {
|
|
11315
|
+
this.cooldownMs = cooldownMs;
|
|
11316
|
+
}
|
|
11317
|
+
prevActive = /* @__PURE__ */ new Map();
|
|
11318
|
+
lastFire = /* @__PURE__ */ new Map();
|
|
11319
|
+
observe(event, now) {
|
|
11320
|
+
if (!event || event.type !== "activity" || !event.sessionId || typeof event.active !== "boolean") return false;
|
|
11321
|
+
const sid = event.sessionId;
|
|
11322
|
+
const fire = shouldFireIdle(this.prevActive.get(sid), event, this.lastFire.get(sid), now, this.cooldownMs);
|
|
11323
|
+
this.prevActive.set(sid, event.active);
|
|
11324
|
+
if (fire) this.lastFire.set(sid, now);
|
|
11325
|
+
return fire;
|
|
11326
|
+
}
|
|
11327
|
+
/** Drop tracked state for a session (e.g. when it is archived/removed) so its memory is reclaimed. */
|
|
11328
|
+
forget(sessionId) {
|
|
11329
|
+
this.prevActive.delete(sessionId);
|
|
11330
|
+
this.lastFire.delete(sessionId);
|
|
11331
|
+
}
|
|
11332
|
+
}
|
|
11333
|
+
function fireIdleWorkflows(root, sessionId, deps = {}) {
|
|
11334
|
+
let workflows;
|
|
11335
|
+
try {
|
|
11336
|
+
workflows = listWorkflows(root);
|
|
11337
|
+
} catch {
|
|
11338
|
+
return [];
|
|
11339
|
+
}
|
|
11340
|
+
const matches = workflows.filter((wf) => wf.on?.idle === true && isWorkflowEnabled(wf));
|
|
11341
|
+
return matches.map((wf) => {
|
|
11342
|
+
deps.log?.(`[workflow] idle fired "${wf.name}" in ${root} for session ${sessionId}`);
|
|
11343
|
+
return runWorkflow(root, wf, {
|
|
11344
|
+
trigger: "idle",
|
|
11345
|
+
triggerContext: sessionId,
|
|
11346
|
+
actor: wf.session || null,
|
|
11347
|
+
execStep: deps.execStep,
|
|
11348
|
+
now: deps.now,
|
|
11349
|
+
log: deps.log
|
|
11350
|
+
}).catch((e) => {
|
|
11351
|
+
deps.log?.(`[workflow] idle run threw for "${wf.name}": ${e?.message || e}`);
|
|
11352
|
+
return void 0;
|
|
11353
|
+
});
|
|
11354
|
+
});
|
|
11355
|
+
}
|
|
11356
|
+
|
|
10923
11357
|
const UNBOUNDED_LOOP_ITERATIONS = 1e6;
|
|
10924
11358
|
function backlogOraclePending(projectRoot, sessionId) {
|
|
10925
11359
|
const inScope = (i) => !sessionId || isVisibleTo(i, sessionId);
|
|
@@ -11010,30 +11444,84 @@ function tokensInLastHour(ledger, now) {
|
|
|
11010
11444
|
if (!ledger || !Array.isArray(ledger.window)) return 0;
|
|
11011
11445
|
return ledger.window.filter(([ts]) => now - ts < HOUR_MS).reduce((s, [, t]) => s + (t > 0 ? t : 0), 0);
|
|
11012
11446
|
}
|
|
11447
|
+
function isCostCeiling(kind) {
|
|
11448
|
+
return !!kind && kind !== "iteration";
|
|
11449
|
+
}
|
|
11013
11450
|
function checkLoopBudget(ledger, limits, now, startedAt) {
|
|
11014
11451
|
const L = limits || {};
|
|
11015
11452
|
const led = ledger || { turns: 0, tokens: 0, window: [] };
|
|
11016
11453
|
const pos = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
11017
11454
|
if (pos(L.max_iterations) && L.max_iterations < UNBOUNDED_LOOP_ITERATIONS && led.turns >= L.max_iterations) {
|
|
11018
|
-
return { exceeded: true, reason: `iteration budget reached (${led.turns}/${L.max_iterations} turns)
|
|
11455
|
+
return { exceeded: true, reason: `iteration budget reached (${led.turns}/${L.max_iterations} turns)`, kind: "iteration" };
|
|
11019
11456
|
}
|
|
11020
11457
|
if (pos(L.max_runtime_sec) && pos(startedAt)) {
|
|
11021
11458
|
const elapsed = (now - startedAt) / 1e3;
|
|
11022
11459
|
if (elapsed >= L.max_runtime_sec) {
|
|
11023
|
-
return { exceeded: true, reason: `runtime budget reached (${Math.round(elapsed)}s/${L.max_runtime_sec}s)
|
|
11460
|
+
return { exceeded: true, reason: `runtime budget reached (${Math.round(elapsed)}s/${L.max_runtime_sec}s)`, kind: "runtime" };
|
|
11024
11461
|
}
|
|
11025
11462
|
}
|
|
11026
11463
|
if (pos(L.max_tokens) && led.tokens >= L.max_tokens) {
|
|
11027
|
-
return { exceeded: true, reason: `token budget reached (${led.tokens}/${L.max_tokens} tokens)
|
|
11464
|
+
return { exceeded: true, reason: `token budget reached (${led.tokens}/${L.max_tokens} tokens)`, kind: "tokens" };
|
|
11028
11465
|
}
|
|
11029
11466
|
if (pos(L.max_tokens_per_hour)) {
|
|
11030
11467
|
const rate = tokensInLastHour(led, now);
|
|
11031
11468
|
if (rate >= L.max_tokens_per_hour) {
|
|
11032
|
-
return { exceeded: true, reason: `token-rate budget reached (${rate}/${L.max_tokens_per_hour} tokens in the last hour)
|
|
11469
|
+
return { exceeded: true, reason: `token-rate budget reached (${rate}/${L.max_tokens_per_hour} tokens in the last hour)`, kind: "tokens_per_hour" };
|
|
11033
11470
|
}
|
|
11034
11471
|
}
|
|
11035
11472
|
return { exceeded: false };
|
|
11036
11473
|
}
|
|
11474
|
+
const DEFAULT_LOOP_STUCK_CHECKPOINTS = 3;
|
|
11475
|
+
const DEFAULT_LOOP_MAX_AUTORESUMES = 3;
|
|
11476
|
+
const PROGRESS_HISTORY_CAP = 12;
|
|
11477
|
+
function resolveStuckLimit(envValue) {
|
|
11478
|
+
const n = Number(envValue);
|
|
11479
|
+
if (Number.isFinite(n) && n >= 1) return Math.floor(n);
|
|
11480
|
+
return DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
11481
|
+
}
|
|
11482
|
+
function resolveMaxAutoResumes(envValue) {
|
|
11483
|
+
const n = Number(envValue);
|
|
11484
|
+
if (Number.isFinite(n) && n >= 0) return Math.floor(n);
|
|
11485
|
+
return DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
11486
|
+
}
|
|
11487
|
+
function recordProgressMarker(history, marker, cap = PROGRESS_HISTORY_CAP) {
|
|
11488
|
+
const base = Array.isArray(history) ? history : [];
|
|
11489
|
+
const next = [...base, marker];
|
|
11490
|
+
return next.length > cap ? next.slice(next.length - cap) : next;
|
|
11491
|
+
}
|
|
11492
|
+
function assessLoopProgress(history, opts) {
|
|
11493
|
+
const stuckLimit = opts && typeof opts.stuckLimit === "number" && opts.stuckLimit >= 1 ? Math.floor(opts.stuckLimit) : DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
11494
|
+
const h = (Array.isArray(history) ? history : []).filter((m) => m && typeof m.pending === "number" && Number.isFinite(m.pending));
|
|
11495
|
+
if (h.length === 0) return { progressing: true, stuckCheckpoints: 0 };
|
|
11496
|
+
let best = Infinity;
|
|
11497
|
+
let stuck = 0;
|
|
11498
|
+
for (const m of h) {
|
|
11499
|
+
if (m.pending < best) {
|
|
11500
|
+
best = m.pending;
|
|
11501
|
+
stuck = 0;
|
|
11502
|
+
} else {
|
|
11503
|
+
stuck++;
|
|
11504
|
+
}
|
|
11505
|
+
}
|
|
11506
|
+
return { progressing: stuck < stuckLimit, stuckCheckpoints: stuck };
|
|
11507
|
+
}
|
|
11508
|
+
function decideLoopContinuation(input) {
|
|
11509
|
+
if (input.costCeilingBreached) {
|
|
11510
|
+
return { action: "stop", kind: "cost", reason: input.costReason || "cost budget exhausted" };
|
|
11511
|
+
}
|
|
11512
|
+
const stuckLimit = typeof input.stuckLimit === "number" && input.stuckLimit >= 1 ? Math.floor(input.stuckLimit) : DEFAULT_LOOP_STUCK_CHECKPOINTS;
|
|
11513
|
+
const maxAutoResumes = typeof input.maxAutoResumes === "number" && input.maxAutoResumes >= 0 ? Math.floor(input.maxAutoResumes) : DEFAULT_LOOP_MAX_AUTORESUMES;
|
|
11514
|
+
const autoResumes = typeof input.autoResumes === "number" && input.autoResumes > 0 ? Math.floor(input.autoResumes) : 0;
|
|
11515
|
+
const { progressing, stuckCheckpoints } = assessLoopProgress(input.progressHistory, { stuckLimit });
|
|
11516
|
+
if (progressing) {
|
|
11517
|
+
return { action: "continue", kind: "progress", consumeAutoResume: false, reason: `still making progress (${stuckCheckpoints}/${stuckLimit} stuck checkpoints)` };
|
|
11518
|
+
}
|
|
11519
|
+
if (autoResumes < maxAutoResumes) {
|
|
11520
|
+
return { action: "continue", kind: "autoresume", consumeAutoResume: true, reason: `no progress across ${stuckCheckpoints} checkpoints \u2014 auto-resume ${autoResumes + 1}/${maxAutoResumes}` };
|
|
11521
|
+
}
|
|
11522
|
+
const tail = `no progress across ${stuckCheckpoints} checkpoints; ${maxAutoResumes} auto-resume(s) exhausted`;
|
|
11523
|
+
return { action: "stop", kind: "stuck", reason: input.stuckReason ? `${input.stuckReason} \u2014 ${tail}` : tail };
|
|
11524
|
+
}
|
|
11037
11525
|
const DEFAULT_LOOP_EVALUATOR_MODEL = "claude-haiku-4-5-20251001";
|
|
11038
11526
|
function resolveEvaluatorModel(envValue) {
|
|
11039
11527
|
if (envValue === void 0) return DEFAULT_LOOP_EVALUATOR_MODEL;
|
|
@@ -12012,6 +12500,19 @@ function writeGoalLoopState(directory, sessionId, state) {
|
|
|
12012
12500
|
} catch {
|
|
12013
12501
|
}
|
|
12014
12502
|
}
|
|
12503
|
+
function safeBacklogPendingCount(projectRoot, sessionId) {
|
|
12504
|
+
try {
|
|
12505
|
+
return backlogOraclePending(projectRoot, sessionId).length;
|
|
12506
|
+
} catch {
|
|
12507
|
+
return 0;
|
|
12508
|
+
}
|
|
12509
|
+
}
|
|
12510
|
+
function updateLoopProgress(ls, pending, now) {
|
|
12511
|
+
const progress_history = recordProgressMarker(ls?.progress_history, { ts: now, pending });
|
|
12512
|
+
const madeProgress = assessLoopProgress(progress_history).stuckCheckpoints === 0;
|
|
12513
|
+
const prev = typeof ls?.auto_resumes === "number" && ls.auto_resumes > 0 ? ls.auto_resumes : 0;
|
|
12514
|
+
return { progress_history, auto_resumes: madeProgress ? 0 : prev };
|
|
12515
|
+
}
|
|
12015
12516
|
function loopResumeMessage(directory, sessionId) {
|
|
12016
12517
|
try {
|
|
12017
12518
|
const ls = readLoopState(directory, sessionId);
|
|
@@ -12218,7 +12719,14 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12218
12719
|
}
|
|
12219
12720
|
}
|
|
12220
12721
|
if (Array.isArray(config.session_links)) {
|
|
12221
|
-
const links = config.session_links.filter((l) => l && typeof l.url === "string" && l.url.trim()).map((l) => ({
|
|
12722
|
+
const links = config.session_links.filter((l) => l && typeof l.url === "string" && l.url.trim()).map((l) => ({
|
|
12723
|
+
id: String(l.id || ""),
|
|
12724
|
+
label: String(l.label || "View"),
|
|
12725
|
+
url: String(l.url).trim(),
|
|
12726
|
+
...l.icon ? { icon: String(l.icon) } : {},
|
|
12727
|
+
// #0266: carry the service backing ({kind,name}) so the Launch Pad can decommission it.
|
|
12728
|
+
...l.service && typeof l.service.name === "string" && (l.service.kind === "tunnel" || l.service.kind === "process") ? { service: { kind: l.service.kind, name: String(l.service.name) } } : {}
|
|
12729
|
+
}));
|
|
12222
12730
|
const cur = meta.sessionLinks;
|
|
12223
12731
|
if (JSON.stringify(cur || []) !== JSON.stringify(links)) {
|
|
12224
12732
|
setMetadata((m) => ({ ...m, sessionLinks: links }));
|
|
@@ -12270,7 +12778,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12270
12778
|
const gaveUpCfg = readLoopJson(directory, sessionId, "loop.config.json") || {};
|
|
12271
12779
|
const curMax = typeof gaveUpCfg.max_iterations === "number" ? gaveUpCfg.max_iterations : iter;
|
|
12272
12780
|
const nextMax = Math.max(curMax * 2, iter + 10);
|
|
12273
|
-
const msg = s.phase === "done" ? `\u{1F501} Loop complete \u2705 \u2014 finished after ${iter} iteration${plural}.` : `\u{1F501} Loop paused \u{1F6D1} \u2014
|
|
12781
|
+
const msg = s.phase === "done" ? `\u{1F501} Loop complete \u2705 \u2014 finished after ${iter} iteration${plural}.` : `\u{1F501} Loop paused \u{1F6D1} \u2014 did NOT finish after ${iter} iteration${plural}${s.gave_up_reason ? ` (${s.gave_up_reason})` : ""}. Resume & extend with: svamp session loop-resume ${sessionId} --max ${nextMax}`;
|
|
12274
12782
|
sessionService.pushMessage(
|
|
12275
12783
|
s.phase === "gave_up" ? { type: "message", message: msg, level: "warning" } : { type: "message", message: msg },
|
|
12276
12784
|
"event"
|
|
@@ -12403,7 +12911,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12403
12911
|
stripOldLoopHooks(directory);
|
|
12404
12912
|
} catch {
|
|
12405
12913
|
}
|
|
12406
|
-
writeGoalLoopState(directory, sessionId, { active: true, phase: "running", iteration: 0, task: condition, goal_task: task, until, oracle, max_iterations: hardMax, budget, started_at: Date.now(), holds: 0, ledger: { turns: 0, tokens: 0, window: [] } });
|
|
12914
|
+
writeGoalLoopState(directory, sessionId, { active: true, phase: "running", iteration: 0, task: condition, goal_task: task, until, oracle, max_iterations: hardMax, budget, started_at: Date.now(), holds: 0, ledger: { turns: 0, tokens: 0, window: [] }, progress_history: [], auto_resumes: 0 });
|
|
12407
12915
|
const existingQueue = getMetadata().messageQueue || [];
|
|
12408
12916
|
setMetadata((m) => ({ ...m, messageQueue: [...existingQueue, { id: randomUUID$1(), text: `/goal ${condition}`, displayText: `\u{1F3AF} Goal: ${(task || until || "").slice(0, 100)}`, createdAt: Date.now() }] }));
|
|
12409
12917
|
onLoopActivated?.();
|
|
@@ -12851,8 +13359,30 @@ async function startDaemon(options) {
|
|
|
12851
13359
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
12852
13360
|
saveExposedTunnels(list);
|
|
12853
13361
|
}
|
|
13362
|
+
async function reconcileTunnelSessionLinks(sessionId, tunnelName, urls) {
|
|
13363
|
+
if (!sessionId || urls.size === 0) return;
|
|
13364
|
+
try {
|
|
13365
|
+
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
13366
|
+
if (!dir) return;
|
|
13367
|
+
const { reconcileServiceLinks } = await import('./agentCommands-B_ozIVKT.mjs');
|
|
13368
|
+
const configPath = getSvampConfigPath(dir, sessionId);
|
|
13369
|
+
const config = readSvampConfig(configPath);
|
|
13370
|
+
const entries = Array.from(urls.entries());
|
|
13371
|
+
const incoming = entries.map(([port, url]) => ({
|
|
13372
|
+
url: String(url),
|
|
13373
|
+
label: entries.length > 1 ? `${tunnelName}:${port}` : tunnelName,
|
|
13374
|
+
service: { kind: "tunnel", name: tunnelName }
|
|
13375
|
+
}));
|
|
13376
|
+
if (reconcileServiceLinks(config, incoming)) {
|
|
13377
|
+
writeSvampConfig(configPath, config);
|
|
13378
|
+
logger.log(`[exposed-tunnels] Reconciled Launch Pad link(s) for ${tunnelName} \u2192 session ${sessionId}`);
|
|
13379
|
+
}
|
|
13380
|
+
} catch (err) {
|
|
13381
|
+
logger.log(`[exposed-tunnels] Link reconcile failed for ${tunnelName}: ${err?.message || err}`);
|
|
13382
|
+
}
|
|
13383
|
+
}
|
|
12854
13384
|
async function createExposedTunnel(spec) {
|
|
12855
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
13385
|
+
const { FrpcTunnel } = await import('./frpc-CsjEXBuS.mjs');
|
|
12856
13386
|
const tunnel = new FrpcTunnel({
|
|
12857
13387
|
name: spec.name,
|
|
12858
13388
|
ports: spec.ports,
|
|
@@ -12872,7 +13402,7 @@ async function startDaemon(options) {
|
|
|
12872
13402
|
return tunnel;
|
|
12873
13403
|
}
|
|
12874
13404
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
12875
|
-
const { ServeManager } = await import('./serveManager-
|
|
13405
|
+
const { ServeManager } = await import('./serveManager-Di3xrvG2.mjs');
|
|
12876
13406
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
12877
13407
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
12878
13408
|
});
|
|
@@ -13077,6 +13607,22 @@ ${v.guidance ? v.guidance + "\n" : ""}Criteria: ${v.criteria || "(none)"}
|
|
|
13077
13607
|
return taken;
|
|
13078
13608
|
};
|
|
13079
13609
|
let machineServiceRef = null;
|
|
13610
|
+
const idleTriggerTracker = new IdleTriggerTracker();
|
|
13611
|
+
const routeSessionEventForIdle = (update, dir) => {
|
|
13612
|
+
try {
|
|
13613
|
+
if (!update || update.type !== "activity") return;
|
|
13614
|
+
if (!idleTriggerTracker.observe(update, Date.now())) return;
|
|
13615
|
+
let root;
|
|
13616
|
+
try {
|
|
13617
|
+
root = resolveProjectRoot(dir);
|
|
13618
|
+
} catch {
|
|
13619
|
+
return;
|
|
13620
|
+
}
|
|
13621
|
+
fireIdleWorkflows(root, String(update.sessionId), { log: (m) => logger.log(m) });
|
|
13622
|
+
} catch (e) {
|
|
13623
|
+
logger.log(`[workflow] idle trigger error: ${e?.message || e}`);
|
|
13624
|
+
}
|
|
13625
|
+
};
|
|
13080
13626
|
const spawnSession = async (options2) => {
|
|
13081
13627
|
logger.log("Spawning session:", JSON.stringify(options2));
|
|
13082
13628
|
const { directory, approvedNewDirectoryCreation = true, resumeSessionId } = options2;
|
|
@@ -13536,6 +14082,7 @@ ${parts.join("\n")}`);
|
|
|
13536
14082
|
);
|
|
13537
14083
|
const ledger = ls.ledger;
|
|
13538
14084
|
const budget = ls.budget;
|
|
14085
|
+
const progress_history = ls.progress_history;
|
|
13539
14086
|
if (result.action === "rekick") {
|
|
13540
14087
|
const cond = ls.task || "";
|
|
13541
14088
|
writeGoalLoopState(directory, sessionId, {
|
|
@@ -13550,17 +14097,55 @@ ${parts.join("\n")}`);
|
|
|
13550
14097
|
budget,
|
|
13551
14098
|
started_at: startedAt,
|
|
13552
14099
|
holds: holds + 1,
|
|
13553
|
-
ledger
|
|
14100
|
+
ledger,
|
|
14101
|
+
progress_history,
|
|
14102
|
+
auto_resumes: ls.auto_resumes
|
|
13554
14103
|
});
|
|
13555
14104
|
const guidance = result.guidance.replace(/\s+/g, " ").slice(0, 1200);
|
|
13556
14105
|
sessionService.pushMessage({ type: "message", message: `\u{1F501} Loop not done \u2014 independent review: ${result.reason}`, level: "warning" }, "event");
|
|
13557
14106
|
enqueueLoopMessage(`/goal ${cond} \u2014 INDEPENDENT REVIEW FEEDBACK (resolve before declaring done): ${guidance}`, "\u{1F501} Re-verifying loop");
|
|
13558
14107
|
if (!trackedSession.stopped) setTimeout(() => processMessageQueueRef?.(), 200);
|
|
13559
14108
|
} else if (result.action === "gave_up") {
|
|
13560
|
-
|
|
13561
|
-
|
|
14109
|
+
const decision = decideLoopContinuation({
|
|
14110
|
+
costCeilingBreached: false,
|
|
14111
|
+
progressHistory: progress_history || [],
|
|
14112
|
+
stuckLimit: resolveStuckLimit(process.env.SVAMP_LOOP_STUCK_CHECKPOINTS),
|
|
14113
|
+
autoResumes: typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0,
|
|
14114
|
+
maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES),
|
|
14115
|
+
stuckReason: result.reason
|
|
14116
|
+
});
|
|
14117
|
+
if (decision.action === "continue") {
|
|
14118
|
+
const cond = ls.task || "";
|
|
14119
|
+
const nextAuto = (typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0) + (decision.consumeAutoResume ? 1 : 0);
|
|
14120
|
+
writeGoalLoopState(directory, sessionId, {
|
|
14121
|
+
active: true,
|
|
14122
|
+
phase: "continue",
|
|
14123
|
+
iteration: ls.iteration || 0,
|
|
14124
|
+
task: ls.task,
|
|
14125
|
+
goal_task: ls.goal_task,
|
|
14126
|
+
until: ls.until,
|
|
14127
|
+
oracle: oracleCmd,
|
|
14128
|
+
max_iterations: ls.max_iterations,
|
|
14129
|
+
budget,
|
|
14130
|
+
started_at: startedAt,
|
|
14131
|
+
holds,
|
|
14132
|
+
ledger,
|
|
14133
|
+
progress_history,
|
|
14134
|
+
auto_resumes: nextAuto
|
|
14135
|
+
});
|
|
14136
|
+
const label = decision.kind === "autoresume" ? "\u{1F501} Auto-resuming stalled loop" : "\u{1F501} Loop still progressing";
|
|
14137
|
+
sessionService.pushMessage({ type: "message", message: `${label} \u2014 ${decision.reason}` }, "event");
|
|
14138
|
+
logger.log(`[Session ${sessionId}] [loop-persist] ${decision.kind}: ${decision.reason}`);
|
|
14139
|
+
const push = decision.kind === "autoresume" ? `/goal ${cond} \u2014 AUTO-RESUME after an apparent stall (${decision.reason}). Re-verify the remaining work and PUSH IT TO COMPLETION; do not stop early. Prior reviewer note: ${result.reason}` : `/goal ${cond} \u2014 still making progress; KEEP GOING until genuinely done. Prior reviewer note: ${result.reason}`;
|
|
14140
|
+
enqueueLoopMessage(push, label);
|
|
14141
|
+
if (!trackedSession.stopped) setTimeout(() => processMessageQueueRef?.(), 200);
|
|
14142
|
+
} else {
|
|
14143
|
+
writeGoalLoopState(directory, sessionId, { active: false, phase: "gave_up", completed_at: Date.now(), gave_up_reason: decision.reason, holds, ledger, progress_history, auto_resumes: ls.auto_resumes });
|
|
14144
|
+
logger.log(`[Session ${sessionId}] [loop-persist] stuck-stop: ${decision.reason}`);
|
|
14145
|
+
checkSvampConfig?.();
|
|
14146
|
+
}
|
|
13562
14147
|
} else {
|
|
13563
|
-
writeGoalLoopState(directory, sessionId, { active: false, phase: "done", completed_at: Date.now(), holds, ledger });
|
|
14148
|
+
writeGoalLoopState(directory, sessionId, { active: false, phase: "done", completed_at: Date.now(), holds, ledger, progress_history, auto_resumes: ls.auto_resumes });
|
|
13564
14149
|
checkSvampConfig?.();
|
|
13565
14150
|
}
|
|
13566
14151
|
} catch (e) {
|
|
@@ -14029,22 +14614,28 @@ ${parts.join("\n")}`);
|
|
|
14029
14614
|
});
|
|
14030
14615
|
ls.ledger = ledger;
|
|
14031
14616
|
const startedAt = typeof ls.started_at === "number" ? ls.started_at : typeof ls.resumed_at === "number" ? ls.resumed_at : void 0;
|
|
14032
|
-
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
|
|
14033
|
-
|
|
14617
|
+
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
|
|
14618
|
+
const pending = safeBacklogPendingCount(resolveProjectRoot(directory), sessionId);
|
|
14619
|
+
const prog = updateLoopProgress(ls, pending, now);
|
|
14620
|
+
ls.progress_history = prog.progress_history;
|
|
14621
|
+
ls.auto_resumes = prog.auto_resumes;
|
|
14622
|
+
if (budgetCheck.exceeded && isCostCeiling(budgetCheck.kind)) {
|
|
14034
14623
|
writeGoalLoopState(directory, sessionId, {
|
|
14035
14624
|
...ls,
|
|
14036
14625
|
active: false,
|
|
14037
14626
|
phase: "gave_up",
|
|
14038
14627
|
completed_at: now,
|
|
14039
14628
|
gave_up_reason: `resource budget exhausted \u2014 ${budgetCheck.reason}`,
|
|
14040
|
-
ledger
|
|
14629
|
+
ledger,
|
|
14630
|
+
progress_history: prog.progress_history,
|
|
14631
|
+
auto_resumes: prog.auto_resumes
|
|
14041
14632
|
});
|
|
14042
|
-
sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason}. Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
|
|
14633
|
+
sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason} (hard cost ceiling \u2014 will not auto-resume). Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
|
|
14043
14634
|
checkSvampConfig?.();
|
|
14044
14635
|
} else if (process.env.SVAMP_LOOP_VERIFY === "0") {
|
|
14045
|
-
writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "done", completed_at: now, ledger });
|
|
14636
|
+
writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "done", completed_at: now, ledger, progress_history: prog.progress_history, auto_resumes: prog.auto_resumes });
|
|
14046
14637
|
} else {
|
|
14047
|
-
writeGoalLoopState(directory, sessionId, { ...ls, ledger });
|
|
14638
|
+
writeGoalLoopState(directory, sessionId, { ...ls, ledger, progress_history: prog.progress_history, auto_resumes: prog.auto_resumes });
|
|
14048
14639
|
void verifyGoalCompletion(ls);
|
|
14049
14640
|
}
|
|
14050
14641
|
}
|
|
@@ -14897,11 +15488,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14897
15488
|
});
|
|
14898
15489
|
},
|
|
14899
15490
|
onIssue: async (params) => {
|
|
14900
|
-
const { issueRpc } = await import('./rpc-
|
|
15491
|
+
const { issueRpc } = await import('./rpc-C1DI7HfZ.mjs');
|
|
14901
15492
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14902
15493
|
},
|
|
14903
15494
|
onWorkflow: async (params) => {
|
|
14904
|
-
const { workflowRpc } = await import('./rpc-
|
|
15495
|
+
const { workflowRpc } = await import('./rpc-BGFgegj5.mjs');
|
|
14905
15496
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14906
15497
|
},
|
|
14907
15498
|
onRipgrep: async (args, cwd) => {
|
|
@@ -14980,7 +15571,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14980
15571
|
return { success: !!tree, tree };
|
|
14981
15572
|
}
|
|
14982
15573
|
},
|
|
14983
|
-
{ messagesDir: getSessionDir(directory, sessionId), onSessionEvent: (update) =>
|
|
15574
|
+
{ messagesDir: getSessionDir(directory, sessionId), onSessionEvent: (update) => {
|
|
15575
|
+
machineServiceRef?.notifySessionEvent(update);
|
|
15576
|
+
routeSessionEventForIdle(update, directory);
|
|
15577
|
+
} }
|
|
14984
15578
|
);
|
|
14985
15579
|
const svampConfig = createSvampConfigChecker(
|
|
14986
15580
|
directory,
|
|
@@ -15434,11 +16028,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15434
16028
|
});
|
|
15435
16029
|
},
|
|
15436
16030
|
onIssue: async (params) => {
|
|
15437
|
-
const { issueRpc } = await import('./rpc-
|
|
16031
|
+
const { issueRpc } = await import('./rpc-C1DI7HfZ.mjs');
|
|
15438
16032
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
15439
16033
|
},
|
|
15440
16034
|
onWorkflow: async (params) => {
|
|
15441
|
-
const { workflowRpc } = await import('./rpc-
|
|
16035
|
+
const { workflowRpc } = await import('./rpc-BGFgegj5.mjs');
|
|
15442
16036
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
15443
16037
|
},
|
|
15444
16038
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15517,7 +16111,10 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15517
16111
|
return { success: !!tree, tree };
|
|
15518
16112
|
}
|
|
15519
16113
|
},
|
|
15520
|
-
{ messagesDir: getSessionDir(directory, sessionId), onSessionEvent: (update) =>
|
|
16114
|
+
{ messagesDir: getSessionDir(directory, sessionId), onSessionEvent: (update) => {
|
|
16115
|
+
machineServiceRef?.notifySessionEvent(update);
|
|
16116
|
+
routeSessionEventForIdle(update, directory);
|
|
16117
|
+
} }
|
|
15521
16118
|
);
|
|
15522
16119
|
let insideOnTurnEnd = false;
|
|
15523
16120
|
const svampConfigChecker = createSvampConfigChecker(
|
|
@@ -15648,10 +16245,14 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15648
16245
|
return { ok: pending.length === 0, output: pending.length ? `${pending.length} pending: ${pending.map((i) => "#" + i.id).join(" ")}` : "No pending issues." };
|
|
15649
16246
|
};
|
|
15650
16247
|
try {
|
|
15651
|
-
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
|
|
15652
|
-
|
|
16248
|
+
const budgetCheck = process.env.SVAMP_LOOP_BUDGET === "0" ? { exceeded: false, kind: void 0 } : checkLoopBudget(ledger, { max_iterations: ls.max_iterations, ...ls.budget || {} }, now, startedAt);
|
|
16249
|
+
const acpPending = safeBacklogPendingCount(projectRoot, sessionId);
|
|
16250
|
+
const acpProg = updateLoopProgress(ls, acpPending, now);
|
|
16251
|
+
ls.progress_history = acpProg.progress_history;
|
|
16252
|
+
ls.auto_resumes = acpProg.auto_resumes;
|
|
16253
|
+
if (budgetCheck.exceeded && isCostCeiling(budgetCheck.kind)) {
|
|
15653
16254
|
writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "gave_up", completed_at: now, gave_up_reason: `resource budget exhausted \u2014 ${budgetCheck.reason}`, ledger });
|
|
15654
|
-
sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason}. Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
|
|
16255
|
+
sessionService.pushMessage({ type: "message", message: `\u{1F6D1} Loop stopped \u2014 ${budgetCheck.reason} (hard cost ceiling \u2014 will not auto-resume). Resume & extend with: svamp session loop-resume ${sessionId} --max <N>`, level: "warning" }, "event");
|
|
15655
16256
|
checkSvampConfig?.();
|
|
15656
16257
|
return;
|
|
15657
16258
|
}
|
|
@@ -15683,8 +16284,26 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
15683
16284
|
sessionService.pushMessage({ type: "message", message: `\u{1F501} Loop not done \u2014 independent review: ${result.reason}`, level: "warning" }, "event");
|
|
15684
16285
|
dispatchAcpPrompt(`Continue toward the goal: ${ls.goal_task || ls.until || "the task"}. INDEPENDENT REVIEW FEEDBACK (resolve before declaring done): ${guidance}`, "\u{1F501} Re-verifying loop");
|
|
15685
16286
|
} else if (result.action === "gave_up") {
|
|
15686
|
-
|
|
15687
|
-
|
|
16287
|
+
const decision = decideLoopContinuation({
|
|
16288
|
+
costCeilingBreached: false,
|
|
16289
|
+
progressHistory: ls.progress_history || [],
|
|
16290
|
+
stuckLimit: resolveStuckLimit(process.env.SVAMP_LOOP_STUCK_CHECKPOINTS),
|
|
16291
|
+
autoResumes: typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0,
|
|
16292
|
+
maxAutoResumes: resolveMaxAutoResumes(process.env.SVAMP_LOOP_MAX_AUTORESUMES),
|
|
16293
|
+
stuckReason: result.reason
|
|
16294
|
+
});
|
|
16295
|
+
if (decision.action === "continue") {
|
|
16296
|
+
const nextAuto = (typeof ls.auto_resumes === "number" ? ls.auto_resumes : 0) + (decision.consumeAutoResume ? 1 : 0);
|
|
16297
|
+
writeGoalLoopState(directory, sessionId, { ...ls, active: true, phase: "continue", holds, started_at: startedAt, ledger, auto_resumes: nextAuto });
|
|
16298
|
+
const label = decision.kind === "autoresume" ? "\u{1F501} Auto-resuming stalled loop" : "\u{1F501} Loop still progressing";
|
|
16299
|
+
sessionService.pushMessage({ type: "message", message: `${label} \u2014 ${decision.reason}` }, "event");
|
|
16300
|
+
logger.log(`[Session ${sessionId}] [loop-persist] ${decision.kind}: ${decision.reason}`);
|
|
16301
|
+
dispatchAcpPrompt(`Continue toward the goal: ${ls.goal_task || ls.until || "the task"}. ${decision.kind === "autoresume" ? `AUTO-RESUME after an apparent stall (${decision.reason}) \u2014 re-verify the remaining work and PUSH IT TO COMPLETION.` : "Still making progress \u2014 KEEP GOING until genuinely done."} Prior reviewer note: ${result.reason}`, label);
|
|
16302
|
+
} else {
|
|
16303
|
+
writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "gave_up", completed_at: Date.now(), gave_up_reason: decision.reason, holds, ledger });
|
|
16304
|
+
logger.log(`[Session ${sessionId}] [loop-persist] stuck-stop: ${decision.reason}`);
|
|
16305
|
+
checkSvampConfig?.();
|
|
16306
|
+
}
|
|
15688
16307
|
} else {
|
|
15689
16308
|
writeGoalLoopState(directory, sessionId, { ...ls, active: false, phase: "done", completed_at: Date.now(), holds, ledger });
|
|
15690
16309
|
checkSvampConfig?.();
|
|
@@ -16082,6 +16701,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16082
16701
|
const tunnel = await createExposedTunnel(spec);
|
|
16083
16702
|
tunnels.set(spec.name, tunnel);
|
|
16084
16703
|
logger.log(`[exposed-tunnels] Restored: ${spec.name} \u2192 ports ${spec.ports.join(",")}`);
|
|
16704
|
+
if (spec.sessionId) await reconcileTunnelSessionLinks(spec.sessionId, spec.name, tunnel.getUrls());
|
|
16085
16705
|
} catch (err) {
|
|
16086
16706
|
logger.log(`[exposed-tunnels] Failed to restore ${spec.name}: ${err.message}`);
|
|
16087
16707
|
}
|
|
@@ -16370,7 +16990,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16370
16990
|
const PING_TIMEOUT_MS = 15e3;
|
|
16371
16991
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
16372
16992
|
const RECONNECT_JITTER_MS = 2500;
|
|
16373
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
16993
|
+
const { WorkflowScheduler } = await import('./scheduler-Csp21zzg.mjs');
|
|
16374
16994
|
const workflowScheduler = new WorkflowScheduler({
|
|
16375
16995
|
projectRoots: () => {
|
|
16376
16996
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -16954,4 +17574,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
16954
17574
|
writeStopMarker: writeStopMarker
|
|
16955
17575
|
});
|
|
16956
17576
|
|
|
16957
|
-
export {
|
|
17577
|
+
export { downloadSkillFile as $, listRuns as A, getWorkflow as B, runWorkflow as C, setWorkflowEnabled as D, removeWorkflow as E, saveWorkflow as F, rawWorkflow as G, listWorkflows as H, isWorkflowEnabled as I, workflowCrons as J, cronMatches as K, summarize as L, workflowSteps as M, loadMachineContext as N, buildMachineInstructions as O, machineToolsForRole as P, buildMachineTools as Q, READ_ONLY_TOOLS as R, ServeAuth as S, parseFrontmatter as T, getSkillsServer as U, getSkillsWorkspaceName as V, getSkillsCollectionName as W, fetchWithTimeout as X, searchSkills as Y, SKILLS_DIR as Z, getSkillInfo as _, createSessionStore as a, listSkillFiles as a0, resolveModel as a1, formatHandle as a2, normalizeAllowedUser as a3, loadSecurityContextConfig as a4, resolveSecurityContext as a5, buildSecurityContextFromFlags as a6, mergeSecurityContexts as a7, buildSessionShareUrl as a8, computeOutboundHop as a9, registerAwaitingReply as aa, buildMachineShareUrl as ab, parseHandle as ac, handleMatchesMetadata as ad, describeMisconfiguration as ae, buildMachineDeps as af, applyClaudeProxyEnv as ag, composeSessionId as ah, generateFriendlyName as ai, generateHookSettings as aj, claudeAuth as ak, projectInfo as al, DefaultTransport$1 as am, acpBackend as an, acpAgentConfig as ao, codexMcpBackend as ap, GeminiTransport$1 as aq, instanceConfig as ar, api as as, run as at, stopDaemon as b, connectToHypha as c, daemonStatus as d, clearStopMarker as e, stopMarkerExists as f, getHyphaServerUrl$1 as g, getFrpsSubdomainHost as h, getFrpsServerPort as i, getFrpsServerAddr as j, shortId as k, getHyphaServerUrl as l, hasCookieToken as m, resolveProjectRoot as n, getIssue as o, resumeIssue as p, pauseIssue as q, registerMachineService as r, startDaemon as s, addComment as t, updateIssue as u, addIssue as v, listIssues as w, searchIssues as x, isVisibleTo as y, getRun as z };
|