svamp-cli 0.2.28 → 0.2.30

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.
@@ -148,7 +148,7 @@ async function sessionBroadcast(action, args) {
148
148
  console.log(`Broadcast sent: ${action}`);
149
149
  }
150
150
  async function connectToMachineService() {
151
- const { connectAndGetMachine } = await import('./commands-Bs2vXq8f.mjs');
151
+ const { connectAndGetMachine } = await import('./commands-BZK0QJdM.mjs');
152
152
  return connectAndGetMachine();
153
153
  }
154
154
  async function inboxSend(targetSessionId, opts) {
@@ -165,7 +165,7 @@ async function inboxSend(targetSessionId, opts) {
165
165
  }
166
166
  const { server, machine } = await connectToMachineService();
167
167
  try {
168
- const { resolveSessionId } = await import('./commands-Bs2vXq8f.mjs');
168
+ const { resolveSessionId } = await import('./commands-BZK0QJdM.mjs');
169
169
  const sessions = await machine.listSessions();
170
170
  const match = resolveSessionId(sessions, targetSessionId);
171
171
  const fullTargetId = match.sessionId;
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-D_OwNYZr.mjs';
1
+ import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-zAG5iduC.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';
@@ -36,7 +36,7 @@ async function main() {
36
36
  await logoutFromHypha();
37
37
  } else if (subcommand === "daemon") {
38
38
  if (daemonSubcommand === "restart") {
39
- const { restartDaemon } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.k; });
39
+ const { restartDaemon } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.k; });
40
40
  await restartDaemon();
41
41
  process.exit(0);
42
42
  }
@@ -238,7 +238,7 @@ async function main() {
238
238
  console.error("svamp service: Service commands are not available in sandboxed sessions.");
239
239
  process.exit(1);
240
240
  }
241
- const { handleServiceCommand } = await import('./commands-DauapAbb.mjs');
241
+ const { handleServiceCommand } = await import('./commands-BHZveuUk.mjs');
242
242
  await handleServiceCommand();
243
243
  } else if (subcommand === "serve") {
244
244
  const { isSandboxed: isSandboxedServe } = await import('./sandboxDetect-DNTcbgWD.mjs');
@@ -246,7 +246,7 @@ async function main() {
246
246
  console.error("svamp serve: Serve commands are not available in sandboxed sessions.");
247
247
  process.exit(1);
248
248
  }
249
- const { handleServeCommand } = await import('./serveCommands-DlblUnsT.mjs');
249
+ const { handleServeCommand } = await import('./serveCommands-9ZQgXdCA.mjs');
250
250
  await handleServeCommand();
251
251
  process.exit(0);
252
252
  } else if (subcommand === "process" || subcommand === "proc") {
@@ -255,7 +255,7 @@ async function main() {
255
255
  console.error("svamp process: Process commands are not available in sandboxed sessions.");
256
256
  process.exit(1);
257
257
  }
258
- const { processCommand } = await import('./commands-CGfaeJTY.mjs');
258
+ const { processCommand } = await import('./commands-bDntDDNU.mjs');
259
259
  let machineId;
260
260
  const processArgs = args.slice(1);
261
261
  const mIdx = processArgs.findIndex((a) => a === "--machine" || a === "-m");
@@ -273,7 +273,7 @@ async function main() {
273
273
  } else if (!subcommand || subcommand === "start") {
274
274
  await handleInteractiveCommand();
275
275
  } else if (subcommand === "--version" || subcommand === "-v") {
276
- const pkg = await import('./package-CU3zzvb7.mjs').catch(() => ({ default: { version: "unknown" } }));
276
+ const pkg = await import('./package-Ck8FHA97.mjs').catch(() => ({ default: { version: "unknown" } }));
277
277
  console.log(`svamp version: ${pkg.default.version}`);
278
278
  } else {
279
279
  console.error(`Unknown command: ${subcommand}`);
@@ -282,7 +282,7 @@ async function main() {
282
282
  }
283
283
  }
284
284
  async function handleInteractiveCommand() {
285
- const { runInteractive } = await import('./run-B6CGgKUJ.mjs');
285
+ const { runInteractive } = await import('./run-C6wKZrsx.mjs');
286
286
  const interactiveArgs = subcommand === "start" ? args.slice(1) : args;
287
287
  let directory = process.cwd();
288
288
  let resumeSessionId;
@@ -327,7 +327,7 @@ async function handleAgentCommand() {
327
327
  return;
328
328
  }
329
329
  if (agentArgs[0] === "list") {
330
- const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.i; });
330
+ const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.i; });
331
331
  console.log("Known agents:");
