@omg-dev/cli 0.4.30 → 0.4.31
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/README.md +15 -9
- package/dist/omg.mjs +143 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Create a new app with `omg create my-app`. The same official generator powers
|
|
|
34
34
|
| `omg whoami` | Print the authenticated account without printing its credential. |
|
|
35
35
|
| `omg logout` | Remove saved credentials. |
|
|
36
36
|
| `omg apps` | List apps visible to the authenticated account. |
|
|
37
|
+
| `omg connect [--new] [--no-install]` | Connect this computer's local LFG to OMG's hosted relay. |
|
|
37
38
|
| `omg deploy [--name X] [--dir .] [--no-wait]` | Upload source, build, and publish. |
|
|
38
39
|
| `omg status [--dir .]` | Show deploy status for the linked directory. |
|
|
39
40
|
| `omg link <slug> [--dir .]` | Link a directory to an app on the account. |
|
|
@@ -43,11 +44,19 @@ Credentials resolve in this order: `OMG_API_KEY`, then
|
|
|
43
44
|
`~/.omg/credentials.json`. Never put an API key directly in a shell command or
|
|
44
45
|
commit either credential file.
|
|
45
46
|
|
|
47
|
+
`omg connect` discovers OMG's relay, obtains a one-time pairing code through
|
|
48
|
+
the authenticated CLI API, and hands both directly to `lfg connect`; the code
|
|
49
|
+
never needs to touch the clipboard. It resumes an existing OMG binding when
|
|
50
|
+
one is already saved. If LFG is missing, the command runs LFG's standard setup
|
|
51
|
+
script first; pass `--no-install` to require an existing install, or `--new` to
|
|
52
|
+
deliberately replace the saved binding.
|
|
53
|
+
|
|
46
54
|
## Implementation notes
|
|
47
55
|
|
|
48
|
-
`omg deploy` uploads source to
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
`omg deploy` uploads source to the authenticated control-plane broker. The
|
|
57
|
+
broker owns the temporary sandbox, takes a `files_only` snapshot, deletes the
|
|
58
|
+
staging sandbox, and asks the normal platform builder to publish it. The
|
|
59
|
+
builder—not the laptop—runs the production build.
|
|
51
60
|
|
|
52
61
|
The first successful deploy writes `.omg/project.json`. Its `projectId` is the
|
|
53
62
|
binding that makes the next deploy update the same slug.
|
|
@@ -57,9 +66,6 @@ The collector excludes generated and local state (`node_modules`, `dist`,
|
|
|
57
66
|
refuses `.env` and `.env.*`, skips symlinks, and enforces a 2 MB per-file and
|
|
58
67
|
40 MB total-source limit.
|
|
59
68
|
|
|
60
|
-
Browser OAuth tokens authenticate the control-plane
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`omg deploy`. API keys also authenticate the final publish hop through the
|
|
64
|
-
control-plane broker; no service credential is stored on the developer's
|
|
65
|
-
machine.
|
|
69
|
+
Browser OAuth tokens and `omg_sk_` API keys both authenticate the control-plane
|
|
70
|
+
CLI surface. The control-plane holds the infra service credential; no sandbox
|
|
71
|
+
or service credential is stored on the developer's machine.
|
package/dist/omg.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
import { readFileSync as readFileSync4, writeFileSync as
|
|
6
|
-
import { join as
|
|
5
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
6
|
+
import { join as join5, resolve as resolve3, basename, dirname as dirname3 } from "path";
|
|
7
7
|
|
|
8
8
|
// src/config.ts
|
|
9
9
|
import { homedir } from "os";
|
|
@@ -28,7 +28,6 @@ var CLI_API_OAUTH_AUDIENCES = [
|
|
|
28
28
|
];
|
|
29
29
|
|
|
30
30
|
// src/config.ts
|
|
31
|
-
var INFRA_URL = process.env.OMG_INFRA_URL?.trim() || "https://infra.omg.dev";
|
|
32
31
|
var CONTROL_PLANE_URL = process.env.OMG_API_URL?.trim() || "https://backend.omg.dev";
|
|
33
32
|
var AUTH_URL = process.env.OMG_AUTH_URL?.trim() || "https://auth.omg.dev";
|
|
34
33
|
var OAUTH_RESOURCE = process.env.OMG_OAUTH_RESOURCE?.trim() || CLI_OAUTH_RESOURCE;
|
|
@@ -431,46 +430,12 @@ async function request(base, path, token, init = {}) {
|
|
|
431
430
|
return text;
|
|
432
431
|
}
|
|
433
432
|
}
|
|
434
|
-
var infra = (path, token, init) => request(INFRA_URL, path, token, init);
|
|
435
433
|
var controlPlane = (path, token, init) => request(CONTROL_PLANE_URL, path, token, init);
|
|
436
434
|
function whoAmI(token) {
|
|
437
435
|
return controlPlane("/api/cli/whoami", token);
|
|
438
436
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
method: "POST",
|
|
442
|
-
body: JSON.stringify({ size: "small", templateId, skipAppProcesses: true })
|
|
443
|
-
});
|
|
444
|
-
return { id: sb.id ?? sb.sandboxId, status: sb.status };
|
|
445
|
-
}
|
|
446
|
-
async function waitForRunning(token, id, timeoutMs = 120000) {
|
|
447
|
-
const deadline = Date.now() + timeoutMs;
|
|
448
|
-
while (Date.now() < deadline) {
|
|
449
|
-
const s = await infra(`/v1/sandboxes/${id}`, token);
|
|
450
|
-
if (s.status === "running")
|
|
451
|
-
return;
|
|
452
|
-
if (s.status === "failed")
|
|
453
|
-
throw new Error(`sandbox ${id} failed to boot`);
|
|
454
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
455
|
-
}
|
|
456
|
-
throw new Error(`sandbox ${id} did not reach running within ${timeoutMs}ms`);
|
|
457
|
-
}
|
|
458
|
-
function writeFiles(token, id, files) {
|
|
459
|
-
return infra(`/v1/sandboxes/${id}/files`, token, {
|
|
460
|
-
method: "POST",
|
|
461
|
-
body: JSON.stringify(files.map((f) => ({ ...f, encoding: "base64" })))
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
function snapshotProject(token, id) {
|
|
465
|
-
return infra(`/v1/sandboxes/${id}/tarball`, token, { method: "POST", body: "{}" });
|
|
466
|
-
}
|
|
467
|
-
async function deleteSandbox(token, id) {
|
|
468
|
-
try {
|
|
469
|
-
await infra(`/v1/sandboxes/${id}`, token, { method: "DELETE" });
|
|
470
|
-
} catch {}
|
|
471
|
-
}
|
|
472
|
-
function deployFromSnapshot(token, body) {
|
|
473
|
-
return controlPlane("/api/cli/apps/deploy", token, {
|
|
437
|
+
function deploySource(token, body) {
|
|
438
|
+
return controlPlane("/api/cli/apps/deploy-source", token, {
|
|
474
439
|
method: "POST",
|
|
475
440
|
body: JSON.stringify(body)
|
|
476
441
|
});
|
|
@@ -481,6 +446,12 @@ function getStatus(token, slug) {
|
|
|
481
446
|
function listApps(token) {
|
|
482
447
|
return controlPlane("/api/cli/apps/list", token);
|
|
483
448
|
}
|
|
449
|
+
function getComputerConnectConfig(token) {
|
|
450
|
+
return controlPlane("/api/cli/computer/connect", token);
|
|
451
|
+
}
|
|
452
|
+
function startComputerPairing(token) {
|
|
453
|
+
return controlPlane("/api/cli/computer/connect", token, { method: "POST" });
|
|
454
|
+
}
|
|
484
455
|
|
|
485
456
|
// src/create.ts
|
|
486
457
|
var DEFAULT_GENERATOR = "create-omg@latest";
|
|
@@ -496,9 +467,110 @@ async function runCreate(args) {
|
|
|
496
467
|
return child.exited;
|
|
497
468
|
}
|
|
498
469
|
|
|
470
|
+
// src/connect.ts
|
|
471
|
+
import { chmodSync as chmodSync2, mkdtempSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
472
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
473
|
+
import { join as join2 } from "path";
|
|
474
|
+
var LFG_SETUP_URL = "https://raw.githubusercontent.com/BennyKok/lfg/main/scripts/setup.sh";
|
|
475
|
+
async function runCommand(command, options = {}) {
|
|
476
|
+
const capture = options.capture === true;
|
|
477
|
+
const child = Bun.spawn(command, {
|
|
478
|
+
env: options.env,
|
|
479
|
+
stdin: capture ? "ignore" : "inherit",
|
|
480
|
+
stdout: capture ? "pipe" : "inherit",
|
|
481
|
+
stderr: capture ? "pipe" : "inherit"
|
|
482
|
+
});
|
|
483
|
+
const [exitCode, stdout2, stderr] = await Promise.all([
|
|
484
|
+
child.exited,
|
|
485
|
+
capture ? new Response(child.stdout).text() : "",
|
|
486
|
+
capture ? new Response(child.stderr).text() : ""
|
|
487
|
+
]);
|
|
488
|
+
return { exitCode, stdout: stdout2, stderr };
|
|
489
|
+
}
|
|
490
|
+
var defaultDependencies = {
|
|
491
|
+
which: (name) => Bun.which(name),
|
|
492
|
+
runCommand,
|
|
493
|
+
installLfg,
|
|
494
|
+
getConfig: getComputerConnectConfig,
|
|
495
|
+
createPairing: startComputerPairing
|
|
496
|
+
};
|
|
497
|
+
function canonicalUrl(value) {
|
|
498
|
+
try {
|
|
499
|
+
const url = new URL(value);
|
|
500
|
+
url.pathname = url.pathname.replace(/\/$/, "") || "/";
|
|
501
|
+
return url.href.replace(/\/$/, "");
|
|
502
|
+
} catch {
|
|
503
|
+
return value.trim().replace(/\/$/, "");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function relayUrlFromStatus(output) {
|
|
507
|
+
if (output.includes("lfg connect: not paired with any relay."))
|
|
508
|
+
return null;
|
|
509
|
+
const match = output.match(/^lfg connect: paired as .+ via (\S+) \(since /m);
|
|
510
|
+
return match?.[1];
|
|
511
|
+
}
|
|
512
|
+
async function installLfg() {
|
|
513
|
+
const response = await fetch(LFG_SETUP_URL);
|
|
514
|
+
if (!response.ok) {
|
|
515
|
+
throw new Error(`Could not download the LFG installer (${response.status}).`);
|
|
516
|
+
}
|
|
517
|
+
const directory = mkdtempSync(join2(tmpdir(), "omg-lfg-install-"));
|
|
518
|
+
const script = join2(directory, "setup.sh");
|
|
519
|
+
try {
|
|
520
|
+
writeFileSync2(script, await response.text(), { mode: 448 });
|
|
521
|
+
chmodSync2(script, 448);
|
|
522
|
+
const result = await runCommand(["bash", script]);
|
|
523
|
+
if (result.exitCode !== 0) {
|
|
524
|
+
throw new Error(`LFG setup exited with code ${result.exitCode}.`);
|
|
525
|
+
}
|
|
526
|
+
} finally {
|
|
527
|
+
rmSync(directory, { recursive: true, force: true });
|
|
528
|
+
}
|
|
529
|
+
return Bun.which("lfg") || join2(homedir2(), ".local", "bin", "lfg");
|
|
530
|
+
}
|
|
531
|
+
async function runConnect(options) {
|
|
532
|
+
const output = options.output ?? ((message) => process.stdout.write(message + `
|
|
533
|
+
`));
|
|
534
|
+
const dependencies = { ...defaultDependencies, ...options.dependencies };
|
|
535
|
+
let lfg = dependencies.which("lfg");
|
|
536
|
+
if (!lfg) {
|
|
537
|
+
if (options.install === false) {
|
|
538
|
+
throw new Error("LFG is not installed. Install it from https://github.com/BennyKok/lfg, then rerun `omg connect`.");
|
|
539
|
+
}
|
|
540
|
+
output("LFG is not installed; installing it now\u2026");
|
|
541
|
+
lfg = await dependencies.installLfg();
|
|
542
|
+
}
|
|
543
|
+
const { connectUrl } = await dependencies.getConfig(options.token);
|
|
544
|
+
if (!connectUrl?.trim())
|
|
545
|
+
throw new Error("OMG returned no relay URL.");
|
|
546
|
+
if (!options.fresh) {
|
|
547
|
+
const status = await dependencies.runCommand([lfg, "connect", "status"], { capture: true });
|
|
548
|
+
if (status.exitCode !== 0) {
|
|
549
|
+
throw new Error(status.stderr.trim() || `Could not read LFG connection status (${status.exitCode}).`);
|
|
550
|
+
}
|
|
551
|
+
const savedRelay = relayUrlFromStatus(status.stdout);
|
|
552
|
+
if (savedRelay === undefined) {
|
|
553
|
+
throw new Error("This LFG version returned an unrecognized connection status. Run `lfg setup`, then retry.");
|
|
554
|
+
}
|
|
555
|
+
if (savedRelay && canonicalUrl(savedRelay) === canonicalUrl(connectUrl)) {
|
|
556
|
+
output("Resuming this computer's existing OMG connection\u2026");
|
|
557
|
+
const result2 = await dependencies.runCommand([lfg, "connect"], { env: process.env });
|
|
558
|
+
return result2.exitCode;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const pairing = await dependencies.createPairing(options.token);
|
|
562
|
+
if (!pairing.code || !pairing.connectUrl)
|
|
563
|
+
throw new Error("OMG returned an incomplete pairing response.");
|
|
564
|
+
output("Pairing this computer with OMG\u2026");
|
|
565
|
+
const result = await dependencies.runCommand([lfg, "connect", pairing.code], {
|
|
566
|
+
env: { ...process.env, LFG_RELAY_URL: pairing.connectUrl }
|
|
567
|
+
});
|
|
568
|
+
return result.exitCode;
|
|
569
|
+
}
|
|
570
|
+
|
|
499
571
|
// src/files.ts
|
|
500
572
|
import { readdirSync, lstatSync, readFileSync as readFileSync2 } from "fs";
|
|
501
|
-
import { join as
|
|
573
|
+
import { join as join3, relative, sep } from "path";
|
|
502
574
|
var ALWAYS_SKIP = new Set([
|
|
503
575
|
"node_modules",
|
|
504
576
|
"dist",
|
|
@@ -522,7 +594,7 @@ function collectProjectFiles(root) {
|
|
|
522
594
|
for (const entry of readdirSync(dir)) {
|
|
523
595
|
if (ALWAYS_SKIP.has(entry))
|
|
524
596
|
continue;
|
|
525
|
-
const abs =
|
|
597
|
+
const abs = join3(dir, entry);
|
|
526
598
|
const st = lstatSync(abs);
|
|
527
599
|
if (st.isDirectory()) {
|
|
528
600
|
walk(abs);
|
|
@@ -555,7 +627,7 @@ function collectProjectFiles(root) {
|
|
|
555
627
|
}
|
|
556
628
|
function assertDeployable(root) {
|
|
557
629
|
try {
|
|
558
|
-
const pkg = JSON.parse(readFileSync2(
|
|
630
|
+
const pkg = JSON.parse(readFileSync2(join3(root, "package.json"), "utf8"));
|
|
559
631
|
if (!pkg.scripts?.build) {
|
|
560
632
|
throw new Error("package.json has no `build` script \u2014 the builder runs `bun run build`, so the deploy would fail.");
|
|
561
633
|
}
|
|
@@ -587,24 +659,9 @@ async function deploy(opts) {
|
|
|
587
659
|
for (const s of collected.skippedLarge)
|
|
588
660
|
onProgress(`skipping ${s} \u2014 over the per-file limit`);
|
|
589
661
|
onProgress(`uploading ${collected.files.length} files (${(collected.totalBytes / 1024).toFixed(0)} KB)`);
|
|
590
|
-
const
|
|
591
|
-
onProgress(`staging sandbox ${sandbox.id}`);
|
|
592
|
-
let snapshotId;
|
|
593
|
-
try {
|
|
594
|
-
await waitForRunning(token, sandbox.id);
|
|
595
|
-
await writeFiles(token, sandbox.id, collected.files);
|
|
596
|
-
const snap = await snapshotProject(token, sandbox.id);
|
|
597
|
-
if (!snap?.id)
|
|
598
|
-
throw new Error("snapshot did not return an id");
|
|
599
|
-
snapshotId = snap.id;
|
|
600
|
-
onProgress(`snapshot ${snap.id}` + (snap.tarballSizeBytes ? ` (${(snap.tarballSizeBytes / 1e6).toFixed(2)} MB)` : ""));
|
|
601
|
-
} finally {
|
|
602
|
-
await deleteSandbox(token, sandbox.id);
|
|
603
|
-
}
|
|
604
|
-
onProgress("publishing");
|
|
605
|
-
const result = await deployFromSnapshot(token, {
|
|
662
|
+
const result = await deploySource(token, {
|
|
606
663
|
name: opts.name,
|
|
607
|
-
|
|
664
|
+
files: collected.files.map(({ path, content }) => ({ path, content })),
|
|
608
665
|
projectId: opts.projectId
|
|
609
666
|
});
|
|
610
667
|
return result;
|
|
@@ -644,12 +701,12 @@ import {
|
|
|
644
701
|
mkdirSync as mkdirSync2,
|
|
645
702
|
readFileSync as readFileSync3,
|
|
646
703
|
readdirSync as readdirSync2,
|
|
647
|
-
rmSync,
|
|
704
|
+
rmSync as rmSync2,
|
|
648
705
|
statSync,
|
|
649
|
-
writeFileSync as
|
|
706
|
+
writeFileSync as writeFileSync3
|
|
650
707
|
} from "fs";
|
|
651
708
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
652
|
-
import { dirname as dirname2, join as
|
|
709
|
+
import { dirname as dirname2, join as join4, relative as relative2, resolve, sep as sep2 } from "path";
|
|
653
710
|
var jsonHeaders = { "content-type": "application/json" };
|
|
654
711
|
function json(body, status = 200, headers) {
|
|
655
712
|
return new Response(JSON.stringify(body), {
|
|
@@ -691,19 +748,19 @@ function safeKey(value) {
|
|
|
691
748
|
class LocalCache {
|
|
692
749
|
root;
|
|
693
750
|
constructor(projectRoot) {
|
|
694
|
-
this.root =
|
|
751
|
+
this.root = join4(projectRoot, ".omg", "cache");
|
|
695
752
|
mkdirSync2(this.root, { recursive: true });
|
|
696
753
|
}
|
|
697
754
|
read(name, fallback) {
|
|
698
|
-
const path =
|
|
755
|
+
const path = join4(this.root, name);
|
|
699
756
|
if (!existsSync(path))
|
|
700
757
|
return fallback;
|
|
701
758
|
return JSON.parse(readFileSync3(path, "utf8"));
|
|
702
759
|
}
|
|
703
760
|
write(name, value) {
|
|
704
|
-
const path =
|
|
761
|
+
const path = join4(this.root, name);
|
|
705
762
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
706
|
-
|
|
763
|
+
writeFileSync3(path, JSON.stringify(value, null, 2) + `
|
|
707
764
|
`);
|
|
708
765
|
}
|
|
709
766
|
}
|
|
@@ -766,7 +823,7 @@ class LocalDevBackend {
|
|
|
766
823
|
}
|
|
767
824
|
subscriberCount(topic) {
|
|
768
825
|
try {
|
|
769
|
-
const raw = JSON.parse(readFileSync3(
|
|
826
|
+
const raw = JSON.parse(readFileSync3(join4(this.projectRoot, ".vibes", "triggers.json"), "utf8"));
|
|
770
827
|
const triggers = Array.isArray(raw) ? raw : raw.triggers ?? [];
|
|
771
828
|
return triggers.filter((trigger) => trigger.kind === "event" && trigger.key === topic).length;
|
|
772
829
|
} catch {
|
|
@@ -882,7 +939,7 @@ class LocalDevBackend {
|
|
|
882
939
|
if (bytes.byteLength > 25 * 1024 * 1024)
|
|
883
940
|
return error("object exceeds 25 MB", 413);
|
|
884
941
|
mkdirSync2(dirname2(path), { recursive: true });
|
|
885
|
-
|
|
942
|
+
writeFileSync3(path, bytes);
|
|
886
943
|
const metadata2 = this.cache.read("storage-content-types.json", {});
|
|
887
944
|
metadata2[`${scope}/${userId}/${key}`] = url.searchParams.get("contentType") || req.headers.get("content-type") || "application/octet-stream";
|
|
888
945
|
this.cache.write("storage-content-types.json", metadata2);
|
|
@@ -914,7 +971,7 @@ class LocalDevBackend {
|
|
|
914
971
|
const files = [];
|
|
915
972
|
const walk = (dir) => {
|
|
916
973
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
917
|
-
const path =
|
|
974
|
+
const path = join4(dir, entry.name);
|
|
918
975
|
if (entry.isDirectory())
|
|
919
976
|
walk(path);
|
|
920
977
|
else if (entry.isFile()) {
|
|
@@ -944,7 +1001,7 @@ class LocalDevBackend {
|
|
|
944
1001
|
try {
|
|
945
1002
|
const input = await body(req);
|
|
946
1003
|
const { scope, userId, key } = this.storageParts(input);
|
|
947
|
-
|
|
1004
|
+
rmSync2(this.storagePath(scope, userId, key), { force: true });
|
|
948
1005
|
return new Response(null, { status: 204 });
|
|
949
1006
|
} catch (err) {
|
|
950
1007
|
return error(err instanceof Error ? err.message : String(err));
|
|
@@ -1102,9 +1159,9 @@ class LocalDevBackend {
|
|
|
1102
1159
|
const artifact = `${jobId}.svg`;
|
|
1103
1160
|
const prompt = String(input.input?.prompt ?? model);
|
|
1104
1161
|
const svg = this.placeholderSvg(model, prompt);
|
|
1105
|
-
const artifactDir =
|
|
1162
|
+
const artifactDir = join4(this.cache.root, "media");
|
|
1106
1163
|
mkdirSync2(artifactDir, { recursive: true });
|
|
1107
|
-
|
|
1164
|
+
writeFileSync3(join4(artifactDir, artifact), svg);
|
|
1108
1165
|
jobs[jobId] = {
|
|
1109
1166
|
jobId,
|
|
1110
1167
|
status: "succeeded",
|
|
@@ -1137,7 +1194,7 @@ class LocalDevBackend {
|
|
|
1137
1194
|
}
|
|
1138
1195
|
getMediaArtifact(name) {
|
|
1139
1196
|
const safeName = safeSegment(name, "artifact");
|
|
1140
|
-
const path =
|
|
1197
|
+
const path = join4(this.cache.root, "media", safeName);
|
|
1141
1198
|
if (!existsSync(path))
|
|
1142
1199
|
return error("artifact not found", 404);
|
|
1143
1200
|
return new Response(readFileSync3(path), { headers: { "content-type": "image/svg+xml" } });
|
|
@@ -1262,15 +1319,15 @@ var out = (msg = "") => process.stdout.write(msg + `
|
|
|
1262
1319
|
var step = (msg) => out(` ${msg}`);
|
|
1263
1320
|
function readLink(root) {
|
|
1264
1321
|
try {
|
|
1265
|
-
return JSON.parse(readFileSync4(
|
|
1322
|
+
return JSON.parse(readFileSync4(join5(root, LINK_FILE), "utf8"));
|
|
1266
1323
|
} catch {
|
|
1267
1324
|
return null;
|
|
1268
1325
|
}
|
|
1269
1326
|
}
|
|
1270
1327
|
function writeLink(root, link) {
|
|
1271
|
-
const path =
|
|
1328
|
+
const path = join5(root, LINK_FILE);
|
|
1272
1329
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
1273
|
-
|
|
1330
|
+
writeFileSync4(path, JSON.stringify(link, null, 2) + `
|
|
1274
1331
|
`);
|
|
1275
1332
|
}
|
|
1276
1333
|
var HELP = `omg \u2014 deploy a local project to omg.dev
|
|
@@ -1283,6 +1340,7 @@ var HELP = `omg \u2014 deploy a local project to omg.dev
|
|
|
1283
1340
|
omg logout
|
|
1284
1341
|
omg whoami
|
|
1285
1342
|
omg apps
|
|
1343
|
+
omg connect [--new] [--no-install]
|
|
1286
1344
|
omg dev [--dir <path>] [--agent-port <port>] [--cloud]
|
|
1287
1345
|
|
|
1288
1346
|
Credentials: OMG_API_KEY, or ~/.omg/credentials.json via \`omg login\`.
|
|
@@ -1336,6 +1394,15 @@ async function main() {
|
|
|
1336
1394
|
out(` ${a.slug.padEnd(32)} ${a.name ?? ""}`);
|
|
1337
1395
|
return 0;
|
|
1338
1396
|
}
|
|
1397
|
+
case "connect": {
|
|
1398
|
+
const token = await requireToken();
|
|
1399
|
+
return runConnect({
|
|
1400
|
+
token,
|
|
1401
|
+
fresh: has("new"),
|
|
1402
|
+
install: !has("no-install"),
|
|
1403
|
+
output: out
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1339
1406
|
case "link": {
|
|
1340
1407
|
const slug = argv[1];
|
|
1341
1408
|
if (!slug) {
|