svamp-cli 0.2.272 → 0.2.274

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.
Files changed (26) hide show
  1. package/dist/{agentCommands-Bpx4k5-9.mjs → agentCommands-iom4Qnpt.mjs} +5 -5
  2. package/dist/{auth-6i4iqmQ6.mjs → auth-C5eN8Hte.mjs} +1 -1
  3. package/dist/{cli-D_rlz1U0.mjs → cli-P3Tx1nrm.mjs} +63 -63
  4. package/dist/cli.mjs +2 -2
  5. package/dist/{commands-Coa5AUr5.mjs → commands-B6LByF-g.mjs} +1 -1
  6. package/dist/{commands-DN-ZJc3M.mjs → commands-C7J1hEg3.mjs} +2 -2
  7. package/dist/{commands-C2zF5GQ2.mjs → commands-CKhDT3cQ.mjs} +41 -18
  8. package/dist/{commands-DZvhiRGC.mjs → commands-CQHlltkU.mjs} +2 -2
  9. package/dist/{commands-CginH27R.mjs → commands-CgrbLgCc.mjs} +1 -1
  10. package/dist/{commands-D1H-lJoD.mjs → commands-D606feNR.mjs} +4 -4
  11. package/dist/{commands-jx07BOIK.mjs → commands-VvErX3uk.mjs} +11 -9
  12. package/dist/{fleet-g2e67_Yp.mjs → fleet-B1PDO1l8.mjs} +2 -2
  13. package/dist/{frpc-DosdqHuk.mjs → frpc-Dzhtizmd.mjs} +73 -32
  14. package/dist/{headlessCli-DHJyqLIl.mjs → headlessCli-1WzEZVMV.mjs} +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist/package-BYElq4R_.mjs +64 -0
  17. package/dist/{pinnedClaudeCode-B9O-hKxm.mjs → pinnedClaudeCode-CgEtGkX2.mjs} +1 -1
  18. package/dist/{rpc-CbFNQklQ.mjs → rpc-BMoQLUTg.mjs} +1 -1
  19. package/dist/{rpc-9Zy0N3ge.mjs → rpc-CIs3wZAu.mjs} +1 -1
  20. package/dist/{run-BD6NkC4f.mjs → run-DxoIx34n.mjs} +1 -1
  21. package/dist/{run-DPUt9qXV.mjs → run-l_8UB-9h.mjs} +154 -22
  22. package/dist/{scheduler-9E9vo0X7.mjs → scheduler-uZ__o3N7.mjs} +1 -1
  23. package/dist/{serveCommands-DwjBIACz.mjs → serveCommands-twuwB_gw.mjs} +5 -5
  24. package/dist/{sideband-D-t-OX0C.mjs → sideband-C8myzP2c.mjs} +1 -1
  25. package/package.json +2 -2
  26. package/dist/package-BIPODvcD.mjs +0 -64
@@ -1,8 +1,8 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
- import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-D1H-lJoD.mjs';
4
- import { execSync } from 'node:child_process';
5
- import { u as updateIssue, m as addComment, n as addIssue, i as shortId } from './run-DPUt9qXV.mjs';
3
+ import { connectAndGetMachine, resolveSessionId, createWorktree, connectAndResolveSession } from './commands-D606feNR.mjs';
4
+ import { execFileSync } from 'node:child_process';
5
+ import { u as updateIssue, m as addComment, n as addIssue, i as shortId } from './run-l_8UB-9h.mjs';
6
6
  import 'node:os';
7
7
  import 'os';
8
8
  import 'fs/promises';
@@ -23,10 +23,10 @@ import 'node:util';
23
23
  import 'yaml';
24
24
 
25
25
  function git(cwd, args) {
26
- return execSync(`git ${args}`, { cwd, stdio: "pipe", encoding: "utf-8" }).toString();
26
+ return execFileSync("git", args, { cwd, stdio: "pipe", encoding: "utf-8" }).toString();
27
27
  }
