@cardelli/ambit 0.3.5 → 0.4.1
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/README.md +29 -3
- package/esm/cli/commands/auth.d.ts +2 -0
- package/esm/cli/commands/auth.d.ts.map +1 -0
- package/esm/cli/commands/auth.js +336 -0
- package/esm/cli/commands/create/index.js +6 -17
- package/esm/cli/commands/deploy/machine.d.ts.map +1 -1
- package/esm/cli/commands/deploy/machine.js +6 -1
- package/esm/cli/commands/doctor.js +14 -0
- package/esm/cli/mod.d.ts.map +1 -1
- package/esm/cli/mod.js +1 -0
- package/esm/deno.js +1 -1
- package/esm/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
- package/esm/main.d.ts +1 -0
- package/esm/main.d.ts.map +1 -1
- package/esm/main.js +1 -0
- package/esm/providers/fly.d.ts +1 -1
- package/esm/providers/fly.d.ts.map +1 -1
- package/esm/providers/fly.js +38 -29
- package/esm/schemas/fly.d.ts +3 -3
- package/esm/util/constants.d.ts +1 -0
- package/esm/util/constants.d.ts.map +1 -1
- package/esm/util/constants.js +1 -0
- package/esm/util/credentials.d.ts +4 -0
- package/esm/util/credentials.d.ts.map +1 -1
- package/esm/util/credentials.js +81 -18
- package/esm/util/session.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,15 +65,41 @@ Open `http://my-crazy-site.lab`. It works for you and nobody else.
|
|
|
65
65
|
|
|
66
66
|
## Commands
|
|
67
67
|
|
|
68
|
+
### `ambit auth login`
|
|
69
|
+
|
|
70
|
+
Authenticates with both Fly.io and Tailscale. Only prompts for credentials that are missing or invalid — existing valid tokens are preserved. Run this before using any other command.
|
|
71
|
+
|
|
72
|
+
| Flag | Description |
|
|
73
|
+
| ----------------------- | ---------------------------------------------- |
|
|
74
|
+
| `--ts-api-key <key>` | Tailscale API access token (tskey-api-...) |
|
|
75
|
+
| `--fly-api-key <token>` | Fly.io API token |
|
|
76
|
+
| `--json` | Machine-readable JSON output |
|
|
77
|
+
|
|
78
|
+
### `ambit auth whoami`
|
|
79
|
+
|
|
80
|
+
Shows the current authentication status for both Fly.io and Tailscale.
|
|
81
|
+
|
|
82
|
+
| Flag | Description |
|
|
83
|
+
| -------- | --------------------------- |
|
|
84
|
+
| `--json` | Machine-readable JSON output |
|
|
85
|
+
|
|
86
|
+
### `ambit auth logout`
|
|
87
|
+
|
|
88
|
+
Clears all stored credentials and logs out of Fly.io.
|
|
89
|
+
|
|
90
|
+
| Flag | Description |
|
|
91
|
+
| ------------- | ------------------------ |
|
|
92
|
+
| `-y`, `--yes` | Skip confirmation prompt |
|
|
93
|
+
| `--json` | Machine-readable JSON |
|
|
94
|
+
|
|
68
95
|
### `ambit create <network>`
|
|
69
96
|
|
|
70
|
-
|
|
97
|
+
Creates a private network. Deploys a Tailscale subnet router on a Fly.io custom network, sets up DNS, configures your local machine to accept the new routes, and automatically adds the router's tag to your Tailscale ACL policy. Requires `ambit auth login` first.
|
|
71
98
|
|
|
72
99
|
| Flag | Description |
|
|
73
100
|
| ------------------- | --------------------------------------------------------------------------- |
|
|
74
101
|
| `--org <org>` | Fly.io organization slug |
|
|
75
102
|
| `--region <region>` | Fly.io region (default: `iad`) |
|
|
76
|
-
| `--api-key <key>` | Tailscale API access token (tskey-api-...) |
|
|
77
103
|
| `--tag <tag>` | Tailscale ACL tag for the router (default: `tag:ambit-<network>`) |
|
|
78
104
|
| `--manual` | Skip automatic Tailscale ACL configuration (tagOwners + autoApprovers) |
|
|
79
105
|
| `--no-auto-approve` | Skip waiting for router and approving routes |
|
|
@@ -104,7 +130,7 @@ npx @cardelli/ambit share browsers group:team alice@example.com group:contractor
|
|
|
104
130
|
|
|
105
131
|
This puts an app onto your private network. This is what you run whenever you want to host something. If the app doesn't exist yet, ambit creates it on the right network for you. The network can be specified as part of the name (`my-app.lab`) or with `--network` (`my-app --network lab`).
|
|
106
132
|
|
|
107
|
-
Before deploying, ambit scans your config for settings that don't make sense on a private network (like `force_https`, which only matters for public traffic). After deploying, it checks that no public IPs were allocated, releases any that were, and verifies the app has a private address on the network you specified.
|
|
133
|
+
Before deploying, ambit scans your config for settings that don't make sense on a private network (like `force_https`, which only matters for public traffic). After deploying, it checks that no public IPs were allocated, releases any that were, and verifies the app has a private address on the network you specified. Three environment variables are set automatically: `AMBIT_APP_NAME`, `AMBIT_NETWORK_NAME`, and `AMBIT_OUTBOUND_PROXY`.
|
|
108
134
|
|
|
109
135
|
There are three deployment modes. They are mutually exclusive — you can only use one at a time.
|
|
110
136
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/auth.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Auth Command - Manage Fly.io and Tailscale Authentication
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
5
|
+
import { parseArgs } from "../../deps/jsr.io/@std/cli/1.0.28/mod.js";
|
|
6
|
+
import { bold, confirm, readSecret } from "../../lib/cli.js";
|
|
7
|
+
import { checkArgs } from "../../lib/args.js";
|
|
8
|
+
import { createOutput } from "../../lib/output.js";
|
|
9
|
+
import { registerCommand } from "../mod.js";
|
|
10
|
+
import { runCommand } from "../../lib/command.js";
|
|
11
|
+
import { createTailscaleProvider } from "../../providers/tailscale.js";
|
|
12
|
+
import { getCredentialStore } from "../../util/credentials.js";
|
|
13
|
+
import { TAILSCALE_API_KEY_PREFIX } from "../../util/constants.js";
|
|
14
|
+
import { FlyAuthSchema } from "../../schemas/fly.js";
|
|
15
|
+
import { fileExists } from "../../lib/cli.js";
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Helpers
|
|
18
|
+
// =============================================================================
|
|
19
|
+
const tryFlyWhoami = async (token) => {
|
|
20
|
+
const result = await runCommand(["fly", "auth", "whoami", "--json"], token ? { env: { FLY_API_TOKEN: token } } : undefined);
|
|
21
|
+
if (!result.ok)
|
|
22
|
+
return null;
|
|
23
|
+
const auth = result.json();
|
|
24
|
+
if (!auth.ok)
|
|
25
|
+
return null;
|
|
26
|
+
const parsed = FlyAuthSchema.safeParse(auth.value);
|
|
27
|
+
return parsed.success ? parsed.data.email : null;
|
|
28
|
+
};
|
|
29
|
+
const readFlyConfigToken = async () => {
|
|
30
|
+
const home = dntShim.Deno.env.get("HOME") || dntShim.Deno.env.get("USERPROFILE") || "";
|
|
31
|
+
const configPath = `${home}/.fly/config.yml`;
|
|
32
|
+
if (!(await fileExists(configPath)))
|
|
33
|
+
return null;
|
|
34
|
+
const content = await dntShim.Deno.readTextFile(configPath);
|
|
35
|
+
const match = content.match(/access_token:\s*(.+)/);
|
|
36
|
+
return match?.[1]?.trim() ?? null;
|
|
37
|
+
};
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Auth Login
|
|
40
|
+
// =============================================================================
|
|
41
|
+
const authLogin = async (argv) => {
|
|
42
|
+
const opts = {
|
|
43
|
+
string: ["ts-api-key", "fly-api-key"],
|
|
44
|
+
boolean: ["help", "json"],
|
|
45
|
+
};
|
|
46
|
+
const args = parseArgs(argv, opts);
|
|
47
|
+
checkArgs(args, opts, "ambit auth login", 0);
|
|
48
|
+
if (args.help) {
|
|
49
|
+
console.log(`
|
|
50
|
+
${bold("ambit auth login")} - Authenticate with Fly.io and Tailscale
|
|
51
|
+
|
|
52
|
+
${bold("USAGE")}
|
|
53
|
+
ambit auth login [options]
|
|
54
|
+
|
|
55
|
+
${bold("OPTIONS")}
|
|
56
|
+
--ts-api-key <key> Tailscale API access token (tskey-api-...)
|
|
57
|
+
--fly-api-key <token> Fly.io API token
|
|
58
|
+
--json Output as JSON
|
|
59
|
+
|
|
60
|
+
${bold("DESCRIPTION")}
|
|
61
|
+
Authenticates with both Fly.io and Tailscale. Only prompts for
|
|
62
|
+
credentials that are missing or invalid — existing valid tokens
|
|
63
|
+
are preserved.
|
|
64
|
+
|
|
65
|
+
${bold("EXAMPLES")}
|
|
66
|
+
ambit auth login
|
|
67
|
+
ambit auth login --ts-api-key tskey-api-... --fly-api-key fo1_...
|
|
68
|
+
`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const out = createOutput(args.json);
|
|
72
|
+
const credentials = getCredentialStore();
|
|
73
|
+
// =========================================================================
|
|
74
|
+
// Step 1: Fly.io
|
|
75
|
+
// =========================================================================
|
|
76
|
+
out.blank().header("Fly.io Authentication").blank();
|
|
77
|
+
let flyEmail;
|
|
78
|
+
if (args["fly-api-key"]) {
|
|
79
|
+
const email = await tryFlyWhoami(args["fly-api-key"]);
|
|
80
|
+
if (!email) {
|
|
81
|
+
return out.die("Invalid Fly.io API Token");
|
|
82
|
+
}
|
|
83
|
+
await credentials.setFlyToken(args["fly-api-key"]);
|
|
84
|
+
out.ok(`Authenticated as ${email}`);
|
|
85
|
+
flyEmail = email;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const storedToken = await credentials.getFlyToken();
|
|
89
|
+
const storedEmail = storedToken
|
|
90
|
+
? await tryFlyWhoami(storedToken)
|
|
91
|
+
: null;
|
|
92
|
+
if (storedEmail) {
|
|
93
|
+
out.ok(`Already Authenticated as ${storedEmail}`);
|
|
94
|
+
flyEmail = storedEmail;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
if (args.json) {
|
|
98
|
+
return out.die("Not Authenticated with Fly.io. Provide --fly-api-key in JSON Mode");
|
|
99
|
+
}
|
|
100
|
+
const loginResult = await runCommand(["fly", "auth", "login"], {
|
|
101
|
+
interactive: true,
|
|
102
|
+
});
|
|
103
|
+
if (!loginResult.ok) {
|
|
104
|
+
return out.die("Fly.io Authentication Failed");
|
|
105
|
+
}
|
|
106
|
+
const token = await readFlyConfigToken();
|
|
107
|
+
if (!token) {
|
|
108
|
+
return out.die("Could Not Read Fly.io Token After Login");
|
|
109
|
+
}
|
|
110
|
+
const email = await tryFlyWhoami(token);
|
|
111
|
+
if (!email) {
|
|
112
|
+
return out.die("Fly.io Authentication Verification Failed");
|
|
113
|
+
}
|
|
114
|
+
await credentials.setFlyToken(token);
|
|
115
|
+
out.ok(`Authenticated as ${email}`);
|
|
116
|
+
flyEmail = email;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// =========================================================================
|
|
120
|
+
// Step 2: Tailscale
|
|
121
|
+
// =========================================================================
|
|
122
|
+
out.blank().header("Tailscale Authentication").blank();
|
|
123
|
+
let tailscaleOk = false;
|
|
124
|
+
if (args["ts-api-key"]) {
|
|
125
|
+
if (!args["ts-api-key"].startsWith(TAILSCALE_API_KEY_PREFIX)) {
|
|
126
|
+
return out.die("Invalid Token Format. Expected 'tskey-api-...' (API Access Token, Not Auth Key)");
|
|
127
|
+
}
|
|
128
|
+
const tailscale = createTailscaleProvider(args["ts-api-key"]);
|
|
129
|
+
const validateSpinner = out.spinner("Validating API Access Token");
|
|
130
|
+
const isValid = await tailscale.auth.validateKey();
|
|
131
|
+
if (!isValid) {
|
|
132
|
+
validateSpinner.fail("Invalid API Access Token");
|
|
133
|
+
return out.die("Failed to Validate Tailscale API Access Token");
|
|
134
|
+
}
|
|
135
|
+
validateSpinner.success("API Access Token Validated");
|
|
136
|
+
await credentials.setTailscaleApiKey(args["ts-api-key"]);
|
|
137
|
+
tailscaleOk = true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const storedKey = await credentials.getTailscaleApiKey();
|
|
141
|
+
if (storedKey) {
|
|
142
|
+
const tailscale = createTailscaleProvider(storedKey);
|
|
143
|
+
const validateSpinner = out.spinner("Checking Stored API Key");
|
|
144
|
+
const isValid = await tailscale.auth.validateKey();
|
|
145
|
+
if (isValid) {
|
|
146
|
+
validateSpinner.success("API Key Already Configured");
|
|
147
|
+
tailscaleOk = true;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
validateSpinner.fail("Stored API Key Is Invalid or Expired");
|
|
151
|
+
// Fall through to prompt
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!tailscaleOk) {
|
|
155
|
+
if (args.json) {
|
|
156
|
+
return out.die("Tailscale API Key Not Configured. Provide --ts-api-key in JSON Mode");
|
|
157
|
+
}
|
|
158
|
+
out.dim("Ambit Needs an API Access Token (Not an Auth Key) to Manage Your Tailnet.")
|
|
159
|
+
.dim("Create One at:").link(" https://login.tailscale.com/admin/settings/keys")
|
|
160
|
+
.blank();
|
|
161
|
+
const apiKey = await readSecret("API access token (tskey-api-...): ");
|
|
162
|
+
if (!apiKey) {
|
|
163
|
+
return out.die("Tailscale API Access Token Required");
|
|
164
|
+
}
|
|
165
|
+
if (!apiKey.startsWith(TAILSCALE_API_KEY_PREFIX)) {
|
|
166
|
+
return out.die("Invalid Token Format. Expected 'tskey-api-...' (API Access Token, Not Auth Key)");
|
|
167
|
+
}
|
|
168
|
+
const tailscale = createTailscaleProvider(apiKey);
|
|
169
|
+
const validateSpinner = out.spinner("Validating API Access Token");
|
|
170
|
+
const isValid = await tailscale.auth.validateKey();
|
|
171
|
+
if (!isValid) {
|
|
172
|
+
validateSpinner.fail("Invalid API Access Token");
|
|
173
|
+
return out.die("Failed to Validate Tailscale API Access Token");
|
|
174
|
+
}
|
|
175
|
+
validateSpinner.success("API Access Token Validated");
|
|
176
|
+
await credentials.setTailscaleApiKey(apiKey);
|
|
177
|
+
tailscaleOk = true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
out.blank();
|
|
181
|
+
out.done({ fly: flyEmail, tailscale: tailscaleOk });
|
|
182
|
+
out.print();
|
|
183
|
+
};
|
|
184
|
+
// =============================================================================
|
|
185
|
+
// Auth Whoami
|
|
186
|
+
// =============================================================================
|
|
187
|
+
const authWhoami = async (argv) => {
|
|
188
|
+
const opts = {
|
|
189
|
+
boolean: ["help", "json"],
|
|
190
|
+
};
|
|
191
|
+
const args = parseArgs(argv, opts);
|
|
192
|
+
checkArgs(args, opts, "ambit auth whoami", 0);
|
|
193
|
+
if (args.help) {
|
|
194
|
+
console.log(`
|
|
195
|
+
${bold("ambit auth whoami")} - Show Current Authentication Status
|
|
196
|
+
|
|
197
|
+
${bold("USAGE")}
|
|
198
|
+
ambit auth whoami [options]
|
|
199
|
+
|
|
200
|
+
${bold("OPTIONS")}
|
|
201
|
+
--json Output as JSON
|
|
202
|
+
|
|
203
|
+
${bold("EXAMPLES")}
|
|
204
|
+
ambit auth whoami
|
|
205
|
+
ambit auth whoami --json
|
|
206
|
+
`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const out = createOutput(args.json);
|
|
210
|
+
const credentials = getCredentialStore();
|
|
211
|
+
// =========================================================================
|
|
212
|
+
// Step 1: Fly.io
|
|
213
|
+
// =========================================================================
|
|
214
|
+
let flyEmail = null;
|
|
215
|
+
const storedToken = await credentials.getFlyToken();
|
|
216
|
+
if (storedToken) {
|
|
217
|
+
flyEmail = await tryFlyWhoami(storedToken);
|
|
218
|
+
}
|
|
219
|
+
if (!flyEmail) {
|
|
220
|
+
flyEmail = await tryFlyWhoami();
|
|
221
|
+
}
|
|
222
|
+
// =========================================================================
|
|
223
|
+
// Step 2: Tailscale
|
|
224
|
+
// =========================================================================
|
|
225
|
+
let tailscaleOk = false;
|
|
226
|
+
const storedKey = await credentials.getTailscaleApiKey();
|
|
227
|
+
if (storedKey) {
|
|
228
|
+
const tailscale = createTailscaleProvider(storedKey);
|
|
229
|
+
tailscaleOk = await tailscale.auth.validateKey();
|
|
230
|
+
}
|
|
231
|
+
// =========================================================================
|
|
232
|
+
// Output
|
|
233
|
+
// =========================================================================
|
|
234
|
+
out.blank();
|
|
235
|
+
out.text(` Fly.io: ${flyEmail ?? "Not Authenticated"}`);
|
|
236
|
+
out.text(` Tailscale: ${tailscaleOk ? "API Key Configured" : "Not Configured"}`);
|
|
237
|
+
out.blank();
|
|
238
|
+
out.done({ fly: flyEmail, tailscale: tailscaleOk });
|
|
239
|
+
out.print();
|
|
240
|
+
};
|
|
241
|
+
// =============================================================================
|
|
242
|
+
// Auth Logout
|
|
243
|
+
// =============================================================================
|
|
244
|
+
const authLogout = async (argv) => {
|
|
245
|
+
const opts = {
|
|
246
|
+
boolean: ["help", "json", "yes"],
|
|
247
|
+
alias: { y: "yes" },
|
|
248
|
+
};
|
|
249
|
+
const args = parseArgs(argv, opts);
|
|
250
|
+
checkArgs(args, opts, "ambit auth logout", 0);
|
|
251
|
+
if (args.help) {
|
|
252
|
+
console.log(`
|
|
253
|
+
${bold("ambit auth logout")} - Clear Stored Credentials
|
|
254
|
+
|
|
255
|
+
${bold("USAGE")}
|
|
256
|
+
ambit auth logout [options]
|
|
257
|
+
|
|
258
|
+
${bold("OPTIONS")}
|
|
259
|
+
-y, --yes Skip confirmation prompt
|
|
260
|
+
--json Output as JSON
|
|
261
|
+
|
|
262
|
+
${bold("EXAMPLES")}
|
|
263
|
+
ambit auth logout
|
|
264
|
+
ambit auth logout --yes
|
|
265
|
+
`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const out = createOutput(args.json);
|
|
269
|
+
if (!args.yes && !args.json) {
|
|
270
|
+
const ok = await confirm("This will clear all stored credentials. Continue?");
|
|
271
|
+
if (!ok) {
|
|
272
|
+
return out.die("Aborted");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const credentials = getCredentialStore();
|
|
276
|
+
await credentials.clear();
|
|
277
|
+
out.ok("Cleared Stored Credentials");
|
|
278
|
+
await runCommand(["fly", "auth", "logout"]);
|
|
279
|
+
out.ok("Logged Out of Fly.io");
|
|
280
|
+
out.blank();
|
|
281
|
+
out.done({ fly: true, tailscale: true });
|
|
282
|
+
out.print();
|
|
283
|
+
};
|
|
284
|
+
// =============================================================================
|
|
285
|
+
// Top-Level Help
|
|
286
|
+
// =============================================================================
|
|
287
|
+
const showAuthHelp = () => {
|
|
288
|
+
console.log(`
|
|
289
|
+
${bold("ambit auth")} - Manage Fly.io and Tailscale Authentication
|
|
290
|
+
|
|
291
|
+
${bold("USAGE")}
|
|
292
|
+
ambit auth <subcommand> [options]
|
|
293
|
+
|
|
294
|
+
${bold("SUBCOMMANDS")}
|
|
295
|
+
login Authenticate with Fly.io and Tailscale
|
|
296
|
+
whoami Show current authentication status
|
|
297
|
+
logout Clear stored credentials
|
|
298
|
+
|
|
299
|
+
${bold("EXAMPLES")}
|
|
300
|
+
ambit auth login
|
|
301
|
+
ambit auth whoami
|
|
302
|
+
ambit auth logout
|
|
303
|
+
|
|
304
|
+
Run 'ambit auth <subcommand> --help' for details.
|
|
305
|
+
`);
|
|
306
|
+
};
|
|
307
|
+
// =============================================================================
|
|
308
|
+
// Dispatcher
|
|
309
|
+
// =============================================================================
|
|
310
|
+
const auth = async (argv) => {
|
|
311
|
+
const subcommand = typeof argv[0] === "string" ? argv[0] : undefined;
|
|
312
|
+
if (subcommand === "login")
|
|
313
|
+
return authLogin(argv.slice(1));
|
|
314
|
+
if (subcommand === "whoami")
|
|
315
|
+
return authWhoami(argv.slice(1));
|
|
316
|
+
if (subcommand === "logout")
|
|
317
|
+
return authLogout(argv.slice(1));
|
|
318
|
+
const opts = { boolean: ["help"] };
|
|
319
|
+
const args = parseArgs(argv, opts);
|
|
320
|
+
checkArgs(args, opts, "ambit auth", 0);
|
|
321
|
+
if (args.help) {
|
|
322
|
+
showAuthHelp();
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
showAuthHelp();
|
|
326
|
+
dntShim.Deno.exit(1);
|
|
327
|
+
};
|
|
328
|
+
// =============================================================================
|
|
329
|
+
// Register Command
|
|
330
|
+
// =============================================================================
|
|
331
|
+
registerCommand({
|
|
332
|
+
name: "auth",
|
|
333
|
+
description: "Manage Fly.io and Tailscale authentication",
|
|
334
|
+
usage: "ambit auth login|whoami|logout [options]",
|
|
335
|
+
run: auth,
|
|
336
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Create Command - Create Tailscale Subnet Router on Fly.io Custom Network
|
|
3
3
|
// =============================================================================
|
|
4
4
|
import { parseArgs } from "../../../deps/jsr.io/@std/cli/1.0.28/mod.js";
|
|
5
|
-
import { bold
|
|
5
|
+
import { bold } from "../../../lib/cli.js";
|
|
6
6
|
import { checkArgs } from "../../../lib/args.js";
|
|
7
7
|
import { createOutput } from "../../../lib/output.js";
|
|
8
8
|
import { runMachine } from "../../../lib/machine.js";
|
|
@@ -21,7 +21,8 @@ import { createTransition, hydrateCreate, reportSkipped, } from "./machine.js";
|
|
|
21
21
|
// =============================================================================
|
|
22
22
|
const stageFlyConfig = async (out, opts) => {
|
|
23
23
|
out.header("Step 1: Fly.io Configuration").blank();
|
|
24
|
-
const
|
|
24
|
+
const flyToken = await getCredentialStore().getFlyToken();
|
|
25
|
+
const fly = createFlyProvider(flyToken ?? undefined);
|
|
25
26
|
await fly.auth.ensureInstalled();
|
|
26
27
|
const email = await fly.auth.login({ interactive: !opts.json });
|
|
27
28
|
out.ok(`Authenticated as ${email}`);
|
|
@@ -44,18 +45,9 @@ const handleAclSetFailure = (out, result, action) => {
|
|
|
44
45
|
const stageTailscaleConfig = async (out, opts) => {
|
|
45
46
|
out.header("Step 2: Tailscale Configuration").blank();
|
|
46
47
|
const credentials = getCredentialStore();
|
|
47
|
-
|
|
48
|
+
const apiKey = await credentials.getTailscaleApiKey();
|
|
48
49
|
if (!apiKey) {
|
|
49
|
-
|
|
50
|
-
return out.die("--api-key Is Required in JSON Mode");
|
|
51
|
-
}
|
|
52
|
-
out.dim("Ambit Needs an API Access Token (Not an Auth Key) to Manage Your Tailnet.")
|
|
53
|
-
.dim("Create One at:").link(" https://login.tailscale.com/admin/settings/keys")
|
|
54
|
-
.blank();
|
|
55
|
-
apiKey = await readSecret("API access token (tskey-api-...): ");
|
|
56
|
-
if (!apiKey) {
|
|
57
|
-
return out.die("Tailscale API Access Token Required");
|
|
58
|
-
}
|
|
50
|
+
return out.die("Tailscale API Key Required. Run 'ambit auth login'");
|
|
59
51
|
}
|
|
60
52
|
if (!apiKey.startsWith(TAILSCALE_API_KEY_PREFIX)) {
|
|
61
53
|
return out.die("Invalid Token Format. Expected 'tskey-api-...' (API Access Token, Not Auth Key)");
|
|
@@ -68,7 +60,6 @@ const stageTailscaleConfig = async (out, opts) => {
|
|
|
68
60
|
return out.die("Failed to Validate Tailscale API Access Token");
|
|
69
61
|
}
|
|
70
62
|
validateSpinner.success("API Access Token Validated");
|
|
71
|
-
await credentials.setTailscaleApiKey(apiKey);
|
|
72
63
|
const tagOwnerSpinner = out.spinner(`Checking tagOwners for ${opts.tag}`);
|
|
73
64
|
let policy = await tailscale.acl.getPolicy();
|
|
74
65
|
const hasTagOwner = isTagOwnerConfigured(policy, opts.tag);
|
|
@@ -240,7 +231,7 @@ const stageSummary = async (out, _fly, tailscale, ctx, opts) => {
|
|
|
240
231
|
// =============================================================================
|
|
241
232
|
const create = async (argv) => {
|
|
242
233
|
const opts = {
|
|
243
|
-
string: ["org", "region", "
|
|
234
|
+
string: ["org", "region", "tag"],
|
|
244
235
|
boolean: ["help", "yes", "json", "no-auto-approve", "manual"],
|
|
245
236
|
alias: { y: "yes" },
|
|
246
237
|
};
|
|
@@ -256,7 +247,6 @@ ${bold("USAGE")}
|
|
|
256
247
|
${bold("OPTIONS")}
|
|
257
248
|
--org <org> Fly.io organization slug
|
|
258
249
|
--region <region> Fly.io region (default: iad)
|
|
259
|
-
--api-key <key> Tailscale API access token (tskey-api-...)
|
|
260
250
|
--tag <tag> Tailscale ACL tag for the router (default: tag:ambit-<network>)
|
|
261
251
|
--manual Skip automatic Tailscale ACL configuration (tagOwners + autoApprovers)
|
|
262
252
|
--no-auto-approve Skip waiting for router and approving routes
|
|
@@ -305,7 +295,6 @@ ${bold("EXAMPLES")}
|
|
|
305
295
|
const tailscale = await stageTailscaleConfig(out, {
|
|
306
296
|
json: args.json,
|
|
307
297
|
manual,
|
|
308
|
-
apiKey: args["api-key"],
|
|
309
298
|
tag,
|
|
310
299
|
network,
|
|
311
300
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy/machine.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,OAAO,GACP,UAAU,CAAC;AAMf,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE;QACL,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,iBAAiB,CAAC;IACjC,KAAK,CAAC,EAAE;QACN,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAaD,eAAO,MAAM,mBAAmB,GAC9B,KAAK,MAAM,CAAC,YAAY,CAAC,EACzB,YAAY,WAAW,SAMxB,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,KAAK,SAAS,KACb,OAAO,CAAC,WAAW,CAMrB,CAAC;AAMF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,WAAW,EAClB,KAAK,SAAS,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy/machine.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,OAAO,GACP,UAAU,CAAC;AAMf,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE;QACL,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,iBAAiB,CAAC;IACjC,KAAK,CAAC,EAAE;QACN,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAaD,eAAO,MAAM,mBAAmB,GAC9B,KAAK,MAAM,CAAC,YAAY,CAAC,EACzB,YAAY,WAAW,SAMxB,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,KAAK,SAAS,KACb,OAAO,CAAC,WAAW,CAMrB,CAAC;AAMF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,WAAW,EAClB,KAAK,SAAS,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAiH7B,CAAC"}
|
|
@@ -59,11 +59,16 @@ export const deployTransition = async (phase, ctx) => {
|
|
|
59
59
|
if (!ctx.created) {
|
|
60
60
|
ctx.out.skip(`App '${ctx.flyAppName}' Exists`);
|
|
61
61
|
}
|
|
62
|
+
const ambitSecrets = {
|
|
63
|
+
AMBIT_APP_NAME: ctx.app,
|
|
64
|
+
AMBIT_NETWORK_NAME: ctx.network,
|
|
65
|
+
};
|
|
62
66
|
if (ctx.routerPrivateIp) {
|
|
63
67
|
const proxyUrl = `socks5://[${ctx.routerPrivateIp}]:${SOCKS_PROXY_PORT}`;
|
|
64
|
-
|
|
68
|
+
ambitSecrets[SECRET_AMBIT_OUTBOUND_PROXY] = proxyUrl;
|
|
65
69
|
ctx.out.ok(`Outbound Proxy: ${proxyUrl}`);
|
|
66
70
|
}
|
|
71
|
+
await ctx.out.spin("Staging Ambit Secrets", () => ctx.fly.secrets.set(ctx.flyAppName, ambitSecrets, { stage: true }));
|
|
67
72
|
return Result.ok("deploy");
|
|
68
73
|
}
|
|
69
74
|
case "deploy": {
|
|
@@ -108,6 +108,20 @@ const stageAppChecks = async (fly, tailscale, org, report, app, network) => {
|
|
|
108
108
|
report(`App Running (${app}.${network})`, started !== undefined, machines.length > 0
|
|
109
109
|
? `Machine State: ${machines[0].state}`
|
|
110
110
|
: "No Machines Found");
|
|
111
|
+
const secrets = await fly.secrets.list(workload.appName);
|
|
112
|
+
const secretNames = new Set(secrets.map((s) => s.name));
|
|
113
|
+
const hasAmbitSecrets = secretNames.has("AMBIT_APP_NAME") &&
|
|
114
|
+
secretNames.has("AMBIT_NETWORK_NAME");
|
|
115
|
+
if (!hasAmbitSecrets) {
|
|
116
|
+
await fly.secrets.set(workload.appName, {
|
|
117
|
+
AMBIT_APP_NAME: app,
|
|
118
|
+
AMBIT_NETWORK_NAME: network,
|
|
119
|
+
});
|
|
120
|
+
report(`Ambit Secrets Set (${app}.${network})`, true, "Auto-configured");
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
report(`Ambit Secrets Set (${app}.${network})`, true);
|
|
124
|
+
}
|
|
111
125
|
const router = await findRouterApp(fly, org, network);
|
|
112
126
|
if (!router) {
|
|
113
127
|
report(`Router Exists (${network})`, false, `Create with: ambit create ${network}`);
|
package/esm/cli/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/cli/mod.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAQD,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,KAAG,IAElD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAO,GAAG,SAEnD,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,OAAO,EAExC,CAAC;AAQF,eAAO,MAAM,QAAQ,QAAO,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/cli/mod.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAQD,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,KAAG,IAElD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAO,GAAG,SAEnD,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,OAAO,EAExC,CAAC;AAQF,eAAO,MAAM,QAAQ,QAAO,IAkC3B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAC;AAMF,eAAO,MAAM,MAAM,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAuCzD,CAAC"}
|
package/esm/cli/mod.js
CHANGED
|
@@ -30,6 +30,7 @@ ${bold("USAGE")}
|
|
|
30
30
|
ambit <command> [options]
|
|
31
31
|
|
|
32
32
|
${bold("COMMANDS")}
|
|
33
|
+
auth Manage Fly.io and Tailscale authentication
|
|
33
34
|
create Create a Tailscale subnet router on a Fly.io custom network
|
|
34
35
|
deploy Deploy an app safely on a custom private network
|
|
35
36
|
share Grant a Tailscale group access to a network via ACL rules
|
package/esm/deno.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The character used to separate entries in the PATH environment variable.
|
|
3
3
|
* On Windows, this is `;`. On all other platforms, this is `:`.
|
|
4
4
|
*/
|
|
5
|
-
export declare const DELIMITER: "
|
|
5
|
+
export declare const DELIMITER: ":" | ";";
|
|
6
6
|
/**
|
|
7
7
|
* The character used to separate components of a file path.
|
|
8
8
|
* On Windows, this is `\`. On all other platforms, this is `/`.
|
package/esm/main.d.ts
CHANGED
package/esm/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,qBAAqB,CAAC;AAoC7B,OAAO,gCAAgC,CAAC;AACxC,OAAO,gCAAgC,CAAC;AACxC,OAAO,yBAAyB,CAAC;AACjC,OAAO,8BAA8B,CAAC;AACtC,OAAO,gCAAgC,CAAC;AACxC,OAAO,iCAAiC,CAAC;AACzC,OAAO,0BAA0B,CAAC;AAClC,OAAO,2BAA2B,CAAC;AACnC,OAAO,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,qBAAqB,CAAC;AAoC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,gCAAgC,CAAC;AACxC,OAAO,gCAAgC,CAAC;AACxC,OAAO,yBAAyB,CAAC;AACjC,OAAO,8BAA8B,CAAC;AACtC,OAAO,gCAAgC,CAAC;AACxC,OAAO,iCAAiC,CAAC;AACzC,OAAO,0BAA0B,CAAC;AAClC,OAAO,2BAA2B,CAAC;AACnC,OAAO,wBAAwB,CAAC"}
|
package/esm/main.js
CHANGED
|
@@ -32,6 +32,7 @@ import * as dntShim from "./_dnt.shims.js";
|
|
|
32
32
|
// =============================================================================
|
|
33
33
|
import { runCli } from "./cli/mod.js";
|
|
34
34
|
import { Spinner, statusErr } from "./lib/cli.js";
|
|
35
|
+
import "./cli/commands/auth.js";
|
|
35
36
|
import "./cli/commands/create/index.js";
|
|
36
37
|
import "./cli/commands/deploy/index.js";
|
|
37
38
|
import "./cli/commands/share.js";
|
package/esm/providers/fly.d.ts
CHANGED
|
@@ -85,5 +85,5 @@ export interface FlyProvider {
|
|
|
85
85
|
app(app: string, options: SafeDeployOptions): Promise<void>;
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
export declare const createFlyProvider: () => FlyProvider;
|
|
88
|
+
export declare const createFlyProvider: (token?: string) => FlyProvider;
|
|
89
89
|
//# sourceMappingURL=fly.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../src/providers/fly.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../src/providers/fly.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,UAAU,EAIf,KAAK,KAAK,EAEV,KAAK,UAAU,EAIhB,MAAM,mBAAmB,CAAC;AAa3B;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMxC;AAMD,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,CAAC;AAE9E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE;QACJ,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE;YAAE,WAAW,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACzD,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;KAC7B,CAAC;IACF,IAAI,EAAE;QACJ,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACzC,CAAC;IACF,IAAI,EAAE;QACJ,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACpD,MAAM,CACJ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACvC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;KAClE,CAAC;IACF,QAAQ,EAAE;QACR,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACzC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACxD,CAAC;IACF,OAAO,EAAE;QACP,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC,CAAC;QACpE,GAAG,CACD,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,OAAO,CAAA;SAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,KAAK,CACH,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,OAAO,CAAA;SAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACpC,CAAC;IACF,GAAG,EAAE;QACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACrD,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9D,CAAC;IACF,KAAK,EAAE;QACL,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtD,CAAC;IACF,MAAM,EAAE;QACN,MAAM,CACJ,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC7D,CAAC;CACH;AAMD,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,WAogBlD,CAAC"}
|
package/esm/providers/fly.js
CHANGED
|
@@ -31,7 +31,14 @@ export class FlyDeployError extends Error {
|
|
|
31
31
|
// =============================================================================
|
|
32
32
|
// Create Fly Provider
|
|
33
33
|
// =============================================================================
|
|
34
|
-
export const createFlyProvider = () => {
|
|
34
|
+
export const createFlyProvider = (token) => {
|
|
35
|
+
const envOverride = token ? { FLY_API_TOKEN: token } : undefined;
|
|
36
|
+
const run = (args, opts) => runCommand(args, envOverride
|
|
37
|
+
? { ...opts, env: { ...opts?.env, ...envOverride } }
|
|
38
|
+
: opts);
|
|
39
|
+
const runJ = (args, opts) => runJson(args, envOverride
|
|
40
|
+
? { ...opts, env: { ...opts?.env, ...envOverride } }
|
|
41
|
+
: opts);
|
|
35
42
|
const provider = {
|
|
36
43
|
auth: {
|
|
37
44
|
async ensureInstalled() {
|
|
@@ -41,7 +48,7 @@ export const createFlyProvider = () => {
|
|
|
41
48
|
},
|
|
42
49
|
async login(opts) {
|
|
43
50
|
const interactive = opts?.interactive ?? true;
|
|
44
|
-
const result = await
|
|
51
|
+
const result = await run(["fly", "auth", "whoami", "--json"]);
|
|
45
52
|
if (result.ok) {
|
|
46
53
|
const auth = result.json();
|
|
47
54
|
if (auth.ok) {
|
|
@@ -52,15 +59,15 @@ export const createFlyProvider = () => {
|
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
if (!interactive) {
|
|
55
|
-
return die("Not Authenticated with Fly.io. Run '
|
|
62
|
+
return die("Not Authenticated with Fly.io. Run 'ambit auth login' First");
|
|
56
63
|
}
|
|
57
|
-
const loginResult = await
|
|
64
|
+
const loginResult = await run(["fly", "auth", "login"], {
|
|
58
65
|
interactive: true,
|
|
59
66
|
});
|
|
60
67
|
if (!loginResult.ok) {
|
|
61
68
|
return die("Fly.io Authentication Failed");
|
|
62
69
|
}
|
|
63
|
-
const checkResult = await
|
|
70
|
+
const checkResult = await run([
|
|
64
71
|
"fly",
|
|
65
72
|
"auth",
|
|
66
73
|
"whoami",
|
|
@@ -76,22 +83,24 @@ export const createFlyProvider = () => {
|
|
|
76
83
|
return parsed.data.email;
|
|
77
84
|
},
|
|
78
85
|
async getToken() {
|
|
86
|
+
if (token)
|
|
87
|
+
return token;
|
|
79
88
|
const home = dntShim.Deno.env.get("HOME") || dntShim.Deno.env.get("USERPROFILE") || "";
|
|
80
89
|
const configPath = `${home}/.fly/config.yml`;
|
|
81
90
|
if (!(await fileExists(configPath))) {
|
|
82
|
-
return die("Fly Config Not Found at ~/.fly/config.yml. Run '
|
|
91
|
+
return die("Fly Config Not Found at ~/.fly/config.yml. Run 'ambit auth login' First");
|
|
83
92
|
}
|
|
84
93
|
const content = await dntShim.Deno.readTextFile(configPath);
|
|
85
94
|
const match = content.match(/access_token:\s*(.+)/);
|
|
86
95
|
if (!match || !match[1]) {
|
|
87
|
-
return die("No Access Token Found in ~/.fly/config.yml. Run '
|
|
96
|
+
return die("No Access Token Found in ~/.fly/config.yml. Run 'ambit auth login' First");
|
|
88
97
|
}
|
|
89
98
|
return match[1].trim();
|
|
90
99
|
},
|
|
91
100
|
},
|
|
92
101
|
orgs: {
|
|
93
102
|
async list() {
|
|
94
|
-
const result = await
|
|
103
|
+
const result = await runJ(["fly", "orgs", "list", "--json"]);
|
|
95
104
|
if (!result.ok) {
|
|
96
105
|
return die("Failed to List Organizations");
|
|
97
106
|
}
|
|
@@ -108,7 +117,7 @@ export const createFlyProvider = () => {
|
|
|
108
117
|
if (org) {
|
|
109
118
|
args.push("--org", org);
|
|
110
119
|
}
|
|
111
|
-
const result = await
|
|
120
|
+
const result = await run(args);
|
|
112
121
|
return result.json().flatMap((data) => {
|
|
113
122
|
const parsed = FlyAppsListSchema.safeParse(data);
|
|
114
123
|
return parsed.success
|
|
@@ -117,10 +126,10 @@ export const createFlyProvider = () => {
|
|
|
117
126
|
}).unwrapOr([]);
|
|
118
127
|
},
|
|
119
128
|
async listWithNetwork(org) {
|
|
120
|
-
const
|
|
129
|
+
const apiToken = await provider.auth.getToken();
|
|
121
130
|
const response = await fetch(`https://api.machines.dev/v1/apps?org_slug=${encodeURIComponent(org)}`, {
|
|
122
131
|
headers: {
|
|
123
|
-
Authorization: `Bearer ${
|
|
132
|
+
Authorization: `Bearer ${apiToken}`,
|
|
124
133
|
"Content-Type": "application/json",
|
|
125
134
|
},
|
|
126
135
|
});
|
|
@@ -142,13 +151,13 @@ export const createFlyProvider = () => {
|
|
|
142
151
|
if (opts?.network) {
|
|
143
152
|
args.push("--network", opts.network);
|
|
144
153
|
}
|
|
145
|
-
const result = await
|
|
154
|
+
const result = await run(args);
|
|
146
155
|
if (!result.ok) {
|
|
147
156
|
return die(`Failed to Create App '${appName}'`);
|
|
148
157
|
}
|
|
149
158
|
},
|
|
150
159
|
async delete(name) {
|
|
151
|
-
const result = await
|
|
160
|
+
const result = await run([
|
|
152
161
|
"fly",
|
|
153
162
|
"apps",
|
|
154
163
|
"destroy",
|
|
@@ -160,7 +169,7 @@ export const createFlyProvider = () => {
|
|
|
160
169
|
}
|
|
161
170
|
},
|
|
162
171
|
async exists(name) {
|
|
163
|
-
const result = await
|
|
172
|
+
const result = await run([
|
|
164
173
|
"fly",
|
|
165
174
|
"status",
|
|
166
175
|
"-a",
|
|
@@ -176,7 +185,7 @@ export const createFlyProvider = () => {
|
|
|
176
185
|
});
|
|
177
186
|
},
|
|
178
187
|
async getConfig(name) {
|
|
179
|
-
const result = await
|
|
188
|
+
const result = await run(["fly", "config", "show", "-a", name]);
|
|
180
189
|
return result.json().match({
|
|
181
190
|
ok: (v) => v,
|
|
182
191
|
err: () => null,
|
|
@@ -185,7 +194,7 @@ export const createFlyProvider = () => {
|
|
|
185
194
|
},
|
|
186
195
|
machines: {
|
|
187
196
|
async list(app) {
|
|
188
|
-
const result = await
|
|
197
|
+
const result = await runJ(["fly", "machines", "list", "-a", app, "--json"]);
|
|
189
198
|
return result.flatMap((data) => {
|
|
190
199
|
const parsed = FlyMachinesListSchema.safeParse(data);
|
|
191
200
|
return parsed.success
|
|
@@ -217,7 +226,7 @@ export const createFlyProvider = () => {
|
|
|
217
226
|
if (config.region) {
|
|
218
227
|
args.push("--region", config.region);
|
|
219
228
|
}
|
|
220
|
-
const result = await
|
|
229
|
+
const result = await run(args);
|
|
221
230
|
if (!result.ok) {
|
|
222
231
|
return die(result.stderr || "Unknown Error");
|
|
223
232
|
}
|
|
@@ -231,7 +240,7 @@ export const createFlyProvider = () => {
|
|
|
231
240
|
},
|
|
232
241
|
async destroy(app, machineId) {
|
|
233
242
|
const shortId = machineId.slice(0, 8);
|
|
234
|
-
const result = await
|
|
243
|
+
const result = await run([
|
|
235
244
|
"fly",
|
|
236
245
|
"machines",
|
|
237
246
|
"destroy",
|
|
@@ -247,7 +256,7 @@ export const createFlyProvider = () => {
|
|
|
247
256
|
},
|
|
248
257
|
secrets: {
|
|
249
258
|
async list(app) {
|
|
250
|
-
const result = await
|
|
259
|
+
const result = await runJ(["fly", "secrets", "list", "-a", app, "--json"]);
|
|
251
260
|
return result.unwrapOr([]);
|
|
252
261
|
},
|
|
253
262
|
async set(app, secrets, opts) {
|
|
@@ -260,7 +269,7 @@ export const createFlyProvider = () => {
|
|
|
260
269
|
if (opts?.stage) {
|
|
261
270
|
args.push("--stage");
|
|
262
271
|
}
|
|
263
|
-
const result = await
|
|
272
|
+
const result = await run(args);
|
|
264
273
|
if (!result.ok) {
|
|
265
274
|
return die("Failed to Set Secrets");
|
|
266
275
|
}
|
|
@@ -272,13 +281,13 @@ export const createFlyProvider = () => {
|
|
|
272
281
|
if (opts?.stage) {
|
|
273
282
|
args.push("--stage");
|
|
274
283
|
}
|
|
275
|
-
const result = await
|
|
284
|
+
const result = await run(args);
|
|
276
285
|
if (!result.ok) {
|
|
277
286
|
return die("Failed to Unset Secrets");
|
|
278
287
|
}
|
|
279
288
|
},
|
|
280
289
|
async deploy(app) {
|
|
281
|
-
const result = await
|
|
290
|
+
const result = await run([
|
|
282
291
|
"fly",
|
|
283
292
|
"secrets",
|
|
284
293
|
"deploy",
|
|
@@ -292,7 +301,7 @@ export const createFlyProvider = () => {
|
|
|
292
301
|
},
|
|
293
302
|
ips: {
|
|
294
303
|
async list(app) {
|
|
295
|
-
const result = await
|
|
304
|
+
const result = await run([
|
|
296
305
|
"fly",
|
|
297
306
|
"ips",
|
|
298
307
|
"list",
|
|
@@ -308,7 +317,7 @@ export const createFlyProvider = () => {
|
|
|
308
317
|
}).unwrapOr([]);
|
|
309
318
|
},
|
|
310
319
|
async release(app, address) {
|
|
311
|
-
const result = await
|
|
320
|
+
const result = await run([
|
|
312
321
|
"fly",
|
|
313
322
|
"ips",
|
|
314
323
|
"release",
|
|
@@ -321,7 +330,7 @@ export const createFlyProvider = () => {
|
|
|
321
330
|
}
|
|
322
331
|
},
|
|
323
332
|
async allocateFlycast(app, network) {
|
|
324
|
-
const result = await
|
|
333
|
+
const result = await run([
|
|
325
334
|
"fly",
|
|
326
335
|
"ips",
|
|
327
336
|
"allocate-v6",
|
|
@@ -338,7 +347,7 @@ export const createFlyProvider = () => {
|
|
|
338
347
|
},
|
|
339
348
|
certs: {
|
|
340
349
|
async list(app) {
|
|
341
|
-
const result = await
|
|
350
|
+
const result = await run([
|
|
342
351
|
"fly",
|
|
343
352
|
"certs",
|
|
344
353
|
"list",
|
|
@@ -351,7 +360,7 @@ export const createFlyProvider = () => {
|
|
|
351
360
|
.filter((h) => typeof h === "string")).unwrapOr([]);
|
|
352
361
|
},
|
|
353
362
|
async remove(app, hostname) {
|
|
354
|
-
await
|
|
363
|
+
await run([
|
|
355
364
|
"fly",
|
|
356
365
|
"certs",
|
|
357
366
|
"remove",
|
|
@@ -376,7 +385,7 @@ export const createFlyProvider = () => {
|
|
|
376
385
|
if (config?.region) {
|
|
377
386
|
args.push("--primary-region", config.region);
|
|
378
387
|
}
|
|
379
|
-
const result = await
|
|
388
|
+
const result = await run(args);
|
|
380
389
|
if (!result.ok) {
|
|
381
390
|
throw new FlyDeployError(app, result.stderr);
|
|
382
391
|
}
|
|
@@ -400,7 +409,7 @@ export const createFlyProvider = () => {
|
|
|
400
409
|
if (options.region) {
|
|
401
410
|
args.push("--primary-region", options.region);
|
|
402
411
|
}
|
|
403
|
-
const result = await
|
|
412
|
+
const result = await run(args);
|
|
404
413
|
if (!result.ok) {
|
|
405
414
|
throw new FlyDeployError(flyAppName, result.stderr);
|
|
406
415
|
}
|
package/esm/schemas/fly.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export type FlyAppStatus = z.infer<typeof FlyAppStatusEnum>;
|
|
|
18
18
|
* Terminal: destroyed, replaced, migrated
|
|
19
19
|
*/
|
|
20
20
|
export declare const FlyMachineStateEnum: z.ZodEnum<{
|
|
21
|
-
suspended: "suspended";
|
|
22
21
|
created: "created";
|
|
22
|
+
suspended: "suspended";
|
|
23
23
|
started: "started";
|
|
24
24
|
stopped: "stopped";
|
|
25
25
|
failed: "failed";
|
|
@@ -85,8 +85,8 @@ export declare const FlyMachineSchema: z.ZodObject<{
|
|
|
85
85
|
id: z.ZodString;
|
|
86
86
|
name: z.ZodString;
|
|
87
87
|
state: z.ZodCatch<z.ZodEnum<{
|
|
88
|
-
suspended: "suspended";
|
|
89
88
|
created: "created";
|
|
89
|
+
suspended: "suspended";
|
|
90
90
|
started: "started";
|
|
91
91
|
stopped: "stopped";
|
|
92
92
|
failed: "failed";
|
|
@@ -130,8 +130,8 @@ export declare const FlyMachinesListSchema: z.ZodArray<z.ZodObject<{
|
|
|
130
130
|
id: z.ZodString;
|
|
131
131
|
name: z.ZodString;
|
|
132
132
|
state: z.ZodCatch<z.ZodEnum<{
|
|
133
|
-
suspended: "suspended";
|
|
134
133
|
created: "created";
|
|
134
|
+
suspended: "suspended";
|
|
135
135
|
started: "started";
|
|
136
136
|
stopped: "stopped";
|
|
137
137
|
failed: "failed";
|
package/esm/util/constants.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const ROUTER_DOCKER_DIR: string;
|
|
|
4
4
|
export declare const SOCKS_PROXY_PORT = 1080;
|
|
5
5
|
export declare const TAILSCALE_API_KEY_PREFIX = "tskey-api-";
|
|
6
6
|
export declare const ENV_TAILSCALE_API_KEY = "TAILSCALE_API_KEY";
|
|
7
|
+
export declare const ENV_FLY_API_TOKEN = "FLY_API_TOKEN";
|
|
7
8
|
export declare const FLYCTL_INSTALL_URL = "https://fly.io/docs/flyctl/install/";
|
|
8
9
|
export declare const SECRET_TAILSCALE_AUTHKEY = "TAILSCALE_AUTHKEY";
|
|
9
10
|
export declare const SECRET_NETWORK_NAME = "NETWORK_NAME";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/util/constants.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,eAAO,MAAM,iBAAiB,QACnB,CAAC;AAMZ,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAMrC,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/util/constants.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,eAAO,MAAM,iBAAiB,QACnB,CAAC;AAMZ,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAMrC,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAMjD,eAAO,MAAM,kBAAkB,wCAAwC,CAAC;AAMxE,eAAO,MAAM,wBAAwB,sBAAsB,CAAC;AAC5D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAM5C,eAAO,MAAM,2BAA2B,yBAAyB,CAAC"}
|
package/esm/util/constants.js
CHANGED
|
@@ -17,6 +17,7 @@ export const SOCKS_PROXY_PORT = 1080;
|
|
|
17
17
|
// =============================================================================
|
|
18
18
|
export const TAILSCALE_API_KEY_PREFIX = "tskey-api-";
|
|
19
19
|
export const ENV_TAILSCALE_API_KEY = "TAILSCALE_API_KEY";
|
|
20
|
+
export const ENV_FLY_API_TOKEN = "FLY_API_TOKEN";
|
|
20
21
|
// =============================================================================
|
|
21
22
|
// External URLs
|
|
22
23
|
// =============================================================================
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export interface CredentialStore {
|
|
2
2
|
getTailscaleApiKey(): Promise<string | null>;
|
|
3
3
|
setTailscaleApiKey(key: string): Promise<void>;
|
|
4
|
+
getFlyToken(): Promise<string | null>;
|
|
5
|
+
setFlyToken(token: string): Promise<void>;
|
|
6
|
+
clear(): Promise<void>;
|
|
4
7
|
}
|
|
5
8
|
export declare const createConfigCredentialStore: () => CredentialStore;
|
|
6
9
|
export declare const getCredentialStore: () => CredentialStore;
|
|
@@ -17,5 +20,6 @@ export declare const checkDependencies: (out: {
|
|
|
17
20
|
die(msg: string): never;
|
|
18
21
|
}) => Promise<{
|
|
19
22
|
tailscaleKey: string;
|
|
23
|
+
flyToken: string | null;
|
|
20
24
|
}>;
|
|
21
25
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/util/credentials.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/util/credentials.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,eAAe;IAC9B,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA+BD,eAAO,MAAM,2BAA2B,QAAO,eAiC9C,CAAC;AAMF,eAAO,MAAM,kBAAkB,QAAO,eA8BrC,CAAC;AAMF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC5B,KAAK;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,KAC1D,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoD3D,CAAC"}
|
package/esm/util/credentials.js
CHANGED
|
@@ -1,41 +1,68 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
// Credential Store - Persistent Tailscale
|
|
2
|
+
// Credential Store - Persistent Tailscale & Fly.io Token Storage
|
|
3
3
|
// =============================================================================
|
|
4
4
|
import * as dntShim from "../_dnt.shims.js";
|
|
5
5
|
import { z } from "../deps/jsr.io/@zod/zod/4.3.6/src/index.js";
|
|
6
6
|
import { commandExists, ensureConfigDir, fileExists, getConfigDir, } from "../lib/cli.js";
|
|
7
|
-
import { ENV_TAILSCALE_API_KEY } from "./constants.js";
|
|
7
|
+
import { ENV_FLY_API_TOKEN, ENV_TAILSCALE_API_KEY } from "./constants.js";
|
|
8
8
|
// =============================================================================
|
|
9
9
|
// Schema
|
|
10
10
|
// =============================================================================
|
|
11
11
|
const CredentialsSchema = z.object({
|
|
12
|
-
apiKey: z.string(),
|
|
12
|
+
apiKey: z.string().optional(),
|
|
13
|
+
flyToken: z.string().optional(),
|
|
13
14
|
});
|
|
14
15
|
// =============================================================================
|
|
15
16
|
// Config File Implementation
|
|
16
17
|
// =============================================================================
|
|
17
18
|
const getCredentialsPath = () => `${getConfigDir()}/credentials.json`;
|
|
19
|
+
const readCredentials = async () => {
|
|
20
|
+
const path = getCredentialsPath();
|
|
21
|
+
if (!(await fileExists(path)))
|
|
22
|
+
return {};
|
|
23
|
+
try {
|
|
24
|
+
const content = await dntShim.Deno.readTextFile(path);
|
|
25
|
+
const result = CredentialsSchema.safeParse(JSON.parse(content));
|
|
26
|
+
return result.success ? result.data : {};
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const writeCredentials = async (data) => {
|
|
33
|
+
await ensureConfigDir();
|
|
34
|
+
const path = getCredentialsPath();
|
|
35
|
+
await dntShim.Deno.writeTextFile(path, JSON.stringify(data, null, 2) + "\n");
|
|
36
|
+
};
|
|
18
37
|
export const createConfigCredentialStore = () => {
|
|
19
38
|
return {
|
|
20
39
|
async getTailscaleApiKey() {
|
|
40
|
+
const data = await readCredentials();
|
|
41
|
+
return data.apiKey ?? null;
|
|
42
|
+
},
|
|
43
|
+
async setTailscaleApiKey(key) {
|
|
44
|
+
const data = await readCredentials();
|
|
45
|
+
data.apiKey = key;
|
|
46
|
+
await writeCredentials(data);
|
|
47
|
+
},
|
|
48
|
+
async getFlyToken() {
|
|
49
|
+
const data = await readCredentials();
|
|
50
|
+
return data.flyToken ?? null;
|
|
51
|
+
},
|
|
52
|
+
async setFlyToken(token) {
|
|
53
|
+
const data = await readCredentials();
|
|
54
|
+
data.flyToken = token;
|
|
55
|
+
await writeCredentials(data);
|
|
56
|
+
},
|
|
57
|
+
async clear() {
|
|
21
58
|
const path = getCredentialsPath();
|
|
22
|
-
if (!(await fileExists(path))) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
59
|
try {
|
|
26
|
-
|
|
27
|
-
const result = CredentialsSchema.safeParse(JSON.parse(content));
|
|
28
|
-
return result.success ? result.data.apiKey : null;
|
|
60
|
+
await dntShim.Deno.remove(path);
|
|
29
61
|
}
|
|
30
62
|
catch {
|
|
31
|
-
|
|
63
|
+
// File may not exist
|
|
32
64
|
}
|
|
33
65
|
},
|
|
34
|
-
async setTailscaleApiKey(key) {
|
|
35
|
-
await ensureConfigDir();
|
|
36
|
-
const path = getCredentialsPath();
|
|
37
|
-
await dntShim.Deno.writeTextFile(path, JSON.stringify({ apiKey: key }, null, 2) + "\n");
|
|
38
|
-
},
|
|
39
66
|
};
|
|
40
67
|
};
|
|
41
68
|
// =============================================================================
|
|
@@ -53,6 +80,18 @@ export const getCredentialStore = () => {
|
|
|
53
80
|
async setTailscaleApiKey(key) {
|
|
54
81
|
await fileStore.setTailscaleApiKey(key);
|
|
55
82
|
},
|
|
83
|
+
async getFlyToken() {
|
|
84
|
+
const envToken = dntShim.Deno.env.get(ENV_FLY_API_TOKEN);
|
|
85
|
+
if (envToken)
|
|
86
|
+
return envToken;
|
|
87
|
+
return await fileStore.getFlyToken();
|
|
88
|
+
},
|
|
89
|
+
async setFlyToken(token) {
|
|
90
|
+
await fileStore.setFlyToken(token);
|
|
91
|
+
},
|
|
92
|
+
async clear() {
|
|
93
|
+
await fileStore.clear();
|
|
94
|
+
},
|
|
56
95
|
};
|
|
57
96
|
};
|
|
58
97
|
// =============================================================================
|
|
@@ -71,9 +110,33 @@ export const checkDependencies = async (out) => {
|
|
|
71
110
|
if (!(await commandExists("fly"))) {
|
|
72
111
|
errors.push("Flyctl Not Found. Install from https://fly.io/docs/flyctl/install/");
|
|
73
112
|
}
|
|
74
|
-
const
|
|
113
|
+
const credentials = getCredentialStore();
|
|
114
|
+
const key = await credentials.getTailscaleApiKey();
|
|
75
115
|
if (!key) {
|
|
76
|
-
errors.push("Tailscale API Key Required. Run 'ambit
|
|
116
|
+
errors.push("Tailscale API Key Required. Run 'ambit auth login' or set TAILSCALE_API_KEY");
|
|
117
|
+
}
|
|
118
|
+
let flyToken = await credentials.getFlyToken();
|
|
119
|
+
if (!flyToken) {
|
|
120
|
+
// Adopt token from flyctl's own config if available
|
|
121
|
+
const home = dntShim.Deno.env.get("HOME") || dntShim.Deno.env.get("USERPROFILE") || "";
|
|
122
|
+
const configPath = `${home}/.fly/config.yml`;
|
|
123
|
+
if (await fileExists(configPath)) {
|
|
124
|
+
try {
|
|
125
|
+
const content = await dntShim.Deno.readTextFile(configPath);
|
|
126
|
+
const match = content.match(/access_token:\s*(.+)/);
|
|
127
|
+
if (match?.[1]) {
|
|
128
|
+
const adopted = match[1].trim();
|
|
129
|
+
await credentials.setFlyToken(adopted);
|
|
130
|
+
flyToken = adopted;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Ignore read errors
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!flyToken) {
|
|
139
|
+
errors.push("Fly.io Token Required. Run 'ambit auth login' or set FLY_API_TOKEN");
|
|
77
140
|
}
|
|
78
141
|
if (errors.length === 1) {
|
|
79
142
|
return out.die(errors[0]);
|
|
@@ -83,5 +146,5 @@ export const checkDependencies = async (out) => {
|
|
|
83
146
|
out.err(e);
|
|
84
147
|
return out.die("Missing Prerequisites");
|
|
85
148
|
}
|
|
86
|
-
return { tailscaleKey: key };
|
|
149
|
+
return { tailscaleKey: key, flyToken };
|
|
87
150
|
};
|
package/esm/util/session.js
CHANGED
|
@@ -10,8 +10,8 @@ import { resolveOrg } from "./resolve.js";
|
|
|
10
10
|
* validates fly CLI + Tailscale key, authenticates with Fly, and resolves org.
|
|
11
11
|
*/
|
|
12
12
|
export const initSession = async (out, opts) => {
|
|
13
|
-
const { tailscaleKey } = await checkDependencies(out);
|
|
14
|
-
const fly = createFlyProvider();
|
|
13
|
+
const { tailscaleKey, flyToken } = await checkDependencies(out);
|
|
14
|
+
const fly = createFlyProvider(flyToken ?? undefined);
|
|
15
15
|
await fly.auth.login({ interactive: !opts.json });
|
|
16
16
|
const tailscale = createTailscaleProvider(tailscaleKey);
|
|
17
17
|
const org = await resolveOrg(fly, opts, out);
|