@cardelli/ambit 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/esm/cli/commands/create/index.d.ts +2 -0
  2. package/esm/cli/commands/create/index.d.ts.map +1 -0
  3. package/esm/cli/commands/create/index.js +292 -0
  4. package/esm/cli/commands/create/machine.d.ts +33 -0
  5. package/esm/cli/commands/create/machine.d.ts.map +1 -0
  6. package/esm/cli/commands/create/machine.js +162 -0
  7. package/esm/cli/commands/deploy/index.d.ts +2 -0
  8. package/esm/cli/commands/deploy/index.d.ts.map +1 -0
  9. package/esm/cli/commands/deploy/index.js +290 -0
  10. package/esm/cli/commands/deploy/machine.d.ts +52 -0
  11. package/esm/cli/commands/deploy/machine.d.ts.map +1 -0
  12. package/esm/cli/commands/deploy/machine.js +116 -0
  13. package/esm/cli/commands/deploy/modes.d.ts +18 -0
  14. package/esm/cli/commands/deploy/modes.d.ts.map +1 -0
  15. package/esm/cli/commands/deploy/modes.js +152 -0
  16. package/esm/cli/commands/destroy/app.d.ts +2 -0
  17. package/esm/cli/commands/destroy/app.d.ts.map +1 -0
  18. package/esm/cli/commands/destroy/app.js +173 -0
  19. package/esm/cli/commands/destroy/index.d.ts +2 -0
  20. package/esm/cli/commands/destroy/index.d.ts.map +1 -0
  21. package/esm/cli/commands/destroy/index.js +63 -0
  22. package/esm/cli/commands/destroy/network.d.ts +2 -0
  23. package/esm/cli/commands/destroy/network.d.ts.map +1 -0
  24. package/esm/cli/commands/destroy/network.js +210 -0
  25. package/esm/cli/commands/doctor.d.ts.map +1 -0
  26. package/esm/cli/commands/doctor.js +295 -0
  27. package/esm/{src/cli → cli}/commands/list.d.ts.map +1 -1
  28. package/esm/{src/cli → cli}/commands/list.js +39 -54
  29. package/esm/cli/commands/status.d.ts.map +1 -0
  30. package/esm/cli/commands/status.js +331 -0
  31. package/esm/cli/mod.d.ts.map +1 -0
  32. package/esm/{src/cli → cli}/mod.js +4 -4
  33. package/esm/deno.d.ts +4 -18
  34. package/esm/deno.js +5 -19
  35. package/esm/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
  36. package/esm/deps/jsr.io/@zod/zod/4.3.6/src/v4/core/json-schema-generator.d.ts +1 -1
  37. package/esm/lib/args.d.ts +11 -0
  38. package/esm/lib/args.d.ts.map +1 -0
  39. package/esm/lib/args.js +28 -0
  40. package/esm/lib/cli.d.ts +0 -2
  41. package/esm/lib/cli.d.ts.map +1 -1
  42. package/esm/lib/cli.js +41 -27
  43. package/esm/lib/command.d.ts +21 -49
  44. package/esm/lib/command.d.ts.map +1 -1
  45. package/esm/lib/command.js +55 -95
  46. package/esm/lib/machine.d.ts +11 -0
  47. package/esm/lib/machine.d.ts.map +1 -0
  48. package/esm/lib/machine.js +15 -0
  49. package/esm/lib/output.d.ts +3 -2
  50. package/esm/lib/output.d.ts.map +1 -1
  51. package/esm/lib/output.js +25 -11
  52. package/esm/lib/result.d.ts +18 -7
  53. package/esm/lib/result.d.ts.map +1 -1
  54. package/esm/lib/result.js +46 -1
  55. package/esm/main.d.ts +6 -6
  56. package/esm/main.d.ts.map +1 -1
  57. package/esm/main.js +7 -9
  58. package/esm/providers/fly.d.ts +81 -0
  59. package/esm/providers/fly.d.ts.map +1 -0
  60. package/esm/providers/fly.js +372 -0
  61. package/esm/providers/tailscale.d.ts +31 -0
  62. package/esm/providers/tailscale.d.ts.map +1 -0
  63. package/esm/providers/tailscale.js +150 -0
  64. package/esm/{src/schemas → schemas}/fly.d.ts +1 -11
  65. package/esm/schemas/fly.d.ts.map +1 -0
  66. package/esm/{src/schemas → schemas}/fly.js +14 -56
  67. package/esm/{src/schemas → schemas}/tailscale.d.ts +1 -2
  68. package/esm/schemas/tailscale.d.ts.map +1 -0
  69. package/esm/{src/schemas → schemas}/tailscale.js +2 -3
  70. package/esm/src/{docker/router → router}/Dockerfile +0 -11
  71. package/esm/src/router/start.sh +101 -0
  72. package/esm/util/constants.d.ts +13 -0
  73. package/esm/util/constants.d.ts.map +1 -0
  74. package/esm/util/constants.js +34 -0
  75. package/esm/{src → util}/credentials.d.ts +0 -1
  76. package/esm/util/credentials.d.ts.map +1 -0
  77. package/esm/{src → util}/credentials.js +3 -5
  78. package/esm/{src → util}/discovery.d.ts +20 -4
  79. package/esm/util/discovery.d.ts.map +1 -0
  80. package/esm/{src → util}/discovery.js +38 -15
  81. package/esm/util/fly-transforms.d.ts +27 -0
  82. package/esm/util/fly-transforms.d.ts.map +1 -0
  83. package/esm/util/fly-transforms.js +87 -0
  84. package/esm/{src → util}/guard.d.ts +1 -2
  85. package/esm/util/guard.d.ts.map +1 -0
  86. package/esm/{src → util}/guard.js +27 -27
  87. package/esm/util/naming.d.ts +5 -0
  88. package/esm/util/naming.d.ts.map +1 -0
  89. package/esm/util/naming.js +12 -0
  90. package/esm/{src → util}/resolve.d.ts +2 -3
  91. package/esm/util/resolve.d.ts.map +1 -0
  92. package/esm/{src → util}/resolve.js +1 -2
  93. package/esm/util/session.d.ts +16 -0
  94. package/esm/util/session.d.ts.map +1 -0
  95. package/esm/util/session.js +19 -0
  96. package/esm/util/tailscale-local.d.ts +13 -0
  97. package/esm/util/tailscale-local.d.ts.map +1 -0
  98. package/esm/util/tailscale-local.js +63 -0
  99. package/esm/{src → util}/template.d.ts +2 -4
  100. package/esm/util/template.d.ts.map +1 -0
  101. package/esm/{src → util}/template.js +14 -17
  102. package/package.json +1 -43
  103. package/esm/lib/paths.d.ts +0 -3
  104. package/esm/lib/paths.d.ts.map +0 -1
  105. package/esm/lib/paths.js +0 -5
  106. package/esm/src/cli/commands/create.d.ts +0 -2
  107. package/esm/src/cli/commands/create.d.ts.map +0 -1
  108. package/esm/src/cli/commands/create.js +0 -294
  109. package/esm/src/cli/commands/deploy.d.ts +0 -2
  110. package/esm/src/cli/commands/deploy.d.ts.map +0 -1
  111. package/esm/src/cli/commands/deploy.js +0 -426
  112. package/esm/src/cli/commands/destroy.d.ts +0 -2
  113. package/esm/src/cli/commands/destroy.d.ts.map +0 -1
  114. package/esm/src/cli/commands/destroy.js +0 -340
  115. package/esm/src/cli/commands/doctor.d.ts.map +0 -1
  116. package/esm/src/cli/commands/doctor.js +0 -141
  117. package/esm/src/cli/commands/status.d.ts.map +0 -1
  118. package/esm/src/cli/commands/status.js +0 -152
  119. package/esm/src/cli/mod.d.ts.map +0 -1
  120. package/esm/src/credentials.d.ts.map +0 -1
  121. package/esm/src/discovery.d.ts.map +0 -1
  122. package/esm/src/docker/router/start.sh +0 -146
  123. package/esm/src/guard.d.ts.map +0 -1
  124. package/esm/src/providers/fly.d.ts +0 -70
  125. package/esm/src/providers/fly.d.ts.map +0 -1
  126. package/esm/src/providers/fly.js +0 -411
  127. package/esm/src/providers/tailscale.d.ts +0 -31
  128. package/esm/src/providers/tailscale.d.ts.map +0 -1
  129. package/esm/src/providers/tailscale.js +0 -195
  130. package/esm/src/resolve.d.ts.map +0 -1
  131. package/esm/src/schemas/config.d.ts +0 -5
  132. package/esm/src/schemas/config.d.ts.map +0 -1
  133. package/esm/src/schemas/config.js +0 -22
  134. package/esm/src/schemas/fly.d.ts.map +0 -1
  135. package/esm/src/schemas/tailscale.d.ts.map +0 -1
  136. package/esm/src/template.d.ts.map +0 -1
  137. /package/esm/{src/cli → cli}/commands/doctor.d.ts +0 -0
  138. /package/esm/{src/cli → cli}/commands/list.d.ts +0 -0
  139. /package/esm/{src/cli → cli}/commands/status.d.ts +0 -0
  140. /package/esm/{src/cli → cli}/mod.d.ts +0 -0
  141. /package/esm/src/{docker/router → router}/fly.toml +0 -0
