@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.
Files changed (46) hide show
  1. package/README.md +32 -13
  2. package/esm/cli/commands/create/index.js +10 -28
  3. package/esm/cli/commands/create/machine.d.ts +2 -1
  4. package/esm/cli/commands/create/machine.d.ts.map +1 -1
  5. package/esm/cli/commands/create/machine.js +69 -28
  6. package/esm/cli/commands/list/apps.d.ts +2 -0
  7. package/esm/cli/commands/list/apps.d.ts.map +1 -0
  8. package/esm/cli/commands/list/apps.js +72 -0
  9. package/esm/cli/commands/list/index.d.ts +2 -0
  10. package/esm/cli/commands/list/index.d.ts.map +1 -0
  11. package/esm/cli/commands/list/index.js +62 -0
  12. package/esm/cli/commands/list/networks.d.ts +2 -0
  13. package/esm/cli/commands/list/networks.d.ts.map +1 -0
  14. package/esm/cli/commands/{list.js → list/networks.js} +17 -27
  15. package/esm/cli/commands/status/app.d.ts +2 -0
  16. package/esm/cli/commands/status/app.d.ts.map +1 -0
  17. package/esm/cli/commands/status/app.js +141 -0
  18. package/esm/cli/commands/status/index.d.ts +2 -0
  19. package/esm/cli/commands/status/index.d.ts.map +1 -0
  20. package/esm/cli/commands/status/index.js +68 -0
  21. package/esm/cli/commands/status/network.d.ts +2 -0
  22. package/esm/cli/commands/status/network.d.ts.map +1 -0
  23. package/esm/cli/commands/status/network.js +104 -0
  24. package/esm/cli/commands/status/networks.d.ts +2 -0
  25. package/esm/cli/commands/status/networks.d.ts.map +1 -0
  26. package/esm/cli/commands/status/networks.js +62 -0
  27. package/esm/deno.js +1 -1
  28. package/esm/main.d.ts +2 -2
  29. package/esm/main.d.ts.map +1 -1
  30. package/esm/main.js +5 -2
  31. package/esm/router/start.sh +7 -4
  32. package/esm/schemas/fly.d.ts +84 -4
  33. package/esm/schemas/fly.d.ts.map +1 -1
  34. package/esm/schemas/fly.js +34 -2
  35. package/esm/util/constants.d.ts +0 -1
  36. package/esm/util/constants.d.ts.map +1 -1
  37. package/esm/util/constants.js +0 -1
  38. package/esm/util/discovery.d.ts +4 -1
  39. package/esm/util/discovery.d.ts.map +1 -1
  40. package/esm/util/discovery.js +3 -0
  41. package/package.json +1 -1
  42. package/esm/cli/commands/list.d.ts +0 -2
  43. package/esm/cli/commands/list.d.ts.map +0 -1
  44. package/esm/cli/commands/status.d.ts +0 -2
  45. package/esm/cli/commands/status.d.ts.map +0 -1
  46. package/esm/cli/commands/status.js +0 -334
@@ -2,7 +2,6 @@ export declare const ROUTER_APP_PREFIX = "ambit-";
2
2
  export declare const DEFAULT_FLY_NETWORK = "default";
3
3
  export declare const ROUTER_DOCKER_DIR: string;
4
4
  export declare const SOCKS_PROXY_PORT = 1080;
5
- export declare const FLY_PRIVATE_SUBNET = "fdaa::/16";
6
5
  export declare const TAILSCALE_API_KEY_PREFIX = "tskey-api-";
7
6
  export declare const ENV_TAILSCALE_API_KEY = "TAILSCALE_API_KEY";
8
7
  export declare const FLYCTL_INSTALL_URL = "https://fly.io/docs/flyctl/install/";
