@swarp/cli 0.0.3 → 0.0.4-rc.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarp/cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.4-rc.31",
4
4
  "description": "SWARP agent orchestration platform — CLI, MCP server, generator",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,54 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+
4
+ export function createWireGuardPeer(org, region, agentName) {
5
+ const confPath = `/tmp/${agentName}.conf`;
6
+ execFileSync("flyctl", ["wireguard", "create", org, region, agentName, confPath]);
7
+ return confPath;
8
+ }
9
+
10
+ export function extractDnsIP(confPath) {
11
+ const contents = readFileSync(confPath, "utf8");
12
+ for (const line of contents.split("\n")) {
13
+ const trimmed = line.trim();
14
+ if (trimmed.startsWith("DNS")) {
15
+ const [, value] = trimmed.split("=");
16
+ return value.trim();
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+
22
+ export function addKeepalive(confPath, interval = 25) {
23
+ const contents = readFileSync(confPath, "utf8");
24
+ if (contents.includes("PersistentKeepalive")) {
25
+ return confPath;
26
+ }
27
+ const updated = contents.replace(
28
+ /(\[Peer\])/,
29
+ `$1\nPersistentKeepalive = ${interval}`
30
+ );
31
+ writeFileSync(confPath, updated, "utf8");
32
+ return confPath;
33
+ }
34
+
35
+ export function pushWireGuardToSprite(agentName, confPath) {
36
+ const conf = readFileSync(confPath, "utf8");
37
+ execFileSync(
38
+ "sprite",
39
+ [
40
+ "exec",
41
+ "-s",
42
+ agentName,
43
+ "--",
44
+ "bash",
45
+ "-c",
46
+ "mkdir -p /etc/wireguard && cat > /etc/wireguard/wg0.conf && wg-quick up wg0",
47
+ ],
48
+ { input: conf }
49
+ );
50
+ }
51
+
52
+ export function removeWireGuardPeer(org, agentName) {
53
+ execFileSync("flyctl", ["wireguard", "remove", org, agentName]);
54
+ }
@@ -0,0 +1,91 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { writeFileSync, readFileSync } from "node:fs";
5
+
6
+ vi.mock("node:child_process", () => ({ execFileSync: vi.fn() }));
7
+
8
+ import { execFileSync } from "node:child_process";
9
+ import {
10
+ createWireGuardPeer,
11
+ extractDnsIP,
12
+ addKeepalive,
13
+ pushWireGuardToSprite,
14
+ removeWireGuardPeer,
15
+ } from "./wireguard.mjs";
16
+
17
+ const SAMPLE_CONF = `[Interface]
18
+ PrivateKey = abc123
19
+ Address = fdaa:0:5e4f:a7b:23c:0:a:2/120
20
+ DNS = fdaa:0:5e4f::3
21
+
22
+ [Peer]
23
+ PublicKey = xyz789
24
+ AllowedIPs = fdaa:0:5e4f::/48
25
+ Endpoint = sea1.gateway.6pn.dev:51820
26
+ `;
27
+
28
+ function writeTempConf(contents = SAMPLE_CONF) {
29
+ const path = join(tmpdir(), `wg-test-${Date.now()}.conf`);
30
+ writeFileSync(path, contents, "utf8");
31
+ return path;
32
+ }
33
+
34
+ describe("createWireGuardPeer", () => {
35
+ beforeEach(() => vi.clearAllMocks());
36
+
37
+ it("calls flyctl with correct args and returns conf path", () => {
38
+ const result = createWireGuardPeer("my-org", "sea", "my-agent");
39
+ expect(execFileSync).toHaveBeenCalledWith("flyctl", [
40
+ "wireguard",
41
+ "create",
42
+ "my-org",
43
+ "sea",
44
+ "my-agent",
45
+ "/tmp/my-agent.conf",
46
+ ]);
47
+ expect(result).toBe("/tmp/my-agent.conf");
48
+ });
49
+ });
50
+
51
+ describe("extractDnsIP", () => {
52
+ it("parses the DNS IP from a WG config", () => {
53
+ const confPath = writeTempConf();
54
+ expect(extractDnsIP(confPath)).toBe("fdaa:0:5e4f::3");
55
+ });
56
+ });
57
+
58
+ describe("addKeepalive", () => {
59
+ it("inserts PersistentKeepalive after [Peer]", () => {
60
+ const confPath = writeTempConf();
61
+ addKeepalive(confPath);
62
+ const result = readFileSync(confPath, "utf8");
63
+ expect(result).toContain("PersistentKeepalive = 25");
64
+ expect(result.indexOf("[Peer]")).toBeLessThan(
65
+ result.indexOf("PersistentKeepalive")
66
+ );
67
+ });
68
+
69
+ it("is idempotent — does not add keepalive twice", () => {
70
+ const confPath = writeTempConf();
71
+ addKeepalive(confPath);
72
+ addKeepalive(confPath);
73
+ const result = readFileSync(confPath, "utf8");
74
+ const count = (result.match(/PersistentKeepalive/g) || []).length;
75
+ expect(count).toBe(1);
76
+ });
77
+ });
78
+
79
+ describe("removeWireGuardPeer", () => {
80
+ beforeEach(() => vi.clearAllMocks());
81
+
82
+ it("calls flyctl with correct args", () => {
83
+ removeWireGuardPeer("my-org", "my-agent");
84
+ expect(execFileSync).toHaveBeenCalledWith("flyctl", [
85
+ "wireguard",
86
+ "remove",
87
+ "my-org",
88
+ "my-agent",
89
+ ]);
90
+ });
91
+ });
@@ -93,11 +93,6 @@ function buildRouterYaml() {
93
93
  registration:
94
94
  ttl_minutes: 30
95
95
  persist_path: /data/registry.json
96
-
97
- tls:
98
- ca_cert_secret: SWARP_MTLS_CA_CERT
99
- router_cert_secret: SWARP_MTLS_ROUTER_CERT
100
- router_key_secret: SWARP_MTLS_ROUTER_KEY
101
96
  `;
102
97
  }
103
98
 
@@ -136,6 +131,7 @@ function updateMcpJson(cwd) {
136
131
  console.log(` update ${mcpPath} — added swarp entry`);
137
132
  }
138
133
 
134
+
139
135
  // ── Main entry point ──────────────────────────────────────────────────────────
140
136
 
141
137
  /**
@@ -4,6 +4,24 @@ import path from 'node:path';
4
4
  import os from 'node:os';
5
5
  import { runInit } from './index.mjs';
6
6
 
7
+ // Track calls made to execFileSync by the module under test.
8
+ // We cannot vi.spyOn a CJS binding in ESM, so we use a shared call log
9
+ // populated via vi.mock instead.
10
+ const execFileSyncCalls = [];
11
+ let execFileSyncImpl = null;
12
+
13
+ vi.mock('node:child_process', async (importOriginal) => {
14
+ const original = await importOriginal();
15
+ return {
16
+ ...original,
17
+ execFileSync: (...args) => {
18
+ execFileSyncCalls.push(args);
19
+ if (execFileSyncImpl) return execFileSyncImpl(...args);
20
+ return original.execFileSync(...args);
21
+ },
22
+ };
23
+ });
24
+
7
25
  // ── Helpers ───────────────────────────────────────────────────────────────────
8
26
 
9
27
  function makeTmpDir() {
@@ -28,6 +46,8 @@ describe('runInit', () => {
28
46
 
29
47
  beforeEach(() => {
30
48
  tmpDir = makeTmpDir();
49
+ execFileSyncCalls.length = 0;
50
+ execFileSyncImpl = null;
31
51
  // Silence stdout/stderr during tests
32
52
  vi.spyOn(console, 'log').mockImplementation(() => {});
33
53
  vi.spyOn(console, 'warn').mockImplementation(() => {});
@@ -35,6 +55,7 @@ describe('runInit', () => {
35
55
 
36
56
  afterEach(() => {
37
57
  cleanDir(tmpDir);
58
+ execFileSyncImpl = null;
38
59
  vi.restoreAllMocks();
39
60
  });
40
61
 
@@ -72,7 +93,7 @@ describe('runInit', () => {
72
93
  expect(fs.existsSync(p)).toBe(true);
73
94
  const content = fs.readFileSync(p, 'utf8');
74
95
  expect(content).toContain('grpc_port: 50051');
75
- expect(content).toContain('SWARP_MTLS_CA_CERT');
96
+ expect(content).toContain('grpc_port: 50051');
76
97
  });
77
98
 
78
99
  it('creates .github/workflows/deploy-agents.yml', async () => {
@@ -165,4 +186,5 @@ describe('runInit', () => {
165
186
  const messages = logSpy.mock.calls.map((c) => c[0]).join('\n');
166
187
  expect(messages).toContain('/swarp');
167
188
  });
189
+
168
190
  });