@cardelli/ambit 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/esm/cli/commands/create/index.d.ts +2 -0
  2. package/esm/cli/commands/create/index.d.ts.map +1 -0
  3. package/esm/cli/commands/create/index.js +292 -0
  4. package/esm/cli/commands/create/machine.d.ts +33 -0
  5. package/esm/cli/commands/create/machine.d.ts.map +1 -0
  6. package/esm/cli/commands/create/machine.js +162 -0
  7. package/esm/cli/commands/deploy/index.d.ts +2 -0
  8. package/esm/cli/commands/deploy/index.d.ts.map +1 -0
  9. package/esm/cli/commands/deploy/index.js +290 -0
  10. package/esm/cli/commands/deploy/machine.d.ts +52 -0
  11. package/esm/cli/commands/deploy/machine.d.ts.map +1 -0
  12. package/esm/cli/commands/deploy/machine.js +116 -0
  13. package/esm/cli/commands/deploy/modes.d.ts +18 -0
  14. package/esm/cli/commands/deploy/modes.d.ts.map +1 -0
  15. package/esm/cli/commands/deploy/modes.js +152 -0
  16. package/esm/cli/commands/destroy/app.d.ts +2 -0
  17. package/esm/cli/commands/destroy/app.d.ts.map +1 -0
  18. package/esm/cli/commands/destroy/app.js +173 -0
  19. package/esm/cli/commands/destroy/index.d.ts +2 -0
  20. package/esm/cli/commands/destroy/index.d.ts.map +1 -0
  21. package/esm/cli/commands/destroy/index.js +63 -0
  22. package/esm/cli/commands/destroy/network.d.ts +2 -0
  23. package/esm/cli/commands/destroy/network.d.ts.map +1 -0
  24. package/esm/cli/commands/destroy/network.js +210 -0
  25. package/esm/cli/commands/doctor.d.ts.map +1 -0
  26. package/esm/cli/commands/doctor.js +295 -0
  27. package/esm/{src/cli → cli}/commands/list.d.ts.map +1 -1
  28. package/esm/{src/cli → cli}/commands/list.js +39 -54
  29. package/esm/cli/commands/status.d.ts.map +1 -0
  30. package/esm/cli/commands/status.js +331 -0
  31. package/esm/cli/mod.d.ts.map +1 -0
  32. package/esm/{src/cli → cli}/mod.js +4 -4
  33. package/esm/deno.d.ts +4 -18
  34. package/esm/deno.js +5 -19
  35. package/esm/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
  36. package/esm/lib/args.d.ts +11 -0
  37. package/esm/lib/args.d.ts.map +1 -0
  38. package/esm/lib/args.js +28 -0
  39. package/esm/lib/cli.d.ts +0 -1
  40. package/esm/lib/cli.d.ts.map +1 -1
  41. package/esm/lib/cli.js +0 -1
  42. package/esm/lib/command.d.ts +0 -3
  43. package/esm/lib/command.d.ts.map +1 -1
  44. package/esm/lib/command.js +2 -13
  45. package/esm/lib/machine.d.ts +11 -0
  46. package/esm/lib/machine.d.ts.map +1 -0
  47. package/esm/lib/machine.js +15 -0
  48. package/esm/lib/output.d.ts +2 -1
  49. package/esm/lib/output.d.ts.map +1 -1
  50. package/esm/lib/output.js +21 -3
  51. package/esm/lib/result.d.ts +0 -1
  52. package/esm/lib/result.d.ts.map +1 -1
  53. package/esm/lib/result.js +0 -1
  54. package/esm/main.d.ts +6 -6
  55. package/esm/main.d.ts.map +1 -1
  56. package/esm/main.js +7 -9
  57. package/esm/providers/fly.d.ts +81 -0
  58. package/esm/providers/fly.d.ts.map +1 -0
  59. package/esm/providers/fly.js +372 -0
  60. package/esm/providers/tailscale.d.ts +31 -0
  61. package/esm/providers/tailscale.d.ts.map +1 -0
  62. package/esm/providers/tailscale.js +150 -0
  63. package/esm/{src/schemas → schemas}/fly.d.ts +1 -11
  64. package/esm/schemas/fly.d.ts.map +1 -0
  65. package/esm/{src/schemas → schemas}/fly.js +14 -56
  66. package/esm/{src/schemas → schemas}/tailscale.d.ts +1 -2
  67. package/esm/schemas/tailscale.d.ts.map +1 -0
  68. package/esm/{src/schemas → schemas}/tailscale.js +2 -3
  69. package/esm/src/{docker/router → router}/Dockerfile +0 -11
  70. package/esm/src/{docker/router → router}/start.sh +18 -9
  71. package/esm/util/constants.d.ts +13 -0
  72. package/esm/util/constants.d.ts.map +1 -0
  73. package/esm/util/constants.js +34 -0
  74. package/esm/{src → util}/credentials.d.ts +0 -1
  75. package/esm/util/credentials.d.ts.map +1 -0
  76. package/esm/{src → util}/credentials.js +3 -5
  77. package/esm/{src → util}/discovery.d.ts +16 -3
  78. package/esm/util/discovery.d.ts.map +1 -0
  79. package/esm/{src → util}/discovery.js +24 -15
  80. package/esm/util/fly-transforms.d.ts +27 -0
  81. package/esm/util/fly-transforms.d.ts.map +1 -0
  82. package/esm/util/fly-transforms.js +87 -0
  83. package/esm/{src → util}/guard.d.ts +1 -2
  84. package/esm/util/guard.d.ts.map +1 -0
  85. package/esm/{src → util}/guard.js +27 -27
  86. package/esm/util/naming.d.ts +5 -0
  87. package/esm/util/naming.d.ts.map +1 -0
  88. package/esm/util/naming.js +12 -0
  89. package/esm/{src → util}/resolve.d.ts +2 -3
  90. package/esm/util/resolve.d.ts.map +1 -0
  91. package/esm/{src → util}/resolve.js +1 -2
  92. package/esm/util/session.d.ts +16 -0
  93. package/esm/util/session.d.ts.map +1 -0
  94. package/esm/util/session.js +19 -0
  95. package/esm/util/tailscale-local.d.ts +13 -0
  96. package/esm/util/tailscale-local.d.ts.map +1 -0
  97. package/esm/util/tailscale-local.js +63 -0
  98. package/esm/{src → util}/template.d.ts +0 -1
  99. package/esm/util/template.d.ts.map +1 -0
  100. package/esm/{src → util}/template.js +0 -1
  101. package/package.json +1 -49
  102. package/esm/lib/paths.d.ts +0 -3
  103. package/esm/lib/paths.d.ts.map +0 -1
  104. package/esm/lib/paths.js +0 -5
  105. package/esm/src/cli/commands/create.d.ts +0 -2
  106. package/esm/src/cli/commands/create.d.ts.map +0 -1
  107. package/esm/src/cli/commands/create.js +0 -308
  108. package/esm/src/cli/commands/deploy.d.ts +0 -2
  109. package/esm/src/cli/commands/deploy.d.ts.map +0 -1
  110. package/esm/src/cli/commands/deploy.js +0 -430
  111. package/esm/src/cli/commands/destroy.d.ts +0 -2
  112. package/esm/src/cli/commands/destroy.d.ts.map +0 -1
  113. package/esm/src/cli/commands/destroy.js +0 -340
  114. package/esm/src/cli/commands/doctor.d.ts.map +0 -1
  115. package/esm/src/cli/commands/doctor.js +0 -141
  116. package/esm/src/cli/commands/status.d.ts.map +0 -1
  117. package/esm/src/cli/commands/status.js +0 -152
  118. package/esm/src/cli/mod.d.ts.map +0 -1
  119. package/esm/src/credentials.d.ts.map +0 -1
  120. package/esm/src/discovery.d.ts.map +0 -1
  121. package/esm/src/guard.d.ts.map +0 -1
  122. package/esm/src/providers/fly.d.ts +0 -76
  123. package/esm/src/providers/fly.d.ts.map +0 -1
  124. package/esm/src/providers/fly.js +0 -407
  125. package/esm/src/providers/tailscale.d.ts +0 -31
  126. package/esm/src/providers/tailscale.d.ts.map +0 -1
  127. package/esm/src/providers/tailscale.js +0 -189
  128. package/esm/src/resolve.d.ts.map +0 -1
  129. package/esm/src/schemas/config.d.ts +0 -5
  130. package/esm/src/schemas/config.d.ts.map +0 -1
  131. package/esm/src/schemas/config.js +0 -22
  132. package/esm/src/schemas/fly.d.ts.map +0 -1
  133. package/esm/src/schemas/tailscale.d.ts.map +0 -1
  134. package/esm/src/template.d.ts.map +0 -1
  135. /package/esm/{src/cli → cli}/commands/doctor.d.ts +0 -0
  136. /package/esm/{src/cli → cli}/commands/list.d.ts +0 -0
  137. /package/esm/{src/cli → cli}/commands/status.d.ts +0 -0
  138. /package/esm/{src/cli → cli}/mod.d.ts +0 -0
  139. /package/esm/src/{docker/router → router}/fly.toml +0 -0
