svamp-cli 0.2.235 → 0.2.237
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-CQYzhyOV.mjs → agentCommands-48COOhJK.mjs} +5 -5
- package/dist/{auth-DJJDXcNo.mjs → auth-CFykcr-b.mjs} +1 -1
- package/dist/cli.mjs +61 -61
- package/dist/{commands-pUbnaaJb.mjs → commands-B_N_O3m8.mjs} +6 -6
- package/dist/{commands-DjVnwCLk.mjs → commands-C0TWs43Y.mjs} +2 -2
- package/dist/{commands-BrkFOjiN.mjs → commands-C2m5ACt3.mjs} +1 -1
- package/dist/{commands-B9uukn75.mjs → commands-C8Eq1RMw.mjs} +1 -1
- package/dist/{commands-BMCNLXjz.mjs → commands-Cbpf3Qxk.mjs} +1 -1
- package/dist/{commands-BhP_0oWb.mjs → commands-D2MszHoT.mjs} +1 -1
- package/dist/{commands-C8eSHhho.mjs → commands-pJ1kxpWX.mjs} +2 -2
- package/dist/{fleet-DYoCJTed.mjs → fleet-CQfui55H.mjs} +2 -2
- package/dist/{frpc-D3UoKvqr.mjs → frpc-Ba_vkNU8.mjs} +1 -1
- package/dist/{headlessCli-BJt2EMMB.mjs → headlessCli-CMUr-41T.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{package-ChjZJIJr.mjs → package-DyegLVIT.mjs} +1 -1
- package/dist/{pinnedClaudeCode-VIupR1NK.mjs → pinnedClaudeCode-ivV7xAp3.mjs} +15 -1
- package/dist/{rpc-DaRTfCjS.mjs → rpc-CTXWi-2N.mjs} +2 -2
- package/dist/{rpc-DPWxDUve.mjs → rpc-hilaJa6_.mjs} +1 -1
- package/dist/{run-Dt0YoXZ-.mjs → run-C7Zt6jLc.mjs} +1 -1
- package/dist/{run-CVg6eagc.mjs → run-D2CpI5au.mjs} +163 -46
- package/dist/{runStore-DxBjBflT.mjs → runStore-BDgSvZBQ.mjs} +3 -2
- package/dist/{scheduler-CPX7QBP-.mjs → scheduler-C_p2K8SX.mjs} +2 -2
- package/dist/{serveCommands-dQo7QYhs.mjs → serveCommands-BcrXpXod.mjs} +5 -5
- package/dist/{serveManager-TUKxMan6.mjs → serveManager-BzWW7OuZ.mjs} +2 -2
- package/dist/{sideband-Bt0Y2j3D.mjs → sideband-CO3ctHV1.mjs} +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
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-
|
|
5
|
-
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-
|
|
4
|
+
import { c as connectToHypha } from './run-D2CpI5au.mjs';
|
|
5
|
+
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-ivV7xAp3.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -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-D2CpI5au.mjs';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|
|
10
10
|
import 'node:crypto';
|
|
@@ -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-D2CpI5au.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-CO3ctHV1.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
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-D2CpI5au.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -3,6 +3,20 @@ import { promisify } from 'node:util';
|
|
|
3
3
|
|
|
4
4
|
const PINNED_CLAUDE_CODE_VERSION = "2.1.195";
|
|
5
5
|
const exec = promisify(exec$1);
|
|
6
|
+
let _reconcile = null;
|
|
7
|
+
function beginClaudeVersionReconcile(log) {
|
|
8
|
+
if (_reconcile) return;
|
|
9
|
+
_reconcile = ensureClaudeCodeVersion(log).catch((e) => {
|
|
10
|
+
log(`[claude-version] check failed: ${e?.message || e}`);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function awaitClaudeVersionReady(timeoutMs = 6e4) {
|
|
14
|
+
if (!_reconcile) return Promise.resolve();
|
|
15
|
+
return Promise.race([
|
|
16
|
+
_reconcile.then(() => void 0),
|
|
17
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
6
20
|
async function readClaudeVersion() {
|
|
7
21
|
try {
|
|
8
22
|
const { stdout } = await exec("claude --version", { timeout: 1e4 });
|
|
@@ -55,4 +69,4 @@ async function ensureClaudeCodeVersion(log, pinned = PINNED_CLAUDE_CODE_VERSION,
|
|
|
55
69
|
return { kind: "installed", from: current, to: after ?? pinned };
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
export { PINNED_CLAUDE_CODE_VERSION, ensureClaudeCodeVersion };
|
|
72
|
+
export { PINNED_CLAUDE_CODE_VERSION, awaitClaudeVersionReady, beginClaudeVersionReconcile, ensureClaudeCodeVersion };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { m as resolveProjectRoot } from './run-
|
|
1
|
+
import { m as resolveProjectRoot } from './run-D2CpI5au.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-BDgSvZBQ.mjs';
|
|
4
4
|
import 'os';
|
|
5
5
|
import 'fs/promises';
|
|
6
6
|
import 'fs';
|
|
@@ -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-D2CpI5au.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -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-D2CpI5au.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';
|
|
@@ -116,6 +116,12 @@ function resolveRoleLevel(sharing, userEmail) {
|
|
|
116
116
|
}
|
|
117
117
|
return level;
|
|
118
118
|
}
|
|
119
|
+
function roleAtLeast(role, min) {
|
|
120
|
+
if (!role) return false;
|
|
121
|
+
const have = ROLE_HIERARCHY[role];
|
|
122
|
+
const need = ROLE_HIERARCHY[min];
|
|
123
|
+
return typeof have === "number" && typeof need === "number" && have >= need;
|
|
124
|
+
}
|
|
119
125
|
function levelToRole(level) {
|
|
120
126
|
const entry = Object.entries(ROLE_HIERARCHY).find(([, v]) => v === level);
|
|
121
127
|
return entry?.[0] ?? null;
|
|
@@ -1471,7 +1477,7 @@ class ChannelStore {
|
|
|
1471
1477
|
const errs = validateChannel(c);
|
|
1472
1478
|
if (errs.length) throw new Error("invalid channel: " + errs.join("; "));
|
|
1473
1479
|
mkdirSync(this.dir, { recursive: true });
|
|
1474
|
-
const tmp = this._path(c.id)
|
|
1480
|
+
const tmp = `${this._path(c.id)}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
1475
1481
|
writeFileSync(tmp, JSON.stringify(c, null, 2));
|
|
1476
1482
|
renameSync(tmp, this._path(c.id));
|
|
1477
1483
|
return c;
|
|
@@ -1708,11 +1714,13 @@ function renderMessage(channel, { sender = {}, body = {}, query = {}, callId, no
|
|
|
1708
1714
|
|
|
1709
1715
|
const MAX_PER_CHANNEL = 200;
|
|
1710
1716
|
const TTL_MS = 60 * 60 * 1e3;
|
|
1717
|
+
const COMPACT_EVERY = 500;
|
|
1711
1718
|
class ChannelOutbox {
|
|
1712
1719
|
file;
|
|
1713
1720
|
byChannel = /* @__PURE__ */ new Map();
|
|
1714
1721
|
seqByChannel = /* @__PURE__ */ new Map();
|
|
1715
1722
|
emitter = new EventEmitter();
|
|
1723
|
+
appendsSinceCompact = 0;
|
|
1716
1724
|
constructor(projectDir) {
|
|
1717
1725
|
const dir = join(projectDir, ".svamp", "channels");
|
|
1718
1726
|
this.file = join(dir, "_outbox.jsonl");
|
|
@@ -1726,9 +1734,11 @@ class ChannelOutbox {
|
|
|
1726
1734
|
_load() {
|
|
1727
1735
|
if (!existsSync(this.file)) return;
|
|
1728
1736
|
const cutoff = Date.now() - TTL_MS;
|
|
1737
|
+
let rawLines = 0;
|
|
1729
1738
|
try {
|
|
1730
1739
|
for (const line of readFileSync(this.file, "utf8").split("\n")) {
|
|
1731
1740
|
if (!line.trim()) continue;
|
|
1741
|
+
rawLines++;
|
|
1732
1742
|
let rec = null;
|
|
1733
1743
|
try {
|
|
1734
1744
|
rec = JSON.parse(line);
|
|
@@ -1745,6 +1755,25 @@ class ChannelOutbox {
|
|
|
1745
1755
|
for (const [ch, arr] of this.byChannel) if (arr.length > MAX_PER_CHANNEL) this.byChannel.set(ch, arr.slice(-MAX_PER_CHANNEL));
|
|
1746
1756
|
} catch {
|
|
1747
1757
|
}
|
|
1758
|
+
let kept = 0;
|
|
1759
|
+
for (const arr of this.byChannel.values()) kept += arr.length;
|
|
1760
|
+
if (rawLines > kept) this._compact();
|
|
1761
|
+
}
|
|
1762
|
+
/** Rewrite _outbox.jsonl to the current in-memory (TTL/MAX-bounded) window. Best-effort. */
|
|
1763
|
+
_compact() {
|
|
1764
|
+
try {
|
|
1765
|
+
const lines = [];
|
|
1766
|
+
for (const [channelId, arr] of this.byChannel) {
|
|
1767
|
+
this._evict(channelId);
|
|
1768
|
+
for (const reply of this.byChannel.get(channelId) || []) lines.push(JSON.stringify({ channelId, ...reply }));
|
|
1769
|
+
}
|
|
1770
|
+
const dir = join(this.file, "..");
|
|
1771
|
+
mkdirSync(dir, { recursive: true });
|
|
1772
|
+
const tmp = `${this.file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
1773
|
+
writeFileSync(tmp, lines.join("\n") + (lines.length ? "\n" : ""));
|
|
1774
|
+
renameSync(tmp, this.file);
|
|
1775
|
+
} catch {
|
|
1776
|
+
}
|
|
1748
1777
|
}
|
|
1749
1778
|
_evict(channelId) {
|
|
1750
1779
|
const cutoff = Date.now() - TTL_MS;
|
|
@@ -1772,6 +1801,10 @@ class ChannelOutbox {
|
|
|
1772
1801
|
} catch {
|
|
1773
1802
|
}
|
|
1774
1803
|
}
|
|
1804
|
+
if (++this.appendsSinceCompact >= COMPACT_EVERY) {
|
|
1805
|
+
this.appendsSinceCompact = 0;
|
|
1806
|
+
this._compact();
|
|
1807
|
+
}
|
|
1775
1808
|
this.emitter.emit(channelId, reply);
|
|
1776
1809
|
return reply;
|
|
1777
1810
|
}
|
|
@@ -1855,6 +1888,25 @@ function getParamNames(fn) {
|
|
|
1855
1888
|
}
|
|
1856
1889
|
const terminalSessions = /* @__PURE__ */ new Map();
|
|
1857
1890
|
let ptyModule = null;
|
|
1891
|
+
const TERMINAL_IDLE_MS = 30 * 60 * 1e3;
|
|
1892
|
+
let terminalReaper = null;
|
|
1893
|
+
function ensureTerminalReaper() {
|
|
1894
|
+
if (terminalReaper) return;
|
|
1895
|
+
terminalReaper = setInterval(() => {
|
|
1896
|
+
const now = Date.now();
|
|
1897
|
+
for (const [id, s] of terminalSessions) {
|
|
1898
|
+
if (s.exited) continue;
|
|
1899
|
+
if (now - s.lastActivity > TERMINAL_IDLE_MS) {
|
|
1900
|
+
try {
|
|
1901
|
+
s.pty.kill();
|
|
1902
|
+
} catch {
|
|
1903
|
+
}
|
|
1904
|
+
terminalSessions.delete(id);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
}, 6e4);
|
|
1908
|
+
terminalReaper.unref?.();
|
|
1909
|
+
}
|
|
1858
1910
|
async function getPtyModule() {
|
|
1859
1911
|
if (!ptyModule) {
|
|
1860
1912
|
try {
|
|
@@ -2559,9 +2611,11 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2559
2611
|
cols,
|
|
2560
2612
|
rows,
|
|
2561
2613
|
createdAt: Date.now(),
|
|
2614
|
+
lastActivity: Date.now(),
|
|
2562
2615
|
cwd
|
|
2563
2616
|
};
|
|
2564
2617
|
terminalSessions.set(sessionId, session);
|
|
2618
|
+
ensureTerminalReaper();
|
|
2565
2619
|
let outputBatch = "";
|
|
2566
2620
|
let outputTimer = null;
|
|
2567
2621
|
const flushOutput = () => {
|
|
@@ -2584,6 +2638,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2584
2638
|
};
|
|
2585
2639
|
ptyProcess.onData((data) => {
|
|
2586
2640
|
if (!data) return;
|
|
2641
|
+
session.lastActivity = Date.now();
|
|
2587
2642
|
outputBatch += data;
|
|
2588
2643
|
if (!outputTimer) outputTimer = setTimeout(flushOutput, 16);
|
|
2589
2644
|
});
|
|
@@ -2614,6 +2669,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2614
2669
|
if (!session) throw new Error(`Terminal session ${params.sessionId} not found`);
|
|
2615
2670
|
const filtered = filterTerminalResponses(params.data);
|
|
2616
2671
|
if (filtered) session.pty.write(filtered);
|
|
2672
|
+
session.lastActivity = Date.now();
|
|
2617
2673
|
return { success: true };
|
|
2618
2674
|
},
|
|
2619
2675
|
/** Resize a terminal session. */
|
|
@@ -2635,6 +2691,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2635
2691
|
const session = terminalSessions.get(params.sessionId);
|
|
2636
2692
|
if (!session) throw new Error(`Terminal session ${params.sessionId} not found`);
|
|
2637
2693
|
const output = session.outputBuffer.splice(0).join("");
|
|
2694
|
+
session.lastActivity = Date.now();
|
|
2638
2695
|
const result = {
|
|
2639
2696
|
output,
|
|
2640
2697
|
exited: session.exited
|
|
@@ -2934,7 +2991,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2934
2991
|
const tunnels = handlers.tunnels;
|
|
2935
2992
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2936
2993
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2937
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2994
|
+
const { FrpcTunnel } = await import('./frpc-Ba_vkNU8.mjs');
|
|
2938
2995
|
const tunnel = new FrpcTunnel({
|
|
2939
2996
|
name: params.name,
|
|
2940
2997
|
ports: params.ports,
|
|
@@ -3114,8 +3171,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3114
3171
|
// require a signed-in, non-anonymous user so the daemon isn't an open
|
|
3115
3172
|
// OpenAI relay. (No context at all = internal call → allowed.)
|
|
3116
3173
|
wiseCreateEphemeralToken: async (params, context) => {
|
|
3117
|
-
if (
|
|
3118
|
-
return { success: false, error: "
|
|
3174
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) {
|
|
3175
|
+
return { success: false, error: "Not authorized: WISE voice requires interact access to this machine." };
|
|
3119
3176
|
}
|
|
3120
3177
|
const resolved = resolveModel({ provider: "openai" }, process.env);
|
|
3121
3178
|
const misconfig = describeMisconfiguration(resolved);
|
|
@@ -3135,7 +3192,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3135
3192
|
// the realtime WISE voice loop (connected to this session's WISE agent).
|
|
3136
3193
|
// sessionId set = attached to that session; omitted = global.
|
|
3137
3194
|
wiseJoinMeeting: async (params, context) => {
|
|
3138
|
-
if (
|
|
3195
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) return { success: false, error: "Not authorized: launching a meeting agent requires interact access to this machine." };
|
|
3139
3196
|
const parsed = parseMeetingUrl(params?.meetingUrl || "");
|
|
3140
3197
|
if (!parsed) return { success: false, error: "Unrecognized meeting link (supports Zoom, Google Meet, Teams)." };
|
|
3141
3198
|
const base = (process.env.SVAMP_VEXA_API_URL || "").replace(/\/$/, "");
|
|
@@ -3216,7 +3273,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3216
3273
|
}
|
|
3217
3274
|
},
|
|
3218
3275
|
wiseLeaveMeeting: async (params, context) => {
|
|
3219
|
-
if (
|
|
3276
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) return { success: false, error: "Not authorized: managing a meeting agent requires interact access to this machine." };
|
|
3220
3277
|
const k = params?.sessionId || (params?.platform && params?.nativeId ? `global:${params.nativeId}` : "");
|
|
3221
3278
|
const rec = k ? activeMeetingAgents.get(k) : void 0;
|
|
3222
3279
|
const platform = params?.platform || rec?.platform;
|
|
@@ -3233,7 +3290,10 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3233
3290
|
}
|
|
3234
3291
|
},
|
|
3235
3292
|
// Drives the title-bar "WISE in a meeting" indicator (poll per session).
|
|
3236
|
-
wiseMeetingStatus: async (params) => {
|
|
3293
|
+
wiseMeetingStatus: async (params, context) => {
|
|
3294
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "view")) {
|
|
3295
|
+
return { active: false, error: "Not authorized to view meeting status on this machine." };
|
|
3296
|
+
}
|
|
3237
3297
|
if (params?.sessionId) {
|
|
3238
3298
|
const rec = activeMeetingAgents.get(params.sessionId);
|
|
3239
3299
|
let transcript = [];
|
|
@@ -3250,7 +3310,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3250
3310
|
// (running inside its session) calls `svamp wise announce "<update>"` when work
|
|
3251
3311
|
// is done so the room hears about it; this POSTs to the bot's Vexa chat endpoint.
|
|
3252
3312
|
wiseAnnounce: async (params, context) => {
|
|
3253
|
-
if (
|
|
3313
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) return { success: false, error: "Not authorized: announcing into a meeting requires interact access to this machine." };
|
|
3254
3314
|
const text = (params?.text || "").trim();
|
|
3255
3315
|
if (!text) return { success: false, error: "Nothing to announce: provide text." };
|
|
3256
3316
|
let rec = params?.sessionId ? activeMeetingAgents.get(params.sessionId) : void 0;
|
|
@@ -3284,8 +3344,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
3284
3344
|
// which has no key post-Option-A and hits CORS/"Load failed" calling OpenAI direct.
|
|
3285
3345
|
wiseSummarize: async (params, context) => {
|
|
3286
3346
|
trackInbound();
|
|
3287
|
-
if (
|
|
3288
|
-
return { success: false, error: "
|
|
3347
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) {
|
|
3348
|
+
return { success: false, error: "Not authorized: WISE requires interact access to this machine." };
|
|
3289
3349
|
}
|
|
3290
3350
|
const text = (params?.text || "").trim();
|
|
3291
3351
|
if (!text) return { success: true, answer: "(nothing to summarize)" };
|
|
@@ -3336,8 +3396,8 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3336
3396
|
// Option A. The browser uses the returned ephemeral secret for its WebRTC leg.
|
|
3337
3397
|
wiseMintTranscriptionToken: async (params, context) => {
|
|
3338
3398
|
trackInbound();
|
|
3339
|
-
if (
|
|
3340
|
-
return { success: false, error: "
|
|
3399
|
+
if (!roleAtLeast(getEffectiveRole(context, currentMetadata.sharing), "interact")) {
|
|
3400
|
+
return { success: false, error: "Not authorized: dictation requires interact access to this machine." };
|
|
3341
3401
|
}
|
|
3342
3402
|
const resolved = resolveModel({ provider: "openai" }, process.env);
|
|
3343
3403
|
const misconfig = describeMisconfiguration(resolved);
|
|
@@ -3382,7 +3442,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3382
3442
|
}
|
|
3383
3443
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3384
3444
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3385
|
-
const { toolsForRole } = await import('./sideband-
|
|
3445
|
+
const { toolsForRole } = await import('./sideband-CO3ctHV1.mjs');
|
|
3386
3446
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3387
3447
|
return fmt(r2);
|
|
3388
3448
|
}
|
|
@@ -3481,7 +3541,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3481
3541
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3482
3542
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3483
3543
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3484
|
-
const { queryCore } = await import('./commands-
|
|
3544
|
+
const { queryCore } = await import('./commands-Cbpf3Qxk.mjs');
|
|
3485
3545
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3486
3546
|
let result;
|
|
3487
3547
|
try {
|
|
@@ -3651,6 +3711,7 @@ ${d?.error || "not found"}`;
|
|
|
3651
3711
|
});
|
|
3652
3712
|
},
|
|
3653
3713
|
getLastInboundRpcAt: () => lastInboundRpcAt,
|
|
3714
|
+
getCurrentMetadata: () => currentMetadata,
|
|
3654
3715
|
setHandlers: (newHandlers) => {
|
|
3655
3716
|
handlers = newHandlers;
|
|
3656
3717
|
},
|
|
@@ -4579,10 +4640,15 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4579
4640
|
}
|
|
4580
4641
|
}
|
|
4581
4642
|
let forwardContent = content;
|
|
4582
|
-
if (!trusted && parsed && parsed.role === "user" && parsed.content
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4643
|
+
if (!trusted && parsed && parsed.role === "user" && parsed.content != null) {
|
|
4644
|
+
if (typeof parsed.content === "object") {
|
|
4645
|
+
const txt = parsed.content.text;
|
|
4646
|
+
if (typeof txt === "string" && txt.trimStart().startsWith("<svamp-message")) {
|
|
4647
|
+
parsed = { ...parsed, content: { ...parsed.content, text: neutralizeForgedEnvelope(txt) } };
|
|
4648
|
+
forwardContent = JSON.stringify(parsed);
|
|
4649
|
+
}
|
|
4650
|
+
} else if (typeof parsed.content === "string" && parsed.content.trimStart().startsWith("<svamp-message")) {
|
|
4651
|
+
parsed = { ...parsed, content: neutralizeForgedEnvelope(parsed.content) };
|
|
4586
4652
|
forwardContent = JSON.stringify(parsed);
|
|
4587
4653
|
}
|
|
4588
4654
|
}
|
|
@@ -4849,7 +4915,10 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4849
4915
|
if (meetingActivity.length > 80) meetingActivity.splice(0, meetingActivity.length - 80);
|
|
4850
4916
|
return { ok: true };
|
|
4851
4917
|
},
|
|
4852
|
-
getMeetingActivity: async () =>
|
|
4918
|
+
getMeetingActivity: async (context) => {
|
|
4919
|
+
authorizeRequest(context, metadata.sharing, "view");
|
|
4920
|
+
return { activity: meetingActivity.slice(-40) };
|
|
4921
|
+
},
|
|
4853
4922
|
channelDescribe: async (id) => {
|
|
4854
4923
|
const c = channelStore.get(id);
|
|
4855
4924
|
if (!c || c.enabled === false || c.system) return { error: "not found" };
|
|
@@ -6085,19 +6154,23 @@ var projectInfo = /*#__PURE__*/Object.freeze({
|
|
|
6085
6154
|
});
|
|
6086
6155
|
|
|
6087
6156
|
async function registerDebugService(server, machineId, deps) {
|
|
6157
|
+
const authorize = (context, role) => authorizeRequest(context, deps.getSharing?.(), role);
|
|
6088
6158
|
const serviceInfo = await server.registerService(
|
|
6089
6159
|
{
|
|
6090
6160
|
id: `svamp-debug-${machineId}`,
|
|
6091
6161
|
name: `Svamp Debug ${machineId.slice(0, 8)}`,
|
|
6092
6162
|
type: "svamp-debug",
|
|
6093
|
-
|
|
6163
|
+
// require_context: true → Hypha injects the caller context as the last arg of every
|
|
6164
|
+
// method, which the authorize() gate below enforces. 'unlisted' only hides discovery.
|
|
6165
|
+
config: { visibility: "unlisted", require_context: true },
|
|
6094
6166
|
healthCheck: async () => ({
|
|
6095
6167
|
ok: true,
|
|
6096
6168
|
machineId,
|
|
6097
6169
|
uptime: process.uptime(),
|
|
6098
6170
|
timestamp: Date.now()
|
|
6099
6171
|
}),
|
|
6100
|
-
getSessionStates: async () => {
|
|
6172
|
+
getSessionStates: async (context) => {
|
|
6173
|
+
authorize(context, "view");
|
|
6101
6174
|
const sessions = deps.getTrackedSessions();
|
|
6102
6175
|
const states = [];
|
|
6103
6176
|
for (const s of sessions) {
|
|
@@ -6120,27 +6193,33 @@ async function registerDebugService(server, machineId, deps) {
|
|
|
6120
6193
|
}
|
|
6121
6194
|
return states;
|
|
6122
6195
|
},
|
|
6123
|
-
getMachineInfo: async () =>
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6196
|
+
getMachineInfo: async (context) => {
|
|
6197
|
+
authorize(context, "view");
|
|
6198
|
+
return {
|
|
6199
|
+
machineId,
|
|
6200
|
+
hostname: (await import('os')).hostname(),
|
|
6201
|
+
platform: (await import('os')).platform(),
|
|
6202
|
+
uptime: process.uptime(),
|
|
6203
|
+
sessions: deps.getTrackedSessions().length
|
|
6204
|
+
};
|
|
6205
|
+
},
|
|
6130
6206
|
// ── Artifact Sync RPCs ──
|
|
6131
|
-
getArtifactSyncStatus: async () => {
|
|
6207
|
+
getArtifactSyncStatus: async (context) => {
|
|
6208
|
+
authorize(context, "view");
|
|
6132
6209
|
const sync = deps.getArtifactSync?.();
|
|
6133
6210
|
if (!sync) return { error: "Artifact sync not available" };
|
|
6134
6211
|
return sync.getStatus();
|
|
6135
6212
|
},
|
|
6136
|
-
forceSyncAll: async () => {
|
|
6213
|
+
forceSyncAll: async (context) => {
|
|
6214
|
+
authorize(context, "admin");
|
|
6137
6215
|
const sync = deps.getArtifactSync?.();
|
|
6138
6216
|
const sessionsDir = deps.getSessionsDir?.();
|
|
6139
6217
|
if (!sync || !sessionsDir) return { error: "Artifact sync not available" };
|
|
6140
6218
|
await sync.syncAll(sessionsDir, machineId);
|
|
6141
6219
|
return { success: true, status: sync.getStatus() };
|
|
6142
6220
|
},
|
|
6143
|
-
listRemoteSessions: async () => {
|
|
6221
|
+
listRemoteSessions: async (context) => {
|
|
6222
|
+
authorize(context, "view");
|
|
6144
6223
|
const sync = deps.getArtifactSync?.();
|
|
6145
6224
|
if (!sync) return [];
|
|
6146
6225
|
return await sync.listRemoteSessions();
|
|
@@ -10629,8 +10708,22 @@ function addIssue(projectRoot, fields) {
|
|
|
10629
10708
|
closed: null,
|
|
10630
10709
|
body: fields.body
|
|
10631
10710
|
};
|
|
10632
|
-
|
|
10633
|
-
|
|
10711
|
+
mkdirSync(dirname(issuePath(projectRoot, issue.id)), { recursive: true });
|
|
10712
|
+
let n = Number(issue.id);
|
|
10713
|
+
for (let attempt = 0; attempt < 1e4; attempt++) {
|
|
10714
|
+
issue.id = String(n).padStart(4, "0");
|
|
10715
|
+
try {
|
|
10716
|
+
writeFileSync(issuePath(projectRoot, issue.id), serializeIssue(issue), { flag: "wx" });
|
|
10717
|
+
return issue;
|
|
10718
|
+
} catch (err) {
|
|
10719
|
+
if (err && err.code === "EEXIST") {
|
|
10720
|
+
n++;
|
|
10721
|
+
continue;
|
|
10722
|
+
}
|
|
10723
|
+
throw err;
|
|
10724
|
+
}
|
|
10725
|
+
}
|
|
10726
|
+
throw new Error("addIssue: could not allocate a free issue id");
|
|
10634
10727
|
}
|
|
10635
10728
|
function updateIssue(projectRoot, id, patch) {
|
|
10636
10729
|
const cur = getIssue(projectRoot, id);
|
|
@@ -10716,6 +10809,7 @@ function summarize(issues) {
|
|
|
10716
10809
|
return { total: issues.length, ready: by("ready"), in_progress: by("in_progress"), archived: by("archived") };
|
|
10717
10810
|
}
|
|
10718
10811
|
|
|
10812
|
+
const UNBOUNDED_LOOP_ITERATIONS = 1e6;
|
|
10719
10813
|
function backlogOraclePending(projectRoot, sessionId) {
|
|
10720
10814
|
const inScope = (i) => !sessionId || isVisibleTo(i, sessionId);
|
|
10721
10815
|
return listIssues(projectRoot).filter(inScope).filter((i) => i.status === "ready" || i.status === "in_progress").map((i) => ({ id: i.id }));
|
|
@@ -10809,7 +10903,7 @@ function checkLoopBudget(ledger, limits, now, startedAt) {
|
|
|
10809
10903
|
const L = limits || {};
|
|
10810
10904
|
const led = ledger || { turns: 0, tokens: 0, window: [] };
|
|
10811
10905
|
const pos = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
10812
|
-
if (pos(L.max_iterations) && led.turns >= L.max_iterations) {
|
|
10906
|
+
if (pos(L.max_iterations) && L.max_iterations < UNBOUNDED_LOOP_ITERATIONS && led.turns >= L.max_iterations) {
|
|
10813
10907
|
return { exceeded: true, reason: `iteration budget reached (${led.turns}/${L.max_iterations} turns)` };
|
|
10814
10908
|
}
|
|
10815
10909
|
if (pos(L.max_runtime_sec) && pos(startedAt)) {
|
|
@@ -11721,7 +11815,8 @@ function writeSvampConfig(configPath, config) {
|
|
|
11721
11815
|
renameSync$1(tmpPath, configPath);
|
|
11722
11816
|
return content;
|
|
11723
11817
|
}
|
|
11724
|
-
const DEFAULT_LOOP_MAX_ITERATIONS =
|
|
11818
|
+
const DEFAULT_LOOP_MAX_ITERATIONS = UNBOUNDED_LOOP_ITERATIONS;
|
|
11819
|
+
const LOOP_CHECKPOINT_CADENCE = 25;
|
|
11725
11820
|
function getLoopDir(directory, sessionId) {
|
|
11726
11821
|
if (sessionId) {
|
|
11727
11822
|
const scoped = join$1(directory, ".svamp", sessionId, "loop");
|
|
@@ -12100,6 +12195,19 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12100
12195
|
sessionService.pushMessage({ type: "message", message: 'No loop to modify \u2014 start one with: svamp session loop <id> "<task>" --until "<criteria>" --max N', level: "warning" }, "event");
|
|
12101
12196
|
} else {
|
|
12102
12197
|
const newMax = typeof cfg.max_iterations === "number" ? cfg.max_iterations : typeof cfg.max_rounds === "number" ? cfg.max_rounds : void 0;
|
|
12198
|
+
const recomposedTask = (() => {
|
|
12199
|
+
try {
|
|
12200
|
+
const gt = existing.goal_task;
|
|
12201
|
+
const un = existing.until;
|
|
12202
|
+
const or = existing.oracle;
|
|
12203
|
+
const effMax = newMax != null ? newMax : existing.max_iterations;
|
|
12204
|
+
if (typeof effMax === "number" && effMax > 0 && (gt || un)) {
|
|
12205
|
+
return buildGoalCondition({ task: gt, until: un, oracle: or, maxIterations: Math.min(effMax, LOOP_CHECKPOINT_CADENCE) });
|
|
12206
|
+
}
|
|
12207
|
+
} catch {
|
|
12208
|
+
}
|
|
12209
|
+
return void 0;
|
|
12210
|
+
})();
|
|
12103
12211
|
try {
|
|
12104
12212
|
const lcPath = join$1(getLoopDir(directory, sessionId), "loop.config.json");
|
|
12105
12213
|
const lc = readLoopJson(directory, sessionId, "loop.config.json") || {};
|
|
@@ -12120,6 +12228,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12120
12228
|
phase: "continue",
|
|
12121
12229
|
iteration: 0,
|
|
12122
12230
|
...newMax != null ? { max_iterations: newMax } : {},
|
|
12231
|
+
...recomposedTask ? { task: recomposedTask } : {},
|
|
12123
12232
|
...bumpedBudget ? { budget: { ...existing.budget || {}, ...bumpedBudget } } : {},
|
|
12124
12233
|
completed_at: void 0,
|
|
12125
12234
|
gave_up_reason: void 0,
|
|
@@ -12138,6 +12247,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12138
12247
|
if (cur) writeGoalLoopState(directory, sessionId, {
|
|
12139
12248
|
...cur,
|
|
12140
12249
|
...newMax != null ? { max_iterations: newMax } : {},
|
|
12250
|
+
...recomposedTask ? { task: recomposedTask } : {},
|
|
12141
12251
|
...bumpedBudget ? { budget: { ...cur.budget || {}, ...bumpedBudget } } : {}
|
|
12142
12252
|
});
|
|
12143
12253
|
} catch {
|
|
@@ -12151,7 +12261,7 @@ function createSvampConfigChecker(directory, sessionId, getMetadata, setMetadata
|
|
|
12151
12261
|
const explicitMax = typeof cfg.max_iterations === "number" ? cfg.max_iterations : typeof cfg.max_rounds === "number" ? cfg.max_rounds : void 0;
|
|
12152
12262
|
const hasExplicitMax = typeof explicitMax === "number" && explicitMax > 0;
|
|
12153
12263
|
const hardMax = hasExplicitMax ? explicitMax : DEFAULT_LOOP_MAX_ITERATIONS;
|
|
12154
|
-
const softTurnCap =
|
|
12264
|
+
const softTurnCap = Math.min(hardMax, LOOP_CHECKPOINT_CADENCE);
|
|
12155
12265
|
const condition = buildGoalCondition({ task, until, oracle, maxIterations: softTurnCap });
|
|
12156
12266
|
const budget = parseLoopBudget(cfg.budget);
|
|
12157
12267
|
try {
|
|
@@ -12607,7 +12717,7 @@ async function startDaemon(options) {
|
|
|
12607
12717
|
saveExposedTunnels(list);
|
|
12608
12718
|
}
|
|
12609
12719
|
async function createExposedTunnel(spec) {
|
|
12610
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12720
|
+
const { FrpcTunnel } = await import('./frpc-Ba_vkNU8.mjs');
|
|
12611
12721
|
const tunnel = new FrpcTunnel({
|
|
12612
12722
|
name: spec.name,
|
|
12613
12723
|
ports: spec.ports,
|
|
@@ -12627,15 +12737,15 @@ async function startDaemon(options) {
|
|
|
12627
12737
|
return tunnel;
|
|
12628
12738
|
}
|
|
12629
12739
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
12630
|
-
const { ServeManager } = await import('./serveManager-
|
|
12740
|
+
const { ServeManager } = await import('./serveManager-BzWW7OuZ.mjs');
|
|
12631
12741
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
12632
12742
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
12633
12743
|
});
|
|
12634
12744
|
ensureAutoInstalledCommands(logger);
|
|
12635
12745
|
(async () => {
|
|
12636
12746
|
try {
|
|
12637
|
-
const {
|
|
12638
|
-
|
|
12747
|
+
const { beginClaudeVersionReconcile } = await import('./pinnedClaudeCode-ivV7xAp3.mjs');
|
|
12748
|
+
beginClaudeVersionReconcile((msg) => logger.log(msg));
|
|
12639
12749
|
} catch (e) {
|
|
12640
12750
|
logger.log(`[claude-version] check failed: ${e?.message || e}`);
|
|
12641
12751
|
}
|
|
@@ -14636,11 +14746,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14636
14746
|
});
|
|
14637
14747
|
},
|
|
14638
14748
|
onIssue: async (params) => {
|
|
14639
|
-
const { issueRpc } = await import('./rpc-
|
|
14749
|
+
const { issueRpc } = await import('./rpc-hilaJa6_.mjs');
|
|
14640
14750
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14641
14751
|
},
|
|
14642
14752
|
onWorkflow: async (params) => {
|
|
14643
|
-
const { workflowRpc } = await import('./rpc-
|
|
14753
|
+
const { workflowRpc } = await import('./rpc-CTXWi-2N.mjs');
|
|
14644
14754
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14645
14755
|
},
|
|
14646
14756
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15173,11 +15283,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15173
15283
|
});
|
|
15174
15284
|
},
|
|
15175
15285
|
onIssue: async (params) => {
|
|
15176
|
-
const { issueRpc } = await import('./rpc-
|
|
15286
|
+
const { issueRpc } = await import('./rpc-hilaJa6_.mjs');
|
|
15177
15287
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
15178
15288
|
},
|
|
15179
15289
|
onWorkflow: async (params) => {
|
|
15180
|
-
const { workflowRpc } = await import('./rpc-
|
|
15290
|
+
const { workflowRpc } = await import('./rpc-CTXWi-2N.mjs');
|
|
15181
15291
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
15182
15292
|
},
|
|
15183
15293
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15854,8 +15964,10 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
15854
15964
|
return null;
|
|
15855
15965
|
},
|
|
15856
15966
|
getArtifactSync: () => artifactSync,
|
|
15857
|
-
getSessionsDir: () => SVAMP_HOME
|
|
15967
|
+
getSessionsDir: () => SVAMP_HOME,
|
|
15858
15968
|
// Legacy; debug service uses session index now
|
|
15969
|
+
// SECURITY #0207: authorize debug RPCs against the machine's live sharing config.
|
|
15970
|
+
getSharing: () => machineService.getCurrentMetadata().sharing
|
|
15859
15971
|
});
|
|
15860
15972
|
logger.log(`Debug service registered: svamp-debug-${machineId}`);
|
|
15861
15973
|
const supervised = process.env.SVAMP_SUPERVISED === "1";
|
|
@@ -15909,6 +16021,11 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
15909
16021
|
}
|
|
15910
16022
|
}
|
|
15911
16023
|
if (persistedSessions.length > 0) {
|
|
16024
|
+
try {
|
|
16025
|
+
const { awaitClaudeVersionReady } = await import('./pinnedClaudeCode-ivV7xAp3.mjs');
|
|
16026
|
+
await awaitClaudeVersionReady();
|
|
16027
|
+
} catch {
|
|
16028
|
+
}
|
|
15912
16029
|
logger.log(`Restoring ${persistedSessions.length} persisted session(s)...`);
|
|
15913
16030
|
const restoreConcurrency = Math.max(1, parseInt(process.env.SVAMP_RESTORE_CONCURRENCY || "8", 10) || 8);
|
|
15914
16031
|
const restoreOne = async (persisted) => {
|
|
@@ -16047,7 +16164,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
16047
16164
|
const PING_TIMEOUT_MS = 15e3;
|
|
16048
16165
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
16049
16166
|
const RECONNECT_JITTER_MS = 2500;
|
|
16050
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
16167
|
+
const { WorkflowScheduler } = await import('./scheduler-C_p2K8SX.mjs');
|
|
16051
16168
|
const workflowScheduler = new WorkflowScheduler({
|
|
16052
16169
|
projectRoots: () => {
|
|
16053
16170
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { c as workflowSteps, d as workflowsDir } from './store-BTs0H_y0.mjs';
|
|
5
|
-
import { v as listIssues, t as addIssue } from './run-
|
|
6
|
+
import { v as listIssues, t as addIssue } from './run-D2CpI5au.mjs';
|
|
6
7
|
|
|
7
8
|
const MAX_RUNS_PER_WORKFLOW = 50;
|
|
8
9
|
const MAX_STREAM_CHARS = 8e3;
|
|
@@ -41,7 +42,7 @@ function recordRun(projectRoot, run) {
|
|
|
41
42
|
if (idx >= 0) existing[idx] = run;
|
|
42
43
|
else existing.push(run);
|
|
43
44
|
const kept = existing.slice(-MAX_RUNS_PER_WORKFLOW);
|
|
44
|
-
const tmp = `${file}.tmp`;
|
|
45
|
+
const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
45
46
|
writeFileSync(tmp, kept.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
46
47
|
renameSync(tmp, file);
|
|
47
48
|
} catch {
|