relight-cli 0.2.0 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relight-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Deploy web apps across clouds with scale-to-zero",
5
5
  "bin": {
6
6
  "relight": "./src/cli.js"
package/src/cli.js CHANGED
@@ -18,9 +18,10 @@ import { ps } from "./commands/ps.js";
18
18
  import { logs } from "./commands/logs.js";
19
19
  import { open } from "./commands/open.js";
20
20
  import { cost } from "./commands/cost.js";
21
+ import { serviceList, serviceAdd, serviceRemove } from "./commands/service.js";
21
22
  import {
22
- dbCreate, dbDestroy, dbInfo, dbShell, dbQuery,
23
- dbImport, dbExport, dbToken, dbReset,
23
+ dbCreate, dbDestroy, dbList, dbInfo, dbAttach, dbDetach,
24
+ dbShell, dbQuery, dbImport, dbExport, dbToken, dbReset,
24
25
  } from "./commands/db.js";
25
26
  import { fmt } from "./lib/output.js";
26
27
  import { createRequire } from "module";
@@ -46,12 +47,32 @@ program
46
47
  )
47
48
  .action(auth);
48
49
 
50
+ // --- Service ---
51
+
52
+ var serviceCmd = program.command("service").description("Manage services (compute, databases, etc.)");
53
+
54
+ serviceCmd
55
+ .command("list", { isDefault: true })
56
+ .description("List registered services")
57
+ .action(serviceList);
58
+
59
+ serviceCmd
60
+ .command("add [name]")
61
+ .description("Register a service")
62
+ .action(serviceAdd);
63
+
64
+ serviceCmd
65
+ .command("remove <name>")
66
+ .description("Remove a service")
67
+ .action(serviceRemove);
68
+
49
69
  // --- Deploy ---
50
70
 
51
71
  program
52
72
  .command("deploy [name] [path]")
53
73
  .description("Deploy an app from a Dockerfile (name auto-generated if omitted)")
54
74
  .option("-c, --cloud <cloud>", "Cloud provider (cf, gcp, aws)")
75
+ .option("--compute <name>", "Compute service name")
55
76
  .option("-t, --tag <tag>", "Image tag (default: deploy-<timestamp>)")
56
77
  .option("-e, --env <vars...>", "Set env vars (KEY=VALUE)")