@@ -1,146 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- # =============================================================================
5
- # ambit - Self-Configuring Tailscale Subnet Router
6
- # =============================================================================
7
- # State is persisted to /var/lib/tailscale via Fly volume.
8
- # On first run: creates auth key (with tags), authenticates, approves routes.
9
- # On restart: reuses existing state, no new device created.
10
- # =============================================================================
11
-
12
- echo "Router: Enabling IP Forwarding"
13
- echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
14
- echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
15
- sysctl -p /etc/sysctl.conf
16
-
17
- echo "Router: Starting Tailscaled"
18
- /usr/local/bin/tailscaled \
19
- --state=/var/lib/tailscale/tailscaled.state \
20
- --socket=/var/run/tailscale/tailscaled.sock &
21
-
22
- # Wait for tailscaled to be ready
23
- sleep 3
24
-
25
- echo "Router: Extracting Fly.io Subnet"
26
- SUBNET=$(grep fly-local-6pn /etc/hosts | awk '{print $1}' | cut -d: -f1-3)::/48
27
- echo "Router: Subnet ${SUBNET}"
28
-
29
- if /usr/local/bin/tailscale status --json 2>/dev/null | jq -e '.BackendState == "Running"' > /dev/null 2>&1; then
30
- echo "Router: Already Authenticated (Using Persisted State)"
31
-
32
- /usr/local/bin/tailscale up \
33
- --hostname="${FLY_APP_NAME:-ambit}" \
34
- --advertise-routes="${SUBNET}"
35
- else
36
- # First run - need to authenticate
37
- if [ -n "${TAILSCALE_API_TOKEN}" ]; then
38
- echo "Router: Creating Auth Key (First Run)"
39
-
40
- TAGS_JSON="[]"
41
- if [ -n "${TAILSCALE_TAGS}" ]; then
42
- TAGS_JSON=$(echo "${TAILSCALE_TAGS}" | jq -R 'split(",")')
43
- fi
44
-
45
- AUTH_KEY_RESPONSE=$(curl -s -X POST \
46
- -u "${TAILSCALE_API_TOKEN}:" \
47
- -H "Content-Type: application/json" \
48
- -d "$(jq -n \
49
- --argjson tags "${TAGS_JSON}" \
50
- '{
51
- capabilities: { devices: { create: {
52
- reusable: false,
53
- ephemeral: false,
54
- preauthorized: true,
55
- tags: $tags
56
- }}},
57
- expirySeconds: 300
58
- }' | jq 'if .capabilities.devices.create.tags == [] then .capabilities.devices.create |= del(.tags) else . end')" \
59
- "https://api.tailscale.com/api/v2/tailnet/-/keys")
60
-
61
- TAILSCALE_AUTHKEY=$(echo "${AUTH_KEY_RESPONSE}" | jq -r '.key')
62
-
63
- if [ -z "${TAILSCALE_AUTHKEY}" ] || [ "${TAILSCALE_AUTHKEY}" = "null" ]; then
64
- echo "Router: ERROR - Failed to Create Auth Key"
65
- echo "${AUTH_KEY_RESPONSE}"
66
- exit 1
67
- fi
68
-
69
- echo "Router: Auth Key Created"
70
- elif [ -z "${TAILSCALE_AUTHKEY}" ]; then
71
- echo "Router: ERROR - No TAILSCALE_API_TOKEN or TAILSCALE_AUTHKEY Provided"
72
- exit 1
73
- fi
74
-
75
- echo "Router: Authenticating to Tailscale"
76
- /usr/local/bin/tailscale up \
77
- --authkey="${TAILSCALE_AUTHKEY}" \
78
- --hostname="${FLY_APP_NAME:-ambit}" \
79
- --advertise-routes="${SUBNET}"
80
- fi
81
-
82
- echo "Router: Getting Node Key"
83
- NODE_KEY=$(/usr/local/bin/tailscale status --json | jq -r '.Self.PublicKey')
84
- echo "Router: Node Key ${NODE_KEY}"
85
-
86
- # Self-approve routes if we have API access
87
- # This is a fallback — if the user has autoApprovers configured in their
88
- # Tailscale policy file, routes are approved automatically and this block
89
- # is a no-op (routes are already enabled).
90
- if [ -n "${TAILSCALE_API_TOKEN}" ]; then
91
- echo "Router: Finding Device ID"
92
-
93
- DEVICES_RESPONSE=$(curl -s \
94
- -u "${TAILSCALE_API_TOKEN}:" \
95
- "https://api.tailscale.com/api/v2/tailnet/-/devices")
96
-
97
- DEVICE_ID=$(echo "${DEVICES_RESPONSE}" | jq -r ".devices[] | select(.nodeKey == \"${NODE_KEY}\") | .id")
98
-
99
- if [ -n "${DEVICE_ID}" ] && [ "${DEVICE_ID}" != "null" ]; then
100
- echo "Router: Device ID ${DEVICE_ID}"
101
- echo "Router: Approving Subnet Routes"
102
-
103
- curl -s -X POST \
104
- -u "${TAILSCALE_API_TOKEN}:" \
105
- -H "Content-Type: application/json" \
106
- -d "{\"routes\": [\"${SUBNET}\"]}" \
107
- "https://api.tailscale.com/api/v2/device/${DEVICE_ID}/routes" > /dev/null
108
-
109
- echo "Router: Routes Approved"
110
- else
111
- echo "Router: WARNING - Could Not Find Device ID"
112
- echo "Router: Routes May Need Manual Approval"
113
- fi
114
- fi
115
-
116
- echo "Router: Fully Configured"
117
-
118
- # Start SOCKS5 proxy for bidirectional tailnet access
119
- PRIVATE_IP=$(grep fly-local-6pn /etc/hosts | awk '{print $1}')
120
- echo "Router: Starting SOCKS5 Proxy on [${PRIVATE_IP}]:1080"
121
- /usr/local/bin/microsocks -i "$PRIVATE_IP" -p 1080 &
122
-
123
- echo "Router: Starting DNS Proxy"
124
-
125
- # Generate Corefile for CoreDNS
126
- # Rewrites NETWORK_NAME TLD to .flycast before forwarding to Fly DNS.
127
- # .flycast resolves to the Flycast address (private_v6) which routes through
128
- # Fly Proxy — enabling autostart/autostop and load balancing. The Flycast IP
129
- # is within the network's /48 subnet so it's routable through the tailnet.
130
- if [ -n "${NETWORK_NAME}" ]; then
131
- echo "Router: DNS Rewrite ${NETWORK_NAME} -> flycast"
132
- cat > /etc/coredns/Corefile <<EOF
133
- .:53 {
134
- rewrite name suffix .${NETWORK_NAME}. .flycast. answer auto
135
- forward . fdaa::3
136
- }
137
- EOF
138
- else
139
- cat > /etc/coredns/Corefile <<EOF
140
- .:53 {
141
- forward . fdaa::3
142
- }
143
- EOF
144
- fi
145
-
146
- exec /usr/local/bin/coredns -conf /etc/coredns/Corefile
@@ -1 +0,0 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/src/guard.ts"],"names":[],"mappings":"AAWA,OAAO,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqLtD;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,OACN,CAAC;AAMtC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAOjD;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAwDhE;AAMD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAwF5B"}
@@ -1,70 +0,0 @@
1
- import "../../_dnt.polyfills.js";
2
- import { type FlyApp, type FlyAppInfo, type FlyIp, type FlyMachine } from "../schemas/fly.js";
3
- /**
4
- * Thrown when a `fly deploy` command fails. Carries the raw stderr so callers
5
- * can surface it through `out` (respecting JSON mode) instead of printing
6
- * directly.
7
- */
8
- export declare class FlyDeployError extends Error {
9
- /** Last meaningful line from flyctl stderr. */
10
- readonly detail: string;
11
- constructor(app: string, stderr: string);
12
- }
13
- export type MachineSize = "shared-cpu-1x" | "shared-cpu-2x" | "shared-cpu-4x";
14
- export interface MachineConfig {
15
- size: MachineSize;
16
- memoryMb?: number;
17
- region?: string;
18
- autoStopSeconds?: number;
19
- }
20
- export declare const getSizeConfig: (size: MachineSize) => {
21
- cpus: number;
22
- memoryMb: number;
23
- };
24
- export interface MachineResult {
25
- id: string;
26
- state: string;
27
- size: string;
28
- region: string;
29
- privateIp?: string;
30
- }
31
- export interface SafeDeployOptions {
32
- image?: string;
33
- config?: string;
34
- region?: string;
35
- }
36
- export interface FlyProvider {
37
- ensureInstalled(): Promise<void>;
38
- ensureAuth(options?: {
39
- interactive?: boolean;
40
- }): Promise<string>;
41
- listOrgs(): Promise<Record<string, string>>;
42
- createApp(name: string, org: string, options?: {
43
- network?: string;
44
- }): Promise<void>;
45
- deleteApp(name: string): Promise<void>;
46
- listApps(org?: string): Promise<FlyApp[]>;
47
- appExists(name: string): Promise<boolean>;
48
- listMachines(app: string): Promise<FlyMachine[]>;
49
- listMachinesMapped(app: string): Promise<MachineResult[]>;
50
- createMachine(app: string, config: MachineConfig): Promise<MachineResult>;
51
- destroyMachine(app: string, machineId: string): Promise<void>;
52
- setSecrets(app: string, secrets: Record<string, string>, options?: {
53
- stage?: boolean;
54
- }): Promise<void>;
55
- routerDeploy(app: string, dockerfilePath: string, config?: {
56
- region?: string;
57
- }): Promise<void>;
58
- listIps(app: string): Promise<FlyIp[]>;
59
- releaseIp(app: string, address: string): Promise<void>;
60
- allocateFlycastIp(app: string, network: string): Promise<void>;
61
- getConfig(app: string): Promise<Record<string, unknown> | null>;
62
- deploySafe(app: string, options: SafeDeployOptions): Promise<void>;
63
- listCerts(app: string): Promise<string[]>;
64
- removeCert(app: string, hostname: string): Promise<void>;
65
- getFlyToken(): Promise<string>;
66
- listAppsWithNetwork(org: string): Promise<FlyAppInfo[]>;
67
- }
68
- export declare const createFlyProvider: () => FlyProvider;
69
- export declare const getRouterAppName: (network: string, randomSuffix: string) => string;
70
- //# sourceMappingURL=fly.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../../src/src/providers/fly.ts"],"names":[],"mappings":"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"}
@@ -1,411 +0,0 @@
1
- // =============================================================================
2
- // Fly.io Provider - Wraps flyctl CLI
3
- // =============================================================================
4
- import "../../_dnt.polyfills.js";
5
- import * as dntShim from "../../_dnt.shims.js";
6
- import { runCommand, runCommandJson, runInteractive, runQuiet, } from "../../lib/command.js";
7
- import { commandExists, die, Spinner } from "../../lib/cli.js";
8
- import { dirname, resolve } from "../../deps/jsr.io/@std/path/1.1.4/mod.js";
9
- import { FlyAppInfoListSchema, FlyAppsListSchema, FlyAuthSchema, FlyIpListSchema, FlyMachinesListSchema, FlyOrgsSchema, FlyStatusSchema, mapFlyMachineSize, mapFlyMachineState, } from "../schemas/fly.js";
10
- import { fileExists } from "../../lib/cli.js";
11
- // =============================================================================
12
- // Constants
13
- // =============================================================================
14
- const ROUTER_APP_PREFIX = "ambit-";
15
- // =============================================================================
16
- // Deploy Error
17
- // =============================================================================
18
- /**
19
- * Thrown when a `fly deploy` command fails. Carries the raw stderr so callers
20
- * can surface it through `out` (respecting JSON mode) instead of printing
21
- * directly.
22
- */
23
- export class FlyDeployError extends Error {
24
- /** Last meaningful line from flyctl stderr. */
25
- detail;
26
- constructor(app, stderr) {
27
- const detail = extractErrorDetail(stderr);
28
- super(`Deploy Failed for '${app}'`);
29
- this.name = "FlyDeployError";
30
- this.detail = detail;
31
- }
32
- }
33
- /**
34
- * Pull the last non-empty, non-decoration line from fly stderr.
35
- * Fly often prints progress lines then the actual error at the end.
36
- */
37
- const extractErrorDetail = (stderr) => {
38
- const lines = stderr
39
- .split("\n")
40
- .map((l) => l.replace(/\x1b\[[0-9;]*m/g, "").trim())
41
- .filter((l) => l.length > 0 && !l.startsWith("-->") && l !== "Error");
42
- return lines[lines.length - 1] ?? "unknown error";
43
- };
44
- export const getSizeConfig = (size) => {
45
- switch (size) {
46
- case "shared-cpu-1x":
47
- return { cpus: 1, memoryMb: 1024 };
48
- case "shared-cpu-2x":
49
- return { cpus: 2, memoryMb: 2048 };
50
- case "shared-cpu-4x":
51
- return { cpus: 4, memoryMb: 4096 };
52
- }
53
- };
54
- // =============================================================================
55
- // Create Fly Provider
56
- // =============================================================================
57
- export const createFlyProvider = () => {
58
- return {
59
- async ensureInstalled() {
60
- if (!(await commandExists("fly"))) {
61
- return die("Flyctl Not Found. Install from https://fly.io/docs/flyctl/install/");
62
- }
63
- },
64
- async ensureAuth(options) {
65
- const interactive = options?.interactive ?? true;
66
- const result = await runCommand(["fly", "auth", "whoami", "--json"]);
67
- if (result.success) {
68
- try {
69
- const parsed = FlyAuthSchema.safeParse(JSON.parse(result.stdout));
70
- if (parsed.success) {
71
- return parsed.data.email;
72
- }
73
- }
74
- catch {
75
- // Parse failed, need to authenticate
76
- }
77
- }
78
- if (!interactive) {
79
- return die("Not Authenticated with Fly.io. Run 'fly auth login' First");
80
- }
81
- const loginResult = await runInteractive(["fly", "auth", "login"]);
82
- if (!loginResult.success) {
83
- return die("Fly.io Authentication Failed");
84
- }
85
- const checkResult = await runCommand(["fly", "auth", "whoami", "--json"]);
86
- if (!checkResult.success) {
87
- return die("Fly.io Authentication Verification Failed");
88
- }
89
- const parsed = FlyAuthSchema.safeParse(JSON.parse(checkResult.stdout));
90
- if (!parsed.success || !parsed.data) {
91
- return die("Fly.io Authentication Response Invalid");
92
- }
93
- return parsed.data.email;
94
- },
95
- async listOrgs() {
96
- const result = await runCommandJson(["fly", "orgs", "list", "--json"]);
97
- if (!result.success || !result.data) {
98
- return die("Failed to List Organizations");
99
- }
100
- const parsed = FlyOrgsSchema.safeParse(result.data);
101
- if (!parsed.success) {
102
- return die("Failed to Parse Organizations");
103
- }
104
- return parsed.data;
105
- },
106
- async listApps(org) {
107
- const args = ["fly", "apps", "list", "--json"];
108
- if (org) {
109
- args.push("--org", org);
110
- }
111
- const result = await runCommand(args);
112
- if (!result.success) {
113
- return [];
114
- }
115
- try {
116
- const parsed = FlyAppsListSchema.safeParse(JSON.parse(result.stdout));
117
- return parsed.success ? parsed.data : [];
118
- }
119
- catch {
120
- return [];
121
- }
122
- },
123
- async createApp(name, org, options) {
124
- const args = ["fly", "apps", "create", name, "--org", org, "--json"];
125
- if (options?.network) {
126
- args.push("--network", options.network);
127
- }
128
- const result = await runQuiet("Creating App", args);
129
- if (!result.success) {
130
- return die(`Failed to Create App '${name}'`);
131
- }
132
- },
133
- async deleteApp(name) {
134
- const result = await runQuiet("Deleting App", [
135
- "fly",
136
- "apps",
137
- "destroy",
138
- name,
139
- "--yes",
140
- ]);
141
- if (!result.success) {
142
- return die(`Failed to Delete App '${name}'`);
143
- }
144
- },
145
- async appExists(name) {
146
- const result = await runCommand(["fly", "status", "-a", name, "--json"]);
147
- if (!result.success)
148
- return false;
149
- try {
150
- const parsed = FlyStatusSchema.safeParse(JSON.parse(result.stdout));
151
- return parsed.success && !!parsed.data.ID;
152
- }
153
- catch {
154
- return false;
155
- }
156
- },
157
- async listMachines(app) {
158
- const result = await runCommandJson(["fly", "machines", "list", "-a", app, "--json"]);
159
- if (!result.success || !result.data) {
160
- return [];
161
- }
162
- const parsed = FlyMachinesListSchema.safeParse(result.data);
163
- return parsed.success ? parsed.data : [];
164
- },
165
- async listMachinesMapped(app) {
166
- const raw = await this.listMachines(app);
167
- return raw.map((m) => ({
168
- id: m.id,
169
- state: mapFlyMachineState(m.state),
170
- size: mapFlyMachineSize(m.config?.guest),
171
- region: m.region,
172
- privateIp: m.private_ip,
173
- }));
174
- },
175
- async createMachine(app, config) {
176
- const existingMachines = await this.listMachinesMapped(app);
177
- if (existingMachines.length === 0) {
178
- return die("No Existing Machine to Clone. Run 'fly deploy' First");
179
- }
180
- const sourceMachine = existingMachines[0];
181
- const sizeConfig = getSizeConfig(config.size);
182
- const memoryMb = config.memoryMb ?? sizeConfig.memoryMb;
183
- const args = [
184
- "fly",
185
- "machine",
186
- "clone",
187
- sourceMachine.id,
188
- "-a",
189
- app,
190
- "--vm-cpus",
191
- String(sizeConfig.cpus),
192
- "--vm-memory",
193
- String(memoryMb),
194
- ];
195
- if (config.region) {
196
- args.push("--region", config.region);
197
- }
198
- const spinner = new Spinner();
199
- spinner.start(`Creating ${config.size} Machine`);
200
- const result = await runCommand(args);
201
- if (!result.success) {
202
- spinner.fail("Machine Creation Failed");
203
- return die(result.stderr || "Unknown Error");
204
- }
205
- spinner.success(`Created ${config.size} Machine`);
206
- const machines = await this.listMachinesMapped(app);
207
- const newest = machines[machines.length - 1];
208
- if (!newest) {
209
- return die("Created Machine Not Found");
210
- }
211
- return newest;
212
- },
213
- async destroyMachine(app, machineId) {
214
- const shortId = machineId.slice(0, 8);
215
- const result = await runQuiet(`Destroying Machine ${shortId}`, [
216
- "fly",
217
- "machines",
218
- "destroy",
219
- machineId,
220
- "-a",
221
- app,
222
- "--force",
223
- ]);
224
- if (!result.success) {
225
- return die(`Failed to Destroy Machine '${shortId}'`);
226
- }
227
- },
228
- async setSecrets(app, secrets, options) {
229
- const pairs = Object.entries(secrets)
230
- .filter(([_, v]) => v !== undefined && v !== "")
231
- .map(([k, v]) => `${k}=${v}`);
232
- if (pairs.length === 0)
233
- return;
234
- const args = ["fly", "secrets", "set", ...pairs, "-a", app];
235
- if (options?.stage) {
236
- args.push("--stage");
237
- }
238
- const result = await runQuiet(`Setting ${pairs.length} Secret(s)`, args);
239
- if (!result.success) {
240
- return die("Failed to Set Secrets");
241
- }
242
- },
243
- async routerDeploy(app, dockerDir, config) {
244
- const args = [
245
- "fly",
246
- "deploy",
247
- dockerDir,
248
- "-a",
249
- app,
250
- "--yes",
251
- "--ha=false",
252
- ];
253
- if (config?.region) {
254
- args.push("--primary-region", config.region);
255
- }
256
- const result = await runCommand(args);
257
- if (!result.success) {
258
- throw new FlyDeployError(app, result.stderr);
259
- }
260
- },
261
- async listIps(app) {
262
- const result = await runCommand([
263
- "fly",
264
- "ips",
265
- "list",
266
- "-a",
267
- app,
268
- "--json",
269
- ]);
270
- if (!result.success)
271
- return [];
272
- try {
273
- const parsed = FlyIpListSchema.safeParse(JSON.parse(result.stdout));
274
- return parsed.success ? parsed.data : [];
275
- }
276
- catch {
277
- return [];
278
- }
279
- },
280
- async releaseIp(app, address) {
281
- const result = await runCommand([
282
- "fly",
283
- "ips",
284
- "release",
285
- address,
286
- "-a",
287
- app,
288
- ]);
289
- if (!result.success) {
290
- return die(`Failed to Release IP ${address} from '${app}'`);
291
- }
292
- },
293
- async allocateFlycastIp(app, network) {
294
- const result = await runCommand([
295
- "fly",
296
- "ips",
297
- "allocate-v6",
298
- "--private",
299
- "--network",
300
- network,
301
- "-a",
302
- app,
303
- ]);
304
- if (!result.success) {
305
- return die(`Failed to Allocate Flycast IP on Network '${network}'`);
306
- }
307
- },
308
- async getConfig(app) {
309
- const result = await runCommand(["fly", "config", "show", "-a", app]);
310
- if (!result.success)
311
- return null;
312
- try {
313
- return JSON.parse(result.stdout);
314
- }
315
- catch {
316
- return null;
317
- }
318
- },
319
- async deploySafe(app, options) {
320
- const args = ["fly", "deploy"];
321
- // When config is provided, use its parent directory as the build context
322
- // so fly deploy finds the Dockerfile and COPY picks up the correct files
323
- if (options.config) {
324
- const configAbs = resolve(options.config);
325
- args.push(dirname(configAbs));
326
- args.push("--config", configAbs);
327
- }
328
- args.push("-a", app, "--yes", "--no-public-ips");
329
- if (options.image) {
330
- args.push("--image", options.image);
331
- }
332
- if (options.region) {
333
- args.push("--primary-region", options.region);
334
- }
335
- const result = await runCommand(args);
336
- if (!result.success) {
337
- throw new FlyDeployError(app, result.stderr);
338
- }
339
- },
340
- async listCerts(app) {
341
- const result = await runCommand([
342
- "fly",
343
- "certs",
344
- "list",
345
- "-a",
346
- app,
347
- "--json",
348
- ]);
349
- if (!result.success)
350
- return [];
351
- try {
352
- const certs = JSON.parse(result.stdout);
353
- return certs
354
- .map((c) => c.Hostname)
355
- .filter((h) => typeof h === "string");
356
- }
357
- catch {
358
- return [];
359
- }
360
- },
361
- async removeCert(app, hostname) {
362
- await runCommand([
363
- "fly",
364
- "certs",
365
- "remove",
366
- hostname,
367
- "-a",
368
- app,
369
- "--yes",
370
- ]);
371
- },
372
- async getFlyToken() {
373
- // Read access_token from ~/.fly/config.yml
374
- const home = dntShim.Deno.env.get("HOME") || dntShim.Deno.env.get("USERPROFILE") || "";
375
- const configPath = `${home}/.fly/config.yml`;
376
- if (!(await fileExists(configPath))) {
377
- return die("Fly Config Not Found at ~/.fly/config.yml. Run 'fly auth login' First");
378
- }
379
- const content = await dntShim.Deno.readTextFile(configPath);
380
- const match = content.match(/access_token:\s*(.+)/);
381
- if (!match || !match[1]) {
382
- return die("No Access Token Found in ~/.fly/config.yml. Run 'fly auth login' First");
383
- }
384
- return match[1].trim();
385
- },
386
- async listAppsWithNetwork(org) {
387
- const token = await this.getFlyToken();
388
- const response = await fetch(`https://api.machines.dev/v1/apps?org_slug=${encodeURIComponent(org)}`, {
389
- headers: {
390
- Authorization: `Bearer ${token}`,
391
- "Content-Type": "application/json",
392
- },
393
- });
394
- if (!response.ok) {
395
- return die(`Failed to List Apps via REST API: HTTP ${response.status}`);
396
- }
397
- const data = await response.json();
398
- const parsed = FlyAppInfoListSchema.safeParse(data);
399
- if (!parsed.success) {
400
- return die("Failed to Parse Apps REST API Response");
401
- }
402
- return parsed.data.apps;
403
- },
404
- };
405
- };
406
- // =============================================================================
407
- // Router App Naming
408
- // =============================================================================
409
- export const getRouterAppName = (network, randomSuffix) => {
410
- return `${ROUTER_APP_PREFIX}${network}-${randomSuffix}`;
411
- };
@@ -1,31 +0,0 @@
1
- import "../../_dnt.polyfills.js";
2
- import { type AuthKeyCapabilities, type TailscaleDevice } from "../schemas/tailscale.js";
3
- export interface TailscaleProvider {
4
- validateApiKey(): Promise<boolean>;
5
- createAuthKey(opts?: AuthKeyCapabilities): Promise<string>;
6
- listDevices(): Promise<TailscaleDevice[]>;
7
- getDeviceByHostname(hostname: string): Promise<TailscaleDevice | null>;
8
- deleteDevice(id: string): Promise<void>;
9
- approveSubnetRoutes(deviceId: string, routes: string[]): Promise<void>;
10
- setSplitDns(domain: string, nameservers: string[]): Promise<void>;
11
- clearSplitDns(domain: string): Promise<void>;
12
- getPolicyFile(): Promise<Record<string, unknown> | null>;
13
- isTagOwnerConfigured(tag: string): Promise<boolean>;
14
- isAutoApproverConfigured(tag: string): Promise<boolean>;
15
- }
16
- export declare const createTailscaleProvider: (tailnet: string, apiKey: string) => TailscaleProvider;
17
- export declare const waitForDevice: (provider: TailscaleProvider, hostname: string, timeoutMs?: number) => Promise<TailscaleDevice>;
18
- /**
19
- * Check if the tailscale CLI is installed.
20
- */
21
- export declare const isTailscaleInstalled: () => Promise<boolean>;
22
- /**
23
- * Check if accept-routes is enabled on the local client.
24
- */
25
- export declare const isAcceptRoutesEnabled: () => Promise<boolean>;
26
- /**
27
- * Enable accept-routes on the local client.
28
- * Returns true if successful, false if it failed (likely permissions).
29
- */
30
- export declare const enableAcceptRoutes: () => Promise<boolean>;
31
- //# sourceMappingURL=tailscale.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tailscale.d.ts","sourceRoot":"","sources":["../../../src/src/providers/tailscale.ts"],"names":[],"mappings":"AAGA,OAAO,yBAAyB,CAAC;AAKjC,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAYjC,MAAM,WAAW,iBAAiB;IAChC,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,aAAa,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACvE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACzD,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAiBD,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,QAAQ,MAAM,KACb,iBAoMF,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,UAAU,iBAAiB,EAC3B,UAAU,MAAM,EAChB,YAAW,MAAe,KACzB,OAAO,CAAC,eAAe,CAazB,CAAC;AAMF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,OAAO,CAE5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,OAAO,CAY7D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,OAAO,CAG1D,CAAC"}