@@ -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;AAErC,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAM9C,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAMzD,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"}
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;AAMzD,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"}
@@ -12,7 +12,6 @@ export const ROUTER_DOCKER_DIR = new URL("../router", globalThis[Symbol.for("imp
12
12
  // Networking
13
13
  // =============================================================================
14
14
  export const SOCKS_PROXY_PORT = 1080;
15
- export const FLY_PRIVATE_SUBNET = "fdaa::/16";
16
15
  // =============================================================================
17
16
  // Tailscale
18
17
  // =============================================================================
@@ -1,4 +1,5 @@
1
1
  import type { FlyProvider } from "../providers/fly.js";
2
+ import type { FlyAppStatus, FlyMachineState } from "../schemas/fly.js";
2
3
  import type { TailscaleProvider } from "../providers/tailscale.js";
3
4
  /** A router app discovered from the Fly REST API. */
4
5
  export interface RouterApp {
@@ -6,11 +7,12 @@ export interface RouterApp {
6
7
  network: string;
7
8
  org: string;
8
9
  routerId: string;
10
+ status: FlyAppStatus;
9
11
  }
10
12
  /** Machine state for a router, from the Fly Machines API. */
11
13
  export interface RouterMachineInfo {
12
14
  region: string;
13
- state: string;
15
+ state: FlyMachineState;
14
16
  privateIp?: string;
15
17
  subnet?: string;
16
18
  }
@@ -30,6 +32,7 @@ export interface WorkloadApp {
30
32
  appName: string;
31
33
  network: string;
32
34
  org: string;
35
+ status: FlyAppStatus;
33
36
  }
34
37
  /** List all non-router apps on a specific custom network in an org. */
35
38
  export declare const listWorkloadAppsOnNetwork: (fly: FlyProvider, org: string, network: string) => Promise<WorkloadApp[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/util/discovery.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAQnE,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD,wDAAwD;AACxD,eAAO,MAAM,cAAc,GACzB,KAAK,WAAW,EAChB,KAAK,MAAM,KACV,OAAO,CAAC,SAAS,EAAE,CAerB,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,aAAa,GACxB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,KACd,OAAO,CAAC,SAAS,GAAG,IAAI,CAG1B,CAAC;AAMF,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GACpC,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,KACd,OAAO,CAAC,WAAW,EAAE,CAcvB,CAAC;AAEF;;+EAE+E;AAC/E,eAAO,MAAM,eAAe,GAC1B,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,EACf,UAAU,MAAM,KACf,OAAO,CAAC,WAAW,GAAG,IAAI,CAgC5B,CAAC;AAMF,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,GAC/B,KAAK,WAAW,EAChB,SAAS,MAAM,KACd,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAclC,CAAC;AAMF,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,GACjC,WAAW,iBAAiB,EAC5B,SAAS,MAAM,KACd,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAcpC,CAAC;AAMF,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG;IACvC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK;IAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,EAC7D,KAAK,WAAW,EAChB,WAAW,iBAAiB,EAC5B,KAAK,MAAM,KACV,OAAO,CAAC,cAAc,EAAE,CAa1B,CAAC"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/util/discovery.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAQnE,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAMD,wDAAwD;AACxD,eAAO,MAAM,cAAc,GACzB,KAAK,WAAW,EAChB,KAAK,MAAM,KACV,OAAO,CAAC,SAAS,EAAE,CAgBrB,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,aAAa,GACxB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,KACd,OAAO,CAAC,SAAS,GAAG,IAAI,CAG1B,CAAC;AAMF,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GACpC,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,KACd,OAAO,CAAC,WAAW,EAAE,CAevB,CAAC;AAEF;;+EAE+E;AAC/E,eAAO,MAAM,eAAe,GAC1B,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,SAAS,MAAM,EACf,UAAU,MAAM,KACf,OAAO,CAAC,WAAW,GAAG,IAAI,CAiC5B,CAAC;AAMF,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,GAC/B,KAAK,WAAW,EAChB,SAAS,MAAM,KACd,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAclC,CAAC;AAMF,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,GACjC,WAAW,iBAAiB,EAC5B,SAAS,MAAM,KACd,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAcpC,CAAC;AAMF,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG;IACvC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK;IAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAAE,EAC7D,KAAK,WAAW,EAChB,WAAW,iBAAiB,EAC5B,KAAK,MAAM,KACV,OAAO,CAAC,cAAc,EAAE,CAa1B,CAAC"}
@@ -29,6 +29,7 @@ export const listRouterApps = async (fly, org) => {
29
29
  network: app.network,
30
30
  org: app.organization?.slug ?? org,
31
31
  routerId: getRouterSuffix(app.name, app.network),
32
+ status: app.status,
32
33
  }));
33
34
  };
34
35
  /** Find the router app for a specific network. */
@@ -46,6 +47,7 @@ export const listWorkloadAppsOnNetwork = async (fly, org, network) => {
46
47
  appName: app.name,
47
48
  network: app.network,
48
49
  org: app.organization?.slug ?? org,
50
+ status: app.status,
49
51
  }));
50
52
  };
