@roxyon/cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BEA Technology Company
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/roxyon.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,611 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/deploy.ts
7
+ import * as p from "@clack/prompts";
8
+ import { DeployError, deployProject } from "@roxyon/deploy-core";
9
+
10
+ // src/context.ts
11
+ import { Roxyon } from "@roxyon/api-client";
12
+
13
+ // src/config.ts
14
+ import {
15
+ buildProjectConfig,
16
+ clearCredentials,
17
+ credentialsPath,
18
+ endpointsFromEnv,
19
+ loadCredentials,
20
+ loadProjectConfig,
21
+ projectConfigPath,
22
+ saveCredentials,
23
+ saveProjectConfig,
24
+ tokenFromEnv
25
+ } from "@roxyon/deploy-core";
26
+
27
+ // src/ui.ts
28
+ import pc from "picocolors";
29
+ var ui = {
30
+ info: (msg) => process.stdout.write(`${pc.cyan("\u203A")} ${msg}
31
+ `),
32
+ success: (msg) => process.stdout.write(`${pc.green("\u2713")} ${msg}
33
+ `),
34
+ warn: (msg) => process.stderr.write(`${pc.yellow("!")} ${msg}
35
+ `),
36
+ error: (msg) => process.stderr.write(`${pc.red("\u2717")} ${msg}
37
+ `),
38
+ line: (msg = "") => process.stdout.write(`${msg}
39
+ `),
40
+ dim: (msg) => process.stdout.write(`${pc.dim(msg)}
41
+ `),
42
+ kv: (key, value) => process.stdout.write(` ${pc.dim(key.padEnd(14))} ${value}
43
+ `)
44
+ };
45
+ var EXIT = {
46
+ ok: 0,
47
+ failure: 1,
48
+ authRequired: 2,
49
+ configError: 3
50
+ };
51
+ var CliError = class extends Error {
52
+ exitCode;
53
+ constructor(message, exitCode = EXIT.failure) {
54
+ super(message);
55
+ this.name = "CliError";
56
+ this.exitCode = exitCode;
57
+ }
58
+ };
59
+ function fail(message, exitCode = EXIT.failure) {
60
+ throw new CliError(message, exitCode);
61
+ }
62
+
63
+ // src/context.ts
64
+ async function requireSession() {
65
+ const { apiUrl, consoleUrl } = endpointsFromEnv();
66
+ const envToken = tokenFromEnv();
67
+ if (envToken) {
68
+ return {
69
+ roxyon: new Roxyon({ sessionToken: envToken, baseUrl: apiUrl, consoleUrl })
70
+ };
71
+ }
72
+ const creds = await loadCredentials();
73
+ if (!creds?.sessionToken) {
74
+ fail("Not signed in. Run `roxyon login` (or set ROXYON_TOKEN for CI).", EXIT.authRequired);
75
+ }
76
+ return {
77
+ roxyon: new Roxyon({ sessionToken: creds.sessionToken, baseUrl: apiUrl, consoleUrl }),
78
+ credentials: creds
79
+ };
80
+ }
81
+ function anonymousRoxyon() {
82
+ const { apiUrl, consoleUrl } = endpointsFromEnv();
83
+ return new Roxyon({ baseUrl: apiUrl, consoleUrl });
84
+ }
85
+
86
+ // src/commands/deploy.ts
87
+ var CODE = {
88
+ "no-config": EXIT.configError,
89
+ auth: EXIT.authRequired,
90
+ "bad-host": EXIT.configError,
91
+ "build-failed": EXIT.failure,
92
+ "no-build-output": EXIT.failure,
93
+ "upload-failed": EXIT.failure,
94
+ "create-failed": EXIT.failure
95
+ };
96
+ async function deploy(opts) {
97
+ const { roxyon, credentials } = await requireSession();
98
+ const spin = p.spinner();
99
+ let spinning = false;
100
+ const reporter = {
101
+ step: (msg) => {
102
+ if (spinning) spin.message(msg);
103
+ else {
104
+ spin.start(msg);
105
+ spinning = true;
106
+ }
107
+ },
108
+ log: (line) => ui.dim(` ${line}`)
109
+ };
110
+ try {
111
+ const outcome = await deployProject({
112
+ cwd: process.cwd(),
113
+ roxyon,
114
+ preferredSubscription: credentials?.subscription,
115
+ build: opts.build,
116
+ follow: opts.follow,
117
+ reporter
118
+ });
119
+ if (outcome.kind === "static" && outcome.ok) {
120
+ spin.stop("Deployed.");
121
+ ui.success(`${outcome.url} is live (${outcome.files} files).`);
122
+ return;
123
+ }
124
+ if (outcome.kind === "app" && outcome.ok) {
125
+ spin.stop(
126
+ outcome.status === "running" ? `Live \u2014 revision ${outcome.revision} running.` : `Build queued (revision ${outcome.revision}).`
127
+ );
128
+ if (outcome.url) ui.success(outcome.url);
129
+ return;
130
+ }
131
+ spin.stop("Deploy failed.");
132
+ if ("error" in outcome && outcome.error) {
133
+ ui.line();
134
+ ui.error(outcome.error);
135
+ }
136
+ process.exitCode = EXIT.failure;
137
+ } catch (err) {
138
+ if (spinning) spin.stop("Failed.");
139
+ if (err instanceof DeployError) fail(err.message, CODE[err.code]);
140
+ throw err;
141
+ }
142
+ }
143
+
144
+ // src/commands/env.ts
145
+ import { RoxyonApiError, formatEnv } from "@roxyon/api-client";
146
+ async function resolveApp(explicit) {
147
+ const appId = explicit ?? (await loadProjectConfig())?.application;
148
+ if (!appId)
149
+ fail("No application. Pass --app <id> or deploy this project first.", EXIT.configError);
150
+ return appId;
151
+ }
152
+ async function envPull(opts) {
153
+ const appId = await resolveApp(opts.app);
154
+ const { roxyon } = await requireSession();
155
+ try {
156
+ const env2 = await roxyon.applications.getEnv(appId);
157
+ ui.line(formatEnv(env2) || "# (no environment variables set)");
158
+ } catch (err) {
159
+ if (err instanceof RoxyonApiError) fail(err.message);
160
+ throw err;
161
+ }
162
+ }
163
+ async function envSet(pairs, opts) {
164
+ if (pairs.length === 0)
165
+ fail("Nothing to set. Usage: roxyon env set KEY=value [KEY2=value2]", EXIT.configError);
166
+ const appId = await resolveApp(opts.app);
167
+ const set = {};
168
+ for (const pair of pairs) {
169
+ const eq = pair.indexOf("=");
170
+ if (eq < 1) fail(`"${pair}" is not KEY=value.`, EXIT.configError);
171
+ const key = pair.slice(0, eq).trim();
172
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) fail(`Invalid env key "${key}".`, EXIT.configError);
173
+ if (key === "PORT" || key === "HOST") {
174
+ ui.warn(`${key} is set by the platform \u2014 ignoring.`);
175
+ continue;
176
+ }
177
+ set[key] = pair.slice(eq + 1);
178
+ }
179
+ const { roxyon } = await requireSession();
180
+ try {
181
+ const r = await roxyon.applications.setEnv(appId, { set });
182
+ ui.success(`Updated ${r.changed.join(", ") || "nothing"}. Run \`roxyon deploy\` to apply.`);
183
+ } catch (err) {
184
+ if (err instanceof RoxyonApiError) fail(err.message);
185
+ throw err;
186
+ }
187
+ }
188
+ async function envRm(keys, opts) {
189
+ if (keys.length === 0)
190
+ fail("Nothing to remove. Usage: roxyon env rm KEY [KEY2]", EXIT.configError);
191
+ const appId = await resolveApp(opts.app);
192
+ const { roxyon } = await requireSession();
193
+ try {
194
+ const r = await roxyon.applications.setEnv(appId, { remove: keys });
195
+ ui.success(`Removed ${r.changed.join(", ") || "nothing"}. Run \`roxyon deploy\` to apply.`);
196
+ } catch (err) {
197
+ if (err instanceof RoxyonApiError) fail(err.message);
198
+ throw err;
199
+ }
200
+ }
201
+
202
+ // src/commands/init.ts
203
+ import { basename } from "path";
204
+ import * as p2 from "@clack/prompts";
205
+ import { isCancel } from "@clack/prompts";
206
+ import {
207
+ buildProjectConfig as buildProjectConfig2,
208
+ detectRuntime,
209
+ loadProjectConfig as loadProjectConfig2,
210
+ saveProjectConfig as saveProjectConfig2
211
+ } from "@roxyon/deploy-core";
212
+ async function init(opts) {
213
+ const cwd = process.cwd();
214
+ const existing = await loadProjectConfig2(cwd);
215
+ if (existing && !opts.yes) {
216
+ const overwrite = await ask(
217
+ () => p2.confirm({ message: "roxyon.json already exists \u2014 overwrite it?", initialValue: false })
218
+ );
219
+ if (!overwrite) return;
220
+ }
221
+ const { roxyon, credentials } = await requireSession();
222
+ const user = await roxyon.auth.me();
223
+ const sub = await roxyon.subscriptions.resolve(user.objectId, credentials?.subscription);
224
+ const domains = await roxyon.domains.list(sub.objectId);
225
+ if (domains.length === 0) {
226
+ fail(
227
+ "This subscription has no hosts yet. Add a domain in the console, then run `roxyon init` again.",
228
+ EXIT.configError
229
+ );
230
+ }
231
+ const detected = await detectRuntime(cwd);
232
+ ui.info(`Detected runtime: ${detected.runtime} \u2014 ${detected.reason}`);
233
+ const runtime = opts.runtime ? opts.runtime : opts.yes ? detected.runtime : await ask(
234
+ () => p2.select({
235
+ message: "Runtime",
236
+ initialValue: detected.runtime,
237
+ options: [
238
+ { value: "lumen", label: "LumenJS (static SPA, no server)" },
239
+ { value: "node", label: "Node.js app" },
240
+ { value: "python", label: "Python app" },
241
+ { value: "php", label: "PHP app" }
242
+ ]
243
+ })
244
+ );
245
+ const host = opts.host ?? (opts.yes ? domains[0]?.Name ?? fail("No host available.", EXIT.configError) : await ask(
246
+ () => p2.select({
247
+ message: "Host",
248
+ options: domains.map((d) => ({ value: d.Name, label: d.Name }))
249
+ })
250
+ ));
251
+ const defaultFolder = basename(cwd).replace(/[^a-z0-9._-]/gi, "-");
252
+ const folder = opts.folder ?? (opts.yes ? runtime === "lumen" ? "" : defaultFolder : await ask(
253
+ () => p2.text({
254
+ message: runtime === "lumen" ? "Sub-path under the host root (blank = site root)" : "Folder under the host (source location)",
255
+ placeholder: runtime === "lumen" ? "(root)" : defaultFolder,
256
+ defaultValue: runtime === "lumen" ? "" : defaultFolder
257
+ })
258
+ ));
259
+ const config = buildProjectConfig2({
260
+ cwd,
261
+ runtime,
262
+ host,
263
+ folder,
264
+ name: basename(cwd),
265
+ start: detected.start
266
+ });
267
+ const path = await saveProjectConfig2(config, cwd);
268
+ ui.success(`Wrote ${path}`);
269
+ ui.line();
270
+ ui.dim(JSON.stringify(config, null, 2));
271
+ ui.line();
272
+ ui.info("Next: `roxyon deploy`");
273
+ }
274
+ async function ask(fn) {
275
+ const v = await fn();
276
+ if (isCancel(v)) {
277
+ p2.cancel("Cancelled.");
278
+ process.exit(EXIT.ok);
279
+ }
280
+ return v;
281
+ }
282
+
283
+ // src/commands/login.ts
284
+ import * as p3 from "@clack/prompts";
285
+ import { isCancel as isCancel2 } from "@clack/prompts";
286
+ import { RoxyonApiError as RoxyonApiError2 } from "@roxyon/api-client";
287
+ async function login(opts) {
288
+ const roxyon = anonymousRoxyon();
289
+ if (opts.token) {
290
+ roxyon.client.setSessionToken(opts.token);
291
+ const user = await roxyon.auth.me().catch((e) => {
292
+ fail(`That token did not authenticate: ${e.message}`, EXIT.authRequired);
293
+ });
294
+ await persist({ sessionToken: opts.token, userId: user.objectId, email: user.Email });
295
+ ui.success(`Signed in as ${user.Email ?? user.objectId} (via --token).`);
296
+ return;
297
+ }
298
+ const interactive = process.stdin.isTTY && !opts.password;
299
+ const email = opts.email ?? (interactive ? await prompt(
300
+ () => p3.text({
301
+ message: "Email",
302
+ validate: (v) => v.includes("@") ? void 0 : "Enter an email"
303
+ })
304
+ ) : fail(
305
+ "Provide --email (and --password), or run in an interactive terminal.",
306
+ EXIT.configError
307
+ ));
308
+ const password2 = opts.password ?? (interactive ? await prompt(() => p3.password({ message: "Password" })) : fail("Provide --password.", EXIT.configError));
309
+ const spin = p3.spinner();
310
+ spin.start("Signing in");
311
+ try {
312
+ let result = await roxyon.auth.login(email, password2, { via: "cli" });
313
+ if (result.stepUpRequired) {
314
+ spin.stop("A one-time code was sent to you.");
315
+ const code = opts.otp ?? (interactive ? await prompt(() => p3.text({ message: "One-time code" })) : fail("This account requires OTP \u2014 pass --otp <code>.", EXIT.authRequired));
316
+ spin.start("Verifying code");
317
+ result = await roxyon.auth.verifyOtp(result.challengeId ?? "", code);
318
+ }
319
+ roxyon.client.setSessionToken(result.sessionToken);
320
+ const user = await roxyon.auth.me();
321
+ await persist({
322
+ sessionToken: result.sessionToken,
323
+ refreshToken: result.refreshToken,
324
+ userId: user.objectId,
325
+ email: user.Email
326
+ });
327
+ spin.stop(`Signed in as ${user.Email ?? user.objectId}.`);
328
+ ui.dim("Token stored in ~/.roxyon/config.json (permissions 0600).");
329
+ } catch (err) {
330
+ spin.stop("Sign-in failed.");
331
+ if (err instanceof RoxyonApiError2) fail(err.message, EXIT.authRequired);
332
+ throw err;
333
+ }
334
+ }
335
+ async function logout() {
336
+ const creds = await loadCredentials();
337
+ await clearCredentials();
338
+ ui.success(creds ? `Signed out ${creds.email ?? creds.userId}.` : "Nothing to sign out.");
339
+ }
340
+ async function persist(partial) {
341
+ await saveCredentials({ ...partial, savedAt: (/* @__PURE__ */ new Date()).toISOString() });
342
+ }
343
+ async function prompt(fn) {
344
+ const value = await fn();
345
+ if (isCancel2(value)) {
346
+ p3.cancel("Cancelled.");
347
+ process.exit(EXIT.ok);
348
+ }
349
+ return value;
350
+ }
351
+
352
+ // src/commands/logs.ts
353
+ import { RoxyonApiError as RoxyonApiError3 } from "@roxyon/api-client";
354
+ async function logs(opts) {
355
+ const appId = opts.app ?? (await loadProjectConfig())?.application;
356
+ if (!appId) {
357
+ fail(
358
+ "No application. Pass --app <id>, or run from a project whose roxyon.json has one.",
359
+ EXIT.configError
360
+ );
361
+ }
362
+ const { roxyon } = await requireSession();
363
+ const lines = opts.lines ?? 100;
364
+ if (!opts.follow) {
365
+ for (const l of await fetchLogs(
366
+ roxyon.applications.logs.bind(roxyon.applications),
367
+ appId,
368
+ lines
369
+ )) {
370
+ ui.line(l);
371
+ }
372
+ return;
373
+ }
374
+ ui.dim(`Following ${appId} \u2014 Ctrl-C to stop.`);
375
+ let seen = /* @__PURE__ */ new Set();
376
+ const tick = async () => {
377
+ try {
378
+ for (const l of await roxyon.applications.logs(appId, lines)) {
379
+ if (l && !seen.has(l)) {
380
+ seen.add(l);
381
+ ui.line(l);
382
+ }
383
+ }
384
+ if (seen.size > 4e3) seen = new Set([...seen].slice(-2e3));
385
+ } catch (err) {
386
+ ui.warn(err.message);
387
+ }
388
+ };
389
+ await tick();
390
+ const timer = setInterval(tick, 7e3);
391
+ process.on("SIGINT", () => {
392
+ clearInterval(timer);
393
+ process.exit(EXIT.ok);
394
+ });
395
+ }
396
+ async function fetchLogs(fn, appId, lines) {
397
+ try {
398
+ return await fn(appId, lines);
399
+ } catch (err) {
400
+ if (err instanceof RoxyonApiError3) fail(err.message);
401
+ throw err;
402
+ }
403
+ }
404
+
405
+ // src/commands/misc.ts
406
+ import { spawn } from "child_process";
407
+ import { platform } from "os";
408
+ import * as p4 from "@clack/prompts";
409
+ import { isCancel as isCancel3 } from "@clack/prompts";
410
+ import { RoxyonApiError as RoxyonApiError4 } from "@roxyon/api-client";
411
+ async function restart(opts) {
412
+ const appId = opts.app ?? (await loadProjectConfig())?.application;
413
+ if (!appId)
414
+ fail("No application. Pass --app <id> or deploy this project first.", EXIT.configError);
415
+ const { roxyon } = await requireSession();
416
+ try {
417
+ await roxyon.applications.restart(appId);
418
+ ui.success("Restart queued.");
419
+ } catch (err) {
420
+ if (err instanceof RoxyonApiError4) fail(err.message);
421
+ throw err;
422
+ }
423
+ }
424
+ async function open() {
425
+ const config = await loadProjectConfig();
426
+ if (!config) fail("No roxyon.json here.", EXIT.configError);
427
+ const url = `https://${config.host}${config.folder ? `/${config.folder}` : ""}`;
428
+ const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "start" : "xdg-open";
429
+ spawn(cmd, [url], { stdio: "ignore", detached: true, shell: platform() === "win32" }).unref();
430
+ ui.info(`Opening ${url}`);
431
+ }
432
+ async function link(repoUrl, opts) {
433
+ const config = await loadProjectConfig();
434
+ const appId = opts.app ?? config?.application;
435
+ if (!appId)
436
+ fail(
437
+ "Deploy the project once first so an application exists, then `roxyon link`.",
438
+ EXIT.configError
439
+ );
440
+ const url = repoUrl ?? (process.stdin.isTTY ? await ask2(
441
+ () => p4.text({ message: "Git remote (SSH URL)", placeholder: "git@github.com:you/app.git" })
442
+ ) : fail("Pass the git remote URL: roxyon link <url>", EXIT.configError));
443
+ const branch = opts.branch ?? "main";
444
+ const { roxyon } = await requireSession();
445
+ try {
446
+ const res = await roxyon.applications.repoConnect(appId, url, branch);
447
+ ui.success(`Connected ${url} (${branch}).`);
448
+ ui.line();
449
+ if (res.deployKey) {
450
+ ui.info("Add this deploy key to the repo (read-only is enough):");
451
+ ui.line(res.deployKey);
452
+ ui.line();
453
+ }
454
+ if (res.webhookUrl) {
455
+ ui.info("Add a push webhook:");
456
+ ui.kv("URL", res.webhookUrl);
457
+ if (res.secret) ui.kv("Secret", res.secret);
458
+ ui.line();
459
+ }
460
+ if (res.hint) ui.dim(res.hint);
461
+ if (config) {
462
+ await saveProjectConfig({ ...config }, process.cwd());
463
+ }
464
+ } catch (err) {
465
+ if (err instanceof RoxyonApiError4) fail(err.message);
466
+ throw err;
467
+ }
468
+ }
469
+ async function ask2(fn) {
470
+ const v = await fn();
471
+ if (isCancel3(v)) {
472
+ p4.cancel("Cancelled.");
473
+ process.exit(EXIT.ok);
474
+ }
475
+ return v;
476
+ }
477
+
478
+ // src/commands/token.ts
479
+ import { RoxyonApiError as RoxyonApiError5 } from "@roxyon/api-client";
480
+ var SCOPES = ["deploy", "logs", "read"];
481
+ async function tokenCreate(name, opts) {
482
+ if (!name) fail("A name is required: roxyon token create <name>", EXIT.configError);
483
+ const scopes = (opts.scopes ? opts.scopes.split(",") : ["deploy", "logs"]).map((s) => s.trim().toLowerCase()).filter((s) => SCOPES.includes(s));
484
+ if (scopes.length === 0)
485
+ fail(`--scopes must be a subset of: ${SCOPES.join(", ")}`, EXIT.configError);
486
+ const { roxyon } = await requireSession();
487
+ try {
488
+ const t = await roxyon.tokens.create(name, {
489
+ scopes,
490
+ expiresInDays: opts.expires && opts.expires > 0 ? opts.expires : void 0
491
+ });
492
+ ui.success(
493
+ `Created "${t.name}" (${t.scopes.join(", ")})${t.expiresAt ? ` \xB7 expires ${t.expiresAt.slice(0, 10)}` : ""}`
494
+ );
495
+ ui.line();
496
+ ui.line(t.token);
497
+ ui.line();
498
+ ui.dim(
499
+ "Copy it now \u2014 it will not be shown again. Use it as ROXYON_TOKEN in CI or the MCP server."
500
+ );
501
+ } catch (err) {
502
+ if (err instanceof RoxyonApiError5) fail(err.message);
503
+ throw err;
504
+ }
505
+ }
506
+ async function tokenList() {
507
+ const { roxyon } = await requireSession();
508
+ try {
509
+ const tokens = await roxyon.tokens.list();
510
+ if (tokens.length === 0) {
511
+ ui.info("No personal access tokens.");
512
+ return;
513
+ }
514
+ ui.line();
515
+ for (const t of tokens) {
516
+ ui.kv(
517
+ t.name,
518
+ `${t.prefix}\u2026 \xB7 ${t.scopes.join(",")}${t.expiresAt ? ` \xB7 expires ${t.expiresAt.slice(0, 10)}` : ""}${t.lastUsedAt ? ` \xB7 used ${t.lastUsedAt.slice(0, 10)}` : " \xB7 never used"}`
519
+ );
520
+ ui.dim(` id ${t.id}`);
521
+ }
522
+ ui.line();
523
+ } catch (err) {
524
+ if (err instanceof RoxyonApiError5) fail(err.message);
525
+ throw err;
526
+ }
527
+ }
528
+ async function tokenRevoke(id) {
529
+ if (!id) fail("A token id is required: roxyon token revoke <id>", EXIT.configError);
530
+ const { roxyon } = await requireSession();
531
+ try {
532
+ await roxyon.tokens.revoke(id);
533
+ ui.success(`Revoked ${id}.`);
534
+ } catch (err) {
535
+ if (err instanceof RoxyonApiError5) fail(err.message);
536
+ throw err;
537
+ }
538
+ }
539
+
540
+ // src/commands/whoami.ts
541
+ import { isAuthError } from "@roxyon/api-client";
542
+ async function whoami() {
543
+ const { roxyon, credentials } = await requireSession();
544
+ try {
545
+ const ctx = await roxyon.account.context();
546
+ ui.line();
547
+ ui.kv("User", ctx.user.email || ctx.user.id);
548
+ ui.kv("User ID", ctx.user.id);
549
+ if (credentials?.sessionToken?.startsWith("roxp_")) {
550
+ ui.kv("Auth", `personal access token (${ctx.scopes.join(", ") || "no scopes"})`);
551
+ }
552
+ if (ctx.subscriptions.length === 0) {
553
+ ui.kv("Subscription", "(none \u2014 add a plan in the console)");
554
+ } else {
555
+ const active = credentials?.subscription && ctx.subscriptions.find(
556
+ (s) => s.id === credentials.subscription || s.name === credentials.subscription
557
+ ) || ctx.subscriptions.find((s) => s.status === "active") || ctx.subscriptions[0];
558
+ for (const s of ctx.subscriptions) {
559
+ const mark = s.id === active?.id ? "\u2192 " : " ";
560
+ ui.kv(
561
+ `${mark}${s.name || s.id}`,
562
+ `${s.status || "?"}${s.datacenter ? ` \xB7 ${s.datacenter}` : ""}`
563
+ );
564
+ }
565
+ }
566
+ ui.kv("Hosts", ctx.domains.map((d) => d.name).join(", ") || "(none)");
567
+ ui.line();
568
+ } catch (err) {
569
+ if (isAuthError(err)) fail("Session expired. Run `roxyon login` again.", EXIT.authRequired);
570
+ throw err;
571
+ }
572
+ }
573
+
574
+ // src/index.ts
575
+ var VERSION = "0.1.0";
576
+ var program = new Command();
577
+ program.name("roxyon").description("Deploy LumenJS apps and web projects to Roxyon infrastructure.").version(VERSION, "-v, --version");
578
+ program.command("login").description("Sign in to Roxyon (email + password, with OTP if required)").option("--email <email>").option("--password <password>").option("--otp <code>", "one-time code, for non-interactive step-up").option("--token <token>", "use a session/personal token directly (CI)").action((opts) => run(() => login(opts)));
579
+ program.command("logout").description("Remove stored credentials").action(() => run(logout));
580
+ program.command("whoami").description("Show the signed-in user and subscriptions").action(() => run(whoami));
581
+ program.command("init").description("Create roxyon.json for this project").option("--host <host>", "route host").option("--folder <folder>", "sub-path / source folder").option("--runtime <runtime>", "lumen | node | python | php").option("-y, --yes", "accept detected defaults, no prompts").action((opts) => run(() => init(opts)));
582
+ program.command("deploy").description("Build (if configured) and deploy this project").option("--no-build", "skip the configured build command").option("--no-follow", "return once the build is queued (app runtimes)").option("--lines <n>", "log lines to stream while building", (v) => Number.parseInt(v, 10)).action((opts) => run(() => deploy(opts)));
583
+ program.command("logs").description("Show application logs").option("-f, --follow", "stream new lines").option("--lines <n>", "how many lines", (v) => Number.parseInt(v, 10)).option("--app <id>", "application id (defaults to roxyon.json)").action((opts) => run(() => logs(opts)));
584
+ var env = program.command("env").description("Manage application environment variables");
585
+ env.command("pull").description("Print the current environment as KEY=value lines").option("--app <id>").action((opts) => run(() => envPull(opts)));
586
+ env.command("set <pairs...>").description("Set one or more KEY=value pairs").option("--app <id>").action((pairs, opts) => run(() => envSet(pairs, opts)));
587
+ env.command("rm <keys...>").description("Remove one or more variables").option("--app <id>").action((keys, opts) => run(() => envRm(keys, opts)));
588
+ program.command("restart").description("Restart the application process (no rebuild)").option("--app <id>").action((opts) => run(() => restart(opts)));
589
+ program.command("open").description("Open the project's URL in a browser").action(() => run(open));
590
+ var token = program.command("token").description("Manage personal access tokens (for CI and the MCP server)");
591
+ token.command("create <name>").description("Create a token \u2014 prints the secret once").option("--scopes <list>", "comma-separated: deploy,logs,read", "deploy,logs").option("--expires <days>", "expire after N days", (v) => Number.parseInt(v, 10)).action((name, opts) => run(() => tokenCreate(name, opts)));
592
+ token.command("list").description("List your tokens").action(() => run(tokenList));
593
+ token.command("revoke <id>").description("Revoke a token").action((id) => run(() => tokenRevoke(id)));
594
+ program.command("link [repoUrl]").description("Connect a git remote for push-to-deploy").option("--branch <branch>", "branch to deploy", "main").option("--app <id>").action((repoUrl, opts) => run(() => link(repoUrl, opts)));
595
+ program.parseAsync().catch((err) => {
596
+ ui.error(err instanceof Error ? err.message : String(err));
597
+ process.exit(EXIT.failure);
598
+ });
599
+ async function run(fn) {
600
+ try {
601
+ await fn();
602
+ } catch (err) {
603
+ if (err instanceof CliError) {
604
+ ui.error(err.message);
605
+ process.exit(err.exitCode);
606
+ }
607
+ ui.error(err instanceof Error ? err.message : String(err));
608
+ process.exit(EXIT.failure);
609
+ }
610
+ }
611
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/deploy.ts","../src/context.ts","../src/config.ts","../src/ui.ts","../src/commands/env.ts","../src/commands/init.ts","../src/commands/login.ts","../src/commands/logs.ts","../src/commands/misc.ts","../src/commands/token.ts","../src/commands/whoami.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { deploy } from './commands/deploy.js';\nimport { envPull, envRm, envSet } from './commands/env.js';\nimport { init } from './commands/init.js';\nimport { login, logout } from './commands/login.js';\nimport { logs } from './commands/logs.js';\nimport { link, open, restart } from './commands/misc.js';\nimport { tokenCreate, tokenList, tokenRevoke } from './commands/token.js';\nimport { whoami } from './commands/whoami.js';\nimport { CliError, EXIT, ui } from './ui.js';\n\nconst VERSION = '0.1.0';\n\nconst program = new Command();\n\nprogram\n .name('roxyon')\n .description('Deploy LumenJS apps and web projects to Roxyon infrastructure.')\n .version(VERSION, '-v, --version');\n\nprogram\n .command('login')\n .description('Sign in to Roxyon (email + password, with OTP if required)')\n .option('--email <email>')\n .option('--password <password>')\n .option('--otp <code>', 'one-time code, for non-interactive step-up')\n .option('--token <token>', 'use a session/personal token directly (CI)')\n .action((opts) => run(() => login(opts)));\n\nprogram\n .command('logout')\n .description('Remove stored credentials')\n .action(() => run(logout));\n\nprogram\n .command('whoami')\n .description('Show the signed-in user and subscriptions')\n .action(() => run(whoami));\n\nprogram\n .command('init')\n .description('Create roxyon.json for this project')\n .option('--host <host>', 'route host')\n .option('--folder <folder>', 'sub-path / source folder')\n .option('--runtime <runtime>', 'lumen | node | python | php')\n .option('-y, --yes', 'accept detected defaults, no prompts')\n .action((opts) => run(() => init(opts)));\n\nprogram\n .command('deploy')\n .description('Build (if configured) and deploy this project')\n .option('--no-build', 'skip the configured build command')\n .option('--no-follow', 'return once the build is queued (app runtimes)')\n .option('--lines <n>', 'log lines to stream while building', (v) => Number.parseInt(v, 10))\n .action((opts) => run(() => deploy(opts)));\n\nprogram\n .command('logs')\n .description('Show application logs')\n .option('-f, --follow', 'stream new lines')\n .option('--lines <n>', 'how many lines', (v) => Number.parseInt(v, 10))\n .option('--app <id>', 'application id (defaults to roxyon.json)')\n .action((opts) => run(() => logs(opts)));\n\nconst env = program.command('env').description('Manage application environment variables');\nenv\n .command('pull')\n .description('Print the current environment as KEY=value lines')\n .option('--app <id>')\n .action((opts) => run(() => envPull(opts)));\nenv\n .command('set <pairs...>')\n .description('Set one or more KEY=value pairs')\n .option('--app <id>')\n .action((pairs, opts) => run(() => envSet(pairs, opts)));\nenv\n .command('rm <keys...>')\n .description('Remove one or more variables')\n .option('--app <id>')\n .action((keys, opts) => run(() => envRm(keys, opts)));\n\nprogram\n .command('restart')\n .description('Restart the application process (no rebuild)')\n .option('--app <id>')\n .action((opts) => run(() => restart(opts)));\n\nprogram\n .command('open')\n .description(\"Open the project's URL in a browser\")\n .action(() => run(open));\n\nconst token = program\n .command('token')\n .description('Manage personal access tokens (for CI and the MCP server)');\ntoken\n .command('create <name>')\n .description('Create a token — prints the secret once')\n .option('--scopes <list>', 'comma-separated: deploy,logs,read', 'deploy,logs')\n .option('--expires <days>', 'expire after N days', (v) => Number.parseInt(v, 10))\n .action((name, opts) => run(() => tokenCreate(name, opts)));\ntoken\n .command('list')\n .description('List your tokens')\n .action(() => run(tokenList));\ntoken\n .command('revoke <id>')\n .description('Revoke a token')\n .action((id) => run(() => tokenRevoke(id)));\n\nprogram\n .command('link [repoUrl]')\n .description('Connect a git remote for push-to-deploy')\n .option('--branch <branch>', 'branch to deploy', 'main')\n .option('--app <id>')\n .action((repoUrl, opts) => run(() => link(repoUrl, opts)));\n\nprogram.parseAsync().catch((err) => {\n ui.error(err instanceof Error ? err.message : String(err));\n process.exit(EXIT.failure);\n});\n\nasync function run(fn: () => Promise<void>): Promise<void> {\n try {\n await fn();\n } catch (err) {\n if (err instanceof CliError) {\n ui.error(err.message);\n process.exit(err.exitCode);\n }\n ui.error(err instanceof Error ? err.message : String(err));\n process.exit(EXIT.failure);\n }\n}\n","import * as p from '@clack/prompts';\nimport { DeployError, deployProject } from '@roxyon/deploy-core';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport interface DeployOptions {\n follow?: boolean;\n build?: boolean;\n lines?: number;\n}\n\nconst CODE: Record<DeployError['code'], number> = {\n 'no-config': EXIT.configError,\n auth: EXIT.authRequired,\n 'bad-host': EXIT.configError,\n 'build-failed': EXIT.failure,\n 'no-build-output': EXIT.failure,\n 'upload-failed': EXIT.failure,\n 'create-failed': EXIT.failure,\n};\n\nexport async function deploy(opts: DeployOptions): Promise<void> {\n const { roxyon, credentials } = await requireSession();\n const spin = p.spinner();\n let spinning = false;\n\n const reporter = {\n step: (msg: string) => {\n if (spinning) spin.message(msg);\n else {\n spin.start(msg);\n spinning = true;\n }\n },\n log: (line: string) => ui.dim(` ${line}`),\n };\n\n try {\n const outcome = await deployProject({\n cwd: process.cwd(),\n roxyon,\n preferredSubscription: credentials?.subscription,\n build: opts.build,\n follow: opts.follow,\n reporter,\n });\n\n if (outcome.kind === 'static' && outcome.ok) {\n spin.stop('Deployed.');\n ui.success(`${outcome.url} is live (${outcome.files} files).`);\n return;\n }\n if (outcome.kind === 'app' && outcome.ok) {\n spin.stop(\n outcome.status === 'running'\n ? `Live — revision ${outcome.revision} running.`\n : `Build queued (revision ${outcome.revision}).`,\n );\n if (outcome.url) ui.success(outcome.url);\n return;\n }\n // app, not ok\n spin.stop('Deploy failed.');\n if ('error' in outcome && outcome.error) {\n ui.line();\n ui.error(outcome.error);\n }\n process.exitCode = EXIT.failure;\n } catch (err) {\n if (spinning) spin.stop('Failed.');\n if (err instanceof DeployError) fail(err.message, CODE[err.code]);\n throw err;\n }\n}\n","import { Roxyon } from '@roxyon/api-client';\nimport { type Credentials, endpointsFromEnv, loadCredentials, tokenFromEnv } from './config.js';\nimport { EXIT, fail } from './ui.js';\n\nexport interface Session {\n roxyon: Roxyon;\n /** Present when authenticated via a stored login (not a bare CI token). */\n credentials?: Credentials;\n}\n\n/** Build a client from a `ROXYON_TOKEN` env var or the stored login. Fails if neither. */\nexport async function requireSession(): Promise<Session> {\n const { apiUrl, consoleUrl } = endpointsFromEnv();\n const envToken = tokenFromEnv();\n if (envToken) {\n return {\n roxyon: new Roxyon({ sessionToken: envToken, baseUrl: apiUrl, consoleUrl: consoleUrl }),\n };\n }\n\n const creds = await loadCredentials();\n if (!creds?.sessionToken) {\n fail('Not signed in. Run `roxyon login` (or set ROXYON_TOKEN for CI).', EXIT.authRequired);\n }\n return {\n roxyon: new Roxyon({ sessionToken: creds.sessionToken, baseUrl: apiUrl, consoleUrl }),\n credentials: creds,\n };\n}\n\n/** A client with no user auth — for the login flow itself. */\nexport function anonymousRoxyon(): Roxyon {\n const { apiUrl, consoleUrl } = endpointsFromEnv();\n return new Roxyon({ baseUrl: apiUrl, consoleUrl });\n}\n","/**\n * All config lives in `@roxyon/deploy-core` so the CLI and the MCP server share\n * one implementation. This module just re-exports it.\n */\nexport {\n buildProjectConfig,\n clearCredentials,\n type Credentials,\n credentialsPath,\n type Endpoints,\n endpointsFromEnv,\n loadCredentials,\n loadProjectConfig,\n type ProjectConfig,\n projectConfigPath,\n saveCredentials,\n saveProjectConfig,\n tokenFromEnv,\n} from '@roxyon/deploy-core';\n","import pc from 'picocolors';\n\nexport const ui = {\n info: (msg: string) => process.stdout.write(`${pc.cyan('›')} ${msg}\\n`),\n success: (msg: string) => process.stdout.write(`${pc.green('✓')} ${msg}\\n`),\n warn: (msg: string) => process.stderr.write(`${pc.yellow('!')} ${msg}\\n`),\n error: (msg: string) => process.stderr.write(`${pc.red('✗')} ${msg}\\n`),\n line: (msg = '') => process.stdout.write(`${msg}\\n`),\n dim: (msg: string) => process.stdout.write(`${pc.dim(msg)}\\n`),\n kv: (key: string, value: string) =>\n process.stdout.write(` ${pc.dim(key.padEnd(14))} ${value}\\n`),\n};\n\n/** Exit codes — kept stable for scripting / CI. */\nexport const EXIT = {\n ok: 0,\n failure: 1,\n authRequired: 2,\n configError: 3,\n} as const;\n\nexport class CliError extends Error {\n readonly exitCode: number;\n constructor(message: string, exitCode: number = EXIT.failure) {\n super(message);\n this.name = 'CliError';\n this.exitCode = exitCode;\n }\n}\n\nexport function fail(message: string, exitCode: number = EXIT.failure): never {\n throw new CliError(message, exitCode);\n}\n","import { RoxyonApiError, formatEnv } from '@roxyon/api-client';\nimport { loadProjectConfig } from '../config.js';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nasync function resolveApp(explicit?: string): Promise<string> {\n const appId = explicit ?? (await loadProjectConfig())?.application;\n if (!appId)\n fail('No application. Pass --app <id> or deploy this project first.', EXIT.configError);\n return appId;\n}\n\nexport async function envPull(opts: { app?: string }): Promise<void> {\n const appId = await resolveApp(opts.app);\n const { roxyon } = await requireSession();\n try {\n const env = await roxyon.applications.getEnv(appId);\n ui.line(formatEnv(env) || '# (no environment variables set)');\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nexport async function envSet(pairs: string[], opts: { app?: string }): Promise<void> {\n if (pairs.length === 0)\n fail('Nothing to set. Usage: roxyon env set KEY=value [KEY2=value2]', EXIT.configError);\n const appId = await resolveApp(opts.app);\n\n const set: Record<string, string> = {};\n for (const pair of pairs) {\n const eq = pair.indexOf('=');\n if (eq < 1) fail(`\"${pair}\" is not KEY=value.`, EXIT.configError);\n const key = pair.slice(0, eq).trim();\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) fail(`Invalid env key \"${key}\".`, EXIT.configError);\n if (key === 'PORT' || key === 'HOST') {\n ui.warn(`${key} is set by the platform — ignoring.`);\n continue;\n }\n set[key] = pair.slice(eq + 1);\n }\n\n const { roxyon } = await requireSession();\n try {\n const r = await roxyon.applications.setEnv(appId, { set });\n ui.success(`Updated ${r.changed.join(', ') || 'nothing'}. Run \\`roxyon deploy\\` to apply.`);\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nexport async function envRm(keys: string[], opts: { app?: string }): Promise<void> {\n if (keys.length === 0)\n fail('Nothing to remove. Usage: roxyon env rm KEY [KEY2]', EXIT.configError);\n const appId = await resolveApp(opts.app);\n const { roxyon } = await requireSession();\n try {\n const r = await roxyon.applications.setEnv(appId, { remove: keys });\n ui.success(`Removed ${r.changed.join(', ') || 'nothing'}. Run \\`roxyon deploy\\` to apply.`);\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n","import { basename } from 'node:path';\nimport * as p from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport type { RuntimeName } from '@roxyon/api-client';\nimport {\n buildProjectConfig,\n detectRuntime,\n loadProjectConfig,\n saveProjectConfig,\n} from '@roxyon/deploy-core';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport interface InitOptions {\n host?: string;\n folder?: string;\n runtime?: RuntimeName;\n yes?: boolean;\n}\n\nexport async function init(opts: InitOptions): Promise<void> {\n const cwd = process.cwd();\n const existing = await loadProjectConfig(cwd);\n if (existing && !opts.yes) {\n const overwrite = await ask(() =>\n p.confirm({ message: 'roxyon.json already exists — overwrite it?', initialValue: false }),\n );\n if (!overwrite) return;\n }\n\n const { roxyon, credentials } = await requireSession();\n const user = await roxyon.auth.me();\n const sub = await roxyon.subscriptions.resolve(user.objectId, credentials?.subscription);\n const domains = await roxyon.domains.list(sub.objectId);\n if (domains.length === 0) {\n fail(\n 'This subscription has no hosts yet. Add a domain in the console, then run `roxyon init` again.',\n EXIT.configError,\n );\n }\n\n const detected = await detectRuntime(cwd);\n ui.info(`Detected runtime: ${detected.runtime} — ${detected.reason}`);\n\n const runtime: RuntimeName = opts.runtime\n ? opts.runtime\n : opts.yes\n ? detected.runtime\n : await ask<RuntimeName>(() =>\n p.select({\n message: 'Runtime',\n initialValue: detected.runtime,\n options: [\n { value: 'lumen', label: 'LumenJS (static SPA, no server)' },\n { value: 'node', label: 'Node.js app' },\n { value: 'python', label: 'Python app' },\n { value: 'php', label: 'PHP app' },\n ],\n }),\n );\n\n const host: string =\n opts.host ??\n (opts.yes\n ? (domains[0]?.Name ?? fail('No host available.', EXIT.configError))\n : await ask<string>(() =>\n p.select({\n message: 'Host',\n options: domains.map((d) => ({ value: d.Name, label: d.Name })),\n }),\n ));\n\n const defaultFolder = basename(cwd).replace(/[^a-z0-9._-]/gi, '-');\n const folder =\n opts.folder ??\n (opts.yes\n ? runtime === 'lumen'\n ? ''\n : defaultFolder\n : await ask<string>(() =>\n p.text({\n message:\n runtime === 'lumen'\n ? 'Sub-path under the host root (blank = site root)'\n : 'Folder under the host (source location)',\n placeholder: runtime === 'lumen' ? '(root)' : defaultFolder,\n defaultValue: runtime === 'lumen' ? '' : defaultFolder,\n }),\n ));\n\n const config = buildProjectConfig({\n cwd,\n runtime,\n host,\n folder,\n name: basename(cwd),\n start: detected.start,\n });\n\n const path = await saveProjectConfig(config, cwd);\n ui.success(`Wrote ${path}`);\n ui.line();\n ui.dim(JSON.stringify(config, null, 2));\n ui.line();\n ui.info('Next: `roxyon deploy`');\n}\n\nasync function ask<T>(fn: () => Promise<T | symbol>): Promise<T> {\n const v = await fn();\n if (isCancel(v)) {\n p.cancel('Cancelled.');\n process.exit(EXIT.ok);\n }\n return v as T;\n}\n","import * as p from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport { RoxyonApiError } from '@roxyon/api-client';\nimport { type Credentials, clearCredentials, loadCredentials, saveCredentials } from '../config.js';\nimport { anonymousRoxyon } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport interface LoginOptions {\n email?: string;\n password?: string;\n token?: string;\n otp?: string;\n}\n\nexport async function login(opts: LoginOptions): Promise<void> {\n const roxyon = anonymousRoxyon();\n\n // Non-interactive: a token was handed in directly.\n if (opts.token) {\n roxyon.client.setSessionToken(opts.token);\n const user = await roxyon.auth.me().catch((e) => {\n fail(`That token did not authenticate: ${(e as Error).message}`, EXIT.authRequired);\n });\n await persist({ sessionToken: opts.token, userId: user.objectId, email: user.Email });\n ui.success(`Signed in as ${user.Email ?? user.objectId} (via --token).`);\n return;\n }\n\n const interactive = process.stdin.isTTY && !opts.password;\n\n const email =\n opts.email ??\n (interactive\n ? await prompt(() =>\n p.text({\n message: 'Email',\n validate: (v) => (v.includes('@') ? undefined : 'Enter an email'),\n }),\n )\n : fail(\n 'Provide --email (and --password), or run in an interactive terminal.',\n EXIT.configError,\n ));\n\n const password =\n opts.password ??\n (interactive\n ? await prompt(() => p.password({ message: 'Password' }))\n : fail('Provide --password.', EXIT.configError));\n\n const spin = p.spinner();\n spin.start('Signing in');\n try {\n let result = await roxyon.auth.login(email, password, { via: 'cli' });\n\n if (result.stepUpRequired) {\n spin.stop('A one-time code was sent to you.');\n const code =\n opts.otp ??\n (interactive\n ? await prompt(() => p.text({ message: 'One-time code' }))\n : fail('This account requires OTP — pass --otp <code>.', EXIT.authRequired));\n spin.start('Verifying code');\n result = await roxyon.auth.verifyOtp(result.challengeId ?? '', code);\n }\n\n roxyon.client.setSessionToken(result.sessionToken);\n const user = await roxyon.auth.me();\n await persist({\n sessionToken: result.sessionToken,\n refreshToken: result.refreshToken,\n userId: user.objectId,\n email: user.Email,\n });\n spin.stop(`Signed in as ${user.Email ?? user.objectId}.`);\n ui.dim('Token stored in ~/.roxyon/config.json (permissions 0600).');\n } catch (err) {\n spin.stop('Sign-in failed.');\n if (err instanceof RoxyonApiError) fail(err.message, EXIT.authRequired);\n throw err;\n }\n}\n\nexport async function logout(): Promise<void> {\n const creds = await loadCredentials();\n await clearCredentials();\n ui.success(creds ? `Signed out ${creds.email ?? creds.userId}.` : 'Nothing to sign out.');\n}\n\nasync function persist(partial: Omit<Credentials, 'savedAt'>): Promise<void> {\n await saveCredentials({ ...partial, savedAt: new Date().toISOString() });\n}\n\nasync function prompt<T>(fn: () => Promise<T | symbol>): Promise<T> {\n const value = await fn();\n if (isCancel(value)) {\n p.cancel('Cancelled.');\n process.exit(EXIT.ok);\n }\n return value as T;\n}\n","import { RoxyonApiError } from '@roxyon/api-client';\nimport { loadProjectConfig } from '../config.js';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport interface LogsOptions {\n follow?: boolean;\n lines?: number;\n app?: string;\n}\n\nexport async function logs(opts: LogsOptions): Promise<void> {\n const appId = opts.app ?? (await loadProjectConfig())?.application;\n if (!appId) {\n fail(\n 'No application. Pass --app <id>, or run from a project whose roxyon.json has one.',\n EXIT.configError,\n );\n }\n\n const { roxyon } = await requireSession();\n const lines = opts.lines ?? 100;\n\n if (!opts.follow) {\n for (const l of await fetchLogs(\n roxyon.applications.logs.bind(roxyon.applications),\n appId,\n lines,\n )) {\n ui.line(l);\n }\n return;\n }\n\n ui.dim(`Following ${appId} — Ctrl-C to stop.`);\n let seen = new Set<string>();\n const tick = async () => {\n try {\n for (const l of await roxyon.applications.logs(appId, lines)) {\n if (l && !seen.has(l)) {\n seen.add(l);\n ui.line(l);\n }\n }\n if (seen.size > 4000) seen = new Set([...seen].slice(-2000));\n } catch (err) {\n ui.warn((err as Error).message);\n }\n };\n await tick();\n const timer = setInterval(tick, 7000);\n process.on('SIGINT', () => {\n clearInterval(timer);\n process.exit(EXIT.ok);\n });\n}\n\nasync function fetchLogs(\n fn: (id: string, n: number) => Promise<string[]>,\n appId: string,\n lines: number,\n): Promise<string[]> {\n try {\n return await fn(appId, lines);\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n","import { spawn } from 'node:child_process';\nimport { platform } from 'node:os';\nimport * as p from '@clack/prompts';\nimport { isCancel } from '@clack/prompts';\nimport { RoxyonApiError } from '@roxyon/api-client';\nimport { loadProjectConfig, saveProjectConfig } from '../config.js';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport async function restart(opts: { app?: string }): Promise<void> {\n const appId = opts.app ?? (await loadProjectConfig())?.application;\n if (!appId)\n fail('No application. Pass --app <id> or deploy this project first.', EXIT.configError);\n const { roxyon } = await requireSession();\n try {\n await roxyon.applications.restart(appId);\n ui.success('Restart queued.');\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nexport async function open(): Promise<void> {\n const config = await loadProjectConfig();\n if (!config) fail('No roxyon.json here.', EXIT.configError);\n const url = `https://${config.host}${config.folder ? `/${config.folder}` : ''}`;\n const cmd = platform() === 'darwin' ? 'open' : platform() === 'win32' ? 'start' : 'xdg-open';\n spawn(cmd, [url], { stdio: 'ignore', detached: true, shell: platform() === 'win32' }).unref();\n ui.info(`Opening ${url}`);\n}\n\nexport async function link(\n repoUrl: string | undefined,\n opts: { branch?: string; app?: string },\n): Promise<void> {\n const config = await loadProjectConfig();\n const appId = opts.app ?? config?.application;\n if (!appId)\n fail(\n 'Deploy the project once first so an application exists, then `roxyon link`.',\n EXIT.configError,\n );\n\n const url =\n repoUrl ??\n (process.stdin.isTTY\n ? await ask(() =>\n p.text({ message: 'Git remote (SSH URL)', placeholder: 'git@github.com:you/app.git' }),\n )\n : fail('Pass the git remote URL: roxyon link <url>', EXIT.configError));\n const branch = opts.branch ?? 'main';\n\n const { roxyon } = await requireSession();\n try {\n const res = await roxyon.applications.repoConnect(appId, url, branch);\n ui.success(`Connected ${url} (${branch}).`);\n ui.line();\n if (res.deployKey) {\n ui.info('Add this deploy key to the repo (read-only is enough):');\n ui.line(res.deployKey);\n ui.line();\n }\n if (res.webhookUrl) {\n ui.info('Add a push webhook:');\n ui.kv('URL', res.webhookUrl);\n if (res.secret) ui.kv('Secret', res.secret);\n ui.line();\n }\n if (res.hint) ui.dim(res.hint);\n if (config) {\n // Record the remote so `roxyon deploy` can note that pushes auto-deploy.\n await saveProjectConfig({ ...config }, process.cwd());\n }\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nasync function ask<T>(fn: () => Promise<T | symbol>): Promise<T> {\n const v = await fn();\n if (isCancel(v)) {\n p.cancel('Cancelled.');\n process.exit(EXIT.ok);\n }\n return v as T;\n}\n","import { RoxyonApiError, type TokenScope } from '@roxyon/api-client';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nconst SCOPES: TokenScope[] = ['deploy', 'logs', 'read'];\n\nexport async function tokenCreate(\n name: string | undefined,\n opts: { scopes?: string; expires?: number },\n): Promise<void> {\n if (!name) fail('A name is required: roxyon token create <name>', EXIT.configError);\n\n const scopes = (opts.scopes ? opts.scopes.split(',') : ['deploy', 'logs'])\n .map((s) => s.trim().toLowerCase())\n .filter((s): s is TokenScope => (SCOPES as string[]).includes(s));\n if (scopes.length === 0)\n fail(`--scopes must be a subset of: ${SCOPES.join(', ')}`, EXIT.configError);\n\n const { roxyon } = await requireSession();\n try {\n const t = await roxyon.tokens.create(name, {\n scopes,\n expiresInDays: opts.expires && opts.expires > 0 ? opts.expires : undefined,\n });\n ui.success(\n `Created \"${t.name}\" (${t.scopes.join(', ')})${t.expiresAt ? ` · expires ${t.expiresAt.slice(0, 10)}` : ''}`,\n );\n ui.line();\n ui.line(t.token);\n ui.line();\n ui.dim(\n 'Copy it now — it will not be shown again. Use it as ROXYON_TOKEN in CI or the MCP server.',\n );\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nexport async function tokenList(): Promise<void> {\n const { roxyon } = await requireSession();\n try {\n const tokens = await roxyon.tokens.list();\n if (tokens.length === 0) {\n ui.info('No personal access tokens.');\n return;\n }\n ui.line();\n for (const t of tokens) {\n ui.kv(\n t.name,\n `${t.prefix}… · ${t.scopes.join(',')}` +\n `${t.expiresAt ? ` · expires ${t.expiresAt.slice(0, 10)}` : ''}` +\n `${t.lastUsedAt ? ` · used ${t.lastUsedAt.slice(0, 10)}` : ' · never used'}`,\n );\n ui.dim(` id ${t.id}`);\n }\n ui.line();\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n\nexport async function tokenRevoke(id: string | undefined): Promise<void> {\n if (!id) fail('A token id is required: roxyon token revoke <id>', EXIT.configError);\n const { roxyon } = await requireSession();\n try {\n await roxyon.tokens.revoke(id);\n ui.success(`Revoked ${id}.`);\n } catch (err) {\n if (err instanceof RoxyonApiError) fail(err.message);\n throw err;\n }\n}\n","import { isAuthError } from '@roxyon/api-client';\nimport { requireSession } from '../context.js';\nimport { EXIT, fail, ui } from '../ui.js';\n\nexport async function whoami(): Promise<void> {\n const { roxyon, credentials } = await requireSession();\n try {\n // One call — works with a session token or a PAT.\n const ctx = await roxyon.account.context();\n ui.line();\n ui.kv('User', ctx.user.email || ctx.user.id);\n ui.kv('User ID', ctx.user.id);\n if (credentials?.sessionToken?.startsWith('roxp_')) {\n ui.kv('Auth', `personal access token (${ctx.scopes.join(', ') || 'no scopes'})`);\n }\n if (ctx.subscriptions.length === 0) {\n ui.kv('Subscription', '(none — add a plan in the console)');\n } else {\n const active =\n (credentials?.subscription &&\n ctx.subscriptions.find(\n (s) => s.id === credentials.subscription || s.name === credentials.subscription,\n )) ||\n ctx.subscriptions.find((s) => s.status === 'active') ||\n ctx.subscriptions[0];\n for (const s of ctx.subscriptions) {\n const mark = s.id === active?.id ? '→ ' : ' ';\n ui.kv(\n `${mark}${s.name || s.id}`,\n `${s.status || '?'}${s.datacenter ? ` · ${s.datacenter}` : ''}`,\n );\n }\n }\n ui.kv('Hosts', ctx.domains.map((d) => d.name).join(', ') || '(none)');\n ui.line();\n } catch (err) {\n if (isAuthError(err)) fail('Session expired. Run `roxyon login` again.', EXIT.authRequired);\n throw err;\n }\n}\n"],"mappings":"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","names":["env","p","buildProjectConfig","loadProjectConfig","saveProjectConfig","loadProjectConfig","buildProjectConfig","saveProjectConfig","p","isCancel","RoxyonApiError","password","RoxyonApiError","isCancel","RoxyonApiError","RoxyonApiError","p","isCancel","RoxyonApiError","RoxyonApiError","ask","isCancel","RoxyonApiError","RoxyonApiError"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@roxyon/cli",
3
+ "version": "0.1.0",
4
+ "description": "Deploy LumenJS apps and web projects to Roxyon infrastructure from anywhere — including AI assistants.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "roxyon": "./bin/roxyon.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "bin",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
18
+ "keywords": [
19
+ "roxyon",
20
+ "lumenjs",
21
+ "deploy",
22
+ "baas",
23
+ "paas",
24
+ "cli"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/bea-technology/roxyon-devtools.git",
29
+ "directory": "packages/cli"
30
+ },
31
+ "dependencies": {
32
+ "@clack/prompts": "^0.7.0",
33
+ "commander": "^12.1.0",
34
+ "picocolors": "^1.1.1",
35
+ "@roxyon/api-client": "0.1.0",
36
+ "@roxyon/deploy-core": "0.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.16.11",
40
+ "tsup": "^8.3.0",
41
+ "typescript": "^5.6.3",
42
+ "vitest": "^2.1.3"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "dev": "node --experimental-strip-types src/index.ts"
52
+ }
53
+ }