@cardelli/ambit 0.3.0 → 0.3.2
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 +32 -13
- package/esm/cli/commands/create/index.js +10 -28
- package/esm/cli/commands/create/machine.d.ts +2 -1
- package/esm/cli/commands/create/machine.d.ts.map +1 -1
- package/esm/cli/commands/create/machine.js +69 -28
- package/esm/cli/commands/list/apps.d.ts +2 -0
- package/esm/cli/commands/list/apps.d.ts.map +1 -0
- package/esm/cli/commands/list/apps.js +72 -0
- package/esm/cli/commands/list/index.d.ts +2 -0
- package/esm/cli/commands/list/index.d.ts.map +1 -0
- package/esm/cli/commands/list/index.js +62 -0
- package/esm/cli/commands/list/networks.d.ts +2 -0
- package/esm/cli/commands/list/networks.d.ts.map +1 -0
- package/esm/cli/commands/{list.js → list/networks.js} +17 -27
- package/esm/cli/commands/status/app.d.ts +2 -0
- package/esm/cli/commands/status/app.d.ts.map +1 -0
- package/esm/cli/commands/status/app.js +141 -0
- package/esm/cli/commands/status/index.d.ts +2 -0
- package/esm/cli/commands/status/index.d.ts.map +1 -0
- package/esm/cli/commands/status/index.js +68 -0
- package/esm/cli/commands/status/network.d.ts +2 -0
- package/esm/cli/commands/status/network.d.ts.map +1 -0
- package/esm/cli/commands/status/network.js +104 -0
- package/esm/cli/commands/status/networks.d.ts +2 -0
- package/esm/cli/commands/status/networks.d.ts.map +1 -0
- package/esm/cli/commands/status/networks.js +62 -0
- package/esm/deno.js +1 -1
- package/esm/main.d.ts +2 -2
- package/esm/main.d.ts.map +1 -1
- package/esm/main.js +5 -2
- package/esm/router/start.sh +7 -4
- package/esm/schemas/fly.d.ts +84 -4
- package/esm/schemas/fly.d.ts.map +1 -1
- package/esm/schemas/fly.js +34 -2
- package/esm/util/constants.d.ts +0 -1
- package/esm/util/constants.d.ts.map +1 -1
- package/esm/util/constants.js +0 -1
- package/esm/util/discovery.d.ts +4 -1
- package/esm/util/discovery.d.ts.map +1 -1
- package/esm/util/discovery.js +3 -0
- package/package.json +1 -1
- package/esm/cli/commands/list.d.ts +0 -2
- package/esm/cli/commands/list.d.ts.map +0 -1
- package/esm/cli/commands/status.d.ts +0 -2
- package/esm/cli/commands/status.d.ts.map +0 -1
- package/esm/cli/commands/status.js +0 -334
package/README.md
CHANGED
|
@@ -160,32 +160,51 @@ These work with all three modes.
|
|
|
160
160
|
| `-y`, `--yes` | Skip confirmation prompts |
|
|
161
161
|
| `--json` | Machine-readable JSON output |
|
|
162
162
|
|
|
163
|
-
### `ambit
|
|
163
|
+
### `ambit list networks|apps`
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
#### `ambit list networks`
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
Lists all networks and their routers in a table showing the network name, app name, region, machine state, Tailscale connectivity status, and ACL tag.
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
| Flag | Description |
|
|
170
|
+
| ----------------- | ------------------------------ |
|
|
171
|
+
| `--org <org>` | Fly.io organization slug |
|
|
172
|
+
| `--json` | Machine-readable JSON output |
|
|
173
|
+
|
|
174
|
+
#### `ambit list apps <network>`
|
|
175
|
+
|
|
176
|
+
Lists all workload apps on a specific network in a table showing the app name, region, and machine state.
|
|
170
177
|
|
|
171
178
|
| Flag | Description |
|
|
172
179
|
| ----------------- | ------------------------------ |
|
|
173
180
|
| `--org <org>` | Fly.io organization slug |
|
|
174
181
|
| `--json` | Machine-readable JSON output |
|
|
175
182
|
|
|
176
|
-
|
|
183
|
+
### `ambit status [networks|network|app]`
|
|
177
184
|
|
|
178
|
-
|
|
185
|
+
Without a subcommand, defaults to showing all networks (same as `status networks`).
|
|
179
186
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
| `--org <org>` | Fly.io organization slug |
|
|
184
|
-
| `--json` | Machine-readable JSON output |
|
|
187
|
+
#### `ambit status networks`
|
|
188
|
+
|
|
189
|
+
Shows a summary table of all networks and their routers.
|
|
185
190
|
|
|
186
|
-
|
|
191
|
+
| Flag | Description |
|
|
192
|
+
| ----------------- | ------------------------------ |
|
|
193
|
+
| `--org <org>` | Fly.io organization slug |
|
|
194
|
+
| `--json` | Machine-readable JSON output |
|
|
195
|
+
|
|
196
|
+
#### `ambit status network <name>`
|
|
197
|
+
|
|
198
|
+
Shows detailed status for a specific network: machine state, SOCKS proxy, Tailscale IP, subnet, and apps on the network.
|
|
199
|
+
|
|
200
|
+
| Flag | Description |
|
|
201
|
+
| ----------------- | ------------------------------ |
|
|
202
|
+
| `--org <org>` | Fly.io organization slug |
|
|
203
|
+
| `--json` | Machine-readable JSON output |
|
|
187
204
|
|
|
188
|
-
|
|
205
|
+
#### `ambit status app <app>.<network>`
|
|
206
|
+
|
|
207
|
+
Shows detailed status for a specific app: machines, Flycast IPs, and the backing router.
|
|
189
208
|
|
|
190
209
|
| Flag | Description |
|
|
191
210
|
| ----------------- | ------------------------------ |
|
|
@@ -12,9 +12,9 @@ import { isPublicTld } from "../../../util/guard.js";
|
|
|
12
12
|
import { createFlyProvider } from "../../../providers/fly.js";
|
|
13
13
|
import { createTailscaleProvider, } from "../../../providers/tailscale.js";
|
|
14
14
|
import { getCredentialStore } from "../../../util/credentials.js";
|
|
15
|
-
import {
|
|
15
|
+
import { TAILSCALE_API_KEY_PREFIX } from "../../../util/constants.js";
|
|
16
16
|
import { resolveOrg } from "../../../util/resolve.js";
|
|
17
|
-
import { assertAdditivePatch, isAutoApproverConfigured, isTagOwnerConfigured,
|
|
17
|
+
import { assertAdditivePatch, isAutoApproverConfigured, isTagOwnerConfigured, patchTagOwner, } from "../../../util/tailscale-local.js";
|
|
18
18
|
import { createTransition, hydrateCreate, reportSkipped, } from "./machine.js";
|
|
19
19
|
// =============================================================================
|
|
20
20
|
// Stage 1: Fly.io Configuration
|
|
@@ -107,40 +107,21 @@ const stageTailscaleConfig = async (out, opts) => {
|
|
|
107
107
|
else {
|
|
108
108
|
tagOwnerSpinner.success(`${opts.tag} Found in Tailscale ACL`);
|
|
109
109
|
}
|
|
110
|
-
if (
|
|
111
|
-
const hasApprover = isAutoApproverConfigured(policy, opts.tag);
|
|
112
|
-
if (!hasApprover && policy) {
|
|
113
|
-
const beforeApprover = policy;
|
|
114
|
-
policy = patchAutoApprover(policy, opts.tag, FLY_PRIVATE_SUBNET);
|
|
115
|
-
assertAdditivePatch(beforeApprover, policy);
|
|
116
|
-
const validateApprover = await tailscale.acl.validatePolicy(policy);
|
|
117
|
-
if (!validateApprover.ok) {
|
|
118
|
-
return handleAclSetFailure(out, validateApprover, `Validating autoApprover patch for ${opts.tag}`);
|
|
119
|
-
}
|
|
120
|
-
const approverSpinner = out.spinner(`Adding autoApprover for ${opts.tag}`);
|
|
121
|
-
const result = await tailscale.acl.setPolicy(policy);
|
|
122
|
-
if (!result.ok) {
|
|
123
|
-
approverSpinner.fail(`Adding autoApprover for ${opts.tag}`);
|
|
124
|
-
return handleAclSetFailure(out, result, `Adding autoApprover for ${opts.tag}`);
|
|
125
|
-
}
|
|
126
|
-
approverSpinner.success(`Added autoApprover for ${opts.tag}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else if (opts.json) {
|
|
110
|
+
if (opts.manual && opts.json) {
|
|
130
111
|
const approverSpinner = out.spinner(`Checking autoApprovers for ${opts.tag}`);
|
|
131
112
|
const hasApprover = isAutoApproverConfigured(policy, opts.tag);
|
|
132
113
|
if (!hasApprover) {
|
|
133
114
|
approverSpinner.fail(`Auto-approve Not Configured for ${opts.tag}`);
|
|
134
115
|
out.blank()
|
|
135
|
-
.text(" In
|
|
136
|
-
.text(
|
|
116
|
+
.text(" In --manual --json mode, ambit can't interactively approve the")
|
|
117
|
+
.text(" router's subnet routes. Set up autoApprovers first:")
|
|
137
118
|
.link(" https://login.tailscale.com/admin/acls/visual/auto-approvers")
|
|
138
|
-
.dim(` Route:
|
|
119
|
+
.dim(` Route: <subnet>/48 Owner: ${opts.tag}`)
|
|
139
120
|
.blank()
|
|
140
|
-
.dim(" Or
|
|
141
|
-
.dim(` "autoApprovers": { "routes": { "
|
|
121
|
+
.dim(" Or in the ACL file:")
|
|
122
|
+
.dim(` "autoApprovers": { "routes": { "<subnet>/48": ["${opts.tag}"] } }`)
|
|
142
123
|
.blank();
|
|
143
|
-
return out.die(`Set Up Auto-approve for ${opts.tag} to Use --json`);
|
|
124
|
+
return out.die(`Set Up Auto-approve for ${opts.tag} to Use --manual --json`);
|
|
144
125
|
}
|
|
145
126
|
approverSpinner.success(`Auto-approve Configured for ${opts.tag}`);
|
|
146
127
|
}
|
|
@@ -334,6 +315,7 @@ ${bold("EXAMPLES")}
|
|
|
334
315
|
region,
|
|
335
316
|
tag,
|
|
336
317
|
shouldApprove,
|
|
318
|
+
manual,
|
|
337
319
|
});
|
|
338
320
|
};
|
|
339
321
|
// =============================================================================
|
|
@@ -3,7 +3,7 @@ import { Result } from "../../../lib/result.js";
|
|
|
3
3
|
import { type FlyProvider } from "../../../providers/fly.js";
|
|
4
4
|
import type { TailscaleProvider } from "../../../providers/tailscale.js";
|
|
5
5
|
import type { TailscaleDevice } from "../../../schemas/tailscale.js";
|
|
6
|
-
export type CreatePhase = "create_app" | "deploy_router" | "
|
|
6
|
+
export type CreatePhase = "create_app" | "deploy_router" | "approve_routes" | "configure_dns" | "accept_routes" | "complete";
|
|
7
7
|
export type CreateResult = {
|
|
8
8
|
network: string;
|
|
9
9
|
router: {
|
|
@@ -22,6 +22,7 @@ export interface CreateCtx {
|
|
|
22
22
|
region: string;
|
|
23
23
|
tag: string;
|
|
24
24
|
shouldApprove: boolean;
|
|
25
|
+
manual: boolean;
|
|
25
26
|
appName: string;
|
|
26
27
|
routerId: string;
|
|
27
28
|
device?: TailscaleDevice;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/create/machine.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQhD,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/create/machine.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAQhD,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAW7E,OAAO,KAAK,EAAgB,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAOrE,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,UAAU,CAAC;AAMf,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAcD,eAAO,MAAM,aAAa,GACxB,KAAK,MAAM,CAAC,YAAY,CAAC,EACzB,YAAY,WAAW,SAMxB,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,KAAK,SAAS,KACb,OAAO,CAAC,WAAW,CA4BrB,CAAC;AAMF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,WAAW,EAClB,KAAK,SAAS,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CA6K7B,CAAC"}
|
|
@@ -7,7 +7,7 @@ import { extractSubnet } from "../../../util/fly-transforms.js";
|
|
|
7
7
|
import { ROUTER_DOCKER_DIR, SECRET_NETWORK_NAME, SECRET_ROUTER_ID, SECRET_TAILSCALE_AUTHKEY, } from "../../../util/constants.js";
|
|
8
8
|
import { FlyDeployError } from "../../../providers/fly.js";
|
|
9
9
|
import { getRouterAppName } from "../../../util/naming.js";
|
|
10
|
-
import { enableAcceptRoutes, isAcceptRoutesEnabled, isAutoApproverConfigured, isTailscaleInstalled, waitForDevice, } from "../../../util/tailscale-local.js";
|
|
10
|
+
import { assertAdditivePatch, enableAcceptRoutes, isAcceptRoutesEnabled, isAutoApproverConfigured, isTailscaleInstalled, patchAutoApprover, waitForDevice, } from "../../../util/tailscale-local.js";
|
|
11
11
|
import { findRouterApp, getRouterMachineInfo } from "../../../util/discovery.js";
|
|
12
12
|
// =============================================================================
|
|
13
13
|
// Phase Labels
|
|
@@ -15,7 +15,6 @@ import { findRouterApp, getRouterMachineInfo } from "../../../util/discovery.js"
|
|
|
15
15
|
const CREATE_PHASES = [
|
|
16
16
|
{ phase: "create_app", label: "Fly App Created" },
|
|
17
17
|
{ phase: "deploy_router", label: "Router Deployed" },
|
|
18
|
-
{ phase: "await_device", label: "Router in Tailnet" },
|
|
19
18
|
{ phase: "approve_routes", label: "Routes Approved" },
|
|
20
19
|
{ phase: "configure_dns", label: "Split DNS Configured" },
|
|
21
20
|
{ phase: "accept_routes", label: "Accept Routes Enabled" },
|
|
@@ -44,7 +43,7 @@ export const hydrateCreate = async (ctx) => {
|
|
|
44
43
|
return "complete";
|
|
45
44
|
const device = await ctx.tailscale.devices.getByHostname(router.appName);
|
|
46
45
|
if (!device)
|
|
47
|
-
return "
|
|
46
|
+
return "approve_routes";
|
|
48
47
|
ctx.device = device;
|
|
49
48
|
const routes = await ctx.tailscale.routes.get(device.id);
|
|
50
49
|
if (!routes || routes.unapproved.length > 0)
|
|
@@ -70,15 +69,7 @@ export const createTransition = async (phase, ctx) => {
|
|
|
70
69
|
return Result.ok("deploy_router");
|
|
71
70
|
}
|
|
72
71
|
case "deploy_router": {
|
|
73
|
-
|
|
74
|
-
reusable: false,
|
|
75
|
-
ephemeral: false,
|
|
76
|
-
preauthorized: true,
|
|
77
|
-
tags: [ctx.tag],
|
|
78
|
-
});
|
|
79
|
-
ctx.out.ok("Auth Key Created");
|
|
80
|
-
await ctx.out.spin("Setting Secrets", () => ctx.fly.secrets.set(ctx.appName, {
|
|
81
|
-
[SECRET_TAILSCALE_AUTHKEY]: authKey,
|
|
72
|
+
await ctx.out.spin("Staging Secrets", () => ctx.fly.secrets.set(ctx.appName, {
|
|
82
73
|
[SECRET_NETWORK_NAME]: ctx.network,
|
|
83
74
|
[SECRET_ROUTER_ID]: ctx.routerId,
|
|
84
75
|
}, { stage: true }));
|
|
@@ -102,34 +93,84 @@ export const createTransition = async (phase, ctx) => {
|
|
|
102
93
|
const m = machines.find((m) => m.private_ip);
|
|
103
94
|
if (m?.private_ip)
|
|
104
95
|
ctx.subnet = extractSubnet(m.private_ip);
|
|
105
|
-
|
|
106
|
-
return Result.ok("complete");
|
|
107
|
-
return Result.ok("await_device");
|
|
96
|
+
return Result.ok("approve_routes");
|
|
108
97
|
}
|
|
109
|
-
case "
|
|
110
|
-
ctx.device = await waitForDevice(ctx.tailscale, ctx.appName, 180000);
|
|
111
|
-
ctx.out.ok(`Router Joined Tailnet: ${ctx.device.addresses[0]}`);
|
|
98
|
+
case "approve_routes": {
|
|
112
99
|
if (!ctx.subnet) {
|
|
113
100
|
const machines = await ctx.fly.machines.list(ctx.appName);
|
|
114
101
|
const m = machines.find((m) => m.private_ip);
|
|
115
102
|
if (m?.private_ip)
|
|
116
103
|
ctx.subnet = extractSubnet(m.private_ip);
|
|
117
104
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!ctx.device || !ctx.subnet) {
|
|
122
|
-
return Result.err("Missing Device or Subnet");
|
|
123
|
-
}
|
|
124
|
-
const policy = await ctx.tailscale.acl.getPolicy();
|
|
105
|
+
if (!ctx.subnet)
|
|
106
|
+
return Result.err("Missing Subnet");
|
|
107
|
+
let policy = await ctx.tailscale.acl.getPolicy();
|
|
125
108
|
const hasAutoApprover = isAutoApproverConfigured(policy, ctx.tag);
|
|
126
|
-
|
|
127
|
-
|
|
109
|
+
let approverReady = hasAutoApprover;
|
|
110
|
+
if (!hasAutoApprover && !ctx.manual && policy) {
|
|
111
|
+
const before = policy;
|
|
112
|
+
policy = patchAutoApprover(policy, ctx.tag, ctx.subnet);
|
|
113
|
+
assertAdditivePatch(before, policy);
|
|
114
|
+
const vr = await ctx.tailscale.acl.validatePolicy(policy);
|
|
115
|
+
if (!vr.ok) {
|
|
116
|
+
ctx.out.warn(`Could Not Validate autoApprover Patch: ${vr.error ?? `HTTP ${vr.status}`}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const sr = await ctx.tailscale.acl.setPolicy(policy);
|
|
120
|
+
if (sr.ok) {
|
|
121
|
+
ctx.out.ok(`Added autoApprover for ${ctx.tag} → ${ctx.subnet}`);
|
|
122
|
+
approverReady = true;
|
|
123
|
+
}
|
|
124
|
+
else if (sr.status === 403) {
|
|
125
|
+
ctx.out.warn("API Token Lacks ACL Write Permission — Will Approve Routes Manually");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
ctx.out.warn(`Could Not Set autoApprover: ${sr.error ?? `HTTP ${sr.status}`}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
128
131
|
}
|
|
129
|
-
|
|
132
|
+
// If the device isn't in the tailnet yet, the router hasn't
|
|
133
|
+
// authenticated. Mint an auth key and deliver it — the non-staged
|
|
134
|
+
// secrets set triggers a Fly restart. The router boots with the key,
|
|
135
|
+
// authenticates, and advertises routes. With autoApprover in place,
|
|
136
|
+
// routes are auto-approved immediately.
|
|
137
|
+
if (!ctx.device) {
|
|
138
|
+
const existing = await ctx.tailscale.devices.getByHostname(ctx.appName);
|
|
139
|
+
if (existing) {
|
|
140
|
+
ctx.device = existing;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const authKey = await ctx.tailscale.auth.createKey({
|
|
144
|
+
reusable: false,
|
|
145
|
+
ephemeral: false,
|
|
146
|
+
preauthorized: true,
|
|
147
|
+
tags: [ctx.tag],
|
|
148
|
+
});
|
|
149
|
+
ctx.out.ok("Auth Key Created");
|
|
150
|
+
const keySpinner = ctx.out.spinner("Delivering Auth Key (restarting router)");
|
|
151
|
+
await ctx.fly.secrets.set(ctx.appName, {
|
|
152
|
+
[SECRET_TAILSCALE_AUTHKEY]: authKey,
|
|
153
|
+
});
|
|
154
|
+
keySpinner.success("Auth Key Delivered");
|
|
155
|
+
if (!ctx.shouldApprove) {
|
|
156
|
+
ctx.out.dim(" Skipping Route Approval (--no-auto-approve)");
|
|
157
|
+
return Result.ok("complete");
|
|
158
|
+
}
|
|
159
|
+
ctx.device = await waitForDevice(ctx.tailscale, ctx.appName, 180000);
|
|
160
|
+
ctx.out.ok(`Router Joined Tailnet: ${ctx.device.addresses[0]}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const routes = await ctx.tailscale.routes.get(ctx.device.id);
|
|
164
|
+
if (routes && routes.unapproved.length > 0) {
|
|
165
|
+
if (approverReady) {
|
|
166
|
+
ctx.out.warn("Routes Not Auto-Approved Despite autoApprover — Approving Manually");
|
|
167
|
+
}
|
|
130
168
|
await ctx.tailscale.routes.approve(ctx.device.id, [ctx.subnet]);
|
|
131
169
|
ctx.out.ok("Subnet Routes Approved");
|
|
132
170
|
}
|
|
171
|
+
else {
|
|
172
|
+
ctx.out.ok("Routes Auto-Approved via ACL Policy");
|
|
173
|
+
}
|
|
133
174
|
return Result.ok("configure_dns");
|
|
134
175
|
}
|
|
135
176
|
case "configure_dns": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/list/apps.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAqE3D,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// List Apps — List All Workload Apps on a Network
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import { parseArgs } from "../../../deps/jsr.io/@std/cli/1.0.28/mod.js";
|
|
5
|
+
import { Table } from "../../../deps/jsr.io/@cliffy/table/1.0.0/mod.js";
|
|
6
|
+
import { bold } from "../../../lib/cli.js";
|
|
7
|
+
import { checkArgs } from "../../../lib/args.js";
|
|
8
|
+
import { createOutput } from "../../../lib/output.js";
|
|
9
|
+
import { findRouterApp, listWorkloadAppsOnNetwork, } from "../../../util/discovery.js";
|
|
10
|
+
import { initSession } from "../../../util/session.js";
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// List Apps Command
|
|
13
|
+
// =============================================================================
|
|
14
|
+
export const listApps = async (argv) => {
|
|
15
|
+
const opts = { string: ["org"], boolean: ["help", "json"] };
|
|
16
|
+
const args = parseArgs(argv, opts);
|
|
17
|
+
checkArgs(args, opts, "ambit list apps");
|
|
18
|
+
if (args.help) {
|
|
19
|
+
console.log(`
|
|
20
|
+
${bold("ambit list apps")} - List Apps on a Network
|
|
21
|
+
|
|
22
|
+
${bold("USAGE")}
|
|
23
|
+
ambit list apps <network> [--org <org>] [--json]
|
|
24
|
+
|
|
25
|
+
${bold("OPTIONS")}
|
|
26
|
+
--org <org> Fly.io organization slug
|
|
27
|
+
--json Output as JSON
|
|
28
|
+
|
|
29
|
+
${bold("EXAMPLES")}
|
|
30
|
+
ambit list apps browsers
|
|
31
|
+
ambit list apps browsers --json
|
|
32
|
+
`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const out = createOutput(args.json);
|
|
36
|
+
const network = typeof args._[0] === "string" ? args._[0] : undefined;
|
|
37
|
+
if (!network) {
|
|
38
|
+
return out.die("Missing Network Name. Usage: ambit list apps <network>");
|
|
39
|
+
}
|
|
40
|
+
const { fly, org } = await initSession(out, {
|
|
41
|
+
json: args.json,
|
|
42
|
+
org: args.org,
|
|
43
|
+
});
|
|
44
|
+
const router = await findRouterApp(fly, org, network);
|
|
45
|
+
if (!router) {
|
|
46
|
+
return out.die(`No Network Found: '${network}'`);
|
|
47
|
+
}
|
|
48
|
+
const workloads = await listWorkloadAppsOnNetwork(fly, org, network);
|
|
49
|
+
if (workloads.length === 0) {
|
|
50
|
+
out.blank()
|
|
51
|
+
.text(`No Apps Found on Network '${network}'.`)
|
|
52
|
+
.dim(" Deploy one with: ambit deploy <app>.<network>")
|
|
53
|
+
.blank();
|
|
54
|
+
out.done({ network, apps: [] });
|
|
55
|
+
out.print();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
out.blank().header(`Apps on '${network}'`).blank();
|
|
59
|
+
const rows = workloads.map((w) => [w.appName, w.status]);
|
|
60
|
+
const table = new Table()
|
|
61
|
+
.header(["App", "Status"])
|
|
62
|
+
.body(rows)
|
|
63
|
+
.indent(2)
|
|
64
|
+
.padding(2);
|
|
65
|
+
out.text(table.toString());
|
|
66
|
+
out.blank();
|
|
67
|
+
out.done({
|
|
68
|
+
network,
|
|
69
|
+
apps: workloads.map((w) => ({ appName: w.appName, status: w.status })),
|
|
70
|
+
});
|
|
71
|
+
out.print();
|
|
72
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/list/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// List Command - List Networks or Apps
|
|
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 } from "../../../lib/cli.js";
|
|
7
|
+
import { registerCommand } from "../../mod.js";
|
|
8
|
+
import { listNetworks } from "./networks.js";
|
|
9
|
+
import { listApps } from "./apps.js";
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Top-Level Help
|
|
12
|
+
// =============================================================================
|
|
13
|
+
const showListHelp = () => {
|
|
14
|
+
console.log(`
|
|
15
|
+
${bold("ambit list")} - List Networks or Apps
|
|
16
|
+
|
|
17
|
+
${bold("USAGE")}
|
|
18
|
+
ambit list networks [options]
|
|
19
|
+
ambit list apps <network> [options]
|
|
20
|
+
|
|
21
|
+
${bold("SUBCOMMANDS")}
|
|
22
|
+
networks List all networks and their routers
|
|
23
|
+
apps List workload apps on a specific network
|
|
24
|
+
|
|
25
|
+
${bold("OPTIONS")}
|
|
26
|
+
--org <org> Fly.io organization slug
|
|
27
|
+
--json Output as JSON
|
|
28
|
+
|
|
29
|
+
${bold("EXAMPLES")}
|
|
30
|
+
ambit list networks
|
|
31
|
+
ambit list apps browsers
|
|
32
|
+
ambit list apps browsers --json
|
|
33
|
+
|
|
34
|
+
Run 'ambit list networks --help' or 'ambit list apps --help' for details.
|
|
35
|
+
`);
|
|
36
|
+
};
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Dispatcher
|
|
39
|
+
// =============================================================================
|
|
40
|
+
const list = async (argv) => {
|
|
41
|
+
const subcommand = typeof argv[0] === "string" ? argv[0] : undefined;
|
|
42
|
+
if (subcommand === "networks")
|
|
43
|
+
return listNetworks(argv.slice(1));
|
|
44
|
+
if (subcommand === "apps")
|
|
45
|
+
return listApps(argv.slice(1));
|
|
46
|
+
const args = parseArgs(argv, { boolean: ["help"] });
|
|
47
|
+
if (args.help) {
|
|
48
|
+
showListHelp();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
showListHelp();
|
|
52
|
+
dntShim.Deno.exit(1);
|
|
53
|
+
};
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// Register Command
|
|
56
|
+
// =============================================================================
|
|
57
|
+
registerCommand({
|
|
58
|
+
name: "list",
|
|
59
|
+
description: "List networks or apps",
|
|
60
|
+
usage: "ambit list networks|apps [options]",
|
|
61
|
+
run: list,
|
|
62
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"networks.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/list/networks.ts"],"names":[],"mappings":"AAqEA,eAAO,MAAM,YAAY,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CA2B/D,CAAC"}
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
// List
|
|
2
|
+
// List Networks — List All Discovered Routers Across Networks
|
|
3
3
|
// =============================================================================
|
|
4
|
-
import { parseArgs } from "
|
|
5
|
-
import { Table } from "
|
|
6
|
-
import { bold } from "
|
|
7
|
-
import { checkArgs } from "
|
|
8
|
-
import { createOutput } from "
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { initSession } from "../../util/session.js";
|
|
4
|
+
import { parseArgs } from "../../../deps/jsr.io/@std/cli/1.0.28/mod.js";
|
|
5
|
+
import { Table } from "../../../deps/jsr.io/@cliffy/table/1.0.0/mod.js";
|
|
6
|
+
import { bold } from "../../../lib/cli.js";
|
|
7
|
+
import { checkArgs } from "../../../lib/args.js";
|
|
8
|
+
import { createOutput } from "../../../lib/output.js";
|
|
9
|
+
import { discoverRouters } from "../../../util/discovery.js";
|
|
10
|
+
import { initSession } from "../../../util/session.js";
|
|
12
11
|
// =============================================================================
|
|
13
12
|
// Stage: Render
|
|
14
13
|
// =============================================================================
|
|
15
14
|
const stageRender = (out, routers) => {
|
|
16
15
|
if (routers.length === 0) {
|
|
17
16
|
out.blank()
|
|
18
|
-
.text("No
|
|
17
|
+
.text("No Networks Found.")
|
|
19
18
|
.dim(" Create one with: ambit create <network>")
|
|
20
19
|
.blank();
|
|
21
20
|
out.done({ routers: [] });
|
|
22
21
|
out.print();
|
|
23
22
|
return;
|
|
24
23
|
}
|
|
25
|
-
out.blank().header("
|
|
24
|
+
out.blank().header("Networks").blank();
|
|
26
25
|
const rows = routers.map((r) => {
|
|
27
26
|
const tsStatus = r.tailscale
|
|
28
27
|
? (r.tailscale.online ? "online" : "offline")
|
|
@@ -31,14 +30,14 @@ const stageRender = (out, routers) => {
|
|
|
31
30
|
return [
|
|
32
31
|
r.network,
|
|
33
32
|
r.appName,
|
|
33
|
+
r.status,
|
|
34
34
|
r.machine?.region ?? "-",
|
|
35
|
-
r.machine?.state ?? "unknown",
|
|
36
35
|
tsStatus,
|
|
37
36
|
tag,
|
|
38
37
|
];
|
|
39
38
|
});
|
|
40
39
|
const table = new Table()
|
|
41
|
-
.header(["Network", "App", "
|
|
40
|
+
.header(["Network", "App", "Status", "Region", "Tailscale", "Tag"])
|
|
42
41
|
.body(rows)
|
|
43
42
|
.indent(2)
|
|
44
43
|
.padding(2);
|
|
@@ -48,18 +47,18 @@ const stageRender = (out, routers) => {
|
|
|
48
47
|
out.print();
|
|
49
48
|
};
|
|
50
49
|
// =============================================================================
|
|
51
|
-
// List Command
|
|
50
|
+
// List Networks Command
|
|
52
51
|
// =============================================================================
|
|
53
|
-
const
|
|
52
|
+
export const listNetworks = async (argv) => {
|
|
54
53
|
const opts = { string: ["org"], boolean: ["help", "json"] };
|
|
55
54
|
const args = parseArgs(argv, opts);
|
|
56
|
-
checkArgs(args, opts, "ambit list");
|
|
55
|
+
checkArgs(args, opts, "ambit list networks");
|
|
57
56
|
if (args.help) {
|
|
58
57
|
console.log(`
|
|
59
|
-
${bold("ambit list")} - List All
|
|
58
|
+
${bold("ambit list networks")} - List All Networks
|
|
60
59
|
|
|
61
60
|
${bold("USAGE")}
|
|
62
|
-
ambit list [--org <org>] [--json]
|
|
61
|
+
ambit list networks [--org <org>] [--json]
|
|
63
62
|
|
|
64
63
|
${bold("OPTIONS")}
|
|
65
64
|
--org <org> Fly.io organization slug
|
|
@@ -75,12 +74,3 @@ ${bold("OPTIONS")}
|
|
|
75
74
|
const routers = await discoverRouters(out, fly, tailscale, org);
|
|
76
75
|
stageRender(out, routers);
|
|
77
76
|
};
|
|
78
|
-
// =============================================================================
|
|
79
|
-
// Register Command
|
|
80
|
-
// =============================================================================
|
|
81
|
-
registerCommand({
|
|
82
|
-
name: "list",
|
|
83
|
-
description: "List all discovered routers across networks",
|
|
84
|
-
usage: "ambit list [--org <org>] [--json]",
|
|
85
|
-
run: list,
|
|
86
|
-
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/status/app.ts"],"names":[],"mappings":"AA4JA,eAAO,MAAM,SAAS,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAyD5D,CAAC"}
|