332
332
  for (const [name, config2] of Object.entries(KNOWN_ACP_AGENTS)) {
333
333
  console.log(` ${name.padEnd(12)} ${config2.command} ${config2.args.join(" ")} (ACP)`);
@@ -339,7 +339,7 @@ async function handleAgentCommand() {
339
339
  console.log('Use "svamp agent -- <command> [args]" for a custom ACP agent.');
340
340
  return;
341
341
  }
342
- const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.i; });
342
+ const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.i; });
343
343
  let cwd = process.cwd();
344
344
  const filteredArgs = [];
345
345
  for (let i = 0; i < agentArgs.length; i++) {
@@ -363,12 +363,12 @@ async function handleAgentCommand() {
363
363
  console.log(`Starting ${config.agentName} agent in ${cwd}...`);
364
364
  let backend;
365
365
  if (KNOWN_MCP_AGENTS[config.agentName]) {
366
- const { CodexMcpBackend } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.j; });
366
+ const { CodexMcpBackend } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.j; });
367
367
  backend = new CodexMcpBackend({ cwd, log: logFn });
368
368
  } else {
369
- const { AcpBackend } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.h; });
370
- const { GeminiTransport } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.G; });
371
- const { DefaultTransport } = await import('./run-D_OwNYZr.mjs').then(function (n) { return n.D; });
369
+ const { AcpBackend } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.h; });
370
+ const { GeminiTransport } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.G; });
371
+ const { DefaultTransport } = await import('./run-zAG5iduC.mjs').then(function (n) { return n.D; });
372
372
  const transportHandler = config.agentName === "gemini" ? new GeminiTransport() : new DefaultTransport(config.agentName);