28
28
  function worktreeStatus(worktreePath) {
29
- const raw = git(worktreePath, "status --porcelain").trim();
29
+ const raw = git(worktreePath, ["status", "--porcelain"]).trim();
30
30
  if (!raw) return "";
31
31
  return raw.split("\n").filter((line) => {
32
32
  const p = line.slice(3);
@@ -35,7 +35,7 @@ function worktreeStatus(worktreePath) {
35
35
  }
36
36
  function aheadBehind(worktreePath, baseBranch) {
37
37
  try {
38
- const out = git(worktreePath, `rev-list --left-right --count ${baseBranch}...HEAD`).trim();
38
+ const out = git(worktreePath, ["rev-list", "--left-right", "--count", `${baseBranch}...HEAD`]).trim();
39
39
  const [behind, ahead] = out.split(/\s+/).map((n) => parseInt(n, 10) || 0);
40
40
  return { ahead, behind };
41
41
  } catch {
@@ -44,7 +44,7 @@ function aheadBehind(worktreePath, baseBranch) {
44
44
  }
45
45
  function currentBranch(worktreePath) {
46
46
  try {
47
- return git(worktreePath, "branch --show-current").trim();
47
+ return git(worktreePath, ["branch", "--show-current"]).trim();
48
48
  } catch {
49
49
  return "";
50
50
  }
@@ -83,7 +83,7 @@ ${childStatus}`
83
83
  }
84
84
  let leadStatus;
85
85
  try {
86
- leadStatus = git(projectRoot, "status --porcelain -uno").trim();
86
+ leadStatus = git(projectRoot, ["status", "--porcelain", "-uno"]).trim();
87
87
  } catch (e) {
88
88
  return { ok: false, stage: "verify", detail: `lead tree status failed: ${e.message}` };
89
89
  }
@@ -95,11 +95,11 @@ ${childStatus}`
95
95
  };
96
96
  }
97
97
  try {
98
- git(projectRoot, `merge --no-ff ${branch} -m "crew: merge ${branch} into ${baseBranch}"`);
98
+ git(projectRoot, ["merge", "--no-ff", branch, "-m", `crew: merge ${branch} into ${baseBranch}`]);
99
99
  } catch (e) {
100
100
  const detail = (e.stdout?.toString() || "") + (e.stderr?.toString() || "") || e.message;
101
101
  try {
102
- git(projectRoot, "merge --abort");
102
+ git(projectRoot, ["merge", "--abort"]);
103
103
  } catch {
104
104
  }
105
105
  return { ok: false, stage: "merge", rework: true, detail: `merge conflict \u2014 aborted:
@@ -107,15 +107,15 @@ ${detail.trim()}` };
107
107
  }
108
108
  let cleanupWarning;
109
109
  if (!isMainWorkingTree(projectRoot, worktreePath)) try {
110
- git(projectRoot, `worktree remove --force ${worktreePath}`);
110
+ git(projectRoot, ["worktree", "remove", "--force", worktreePath]);
111
111
  } catch (e) {
112
112
  try {
113
- git(projectRoot, "worktree prune");
113
+ git(projectRoot, ["worktree", "prune"]);
114
114
  } catch {
115
115
  }
116
116
  let stillRegistered = false;
117
117
  try {
118
- stillRegistered = git(projectRoot, "worktree list --porcelain").includes(worktreePath);
118
+ stillRegistered = git(projectRoot, ["worktree", "list", "--porcelain"]).includes(worktreePath);
119
119
  } catch {
120
120
  }
121
121
  if (stillRegistered) {
@@ -125,7 +125,7 @@ ${detail.trim()}` };
125
125
  }
126
126
  if (input.deleteBranch !== false && branch !== baseBranch) {
127
127
  try {
128
- git(projectRoot, `branch -d ${branch}`);
128
+ git(projectRoot, ["branch", "-d", branch]);
129
129
  } catch {
130
130
  }
131
131
  }
@@ -134,7 +134,7 @@ ${detail.trim()}` };
134
134
  function isMainWorkingTree(projectRoot, worktreePath) {
135
135
  if (resolve(worktreePath) === resolve(projectRoot)) return true;
136
136
  try {
137
- const first = git(projectRoot, "worktree list --porcelain").split("\n").find((l) => l.startsWith("worktree "));
137
+ const first = git(projectRoot, ["worktree", "list", "--porcelain"]).split("\n").find((l) => l.startsWith("worktree "));
138
138
  if (first) return resolve(first.slice("worktree ".length).trim()) === resolve(worktreePath);
139
139
  } catch {
140
140
  }
@@ -155,16 +155,16 @@ function removeWorktree(worktreePath) {
155
155
  } catch {
156
156
  }
157
157
  try {
158
- git(projectRoot, `worktree remove --force ${worktreePath}`);
158
+ git(projectRoot, ["worktree", "remove", "--force", worktreePath]);
159
159
  } catch {
160
160
  }
161
161
  try {
162
- git(projectRoot, "worktree prune");
162
+ git(projectRoot, ["worktree", "prune"]);
163
163
  } catch {
164
164
  }
165
165
  if (branch) {
166
166
  try {
167
- git(projectRoot, `branch -D ${branch}`);
167
+ git(projectRoot, ["branch", "-D", branch]);
168
168
  } catch {
169
169
  }
170
170
  }
@@ -217,6 +217,25 @@ async function getMeta(machine, id) {
217
217
  const r = await rpc(machine, id, "getMetadata");
218
218
  return r?.metadata ?? r ?? {};
219
219
  }
220
+ async function waitForChildIdle(machine, childId, timeoutMs = 2e4) {
221
+ const deadline = Date.now() + timeoutMs;
222
+ let idleStreak = 0;
223
+ while (Date.now() < deadline) {
224
+ let thinking = true;
225
+ try {
226
+ const st = await rpc(machine, childId, "getActivityState");
227
+ thinking = !!st?.thinking;
228
+ } catch {
229
+ }
230
+ if (!thinking) {
231
+ if (++idleStreak >= 2) return true;
232
+ } else {
233
+ idleStreak = 0;
234
+ }
235
+ await new Promise((r) => setTimeout(r, 750));
236
+ }
237
+ return false;
238
+ }
220
239
  async function inbox(machine, fromId, toId, body, subject) {
221
240
  await rpc(machine, toId, "sendInboxMessage", {
222
241
  message: {
@@ -568,6 +587,10 @@ async function featureMerge(childPartial, opts = {}) {
568
587
  );
569
588
  } catch {
570
589
  }
590
+ const settled = await waitForChildIdle(machine, childId);
591
+ if (!settled) {
592
+ console.warn(`\u26A0\uFE0F Child ${childId.slice(0, 8)} did not confirm idle within 20s after freeze \u2014 proceeding (mergeBack still refuses to discard uncommitted work).`);
593
+ }
571
594
  const r = mergeBack({ projectRoot, branch, worktreePath, baseBranch: base, deleteBranch: opts.deleteBranch });
572
595
  if (!r.ok) {
573
596
  if (r.rework) {
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-D1H-lJoD.mjs';
3
+ import { connectAndGetMachine } from './commands-D606feNR.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:child_process';
6
6
  import 'node:path';
7
7
  import 'node:os';
8
- import './run-DPUt9qXV.mjs';
8
+ import './run-l_8UB-9h.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,7 +1,7 @@
1
1
  import os from 'os';
2
2
  import fs__default from 'fs';
3
3
  import { resolve, join, relative } from 'path';
4
- import { N as parseFrontmatter, O as getSkillsServer, P as getSkillsWorkspaceName, Q as getSkillsCollectionName, S as fetchWithTimeout, T as searchSkills, U as SKILLS_DIR, V as getSkillInfo, W as downloadSkillFile, X as listSkillFiles } from './run-DPUt9qXV.mjs';
4
+ import { N as parseFrontmatter, O as getSkillsServer, P as getSkillsWorkspaceName, Q as getSkillsCollectionName, S as fetchWithTimeout, T as searchSkills, U as SKILLS_DIR, V as getSkillInfo, W as downloadSkillFile, X as listSkillFiles } from './run-l_8UB-9h.mjs';
5
5
  import 'fs/promises';
6
6
  import 'url';
7
7
  import 'child_process';
@@ -1,8 +1,8 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { execSync } from 'node:child_process';
2
+ import { execSync, execFileSync } from 'node:child_process';
3
3
  import { basename, resolve, join, isAbsolute } from 'node:path';
4
4
  import os from 'node:os';
5
- import { $ as formatHandle, a0 as normalizeAllowedUser, a1 as loadSecurityContextConfig, a2 as resolveSecurityContext, a3 as buildSecurityContextFromFlags, a4 as mergeSecurityContexts, c as connectToHypha, a5 as buildSessionShareUrl, a6 as computeOutboundHop, i as shortId, a7 as registerAwaitingReply, a8 as buildMachineShareUrl, a9 as parseHandle, aa as handleMatchesMetadata } from './run-DPUt9qXV.mjs';
5
+ import { $ as formatHandle, a0 as normalizeAllowedUser, a1 as loadSecurityContextConfig, a2 as resolveSecurityContext, a3 as buildSecurityContextFromFlags, a4 as mergeSecurityContexts, c as connectToHypha, a5 as buildSessionShareUrl, a6 as computeOutboundHop, i as shortId, a7 as registerAwaitingReply, a8 as buildMachineShareUrl, a9 as parseHandle, aa as handleMatchesMetadata } from './run-l_8UB-9h.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -1071,13 +1071,13 @@ function createWorktree(baseDir) {
1071
1071
  const name = generateWorktreeName();
1072
1072
  const relPath = `.dev/worktree/${name}`;
1073
1073
  try {
1074
- execSync(`git worktree add -b ${name} ${relPath}`, { cwd: projectRoot, stdio: "pipe" });
1074
+ execFileSync("git", ["worktree", "add", "-b", name, relPath], { cwd: projectRoot, stdio: "pipe" });
1075
1075
  } catch (err) {
1076
1076
  for (let i = 2; i <= 4; i++) {
1077
1077
  const suffixed = `${name}-${i}`;
1078
1078
  const suffixedPath = `.dev/worktree/${suffixed}`;
1079
1079
  try {
1080
- execSync(`git worktree add -b ${suffixed} ${suffixedPath}`, { cwd: projectRoot, stdio: "pipe" });
1080
+ execFileSync("git", ["worktree", "add", "-b", suffixed, suffixedPath], { cwd: projectRoot, stdio: "pipe" });
1081
1081
  return { path: join(projectRoot, suffixedPath), branch: suffixed };
1082
1082
  } catch {
1083
1083
  continue;
@@ -17,11 +17,13 @@ function getAllFlags(args, flag) {
17
17
  function hasFlag(args, ...flags) {
18
18
  return flags.some((f) => args.includes(f));
19
19
  }
20
+ const VALUELESS_SERVICE_FLAGS = /* @__PURE__ */ new Set(["--foreground", "--no-listing"]);
20
21
  function positionalArgs(args) {
21
22
  const result = [];
22
23
  for (let i = 0; i < args.length; i++) {
23
24
  if (args[i].startsWith("--")) {
24
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) i++;
25
+ const valueless = VALUELESS_SERVICE_FLAGS.has(args[i]) || args[i].startsWith("--no-");
26
+ if (!valueless && i + 1 < args.length && !args[i + 1].startsWith("--")) i++;
25
27
  continue;
26
28
  }
27
29
  result.push(args[i]);
@@ -58,7 +60,7 @@ async function serviceExpose(args) {
58
60
  process.exit(1);
59
61
  }
60
62
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-DosdqHuk.mjs');
63
+ const { runFrpcTunnel } = await import('./frpc-Dzhtizmd.mjs');
62
64
  await runFrpcTunnel(name, ports, void 0, {
63
65
  group,
64
66
  groupKey,
@@ -68,7 +70,7 @@ async function serviceExpose(args) {
68
70
  });
69
71
  return;
70
72
  }
71
- const { connectAndGetMachine } = await import('./commands-D1H-lJoD.mjs');
73
+ const { connectAndGetMachine } = await import('./commands-D606feNR.mjs');
72
74
  const { server, machine } = await connectAndGetMachine();
73
75
  try {
74
76
  const status = await machine.tunnelStart({
@@ -90,7 +92,7 @@ async function serviceExpose(args) {
90
92
  console.log(` port ${port}: ${url}`);
91
93
  }
92
94
  if (process.env.SVAMP_SESSION_ID) {
93
- const { autoAddSessionLink } = await import('./agentCommands-Bpx4k5-9.mjs');
95
+ const { autoAddSessionLink } = await import('./agentCommands-iom4Qnpt.mjs');
94
96
  let added = 0;
95
97
  for (const [port, url] of urlEntries) {
96
98
  const label = urlEntries.length > 1 ? `${name}:${port}` : name;
@@ -127,7 +129,7 @@ async function serviceServe(args) {
127
129
  console.log(`Serving ${resolvedDir}`);
128
130
  const servePort = 18080;
129
131
  const http = await import('http');
130
- const { serveStaticMount } = await import('./run-DPUt9qXV.mjs').then(function (n) { return n.ah; });
132
+ const { serveStaticMount } = await import('./run-l_8UB-9h.mjs').then(function (n) { return n.ah; });
131
133
  const server = http.createServer((req, res) => {
132
134
  const u = new URL(req.url || "/", "http://127.0.0.1");
133
135
  let rel = u.pathname;
@@ -138,7 +140,7 @@ async function serviceServe(args) {
138
140
  server.once("error", reject);
139
141
  server.listen(servePort, "127.0.0.1", () => resolve());
140
142
  });
141
- const { runFrpcTunnel } = await import('./frpc-DosdqHuk.mjs');
143
+ const { runFrpcTunnel } = await import('./frpc-Dzhtizmd.mjs');
142
144
  void server;
143
145
  await runFrpcTunnel(name, [servePort]);
144
146
  } catch (err) {
@@ -148,7 +150,7 @@ async function serviceServe(args) {
148
150
  }
149
151
  async function serviceList(_args) {
150
152
  try {
151
- const { connectAndGetMachine } = await import('./commands-D1H-lJoD.mjs');
153
+ const { connectAndGetMachine } = await import('./commands-D606feNR.mjs');
152
154
  const { server, machine } = await connectAndGetMachine();
153
155
  try {
154
156
  const tunnels = await machine.tunnelList({});
@@ -188,7 +190,7 @@ async function serviceDelete(args) {
188
190
  process.exit(1);
189
191
  }
190
192
  try {
191
- const { connectAndGetMachine } = await import('./commands-D1H-lJoD.mjs');
193
+ const { connectAndGetMachine } = await import('./commands-D606feNR.mjs');
192
194
  const { server, machine } = await connectAndGetMachine();
193
195
  try {
194
196
  await machine.tunnelStop({ name });
@@ -268,4 +270,4 @@ Examples:
268
270
  `.trim());
269
271
  }
270
272
 
271
- export { handleServiceCommand, serviceDelete, serviceExpose, serviceList, serviceServe };
273
+ export { handleServiceCommand, positionalArgs, serviceDelete, serviceExpose, serviceList, serviceServe };
@@ -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-DPUt9qXV.mjs';
5
- import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-B9O-hKxm.mjs';
4
+ import { c as connectToHypha } from './run-l_8UB-9h.mjs';
5
+ import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-CgEtGkX2.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -4,7 +4,7 @@ import { mkdirSync, chmodSync, writeFileSync, unlinkSync, existsSync, readFileSy
4
4
  import { join } from 'path';
5
5
  import { homedir, platform, arch } from 'os';
6
6
  import { randomUUID, createHash } from 'crypto';
7
- import { e as getFrpsSubdomainHost, f as getFrpsServerPort, h as getFrpsServerAddr } from './run-DPUt9qXV.mjs';
7
+ import { e as getFrpsSubdomainHost, f as getFrpsServerPort, h as getFrpsServerAddr } from './run-l_8UB-9h.mjs';
8
8
  import 'fs/promises';
9
9
  import 'url';
10
10
  import 'node:crypto';
@@ -80,6 +80,7 @@ function getFrpcDownloadUrl() {
80
80
  const filename = `frp_${FRP_VERSION}_${osStr}_${archStr}`;
81
81
  return `https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/${filename}.${ext}`;
82
82
  }
83
+ let _frpcDownloadInFlight = null;
83
84
  async function ensureFrpc(log) {
84
85
  if (existsSync(FRPC_BIN)) {
85
86
  try {
@@ -89,16 +90,33 @@ async function ensureFrpc(log) {
89
90
  } catch {
90
91
  (log || console.log)("frpc binary broken or invalid signature, re-downloading...");
91
92
  }
92
- try {
93
- unlinkSync(FRPC_BIN);
94
- } catch {
95
- }
93
+ }
94
+ if (_frpcDownloadInFlight) return _frpcDownloadInFlight;
95
+ _frpcDownloadInFlight = downloadFrpc(log).finally(() => {
96
+ _frpcDownloadInFlight = null;
97
+ });
98
+ return _frpcDownloadInFlight;
99
+ }
100
+ async function downloadFrpc(log) {
101
+ try {
102
+ if (existsSync(FRPC_BIN)) unlinkSync(FRPC_BIN);
103
+ } catch {
96
104
  }
97
105
  const logger = log || console.log;
98
106
  mkdirSync(BIN_DIR, { recursive: true });
99
107
  const url = getFrpcDownloadUrl();
100
108
  logger(`Downloading frpc ${FRP_VERSION} from ${url}...`);
101
- const response = await fetch(url);
109
+ const ctrl = new AbortController();
110
+ const timer = setTimeout(() => ctrl.abort(), 6e4);
111
+ let response;
112
+ try {
113
+ response = await fetch(url, { signal: ctrl.signal });
114
+ } catch (err) {
115
+ const why = err?.name === "AbortError" ? "timed out after 60s" : err?.message ?? String(err);
116
+ throw new Error(`Failed to download frpc from ${url}: ${why}`);
117
+ } finally {
118
+ clearTimeout(timer);
119
+ }
102
120
  if (!response.ok) {
103
121
  throw new Error(`Failed to download frpc: ${response.status} ${response.statusText}`);
104
122
  }
@@ -106,7 +124,7 @@ async function ensureFrpc(log) {
106
124
  if (platform() === "win32") {
107
125
  throw new Error("Windows ZIP extraction not implemented \u2014 please install frpc manually");
108
126
  }
109
- const tmpTar = join(BIN_DIR, `frpc-${FRP_VERSION}.tar.gz`);
127
+ const tmpTar = join(BIN_DIR, `frpc-${FRP_VERSION}-${process.pid}-${Math.random().toString(36).slice(2, 8)}.tar.gz`);
110
128
  writeFileSync(tmpTar, buffer);
111
129
  try {
112
130
  const dirName = `frp_${FRP_VERSION}_${platform() === "darwin" ? "darwin" : "linux"}_${arch() === "x64" ? "amd64" : arch()}`;
@@ -124,18 +142,21 @@ async function ensureFrpc(log) {
124
142
  }
125
143
  return FRPC_BIN;
126
144
  }
145
+ function tomlStr(v) {
146
+ return String(v).replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
147
+ }
127
148
  function generateFrpcConfig(config, proxies, admin) {
128
149
  const useWSS = config.serverPort === 443;
129
150
  const lines = [
130
151
  "# Auto-generated by svamp \u2014 do not edit",
131
- `serverAddr = "${config.serverAddr}"`,
152
+ `serverAddr = "${tomlStr(config.serverAddr)}"`,
132
153
  `serverPort = ${config.serverPort}`,
133
154
  "",
134
155
  'auth.method = "token"',
135
- `auth.token = "${config.authToken}"`,
156
+ `auth.token = "${tomlStr(config.authToken)}"`,
136
157
  "",
137
158
  "# Hypha JWT token for server-side authentication",
138
- ...config.hyphaToken ? [`metadatas.token = "${config.hyphaToken}"`] : [],
159
+ ...config.hyphaToken ? [`metadatas.token = "${tomlStr(config.hyphaToken)}"`] : [],
139
160
  "",
140
161
  "# Transport",
141
162
  ...useWSS ? ['transport.protocol = "wss"'] : [],
@@ -158,32 +179,32 @@ function generateFrpcConfig(config, proxies, admin) {
158
179
  lines.push("# Local admin/status API (loopback only \u2014 polled by the svamp daemon)");
159
180
  lines.push('webServer.addr = "127.0.0.1"');
160
181
  lines.push(`webServer.port = ${admin.port}`);
161
- lines.push(`webServer.user = "${admin.user}"`);
162
- lines.push(`webServer.password = "${admin.password}"`);
182
+ lines.push(`webServer.user = "${tomlStr(admin.user)}"`);
183
+ lines.push(`webServer.password = "${tomlStr(admin.password)}"`);
163
184
  lines.push("");
164
185
  }
165
186
  for (const proxy of proxies) {
166
187
  lines.push(`[[proxies]]`);
167
- lines.push(`name = "${proxy.name}"`);
168
- lines.push(`type = "${proxy.type}"`);
169
- lines.push(`localIP = "${proxy.localIP || "127.0.0.1"}"`);
188
+ lines.push(`name = "${tomlStr(proxy.name)}"`);
189
+ lines.push(`type = "${tomlStr(proxy.type)}"`);
190
+ lines.push(`localIP = "${tomlStr(proxy.localIP || "127.0.0.1")}"`);
170
191
  lines.push(`localPort = ${proxy.localPort}`);
171
192
  if (proxy.subdomain) {
172
- lines.push(`subdomain = "${proxy.subdomain}"`);
193
+ lines.push(`subdomain = "${tomlStr(proxy.subdomain)}"`);
173
194
  }
174
195
  if (proxy.customDomains && proxy.customDomains.length > 0) {
175
- lines.push(`customDomains = [${proxy.customDomains.map((d) => `"${d}"`).join(", ")}]`);
196
+ lines.push(`customDomains = [${proxy.customDomains.map((d) => `"${tomlStr(d)}"`).join(", ")}]`);
176
197
  }
177
198
  if (proxy.group) {
178
- lines.push(`loadBalancer.group = "${proxy.group}"`);
199
+ lines.push(`loadBalancer.group = "${tomlStr(proxy.group)}"`);
179
200
  if (proxy.groupKey) {
180
- lines.push(`loadBalancer.groupKey = "${proxy.groupKey}"`);
201
+ lines.push(`loadBalancer.groupKey = "${tomlStr(proxy.groupKey)}"`);
181
202
  }
182
203
  }
183
204
  if (proxy.healthCheckType) {
184
- lines.push(`healthCheck.type = "${proxy.healthCheckType}"`);
205
+ lines.push(`healthCheck.type = "${tomlStr(proxy.healthCheckType)}"`);
185
206
  if (proxy.healthCheckType === "http" && proxy.healthCheckPath) {
186
- lines.push(`healthCheck.path = "${proxy.healthCheckPath}"`);
207
+ lines.push(`healthCheck.path = "${tomlStr(proxy.healthCheckPath)}"`);
187
208
  }
188
209
  lines.push(`healthCheck.intervalSeconds = ${proxy.healthCheckInterval || 10}`);
189
210
  lines.push(`healthCheck.timeoutSeconds = ${proxy.healthCheckTimeout || 3}`);
@@ -237,6 +258,13 @@ class FrpcTunnel {
237
258
  process = null;
238
259
  _connected = false;
239
260
  _destroyed = false;
261
+ // #0460: in-flight guard. `this.process` is only set AFTER the `await ensureFrpc()`
262
+ // inside connect(), so two connect() calls interleaved across that await (e.g. the
263
+ // auto-restart timer firing while an external caller also connects) both pass the
264
+ // `if (this.process) return` null-check and spawn a duplicate frpc child — the first
265
+ // is overwritten and never killed, leaving a ghost tunnel registration. This flag is
266
+ // set synchronously before the await and makes connect() idempotent across it.
267
+ _connecting = false;
240
268
  configPath;
241
269
  options;
242
270
  serverConfig;
@@ -307,27 +335,40 @@ class FrpcTunnel {
307
335
  async connect() {
308
336
  if (this._destroyed) return;
309
337
  if (this.process) return;
310
- const frpcPath = await ensureFrpc(this.log);
338
+ if (this._connecting) return;
339
+ this._connecting = true;
340
+ let frpcPath;
311
341
  let admin;
312
- if (this.options.adminStatus) {
313
- if (!this._adminPort) {
314
- try {
315
- this._adminPort = await getFreePort();
316
- } catch (err) {
317
- this.log(`admin port alloc failed: ${err?.message ?? err}`);
342
+ try {
343
+ frpcPath = await ensureFrpc(this.log);
344
+ if (this.options.adminStatus) {
345
+ if (!this._adminPort) {
346
+ try {
347
+ this._adminPort = await getFreePort();
348
+ } catch (err) {
349
+ this.log(`admin port alloc failed: ${err?.message ?? err}`);
350
+ }
318
351
  }
352
+ if (this._adminPort) admin = { port: this._adminPort, user: this._adminUser, password: this._adminPassword };
319
353
  }
320
- if (this._adminPort) admin = { port: this._adminPort, user: this._adminUser, password: this._adminPassword };
354
+ const configContent = generateFrpcConfig(this.serverConfig, this.proxies, admin);
355
+ writeFileSync(this.configPath, configContent, { mode: 384 });
356
+ this.log(`Config written to ${this.configPath}`);
357
+ } catch (err) {
358
+ this._connecting = false;
359
+ throw err;
360
+ }
361
+ if (this._destroyed) {
362
+ this._connecting = false;
363
+ return;
321
364
  }
322
- const configContent = generateFrpcConfig(this.serverConfig, this.proxies, admin);
323
- writeFileSync(this.configPath, configContent, { mode: 384 });
324
- this.log(`Config written to ${this.configPath}`);
325
365
  return new Promise((resolve, reject) => {
326
366
  const child = spawn(frpcPath, ["-c", this.configPath], {
327
367
  stdio: ["ignore", "pipe", "pipe"],
328
368
  env: { ...process.env }
329
369
  });
330
370
  this.process = child;
371
+ this._connecting = false;
331
372
  let resolved = false;
332
373
  const onOutput = (data) => {
333
374
  const text = data.toString().trim();
@@ -1,5 +1,5 @@
1
- import { Y as resolveModel, ab as describeMisconfiguration, ac as buildMachineDeps } from './run-DPUt9qXV.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-D-t-OX0C.mjs';
1
+ import { Y as resolveModel, ab as describeMisconfiguration, ac as buildMachineDeps } from './run-l_8UB-9h.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-C8myzP2c.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-DPUt9qXV.mjs';
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-l_8UB-9h.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -0,0 +1,64 @@
1
+ var name = "svamp-cli";
2
+ var version = "0.2.274";
3
+ var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
+ var author = "Amun AI AB";
5
+ var license = "SEE LICENSE IN LICENSE";
6
+ var type = "module";
7
+ var bin = {
8
+ svamp: "./bin/svamp.mjs"
9
+ };
10
+ var files = [
11
+ "dist",
12
+ "bin"
13
+ ];
14
+ var main = "./dist/index.mjs";
15
+ var exports$1 = {
16
+ ".": "./dist/index.mjs",
17
+ "./cli": "./dist/cli.mjs"
18
+ };
19
+ var scripts = {
20
+ build: "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
21
+ typecheck: "tsc --noEmit",
22
+ test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-backend-accounts.mjs && npx tsx test/test-backend-oauth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-inbox-reconcile.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-session-access-fallback.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-app-server.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-compact-detect.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-workflow-idle.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-ws-upgrade.mjs && npx tsx test/test-serve-auth.mjs && npx tsx test/test-static-file-server.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-outbox-reload.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-graceful-restart.mjs && npx tsx test/test-flush-exit.mjs",
23
+ "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
+ dev: "tsx src/cli.ts",
25
+ "dev:daemon": "tsx src/cli.ts daemon start-sync",
26
+ "test:e2e": "node --no-warnings test/e2e-session-tests.mjs",
27
+ "test:frpc": "npx tsx test/test-frpc-e2e.mjs",
28
+ prepublishOnly: "yarn build"
29
+ };
30
+ var dependencies = {
31
+ "@agentclientprotocol/sdk": "^0.14.1",
32
+ "@modelcontextprotocol/sdk": "^1.25.3",
33
+ "hypha-rpc": "0.21.42",
34
+ "node-pty": "1.2.0-beta.11",
35
+ ws: "^8.18.0",
36
+ yaml: "^2.8.2",
37
+ zod: "^3.24.4"
38
+ };
39
+ var devDependencies = {
40
+ "@types/node": ">=20",
41
+ "@types/ws": "^8.5.14",
42
+ pkgroll: "^2.14.2",
43
+ tsx: "^4.20.6",
44
+ typescript: "5.9.3"
45
+ };
46
+ var packageManager = "yarn@1.22.22";
47
+ var _package = {
48
+ name: name,
49
+ version: version,
50
+ description: description,
51
+ author: author,
52
+ license: license,
53
+ type: type,
54
+ bin: bin,
55
+ files: files,
56
+ main: main,
57
+ exports: exports$1,
58
+ scripts: scripts,
59
+ dependencies: dependencies,
60
+ devDependencies: devDependencies,
61
+ packageManager: packageManager
62
+ };
63
+
64
+ export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
@@ -1,7 +1,7 @@
1
1
  import { spawn, exec as exec$1 } from 'node:child_process';
2
2
  import { promisify } from 'node:util';
3
3
 
4
- const PINNED_CLAUDE_CODE_VERSION = "2.1.210";
4
+ const PINNED_CLAUDE_CODE_VERSION = "2.1.214";
5
5
  const exec = promisify(exec$1);
6
6
  let _reconcile = null;
7
7
  function beginClaudeVersionReconcile(log) {
@@ -1,4 +1,4 @@
1
- import { j as resolveProjectRoot, v as getRun, w as listRuns, x as getWorkflow, y as runWorkflow, z as setWorkflowEnabled, A as removeWorkflow, B as saveWorkflow, C as rawWorkflow, D as listWorkflows } from './run-DPUt9qXV.mjs';
1
+ import { j as resolveProjectRoot, v as getRun, w as listRuns, x as getWorkflow, y as runWorkflow, z as setWorkflowEnabled, A as removeWorkflow, B as saveWorkflow, C as rawWorkflow, D as listWorkflows } from './run-l_8UB-9h.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,4 +1,4 @@
1
- import { j as resolveProjectRoot, u as updateIssue, k as getIssue, l as resumeIssue, p as pauseIssue, m as addComment, n as addIssue, o as listIssues, q as searchIssues, t as isVisibleTo } from './run-DPUt9qXV.mjs';
1
+ import { j as resolveProjectRoot, u as updateIssue, k as getIssue, l as resumeIssue, p as pauseIssue, m as addComment, n as addIssue, o as listIssues, q as searchIssues, t as isVisibleTo } from './run-l_8UB-9h.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 { ad as applyClaudeProxyEnv, ae as composeSessionId, af as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, ag as generateHookSettings } from './run-DPUt9qXV.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { ad as applyClaudeProxyEnv, ae as composeSessionId, af as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, ag as generateHookSettings } from './run-l_8UB-9h.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';