57
78
  .option(
@@ -65,11 +86,7 @@ program
65
86
  .option("--vcpu <n>", "vCPU allocation (e.g. 0.0625, 0.5, 1, 2)", parseFloat)
66
87
  .option("--memory <mb>", "Memory in MiB (e.g. 256, 512, 1024)", parseInt)
67
88
  .option("--disk <mb>", "Disk in MB (e.g. 2000, 5000)", parseInt)
68
- .option("--db", "Create and attach a D1 database")
69
- .option("--db-location <hint>", "D1 location hint (wnam, enam, weur, eeur, apac)")
70
- .option("--db-jurisdiction <j>", "D1 jurisdiction (eu, fedramp)")
71
89
  .option("--dns <cloud>", "Cloud provider for DNS records (cross-cloud)")
72
- .option("--db-cloud <cloud>", "Cloud provider for the database (cross-cloud)")
73
90
  .option("--no-observability", "Disable Workers observability/logs")
74
91
  .option("--json", "Output result as JSON")
75
92
  .option("-y, --yes", "Skip confirmation prompt")
@@ -83,6 +100,7 @@ apps
83
100
  .command("list", { isDefault: true })
84
101
  .description("List all deployed apps")
85
102
  .option("-c, --cloud <cloud>", "Cloud provider")
103
+ .option("--compute <name>", "Compute service name")
86
104
  .option("--json", "Output as JSON")
87
105
  .action(appsList);
88
106
 
@@ -90,6 +108,7 @@ apps
90
108
  .command("info [name]")
91
109
  .description("Show detailed app information")
92
110
  .option("-c, --cloud <cloud>", "Cloud provider")
111
+ .option("--compute <name>", "Compute service name")
93
112
  .option("--json", "Output as JSON")
94
113
  .action(appsInfo);
95
114
 
@@ -97,6 +116,7 @@ apps
97
116
  .command("destroy [name]")
98
117
  .description("Destroy an app and its resources")
99
118
  .option("-c, --cloud <cloud>", "Cloud provider")
119
+ .option("--compute <name>", "Compute service name")
100
120
  .option("--confirm <name>", "Confirm by providing the app name")
101
121
  .action(appsDestroy);
102
122
 
@@ -105,40 +125,42 @@ apps
105
125
  var configCmd = program
106
126
  .command("config")
107
127
  .description("Manage app config/env vars")
108
- .option("-c, --cloud <cloud>", "Cloud provider");
128
+ .option("-c, --cloud <cloud>", "Cloud provider")
129
+ .option("--compute <name>", "Compute service name");
109
130
 
110
- function configCloud(cmd) {
111
- return cmd?.parent?.opts()?.cloud;
131
+ function configOpts(cmd) {
132
+ var parentOpts = cmd?.parent?.opts() || {};
133
+ return { cloud: parentOpts.cloud, compute: parentOpts.compute };
112
134
  }
113
135
 
114
136
  configCmd
115
137
  .command("show [name]", { isDefault: true })
116
138
  .description("Show all env vars for an app")
117
139
  .option("--json", "Output as JSON")
118
- .action((name, options, cmd) => configShow(name, { ...options, cloud: configCloud(cmd) }));
140
+ .action((name, options, cmd) => configShow(name, { ...options, ...configOpts(cmd) }));
119
141
 
120
142
  configCmd
121
143
  .command("set <args...>")
122
144
  .description("Set env vars ([name] KEY=VALUE ...) - applies live")
123
145
  .option("-s, --secret", "Store values as encrypted secrets (write-only)")
124
- .action((args, options, cmd) => configSet(args, { ...options, cloud: configCloud(cmd) }));
146
+ .action((args, options, cmd) => configSet(args, { ...options, ...configOpts(cmd) }));
125
147
 
126
148
  configCmd
127
149
  .command("get <args...>")
128
150
  .description("Get a single env var value ([name] KEY)")
129
- .action((args, options, cmd) => configGet(args, { ...options, cloud: configCloud(cmd) }));
151
+ .action((args, options, cmd) => configGet(args, { ...options, ...configOpts(cmd) }));
130
152
 
131
153
  configCmd
132
154
  .command("unset <args...>")
133
155
  .description("Remove env vars ([name] KEY ...) - applies live")
134
- .action((args, options, cmd) => configUnset(args, { ...options, cloud: configCloud(cmd) }));
156
+ .action((args, options, cmd) => configUnset(args, { ...options, ...configOpts(cmd) }));
135
157
 
136
158
  configCmd
137
159
  .command("import [name]")
138
160
  .description("Import env vars from .env file or stdin")
139
161
  .option("-f, --file <path>", "Path to .env file")
140
162
  .option("-s, --secret", "Store values as encrypted secrets (write-only)")
141
- .action((name, options, cmd) => configImport(name, { ...options, cloud: configCloud(cmd) }));
163
+ .action((name, options, cmd) => configImport(name, { ...options, ...configOpts(cmd) }));
142
164
 
143
165
  // --- Scale ---
144
166
 
@@ -146,6 +168,7 @@ program
146
168
  .command("scale [name]")
147
169
  .description("Show or adjust app scaling")
148
170
  .option("-c, --cloud <cloud>", "Cloud provider")
171
+ .option("--compute <name>", "Compute service name")
149
172
  .option(
150
173
  "-r, --regions <hints>",
151
174
  "Comma-separated location hints (wnam,enam,sam,weur,eeur,apac,oc,afr,me)"
@@ -164,18 +187,20 @@ var domainsCmd = program
164
187
  .command("domains")
165
188
  .description("Manage custom domains")
166
189
  .option("-c, --cloud <cloud>", "Cloud provider")
190
+ .option("--compute <name>", "Compute service name")
167
191
  .option("--dns <cloud>", "Cloud provider for DNS records (cross-cloud)");
168
192
 
169
193
  domainsCmd
170
194
  .command("list [name]", { isDefault: true })
171
195
  .description("List custom domains for an app")
172
196
  .option("-c, --cloud <cloud>", "Cloud provider")
197
+ .option("--compute <name>", "Compute service name")
173
198
  .option("--json", "Output as JSON")
174
199
  .action(domainsList);
175
200
 
176
201
  function domainsOpts(cmd) {
177
202
  var parentOpts = cmd?.parent?.opts() || {};
178
- return { cloud: parentOpts.cloud, dns: parentOpts.dns };
203
+ return { cloud: parentOpts.cloud, compute: parentOpts.compute, dns: parentOpts.dns };
179
204
  }
180
205
 
181
206
  domainsCmd
@@ -188,72 +213,86 @@ domainsCmd
188
213
  .description("Remove a custom domain ([name] domain) - applies live")
189
214
  .action((args, options, cmd) => domainsRemove(args, { ...options, ...domainsOpts(cmd) }));
190
215
 
191
- // --- DB (topic root = info) ---
216
+ // --- DB (topic root = list) ---
192
217
 
193
218
  var dbCmd = program
194
219
  .command("db")
195
- .description("Manage app database")
196
- .option("-c, --cloud <cloud>", "Cloud provider")
197
- .option("--db <cloud>", "Cloud provider for the database (cross-cloud)");
198
-
199
- function dbOpts(cmd) {
200
- var parentOpts = cmd?.parent?.opts() || {};
201
- return { cloud: parentOpts.cloud, db: parentOpts.db };
202
- }
220
+ .description("Manage databases");
203
221
 
204
222
  dbCmd
205
- .command("info [name]", { isDefault: true })
206
- .description("Show database details and connection string")
223
+ .command("list", { isDefault: true })
224
+ .description("List all databases")
207
225
  .option("--json", "Output as JSON")
208
- .action((name, options, cmd) => dbInfo(name, { ...options, ...dbOpts(cmd) }));
226
+ .action(dbList);
209
227
 
210
228
  dbCmd
211
- .command("create [name]")
212
- .description("Create and attach a database")
229
+ .command("create <name>")
230
+ .description("Create a standalone database")
231
+ .option("--provider <id>", "Provider: cf, gcp, aws, or service name (e.g. neon)")
213
232
  .option("--location <hint>", "Location hint (wnam, enam, weur, eeur, apac)")
214
233
  .option("--jurisdiction <j>", "Jurisdiction (eu, fedramp)")
215
234
  .option("--json", "Output as JSON")
216
- .action((name, options, cmd) => dbCreate(name, { ...options, ...dbOpts(cmd) }));
235
+ .action(dbCreate);
217
236
 
218
237
  dbCmd
219
- .command("destroy [name]")
220
- .description("Destroy the attached database")
221
- .option("--confirm <name>", "Confirm by providing the app name")
222
- .action((name, options, cmd) => dbDestroy(name, { ...options, ...dbOpts(cmd) }));
238
+ .command("destroy <name>")
239
+ .description("Destroy a database")
240
+ .option("--confirm <name>", "Confirm by providing the database name")
241
+ .action(dbDestroy);
242
+
243
+ dbCmd
244
+ .command("info <name>")
245
+ .description("Show database details")
246
+ .option("--json", "Output as JSON")
247
+ .action(dbInfo);
223
248
 
224
249
  dbCmd
225
- .command("shell [name]")
250
+ .command("attach <name> [app]")
251
+ .description("Attach database to an app (injects env vars)")
252
+ .option("-c, --cloud <cloud>", "App cloud")
253
+ .option("--compute <name>", "App compute service")
254
+ .action(dbAttach);
255
+
256
+ dbCmd
257
+ .command("detach [app]")
258
+ .description("Detach database from an app (removes env vars)")
259
+ .option("-c, --cloud <cloud>", "App cloud")
260
+ .option("--compute <name>", "App compute service")
261
+ .action(dbDetach);
262
+
263
+ dbCmd
264
+ .command("shell <name>")
226
265
  .description("Interactive SQL REPL")
227
- .action((name, options, cmd) => dbShell(name, { ...options, ...dbOpts(cmd) }));
266
+ .action(dbShell);
228
267
 
229
268
  dbCmd
230
269
  .command("query <args...>")
231
270
  .description("Run a single SQL query ([name] <sql>)")
232
271
  .option("--json", "Output as JSON")
233
- .action((args, options, cmd) => dbQuery(args, { ...options, ...dbOpts(cmd) }));
272
+ .action(dbQuery);
234
273
 
235
274
  dbCmd
236
275
  .command("import <args...>")
237
- .description("Import a .sql file ([name] <path>)")
238
- .action((args, options, cmd) => dbImport(args, { ...options, ...dbOpts(cmd) }));
276
+ .description("Import a .sql file (<name> <path>)")
277
+ .action(dbImport);
239
278
 
240
279
  dbCmd
241
- .command("export [name]")
280
+ .command("export <name>")
242
281
  .description("Export database as SQL dump")
243
282
  .option("-o, --output <path>", "Write to file instead of stdout")
244
- .action((name, options, cmd) => dbExport(name, { ...options, ...dbOpts(cmd) }));
283
+ .action(dbExport);
245
284
 
246
285
  dbCmd
247
- .command("token [name]")
286
+ .command("token <name>")
248
287
  .description("Show or rotate auth token")
249
288
  .option("--rotate", "Generate a new token")
250
- .action((name, options, cmd) => dbToken(name, { ...options, ...dbOpts(cmd) }));
289
+ .action(dbToken);
251
290
 
252
291
  dbCmd
253
- .command("reset [name]")
292
+ .command("reset <name>")
254
293
  .description("Drop all tables (confirmation required)")
255
- .option("--confirm <name>", "Confirm by providing the app name")
256
- .action((name, options, cmd) => dbReset(name, { ...options, ...dbOpts(cmd) }));
294
+ .option("--confirm <name>", "Confirm by providing the database name")
295
+ .action(dbReset);
257
296
 
258
297
  // --- PS ---
259
298
 
@@ -261,6 +300,7 @@ program
261
300
  .command("ps [name]")
262
301
  .description("Show app containers and status")
263
302
  .option("-c, --cloud <cloud>", "Cloud provider")
303
+ .option("--compute <name>", "Compute service name")
264
304
  .option("--json", "Output as JSON")
265
305
  .action(ps);
266
306
 
@@ -270,6 +310,7 @@ program
270
310
  .command("logs [name]")
271
311
  .description("Stream live logs from an app")
272
312
  .option("-c, --cloud <cloud>", "Cloud provider")
313
+ .option("--compute <name>", "Compute service name")
273
314
  .action(logs);
274
315
 
275
316
  // --- Open ---
@@ -278,6 +319,7 @@ program
278
319
  .command("open [name]")
279
320
  .description("Open app in browser")
280
321
  .option("-c, --cloud <cloud>", "Cloud provider")
322
+ .option("--compute <name>", "Compute service name")
281
323
  .action(open);
282
324
 
283
325
  // --- Regions ---
@@ -286,11 +328,12 @@ program
286
328
  .command("regions")
287
329
  .description("List available deployment regions")
288
330
  .option("-c, --cloud <cloud>", "Cloud provider")
331
+ .option("--compute <name>", "Compute service name")
289
332
  .option("--json", "Output as JSON")
290
333
  .action(async function (options) {
291
- var { resolveCloudId, getCloudCfg, getProvider } = await import("./lib/providers/resolve.js");
292
- var cloud = resolveCloudId(options.cloud);
293
- var appProvider = await getProvider(cloud, "app");
334
+ var { resolveTarget } = await import("./lib/providers/resolve.js");
335
+ var target = await resolveTarget(options);
336
+ var appProvider = await target.provider("app");
294
337
 
295
338
  var regions = appProvider.getRegions();
296
339
  if (options.json) {
@@ -313,6 +356,7 @@ program
313
356
  .command("cost [name]")
314
357
  .description("Show estimated costs for an app or all apps")
315
358
  .option("-c, --cloud <cloud>", "Cloud provider")
359
+ .option("--compute <name>", "Compute service name")
316
360
  .option("--since <period>", "Date range: Nd (e.g. 7d) or YYYY-MM-DD (default: month to date)")
317
361
  .option("--json", "Output as JSON")
318
362
  .action(cost);
@@ -330,6 +374,7 @@ program
330
374
  .command("destroy [name]")
331
375
  .description("Destroy an app (alias for apps destroy)")
332
376
  .option("-c, --cloud <cloud>", "Cloud provider")
377
+ .option("--compute <name>", "Compute service name")
333
378
  .option("--confirm <name>", "Confirm by providing the app name")
334
379
  .action(appsDestroy);
335
380
 
@@ -1,12 +1,12 @@
1
1
  import { success, fatal, hint, fmt, table } from "../lib/output.js";
2
2
  import { resolveAppName, readLink, unlinkApp } from "../lib/link.js";
3
- import { resolveCloudId, getCloudCfg, getProvider } from "../lib/providers/resolve.js";
3
+ import { resolveTarget } from "../lib/providers/resolve.js";
4
4
  import { createInterface } from "readline";
5
5
 
6
6
  export async function appsList(options) {
7
- var cloud = resolveCloudId(options.cloud);
8
- var cfg = getCloudCfg(cloud);
9
- var appProvider = await getProvider(cloud, "app");
7
+ var target = await resolveTarget(options);
8
+ var cfg = target.cfg;
9
+ var appProvider = await target.provider("app");
10
10
 
11
11
  var apps = await appProvider.listApps(cfg);
12
12
 
@@ -35,9 +35,9 @@ export async function appsList(options) {
35
35
 
36
36
  export async function appsInfo(name, options) {
37
37
  name = resolveAppName(name);
38
- var cloud = resolveCloudId(options.cloud);
39
- var cfg = getCloudCfg(cloud);
40
- var appProvider = await getProvider(cloud, "app");
38
+ var target = await resolveTarget(options);
39
+ var cfg = target.cfg;
40
+ var appProvider = await target.provider("app");
41
41
 
42
42
  var info = await appProvider.getAppInfo(cfg, name);
43
43
 
@@ -88,9 +88,9 @@ export async function appsInfo(name, options) {
88
88
 
89
89
  export async function appsDestroy(name, options) {
90
90
  name = resolveAppName(name);
91
- var cloud = resolveCloudId(options.cloud);
92
- var cfg = getCloudCfg(cloud);
93
- var appProvider = await getProvider(cloud, "app");
91
+ var target = await resolveTarget(options);
92
+ var cfg = target.cfg;
93
+ var appProvider = await target.provider("app");
94
94
 
95
95
  if (options.confirm !== name) {
96
96
  if (process.stdin.isTTY) {
@@ -16,7 +16,6 @@ import {
16
16
  verifyProject,
17
17
  } from "../lib/clouds/gcp.js";
18
18
  import { verifyCredentials as awsVerify } from "../lib/clouds/aws.js";
19
- import { verifyConnection as slicerVerify } from "../lib/clouds/slicervm.js";
20
19
  import kleur from "kleur";
21
20
 
22
21
  function prompt(rl, question) {
@@ -73,9 +72,6 @@ export async function auth(options) {
73
72
  case "aws":
74
73
  cloudConfig = await authAWS(rl);
75
74
  break;
76
- case "slicervm":
77
- cloudConfig = await authSlicerVM(rl);
78
- break;
79
75
  }
80
76
 
81
77
  rl.close();
@@ -109,8 +105,6 @@ function normalizeCompute(input) {
109
105
  aws: "aws",
110
106
  amazon: "aws",
111
107
  "app-runner": "aws",
112
- slicervm: "slicervm",
113
- slicer: "slicervm",
114
108
  };
115
109
  return aliases[input.toLowerCase()] || input.toLowerCase();
116
110
  }
@@ -302,62 +296,6 @@ async function authAWS(rl) {
302
296
  return { accessKeyId, secretAccessKey, region };
303
297
  }
304
298
 
305
- // --- SlicerVM ---
306
-
307
- async function authSlicerVM(rl) {
308
- process.stderr.write(`\n ${kleur.bold("Connection mode")}\n\n`);
309
- process.stderr.write(` ${kleur.bold("[1]")} Unix socket (local dev)\n`);
310
- process.stderr.write(` ${kleur.bold("[2]")} HTTP API (remote)\n\n`);
311
-
312
- var modeChoice = await prompt(rl, "Select [1-2]: ");
313
- var useSocket = modeChoice.trim() === "1";
314
-
315
- var config = {};
316
-
317
- if (useSocket) {
318
- var defaultSocket = "/var/run/slicer/slicer.sock";
319
- var socketPath = await prompt(rl, `Socket path [${defaultSocket}]: `);
320
- socketPath = (socketPath || "").trim() || defaultSocket;
321
- config.socketPath = socketPath;
322
- } else {
323
- var apiUrl = await prompt(rl, "Slicer API URL (e.g. https://slicer.example.com:8080): ");
324
- apiUrl = (apiUrl || "").trim().replace(/\/+$/, "");
325
- if (!apiUrl) fatal("No API URL provided.");
326
- config.apiUrl = apiUrl;
327
-
328
- var token = await prompt(rl, "API token: ");
329
- token = (token || "").trim();
330
- if (!token) fatal("No token provided.");
331
- config.token = token;
332
- }
333
-
334
- var hostGroup = await prompt(rl, "Host group [apps]: ");
335
- config.hostGroup = (hostGroup || "").trim() || "apps";
336
-
337
- var baseDomain = await prompt(rl, "Base domain (e.g. apps.example.com) [localhost]: ");
338
- config.baseDomain = (baseDomain || "").trim() || "localhost";
339
-
340
- process.stderr.write("\nVerifying...\n");
341
- var verifyCfg = useSocket
342
- ? { socketPath: config.socketPath }
343
- : { apiUrl: config.apiUrl, apiToken: config.token };
344
- try {
345
- await slicerVerify(verifyCfg);
346
- } catch (e) {
347
- fatal("Connection failed.", e.message);
348
- }
349
-
350
- if (useSocket) {
351
- process.stderr.write(` Socket: ${fmt.bold(config.socketPath)}\n`);
352
- } else {
353
- process.stderr.write(` API: ${fmt.bold(config.apiUrl)}\n`);
354
- }
355
- process.stderr.write(` Host group: ${fmt.dim(config.hostGroup)}\n`);
356
- process.stderr.write(` Base domain: ${fmt.dim(config.baseDomain)}\n`);
357
-
358
- return config;
359
- }
360
-
361
299
  // --- Helpers ---
362
300
 
363
301
  function tryExec(cmd) {
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { status, success, fatal, hint, fmt } from "../lib/output.js";
3
3
  import { resolveAppName } from "../lib/link.js";
4
- import { resolveCloudId, getCloudCfg, getProvider } from "../lib/providers/resolve.js";
4
+ import { resolveTarget } from "../lib/providers/resolve.js";
5
5
 
6
6
  var RESERVED_NAMES = ["RELIGHT_APP_CONFIG", "APP_CONTAINER", "DB", "DB_URL", "DB_TOKEN"];
7
7
 
@@ -19,9 +19,9 @@ function ensureKeyLists(appConfig) {
19
19
 
20
20
  export async function configShow(name, options) {
21
21
  name = resolveAppName(name);
22
- var cloud = resolveCloudId(options.cloud);
23
- var cfg = getCloudCfg(cloud);
24
- var appProvider = await getProvider(cloud, "app");
22
+ var target = await resolveTarget(options);
23
+ var cfg = target.cfg;
24
+ var appProvider = await target.provider("app");
25
25
  var appConfig = await appProvider.getAppConfig(cfg, name);
26
26
 
27
27
  if (!appConfig) {
@@ -70,9 +70,9 @@ export async function configSet(args, options) {
70
70
  fatal("No env vars provided.", "Usage: relight config set [name] KEY=VALUE ...");
71
71
  }
72
72
 
73
- var cloud = resolveCloudId(options.cloud);
74
- var cfg = getCloudCfg(cloud);
75
- var appProvider = await getProvider(cloud, "app");
73
+ var target = await resolveTarget(options);
74
+ var cfg = target.cfg;
75
+ var appProvider = await target.provider("app");
76
76
  var appConfig = await appProvider.getAppConfig(cfg, name);
77
77
 
78
78
  if (!appConfig) {
@@ -126,9 +126,9 @@ export async function configGet(args, options) {
126
126
  fatal("Usage: relight config get [name] <key>");
127
127
  }
128
128
 
129
- var cloud = resolveCloudId(options.cloud);
130
- var cfg = getCloudCfg(cloud);
131
- var appProvider = await getProvider(cloud, "app");
129
+ var target = await resolveTarget(options);
130
+ var cfg = target.cfg;
131
+ var appProvider = await target.provider("app");
132
132
  var appConfig = await appProvider.getAppConfig(cfg, name);
133
133
 
134
134
  if (!appConfig) {
@@ -173,9 +173,9 @@ export async function configUnset(args, options) {
173
173
  fatal("No keys provided.", "Usage: relight config unset [name] KEY ...");
174
174
  }
175
175
 
176
- var cloud = resolveCloudId(options.cloud);
177
- var cfg = getCloudCfg(cloud);
178
- var appProvider = await getProvider(cloud, "app");
176
+ var target = await resolveTarget(options);
177
+ var cfg = target.cfg;
178
+ var appProvider = await target.provider("app");
179
179
  var appConfig = await appProvider.getAppConfig(cfg, name);
180
180
 
181
181
  if (!appConfig) {
@@ -206,9 +206,9 @@ export async function configUnset(args, options) {
206
206
 
207
207
  export async function configImport(name, options) {
208
208
  name = resolveAppName(name);
209
- var cloud = resolveCloudId(options.cloud);
210
- var cfg = getCloudCfg(cloud);
211
- var appProvider = await getProvider(cloud, "app");
209
+ var target = await resolveTarget(options);
210
+ var cfg = target.cfg;
211
+ var appProvider = await target.provider("app");
212
212
  var appConfig = await appProvider.getAppConfig(cfg, name);
213
213
 
214
214
  if (!appConfig) {
@@ -1,6 +1,6 @@
1
1
  import { phase, status, fatal, fmt, table } from "../lib/output.js";
2
2
  import { resolveAppName } from "../lib/link.js";
3
- import { resolveCloudId, getCloudCfg, getProvider } from "../lib/providers/resolve.js";
3
+ import { resolveTarget } from "../lib/providers/resolve.js";
4
4
 
5
5
  // --- Pricing (Workers Paid plan, $5/mo) ---
6
6
 
@@ -456,9 +456,9 @@ function renderAwsFleet(appResults, range) {
456
456
  // --- Main command ---
457
457
 
458
458
  export async function cost(name, options) {
459
- var cloud = resolveCloudId(options.cloud);
460
- var cfg = getCloudCfg(cloud);
461
- var appProvider = await getProvider(cloud, "app");
459
+ var target = await resolveTarget(options);
460
+ var cfg = target.cfg;
461
+ var appProvider = await target.provider("app");
462
462
 
463
463
  var range = parseDateRange(options.since);
464
464
 
@@ -477,7 +477,7 @@ export async function cost(name, options) {
477
477
  );
478
478
  }
479
479
 
480
- if (cloud === "aws") {
480
+ if (target.type === "aws") {
481
481
  // AWS rendering
482
482
  if (options.json) {
483
483
  var jsonOut = singleApp
@@ -512,7 +512,7 @@ export async function cost(name, options) {
512
512
  return;
513
513
  }
514
514
 
515
- if (cloud === "gcp") {
515
+ if (target.type === "gcp") {
516
516
  // GCP rendering
517
517
  if (options.json) {
518
518
  var jsonOut = singleApp