373
373
  backend = new AcpBackend({
374
374
  agentName: config.agentName,
@@ -495,7 +495,7 @@ async function handleSessionCommand() {
495
495
  process.exit(1);
496
496
  }
497
497
  }
498
- const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-Bs2vXq8f.mjs');
498
+ const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-BZK0QJdM.mjs');
499
499
  const parseFlagStr = (flag, shortFlag) => {
500
500
  for (let i = 1; i < sessionArgs.length; i++) {
501
501
  if ((sessionArgs[i] === flag || shortFlag) && i + 1 < sessionArgs.length) {
@@ -555,7 +555,7 @@ async function handleSessionCommand() {
555
555
  allowDomain.push(sessionArgs[++i]);
556
556
  }
557
557
  }
558
- const { parseShareArg } = await import('./commands-Bs2vXq8f.mjs');
558
+ const { parseShareArg } = await import('./commands-BZK0QJdM.mjs');
559
559
  const shareEntries = share.map((s) => parseShareArg(s));
560
560
  await sessionSpawn(agent, dir, targetMachineId, {
561
561
  message,
@@ -641,7 +641,7 @@ async function handleSessionCommand() {
641
641
  console.error("Usage: svamp session approve <session-id> [request-id] [--json]");
642
642
  process.exit(1);
643
643
  }
644
- const { sessionApprove } = await import('./commands-Bs2vXq8f.mjs');
644
+ const { sessionApprove } = await import('./commands-BZK0QJdM.mjs');
645
645
  const approveReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
646
646
  await sessionApprove(sessionArgs[1], approveReqId, targetMachineId, {
647
647
  json: hasFlag("--json")
@@ -651,7 +651,7 @@ async function handleSessionCommand() {
651
651
  console.error("Usage: svamp session deny <session-id> [request-id] [--json]");
652
652
  process.exit(1);
653
653
  }
654
- const { sessionDeny } = await import('./commands-Bs2vXq8f.mjs');
654
+ const { sessionDeny } = await import('./commands-BZK0QJdM.mjs');
655
655
  const denyReqId = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
656
656
  await sessionDeny(sessionArgs[1], denyReqId, targetMachineId, {
657
657
  json: hasFlag("--json")
@@ -687,7 +687,7 @@ async function handleSessionCommand() {
687
687
  console.error("Usage: svamp session set-title <title>");
688
688
  process.exit(1);
689
689
  }
690
- const { sessionSetTitle } = await import('./agentCommands-C2yYtmsz.mjs');
690
+ const { sessionSetTitle } = await import('./agentCommands-BEKP7D0N.mjs');
691
691
  await sessionSetTitle(title);
692
692
  } else if (sessionSubcommand === "set-link") {
693
693
  const url = sessionArgs[1];
@@ -696,7 +696,7 @@ async function handleSessionCommand() {
696
696
  process.exit(1);
697
697
  }
698
698
  const label = sessionArgs[2] && !sessionArgs[2].startsWith("--") ? sessionArgs[2] : void 0;
699
- const { sessionSetLink } = await import('./agentCommands-C2yYtmsz.mjs');
699
+ const { sessionSetLink } = await import('./agentCommands-BEKP7D0N.mjs');
700
700
  await sessionSetLink(url, label);
701
701
  } else if (sessionSubcommand === "notify") {
702
702
  const message = sessionArgs[1];
@@ -705,7 +705,7 @@ async function handleSessionCommand() {
705
705
  process.exit(1);
706
706
  }
707
707
  const level = parseFlagStr("--level") || "info";
708
- const { sessionNotify } = await import('./agentCommands-C2yYtmsz.mjs');
708
+ const { sessionNotify } = await import('./agentCommands-BEKP7D0N.mjs');
709
709
  await sessionNotify(message, level);
710
710
  } else if (sessionSubcommand === "broadcast") {
711
711
  const action = sessionArgs[1];
@@ -713,7 +713,7 @@ async function handleSessionCommand() {
713
713
  console.error("Usage: svamp session broadcast <action> [args...]\nActions: open-canvas <url> [label], close-canvas, toast <message>");
714
714
  process.exit(1);
715
715
  }
716
- const { sessionBroadcast } = await import('./agentCommands-C2yYtmsz.mjs');
716
+ const { sessionBroadcast } = await import('./agentCommands-BEKP7D0N.mjs');
717
717
  await sessionBroadcast(action, sessionArgs.slice(2).filter((a) => !a.startsWith("--")));
718
718
  } else if (sessionSubcommand === "inbox") {
719
719
  const inboxSubcmd = sessionArgs[1];
@@ -724,7 +724,7 @@ async function handleSessionCommand() {
724
724
  process.exit(1);
725
725
  }
726
726
  if (agentSessionId) {
727
- const { inboxSend } = await import('./agentCommands-C2yYtmsz.mjs');
727
+ const { inboxSend } = await import('./agentCommands-BEKP7D0N.mjs');
728
728
  await inboxSend(sessionArgs[2], {
729
729
  body: sessionArgs[3],
730
730
  subject: parseFlagStr("--subject"),
@@ -739,7 +739,7 @@ async function handleSessionCommand() {
739
739
  }
740
740
  } else if (inboxSubcmd === "list" || inboxSubcmd === "ls") {
741
741
  if (agentSessionId && !sessionArgs[2]) {
742
- const { inboxList } = await import('./agentCommands-C2yYtmsz.mjs');
742
+ const { inboxList } = await import('./agentCommands-BEKP7D0N.mjs');
743
743
  await inboxList({
744
744
  unread: hasFlag("--unread"),
745
745
  limit: parseFlagInt("--limit"),
@@ -761,7 +761,7 @@ async function handleSessionCommand() {
761
761
  process.exit(1);
762
762
  }
763
763
  if (agentSessionId && !sessionArgs[3]) {
764
- const { inboxList } = await import('./agentCommands-C2yYtmsz.mjs');
764
+ const { inboxList } = await import('./agentCommands-BEKP7D0N.mjs');
765
765
  await sessionInboxRead(agentSessionId, sessionArgs[2], targetMachineId);
766
766
  } else if (sessionArgs[3]) {
767
767
  await sessionInboxRead(sessionArgs[2], sessionArgs[3], targetMachineId);
@@ -771,7 +771,7 @@ async function handleSessionCommand() {
771
771
  }
772
772
  } else if (inboxSubcmd === "reply") {
773
773
  if (agentSessionId && sessionArgs[2] && sessionArgs[3] && !sessionArgs[4]) {
774
- const { inboxReply } = await import('./agentCommands-C2yYtmsz.mjs');
774
+ const { inboxReply } = await import('./agentCommands-BEKP7D0N.mjs');
775
775
  await inboxReply(sessionArgs[2], sessionArgs[3]);
776
776
  } else if (sessionArgs[2] && sessionArgs[3] && sessionArgs[4]) {
777
777
  await sessionInboxReply(sessionArgs[2], sessionArgs[3], sessionArgs[4], targetMachineId);
@@ -807,7 +807,7 @@ async function handleMachineCommand() {
807
807
  return;
808
808
  }
809
809
  if (machineSubcommand === "share") {
810
- const { machineShare } = await import('./commands-Bs2vXq8f.mjs');
810
+ const { machineShare } = await import('./commands-BZK0QJdM.mjs');
811
811
  let machineId;
812
812
  const shareArgs = [];
813
813
  for (let i = 1; i < machineArgs.length; i++) {
@@ -837,7 +837,7 @@ async function handleMachineCommand() {
837
837
  }
838
838
  await machineShare(machineId, { add, remove, list, configPath, showConfig });
839
839
  } else if (machineSubcommand === "exec") {
840
- const { machineExec } = await import('./commands-Bs2vXq8f.mjs');
840
+ const { machineExec } = await import('./commands-BZK0QJdM.mjs');
841
841
  let machineId;
842
842
  let cwd;
843
843
  const cmdParts = [];
@@ -857,7 +857,7 @@ async function handleMachineCommand() {
857
857
  }
858
858
  await machineExec(machineId, command, cwd);
859
859
  } else if (machineSubcommand === "info") {
860
- const { machineInfo } = await import('./commands-Bs2vXq8f.mjs');
860
+ const { machineInfo } = await import('./commands-BZK0QJdM.mjs');
861
861
  let machineId;
862
862
  for (let i = 1; i < machineArgs.length; i++) {
863
863
  if ((machineArgs[i] === "--machine" || machineArgs[i] === "-m") && i + 1 < machineArgs.length) {
@@ -877,10 +877,10 @@ async function handleMachineCommand() {
877
877
  level = machineArgs[++i];
878
878
  }
879
879
  }
880
- const { machineNotify } = await import('./agentCommands-C2yYtmsz.mjs');
880
+ const { machineNotify } = await import('./agentCommands-BEKP7D0N.mjs');
881
881
  await machineNotify(message, level);
882
882
  } else if (machineSubcommand === "ls") {
883
- const { machineLs } = await import('./commands-Bs2vXq8f.mjs');
883
+ const { machineLs } = await import('./commands-BZK0QJdM.mjs');
884
884
  let machineId;
885
885
  let showHidden = false;
886
886
  let path;
@@ -55,7 +55,7 @@ async function serviceExpose(args) {
55
55
  console.error("Usage: svamp service expose <name> --port <port> [--port <port2>] [options]");
56
56
  process.exit(1);
57
57
  }
58
- const { runFrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
58
+ const { runFrpcTunnel } = await import('./frpc-DI5V-ecs.mjs');
59
59
  await runFrpcTunnel(name, ports, void 0, {
60
60
  group,
61
61
  groupKey,
@@ -88,7 +88,7 @@ async function serviceServe(args) {
88
88
  };
89
89
  process.on("SIGINT", cleanup);
90
90
  process.on("SIGTERM", cleanup);
91
- const { runFrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
91
+ const { runFrpcTunnel } = await import('./frpc-DI5V-ecs.mjs');
92
92
  await runFrpcTunnel(name, [caddyPort]);
93
93
  } catch (err) {
94
94
  console.error(`Error serving directory: ${err.message}`);
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { resolve, join } from 'node:path';
4
4
  import os from 'node:os';
5
- import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-D_OwNYZr.mjs';
5
+ import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-zAG5iduC.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -1,11 +1,11 @@
1
1
  import { writeFileSync, readFileSync } from 'fs';
2
2
  import { resolve } from 'path';
3
- import { connectAndGetMachine } from './commands-Bs2vXq8f.mjs';
3
+ import { connectAndGetMachine } from './commands-BZK0QJdM.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-D_OwNYZr.mjs';
8
+ import './run-zAG5iduC.mjs';
9
9
  import 'os';
10
10
  import 'fs/promises';
11
11
  import 'url';
@@ -1,9 +1,31 @@
1
1
  import { spawn, execSync } from 'child_process';
2
2
  import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync } from 'fs';
3
3
  import { join } from 'path';
4
- import { homedir, platform, arch } from 'os';
4
+ import { homedir, platform, arch, networkInterfaces, hostname } from 'os';
5
5
  import { createHash } from 'crypto';
6
6
 
7
+ function getMachineFingerprint() {
8
+ try {
9
+ const ifaces = networkInterfaces();
10
+ const macs = [];
11
+ for (const name of Object.keys(ifaces).sort()) {
12
+ const addrs = ifaces[name] || [];
13
+ for (const addr of addrs) {
14
+ if (addr.internal) continue;
15
+ if (!addr.mac || addr.mac === "00:00:00:00:00:00") continue;
16
+ macs.push(addr.mac);
17
+ }
18
+ }
19
+ if (macs.length > 0) return `mac:${macs[0]}`;
20
+ } catch {
21
+ }
22
+ try {
23
+ const hn = hostname();
24
+ if (hn) return `host:${hn}`;
25
+ } catch {
26
+ }
27
+ return `home:${homedir()}`;
28
+ }
7
29
  const FRP_VERSION = "0.68.0";
8
30
  const BIN_DIR = join(homedir(), ".svamp", "bin");
9
31
  const FRPC_BIN = join(BIN_DIR, platform() === "win32" ? "frpc.exe" : "frpc");
@@ -199,11 +221,12 @@ class FrpcTunnel {
199
221
  'Hypha token required for frpc authentication. Run "svamp login" or set HYPHA_TOKEN.'
200
222
  );
201
223
  }
202
- const machineId = homedir();
203
- const machineHash = createHash("sha256").update(machineId).digest("hex").slice(0, 6);
224
+ const machineFingerprint = getMachineFingerprint();
225
+ const machineHash = createHash("sha256").update(machineFingerprint).digest("hex").slice(0, 6);
204
226
  this.proxies = options.ports.map((port) => {
205
- const subdomain = options.subdomains?.get(port) || (options.group ? `${options.group}-${createHash("sha256").update(options.group).digest("hex").slice(0, 8)}` : `${options.name}-${port}-${createHash("sha256").update(`${options.name}-${port}-${machineId}`).digest("hex").slice(0, 8)}`);
206
- const proxyName = options.group ? `${options.name}-${port}-${machineHash}` : `${options.name}-${port}`;
227
+ const standaloneHash = createHash("sha256").update(`${options.name}|${machineFingerprint}`).digest("hex").slice(0, 8);
228
+ const subdomain = options.subdomains?.get(port) || (options.group ? `${options.group}-${createHash("sha256").update(options.group).digest("hex").slice(0, 8)}` : `${options.name}-${standaloneHash}`);
229
+ const proxyName = options.group ? `${options.name}-${port}-${machineHash}` : `${options.name}-${port}-${machineHash}`;
207
230
  return {
208
231
  name: proxyName,
209
232
  type: "http",
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-D_OwNYZr.mjs';
1
+ export { c as connectToHypha, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, a as registerSessionService, s as startDaemon, b as stopDaemon } from './run-zAG5iduC.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.28";
2
+ var version = "0.2.30";
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";
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
2
2
  import os from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { mkdirSync, writeFileSync, existsSync, unlinkSync, readFileSync, watch } from 'node:fs';
5
- import { c as connectToHypha, a as registerSessionService } from './run-D_OwNYZr.mjs';
5
+ import { c as connectToHypha, a as registerSessionService } from './run-zAG5iduC.mjs';
6
6
  import { createServer } from 'node:http';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { createInterface } from 'node:readline';
@@ -1107,7 +1107,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
1107
1107
  const tunnels = handlers.tunnels;
1108
1108
  if (!tunnels) throw new Error("Tunnel management not available");
1109
1109
  if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
1110
- const { FrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
1110
+ const { FrpcTunnel } = await import('./frpc-DI5V-ecs.mjs');
1111
1111
  const tunnel = new FrpcTunnel({
1112
1112
  name: params.name,
1113
1113
  ports: params.ports,
@@ -6424,7 +6424,7 @@ async function startDaemon(options) {
6424
6424
  const supervisor = new ProcessSupervisor(join(SVAMP_HOME, "processes"));
6425
6425
  await supervisor.init();
6426
6426
  const tunnels = /* @__PURE__ */ new Map();
6427
- const { ServeManager } = await import('./serveManager-CdJgafp4.mjs');
6427
+ const { ServeManager } = await import('./serveManager-C6exOzqc.mjs');
6428
6428
  const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
6429
6429
  ensureAutoInstalledSkills(logger).catch(() => {
6430
6430
  });
@@ -52,7 +52,7 @@ async function handleServeCommand() {
52
52
  }
53
53
  }
54
54
  async function serveAdd(args, machineId) {
55
- const { connectAndGetMachine } = await import('./commands-Bs2vXq8f.mjs');
55
+ const { connectAndGetMachine } = await import('./commands-BZK0QJdM.mjs');
56
56
  const pos = positionalArgs(args);
57
57
  const name = pos[0];
58
58
  if (!name) {
@@ -84,7 +84,7 @@ async function serveAdd(args, machineId) {
84
84
  }
85
85
  }
86
86
  async function serveRemove(args, machineId) {
87
- const { connectAndGetMachine } = await import('./commands-Bs2vXq8f.mjs');
87
+ const { connectAndGetMachine } = await import('./commands-BZK0QJdM.mjs');
88
88
  const pos = positionalArgs(args);
89
89
  const name = pos[0];
90
90
  if (!name) {
@@ -104,7 +104,7 @@ async function serveRemove(args, machineId) {
104
104
  }
105
105
  }
106
106
  async function serveList(args, machineId) {
107
- const { connectAndGetMachine } = await import('./commands-Bs2vXq8f.mjs');
107
+ const { connectAndGetMachine } = await import('./commands-BZK0QJdM.mjs');
108
108
  const all = hasFlag(args, "--all", "-a");
109
109
  const json = hasFlag(args, "--json");
110
110
  const sessionId = getFlag(args, "--session");
@@ -137,7 +137,7 @@ async function serveList(args, machineId) {
137
137
  }
138
138
  }
139
139
  async function serveInfo(machineId) {
140
- const { connectAndGetMachine } = await import('./commands-Bs2vXq8f.mjs');
140
+ const { connectAndGetMachine } = await import('./commands-BZK0QJdM.mjs');
141
141
  const { machine, server } = await connectAndGetMachine(machineId);
142
142
  try {
143
143
  const info = await machine.serveInfo();
@@ -153,24 +153,27 @@ class ServeAuth {
153
153
  <html><head>
154
154
  <meta charset="utf-8">
155
155
  <meta name="viewport" content="width=device-width,initial-scale=1">
156
- <title>Login \u2014 Svamp File Server</title>
156
+ <title>Sign in \u2014 Svamp File Server</title>
157
157
  <style>
158
158
  *{box-sizing:border-box;margin:0;padding:0}
159
- body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#f6f8fa;color:#24292f}
160
- .card{background:#fff;border:1px solid #d0d7de;border-radius:12px;padding:32px;max-width:400px;width:100%;text-align:center;box-shadow:0 4px 12px rgba(31,35,40,0.06)}
159
+ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#f6f8fa;color:#24292f;padding:16px}
160
+ .card{background:#fff;border:1px solid #d0d7de;border-radius:12px;padding:32px;max-width:420px;width:100%;text-align:center;box-shadow:0 4px 12px rgba(31,35,40,0.06)}
161
161
  h1{font-size:1.25rem;margin-bottom:8px}
162
162
  .subtitle{color:#656d76;font-size:0.9rem;margin-bottom:24px}
163
163
  button{background:#0969da;color:#fff;border:none;border-radius:8px;padding:12px 24px;font-size:1rem;cursor:pointer;font-weight:500;width:100%}
164
164
  button:hover{background:#0860c4}
165
165
  button:disabled{background:#94d3a2;cursor:wait}
166
- .status{margin-top:16px;color:#656d76;font-size:0.85rem;min-height:20px}
166
+ .status{margin-top:16px;color:#656d76;font-size:0.85rem;min-height:20px;word-break:break-word}
167
167
  .error{color:#cf222e}
168
+ .ok{color:#1a7f37}
169
+ a{color:#0969da;text-decoration:none}
170
+ a:hover{text-decoration:underline}
168
171
  </style>
169
172
  </head><body>
170
173
  <div class="card">
171
- <h1>Svamp File Server</h1>
172
- <p class="subtitle">Authentication required to access this content.</p>
173
- <button id="login-btn" onclick="startLogin()">Sign in with Hypha</button>
174
+ <h1>Sign in required</h1>
175
+ <p class="subtitle">This resource is private. Sign in with your Hypha account to continue.</p>
176
+ <button id="login-btn">Sign in with Hypha</button>
174
177
  <div class="status" id="status"></div>
175
178
  </div>
176
179
  <script>
@@ -178,57 +181,94 @@ const hyphaServer = ${JSON.stringify(this.hyphaServerUrl)};
178
181
  const redirectUrl = ${JSON.stringify(redirectUrl)};
179
182
  const cookieName = ${JSON.stringify(COOKIE_NAME)};
180
183
 
181
- async function startLogin() {
182
- const btn = document.getElementById('login-btn');
183
- const status = document.getElementById('status');
184
+ const btn = document.getElementById('login-btn');
185
+ const status = document.getElementById('status');
186
+
187
+ function setError(msg, linkUrl) {
188
+ if (linkUrl) {
189
+ status.innerHTML = '<span class="error">' + msg + '</span><br><a href="' + linkUrl + '" target="_blank" rel="noopener">Open login in new tab</a>';
190
+ } else {
191
+ status.innerHTML = '<span class="error">' + msg + '</span>';
192
+ }
193
+ btn.disabled = false;
194
+ }
195
+
196
+ btn.addEventListener('click', () => {
197
+ // IMPORTANT: open the popup SYNCHRONOUSLY inside the click handler so
198
+ // browsers do not flag it as a blocked popup. We then navigate it once
199
+ // the login URL comes back from the server.
200
+ const popup = window.open('about:blank', 'hypha-login', 'width=560,height=720');
201
+ if (!popup || popup.closed) {
202
+ setError('Popup blocked. Please allow popups for this site and try again.');
203
+ return;
204
+ }
205
+ startLogin(popup);
206
+ });
207
+
208
+ async function startLogin(popup) {
184
209
  btn.disabled = true;
185
- status.textContent = 'Starting login...';
210
+ status.textContent = 'Starting login\u2026';
186
211
 
212
+ let key, loginUrl;
187
213
  try {
188
- // Call hypha-login.start()
189
214
  const startResp = await fetch(hyphaServer + '/public/services/hypha-login/start');
190
- if (!startResp.ok) throw new Error('Failed to start login');
191
- const { key, login_url } = await startResp.json();
215
+ if (!startResp.ok) throw new Error('HTTP ' + startResp.status + ' from hypha-login/start');
216
+ const data = await startResp.json();
217
+ key = data.key;
218
+ loginUrl = data.login_url;
219
+ if (!key || !loginUrl) throw new Error('Invalid response from hypha-login/start');
220
+ } catch (err) {
221
+ try { popup.close(); } catch {}
222
+ setError('Failed to contact Hypha: ' + (err && err.message ? err.message : err));
223
+ return;
224
+ }
192
225
 
193
- status.textContent = 'Redirecting to login...';
226
+ // Navigate the already-opened popup to the login URL.
227
+ try { popup.location.href = loginUrl; } catch {
228
+ setError('Could not open login window.', loginUrl);
229
+ return;
230
+ }
194
231
 
195
- // Open login in a popup
196
- const popup = window.open(login_url, 'hypha-login', 'width=500,height=600');
232
+ status.textContent = 'Waiting for you to sign in\u2026';
197
233
 
198
- // Poll for token
199
- status.textContent = 'Waiting for authentication...';
200
- let attempts = 0;
201
- const maxAttempts = 120; // 2 minutes
202
- const poll = setInterval(async () => {
203
- attempts++;
204
- if (attempts > maxAttempts) {
205
- clearInterval(poll);
206
- status.innerHTML = '<span class="error">Login timed out. Please try again.</span>';
207
- btn.disabled = false;
208
- return;
209
- }
210
- try {
211
- const checkResp = await fetch(
212
- hyphaServer + '/public/services/hypha-login/check?key=' + encodeURIComponent('"' + key + '"') + '&timeout=' + encodeURIComponent('"0"')
213
- );
214
- if (!checkResp.ok) return;
215
- const result = await checkResp.json();
216
- if (result && result.token) {
217
- clearInterval(poll);
218
- if (popup && !popup.closed) popup.close();
234
+ // Poll hypha-login/check for the token. Pass key and timeout as plain
235
+ // query-string values (the HTTP proxy does NOT JSON-decode query params
236
+ // for hypha-login \u2014 wrapping the key in quotes breaks the lookup).
237
+ const checkUrl = hyphaServer
238
+ + '/public/services/hypha-login/check?key=' + encodeURIComponent(key)
239
+ + '&timeout=0';
219
240
 
220
- // Set cookie (session cookie \u2014 no explicit expiry)
221
- document.cookie = cookieName + '=' + result.token + '; path=/; SameSite=Lax; Secure';
241
+ const deadline = Date.now() + 5 * 60 * 1000; // 5 minutes
242
+ let lastErr = null;
222
243
 
223
- status.textContent = 'Login successful! Redirecting...';
224
- setTimeout(() => { window.location.href = redirectUrl; }, 500);
244
+ // Intentionally do NOT bail out when popup.closed \u2014 the Hypha login popup
245
+ // closes itself immediately after posting the token, and the token often
246
+ // arrives on the *next* poll. Just keep polling until the deadline.
247
+ while (Date.now() < deadline) {
248
+ try {
249
+ const resp = await fetch(checkUrl, { cache: 'no-store' });
250
+ if (resp.ok) {
251
+ const result = await resp.json();
252
+ if (result && typeof result === 'object' && result.token) {
253
+ try { if (!popup.closed) popup.close(); } catch {}
254
+ // Session cookie \u2014 cleared when browser closes.
255
+ const secure = location.protocol === 'https:' ? '; Secure' : '';
256
+ document.cookie = cookieName + '=' + result.token + '; path=/; SameSite=Lax' + secure;
257
+ status.innerHTML = '<span class="ok">Signed in. Redirecting\u2026</span>';
258
+ setTimeout(() => { window.location.replace(redirectUrl); }, 300);
259
+ return;
225
260
  }
226
- } catch { /* ignore polling errors */ }
227
- }, 1000);
228
- } catch (err) {
229
- status.innerHTML = '<span class="error">Login failed: ' + err.message + '</span>';
230
- btn.disabled = false;
261
+ } else {
262
+ lastErr = 'HTTP ' + resp.status;
263
+ }
264
+ } catch (err) {
265
+ lastErr = err && err.message ? err.message : String(err);
266
+ }
267
+ await new Promise(r => setTimeout(r, 1200));
231
268
  }
269
+
270
+ try { if (!popup.closed) popup.close(); } catch {}
271
+ setError('Login timed out' + (lastErr ? ' (' + lastErr + ')' : '') + '. Please try again.');
232
272
  }
233
273
  <\/script>
234
274
  </body></html>`;
@@ -444,26 +484,17 @@ class ServeManager {
444
484
  /** Start a lightweight Node.js HTTP proxy with auth + upload support. */
445
485
  startAuthProxy() {
446
486
  return new Promise((resolve, reject) => {
447
- const LOGIN_PAGE = `<!DOCTYPE html>
448
- <html><head><meta charset="utf-8"><title>Sign In \u2014 Svamp</title>
449
- <style>body{font-family:system-ui,sans-serif;max-width:400px;margin:80px auto;text-align:center}
450
- input{width:100%;padding:8px;margin:8px 0;box-sizing:border-box}
451
- button{padding:10px 24px;background:#0969da;color:#fff;border:none;border-radius:4px;cursor:pointer}
452
- </style></head>
453
- <body><h2>Sign In</h2>
454
- <p>Paste your Hypha token to access this resource.</p>
455
- <form method="GET">
456
- <input type="text" name="token" placeholder="Bearer token or Hypha token" autofocus>
457
- <input type="hidden" name="return" id="return-field">
458
- <button type="submit">Access</button>
459
- </form>
460
- <script>document.getElementById('return-field').value=new URLSearchParams(location.search).get('return')||'/';<\/script>
461
- </body></html>`;
462
487
  const server = http.createServer(async (req, res) => {
463
488
  const url = new URL(req.url || "/", `http://127.0.0.1:${this.port}`);
464
489
  if (url.pathname === "/__login__") {
465
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
466
- res.end(LOGIN_PAGE);
490
+ const returnUrl = url.searchParams.get("return") || "/";
491
+ const safeReturn = returnUrl.startsWith("/__login__") ? "/" : returnUrl;
492
+ const html = this.auth ? this.auth.getLoginPageHtml(safeReturn) : "<h1>Auth not configured</h1>";
493
+ res.writeHead(200, {
494
+ "Content-Type": "text/html; charset=utf-8",
495
+ "Cache-Control": "no-store"
496
+ });
497
+ res.end(html);
467
498
  return;
468
499
  }
469
500
  const mountName = url.pathname.split("/").filter(Boolean)[0];
@@ -546,7 +577,7 @@ button{padding:10px 24px;background:#0969da;color:#fff;border:none;border-radius
546
577
  /** Start frpc tunnel for the Caddy port. */
547
578
  async ensureTunnel() {
548
579
  try {
549
- const { FrpcTunnel } = await import('./frpc-BhS2e6r-.mjs');
580
+ const { FrpcTunnel } = await import('./frpc-DI5V-ecs.mjs');
550
581
  this.frpcTunnel = new FrpcTunnel({
551
582
  name: this.serviceName,
552
583
  ports: [this.port],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svamp-cli",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
5
5
  "author": "Amun AI AB",
6
6
  "license": "SEE LICENSE IN LICENSE",