@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.
- package/esm/cli/commands/create/index.d.ts +2 -0
- package/esm/cli/commands/create/index.d.ts.map +1 -0
- package/esm/cli/commands/create/index.js +292 -0
- package/esm/cli/commands/create/machine.d.ts +33 -0
- package/esm/cli/commands/create/machine.d.ts.map +1 -0
- package/esm/cli/commands/create/machine.js +162 -0
- package/esm/cli/commands/deploy/index.d.ts +2 -0
- package/esm/cli/commands/deploy/index.d.ts.map +1 -0
- package/esm/cli/commands/deploy/index.js +290 -0
- package/esm/cli/commands/deploy/machine.d.ts +52 -0
- package/esm/cli/commands/deploy/machine.d.ts.map +1 -0
- package/esm/cli/commands/deploy/machine.js +116 -0
- package/esm/cli/commands/deploy/modes.d.ts +18 -0
- package/esm/cli/commands/deploy/modes.d.ts.map +1 -0
- package/esm/cli/commands/deploy/modes.js +152 -0
- package/esm/cli/commands/destroy/app.d.ts +2 -0
- package/esm/cli/commands/destroy/app.d.ts.map +1 -0
- package/esm/cli/commands/destroy/app.js +173 -0
- package/esm/cli/commands/destroy/index.d.ts +2 -0
- package/esm/cli/commands/destroy/index.d.ts.map +1 -0
- package/esm/cli/commands/destroy/index.js +63 -0
- package/esm/cli/commands/destroy/network.d.ts +2 -0
- package/esm/cli/commands/destroy/network.d.ts.map +1 -0
- package/esm/cli/commands/destroy/network.js +210 -0
- package/esm/cli/commands/doctor.d.ts.map +1 -0
- package/esm/cli/commands/doctor.js +295 -0
- package/esm/{src/cli → cli}/commands/list.d.ts.map +1 -1
- package/esm/{src/cli → cli}/commands/list.js +39 -54
- package/esm/cli/commands/status.d.ts.map +1 -0
- package/esm/cli/commands/status.js +331 -0
- package/esm/cli/mod.d.ts.map +1 -0
- package/esm/{src/cli → cli}/mod.js +4 -4
- package/esm/deno.d.ts +4 -18
- package/esm/deno.js +5 -19
- package/esm/deps/jsr.io/@std/path/1.1.4/constants.d.ts +1 -1
- package/esm/lib/args.d.ts +11 -0
- package/esm/lib/args.d.ts.map +1 -0
- package/esm/lib/args.js +28 -0
- package/esm/lib/cli.d.ts +0 -1
- package/esm/lib/cli.d.ts.map +1 -1
- package/esm/lib/cli.js +0 -1
- package/esm/lib/command.d.ts +0 -3
- package/esm/lib/command.d.ts.map +1 -1
- package/esm/lib/command.js +2 -13
- package/esm/lib/machine.d.ts +11 -0
- package/esm/lib/machine.d.ts.map +1 -0
- package/esm/lib/machine.js +15 -0
- package/esm/lib/output.d.ts +2 -1
- package/esm/lib/output.d.ts.map +1 -1
- package/esm/lib/output.js +21 -3
- package/esm/lib/result.d.ts +0 -1
- package/esm/lib/result.d.ts.map +1 -1
- package/esm/lib/result.js +0 -1
- package/esm/main.d.ts +6 -6
- package/esm/main.d.ts.map +1 -1
- package/esm/main.js +7 -9
- package/esm/providers/fly.d.ts +81 -0
- package/esm/providers/fly.d.ts.map +1 -0
- package/esm/providers/fly.js +372 -0
- package/esm/providers/tailscale.d.ts +31 -0
- package/esm/providers/tailscale.d.ts.map +1 -0
- package/esm/providers/tailscale.js +150 -0
- package/esm/{src/schemas → schemas}/fly.d.ts +1 -11
- package/esm/schemas/fly.d.ts.map +1 -0
- package/esm/{src/schemas → schemas}/fly.js +14 -56
- package/esm/{src/schemas → schemas}/tailscale.d.ts +1 -2
- package/esm/schemas/tailscale.d.ts.map +1 -0
- package/esm/{src/schemas → schemas}/tailscale.js +2 -3
- package/esm/src/{docker/router → router}/Dockerfile +0 -11
- package/esm/src/{docker/router → router}/start.sh +18 -9
- package/esm/util/constants.d.ts +13 -0
- package/esm/util/constants.d.ts.map +1 -0
- package/esm/util/constants.js +34 -0
- package/esm/{src → util}/credentials.d.ts +0 -1
- package/esm/util/credentials.d.ts.map +1 -0
- package/esm/{src → util}/credentials.js +3 -5
- package/esm/{src → util}/discovery.d.ts +16 -3
- package/esm/util/discovery.d.ts.map +1 -0
- package/esm/{src → util}/discovery.js +24 -15
- package/esm/util/fly-transforms.d.ts +27 -0
- package/esm/util/fly-transforms.d.ts.map +1 -0
- package/esm/util/fly-transforms.js +87 -0
- package/esm/{src → util}/guard.d.ts +1 -2
- package/esm/util/guard.d.ts.map +1 -0
- package/esm/{src → util}/guard.js +27 -27
- package/esm/util/naming.d.ts +5 -0
- package/esm/util/naming.d.ts.map +1 -0
- package/esm/util/naming.js +12 -0
- package/esm/{src → util}/resolve.d.ts +2 -3
- package/esm/util/resolve.d.ts.map +1 -0
- package/esm/{src → util}/resolve.js +1 -2
- package/esm/util/session.d.ts +16 -0
- package/esm/util/session.d.ts.map +1 -0
- package/esm/util/session.js +19 -0
- package/esm/util/tailscale-local.d.ts +13 -0
- package/esm/util/tailscale-local.d.ts.map +1 -0
- package/esm/util/tailscale-local.js +63 -0
- package/esm/{src → util}/template.d.ts +0 -1
- package/esm/util/template.d.ts.map +1 -0
- package/esm/{src → util}/template.js +0 -1
- package/package.json +1 -49
- package/esm/lib/paths.d.ts +0 -3
- package/esm/lib/paths.d.ts.map +0 -1
- package/esm/lib/paths.js +0 -5
- package/esm/src/cli/commands/create.d.ts +0 -2
- package/esm/src/cli/commands/create.d.ts.map +0 -1
- package/esm/src/cli/commands/create.js +0 -308
- package/esm/src/cli/commands/deploy.d.ts +0 -2
- package/esm/src/cli/commands/deploy.d.ts.map +0 -1
- package/esm/src/cli/commands/deploy.js +0 -430
- package/esm/src/cli/commands/destroy.d.ts +0 -2
- package/esm/src/cli/commands/destroy.d.ts.map +0 -1
- package/esm/src/cli/commands/destroy.js +0 -340
- package/esm/src/cli/commands/doctor.d.ts.map +0 -1
- package/esm/src/cli/commands/doctor.js +0 -141
- package/esm/src/cli/commands/status.d.ts.map +0 -1
- package/esm/src/cli/commands/status.js +0 -152
- package/esm/src/cli/mod.d.ts.map +0 -1
- package/esm/src/credentials.d.ts.map +0 -1
- package/esm/src/discovery.d.ts.map +0 -1
- package/esm/src/guard.d.ts.map +0 -1
- package/esm/src/providers/fly.d.ts +0 -76
- package/esm/src/providers/fly.d.ts.map +0 -1
- package/esm/src/providers/fly.js +0 -407
- package/esm/src/providers/tailscale.d.ts +0 -31
- package/esm/src/providers/tailscale.d.ts.map +0 -1
- package/esm/src/providers/tailscale.js +0 -189
- package/esm/src/resolve.d.ts.map +0 -1
- package/esm/src/schemas/config.d.ts +0 -5
- package/esm/src/schemas/config.d.ts.map +0 -1
- package/esm/src/schemas/config.js +0 -22
- package/esm/src/schemas/fly.d.ts.map +0 -1
- package/esm/src/schemas/tailscale.d.ts.map +0 -1
- package/esm/src/template.d.ts.map +0 -1
- /package/esm/{src/cli → cli}/commands/doctor.d.ts +0 -0
- /package/esm/{src/cli → cli}/commands/list.d.ts +0 -0
- /package/esm/{src/cli → cli}/commands/status.d.ts +0 -0
- /package/esm/{src/cli → cli}/mod.d.ts +0 -0
- /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
|
-
|
|
117
|
+
throw new Error("exit");
|
|
100
118
|
}
|
|
101
119
|
isJson() {
|
|
102
120
|
return this.jsonMode;
|
package/esm/lib/result.d.ts
CHANGED
package/esm/lib/result.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"
|
|
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 "./
|
|
4
|
-
import "./
|
|
5
|
-
import "./
|
|
6
|
-
import "./
|
|
7
|
-
import "./
|
|
8
|
-
import "./
|
|
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;
|
|
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 "./
|
|
27
|
+
import { runCli } from "./cli/mod.js";
|
|
28
28
|
import { Spinner, statusErr } from "./lib/cli.js";
|
|
29
|
-
|
|
30
|
-
import "./
|
|
31
|
-
import "./
|
|
32
|
-
import "./
|
|
33
|
-
import "./
|
|
34
|
-
import "./
|
|
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"}
|