package/esm/lib/output.js CHANGED
@@ -1,8 +1,6 @@
1
1
  // =============================================================================
2
2
  // Output - Unified Output Handling for CLI Commands
3
3
  // =============================================================================
4
- import "../_dnt.polyfills.js";
5
- import * as dntShim from "../_dnt.shims.js";
6
4
  import { bold, dim, Spinner, statusErr, statusInfo, statusOk, statusWarn, } from "./cli.js";
7
5
  // =============================================================================
8
6
  // Output Class
@@ -34,6 +32,11 @@ export class Output {
34
32
  // ===========================================================================
35
33
  // Human-Mode Output (no-op in JSON mode)
36
34
  // ===========================================================================
35
+ skip(text) {
36
+ if (!this.jsonMode)
37
+ console.log(dim(`~ ${text}`));
38
+ return this;
39
+ }
37
40
  ok(text) {
38
41
  if (!this.jsonMode)
39
42
  statusOk(text);
@@ -87,6 +90,21 @@ export class Output {
87
90
  };
88
91
  }
89
92
  // ===========================================================================
93
+ // Async Spinner Wrapper
94
+ // ===========================================================================
95
+ async spin(label, fn) {
96
+ const s = this.spinner(label);
97
+ try {
98
+ const result = await fn();
99
+ s.success(label);
100
+ return result;
101
+ }
102
+ catch (e) {
103
+ s.fail(label);
104
+ throw e;
105
+ }
106
+ }
107
+ // ===========================================================================
90
108
  // Terminal Output
91
109
  // ===========================================================================
92
110
  die(message) {
@@ -96,7 +114,7 @@ export class Output {
96
114
  else {
97
115
  statusErr(message);
98
116
  }
99
- dntShim.Deno.exit(1);
117
+ throw new Error("exit");
100
118
  }
101
119
  isJson() {
102
120
  return this.jsonMode;
@@ -1,4 +1,3 @@
1
- import "../_dnt.polyfills.js";
2
1
  export declare class Result<T> {
3
2
  readonly ok: boolean;
4
3
  private readonly _value;
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AAGA,OAAO,sBAAsB,CAAC;AAG9B,qBAAa,MAAM,CAAC,CAAC;IAEjB,QAAQ,CAAC,EAAE,EAAE,OAAO;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHzB,OAAO;IAMP,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAIjC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAI/C,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAEzB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAKtC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAKlD,MAAM,IAAI,CAAC;IAKX,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IAIxB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE;QAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;QAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAA;KAAE,GAAG,CAAC;CAGvE"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AAIA,qBAAa,MAAM,CAAC,CAAC;IAEjB,QAAQ,CAAC,EAAE,EAAE,OAAO;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHzB,OAAO;IAMP,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAIjC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAI/C,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAEzB;IAED,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAKtC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAKlD,MAAM,IAAI,CAAC;IAKX,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IAIxB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE;QAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;QAAC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAA;KAAE,GAAG,CAAC;CAGvE"}
package/esm/lib/result.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // =============================================================================
2
2
  // Result<T> — Shared Success/Failure Type
3
3
  // =============================================================================
4
- import "../_dnt.polyfills.js";
5
4
  export class Result {
6
5
  ok;
7
6
  _value;
package/esm/main.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import "./_dnt.polyfills.js";
3
- import "./src/cli/commands/create.js";
4
- import "./src/cli/commands/deploy.js";
5
- import "./src/cli/commands/list.js";
6
- import "./src/cli/commands/status.js";
7
- import "./src/cli/commands/destroy.js";
8
- import "./src/cli/commands/doctor.js";
3
+ import "./cli/commands/create/index.js";
4
+ import "./cli/commands/deploy/index.js";
5
+ import "./cli/commands/list.js";
6
+ import "./cli/commands/status.js";
7
+ import "./cli/commands/destroy/index.js";
8
+ import "./cli/commands/doctor.js";
9
9
  //# sourceMappingURL=main.d.ts.map
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;AA+B7B,OAAO,8BAA8B,CAAC;AACtC,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,8BAA8B,CAAC;AACtC,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,qBAAqB,CAAC;AA8B7B,OAAO,gCAAgC,CAAC;AACxC,OAAO,gCAAgC,CAAC;AACxC,OAAO,wBAAwB,CAAC;AAChC,OAAO,0BAA0B,CAAC;AAClC,OAAO,iCAAiC,CAAC;AACzC,OAAO,0BAA0B,CAAC"}
package/esm/main.js CHANGED
@@ -24,21 +24,19 @@ import * as dntShim from "./_dnt.shims.js";
24
24
  // ambit doctor
25
25
  //
26
26
  // =============================================================================
27
- import { runCli } from "./src/cli/mod.js";
27
+ import { runCli } from "./cli/mod.js";
28
28
  import { Spinner, statusErr } from "./lib/cli.js";
29
- // Import commands to register them
30
- import "./src/cli/commands/create.js";
31
- import "./src/cli/commands/deploy.js";
32
- import "./src/cli/commands/list.js";
33
- import "./src/cli/commands/status.js";
34
- import "./src/cli/commands/destroy.js";
35
- import "./src/cli/commands/doctor.js";
29
+ import "./cli/commands/create/index.js";
30
+ import "./cli/commands/deploy/index.js";
31
+ import "./cli/commands/list.js";
32
+ import "./cli/commands/status.js";
33
+ import "./cli/commands/destroy/index.js";
34
+ import "./cli/commands/doctor.js";
36
35
  // =============================================================================
37
36
  // Main
38
37
  // =============================================================================
39
38
  const main = async () => {
40
39
  const spinner = new Spinner();
41
- // Handle signals
42
40
  try {
43
41
  dntShim.Deno.addSignalListener("SIGINT", () => {
44
42
  spinner.stop();
@@ -0,0 +1,81 @@
1
+ import { type FlyApp, type FlyAppInfo, type FlyIp, type FlyMachine } from "../schemas/fly.js";
2
+ /**
3
+ * Thrown when a `fly deploy` command fails. Carries the raw stderr so callers
4
+ * can surface it through `out` (respecting JSON mode) instead of printing
5
+ * directly.
6
+ */
7
+ export declare class FlyDeployError extends Error {
8
+ /** Last meaningful line from flyctl stderr. */
9
+ readonly detail: string;
10
+ constructor(app: string, stderr: string);
11
+ }
12
+ export type MachineSize = "shared-cpu-1x" | "shared-cpu-2x" | "shared-cpu-4x";
13
+ export interface MachineConfig {
14
+ size: MachineSize;
15
+ memoryMb?: number;
16
+ region?: string;
17
+ autoStopSeconds?: number;
18
+ }
19
+ export interface MachineResult {
20
+ id: string;
21
+ state: string;
22
+ size: string;
23
+ region: string;
24
+ privateIp?: string;
25
+ }
26
+ export interface SafeDeployOptions {
27
+ image?: string;
28
+ config?: string;
29
+ region?: string;
30
+ routerId?: string;
31
+ }
32
+ export interface FlyProvider {
33
+ auth: {
34
+ ensureInstalled(): Promise<void>;
35
+ login(opts?: {
36
+ interactive?: boolean;
37
+ }): Promise<string>;
38
+ getToken(): Promise<string>;
39
+ };
40
+ orgs: {
41
+ list(): Promise<Record<string, string>>;
42
+ };
43
+ apps: {
44
+ list(org?: string): Promise<FlyApp[]>;
45
+ listWithNetwork(org: string): Promise<FlyAppInfo[]>;
46
+ create(name: string, org: string, opts?: {
47
+ network?: string;
48
+ routerId?: string;
49
+ }): Promise<void>;
50
+ delete(name: string): Promise<void>;
51
+ exists(name: string): Promise<boolean>;
52
+ getConfig(name: string): Promise<Record<string, unknown> | null>;
53
+ };
54
+ machines: {
55
+ list(app: string): Promise<FlyMachine[]>;
56
+ clone(app: string, config: MachineConfig): Promise<MachineResult>;
57
+ destroy(app: string, machineId: string): Promise<void>;
58
+ };
59
+ secrets: {
60
+ set(app: string, secrets: Record<string, string>, opts?: {
61
+ stage?: boolean;
62
+ }): Promise<void>;
63
+ };
64
+ ips: {
65
+ list(app: string): Promise<FlyIp[]>;
66
+ release(app: string, address: string): Promise<void>;
67
+ allocateFlycast(app: string, network: string): Promise<void>;
68
+ };
69
+ certs: {
70
+ list(app: string): Promise<string[]>;
71
+ remove(app: string, hostname: string): Promise<void>;
72
+ };
73
+ deploy: {
74
+ router(app: string, dir: string, config?: {
75
+ region?: string;
76
+ }): Promise<void>;
77
+ app(app: string, options: SafeDeployOptions): Promise<void>;
78
+ };
79
+ }
80
+ export declare const createFlyProvider: () => FlyProvider;
81
+ //# sourceMappingURL=fly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fly.d.ts","sourceRoot":"","sources":["../../src/providers/fly.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,372 @@
1
+ // =============================================================================
2
+ // Fly.io Provider - Wraps flyctl CLI
3
+ // =============================================================================
4
+ import * as dntShim from "../_dnt.shims.js";
5
+ import { runCommand, runJson } from "../lib/command.js";
6
+ import { Result } from "../lib/result.js";
7
+ import { commandExists, die } from "../lib/cli.js";
8
+ import { dirname, resolve } from "../deps/jsr.io/@std/path/1.1.4/mod.js";
9
+ import { FlyAppInfoListSchema, FlyAppsListSchema, FlyAuthSchema, FlyIpListSchema, FlyMachinesListSchema, FlyOrgsSchema, FlyStatusSchema, } from "../schemas/fly.js";
10
+ import { fileExists } from "../lib/cli.js";
11
+ import { getWorkloadAppName } from "../util/naming.js";
12
+ import { extractErrorDetail, getSizeConfig, mapMachines } from "../util/fly-transforms.js";
13
+ // =============================================================================
14
+ // Deploy Error
15
+ // =============================================================================
16
+ /**
17
+ * Thrown when a `fly deploy` command fails. Carries the raw stderr so callers
18
+ * can surface it through `out` (respecting JSON mode) instead of printing
19
+ * directly.
20
+ */
21
+ export class FlyDeployError extends Error {
22
+ /** Last meaningful line from flyctl stderr. */
23
+ detail;
24
+ constructor(app, stderr) {
25
+ const detail = extractErrorDetail(stderr);
26
+ super(`Deploy Failed for '${app}'`);
27
+ this.name = "FlyDeployError";
28
+ this.detail = detail;
29
+ }
30
+ }
31
+ // =============================================================================
32
+ // Create Fly Provider
33
+ // =============================================================================
34
+ export const createFlyProvider = () => {
35
+ const provider = {
36
+ auth: {
37
+ async ensureInstalled() {
38
+ if (!(await commandExists("fly"))) {
39
+ return die("Flyctl Not Found. Install from https://fly.io/docs/flyctl/install/");
40
+ }
41
+ },
42
+ async login(opts) {
43
+ const interactive = opts?.interactive ?? true;
44
+ const result = await runCommand(["fly", "auth", "whoami", "--json"]);
45
+ if (result.ok) {
46
+ const auth = result.json();
47
+ if (auth.ok) {
48
+ const parsed = FlyAuthSchema.safeParse(auth.value);
49
+ if (parsed.success) {
50
+ return parsed.data.email;
51
+ }
52
+ }
53
+ }
54
+ if (!interactive) {
55
+ return die("Not Authenticated with Fly.io. Run 'fly auth login' First");
56
+ }
57
+ const loginResult = await runCommand(["fly", "auth", "login"], {
58
+ interactive: true,
59
+ });
60
+ if (!loginResult.ok) {
61
+ return die("Fly.io Authentication Failed");
62
+ }
63
+ const checkResult = await runCommand(["fly", "auth", "whoami", "--json"]);
64
+ if (!checkResult.ok) {
65
+ return die("Fly.io Authentication Verification Failed");
66
+ }
67
+ const parsed = FlyAuthSchema.safeParse(checkResult.json().unwrap());
68
+ if (!parsed.success || !parsed.data) {
69
+ return die("Fly.io Authentication Response Invalid");
70
+ }
71
+ return parsed.data.email;
72
+ },
73
+ async getToken() {
74
+ const home = dntShim.Deno.env.get("HOME") || dntShim.Deno.env.get("USERPROFILE") || "";
75
+ const configPath = `${home}/.fly/config.yml`;
76
+ if (!(await fileExists(configPath))) {
77
+ return die("Fly Config Not Found at ~/.fly/config.yml. Run 'fly auth login' First");
78
+ }
79
+ const content = await dntShim.Deno.readTextFile(configPath);
80
+ const match = content.match(/access_token:\s*(.+)/);
81
+ if (!match || !match[1]) {
82
+ return die("No Access Token Found in ~/.fly/config.yml. Run 'fly auth login' First");
83
+ }
84
+ return match[1].trim();
85
+ },
86
+ },
87
+ orgs: {
88
+ async list() {
89
+ const result = await runJson(["fly", "orgs", "list", "--json"]);
90
+ if (!result.ok) {
91
+ return die("Failed to List Organizations");
92
+ }
93
+ const parsed = FlyOrgsSchema.safeParse(result.value);
94
+ if (!parsed.success) {
95
+ return die("Failed to Parse Organizations");
96
+ }
97
+ return parsed.data;
98
+ },
99
+ },
100
+ apps: {
101
+ async list(org) {
102
+ const args = ["fly", "apps", "list", "--json"];
103
+ if (org) {
104
+ args.push("--org", org);
105
+ }
106
+ const result = await runCommand(args);
107
+ return result.json().flatMap((data) => {
108
+ const parsed = FlyAppsListSchema.safeParse(data);
109
+ return parsed.success
110
+ ? Result.ok(parsed.data)
111
+ : Result.err("Parse Failed");
112
+ }).unwrapOr([]);
113
+ },
114
+ async listWithNetwork(org) {
115
+ const token = await provider.auth.getToken();
116
+ const response = await fetch(`https://api.machines.dev/v1/apps?org_slug=${encodeURIComponent(org)}`, {
117
+ headers: {
118
+ Authorization: `Bearer ${token}`,
119
+ "Content-Type": "application/json",
120
+ },
121
+ });
122
+ if (!response.ok) {
123
+ return die(`Failed to List Apps via REST API: HTTP ${response.status}`);
124
+ }
125
+ const data = await response.json();
126
+ const parsed = FlyAppInfoListSchema.safeParse(data);
127
+ if (!parsed.success) {
128
+ return die("Failed to Parse Apps REST API Response");
129
+ }
130
+ return parsed.data.apps;
131
+ },
132
+ async create(name, org, opts) {
133
+ const appName = opts?.routerId
134
+ ? getWorkloadAppName(name, opts.routerId)
135
+ : name;
136
+ const args = ["fly", "apps", "create", appName, "--org", org, "--json"];
137
+ if (opts?.network) {
138
+ args.push("--network", opts.network);
139
+ }
140
+ const result = await runCommand(args);
141
+ if (!result.ok) {
142
+ return die(`Failed to Create App '${appName}'`);
143
+ }
144
+ },
145
+ async delete(name) {
146
+ const result = await runCommand([
147
+ "fly",
148
+ "apps",
149
+ "destroy",
150
+ name,
151
+ "--yes",
152
+ ]);
153
+ if (!result.ok) {
154
+ return die(`Failed to Delete App '${name}'`);
155
+ }
156
+ },
157
+ async exists(name) {
158
+ const result = await runCommand(["fly", "status", "-a", name, "--json"]);
159
+ return result.json().match({
160
+ ok: (data) => {
161
+ const parsed = FlyStatusSchema.safeParse(data);
162
+ return parsed.success && !!parsed.data.ID;
163
+ },
164
+ err: () => false,
165
+ });
166
+ },
167
+ async getConfig(name) {
168
+ const result = await runCommand(["fly", "config", "show", "-a", name]);
169
+ return result.json().match({
170
+ ok: (v) => v,
171
+ err: () => null,
172
+ });
173
+ },
174
+ },
175
+ machines: {
176
+ async list(app) {
177
+ const result = await runJson(["fly", "machines", "list", "-a", app, "--json"]);
178
+ return result.flatMap((data) => {
179
+ const parsed = FlyMachinesListSchema.safeParse(data);
180
+ return parsed.success
181
+ ? Result.ok(parsed.data)
182
+ : Result.err("Parse Failed");
183
+ }).unwrapOr([]);
184
+ },
185
+ async clone(app, config) {
186
+ const raw = await provider.machines.list(app);
187
+ const existingMachines = mapMachines(raw);
188
+ if (existingMachines.length === 0) {
189
+ return die("No Existing Machine to Clone. Run 'fly deploy' First");
190
+ }
191
+ const sourceMachine = existingMachines[0];
192
+ const sizeConfig = getSizeConfig(config.size);
193
+ const memoryMb = config.memoryMb ?? sizeConfig.memoryMb;
194
+ const args = [
195
+ "fly",
196
+ "machine",
197
+ "clone",
198
+ sourceMachine.id,
199
+ "-a",
200
+ app,
201
+ "--vm-cpus",
202
+ String(sizeConfig.cpus),
203
+ "--vm-memory",
204
+ String(memoryMb),
205
+ ];
206
+ if (config.region) {
207
+ args.push("--region", config.region);
208
+ }
209
+ const result = await runCommand(args);
210
+ if (!result.ok) {
211
+ return die(result.stderr || "Unknown Error");
212
+ }
213
+ const rawAfter = await provider.machines.list(app);
214
+ const machines = mapMachines(rawAfter);
215
+ const newest = machines[machines.length - 1];
216
+ if (!newest) {
217
+ return die("Created Machine Not Found");
218
+ }
219
+ return newest;
220
+ },
221
+ async destroy(app, machineId) {
222
+ const shortId = machineId.slice(0, 8);
223
+ const result = await runCommand([
224
+ "fly",
225
+ "machines",
226
+ "destroy",
227
+ machineId,
228
+ "-a",
229
+ app,
230
+ "--force",
231
+ ]);
232
+ if (!result.ok) {
233
+ return die(`Failed to Destroy Machine '${shortId}'`);
234
+ }
235
+ },
236
+ },
237
+ secrets: {
238
+ async set(app, secrets, opts) {
239
+ const pairs = Object.entries(secrets)
240
+ .filter(([_, v]) => v !== undefined && v !== "")
241
+ .map(([k, v]) => `${k}=${v}`);
242
+ if (pairs.length === 0)
243
+ return;
244
+ const args = ["fly", "secrets", "set", ...pairs, "-a", app];
245
+ if (opts?.stage) {
246
+ args.push("--stage");
247
+ }
248
+ const result = await runCommand(args);
249
+ if (!result.ok) {
250
+ return die("Failed to Set Secrets");
251
+ }
252
+ },
253
+ },
254
+ ips: {
255
+ async list(app) {
256
+ const result = await runCommand([
257
+ "fly",
258
+ "ips",
259
+ "list",
260
+ "-a",
261
+ app,
262
+ "--json",
263
+ ]);
264
+ return result.json().flatMap((data) => {
265
+ const parsed = FlyIpListSchema.safeParse(data);
266
+ return parsed.success
267
+ ? Result.ok(parsed.data)
268
+ : Result.err("Parse Failed");
269
+ }).unwrapOr([]);
270
+ },
271
+ async release(app, address) {
272
+ const result = await runCommand([
273
+ "fly",
274
+ "ips",
275
+ "release",
276
+ address,
277
+ "-a",
278
+ app,
279
+ ]);
280
+ if (!result.ok) {
281
+ return die(`Failed to Release IP ${address} from '${app}'`);
282
+ }
283
+ },
284
+ async allocateFlycast(app, network) {
285
+ const result = await runCommand([
286
+ "fly",
287
+ "ips",
288
+ "allocate-v6",
289
+ "--private",
290
+ "--network",
291
+ network,
292
+ "-a",
293
+ app,
294
+ ]);
295
+ if (!result.ok) {
296
+ return die(`Failed to Allocate Flycast IP on Network '${network}'`);
297
+ }
298
+ },
299
+ },
300
+ certs: {
301
+ async list(app) {
302
+ const result = await runCommand([
303
+ "fly",
304
+ "certs",
305
+ "list",
306
+ "-a",
307
+ app,
308
+ "--json",
309
+ ]);
310
+ return result.json().map((certs) => certs
311
+ .map((c) => c.Hostname)
312
+ .filter((h) => typeof h === "string")).unwrapOr([]);
313
+ },
314
+ async remove(app, hostname) {
315
+ await runCommand([
316
+ "fly",
317
+ "certs",
318
+ "remove",
319
+ hostname,
320
+ "-a",
321
+ app,
322
+ "--yes",
323
+ ]);
324
+ },
325
+ },
326
+ deploy: {
327
+ async router(app, dockerDir, config) {
328
+ const args = [
329
+ "fly",
330
+ "deploy",
331
+ dockerDir,
332
+ "-a",
333
+ app,
334
+ "--yes",
335
+ "--ha=false",
336
+ ];
337
+ if (config?.region) {
338
+ args.push("--primary-region", config.region);
339
+ }
340
+ const result = await runCommand(args);
341
+ if (!result.ok) {
342
+ throw new FlyDeployError(app, result.stderr);
343
+ }
344
+ },
345
+ async app(appName, options) {
346
+ const flyAppName = options.routerId
347
+ ? getWorkloadAppName(appName, options.routerId)
348
+ : appName;
349
+ const args = ["fly", "deploy"];
350
+ // When config is provided, use its parent directory as the build context
351
+ // so fly deploy finds the Dockerfile and COPY picks up the correct files
352
+ if (options.config) {
353
+ const configAbs = resolve(options.config);
354
+ args.push(dirname(configAbs));
355
+ args.push("--config", configAbs);
356
+ }
357
+ args.push("-a", flyAppName, "--yes", "--no-public-ips");
358
+ if (options.image) {
359
+ args.push("--image", options.image);
360
+ }
361
+ if (options.region) {
362
+ args.push("--primary-region", options.region);
363
+ }
364
+ const result = await runCommand(args);
365
+ if (!result.ok) {
366
+ throw new FlyDeployError(flyAppName, result.stderr);
367
+ }
368
+ },
369
+ },
370
+ };
371
+ return provider;
372
+ };
@@ -0,0 +1,31 @@
1
+ import { type AuthKeyCapabilities, type TailscaleDevice } from "../schemas/tailscale.js";
2
+ export interface DeviceRoutes {
3
+ advertised: string[];
4
+ enabled: string[];
5
+ unapproved: string[];
6
+ }
7
+ export interface TailscaleProvider {
8
+ auth: {
9
+ validateKey(): Promise<boolean>;
10
+ createKey(opts?: AuthKeyCapabilities): Promise<string>;
11
+ };
12
+ devices: {
13
+ list(): Promise<TailscaleDevice[]>;
14
+ getByHostname(hostname: string): Promise<TailscaleDevice | null>;
15
+ delete(id: string): Promise<void>;
16
+ };
17
+ routes: {
18
+ get(deviceId: string): Promise<DeviceRoutes | null>;
19
+ approve(deviceId: string, routes: string[]): Promise<void>;
20
+ };
21
+ dns: {
22
+ getSplit(): Promise<Record<string, string[]>>;
23
+ setSplit(domain: string, nameservers: string[]): Promise<void>;
24
+ clearSplit(domain: string): Promise<void>;
25
+ };
26
+ acl: {
27
+ getPolicy(): Promise<Record<string, unknown> | null>;
28
+ };
29
+ }
30
+ export declare const createTailscaleProvider: (apiKey: string, tailnet?: string) => TailscaleProvider;
31
+ //# sourceMappingURL=tailscale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailscale.d.ts","sourceRoot":"","sources":["../../src/providers/tailscale.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAYjC,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KACxD,CAAC;IACF,OAAO,EAAE;QACP,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QACnC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACnC,CAAC;IACF,MAAM,EAAE;QACN,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QACpD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC5D,CAAC;IACF,GAAG,EAAE;QACH,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/D,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC3C,CAAC;IACF,GAAG,EAAE;QACH,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;KACtD,CAAC;CACH;AAiBD,eAAO,MAAM,uBAAuB,GAClC,QAAQ,MAAM,EACd,UAAS,MAAY,KACpB,iBAyMF,CAAC"}