svamp-cli 0.2.128 → 0.2.130

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.
@@ -58,7 +58,7 @@ async function serviceExpose(args) {
58
58
  process.exit(1);
59
59
  }
60
60
  if (foreground) {
61
- const { runFrpcTunnel } = await import('./frpc-CWyoLax7.mjs');
61
+ const { runFrpcTunnel } = await import('./frpc-DrfDPPux.mjs');
62
62
  await runFrpcTunnel(name, ports, void 0, {
63
63
  group,
64
64
  groupKey,
@@ -68,7 +68,7 @@ async function serviceExpose(args) {
68
68
  });
69
69
  return;
70
70
  }
71
- const { connectAndGetMachine } = await import('./commands-BVx72l2K.mjs');
71
+ const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
72
72
  const { server, machine } = await connectAndGetMachine();
73
73
  try {
74
74
  const status = await machine.tunnelStart({
@@ -123,7 +123,7 @@ async function serviceServe(args) {
123
123
  };
124
124
  process.on("SIGINT", cleanup);
125
125
  process.on("SIGTERM", cleanup);
126
- const { runFrpcTunnel } = await import('./frpc-CWyoLax7.mjs');
126
+ const { runFrpcTunnel } = await import('./frpc-DrfDPPux.mjs');
127
127
  await runFrpcTunnel(name, [caddyPort]);
128
128
  } catch (err) {
129
129
  console.error(`Error serving directory: ${err.message}`);
@@ -132,18 +132,29 @@ async function serviceServe(args) {
132
132
  }
133
133
  async function serviceList(_args) {
134
134
  try {
135
- const { connectAndGetMachine } = await import('./commands-BVx72l2K.mjs');
135
+ const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
136
136
  const { server, machine } = await connectAndGetMachine();
137
137
  try {
138
138
  const tunnels = await machine.tunnelList({});
139
139
  if (!tunnels || tunnels.length === 0) {
140
- console.log("No active daemon-managed tunnels.");
140
+ console.log("No daemon-managed tunnels configured.");
141
141
  console.log("Standalone foreground tunnels (from `svamp service expose`) are not listed here \u2014 check `pgrep -af frpc`.");
142
142
  return;
143
143
  }
144
- console.log("Active daemon-managed tunnels:");
144
+ const label = (t) => typeof t.state === "string" ? t.state : t.connected ? "connected" : "disconnected";
145
+ const icon = { connected: "\u2713", reconnecting: "\u21BB", failed: "\u2717", disconnected: "\u2717" };
146
+ console.log("Daemon-managed tunnels:");
145
147
  for (const t of tunnels) {
146
- console.log(` ${t.name} \u2014 ${t.connected ? "connected" : "disconnected"}`);
148
+ const st = label(t);
149
+ const ports = Array.isArray(t.ports) && t.ports.length ? ` :${t.ports.join(",")}` : "";
150
+ let extra = "";
151
+ if (st !== "connected") {
152
+ const bits = [];
153
+ if (t.restartAttempts) bits.push(`${t.restartAttempts} restarts`);
154
+ if (t.probe && !t.probe.ok && t.probe.stalenessMs) bits.push(`probe stale ${Math.round(t.probe.stalenessMs / 1e3)}s`);
155
+ if (bits.length) extra = ` (${bits.join(", ")})`;
156
+ }
157
+ console.log(` ${icon[st] ?? "\u2022"} ${t.name}${ports} \u2014 ${st}${extra}`);
147
158
  }
148
159
  } finally {
149
160
  await server.disconnect();
@@ -161,7 +172,7 @@ async function serviceDelete(args) {
161
172
  process.exit(1);
162
173
  }
163
174
  try {
164
- const { connectAndGetMachine } = await import('./commands-BVx72l2K.mjs');
175
+ const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
165
176
  const { server, machine } = await connectAndGetMachine();
166
177
  try {
167
178
  await machine.tunnelStop({ name });
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-BVx72l2K.mjs';
3
+ import { connectAndGetMachine } from './commands-QGaI-ukW.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-C23-A9KM.mjs';
8
+ import './run-DMahGhJP.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,7 +1,7 @@
1
1
  import { execSync, execFileSync } from 'node:child_process';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { createServer } from 'node:http';
4
- import { E as readChecklist, F as compileChecklist, G as RoutineStore, H as RoutineRunner } from './run-C23-A9KM.mjs';
4
+ import { E as readChecklist, F as compileChecklist, G as RoutineStore, H as RoutineRunner } from './run-DMahGhJP.mjs';
5
5
  import 'os';
6
6
  import 'fs/promises';
7
7
  import 'fs';
@@ -104,7 +104,7 @@ Criteria: ${res.criteria || "(none)"}
104
104
  urgency: "normal",
105
105
  hopCount: 1
106
106
  };
107
- const { connectAndGetMachine } = await import('./commands-BVx72l2K.mjs');
107
+ const { connectAndGetMachine } = await import('./commands-QGaI-ukW.mjs');
108
108
  const { server, machine } = await connectAndGetMachine();
109
109
  try {
110
110
  await machine.sessionRPC(reportTo, "sendInboxMessage", { message });
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { basename, resolve, join, isAbsolute } from 'node:path';
4
4
  import os from 'node:os';
5
- import { I as formatHandle, J as normalizeAllowedUser, K as loadSecurityContextConfig, L as resolveSecurityContext, M as buildSecurityContextFromFlags, N as mergeSecurityContexts, c as connectToHypha, O as buildSessionShareUrl, P as computeOutboundHop, m as shortId, Q as buildMachineShareUrl, T as parseHandle, U as handleMatchesMetadata } from './run-C23-A9KM.mjs';
5
+ import { I as formatHandle, J as normalizeAllowedUser, K as loadSecurityContextConfig, L as resolveSecurityContext, M as buildSecurityContextFromFlags, N as mergeSecurityContexts, c as connectToHypha, O as buildSessionShareUrl, P as computeOutboundHop, m as shortId, Q as buildMachineShareUrl, T as parseHandle, U as handleMatchesMetadata } from './run-DMahGhJP.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -2384,23 +2384,27 @@ async function sessionLoopStart(sessionIdPartial, task, machineId, opts) {
2384
2384
  const { server, machine, fullId } = await connectAndResolveSession(sessionIdPartial, machineId);
2385
2385
  try {
2386
2386
  const svc = getSessionProxy(machine, fullId);
2387
+ const until = opts?.until || opts?.criteria;
2387
2388
  const maxIterations = opts?.maxIterations ?? 20;
2388
- const evaluator = opts?.evaluator !== false;
2389
+ const evaluator = opts?.agent ? true : opts?.evaluator !== false;
2389
2390
  await svc.updateConfig({
2390
2391
  loop: {
2391
- task,
2392
- ...opts?.criteria ? { criteria: opts.criteria } : {},
2392
+ ...task ? { task } : {},
2393
+ ...until ? { until } : {},
2393
2394
  ...opts?.oracle ? { oracle: opts.oracle } : {},
2395
+ ...opts?.parent ? { parent: opts.parent } : {},
2394
2396
  max_iterations: maxIterations,
2395
2397
  evaluator
2396
2398
  }
2397
2399
  });
2398
- console.log(`Loop started on session ${fullId.slice(0, 8)}`);
2399
- console.log(` Task: ${task.slice(0, 100)}${task.length > 100 ? "..." : ""}`);
2400
- if (opts?.criteria) console.log(` Success criteria: ${opts.criteria.slice(0, 100)}${opts.criteria.length > 100 ? "..." : ""}`);
2400
+ console.log(`\u{1F501} Loop ${task ? "started" : "attached"} on session ${fullId.slice(0, 8)}`);
2401
+ if (task) console.log(` Task: ${task.slice(0, 100)}${task.length > 100 ? "..." : ""}`);
2402
+ if (until) console.log(` Until: ${until.slice(0, 100)}${until.length > 100 ? "..." : ""}`);
2401
2403
  console.log(` Oracle: ${opts?.oracle || "(none)"}`);
2402
2404
  console.log(` Evaluator: ${evaluator ? "on" : "off"}`);
2405
+ if (opts?.parent) console.log(` Parent review: ${opts.parent.slice(0, 8)}`);
2403
2406
  console.log(` Max iterations: ${maxIterations}`);
2407
+ if (!task) console.log(` (hot-plug \u2014 gating the session's current work)`);
2404
2408
  } finally {
2405
2409
  await server.disconnect();
2406
2410
  }
@@ -2456,44 +2460,6 @@ async function sessionLoopStatus(sessionIdPartial, machineId) {
2456
2460
  await server.disconnect();
2457
2461
  }
2458
2462
  }
2459
- async function sessionSupervise(sessionIdPartial, criteria, machineId, opts) {
2460
- const { server, machine, fullId } = await connectAndResolveSession(sessionIdPartial, machineId);
2461
- try {
2462
- const svc = getSessionProxy(machine, fullId);
2463
- const judges = [];
2464
- if (opts?.oracle) judges.push({ type: "oracle", cmd: opts.oracle, on_fail: opts?.parent ? "escalate" : "reject" });
2465
- if (opts?.parent) judges.push({ type: "parent", parent: opts.parent });
2466
- if (opts?.agent || judges.length === 0) judges.push({ type: "agent" });
2467
- const maxRounds = opts?.maxRounds ?? 20;
2468
- await svc.updateConfig({
2469
- supervisor: {
2470
- criteria,
2471
- judges,
2472
- action: opts?.action || "block",
2473
- max_rounds: maxRounds
2474
- }
2475
- });
2476
- const judgeLabel = judges.map((j) => j.type).join("\u2192");
2477
- console.log(`\u{1F441} Supervisor attached to session ${fullId.slice(0, 8)}`);
2478
- console.log(` Criteria: ${criteria.slice(0, 100)}${criteria.length > 100 ? "..." : ""}`);
2479
- console.log(` Judges: ${judgeLabel}`);
2480
- if (opts?.oracle) console.log(` Oracle: ${opts.oracle}`);
2481
- if (opts?.parent) console.log(` Parent review: ${opts.parent.slice(0, 8)} (async)`);
2482
- console.log(` Max rounds: ${maxRounds}`);
2483
- } finally {
2484
- await server.disconnect();
2485
- }
2486
- }
2487
- async function sessionUnsupervise(sessionIdPartial, machineId) {
2488
- const { server, machine, fullId } = await connectAndResolveSession(sessionIdPartial, machineId);
2489
- try {
2490
- const svc = getSessionProxy(machine, fullId);
2491
- await svc.updateConfig({ supervisor: null });
2492
- console.log(`Supervisor detached from session ${fullId.slice(0, 8)}`);
2493
- } finally {
2494
- await server.disconnect();
2495
- }
2496
- }
2497
2463
  async function sessionInboxSend(sessionIdPartial, body, machineId, opts) {
2498
2464
  const { server, machine, fullId } = await connectAndResolveSession(sessionIdPartial, machineId);
2499
2465
  try {
@@ -2634,4 +2600,4 @@ async function sessionInboxClear(sessionIdPartial, machineId, opts) {
2634
2600
  }
2635
2601
  }
2636
2602
 
2637
- export { collectAssistantResponse, connectAndGetMachine, connectAndResolveSession, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, queryCore, renderMessage, resolveSessionId, sendCore, sessionApprove, sessionArchive, sessionAttach, sessionDelete, sessionDeny, sessionEditMessage, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionLoopCancel, sessionLoopStart, sessionLoopStatus, sessionMachines, sessionMessages, sessionQuery, sessionRefineLastReply, sessionResume, sessionSend, sessionShare, sessionSpawn, sessionSupervise, sessionUndoEdit, sessionUnsupervise, sessionWait, sessionWhoami, snapshotLatestSeq, validateSendOptions, wiseAskCli };
2603
+ export { collectAssistantResponse, connectAndGetMachine, connectAndResolveSession, createWorktree, generateWorktreeName, machineExec, machineInfo, machineLs, machineShare, parseShareArg, queryCore, renderMessage, resolveSessionId, sendCore, sessionApprove, sessionArchive, sessionAttach, sessionDelete, sessionDeny, sessionEditMessage, sessionInboxClear, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxSend, sessionInfo, sessionList, sessionLoopCancel, sessionLoopStart, sessionLoopStatus, sessionMachines, sessionMessages, sessionQuery, sessionRefineLastReply, sessionResume, sessionSend, sessionShare, sessionSpawn, sessionUndoEdit, sessionWait, sessionWhoami, snapshotLatestSeq, validateSendOptions, wiseAskCli };
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import os from 'node:os';
4
- import { c as connectToHypha } from './run-C23-A9KM.mjs';
4
+ import { c as connectToHypha } from './run-DMahGhJP.mjs';
5
5
  import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-HydRNEt7.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
@@ -1,9 +1,10 @@
1
1
  import { spawn, execSync } from 'child_process';
2
+ import { createServer } from 'net';
2
3
  import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSync } from 'fs';
3
4
  import { join } from 'path';
4
5
  import { homedir, platform, arch } from 'os';
5
- import { createHash, randomUUID } from 'crypto';
6
- import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-C23-A9KM.mjs';
6
+ import { randomUUID, createHash } from 'crypto';
7
+ import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-DMahGhJP.mjs';
7
8
  import 'fs/promises';
8
9
  import 'url';
9
10
  import 'node:crypto';
@@ -124,7 +125,7 @@ async function ensureFrpc(log) {
124
125
  }
125
126
  return FRPC_BIN;
126
127
  }
127
- function generateFrpcConfig(config, proxies) {
128
+ function generateFrpcConfig(config, proxies, admin) {
128
129
  const useWSS = config.serverPort === 443;
129
130
  const lines = [
130
131
  "# Auto-generated by svamp \u2014 do not edit",
@@ -154,6 +155,14 @@ function generateFrpcConfig(config, proxies) {
154
155
  'log.level = "info"',
155
156
  ""
156
157
  ];
158
+ if (admin) {
159
+ lines.push("# Local admin/status API (loopback only \u2014 polled by the svamp daemon)");
160
+ lines.push('webServer.addr = "127.0.0.1"');
161
+ lines.push(`webServer.port = ${admin.port}`);
162
+ lines.push(`webServer.user = "${admin.user}"`);
163
+ lines.push(`webServer.password = "${admin.password}"`);
164
+ lines.push("");
165
+ }
157
166
  for (const proxy of proxies) {
158
167
  lines.push(`[[proxies]]`);
159
168
  lines.push(`name = "${proxy.name}"`);
@@ -189,6 +198,42 @@ function generateFrpcConfig(config, proxies) {
189
198
  }
190
199
  return lines.join("\n");
191
200
  }
201
+ function getFreePort() {
202
+ return new Promise((resolve, reject) => {
203
+ const srv = createServer();
204
+ srv.unref();
205
+ srv.on("error", reject);
206
+ srv.listen(0, "127.0.0.1", () => {
207
+ const addr = srv.address();
208
+ const port = typeof addr === "object" && addr ? addr.port : 0;
209
+ srv.close(() => port ? resolve(port) : reject(new Error("no port")));
210
+ });
211
+ });
212
+ }
213
+ function parseFrpcStatus(payload, proxyNames) {
214
+ const byName = /* @__PURE__ */ new Map();
215
+ if (payload && typeof payload === "object") {
216
+ for (const group of Object.values(payload)) {
217
+ if (!Array.isArray(group)) continue;
218
+ for (const p of group) {
219
+ if (p && typeof p.name === "string") {
220
+ byName.set(p.name, { status: String(p.status ?? ""), err: p.err ? String(p.err) : void 0 });
221
+ }
222
+ }
223
+ }
224
+ }
225
+ const missing = [];
226
+ const failed = [];
227
+ for (const name of proxyNames) {
228
+ const entry = byName.get(name);
229
+ if (!entry) {
230
+ missing.push(name);
231
+ continue;
232
+ }
233
+ if (entry.status !== "running") failed.push({ name, status: entry.status, err: entry.err });
234
+ }
235
+ return { allRunning: missing.length === 0 && failed.length === 0, missing, failed };
236
+ }
192
237
  class FrpcTunnel {
193
238
  process = null;
194
239
  _connected = false;
@@ -211,6 +256,11 @@ class FrpcTunnel {
211
256
  _lastProbeOkAt = 0;
212
257
  _lastProbeFailAt = 0;
213
258
  _probeOk = false;
259
+ // frpc admin/status API state (set when options.adminStatus is true).
260
+ _adminPort = 0;
261
+ _adminUser = "svamp";
262
+ _adminPassword = randomUUID();
263
+ _statusTimer = null;
214
264
  constructor(options) {
215
265
  this.options = options;
216
266
  this.log = options.log || ((msg) => console.log(`[FRPC] ${msg}`));
@@ -255,7 +305,18 @@ class FrpcTunnel {
255
305
  if (this._destroyed) return;
256
306
  if (this.process) return;
257
307
  const frpcPath = await ensureFrpc(this.log);
258
- const configContent = generateFrpcConfig(this.serverConfig, this.proxies);
308
+ let admin;
309
+ if (this.options.adminStatus) {
310
+ if (!this._adminPort) {
311
+ try {
312
+ this._adminPort = await getFreePort();
313
+ } catch (err) {
314
+ this.log(`admin port alloc failed: ${err?.message ?? err}`);
315
+ }
316
+ }
317
+ if (this._adminPort) admin = { port: this._adminPort, user: this._adminUser, password: this._adminPassword };
318
+ }
319
+ const configContent = generateFrpcConfig(this.serverConfig, this.proxies, admin);
259
320
  writeFileSync(this.configPath, configContent);
260
321
  this.log(`Config written to ${this.configPath}`);
261
322
  return new Promise((resolve, reject) => {
@@ -335,6 +396,8 @@ class FrpcTunnel {
335
396
  }
336
397
  }
337
398
  });
399
+ } else if (this.options.adminStatus && this._adminPort) {
400
+ this.startAdminStatusLoop();
338
401
  }
339
402
  setTimeout(() => {
340
403
  if (!resolved) {
@@ -433,11 +496,86 @@ class FrpcTunnel {
433
496
  this._probeTimer = null;
434
497
  }
435
498
  }
499
+ /**
500
+ * Poll frpc's loopback admin API (`/api/status`) to track whether this
501
+ * tunnel's proxies are registered ("running"). Feeds the same `_probeOk` /
502
+ * staleness fields the daemon health loop watches, so a ghosted/stuck proxy
503
+ * is detected and recreated even with no HTTP health endpoint on the backend.
504
+ *
505
+ * Conservative semantics (critical infra — must not churn healthy tunnels):
506
+ * - all proxies "running" → ok (refresh lastProbeOkAt)
507
+ * - a proxy present but not running, or missing after a grace period
508
+ * → fail (sets lastProbeFailAt; daemon recreates after staleness)
509
+ * - admin API unreachable → INCONCLUSIVE: leave _probeOk untouched
510
+ * (a transient blip can't flip ok→fail)
511
+ */
512
+ startAdminStatusLoop() {
513
+ this.stopAdminStatusLoop();
514
+ if (!this._adminPort) return;
515
+ const proxyNames = this.proxies.map((p) => p.name);
516
+ const intervalMs = this.options.probeIntervalMs ?? 3e4;
517
+ const startedAt = Date.now();
518
+ const auth = "Basic " + Buffer.from(`${this._adminUser}:${this._adminPassword}`).toString("base64");
519
+ this._probeOk = true;
520
+ this._lastProbeOkAt = Date.now();
521
+ let inFlight = false;
522
+ const poll = async () => {
523
+ if (this._destroyed || inFlight || !this.process) return;
524
+ inFlight = true;
525
+ try {
526
+ const ctrl = new AbortController();
527
+ const timer = setTimeout(() => ctrl.abort(), 5e3);
528
+ let payload;
529
+ try {
530
+ const resp = await fetch(`http://127.0.0.1:${this._adminPort}/api/status`, {
531
+ headers: { Authorization: auth },
532
+ signal: ctrl.signal
533
+ });
534
+ if (!resp.ok) throw new Error(`admin status ${resp.status}`);
535
+ payload = await resp.json();
536
+ } finally {
537
+ clearTimeout(timer);
538
+ }
539
+ const { allRunning, missing, failed } = parseFrpcStatus(payload, proxyNames);
540
+ const graceElapsed = Date.now() - startedAt > 2e4;
541
+ if (allRunning || !failed.length && !graceElapsed) {
542
+ const wasFailing = !this._probeOk;
543
+ this._probeOk = true;
544
+ this._lastProbeOkAt = Date.now();
545
+ if (wasFailing) this.log(`admin status ok: all proxies running`);
546
+ } else {
547
+ const wasOk = this._probeOk;
548
+ this._probeOk = false;
549
+ this._lastProbeFailAt = Date.now();
550
+ if (wasOk) {
551
+ const detail = [
552
+ ...failed.map((f) => `${f.name}=${f.status}${f.err ? ` (${f.err})` : ""}`),
553
+ ...missing.map((m) => `${m}=missing`)
554
+ ].join(", ");
555
+ this.log(`admin status fail: ${detail}`);
556
+ this.options.onProbeFail?.(new Error(`frpc proxy not running: ${detail}`));
557
+ }
558
+ }
559
+ } catch {
560
+ } finally {
561
+ inFlight = false;
562
+ }
563
+ };
564
+ if (intervalMs > 0) this._statusTimer = setInterval(poll, intervalMs);
565
+ setTimeout(() => void poll(), 3e3);
566
+ }
567
+ stopAdminStatusLoop() {
568
+ if (this._statusTimer) {
569
+ clearInterval(this._statusTimer);
570
+ this._statusTimer = null;
571
+ }
572
+ }
436
573
  /** Disconnect and stop the frpc process. */
437
574
  destroy() {
438
575
  this._destroyed = true;
439
576
  this._connected = false;
440
577
  this.stopProbeLoop();
578
+ this.stopAdminStatusLoop();
441
579
  if (this.process) {
442
580
  this.process.kill("SIGTERM");
443
581
  const p = this.process;
@@ -473,8 +611,8 @@ class FrpcTunnel {
473
611
  firstErrorAt: this._firstErrorAt,
474
612
  restartAttempts: this._restartAttempts,
475
613
  failingDurationMs: this._firstErrorAt > 0 ? Date.now() - this._firstErrorAt : 0,
476
- probe: this.resolveProbeUrl() ? {
477
- url: this.resolveProbeUrl(),
614
+ probe: this.resolveProbeUrl() || this.options.adminStatus && this._adminPort ? {
615
+ url: this.resolveProbeUrl() || `frpc-admin:${this._adminPort}/api/status`,
478
616
  ok: this._probeOk,
479
617
  lastOkAt: this._lastProbeOkAt,
480
618
  lastFailAt: this._lastProbeFailAt,
@@ -485,7 +623,8 @@ class FrpcTunnel {
485
623
  /** Update the Hypha token. Rewrites config; takes effect on next frpc restart. */
486
624
  updateToken(newToken) {
487
625
  this.serverConfig.hyphaToken = newToken;
488
- const configContent = generateFrpcConfig(this.serverConfig, this.proxies);
626
+ const admin = this.options.adminStatus && this._adminPort ? { port: this._adminPort, user: this._adminUser, password: this._adminPassword } : void 0;
627
+ const configContent = generateFrpcConfig(this.serverConfig, this.proxies, admin);
489
628
  writeFileSync(this.configPath, configContent);
490
629
  this.log("Config updated with fresh token");
491
630
  }
@@ -539,4 +678,4 @@ async function runFrpcTunnel(name, ports, serverConfig, tunnelOptions) {
539
678
  }
540
679
  }
541
680
 
542
- export { FrpcTunnel, ensureFrpc, generateFrpcConfig, runFrpcTunnel };
681
+ export { FrpcTunnel, ensureFrpc, generateFrpcConfig, getFreePort, parseFrpcStatus, runFrpcTunnel };
@@ -1,5 +1,5 @@
1
- import { D as resolveModel, V as describeMisconfiguration, W as buildMachineDeps } from './run-C23-A9KM.mjs';
2
- import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-D5F6XGss.mjs';
1
+ import { D as resolveModel, V as describeMisconfiguration, W as buildMachineDeps } from './run-DMahGhJP.mjs';
2
+ import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-Bk7iN3dp.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-C23-A9KM.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-DMahGhJP.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -1,5 +1,5 @@
1
1
  var name = "svamp-cli";
2
- var version = "0.2.128";
2
+ var version = "0.2.130";
3
3
  var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
4
4
  var author = "Amun AI AB";
5
5
  var license = "SEE LICENSE IN LICENSE";
@@ -19,7 +19,7 @@ var exports$1 = {
19
19
  var scripts = {
20
20
  build: "rm -rf dist bin/skills && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/loop bin/skills/loop && cp -r ../../skills/crew bin/skills/crew && tsc --noEmit && pkgroll",
21
21
  typecheck: "tsc --noEmit",
22
- test: "npx tsx test/test-context-window.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-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-loop-activation.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-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-checklist-watchdog.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-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.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",
22
+ test: "npx tsx test/test-context-window.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-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-loop-activation.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-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-checklist.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-routine.mjs && npx tsx test/test-routine-rpc.mjs && npx tsx test/test-checklist-watchdog.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-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.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",
23
23
  "test:hypha": "node --no-warnings test/test-hypha-service.mjs",
24
24
  dev: "tsx src/cli.ts",
25
25
  "dev:daemon": "tsx src/cli.ts daemon start-sync",
@@ -1,4 +1,4 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { X as generateFriendlyName, m as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Y as generateHookSettings } from './run-C23-A9KM.mjs';
1
+ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { X as generateFriendlyName, m as shortId, c as connectToHypha, a as createSessionStore, r as registerMachineService, Y as generateHookSettings } from './run-DMahGhJP.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';