@omg-dev/cli 0.4.31 → 0.4.33
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/dist/omg.mjs +248 -6
- package/package.json +1 -1
package/dist/omg.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
import { readFileSync as
|
|
6
|
-
import { join as
|
|
5
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
6
|
+
import { join as join6, resolve as resolve3, basename, dirname as dirname3 } from "path";
|
|
7
7
|
|
|
8
8
|
// src/config.ts
|
|
9
9
|
import { homedir } from "os";
|
|
@@ -446,6 +446,39 @@ function getStatus(token, slug) {
|
|
|
446
446
|
function listApps(token) {
|
|
447
447
|
return controlPlane("/api/cli/apps/list", token);
|
|
448
448
|
}
|
|
449
|
+
function getVisibility(token, slug) {
|
|
450
|
+
return controlPlane(`/api/cli/apps/visibility?slug=${encodeURIComponent(slug)}`, token);
|
|
451
|
+
}
|
|
452
|
+
function setVisibility(token, slug, visibility) {
|
|
453
|
+
return controlPlane("/api/cli/apps/visibility", token, {
|
|
454
|
+
method: "POST",
|
|
455
|
+
body: JSON.stringify({ slug, visibility })
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function envList(token, slug) {
|
|
459
|
+
return controlPlane(`/api/cli/env/list?slug=${encodeURIComponent(slug)}`, token);
|
|
460
|
+
}
|
|
461
|
+
function envPull(token, slug) {
|
|
462
|
+
return controlPlane(`/api/cli/env/pull?slug=${encodeURIComponent(slug)}`, token);
|
|
463
|
+
}
|
|
464
|
+
function envSet(token, slug, vars) {
|
|
465
|
+
return controlPlane("/api/cli/env/set", token, {
|
|
466
|
+
method: "POST",
|
|
467
|
+
body: JSON.stringify({ slug, vars })
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function envRemove(token, slug, keys) {
|
|
471
|
+
return controlPlane("/api/cli/env/rm", token, {
|
|
472
|
+
method: "POST",
|
|
473
|
+
body: JSON.stringify({ slug, keys })
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
function envImport(token, slug, contents) {
|
|
477
|
+
return controlPlane("/api/cli/env/import", token, {
|
|
478
|
+
method: "POST",
|
|
479
|
+
body: JSON.stringify({ slug, contents })
|
|
480
|
+
});
|
|
481
|
+
}
|
|
449
482
|
function getComputerConnectConfig(token) {
|
|
450
483
|
return controlPlane("/api/cli/computer/connect", token);
|
|
451
484
|
}
|
|
@@ -654,7 +687,7 @@ async function deploy(opts) {
|
|
|
654
687
|
if (collected.files.length === 0)
|
|
655
688
|
throw new Error(`No files to deploy in ${root}`);
|
|
656
689
|
for (const s of collected.skippedSecrets) {
|
|
657
|
-
onProgress(`skipping ${s} \u2014
|
|
690
|
+
onProgress(`skipping ${s} \u2014 import it with \`omg env push --file ${s}\``);
|
|
658
691
|
}
|
|
659
692
|
for (const s of collected.skippedLarge)
|
|
660
693
|
onProgress(`skipping ${s} \u2014 over the per-file limit`);
|
|
@@ -1306,6 +1339,190 @@ async function runDev(options) {
|
|
|
1306
1339
|
}
|
|
1307
1340
|
}
|
|
1308
1341
|
|
|
1342
|
+
// src/env.ts
|
|
1343
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync2 } from "fs";
|
|
1344
|
+
import { join as join5 } from "path";
|
|
1345
|
+
async function defaultReadStdin() {
|
|
1346
|
+
const chunks = [];
|
|
1347
|
+
for await (const chunk of process.stdin)
|
|
1348
|
+
chunks.push(chunk);
|
|
1349
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1350
|
+
}
|
|
1351
|
+
function formatList(vars, out) {
|
|
1352
|
+
if (!vars.length) {
|
|
1353
|
+
out("No env vars set.");
|
|
1354
|
+
out("");
|
|
1355
|
+
out(" omg env set KEY=value set one");
|
|
1356
|
+
out(" omg env push --file .env import a local .env");
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const width = Math.max(...vars.map((v) => v.key.length));
|
|
1360
|
+
for (const v of vars)
|
|
1361
|
+
out(` ${v.key.padEnd(width)} ${v.preview}`);
|
|
1362
|
+
}
|
|
1363
|
+
async function collectAssignments(args, readStdin) {
|
|
1364
|
+
const vars = {};
|
|
1365
|
+
for (let i = 0;i < args.length; i++) {
|
|
1366
|
+
const arg = args[i];
|
|
1367
|
+
const eq = arg.indexOf("=");
|
|
1368
|
+
if (eq > 0) {
|
|
1369
|
+
vars[arg.slice(0, eq)] = arg.slice(eq + 1);
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
if (args[i + 1] === "-") {
|
|
1373
|
+
vars[arg] = (await readStdin()).replace(/\n$/, "");
|
|
1374
|
+
i++;
|
|
1375
|
+
continue;
|
|
1376
|
+
}
|
|
1377
|
+
throw new Error(`Expected KEY=value (or \`${arg} -\` to read the value from stdin), got "${arg}"`);
|
|
1378
|
+
}
|
|
1379
|
+
return vars;
|
|
1380
|
+
}
|
|
1381
|
+
function toDotenv(env) {
|
|
1382
|
+
return Object.keys(env).sort().map((k) => {
|
|
1383
|
+
const v = env[k] ?? "";
|
|
1384
|
+
const needsQuotes = /[\s"'#$`\\]/.test(v) || v === "";
|
|
1385
|
+
return `${k}=${needsQuotes ? JSON.stringify(v) : v}`;
|
|
1386
|
+
}).join(`
|
|
1387
|
+
`) + `
|
|
1388
|
+
`;
|
|
1389
|
+
}
|
|
1390
|
+
async function runEnv(opts) {
|
|
1391
|
+
const { args, slug, token, root, flag, out } = opts;
|
|
1392
|
+
const readStdin = opts.readStdin ?? defaultReadStdin;
|
|
1393
|
+
const sub = args[0] ?? "list";
|
|
1394
|
+
const rest = args.slice(1);
|
|
1395
|
+
switch (sub) {
|
|
1396
|
+
case "list":
|
|
1397
|
+
case "ls": {
|
|
1398
|
+
const { vars } = await envList(token, slug);
|
|
1399
|
+
formatList(vars, out);
|
|
1400
|
+
return 0;
|
|
1401
|
+
}
|
|
1402
|
+
case "set": {
|
|
1403
|
+
if (!rest.length) {
|
|
1404
|
+
out("Usage: omg env set KEY=value [KEY2=value2 ...]");
|
|
1405
|
+
out(" omg env set KEY - read the value from stdin");
|
|
1406
|
+
return 1;
|
|
1407
|
+
}
|
|
1408
|
+
const vars = await collectAssignments(rest, readStdin);
|
|
1409
|
+
const res = await envSet(token, slug, vars);
|
|
1410
|
+
for (const k of res.created)
|
|
1411
|
+
out(` + ${k}`);
|
|
1412
|
+
for (const k of res.updated)
|
|
1413
|
+
out(` ~ ${k} (updated)`);
|
|
1414
|
+
out("");
|
|
1415
|
+
out("Applies on next `omg deploy`.");
|
|
1416
|
+
return 0;
|
|
1417
|
+
}
|
|
1418
|
+
case "rm":
|
|
1419
|
+
case "remove":
|
|
1420
|
+
case "unset": {
|
|
1421
|
+
if (!rest.length) {
|
|
1422
|
+
out("Usage: omg env rm KEY [KEY2 ...]");
|
|
1423
|
+
return 1;
|
|
1424
|
+
}
|
|
1425
|
+
const res = await envRemove(token, slug, rest);
|
|
1426
|
+
for (const k of res.removed)
|
|
1427
|
+
out(` - ${k}`);
|
|
1428
|
+
for (const k of res.missing)
|
|
1429
|
+
out(` ? ${k} was not set`);
|
|
1430
|
+
if (res.removed.length) {
|
|
1431
|
+
out("");
|
|
1432
|
+
out("Applies on next `omg deploy`.");
|
|
1433
|
+
}
|
|
1434
|
+
return res.removed.length === 0 && res.missing.length ? 1 : 0;
|
|
1435
|
+
}
|
|
1436
|
+
case "pull": {
|
|
1437
|
+
const target = join5(root, flag("out") ?? ".env");
|
|
1438
|
+
if (existsSync2(target) && !args.includes("--force")) {
|
|
1439
|
+
out(`${target} already exists. Re-run with --force to overwrite.`);
|
|
1440
|
+
return 1;
|
|
1441
|
+
}
|
|
1442
|
+
const { env } = await envPull(token, slug);
|
|
1443
|
+
const keys = Object.keys(env);
|
|
1444
|
+
if (!keys.length) {
|
|
1445
|
+
out(`No env vars set for ${slug} \u2014 nothing to pull.`);
|
|
1446
|
+
return 0;
|
|
1447
|
+
}
|
|
1448
|
+
writeFileSync4(target, toDotenv(env), { mode: 384 });
|
|
1449
|
+
out(`Wrote ${keys.length} var${keys.length === 1 ? "" : "s"} to ${target}`);
|
|
1450
|
+
out("Add it to .gitignore \u2014 it holds real secrets.");
|
|
1451
|
+
return 0;
|
|
1452
|
+
}
|
|
1453
|
+
case "push":
|
|
1454
|
+
case "import": {
|
|
1455
|
+
const source = join5(root, flag("file") ?? ".env");
|
|
1456
|
+
if (!existsSync2(source)) {
|
|
1457
|
+
out(`No file at ${source}. Point at one with --file <path>.`);
|
|
1458
|
+
return 1;
|
|
1459
|
+
}
|
|
1460
|
+
const res = await envImport(token, slug, readFileSync4(source, "utf8"));
|
|
1461
|
+
for (const k of res.created)
|
|
1462
|
+
out(` + ${k}`);
|
|
1463
|
+
for (const k of res.updated)
|
|
1464
|
+
out(` ~ ${k} (updated)`);
|
|
1465
|
+
out("");
|
|
1466
|
+
out("Applies on next `omg deploy`.");
|
|
1467
|
+
return 0;
|
|
1468
|
+
}
|
|
1469
|
+
default:
|
|
1470
|
+
out(`Unknown: omg env ${sub}`);
|
|
1471
|
+
out("");
|
|
1472
|
+
out(" omg env list (masked)");
|
|
1473
|
+
out(" omg env set KEY=value set one or many");
|
|
1474
|
+
out(" omg env rm KEY remove");
|
|
1475
|
+
out(" omg env pull [--out .env] write real values locally");
|
|
1476
|
+
out(" omg env push [--file .env] import a local .env");
|
|
1477
|
+
return 1;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// src/visibility.ts
|
|
1482
|
+
var PUBLIC = "public";
|
|
1483
|
+
var OMG_USERS = "omg-users";
|
|
1484
|
+
function parseVisibility(arg) {
|
|
1485
|
+
const v = arg.trim().toLowerCase();
|
|
1486
|
+
if (v === PUBLIC || v === "anyone" || v === "open")
|
|
1487
|
+
return PUBLIC;
|
|
1488
|
+
if (v === OMG_USERS || v === "omg" || v === "users" || v === "private")
|
|
1489
|
+
return OMG_USERS;
|
|
1490
|
+
return null;
|
|
1491
|
+
}
|
|
1492
|
+
function describeVisibility(v) {
|
|
1493
|
+
return v === PUBLIC ? "public \u2014 anyone with the link" : "omg-users \u2014 any signed-in omg user";
|
|
1494
|
+
}
|
|
1495
|
+
async function runVisibility(opts) {
|
|
1496
|
+
const { args, slug, token, out } = opts;
|
|
1497
|
+
const target = args[0];
|
|
1498
|
+
if (!target) {
|
|
1499
|
+
const { visibility: visibility2, published } = await getVisibility(token, slug);
|
|
1500
|
+
out(`${slug}: ${describeVisibility(visibility2)}`);
|
|
1501
|
+
if (!published) {
|
|
1502
|
+
out("");
|
|
1503
|
+
out("Not published yet \u2014 run `omg deploy` first.");
|
|
1504
|
+
} else if (visibility2 === OMG_USERS) {
|
|
1505
|
+
out("");
|
|
1506
|
+
out("Non-browser clients (mobile apps, webhooks) will be redirected to sign in.");
|
|
1507
|
+
out("Run `omg visibility public` to serve anyone with the link.");
|
|
1508
|
+
}
|
|
1509
|
+
return 0;
|
|
1510
|
+
}
|
|
1511
|
+
const next = parseVisibility(target);
|
|
1512
|
+
if (!next) {
|
|
1513
|
+
out(`Unknown visibility "${target}".`);
|
|
1514
|
+
out("");
|
|
1515
|
+
out(" omg visibility public anyone with the link");
|
|
1516
|
+
out(" omg visibility omg-users any signed-in omg user (default)");
|
|
1517
|
+
return 1;
|
|
1518
|
+
}
|
|
1519
|
+
const { visibility } = await setVisibility(token, slug, next);
|
|
1520
|
+
out(`${slug}: ${describeVisibility(visibility)}`);
|
|
1521
|
+
out("");
|
|
1522
|
+
out("Live now \u2014 the gate is enforced at the edge, no redeploy needed.");
|
|
1523
|
+
return 0;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1309
1526
|
// src/index.ts
|
|
1310
1527
|
var argv = process.argv.slice(2);
|
|
1311
1528
|
var cmd = argv[0] ?? "help";
|
|
@@ -1319,15 +1536,15 @@ var out = (msg = "") => process.stdout.write(msg + `
|
|
|
1319
1536
|
var step = (msg) => out(` ${msg}`);
|
|
1320
1537
|
function readLink(root) {
|
|
1321
1538
|
try {
|
|
1322
|
-
return JSON.parse(
|
|
1539
|
+
return JSON.parse(readFileSync5(join6(root, LINK_FILE), "utf8"));
|
|
1323
1540
|
} catch {
|
|
1324
1541
|
return null;
|
|
1325
1542
|
}
|
|
1326
1543
|
}
|
|
1327
1544
|
function writeLink(root, link) {
|
|
1328
|
-
const path =
|
|
1545
|
+
const path = join6(root, LINK_FILE);
|
|
1329
1546
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
1330
|
-
|
|
1547
|
+
writeFileSync5(path, JSON.stringify(link, null, 2) + `
|
|
1331
1548
|
`);
|
|
1332
1549
|
}
|
|
1333
1550
|
var HELP = `omg \u2014 deploy a local project to omg.dev
|
|
@@ -1335,6 +1552,12 @@ var HELP = `omg \u2014 deploy a local project to omg.dev
|
|
|
1335
1552
|
omg create <name> [--no-install]
|
|
1336
1553
|
omg deploy [--name <name>] [--dir <path>] [--no-wait]
|
|
1337
1554
|
omg status [--dir <path>]
|
|
1555
|
+
omg env list env vars (masked)
|
|
1556
|
+
omg env set KEY=value [KEY2=v2 ...] set (KEY - reads stdin)
|
|
1557
|
+
omg env rm KEY [KEY2 ...] remove
|
|
1558
|
+
omg env pull [--out .env] [--force] write real values locally
|
|
1559
|
+
omg env push [--file .env] import a local .env
|
|
1560
|
+
omg visibility [public|omg-users] the published app's access gate
|
|
1338
1561
|
omg link <slug> [--dir <path>]
|
|
1339
1562
|
omg login [--token <omg_sk_...>]
|
|
1340
1563
|
omg logout
|
|
@@ -1435,6 +1658,25 @@ async function main() {
|
|
|
1435
1658
|
out(`${link.slug}: ${status}`);
|
|
1436
1659
|
return 0;
|
|
1437
1660
|
}
|
|
1661
|
+
case "env": {
|
|
1662
|
+
const token = await requireToken();
|
|
1663
|
+
const link = readLink(root);
|
|
1664
|
+
if (!link?.slug) {
|
|
1665
|
+
out("Not linked to an app. Run `omg deploy` or `omg link <slug>` first.");
|
|
1666
|
+
return 1;
|
|
1667
|
+
}
|
|
1668
|
+
return runEnv({ args: argv.slice(1), slug: link.slug, token, root, flag, out });
|
|
1669
|
+
}
|
|
1670
|
+
case "visibility":
|
|
1671
|
+
case "access": {
|
|
1672
|
+
const token = await requireToken();
|
|
1673
|
+
const link = readLink(root);
|
|
1674
|
+
if (!link?.slug) {
|
|
1675
|
+
out("Not linked to an app. Run `omg deploy` or `omg link <slug>` first.");
|
|
1676
|
+
return 1;
|
|
1677
|
+
}
|
|
1678
|
+
return runVisibility({ args: argv.slice(1), slug: link.slug, token, out });
|
|
1679
|
+
}
|
|
1438
1680
|
case "deploy": {
|
|
1439
1681
|
const token = await requireToken();
|
|
1440
1682
|
const link = readLink(root);
|