@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
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Deploy Command - Safely Deploy a Workload App on a Custom Private Network
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import { parseArgs } from "../../../deps/jsr.io/@std/cli/1.0.28/mod.js";
|
|
5
|
+
import { bold } from "../../../lib/cli.js";
|
|
6
|
+
import { checkArgs } from "../../../lib/args.js";
|
|
7
|
+
import { createOutput } from "../../../lib/output.js";
|
|
8
|
+
import { runMachine } from "../../../lib/machine.js";
|
|
9
|
+
import { registerCommand } from "../../mod.js";
|
|
10
|
+
import { createFlyProvider, } from "../../../providers/fly.js";
|
|
11
|
+
import { getWorkloadAppName } from "../../../util/naming.js";
|
|
12
|
+
import { findRouterApp, getRouterMachineInfo } from "../../../util/discovery.js";
|
|
13
|
+
import { resolveOrg } from "../../../util/resolve.js";
|
|
14
|
+
import { assertNotRouter } from "../../../util/guard.js";
|
|
15
|
+
import { resolveConfigMode, resolveImageMode, resolveTemplateMode, } from "./modes.js";
|
|
16
|
+
import { deployTransition, hydrateDeploy, reportDeploySkipped, } from "./machine.js";
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Stage 1: Fly.io Configuration
|
|
19
|
+
// =============================================================================
|
|
20
|
+
const stageFlyConfig = async (out, opts) => {
|
|
21
|
+
out.header("Step 1: Fly.io Configuration").blank();
|
|
22
|
+
const fly = createFlyProvider();
|
|
23
|
+
await fly.auth.ensureInstalled();
|
|
24
|
+
const email = await fly.auth.login({ interactive: !opts.json });
|
|
25
|
+
out.ok(`Authenticated as ${email}`);
|
|
26
|
+
const org = await resolveOrg(fly, opts, out);
|
|
27
|
+
out.blank();
|
|
28
|
+
return { fly, org };
|
|
29
|
+
};
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Stage 2: Network Verification
|
|
32
|
+
// =============================================================================
|
|
33
|
+
const stageNetworkVerification = async (out, fly, opts) => {
|
|
34
|
+
out.header("Step 2: Network Verification").blank();
|
|
35
|
+
const routerSpinner = out.spinner("Checking for Router on Network");
|
|
36
|
+
const router = await findRouterApp(fly, opts.org, opts.network);
|
|
37
|
+
if (!router) {
|
|
38
|
+
routerSpinner.fail("No Router Found");
|
|
39
|
+
return out.die(`No Ambit Router Found on Network '${opts.network}'. ` +
|
|
40
|
+
`Run 'ambit create ${opts.network}' First.`);
|
|
41
|
+
}
|
|
42
|
+
routerSpinner.success(`Router Found: ${router.appName}`);
|
|
43
|
+
const routerMachine = await getRouterMachineInfo(fly, router.appName);
|
|
44
|
+
out.blank();
|
|
45
|
+
return {
|
|
46
|
+
routerId: router.routerId,
|
|
47
|
+
flyAppName: getWorkloadAppName(opts.app, router.routerId),
|
|
48
|
+
routerPrivateIp: routerMachine?.privateIp,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Stage 3: Pre-flight Check
|
|
53
|
+
// =============================================================================
|
|
54
|
+
const stagePreflightCheck = async (out, opts) => {
|
|
55
|
+
out.header("Step 3: Pre-flight Check").blank();
|
|
56
|
+
let deployConfig;
|
|
57
|
+
if (opts.template) {
|
|
58
|
+
deployConfig = await resolveTemplateMode(opts.template, out);
|
|
59
|
+
}
|
|
60
|
+
else if (opts.image) {
|
|
61
|
+
deployConfig = resolveImageMode(opts.image, opts.mainPort, out);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
deployConfig = await resolveConfigMode(opts.config, out);
|
|
65
|
+
}
|
|
66
|
+
if (!deployConfig)
|
|
67
|
+
return out.die("Pre-flight Check Failed");
|
|
68
|
+
out.blank();
|
|
69
|
+
return deployConfig;
|
|
70
|
+
};
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// Stage 4: Deploy
|
|
73
|
+
// =============================================================================
|
|
74
|
+
const stageDeploy = async (out, fly, deployConfig, opts) => {
|
|
75
|
+
out.header("Step 4: Deploy").blank();
|
|
76
|
+
const ctx = {
|
|
77
|
+
fly,
|
|
78
|
+
out,
|
|
79
|
+
...opts,
|
|
80
|
+
created: false,
|
|
81
|
+
deployConfig,
|
|
82
|
+
deployOptions: {
|
|
83
|
+
routerId: opts.routerId,
|
|
84
|
+
image: deployConfig.image,
|
|
85
|
+
config: deployConfig.configPath,
|
|
86
|
+
region: opts.region,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const phase = await hydrateDeploy(ctx);
|
|
90
|
+
reportDeploySkipped(out, phase);
|
|
91
|
+
const machine = {
|
|
92
|
+
terminal: "complete",
|
|
93
|
+
transition: deployTransition,
|
|
94
|
+
};
|
|
95
|
+
const result = await runMachine(machine, phase, ctx);
|
|
96
|
+
if (!result.ok) {
|
|
97
|
+
if (result.error === "Cancelled")
|
|
98
|
+
return;
|
|
99
|
+
return out.die(result.error);
|
|
100
|
+
}
|
|
101
|
+
stageSummary(out, ctx, deployConfig);
|
|
102
|
+
};
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Stage 5: Summary
|
|
105
|
+
// =============================================================================
|
|
106
|
+
const stageSummary = (out, ctx, deployConfig) => {
|
|
107
|
+
const audit = ctx.audit ?? {
|
|
108
|
+
public_ips_released: 0,
|
|
109
|
+
certs_removed: 0,
|
|
110
|
+
flycast_allocations: [],
|
|
111
|
+
warnings: [],
|
|
112
|
+
};
|
|
113
|
+
const hasIssues = audit.public_ips_released > 0 || audit.warnings.length > 0;
|
|
114
|
+
const resultData = {
|
|
115
|
+
app: ctx.app,
|
|
116
|
+
network: ctx.network,
|
|
117
|
+
created: ctx.created,
|
|
118
|
+
audit,
|
|
119
|
+
preflight: deployConfig.preflight,
|
|
120
|
+
};
|
|
121
|
+
if (hasIssues) {
|
|
122
|
+
out.fail("Deploy Completed with Issues", resultData);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
out.done(resultData);
|
|
126
|
+
}
|
|
127
|
+
out.blank()
|
|
128
|
+
.header("=".repeat(50))
|
|
129
|
+
.header(hasIssues
|
|
130
|
+
? " Deploy Completed (with Warnings)"
|
|
131
|
+
: " Deploy Completed!")
|
|
132
|
+
.header("=".repeat(50))
|
|
133
|
+
.blank()
|
|
134
|
+
.text(`App '${ctx.app}' Is Reachable from Your Tailnet as:`)
|
|
135
|
+
.text(` ${ctx.app}.${ctx.network}`)
|
|
136
|
+
.blank();
|
|
137
|
+
out.print();
|
|
138
|
+
};
|
|
139
|
+
// =============================================================================
|
|
140
|
+
// Deploy Command
|
|
141
|
+
// =============================================================================
|
|
142
|
+
const deploy = async (argv) => {
|
|
143
|
+
const opts = {
|
|
144
|
+
string: [
|
|
145
|
+
"network",
|
|
146
|
+
"org",
|
|
147
|
+
"region",
|
|
148
|
+
"image",
|
|
149
|
+
"config",
|
|
150
|
+
"main-port",
|
|
151
|
+
"template",
|
|
152
|
+
],
|
|
153
|
+
boolean: ["help", "yes", "json"],
|
|
154
|
+
alias: { y: "yes" },
|
|
155
|
+
default: { "main-port": "80" },
|
|
156
|
+
};
|
|
157
|
+
const args = parseArgs(argv, opts);
|
|
158
|
+
checkArgs(args, opts, "ambit deploy");
|
|
159
|
+
if (args.help) {
|
|
160
|
+
console.log(`
|
|
161
|
+
${bold("ambit deploy")} - Deploy an App Safely on a Custom Private Network
|
|
162
|
+
|
|
163
|
+
${bold("USAGE")}
|
|
164
|
+
ambit deploy <app>.<network> [options]
|
|
165
|
+
ambit deploy <app> --network <name> [options]
|
|
166
|
+
|
|
167
|
+
The network can be specified as part of the name (app.network) or with --network.
|
|
168
|
+
|
|
169
|
+
${bold("MODES")}
|
|
170
|
+
Config mode (default):
|
|
171
|
+
ambit deploy my-app.lab Uses ./fly.toml
|
|
172
|
+
ambit deploy my-app.lab --config path Explicit fly.toml
|
|
173
|
+
|
|
174
|
+
Image mode:
|
|
175
|
+
ambit deploy my-app.lab --image <img> Docker image, no toml
|
|
176
|
+
|
|
177
|
+
Template mode:
|
|
178
|
+
ambit deploy my-app.lab --template <ref> GitHub template
|
|
179
|
+
|
|
180
|
+
${bold("OPTIONS")}
|
|
181
|
+
--network <name> Target network
|
|
182
|
+
--org <org> Fly.io organization slug
|
|
183
|
+
--region <region> Primary deployment region
|
|
184
|
+
-y, --yes Skip confirmation prompts
|
|
185
|
+
--json Output as JSON
|
|
186
|
+
|
|
187
|
+
${bold("CONFIG MODE")} (default)
|
|
188
|
+
--config <path> Explicit fly.toml path (auto-detects ./fly.toml if omitted)
|
|
189
|
+
|
|
190
|
+
${bold("IMAGE MODE")}
|
|
191
|
+
--image <img> Docker image to deploy (no fly.toml needed)
|
|
192
|
+
--main-port <port> Internal port for HTTP service (default: 80, "none" to skip)
|
|
193
|
+
|
|
194
|
+
${bold("TEMPLATE MODE")}
|
|
195
|
+
--template <ref> GitHub template as owner/repo[/path][@ref]
|
|
196
|
+
|
|
197
|
+
Reference format:
|
|
198
|
+
owner/repo Fetch repo root from the default branch
|
|
199
|
+
owner/repo/path Fetch subdirectory from the default branch
|
|
200
|
+
owner/repo/path@tag Fetch a tagged release
|
|
201
|
+
owner/repo/path@branch Fetch a specific branch
|
|
202
|
+
owner/repo/path@commit Fetch a specific commit
|
|
203
|
+
|
|
204
|
+
${bold("SAFETY")}
|
|
205
|
+
Always deploys with --no-public-ips and --flycast.
|
|
206
|
+
Post-deploy audit releases any public IPs and verifies Flycast allocation.
|
|
207
|
+
Pre-flight scan rejects fly.toml with force_https or TLS on 443.
|
|
208
|
+
|
|
209
|
+
${bold("EXAMPLES")}
|
|
210
|
+
ambit deploy my-app.lab
|
|
211
|
+
ambit deploy my-app.lab --image registry/img:latest
|
|
212
|
+
ambit deploy my-app.lab --config ./fly.toml --region sea
|
|
213
|
+
ambit deploy my-claw.lab --template ToxicPine/ambit-openclaw
|
|
214
|
+
ambit deploy my-browser.lab --template ToxicPine/ambit-templates/chromatic
|
|
215
|
+
ambit deploy my-browser --network lab --template ToxicPine/ambit-templates/chromatic@v1.0
|
|
216
|
+
`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const out = createOutput(args.json);
|
|
220
|
+
const appArg = args._[0];
|
|
221
|
+
if (!appArg || typeof appArg !== "string") {
|
|
222
|
+
return out.die("Missing App Name. Usage: ambit deploy <app>.<network>");
|
|
223
|
+
}
|
|
224
|
+
let app;
|
|
225
|
+
let network;
|
|
226
|
+
if (appArg.includes(".")) {
|
|
227
|
+
const parts = appArg.split(".");
|
|
228
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
229
|
+
return out.die(`'${appArg}' Should Have Exactly One Dot, Like my-app.my-network`);
|
|
230
|
+
}
|
|
231
|
+
if (args.network) {
|
|
232
|
+
return out.die(`Network Is Already Part of the Name ('${appArg}'), --network Is Not Needed`);
|
|
233
|
+
}
|
|
234
|
+
app = parts[0];
|
|
235
|
+
network = parts[1];
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
app = appArg;
|
|
239
|
+
if (!args.network) {
|
|
240
|
+
return out.die(`Missing Network. Use: ambit deploy ${app}.<network>`);
|
|
241
|
+
}
|
|
242
|
+
network = args.network;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
assertNotRouter(app);
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
return out.die(e instanceof Error ? e.message : String(e));
|
|
249
|
+
}
|
|
250
|
+
const modeFlags = [args.image, args.config, args.template].filter(Boolean);
|
|
251
|
+
if (modeFlags.length > 1) {
|
|
252
|
+
return out.die("--image, --config, and --template Are Mutually Exclusive");
|
|
253
|
+
}
|
|
254
|
+
out.blank()
|
|
255
|
+
.header("=".repeat(50))
|
|
256
|
+
.header(` ambit Deploy: ${app}`)
|
|
257
|
+
.header("=".repeat(50))
|
|
258
|
+
.blank();
|
|
259
|
+
const { fly, org } = await stageFlyConfig(out, {
|
|
260
|
+
json: args.json,
|
|
261
|
+
org: args.org,
|
|
262
|
+
});
|
|
263
|
+
const { routerId, flyAppName, routerPrivateIp } = await stageNetworkVerification(out, fly, { org, network, app });
|
|
264
|
+
const deployConfig = await stagePreflightCheck(out, {
|
|
265
|
+
template: args.template,
|
|
266
|
+
image: args.image,
|
|
267
|
+
config: args.config,
|
|
268
|
+
mainPort: String(args["main-port"]),
|
|
269
|
+
});
|
|
270
|
+
await stageDeploy(out, fly, deployConfig, {
|
|
271
|
+
app,
|
|
272
|
+
network,
|
|
273
|
+
org,
|
|
274
|
+
region: args.region,
|
|
275
|
+
yes: args.yes,
|
|
276
|
+
json: args.json,
|
|
277
|
+
routerId,
|
|
278
|
+
flyAppName,
|
|
279
|
+
routerPrivateIp,
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
// =============================================================================
|
|
283
|
+
// Register Command
|
|
284
|
+
// =============================================================================
|
|
285
|
+
registerCommand({
|
|
286
|
+
name: "deploy",
|
|
287
|
+
description: "Deploy an app safely on a custom private network",
|
|
288
|
+
usage: "ambit deploy <app> --network <name> [--image <img>] [--template <ref>] [--org <org>] [--region <region>]",
|
|
289
|
+
run: deploy,
|
|
290
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Output } from "../../../lib/output.js";
|
|
2
|
+
import { Result } from "../../../lib/result.js";
|
|
3
|
+
import { type FlyProvider, type SafeDeployOptions } from "../../../providers/fly.js";
|
|
4
|
+
import type { DeployConfig } from "./modes.js";
|
|
5
|
+
export type DeployPhase = "create_app" | "set_proxy" | "deploy" | "audit" | "complete";
|
|
6
|
+
export type DeployResult = {
|
|
7
|
+
app: string;
|
|
8
|
+
network: string;
|
|
9
|
+
created: boolean;
|
|
10
|
+
audit: {
|
|
11
|
+
public_ips_released: number;
|
|
12
|
+
certs_removed: number;
|
|
13
|
+
flycast_allocations: Array<{
|
|
14
|
+
address: string;
|
|
15
|
+
network: string;
|
|
16
|
+
}>;
|
|
17
|
+
warnings: string[];
|
|
18
|
+
};
|
|
19
|
+
preflight: {
|
|
20
|
+
scanned: boolean;
|
|
21
|
+
warnings: string[];
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export interface DeployCtx {
|
|
25
|
+
fly: FlyProvider;
|
|
26
|
+
out: Output<DeployResult>;
|
|
27
|
+
app: string;
|
|
28
|
+
network: string;
|
|
29
|
+
org: string;
|
|
30
|
+
region?: string;
|
|
31
|
+
yes: boolean;
|
|
32
|
+
json: boolean;
|
|
33
|
+
routerId: string;
|
|
34
|
+
flyAppName: string;
|
|
35
|
+
routerPrivateIp?: string;
|
|
36
|
+
created: boolean;
|
|
37
|
+
deployConfig: DeployConfig;
|
|
38
|
+
deployOptions: SafeDeployOptions;
|
|
39
|
+
audit?: {
|
|
40
|
+
public_ips_released: number;
|
|
41
|
+
certs_removed: number;
|
|
42
|
+
flycast_allocations: Array<{
|
|
43
|
+
address: string;
|
|
44
|
+
network: string;
|
|
45
|
+
}>;
|
|
46
|
+
warnings: string[];
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export declare const reportDeploySkipped: (out: Output<DeployResult>, startPhase: DeployPhase) => void;
|
|
50
|
+
export declare const hydrateDeploy: (ctx: DeployCtx) => Promise<DeployPhase>;
|
|
51
|
+
export declare const deployTransition: (phase: DeployPhase, ctx: DeployCtx) => Promise<Result<DeployPhase>>;
|
|
52
|
+
//# sourceMappingURL=machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy/machine.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,OAAO,GACP,UAAU,CAAC;AAMf,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE;QACL,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,iBAAiB,CAAC;IACjC,KAAK,CAAC,EAAE;QACN,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAaD,eAAO,MAAM,mBAAmB,GAC9B,KAAK,MAAM,CAAC,YAAY,CAAC,EACzB,YAAY,WAAW,SAMxB,CAAC;AAMF,eAAO,MAAM,aAAa,GACxB,KAAK,SAAS,KACb,OAAO,CAAC,WAAW,CAMrB,CAAC;AAMF,eAAO,MAAM,gBAAgB,GAC3B,OAAO,WAAW,EAClB,KAAK,SAAS,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAyG7B,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Deploy — Phases, Context, Hydration, Transitions
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
5
|
+
import { confirm } from "../../../lib/cli.js";
|
|
6
|
+
import { SECRET_AMBIT_OUTBOUND_PROXY, SOCKS_PROXY_PORT, } from "../../../util/constants.js";
|
|
7
|
+
import { Result } from "../../../lib/result.js";
|
|
8
|
+
import { FlyDeployError, } from "../../../providers/fly.js";
|
|
9
|
+
import { auditDeploy } from "../../../util/guard.js";
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Phase Labels
|
|
12
|
+
// =============================================================================
|
|
13
|
+
const DEPLOY_PHASES = [
|
|
14
|
+
{ phase: "create_app", label: "App Created" },
|
|
15
|
+
{ phase: "set_proxy", label: "Outbound Proxy Set" },
|
|
16
|
+
{ phase: "deploy", label: "Deployed" },
|
|
17
|
+
{ phase: "audit", label: "Audit Passed" },
|
|
18
|
+
];
|
|
19
|
+
export const reportDeploySkipped = (out, startPhase) => {
|
|
20
|
+
for (const { phase, label } of DEPLOY_PHASES) {
|
|
21
|
+
if (phase === startPhase)
|
|
22
|
+
break;
|
|
23
|
+
out.skip(label);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Hydration
|
|
28
|
+
// =============================================================================
|
|
29
|
+
export const hydrateDeploy = async (ctx) => {
|
|
30
|
+
const exists = await ctx.fly.apps.exists(ctx.flyAppName);
|
|
31
|
+
if (!exists)
|
|
32
|
+
return "create_app";
|
|
33
|
+
// App exists — always re-run set_proxy, deploy, and audit (they're idempotent)
|
|
34
|
+
return "set_proxy";
|
|
35
|
+
};
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// Transitions
|
|
38
|
+
// =============================================================================
|
|
39
|
+
export const deployTransition = async (phase, ctx) => {
|
|
40
|
+
switch (phase) {
|
|
41
|
+
case "create_app": {
|
|
42
|
+
ctx.out.info(`App '${ctx.flyAppName}' Does Not Exist — Will Create on Network '${ctx.network}'`);
|
|
43
|
+
if (!ctx.yes && !ctx.json) {
|
|
44
|
+
const confirmed = await confirm(`Create App '${ctx.flyAppName}' on Network '${ctx.network}'?`);
|
|
45
|
+
if (!confirmed) {
|
|
46
|
+
ctx.out.text("Cancelled.");
|
|
47
|
+
return Result.err("Cancelled");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
await ctx.out.spin("Creating App", () => ctx.fly.apps.create(ctx.app, ctx.org, {
|
|
51
|
+
network: ctx.network,
|
|
52
|
+
routerId: ctx.routerId,
|
|
53
|
+
}));
|
|
54
|
+
ctx.out.ok(`Created App '${ctx.flyAppName}' on Network '${ctx.network}'`);
|
|
55
|
+
ctx.created = true;
|
|
56
|
+
return Result.ok("set_proxy");
|
|
57
|
+
}
|
|
58
|
+
case "set_proxy": {
|
|
59
|
+
if (!ctx.created) {
|
|
60
|
+
ctx.out.skip(`App '${ctx.flyAppName}' Exists`);
|
|
61
|
+
}
|
|
62
|
+
if (ctx.routerPrivateIp) {
|
|
63
|
+
const proxyUrl = `socks5://[${ctx.routerPrivateIp}]:${SOCKS_PROXY_PORT}`;
|
|
64
|
+
await ctx.out.spin("Setting Outbound Proxy", () => ctx.fly.secrets.set(ctx.flyAppName, { [SECRET_AMBIT_OUTBOUND_PROXY]: proxyUrl }, { stage: true }));
|
|
65
|
+
ctx.out.ok(`Outbound Proxy: ${proxyUrl}`);
|
|
66
|
+
}
|
|
67
|
+
return Result.ok("deploy");
|
|
68
|
+
}
|
|
69
|
+
case "deploy": {
|
|
70
|
+
ctx.out.blank().dim("Deploying with --no-public-ips --flycast ...");
|
|
71
|
+
try {
|
|
72
|
+
await ctx.fly.deploy.app(ctx.app, ctx.deployOptions);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
if (e instanceof FlyDeployError) {
|
|
76
|
+
ctx.out.dim(` ${e.detail}`);
|
|
77
|
+
return Result.err(e.message);
|
|
78
|
+
}
|
|
79
|
+
throw e;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
if (ctx.deployConfig.tempDir) {
|
|
83
|
+
try {
|
|
84
|
+
dntShim.Deno.removeSync(ctx.deployConfig.tempDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
/* ignore */
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
ctx.out.ok("Deploy Succeeded");
|
|
92
|
+
return Result.ok("audit");
|
|
93
|
+
}
|
|
94
|
+
case "audit": {
|
|
95
|
+
ctx.out.blank();
|
|
96
|
+
const auditSpinner = ctx.out.spinner("Auditing Deployment");
|
|
97
|
+
ctx.audit = await auditDeploy(ctx.fly, ctx.flyAppName, ctx.network);
|
|
98
|
+
auditSpinner.success("Audit Complete");
|
|
99
|
+
if (ctx.audit.public_ips_released > 0) {
|
|
100
|
+
ctx.out.warn(`Released ${ctx.audit.public_ips_released} Public IP(s)`);
|
|
101
|
+
}
|
|
102
|
+
if (ctx.audit.certs_removed > 0) {
|
|
103
|
+
ctx.out.ok(`Removed ${ctx.audit.certs_removed} Public Certificate(s)`);
|
|
104
|
+
}
|
|
105
|
+
for (const alloc of ctx.audit.flycast_allocations) {
|
|
106
|
+
ctx.out.ok(`Flycast: ${alloc.address} (network: ${alloc.network})`);
|
|
107
|
+
}
|
|
108
|
+
for (const warn of ctx.audit.warnings) {
|
|
109
|
+
ctx.out.warn(warn);
|
|
110
|
+
}
|
|
111
|
+
return Result.ok("complete");
|
|
112
|
+
}
|
|
113
|
+
default:
|
|
114
|
+
return Result.err(`Unknown Phase: ${phase}`);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createOutput } from "../../../lib/output.js";
|
|
2
|
+
/** Resolved deploy configuration — the output of mode-specific validation. */
|
|
3
|
+
export interface DeployConfig {
|
|
4
|
+
image?: string;
|
|
5
|
+
configPath?: string;
|
|
6
|
+
preflight: {
|
|
7
|
+
scanned: boolean;
|
|
8
|
+
warnings: string[];
|
|
9
|
+
};
|
|
10
|
+
tempDir?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Resolve deploy config for image mode (--image). */
|
|
13
|
+
export declare const resolveImageMode: (image: string, mainPortRaw: string, out: ReturnType<typeof createOutput>) => DeployConfig | null;
|
|
14
|
+
/** Resolve deploy config for config mode (default — uses fly.toml). */
|
|
15
|
+
export declare const resolveConfigMode: (explicitConfig: string | undefined, out: ReturnType<typeof createOutput>) => Promise<DeployConfig | null>;
|
|
16
|
+
/** Resolve deploy config for template mode (--template). */
|
|
17
|
+
export declare const resolveTemplateMode: (templateRaw: string, out: ReturnType<typeof createOutput>) => Promise<DeployConfig | null>;
|
|
18
|
+
//# sourceMappingURL=modes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/deploy/modes.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAQtD,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoCD,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,EACb,aAAa,MAAM,EACnB,KAAK,UAAU,CAAC,OAAO,YAAY,CAAC,KACnC,YAAY,GAAG,IAmBjB,CAAC;AAMF,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,GAC5B,gBAAgB,MAAM,GAAG,SAAS,EAClC,KAAK,UAAU,CAAC,OAAO,YAAY,CAAC,KACnC,OAAO,CAAC,YAAY,GAAG,IAAI,CAqC7B,CAAC;AAMF,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,GAC9B,aAAa,MAAM,EACnB,KAAK,UAAU,CAAC,OAAO,YAAY,CAAC,KACnC,OAAO,CAAC,YAAY,GAAG,IAAI,CAyE7B,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Deploy Modes — Image, Config, and Template Resolution
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import * as dntShim from "../../../_dnt.shims.js";
|
|
5
|
+
import { join } from "../../../deps/jsr.io/@std/path/1.1.4/mod.js";
|
|
6
|
+
import { fileExists } from "../../../lib/cli.js";
|
|
7
|
+
import { scanFlyToml } from "../../../util/guard.js";
|
|
8
|
+
import { fetchTemplate, parseTemplateRef } from "../../../util/template.js";
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Image Mode
|
|
11
|
+
// =============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Generate a minimal fly.toml with http_service config for auto start/stop.
|
|
14
|
+
* Written to a temp directory and cleaned up after deploy.
|
|
15
|
+
*/
|
|
16
|
+
const generateServiceToml = (port) => `[http_service]\n` +
|
|
17
|
+
` internal_port = ${port}\n` +
|
|
18
|
+
` auto_stop_machines = "stop"\n` +
|
|
19
|
+
` auto_start_machines = true\n` +
|
|
20
|
+
` min_machines_running = 0\n`;
|
|
21
|
+
/**
|
|
22
|
+
* Parse --main-port value. Returns the port number, or null if "none".
|
|
23
|
+
* Dies on invalid input.
|
|
24
|
+
*/
|
|
25
|
+
const parseMainPort = (raw, out) => {
|
|
26
|
+
if (raw === "none")
|
|
27
|
+
return null;
|
|
28
|
+
const port = Number(raw);
|
|
29
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
30
|
+
out.die(`Invalid --main-port: "${raw}". Use a Port Number (1-65535) or "none".`);
|
|
31
|
+
return "error";
|
|
32
|
+
}
|
|
33
|
+
return port;
|
|
34
|
+
};
|
|
35
|
+
/** Resolve deploy config for image mode (--image). */
|
|
36
|
+
export const resolveImageMode = (image, mainPortRaw, out) => {
|
|
37
|
+
const mainPort = parseMainPort(mainPortRaw, out);
|
|
38
|
+
if (mainPort === "error")
|
|
39
|
+
return null;
|
|
40
|
+
const preflight = {
|
|
41
|
+
scanned: false,
|
|
42
|
+
warnings: [],
|
|
43
|
+
};
|
|
44
|
+
if (mainPort !== null) {
|
|
45
|
+
const tempDir = dntShim.Deno.makeTempDirSync();
|
|
46
|
+
const configPath = join(tempDir, "fly.toml");
|
|
47
|
+
dntShim.Deno.writeTextFileSync(configPath, generateServiceToml(mainPort));
|
|
48
|
+
out.ok(`HTTP Service on Port ${mainPort} (auto start/stop)`);
|
|
49
|
+
return { image, configPath, preflight, tempDir };
|
|
50
|
+
}
|
|
51
|
+
out.info("Image Mode — No Service Config");
|
|
52
|
+
return { image, preflight };
|
|
53
|
+
};
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// Config Mode
|
|
56
|
+
// =============================================================================
|
|
57
|
+
/** Resolve deploy config for config mode (default — uses fly.toml). */
|
|
58
|
+
export const resolveConfigMode = async (explicitConfig, out) => {
|
|
59
|
+
let configPath = explicitConfig;
|
|
60
|
+
if (!configPath && (await fileExists("./fly.toml"))) {
|
|
61
|
+
configPath = "./fly.toml";
|
|
62
|
+
}
|
|
63
|
+
if (!configPath) {
|
|
64
|
+
out.info("No fly.toml Found — Deploying Without Config Scan");
|
|
65
|
+
return { preflight: { scanned: false, warnings: [] } };
|
|
66
|
+
}
|
|
67
|
+
if (!(await fileExists(configPath))) {
|
|
68
|
+
out.die(`Config File Not Found: ${configPath}`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const tomlContent = await dntShim.Deno.readTextFile(configPath);
|
|
72
|
+
const scan = scanFlyToml(tomlContent);
|
|
73
|
+
if (scan.errors.length > 0) {
|
|
74
|
+
for (const err of scan.errors) {
|
|
75
|
+
out.err(err);
|
|
76
|
+
}
|
|
77
|
+
out.die("Pre-flight Check Failed. Fix fly.toml Before Deploying.");
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
for (const warn of scan.warnings) {
|
|
81
|
+
out.warn(warn);
|
|
82
|
+
}
|
|
83
|
+
out.ok(`Scanned ${configPath}`);
|
|
84
|
+
return {
|
|
85
|
+
configPath,
|
|
86
|
+
preflight: { scanned: scan.scanned, warnings: scan.warnings },
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// Template Mode
|
|
91
|
+
// =============================================================================
|
|
92
|
+
/** Resolve deploy config for template mode (--template). */
|
|
93
|
+
export const resolveTemplateMode = async (templateRaw, out) => {
|
|
94
|
+
const ref = parseTemplateRef(templateRaw);
|
|
95
|
+
if (!ref) {
|
|
96
|
+
out.die(`Invalid Template Reference: "${templateRaw}". ` +
|
|
97
|
+
`Format: owner/repo[/path][@ref]`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const label = (ref.path === "."
|
|
101
|
+
? `${ref.owner}/${ref.repo}`
|
|
102
|
+
: `${ref.owner}/${ref.repo}/${ref.path}`) +
|
|
103
|
+
(ref.ref ? `@${ref.ref}` : "");
|
|
104
|
+
out.info(`Template: ${label}`);
|
|
105
|
+
const fetchSpinner = out.spinner("Fetching Template from GitHub");
|
|
106
|
+
const result = await fetchTemplate(ref);
|
|
107
|
+
if (!result.ok) {
|
|
108
|
+
fetchSpinner.fail("Template Fetch Failed");
|
|
109
|
+
out.die(result.error);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const { tempDir, templateDir } = result.value;
|
|
113
|
+
fetchSpinner.success("Template Fetched");
|
|
114
|
+
const configPath = join(templateDir, "fly.toml");
|
|
115
|
+
let tomlContent;
|
|
116
|
+
try {
|
|
117
|
+
tomlContent = await dntShim.Deno.readTextFile(configPath);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
try {
|
|
121
|
+
dntShim.Deno.removeSync(tempDir, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
/* ignore */
|
|
125
|
+
}
|
|
126
|
+
out.die(`Template '${ref.path === "." ? ref.repo : ref.path}' Has No fly.toml`);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const scan = scanFlyToml(tomlContent);
|
|
130
|
+
if (scan.errors.length > 0) {
|
|
131
|
+
try {
|
|
132
|
+
dntShim.Deno.removeSync(tempDir, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* ignore */
|
|
136
|
+
}
|
|
137
|
+
for (const err of scan.errors) {
|
|
138
|
+
out.err(err);
|
|
139
|
+
}
|
|
140
|
+
out.die("Pre-flight Check Failed for Template fly.toml");
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
for (const warn of scan.warnings) {
|
|
144
|
+
out.warn(warn);
|
|
145
|
+
}
|
|
146
|
+
out.ok(`Scanned ${ref.path === "." ? "" : ref.path + "/"}fly.toml`);
|
|
147
|
+
return {
|
|
148
|
+
configPath,
|
|
149
|
+
preflight: { scanned: scan.scanned, warnings: scan.warnings },
|
|
150
|
+
tempDir,
|
|
151
|
+
};
|
|
152
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/destroy/app.ts"],"names":[],"mappings":"AAiLA,eAAO,MAAM,UAAU,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,IAAI,CAoF7D,CAAC"}
|