@genex-ai/cli-demo 0.1.0 → 0.1.1

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,19 +1,19 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Set up your ~/.claude workspace, authorize, create a game project, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
7
- "genex": "./src/index.ts"
7
+ "genex": "./dist/index.js"
8
8
  },
9
9
  "files": [
10
- "src",
10
+ "dist",
11
11
  "templates",
12
12
  "README.md",
13
13
  "LICENSE"
14
14
  ],
15
15
  "engines": {
16
- "node": ">=24"
16
+ "node": ">=20"
17
17
  },
18
18
  "keywords": [
19
19
  "cli",
@@ -27,6 +27,9 @@
27
27
  "publishConfig": {
28
28
  "access": "public"
29
29
  },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0"
32
+ },
30
33
  "repository": {
31
34
  "type": "git",
32
35
  "url": "git+https://github.com/me-ai-org/genex-demo.git",
@@ -37,6 +40,7 @@
37
40
  },
38
41
  "homepage": "https://github.com/me-ai-org/genex-demo/tree/main/apps/cli#readme",
39
42
  "scripts": {
43
+ "build": "tsup",
40
44
  "start": "node src/index.ts",
41
45
  "dev": "node --watch src/index.ts",
42
46
  "typecheck": "tsc --noEmit",
@@ -1,131 +0,0 @@
1
- import path from "node:path";
2
- import {
3
- ENV_TOKEN_KEY,
4
- getApiUrl,
5
- getAuthUrl,
6
- getClaudeDir,
7
- getColyseusUrl,
8
- getTemplatesDir,
9
- } from "../config.ts";
10
- import { copyTemplates } from "../lib/copy-templates.ts";
11
- import { authorize } from "../lib/auth.ts";
12
- import { createDraftProject } from "../lib/project.ts";
13
- import { generateSshKeypair, writeGitignore } from "../lib/ssh.ts";
14
- import { writeProject, writeUserToken } from "../lib/store.ts";
15
- import { createLogger } from "../utils/logger.ts";
16
- import { c } from "../utils/colors.ts";
17
-
18
- export interface InitOptions {
19
- /** Destination workspace. Defaults to `~/.claude`. */
20
- dir?: string;
21
- /** Override the auth site (web) URL. */
22
- authUrl?: string;
23
- /** Override the API base URL (where the project is created). */
24
- apiUrl?: string;
25
- /** Override the Colyseus relay URL (stored in project metadata). */
26
- colyseusUrl?: string;
27
- /** Project name. Defaults to the current directory's name. */
28
- name?: string;
29
- /** Path to the token env file. Defaults to `~/.genex/env`. */
30
- envPath?: string;
31
- /** Skip the authorization step; only scaffold templates. */
32
- noAuth?: boolean;
33
- /** Overwrite existing files in the destination (off by default). */
34
- force?: boolean;
35
- /** Auth redirect wait timeout, in seconds. */
36
- timeoutSec?: number;
37
- quiet?: boolean;
38
- }
39
-
40
- export async function runInit(opts: InitOptions): Promise<void> {
41
- const log = createLogger({ quiet: opts.quiet });
42
-
43
- log.plain(c.bold("genex init"));
44
- log.plain("");
45
-
46
- // 1. Scaffold the ~/.claude workspace from bundled templates.
47
- const templatesDir = getTemplatesDir();
48
- const claudeDir = getClaudeDir(opts.dir);
49
-
50
- log.step(`Setting up your workspace at ${c.cyan(claudeDir)}`);
51
- const { copied, skipped } = await copyTemplates(templatesDir, claudeDir, {
52
- force: opts.force,
53
- });
54
-
55
- if (copied.length > 0) {
56
- log.success(`Added ${copied.length} file${copied.length === 1 ? "" : "s"}.`);
57
- for (const f of copied) log.dim(` + ${f}`);
58
- }
59
- if (skipped.length > 0) {
60
- log.info(
61
- `Left ${skipped.length} existing file${skipped.length === 1 ? "" : "s"} untouched.`,
62
- );
63
- for (const f of skipped) log.dim(` = ${f}`);
64
- }
65
- if (copied.length === 0 && skipped.length === 0) {
66
- log.info("No template files to install.");
67
- }
68
- log.plain("");
69
-
70
- // 2. Authorize (unless skipped).
71
- if (opts.noAuth) {
72
- log.info("Skipping authorization (--no-auth).");
73
- log.success("Done.");
74
- return;
75
- }
76
-
77
- const authBaseUrl = getAuthUrl(opts.authUrl);
78
- let token: string;
79
- try {
80
- token = await authorize(authBaseUrl, {
81
- log,
82
- timeoutMs: opts.timeoutSec ? opts.timeoutSec * 1000 : undefined,
83
- });
84
- } catch (err) {
85
- log.error(err instanceof Error ? err.message : String(err));
86
- log.dim("Your workspace files were installed. Re-run `genex init` to finish authorizing.");
87
- throw err;
88
- }
89
-
90
- log.success("Authorized.");
91
-
92
- // 3. Persist the token to ~/.genex/env (per-user; reused across projects).
93
- const { path: tokenPath } = await writeUserToken(token, opts.envPath);
94
- log.success(`Saved your token to ${c.cyan(tokenPath)} (${ENV_TOKEN_KEY}).`);
95
- log.plain("");
96
-
97
- // 4. Generate a per-project deploy key, then create the draft project so it
98
- // shows up in the dashboard's "My games" immediately. The private key stays
99
- // local (gitignored); only its public half is registered with the repo.
100
- const key = await generateSshKeypair(process.cwd(), log);
101
- await writeGitignore(process.cwd(), log);
102
- if (!key) {
103
- log.warn(
104
- "Skipping project creation — no deploy key. Install ssh-keygen (OpenSSH) and re-run `genex init`.",
105
- );
106
- log.plain("");
107
- log.success("Workspace ready (no project created yet).");
108
- return;
109
- }
110
-
111
- const apiUrl = getApiUrl(opts.apiUrl);
112
- const colyseusUrl = getColyseusUrl(opts.colyseusUrl);
113
- const projectName = opts.name?.trim() || path.basename(process.cwd());
114
-
115
- const meta = await createDraftProject({
116
- apiUrl,
117
- token,
118
- name: projectName,
119
- deployKey: key.publicKey,
120
- colyseusUrl,
121
- dashboardUrl: authBaseUrl,
122
- log,
123
- });
124
- if (meta) {
125
- const { path: metaPath } = await writeProject(meta);
126
- log.dim(` saved ${c.cyan(metaPath)}`);
127
- }
128
-
129
- log.plain("");
130
- log.success("All set. 🚀");
131
- }
@@ -1,151 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { getApiUrl } from "../config.ts";
5
- import { readProject, readUserToken } from "../lib/store.ts";
6
- import { KEY_NAME } from "../lib/ssh.ts";
7
- import { createLogger, type Logger } from "../utils/logger.ts";
8
- import { c } from "../utils/colors.ts";
9
-
10
- export interface PublishOptions {
11
- /** Override the API base URL (defaults to the project's stored apiUrl). */
12
- apiUrl?: string;
13
- /** Override the bearer token (defaults to ~/.genex/env). */
14
- token?: string;
15
- /** Path to the token env file (defaults to ~/.genex/env). */
16
- envPath?: string;
17
- /** Skip the git push; only flip the gallery flag. */
18
- noPush?: boolean;
19
- /** Optional gallery metadata. */
20
- title?: string;
21
- description?: string;
22
- quiet?: boolean;
23
- }
24
-
25
- interface RunResult {
26
- code: number;
27
- out: string;
28
- err: string;
29
- }
30
-
31
- /** Run a command, capturing output. Resolves with the exit code (never throws). */
32
- function run(cmd: string, args: string[], env?: NodeJS.ProcessEnv): Promise<RunResult> {
33
- return new Promise((resolve) => {
34
- let child;
35
- try {
36
- child = spawn(cmd, args, { env: env ? { ...process.env, ...env } : process.env });
37
- } catch {
38
- resolve({ code: -1, out: "", err: `${cmd} not found` });
39
- return;
40
- }
41
- let out = "";
42
- let err = "";
43
- child.stdout?.on("data", (d) => (out += String(d)));
44
- child.stderr?.on("data", (d) => (err += String(d)));
45
- child.on("error", () => resolve({ code: -1, out, err: `${cmd} not found` }));
46
- child.on("close", (code) => resolve({ code: code ?? -1, out, err }));
47
- });
48
- }
49
-
50
- /**
51
- * `genex publish` — push the built game to GitHub over the project's deploy key
52
- * (best-effort), then flip the project from draft to published in the gallery.
53
- *
54
- * push (updates the live Pages site) and publish (the gallery flag) are
55
- * independent per CLI_INTEGRATION.md, so a failed/absent push (e.g. locally with
56
- * the mock provider — no real repo) never blocks the gallery flip.
57
- */
58
- export async function runPublish(opts: PublishOptions): Promise<void> {
59
- const log = createLogger({ quiet: opts.quiet });
60
- log.plain(c.bold("genex publish"));
61
- log.plain("");
62
-
63
- const token = opts.token ?? (await readUserToken(opts.envPath));
64
- if (!token) {
65
- log.error("Not authorized. Run `genex init` first to sign in.");
66
- process.exitCode = 1;
67
- return;
68
- }
69
-
70
- const meta = await readProject();
71
- if (!meta) {
72
- log.error("No genex project here. Run `genex init` in this directory first.");
73
- process.exitCode = 1;
74
- return;
75
- }
76
-
77
- const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
78
-
79
- // 1. Push the built game (best-effort).
80
- if (opts.noPush) {
81
- log.info("Skipping git push (--no-push).");
82
- } else {
83
- await pushGame(meta.sshUrl, log);
84
- }
85
-
86
- // 2. Flip the gallery flag (authoritative).
87
- log.step("Publishing to the gallery…");
88
- let res: Response;
89
- try {
90
- const body: Record<string, string> = {};
91
- if (opts.title) body.title = opts.title;
92
- if (opts.description) body.description = opts.description;
93
- res = await fetch(`${apiUrl}/api/projects/${meta.id}/publish`, {
94
- method: "POST",
95
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
96
- body: JSON.stringify(body),
97
- });
98
- } catch (err) {
99
- log.error(`Couldn't reach the API at ${apiUrl}: ${String(err)}`);
100
- process.exitCode = 1;
101
- return;
102
- }
103
- if (!res.ok) {
104
- log.error(`Publish failed (HTTP ${res.status}).`);
105
- process.exitCode = 1;
106
- return;
107
- }
108
-
109
- log.plain("");
110
- log.success("Published. 🎉");
111
- if (meta.playUrl) {
112
- log.dim(` play: ${meta.playUrl}`);
113
- log.dim(" (GitHub Pages rebuilds ~30–90s after a push.)");
114
- }
115
- }
116
-
117
- async function pushGame(sshUrl: string, log: Logger): Promise<void> {
118
- // GitHub Pages serves the repo root — the game's entry must be index.html there.
119
- try {
120
- await fs.access(path.join(process.cwd(), "index.html"));
121
- } catch {
122
- log.warn("No index.html at the project root — GitHub Pages needs one to serve the game.");
123
- }
124
-
125
- const isRepo = (await run("git", ["rev-parse", "--git-dir"])).code === 0;
126
- if (!isRepo) {
127
- log.step("Initializing git…");
128
- if ((await run("git", ["init"])).code !== 0) {
129
- log.warn("git init failed — skipping push (use `genex publish --no-push` to silence).");
130
- return;
131
- }
132
- }
133
-
134
- await run("git", ["add", "-A"]);
135
- const commit = await run("git", ["commit", "-m", "build"]);
136
- if (commit.code !== 0 && !/nothing to commit/i.test(commit.out + commit.err)) {
137
- log.dim(" (no new commit to push)");
138
- }
139
-
140
- log.step("Pushing your game over SSH…");
141
- const push = await run("git", ["push", sshUrl, "HEAD:main"], {
142
- GIT_SSH_COMMAND: `ssh -i ./${KEY_NAME} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new`,
143
- });
144
- if (push.code === 0) {
145
- log.success("Pushed to main.");
146
- } else {
147
- log.warn("git push didn't succeed (expected locally — the mock provider has no real repo).");
148
- const tail = push.err.trim().split("\n").slice(-1)[0];
149
- if (tail) log.dim(` ${tail}`);
150
- }
151
- }
package/src/config.ts DELETED
@@ -1,102 +0,0 @@
1
- import os from "node:os";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- /**
6
- * The Genex authorization SITE — the web app that renders `/cli/auth` + `/login`
7
- * (NOT the API). The CLI opens `<authUrl>/cli/auth` in the browser. Override with
8
- * `GENEX_AUTH_URL` or `--auth-url` (locally: http://localhost:5173).
9
- */
10
- export const DEFAULT_AUTH_URL = "https://demo-web.glotech.world";
11
-
12
- /**
13
- * The Genex API base URL (where projects are created + published). Distinct from
14
- * the auth site. Override with `GENEX_API_URL` or `--api-url` (locally:
15
- * http://localhost:3000).
16
- */
17
- export const DEFAULT_API_URL = "https://demo-api.glotech.world";
18
-
19
- /**
20
- * The Colyseus multiplayer relay URL. Stored in the project metadata + printed so
21
- * the agent's game can `connect({ url, room: slug })`. Override with
22
- * `GENEX_COLYSEUS_URL` or `--colyseus-url` (locally: ws://localhost:2567).
23
- */
24
- export const DEFAULT_COLYSEUS_URL = "wss://demo-colyseus.glotech.world";
25
-
26
- /** Environment variable name written to `~/.genex/env` after authorizing. */
27
- export const ENV_TOKEN_KEY = "GENEX_TOKEN";
28
-
29
- /** Environment variable that overrides {@link DEFAULT_AUTH_URL}. */
30
- export const AUTH_URL_ENV = "GENEX_AUTH_URL";
31
-
32
- /** Environment variable that overrides {@link DEFAULT_API_URL}. */
33
- export const API_URL_ENV = "GENEX_API_URL";
34
-
35
- /** Environment variable that overrides {@link DEFAULT_COLYSEUS_URL}. */
36
- export const COLYSEUS_URL_ENV = "GENEX_COLYSEUS_URL";
37
-
38
- /**
39
- * Resolve the auth site URL, honoring (in order): an explicit override
40
- * (the `--auth-url` flag), the `GENEX_AUTH_URL` env var, then the default.
41
- * Any trailing slashes are trimmed so we can safely append paths.
42
- */
43
- export function getAuthUrl(override?: string): string {
44
- const raw = override || process.env[AUTH_URL_ENV] || DEFAULT_AUTH_URL;
45
- return raw.replace(/\/+$/, "");
46
- }
47
-
48
- /**
49
- * Resolve the API base URL, honoring (in order): an explicit override (the
50
- * `--api-url` flag), the `GENEX_API_URL` env var, then the default. Trailing
51
- * slashes are trimmed so we can safely append paths.
52
- */
53
- export function getApiUrl(override?: string): string {
54
- const raw = override || process.env[API_URL_ENV] || DEFAULT_API_URL;
55
- return raw.replace(/\/+$/, "");
56
- }
57
-
58
- /**
59
- * Resolve the Colyseus relay URL, honoring (in order): an explicit override (the
60
- * `--colyseus-url` flag), the `GENEX_COLYSEUS_URL` env var, then the default.
61
- */
62
- export function getColyseusUrl(override?: string): string {
63
- const raw = override || process.env[COLYSEUS_URL_ENV] || DEFAULT_COLYSEUS_URL;
64
- return raw.replace(/\/+$/, "");
65
- }
66
-
67
- /** The per-user Genex config dir (`~/.genex`) holding the auth token. */
68
- export function getGenexDir(): string {
69
- return path.join(os.homedir(), ".genex");
70
- }
71
-
72
- /**
73
- * Default location of the per-user env file the token is written to
74
- * (`~/.genex/env`), or an explicit path when `--env` is supplied.
75
- */
76
- export function getGenexEnvPath(override?: string): string {
77
- if (override) return path.resolve(override);
78
- return path.join(getGenexDir(), "env");
79
- }
80
-
81
- /** Absolute path to the bundled `templates/` directory shipped with the package. */
82
- export function getTemplatesDir(): string {
83
- // After bundling, this module lives at `dist/index.js`; templates sit at the
84
- // package root, one level up.
85
- const here = path.dirname(fileURLToPath(import.meta.url));
86
- return path.resolve(here, "..", "templates");
87
- }
88
-
89
- /**
90
- * The destination workspace — the user's global `~/.claude` directory by
91
- * default, or an explicit path when `--dir` is supplied.
92
- */
93
- export function getClaudeDir(override?: string): string {
94
- if (override) return path.resolve(override);
95
- return path.join(os.homedir(), ".claude");
96
- }
97
-
98
- /** Default location of the `.env` file we write the token into. */
99
- export function getEnvPath(override?: string): string {
100
- if (override) return path.resolve(override);
101
- return path.join(process.cwd(), ".env");
102
- }
package/src/index.ts DELETED
@@ -1,238 +0,0 @@
1
- #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { runInit, type InitOptions } from "./commands/init.ts";
6
- import { runPublish, type PublishOptions } from "./commands/publish.ts";
7
- import { DEFAULT_API_URL, DEFAULT_AUTH_URL } from "./config.ts";
8
- import { createLogger } from "./utils/logger.ts";
9
- import { c } from "./utils/colors.ts";
10
-
11
- type CliOptions = InitOptions & PublishOptions;
12
-
13
- function getVersion(): string {
14
- try {
15
- const here = path.dirname(fileURLToPath(import.meta.url));
16
- const pkg = JSON.parse(
17
- readFileSync(path.resolve(here, "..", "package.json"), "utf8"),
18
- ) as { version?: string };
19
- return pkg.version ?? "0.0.0";
20
- } catch {
21
- return "0.0.0";
22
- }
23
- }
24
-
25
- const HELP = `${c.bold("genex")} — set up your ~/.claude workspace, authorize, and publish 3D games.
26
-
27
- ${c.bold("Usage")}
28
- genex init [<name>] [options] Scaffold + authorize + create the draft project.
29
- genex publish [options] Push the built game and list it in the gallery.
30
-
31
- ${c.bold("Options for `init`")}
32
- <name> Project name (positional; default: current directory name).
33
- --name <name> Same as the positional name.
34
- --dir <path> Destination workspace (default: ~/.claude).
35
- --env <path> Token env file (default: ~/.genex/env).
36
- --auth-url <url> Override the auth site (default: ${DEFAULT_AUTH_URL}).
37
- --api-url <url> Override the API base URL (default: ${DEFAULT_API_URL}).
38
- --colyseus-url <url> Override the multiplayer URL (stored in project metadata).
39
- --no-auth Only scaffold templates; skip authorization.
40
- --force Overwrite existing files (default: never overwrite).
41
- --timeout <seconds> How long to wait for the auth redirect (default: 300).
42
-
43
- ${c.bold("Options for `publish`")}
44
- --no-push Skip the git push; only flip the gallery flag.
45
- --title <title> Gallery title.
46
- --description <text> Gallery description.
47
- --api-url <url> Override the API base URL.
48
- --env <path> Token env file (default: ~/.genex/env).
49
-
50
- ${c.bold("Global")}
51
- --quiet Reduce output.
52
- -h, --help Show this help.
53
- -v, --version Show the version.
54
-
55
- ${c.bold("Environment")}
56
- GENEX_AUTH_URL Overrides the default auth site URL.
57
- GENEX_API_URL Overrides the default API base URL.
58
- GENEX_COLYSEUS_URL Overrides the default multiplayer URL.
59
- GENEX_BROWSER Command used to open the browser (falls back to BROWSER).
60
-
61
- ${c.bold("Examples")}
62
- genex init my-game
63
- genex init my-game --api-url http://localhost:3000 --auth-url http://localhost:5173
64
- genex publish
65
- genex publish --no-push --title "My Game"
66
- `;
67
-
68
- interface ParsedArgs {
69
- command: string | undefined;
70
- options: CliOptions;
71
- help: boolean;
72
- version: boolean;
73
- error?: string;
74
- }
75
-
76
- function parseArgs(argv: string[]): ParsedArgs {
77
- const parsed: ParsedArgs = {
78
- command: undefined,
79
- options: {},
80
- help: false,
81
- version: false,
82
- };
83
-
84
- // The value-taking flags and where they land on the options object.
85
- const needsValue = new Set([
86
- "--dir",
87
- "--env",
88
- "--auth-url",
89
- "--api-url",
90
- "--colyseus-url",
91
- "--name",
92
- "--title",
93
- "--description",
94
- "--timeout",
95
- ]);
96
-
97
- let i = 0;
98
- while (i < argv.length) {
99
- const arg = argv[i++];
100
- if (arg === undefined) break;
101
-
102
- switch (arg) {
103
- case "-h":
104
- case "--help":
105
- parsed.help = true;
106
- break;
107
- case "-v":
108
- case "--version":
109
- parsed.version = true;
110
- break;
111
- case "--no-auth":
112
- parsed.options.noAuth = true;
113
- break;
114
- case "--no-push":
115
- parsed.options.noPush = true;
116
- break;
117
- case "--force":
118
- parsed.options.force = true;
119
- break;
120
- case "--quiet":
121
- parsed.options.quiet = true;
122
- break;
123
- default: {
124
- if (needsValue.has(arg)) {
125
- const value = argv[i++];
126
- if (value === undefined) {
127
- parsed.error = `Missing value for ${arg}`;
128
- return parsed;
129
- }
130
- applyValueFlag(parsed.options, arg, value);
131
- } else if (arg.startsWith("-")) {
132
- parsed.error = `Unknown option: ${arg}`;
133
- return parsed;
134
- } else if (parsed.command === undefined) {
135
- parsed.command = arg;
136
- } else if (parsed.options.name === undefined) {
137
- // Second positional → project name (e.g. `genex init my-game`).
138
- parsed.options.name = arg;
139
- } else {
140
- parsed.error = `Unexpected argument: ${arg}`;
141
- return parsed;
142
- }
143
- break;
144
- }
145
- }
146
- }
147
-
148
- return parsed;
149
- }
150
-
151
- function applyValueFlag(
152
- options: CliOptions,
153
- flag: string,
154
- value: string,
155
- ): void {
156
- switch (flag) {
157
- case "--dir":
158
- options.dir = value;
159
- break;
160
- case "--env":
161
- options.envPath = value;
162
- break;
163
- case "--auth-url":
164
- options.authUrl = value;
165
- break;
166
- case "--api-url":
167
- options.apiUrl = value;
168
- break;
169
- case "--colyseus-url":
170
- options.colyseusUrl = value;
171
- break;
172
- case "--name":
173
- options.name = value;
174
- break;
175
- case "--title":
176
- options.title = value;
177
- break;
178
- case "--description":
179
- options.description = value;
180
- break;
181
- case "--timeout": {
182
- const n = Number(value);
183
- if (!Number.isFinite(n) || n <= 0) {
184
- throw new Error(`Invalid --timeout value: ${value}`);
185
- }
186
- options.timeoutSec = n;
187
- break;
188
- }
189
- }
190
- }
191
-
192
- async function main(): Promise<void> {
193
- const log = createLogger();
194
- let parsed: ParsedArgs;
195
- try {
196
- parsed = parseArgs(process.argv.slice(2));
197
- } catch (err) {
198
- log.error(err instanceof Error ? err.message : String(err));
199
- process.exitCode = 1;
200
- return;
201
- }
202
-
203
- if (parsed.error) {
204
- log.error(parsed.error);
205
- log.plain(`Run ${c.cyan("genex --help")} for usage.`);
206
- process.exitCode = 1;
207
- return;
208
- }
209
-
210
- if (parsed.version) {
211
- log.plain(getVersion());
212
- return;
213
- }
214
-
215
- if (parsed.help || parsed.command === undefined) {
216
- log.plain(HELP);
217
- return;
218
- }
219
-
220
- switch (parsed.command) {
221
- case "init":
222
- await runInit(parsed.options);
223
- break;
224
- case "publish":
225
- await runPublish(parsed.options);
226
- break;
227
- default:
228
- log.error(`Unknown command: ${parsed.command}`);
229
- log.plain(`Run ${c.cyan("genex --help")} for usage.`);
230
- process.exitCode = 1;
231
- }
232
- }
233
-
234
- main().catch((err: unknown) => {
235
- const log = createLogger();
236
- log.error(err instanceof Error ? err.message : String(err));
237
- process.exitCode = 1;
238
- });