51
53
  /** Find a specific workload app by logical name, optionally scoped to a network.
@@ -60,6 +62,7 @@ export const findWorkloadApp = async (fly, org, appName, network) => {
60
62
  appName: app.name,
61
63
  network: app.network,
62
64
  org: app.organization?.slug ?? org,
65
+ status: app.status,
63
66
  }));
64
67
  if (network) {
65
68
  // Resolve the router's suffix so we can match the suffixed Fly app name
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardelli/ambit",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Deploy apps to the cloud that only you and your AI agents can reach",
5
5
  "license": "MIT",
6
6
  "scripts": {},
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=list.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=status.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":""}
@@ -1,334 +0,0 @@
1
- // =============================================================================
2
- // Status Command - Show Network, App, and Router Status
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 { registerCommand } from "../mod.js";
10
- import { discoverRouters, findRouterApp, findWorkloadApp, getRouterMachineInfo, getRouterTailscaleInfo, listWorkloadAppsOnNetwork, } from "../../util/discovery.js";
11
- import { initSession } from "../../util/session.js";
12
- import { SOCKS_PROXY_PORT } from "../../util/constants.js";
13
- // =============================================================================
14
- // Network Status: Single Router Detailed View
15
- // =============================================================================
16
- const stageNetworkStatus = async (fly, tailscale, network, org, json) => {
17
- const out = createOutput(json);
18
- const app = await findRouterApp(fly, org, network);
19
- if (!app) {
20
- return out.die(`No Router Found for Network '${network}'`);
21
- }
22
- const machine = await getRouterMachineInfo(fly, app.appName);
23
- const ts = await getRouterTailscaleInfo(tailscale, app.appName);
24
- const tag = ts?.tags?.[0] ?? null;
25
- out.blank()
26
- .header("Ambit Status: Network")
27
- .blank()
28
- .text(` Network: ${bold(app.network)}`)
29
- .text(` TLD: *.${app.network}`)
30
- .text(` Tag: ${tag ?? "unknown"}`)
31
- .blank()
32
- .text(` Router App: ${app.appName}`)
33
- .text(` Region: ${machine?.region ?? "unknown"}`)
34
- .text(` Machine State: ${machine?.state ?? "unknown"}`)
35
- .text(` Private IP: ${machine?.privateIp ?? "unknown"}`)
36
- .text(` SOCKS Proxy: ${machine?.privateIp
37
- ? `socks5://[${machine.privateIp}]:${SOCKS_PROXY_PORT}`
38
- : "unknown"}`);
39
- if (machine?.subnet) {
40
- out.text(` Subnet: ${machine.subnet}`);
41
- }
42
- out.blank();
43
- if (ts) {
44
- out.text(` Tailscale IP: ${ts.ip}`)
45
- .text(` Online: ${ts.online ? "yes" : "no"}`);
46
- }
47
- else {
48
- out.text(" Tailscale: Not Found in Tailnet");
49
- }
50
- // Show workload apps on this network
51
- const workloads = await listWorkloadAppsOnNetwork(fly, org, network);
52
- if (workloads.length > 0) {
53
- out.blank().header(" Apps on Network").blank();
54
- for (const w of workloads) {
55
- const machines = await fly.machines.list(w.appName);
56
- const state = machines[0]?.state ?? "no machines";
57
- const region = machines[0]?.region ?? "-";
58
- out.text(` ${w.appName} ${region} ${state}`);
59
- }
60
- }
61
- out.blank();
62
- out.done({
63
- network: app.network,
64
- router: app,
65
- machine,
66
- tag,
67
- tailscale: ts,
68
- });
69
- out.print();
70
- };
71
- // =============================================================================
72
- // Network Status: Summary Table of All Routers
73
- // =============================================================================
74
- const stageAllStatus = async (fly, tailscale, org, json) => {
75
- const out = createOutput(json);
76
- const routers = await discoverRouters(out, fly, tailscale, org);
77
- if (routers.length === 0) {
78
- out.blank()
79
- .text("No Routers Found.")
80
- .dim(" Create One with: ambit create <network>")
81
- .blank();
82
- out.done({ routers: [] });
83
- out.print();
84
- return;
85
- }
86
- out.blank().header("Router Status").blank();
87
- const rows = routers.map((r) => {
88
- const tsStatus = r.tailscale
89
- ? (r.tailscale.online ? "online" : "offline")
90
- : "not found";
91
- return [r.network, r.appName, r.machine?.state ?? "unknown", tsStatus];
92
- });
93
- const table = new Table()
94
- .header(["Network", "App", "State", "Tailscale"])
95
- .body(rows)
96
- .indent(2)
97
- .padding(2);
98
- out.text(table.toString());
99
- out.blank();
100
- out.done({ routers });
101
- out.print();
102
- };
103
- // =============================================================================
104
- // Status Network Subcommand
105
- // =============================================================================
106
- const statusNetwork = async (argv) => {
107
- const opts = { string: ["org"], boolean: ["help", "json"] };
108
- const args = parseArgs(argv, opts);
109
- checkArgs(args, opts, "ambit status network");
110
- if (args.help) {
111
- console.log(`
112
- ${bold("ambit status network")} - Show Router and Network Status
113
-
114
- ${bold("USAGE")}
115
- ambit status network [<name>] [--org <org>] [--json]
116
-
117
- ${bold("OPTIONS")}
118
- <name> Network to show status for (shows all if omitted)
119
- --org <org> Fly.io organization slug
120
- --json Output as JSON
121
-
122
- ${bold("EXAMPLES")}
123
- ambit status network Show summary of all routers
124
- ambit status network browsers Show detailed status for one network
125
- `);
126
- return;
127
- }
128
- const network = typeof args._[0] === "string" ? args._[0] : undefined;
129
- const prereqOut = createOutput(args.json);
130
- const { fly, tailscale, org } = await initSession(prereqOut, {
131
- json: args.json,
132
- org: args.org,
133
- });
134
- if (network) {
135
- await stageNetworkStatus(fly, tailscale, network, org, args.json);
136
- }
137
- else {
138
- await stageAllStatus(fly, tailscale, org, args.json);
139
- }
140
- };
141
- // =============================================================================
142
- // App Status: Detailed App View
143
- // =============================================================================
144
- const stageAppStatus = async (fly, tailscale, app, network, org, json) => {
145
- const out = createOutput(json);
146
- const workload = await findWorkloadApp(fly, org, app, network);
147
- if (!workload) {
148
- return out.die(`App '${app}' Not Found on Network '${network}'`);
149
- }
150
- const machines = await fly.machines.list(workload.appName);
151
- const ips = await fly.ips.list(workload.appName);
152
- const mappedMachines = machines.map((m) => ({
153
- id: m.id,
154
- region: m.region,
155
- state: m.state,
156
- privateIp: m.private_ip,
157
- }));
158
- const mappedIps = ips.map((ip) => ({
159
- address: ip.Address,
160
- type: ip.Type,
161
- network: ip.Network?.Name,
162
- }));
163
- const router = await findRouterApp(fly, org, network);
164
- out.blank()
165
- .header(`Ambit Status: ${app}.${network}`)
166
- .blank()
167
- .text(` App: ${bold(app)}`)
168
- .text(` Network: ${network}`)
169
- .text(` Fly App: ${workload.appName}`)
170
- .blank();
171
- if (machines.length === 0) {
172
- out.text(" Machines: None");
173
- }
174
- else {
175
- out.header(" Machines").blank();
176
- for (const m of machines) {
177
- out.text(` ${m.id} ${m.region} ${m.state}${m.private_ip ? ` ${m.private_ip}` : ""}`);
178
- }
179
- }
180
- out.blank();
181
- const flycastIps = ips.filter((ip) => ip.Type === "private_v6");
182
- const publicIps = ips.filter((ip) => ip.Type !== "private_v6");
183
- if (flycastIps.length > 0) {
184
- out.header(" Flycast IPs").blank();
185
- for (const ip of flycastIps) {
186
- out.text(` ${ip.Address} (network: ${ip.Network?.Name ?? "default"})`);
187
- }
188
- out.blank();
189
- }
190
- if (publicIps.length > 0) {
191
- out.header(" Public IPs (Warning: Ambit Apps Should Not Have Public IPs)")
192
- .blank();
193
- for (const ip of publicIps) {
194
- out.text(` ${ip.Address} ${ip.Type}`);
195
- }
196
- out.blank();
197
- }
198
- if (router) {
199
- const routerMachine = await getRouterMachineInfo(fly, router.appName);
200
- const ts = await getRouterTailscaleInfo(tailscale, router.appName);
201
- out.header(" Router").blank()
202
- .text(` App: ${router.appName}`)
203
- .text(` State: ${routerMachine?.state ?? "unknown"}`)
204
- .text(` Tailscale: ${ts ? (ts.online ? "online" : "offline") : "not found"}`);
205
- if (routerMachine?.privateIp) {
206
- out.text(` SOCKS Proxy: socks5://[${routerMachine.privateIp}]:${SOCKS_PROXY_PORT}`);
207
- }
208
- }
209
- else {
210
- out.text(" Router: Not Found");
211
- }
212
- out.blank();
213
- out.done({
214
- app,
215
- network,
216
- flyAppName: workload.appName,
217
- machines: mappedMachines,
218
- ips: mappedIps,
219
- router,
220
- });
221
- out.print();
222
- };
223
- // =============================================================================
224
- // Status App Subcommand
225
- // =============================================================================
226
- const statusApp = async (argv) => {
227
- const opts = {
228
- string: ["network", "org"],
229
- boolean: ["help", "json"],
230
- };
231
- const args = parseArgs(argv, opts);
232
- checkArgs(args, opts, "ambit status app");
233
- if (args.help) {
234
- console.log(`
235
- ${bold("ambit status app")} - Show App Status
236
-
237
- ${bold("USAGE")}
238
- ambit status app <app>.<network> [--org <org>] [--json]
239
- ambit status app <app> --network <name> [--org <org>] [--json]
240
-
241
- ${bold("OPTIONS")}
242
- --network <name> Target network (if not using dot syntax)
243
- --org <org> Fly.io organization slug
244
- --json Output as JSON
245
-
246
- ${bold("EXAMPLES")}
247
- ambit status app my-app.browsers
248
- ambit status app my-app --network browsers --json
249
- `);
250
- return;
251
- }
252
- const out = createOutput(args.json);
253
- const appArg = args._[0];
254
- if (!appArg || typeof appArg !== "string") {
255
- return out.die("Missing App Name. Usage: ambit status app <app>.<network>");
256
- }
257
- let app;
258
- let network;
259
- if (appArg.includes(".")) {
260
- const parts = appArg.split(".");
261
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
262
- return out.die(`'${appArg}' Should Have Exactly One Dot, Like my-app.my-network`);
263
- }
264
- app = parts[0];
265
- network = parts[1];
266
- }
267
- else {
268
- app = appArg;
269
- if (!args.network) {
270
- return out.die(`Missing Network. Use: ambit status app ${app}.<network>`);
271
- }
272
- network = args.network;
273
- }
274
- const prereqOut = createOutput(args.json);
275
- const { fly, tailscale, org } = await initSession(prereqOut, {
276
- json: args.json,
277
- org: args.org,
278
- });
279
- await stageAppStatus(fly, tailscale, app, network, org, args.json);
280
- };
281
- // =============================================================================
282
- // Top-Level Help
283
- // =============================================================================
284
- const showStatusHelp = () => {
285
- console.log(`
286
- ${bold("ambit status")} - Show Network, App, and Router Status
287
-
288
- ${bold("USAGE")}
289
- ambit status [options]
290
- ambit status network [<name>] [options]
291
- ambit status app <app>.<network> [options]
292
-
293
- ${bold("SUBCOMMANDS")}
294
- network Show router/network status — default when no subcommand given
295
- app Show a specific app's status
296
-
297
- ${bold("OPTIONS")}
298
- --org <org> Fly.io organization slug
299
- --json Output as JSON
300
-
301
- ${bold("EXAMPLES")}
302
- ambit status
303
- ambit status network
304
- ambit status network browsers
305
- ambit status app my-app.browsers
306
-
307
- Run 'ambit status network --help' or 'ambit status app --help' for details.
308
- `);
309
- };
310
- // =============================================================================
311
- // Dispatcher
312
- // =============================================================================
313
- const status = async (argv) => {
314
- const subcommand = typeof argv[0] === "string" ? argv[0] : undefined;
315
- if (subcommand === "network")
316
- return statusNetwork(argv.slice(1));
317
- if (subcommand === "app")
318
- return statusApp(argv.slice(1));
319
- const args = parseArgs(argv, { boolean: ["help"] });
320
- if (args.help) {
321
- showStatusHelp();
322
- return;
323
- }
324
- return statusNetwork(argv);
325
- };
326
- // =============================================================================
327
- // Register Command
328
- // =============================================================================
329
- registerCommand({
330
- name: "status",
331
- description: "Show network, app, and router status",
332
- usage: "ambit status [network|app] [<name>] [--org <org>] [--json]",
333
- run: status,
334
- });