svamp-cli 0.2.21 → 0.2.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agentCommands-DRxsYzlw.mjs → agentCommands-rofwcLx3.mjs} +2 -2
- package/dist/cli.mjs +30 -30
- package/dist/commands-CqULIXFK.mjs +179 -0
- package/dist/{commands-DkmPKHhv.mjs → commands-DCI1dZuS.mjs} +2 -2
- package/dist/{commands-DVCdUK0p.mjs → commands-DrNOSpki.mjs} +1 -1
- package/dist/{frpc-Ckiiq9Lw.mjs → frpc-qqYQ6F9T.mjs} +71 -23
- package/dist/index.mjs +1 -1
- package/dist/{package-DkmeUIJ-.mjs → package-D-GTa0i_.mjs} +4 -3
- package/dist/{run-B_yFI97i.mjs → run-Ckm62Pws.mjs} +1 -1
- package/dist/{run-CdjKXuaY.mjs → run-YVIJdP0j.mjs} +17 -20
- package/dist/{serveCommands-PxeZcd-5.mjs → serveCommands-CCbnvtwL.mjs} +4 -4
- package/dist/{serveManager-Be7Dgpoe.mjs → serveManager-Cgbl706Q.mjs} +17 -38
- package/package.json +4 -3
- package/dist/api-BRbsyqJ4.mjs +0 -147
- package/dist/commands-DdwOBsOi.mjs +0 -477
|
@@ -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-
|
|
151
|
+
const { connectAndGetMachine } = await import('./commands-DrNOSpki.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-
|
|
168
|
+
const { resolveSessionId } = await import('./commands-DrNOSpki.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-
|
|
1
|
+
import { s as startDaemon, b as stopDaemon, d as daemonStatus } from './run-YVIJdP0j.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-
|
|
39
|
+
const { restartDaemon } = await import('./run-YVIJdP0j.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-
|
|
241
|
+
const { handleServiceCommand } = await import('./commands-CqULIXFK.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-
|
|
249
|
+
const { handleServeCommand } = await import('./serveCommands-CCbnvtwL.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-
|
|
258
|
+
const { processCommand } = await import('./commands-DCI1dZuS.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-
|
|
276
|
+
const pkg = await import('./package-D-GTa0i_.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-
|
|
285
|
+
const { runInteractive } = await import('./run-Ckm62Pws.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-
|
|
330
|
+
const { KNOWN_ACP_AGENTS, KNOWN_MCP_AGENTS: KNOWN_MCP_AGENTS2 } = await import('./run-YVIJdP0j.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-
|
|
342
|
+
const { resolveAcpAgentConfig, KNOWN_MCP_AGENTS } = await import('./run-YVIJdP0j.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-
|
|
366
|
+
const { CodexMcpBackend } = await import('./run-YVIJdP0j.mjs').then(function (n) { return n.j; });
|
|
367
367
|
backend = new CodexMcpBackend({ cwd, log: logFn });
|
|
368
368
|
} else {
|
|
369
|
-
const { AcpBackend } = await import('./run-
|
|
370
|
-
const { GeminiTransport } = await import('./run-
|
|
371
|
-
const { DefaultTransport } = await import('./run-
|
|
369
|
+
const { AcpBackend } = await import('./run-YVIJdP0j.mjs').then(function (n) { return n.h; });
|
|
370
|
+
const { GeminiTransport } = await import('./run-YVIJdP0j.mjs').then(function (n) { return n.G; });
|
|
371
|
+
const { DefaultTransport } = await import('./run-YVIJdP0j.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-
|
|
498
|
+
const { sessionList, sessionSpawn, sessionStop, sessionInfo, sessionMessages, sessionAttach, sessionMachines, sessionSend, sessionWait, sessionShare, sessionRalphStart, sessionRalphCancel, sessionRalphStatus, sessionInboxSend, sessionInboxList, sessionInboxRead, sessionInboxReply, sessionInboxClear } = await import('./commands-DrNOSpki.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-
|
|
558
|
+
const { parseShareArg } = await import('./commands-DrNOSpki.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-
|
|
644
|
+
const { sessionApprove } = await import('./commands-DrNOSpki.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-
|
|
654
|
+
const { sessionDeny } = await import('./commands-DrNOSpki.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-
|
|
690
|
+
const { sessionSetTitle } = await import('./agentCommands-rofwcLx3.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-
|
|
699
|
+
const { sessionSetLink } = await import('./agentCommands-rofwcLx3.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-
|
|
708
|
+
const { sessionNotify } = await import('./agentCommands-rofwcLx3.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-
|
|
716
|
+
const { sessionBroadcast } = await import('./agentCommands-rofwcLx3.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-
|
|
727
|
+
const { inboxSend } = await import('./agentCommands-rofwcLx3.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-
|
|
742
|
+
const { inboxList } = await import('./agentCommands-rofwcLx3.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-
|
|
764
|
+
const { inboxList } = await import('./agentCommands-rofwcLx3.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-
|
|
774
|
+
const { inboxReply } = await import('./agentCommands-rofwcLx3.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-
|
|
810
|
+
const { machineShare } = await import('./commands-DrNOSpki.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-
|
|
840
|
+
const { machineExec } = await import('./commands-DrNOSpki.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-
|
|
860
|
+
const { machineInfo } = await import('./commands-DrNOSpki.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-
|
|
880
|
+
const { machineNotify } = await import('./agentCommands-rofwcLx3.mjs');
|
|
881
881
|
await machineNotify(message, level);
|
|
882
882
|
} else if (machineSubcommand === "ls") {
|
|
883
|
-
const { machineLs } = await import('./commands-
|
|
883
|
+
const { machineLs } = await import('./commands-DrNOSpki.mjs');
|
|
884
884
|
let machineId;
|
|
885
885
|
let showHidden = false;
|
|
886
886
|
let path;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
|
|
3
|
+
function getFlag(args, flag) {
|
|
4
|
+
const idx = args.indexOf(flag);
|
|
5
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : void 0;
|
|
6
|
+
}
|
|
7
|
+
function getAllFlags(args, flag) {
|
|
8
|
+
const values = [];
|
|
9
|
+
for (let i = 0; i < args.length; i++) {
|
|
10
|
+
if (args[i] === flag && i + 1 < args.length) {
|
|
11
|
+
values.push(args[i + 1]);
|
|
12
|
+
i++;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return values;
|
|
16
|
+
}
|
|
17
|
+
function hasFlag(args, ...flags) {
|
|
18
|
+
return flags.some((f) => args.includes(f));
|
|
19
|
+
}
|
|
20
|
+
function positionalArgs(args) {
|
|
21
|
+
const result = [];
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
if (args[i].startsWith("--")) {
|
|
24
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
result.push(args[i]);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function parsePorts(args) {
|
|
34
|
+
const portStrs = getAllFlags(args, "--port");
|
|
35
|
+
if (portStrs.length === 0) return [];
|
|
36
|
+
const ports = [];
|
|
37
|
+
for (const s of portStrs) {
|
|
38
|
+
const p = parseInt(s, 10);
|
|
39
|
+
if (isNaN(p) || p < 1 || p > 65535) {
|
|
40
|
+
console.error(`Error: invalid port '${s}' \u2014 must be 1-65535`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
ports.push(p);
|
|
44
|
+
}
|
|
45
|
+
return ports;
|
|
46
|
+
}
|
|
47
|
+
async function serviceExpose(args) {
|
|
48
|
+
const positional = positionalArgs(args);
|
|
49
|
+
const name = positional[0];
|
|
50
|
+
const ports = parsePorts(args);
|
|
51
|
+
const group = getFlag(args, "--group");
|
|
52
|
+
const groupKey = getFlag(args, "--group-key");
|
|
53
|
+
const healthCheck = getFlag(args, "--health-check");
|
|
54
|
+
const healthPath = getFlag(args, "--health-path");
|
|
55
|
+
const healthInterval = getFlag(args, "--health-interval");
|
|
56
|
+
if (!name || ports.length === 0) {
|
|
57
|
+
console.error("Usage: svamp service expose <name> --port <port> [--port <port2>] [options]");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const { runFrpcTunnel } = await import('./frpc-qqYQ6F9T.mjs');
|
|
61
|
+
await runFrpcTunnel(name, ports, void 0, {
|
|
62
|
+
group,
|
|
63
|
+
groupKey,
|
|
64
|
+
healthCheckType: healthCheck,
|
|
65
|
+
healthCheckPath: healthPath,
|
|
66
|
+
healthCheckInterval: healthInterval ? parseInt(healthInterval, 10) : void 0
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function serviceServe(args) {
|
|
70
|
+
const positional = positionalArgs(args);
|
|
71
|
+
const name = positional[0];
|
|
72
|
+
const directory = positional[1] || ".";
|
|
73
|
+
const noListing = hasFlag(args, "--no-listing");
|
|
74
|
+
if (!name) {
|
|
75
|
+
console.error("Usage: svamp service serve <name> [directory] [--no-listing]");
|
|
76
|
+
console.error(" directory defaults to current directory");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const resolvedDir = path.resolve(directory);
|
|
81
|
+
console.log(`Serving ${resolvedDir}`);
|
|
82
|
+
const caddyPort = 18080;
|
|
83
|
+
const { CaddyManager } = await import('./caddy-fJWXn1kE.mjs');
|
|
84
|
+
const caddy = new CaddyManager({ listenPort: caddyPort });
|
|
85
|
+
await caddy.addMount(name, resolvedDir, !noListing);
|
|
86
|
+
await caddy.start();
|
|
87
|
+
const cleanup = () => {
|
|
88
|
+
caddy.stop();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
};
|
|
91
|
+
process.on("SIGINT", cleanup);
|
|
92
|
+
process.on("SIGTERM", cleanup);
|
|
93
|
+
const { runFrpcTunnel } = await import('./frpc-qqYQ6F9T.mjs');
|
|
94
|
+
await runFrpcTunnel(name, [caddyPort]);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error(`Error serving directory: ${err.message}`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function serviceTunnel(args) {
|
|
101
|
+
await serviceExpose(args);
|
|
102
|
+
}
|
|
103
|
+
async function serviceList(args) {
|
|
104
|
+
console.log("Active tunnels are managed by the svamp daemon.");
|
|
105
|
+
console.log('Use "svamp service expose" to start a tunnel in the foreground.');
|
|
106
|
+
}
|
|
107
|
+
async function serviceDelete(args) {
|
|
108
|
+
const positional = positionalArgs(args);
|
|
109
|
+
const name = positional[0];
|
|
110
|
+
if (!name) {
|
|
111
|
+
console.error("Usage: svamp service delete <name>");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log(`Tunnel '${name}' can only be stopped via the daemon (tunnelStop RPC).`);
|
|
115
|
+
}
|
|
116
|
+
async function handleServiceCommand() {
|
|
117
|
+
const args = process.argv.slice(2);
|
|
118
|
+
const serviceArgs = args.slice(1);
|
|
119
|
+
const sub = serviceArgs[0];
|
|
120
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
121
|
+
printServiceHelp();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const commandArgs = serviceArgs.slice(1);
|
|
125
|
+
if (sub === "expose") {
|
|
126
|
+
await serviceExpose(commandArgs);
|
|
127
|
+
} else if (sub === "serve") {
|
|
128
|
+
await serviceServe(commandArgs);
|
|
129
|
+
} else if (sub === "tunnel") {
|
|
130
|
+
await serviceTunnel(commandArgs);
|
|
131
|
+
} else if (sub === "list" || sub === "ls") {
|
|
132
|
+
await serviceList();
|
|
133
|
+
} else if (sub === "delete" || sub === "rm") {
|
|
134
|
+
await serviceDelete(commandArgs);
|
|
135
|
+
} else {
|
|
136
|
+
console.error(`Unknown service command: ${sub}`);
|
|
137
|
+
printServiceHelp();
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function printServiceHelp() {
|
|
142
|
+
console.log(`
|
|
143
|
+
svamp service \u2014 Expose local services via frpc tunnels
|
|
144
|
+
|
|
145
|
+
Usage:
|
|
146
|
+
svamp service expose <name> --port <port> [options] Expose local ports via tunnel
|
|
147
|
+
svamp service serve <name> [directory] [--no-listing] Serve static files via tunnel
|
|
148
|
+
svamp service tunnel <name> --port <port> [options] Alias for expose
|
|
149
|
+
svamp service list List active tunnels (daemon)
|
|
150
|
+
svamp service delete <name> Stop a tunnel (daemon)
|
|
151
|
+
|
|
152
|
+
Options:
|
|
153
|
+
--port <port> Port to expose (repeatable for multi-port)
|
|
154
|
+
--group <name> Service group \u2014 multiple machines share the same URL
|
|
155
|
+
--group-key <secret> Shared key for the service group
|
|
156
|
+
--health-check <tcp|http> Enable health checks on local backend
|
|
157
|
+
--health-path <path> HTTP health check path (e.g., /health)
|
|
158
|
+
--health-interval <sec> Health check interval (default: 10s)
|
|
159
|
+
|
|
160
|
+
Service Groups:
|
|
161
|
+
Run the same command on different machines with --group to create a
|
|
162
|
+
load-balanced service. frps round-robins traffic across all group members.
|
|
163
|
+
Unhealthy backends are auto-removed when --health-check is enabled.
|
|
164
|
+
|
|
165
|
+
Machine A: svamp service expose my-api --port 8000 --group my-api --group-key secret123
|
|
166
|
+
Machine B: svamp service expose my-api --port 8000 --group my-api --group-key secret123
|
|
167
|
+
\u2192 Both serve at the same URL, frps load-balances between them
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
svamp service expose my-api --port 8000
|
|
171
|
+
svamp service expose my-api --port 8000 --health-check http --health-path /health
|
|
172
|
+
svamp service expose my-api --port 8000 --port 3000
|
|
173
|
+
svamp service expose my-api --port 8000 --group my-api --group-key s3cret
|
|
174
|
+
svamp service serve my-site ./dist
|
|
175
|
+
svamp service serve my-site
|
|
176
|
+
`.trim());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { handleServiceCommand, serviceDelete, serviceExpose, serviceList, serviceServe, serviceTunnel };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
import { connectAndGetMachine } from './commands-
|
|
3
|
+
import { connectAndGetMachine } from './commands-DrNOSpki.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-
|
|
8
|
+
import './run-YVIJdP0j.mjs';
|
|
9
9
|
import 'os';
|
|
10
10
|
import 'fs/promises';
|
|
11
11
|
import 'url';
|
|
@@ -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-
|
|
5
|
+
import { l as loadSecurityContextConfig, e as resolveSecurityContext, f as buildSecurityContextFromFlags, m as mergeSecurityContexts, c as connectToHypha } from './run-YVIJdP0j.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
8
8
|
import 'fs';
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { spawn, execSync } from 'child_process';
|
|
2
|
-
import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync } from 'fs';
|
|
2
|
+
import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { homedir, platform, arch } from 'os';
|
|
5
|
-
import {
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
6
|
|
|
7
7
|
const FRP_VERSION = "0.61.1";
|
|
8
8
|
const BIN_DIR = join(homedir(), ".svamp", "bin");
|
|
9
9
|
const FRPC_BIN = join(BIN_DIR, platform() === "win32" ? "frpc.exe" : "frpc");
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
function isInCluster() {
|
|
11
|
+
return !!(process.env.KUBERNETES_SERVICE_HOST || process.env.SANDBOX_ID);
|
|
12
|
+
}
|
|
13
|
+
const DEFAULT_FRPS_ADDR = isInCluster() ? "frps.hypha.svc.cluster.local" : "frps-ctrl.svc.hypha.aicell.io";
|
|
14
|
+
const DEFAULT_FRPS_PORT = isInCluster() ? 7e3 : 443;
|
|
15
|
+
const FRPS_PUBLIC_TOKEN = "hypha-frps-public";
|
|
12
16
|
function getFrpcDownloadUrl() {
|
|
13
17
|
const os = platform();
|
|
14
18
|
const a = arch();
|
|
@@ -80,6 +84,15 @@ async function ensureFrpc(log) {
|
|
|
80
84
|
{ stdio: "pipe" }
|
|
81
85
|
);
|
|
82
86
|
chmodSync(FRPC_BIN, 493);
|
|
87
|
+
const bin = readFileSync(FRPC_BIN);
|
|
88
|
+
const old = Buffer.from("/~!frp");
|
|
89
|
+
const patched = Buffer.from("/frpws");
|
|
90
|
+
const idx = bin.indexOf(old);
|
|
91
|
+
if (idx !== -1) {
|
|
92
|
+
patched.copy(bin, idx);
|
|
93
|
+
writeFileSync(FRPC_BIN, bin);
|
|
94
|
+
logger("Patched frpc WebSocket path: /~!frp \u2192 /frpws");
|
|
95
|
+
}
|
|
83
96
|
logger(`frpc installed at ${FRPC_BIN}`);
|
|
84
97
|
} finally {
|
|
85
98
|
try {
|
|
@@ -90,6 +103,7 @@ async function ensureFrpc(log) {
|
|
|
90
103
|
return FRPC_BIN;
|
|
91
104
|
}
|
|
92
105
|
function generateFrpcConfig(config, proxies) {
|
|
106
|
+
const useWSS = config.serverPort === 443;
|
|
93
107
|
const lines = [
|
|
94
108
|
"# Auto-generated by svamp \u2014 do not edit",
|
|
95
109
|
`serverAddr = "${config.serverAddr}"`,
|
|
@@ -98,11 +112,18 @@ function generateFrpcConfig(config, proxies) {
|
|
|
98
112
|
'auth.method = "token"',
|
|
99
113
|
`auth.token = "${config.authToken}"`,
|
|
100
114
|
"",
|
|
115
|
+
"# Hypha JWT token for server-side authentication",
|
|
116
|
+
...config.hyphaToken ? [`metadatas.token = "${config.hyphaToken}"`] : [],
|
|
117
|
+
"",
|
|
101
118
|
"# Transport",
|
|
119
|
+
...useWSS ? ['transport.protocol = "wss"'] : [],
|
|
102
120
|
"transport.heartbeatInterval = 30",
|
|
103
121
|
"transport.heartbeatTimeout = 90",
|
|
104
122
|
"transport.poolCount = 5",
|
|
105
123
|
"",
|
|
124
|
+
"# Don't exit on login failure \u2014 let frpc keep retrying",
|
|
125
|
+
"loginFailExit = false",
|
|
126
|
+
"",
|
|
106
127
|
'log.to = "console"',
|
|
107
128
|
'log.level = "info"',
|
|
108
129
|
""
|
|
@@ -119,6 +140,21 @@ function generateFrpcConfig(config, proxies) {
|
|
|
119
140
|
if (proxy.customDomains && proxy.customDomains.length > 0) {
|
|
120
141
|
lines.push(`customDomains = [${proxy.customDomains.map((d) => `"${d}"`).join(", ")}]`);
|
|
121
142
|
}
|
|
143
|
+
if (proxy.group) {
|
|
144
|
+
lines.push(`loadBalancer.group = "${proxy.group}"`);
|
|
145
|
+
if (proxy.groupKey) {
|
|
146
|
+
lines.push(`loadBalancer.groupKey = "${proxy.groupKey}"`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (proxy.healthCheckType) {
|
|
150
|
+
lines.push(`healthCheck.type = "${proxy.healthCheckType}"`);
|
|
151
|
+
if (proxy.healthCheckType === "http" && proxy.healthCheckPath) {
|
|
152
|
+
lines.push(`healthCheck.path = "${proxy.healthCheckPath}"`);
|
|
153
|
+
}
|
|
154
|
+
lines.push(`healthCheck.intervalSeconds = ${proxy.healthCheckInterval || 10}`);
|
|
155
|
+
lines.push(`healthCheck.timeoutSeconds = ${proxy.healthCheckTimeout || 3}`);
|
|
156
|
+
lines.push(`healthCheck.maxFailed = ${proxy.healthCheckMaxFailed || 3}`);
|
|
157
|
+
}
|
|
122
158
|
if (proxy.type === "http") {
|
|
123
159
|
lines.push("transport.useEncryption = false");
|
|
124
160
|
lines.push("transport.useCompression = false");
|
|
@@ -137,20 +173,41 @@ class FrpcTunnel {
|
|
|
137
173
|
log;
|
|
138
174
|
logBuffer = [];
|
|
139
175
|
maxLogLines = 200;
|
|
176
|
+
proxies = [];
|
|
140
177
|
constructor(options) {
|
|
141
178
|
this.options = options;
|
|
142
179
|
this.log = options.log || ((msg) => console.log(`[FRPC] ${msg}`));
|
|
180
|
+
const hyphaToken = options.serverConfig?.hyphaToken || process.env.HYPHA_TOKEN || "";
|
|
143
181
|
this.serverConfig = {
|
|
144
182
|
serverAddr: options.serverConfig?.serverAddr || process.env.FRPS_SERVER_ADDR || DEFAULT_FRPS_ADDR,
|
|
145
183
|
serverPort: options.serverConfig?.serverPort || parseInt(process.env.FRPS_SERVER_PORT || "", 10) || DEFAULT_FRPS_PORT,
|
|
146
|
-
authToken: options.serverConfig?.authToken || process.env.FRPS_AUTH_TOKEN ||
|
|
147
|
-
|
|
184
|
+
authToken: options.serverConfig?.authToken || process.env.FRPS_AUTH_TOKEN || FRPS_PUBLIC_TOKEN,
|
|
185
|
+
hyphaToken,
|
|
186
|
+
subDomainHost: options.serverConfig?.subDomainHost || process.env.FRPS_SUBDOMAIN_HOST || "svc.hypha.aicell.io"
|
|
148
187
|
};
|
|
149
|
-
if (!this.serverConfig.
|
|
188
|
+
if (!this.serverConfig.hyphaToken) {
|
|
150
189
|
throw new Error(
|
|
151
|
-
|
|
190
|
+
'Hypha token required for frpc authentication. Run "svamp login" or set HYPHA_TOKEN.'
|
|
152
191
|
);
|
|
153
192
|
}
|
|
193
|
+
const machineId = homedir();
|
|
194
|
+
const machineHash = createHash("sha256").update(machineId).digest("hex").slice(0, 6);
|
|
195
|
+
this.proxies = options.ports.map((port) => {
|
|
196
|
+
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)}`);
|
|
197
|
+
const proxyName = options.group ? `${options.name}-${port}-${machineHash}` : `${options.name}-${port}`;
|
|
198
|
+
return {
|
|
199
|
+
name: proxyName,
|
|
200
|
+
type: "http",
|
|
201
|
+
localIP: options.localHost || "127.0.0.1",
|
|
202
|
+
localPort: port,
|
|
203
|
+
subdomain,
|
|
204
|
+
group: options.group,
|
|
205
|
+
groupKey: options.groupKey,
|
|
206
|
+
healthCheckType: options.healthCheckType,
|
|
207
|
+
healthCheckPath: options.healthCheckPath,
|
|
208
|
+
healthCheckInterval: options.healthCheckInterval
|
|
209
|
+
};
|
|
210
|
+
});
|
|
154
211
|
const configDir = join(homedir(), ".svamp", "frpc");
|
|
155
212
|
mkdirSync(configDir, { recursive: true });
|
|
156
213
|
this.configPath = join(configDir, `${options.name}.toml`);
|
|
@@ -160,17 +217,7 @@ class FrpcTunnel {
|
|
|
160
217
|
if (this._destroyed) return;
|
|
161
218
|
if (this.process) return;
|
|
162
219
|
const frpcPath = await ensureFrpc(this.log);
|
|
163
|
-
const
|
|
164
|
-
const subdomain = this.options.subdomains?.get(port) || `${this.options.name}-${port}-${randomUUID().slice(0, 8)}`;
|
|
165
|
-
return {
|
|
166
|
-
name: `${this.options.name}-${port}`,
|
|
167
|
-
type: "http",
|
|
168
|
-
localIP: this.options.localHost || "127.0.0.1",
|
|
169
|
-
localPort: port,
|
|
170
|
-
subdomain
|
|
171
|
-
};
|
|
172
|
-
});
|
|
173
|
-
const configContent = generateFrpcConfig(this.serverConfig, proxies);
|
|
220
|
+
const configContent = generateFrpcConfig(this.serverConfig, this.proxies);
|
|
174
221
|
writeFileSync(this.configPath, configContent);
|
|
175
222
|
this.log(`Config written to ${this.configPath}`);
|
|
176
223
|
return new Promise((resolve, reject) => {
|
|
@@ -279,19 +326,20 @@ class FrpcTunnel {
|
|
|
279
326
|
getUrls() {
|
|
280
327
|
const urls = /* @__PURE__ */ new Map();
|
|
281
328
|
const base = this.serverConfig.subDomainHost || "svc.hypha.amun.ai";
|
|
282
|
-
for (const
|
|
283
|
-
const subdomain =
|
|
284
|
-
urls.set(
|
|
329
|
+
for (const proxy of this.proxies) {
|
|
330
|
+
const subdomain = proxy.subdomain || proxy.name;
|
|
331
|
+
urls.set(proxy.localPort, `https://${subdomain}.${base}`);
|
|
285
332
|
}
|
|
286
333
|
return urls;
|
|
287
334
|
}
|
|
288
335
|
}
|
|
289
|
-
async function runFrpcTunnel(name, ports, serverConfig) {
|
|
336
|
+
async function runFrpcTunnel(name, ports, serverConfig, tunnelOptions) {
|
|
290
337
|
const portList = ports.join(", ");
|
|
291
338
|
const tunnel = new FrpcTunnel({
|
|
292
339
|
name,
|
|
293
340
|
ports,
|
|
294
341
|
serverConfig,
|
|
342
|
+
...tunnelOptions,
|
|
295
343
|
onConnect: () => {
|
|
296
344
|
console.log(`Tunnel connected: ${name} \u2192 localhost:[${portList}]`);
|
|
297
345
|
const urls = tunnel.getUrls();
|
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-
|
|
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-YVIJdP0j.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|