@puzzmo/cli 1.0.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.
Files changed (54) hide show
  1. package/README.md +75 -0
  2. package/lib/commands/game/create.d.ts +2 -0
  3. package/lib/commands/game/create.js +145 -0
  4. package/lib/commands/game/create.js.map +1 -0
  5. package/lib/commands/login.d.ts +2 -0
  6. package/lib/commands/login.js +13 -0
  7. package/lib/commands/login.js.map +1 -0
  8. package/lib/commands/upload.d.ts +2 -0
  9. package/lib/commands/upload.js +87 -0
  10. package/lib/commands/upload.js.map +1 -0
  11. package/lib/download/page-downloader.d.ts +2 -0
  12. package/lib/download/page-downloader.js +94 -0
  13. package/lib/download/page-downloader.js.map +1 -0
  14. package/lib/index.d.ts +2 -0
  15. package/lib/index.js +53 -0
  16. package/lib/index.js.map +1 -0
  17. package/lib/lib/api.d.ts +10 -0
  18. package/lib/lib/api.js +53 -0
  19. package/lib/lib/api.js.map +1 -0
  20. package/lib/lib/config.d.ts +13 -0
  21. package/lib/lib/config.js +29 -0
  22. package/lib/lib/config.js.map +1 -0
  23. package/lib/lib/exec.d.ts +15 -0
  24. package/lib/lib/exec.js +34 -0
  25. package/lib/lib/exec.js.map +1 -0
  26. package/lib/lib/package-manager.d.ts +2 -0
  27. package/lib/lib/package-manager.js +15 -0
  28. package/lib/lib/package-manager.js.map +1 -0
  29. package/lib/skills/registry.d.ts +13 -0
  30. package/lib/skills/registry.js +49 -0
  31. package/lib/skills/registry.js.map +1 -0
  32. package/lib/skills/runner.d.ts +4 -0
  33. package/lib/skills/runner.js +80 -0
  34. package/lib/skills/runner.js.map +1 -0
  35. package/lib/tui/pipeline.d.ts +2 -0
  36. package/lib/tui/pipeline.js +426 -0
  37. package/lib/tui/pipeline.js.map +1 -0
  38. package/lib/wizard/agent-detect.d.ts +4 -0
  39. package/lib/wizard/agent-detect.js +4 -0
  40. package/lib/wizard/agent-detect.js.map +1 -0
  41. package/package.json +20 -0
  42. package/src/commands/game/create.ts +171 -0
  43. package/src/commands/login.ts +15 -0
  44. package/src/commands/upload.ts +94 -0
  45. package/src/download/page-downloader.ts +104 -0
  46. package/src/index.ts +56 -0
  47. package/src/lib/api.ts +75 -0
  48. package/src/lib/config.ts +37 -0
  49. package/src/lib/exec.ts +38 -0
  50. package/src/skills/registry.ts +57 -0
  51. package/src/skills/runner.ts +91 -0
  52. package/src/tui/pipeline.ts +460 -0
  53. package/src/wizard/agent-detect.ts +6 -0
  54. package/tsconfig.json +15 -0
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @puzzmo/cli
2
+
3
+ Command-line tool for Puzzmo game developers. Authenticate, upload game builds, and create new game projects.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @puzzmo/cli
9
+ ```
10
+
11
+ ## Commands
12
+
13
+ ### `puzzmo login <token>`
14
+
15
+ Save a CLI auth token. Generate one from studio.puzzmo.com.
16
+
17
+ ```bash
18
+ puzzmo login pzt-your-token-here
19
+ ```
20
+
21
+ ### `puzzmo upload <slug> <dir>`
22
+
23
+ Upload a game build directory to Puzzmo.
24
+
25
+ ```bash
26
+ puzzmo upload my-game dist/
27
+ ```
28
+
29
+ The command collects all files in the directory, computes a SHA from git (or file contents), and uploads them in batches.
30
+
31
+ ### `puzzmo game create [token]`
32
+
33
+ Interactive wizard to create a new Puzzmo game project.
34
+
35
+ ```bash
36
+ puzzmo game create --name my-game --url https://example.com/game/
37
+ ```
38
+
39
+ Options:
40
+
41
+ | Option | Description |
42
+ | ----------------- | --------------------------------------------------------- |
43
+ | `[token]` | Optional access token (runs `puzzmo login` automatically) |
44
+ | `--name <name>` | Game name |
45
+ | `--url <url>` | Source URL to import |
46
+ | `--agent <agent>` | LLM agent: `claude`, `codex`, `gemini`, `none` |
47
+
48
+ The wizard:
49
+
50
+ 1. Downloads the source HTML page and assets
51
+ 2. Detects installed LLM coding agents
52
+ 3. Installs migration skills via the Vercel skills ecosystem
53
+ 4. Runs each skill through the selected agent with build verification
54
+ 5. Commits after each successful step
55
+
56
+ ## Configuration
57
+
58
+ Config is stored in `~/.puzzmo/config.json`:
59
+
60
+ ```json
61
+ {
62
+ "token": "pzt-...",
63
+ "apiURL": "https://api.puzzmo.com"
64
+ }
65
+ ```
66
+
67
+ The token can also be set via the `PUZZMO_TOKEN` environment variable, and the API URL via `PUZZMO_API_URL`.
68
+
69
+ ## Also available via
70
+
71
+ ```bash
72
+ yarn create puzzmo game
73
+ ```
74
+
75
+ This uses the `create-puzzmo` package which forwards to `puzzmo game create`.
@@ -0,0 +1,2 @@
1
+ /** Main game create wizard */
2
+ export declare const gameCreate: (args: string[]) => Promise<void>;
@@ -0,0 +1,145 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { detectAgent } from "../../wizard/agent-detect.js";
4
+ import { downloadPage } from "../../download/page-downloader.js";
5
+ import { runCommand, gitCommit } from "../../lib/exec.js";
6
+ import { runSkillsPipelineTUI } from "../../skills/runner.js";
7
+ import { installSkills } from "../../skills/registry.js";
8
+ import { login } from "../login.js";
9
+ /** Parses CLI args into CreateOptions */
10
+ const parseArgs = (args) => {
11
+ const opts = {};
12
+ let i = 0;
13
+ // First positional arg could be access token
14
+ if (args[0] && !args[0].startsWith("--")) {
15
+ opts.accessToken = args[0];
16
+ i = 1;
17
+ }
18
+ while (i < args.length) {
19
+ const arg = args[i];
20
+ if (arg === "--name" && args[i + 1]) {
21
+ opts.name = args[++i];
22
+ }
23
+ else if (arg === "--url" && args[i + 1]) {
24
+ opts.url = args[++i];
25
+ }
26
+ else if (arg === "--agent" && args[i + 1]) {
27
+ opts.agent = args[++i];
28
+ }
29
+ i++;
30
+ }
31
+ return opts;
32
+ };
33
+ /** Interactive text prompt (simple readline-based fallback) */
34
+ const askText = async (message, defaultValue) => {
35
+ const { createInterface } = await import("node:readline");
36
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
37
+ const prompt = defaultValue ? `${message} (${defaultValue}): ` : `${message}: `;
38
+ return new Promise((resolve) => {
39
+ rl.question(prompt, (answer) => {
40
+ rl.close();
41
+ resolve(answer.trim() || defaultValue || "");
42
+ });
43
+ });
44
+ };
45
+ /** Interactive select prompt */
46
+ const askSelect = async (message, options) => {
47
+ console.log(`\n${message}`);
48
+ const enabled = options.filter((o) => !o.disabled);
49
+ options.forEach((opt) => {
50
+ const prefix = opt.disabled ? " " : ` ${enabled.indexOf(opt) + 1}.`;
51
+ const suffix = opt.disabled ? " (Coming soon)" : "";
52
+ console.log(`${prefix} ${opt.label}${suffix}`);
53
+ });
54
+ const { createInterface } = await import("node:readline");
55
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
56
+ return new Promise((resolve) => {
57
+ rl.question(`\nChoice (1-${enabled.length}): `, (answer) => {
58
+ rl.close();
59
+ const idx = parseInt(answer.trim(), 10) - 1;
60
+ resolve(enabled[idx]?.value ?? enabled[0].value);
61
+ });
62
+ });
63
+ };
64
+ /** Main game create wizard */
65
+ export const gameCreate = async (args) => {
66
+ const opts = parseArgs(args);
67
+ console.log("\n Puzzmo Game Creator\n");
68
+ // Step 1: Game name
69
+ const name = opts.name || (await askText("Game name"));
70
+ if (!name) {
71
+ console.error("Game name is required");
72
+ process.exit(1);
73
+ }
74
+ // Step 2: Mode selection
75
+ const mode = await askSelect("How would you like to create your game?", [
76
+ { value: "import", label: "Import an HTML game from a web page" },
77
+ { value: "new-repo", label: "Create game in a new repo", disabled: true },
78
+ { value: "add-to-repo", label: "Add a game to this repo", disabled: true },
79
+ ]);
80
+ if (mode !== "import") {
81
+ console.log("This mode is not yet supported.");
82
+ process.exit(0);
83
+ }
84
+ // Step 3: Source URL
85
+ const url = opts.url || (await askText("Source URL to import"));
86
+ if (!url) {
87
+ console.error("Source URL is required for import mode");
88
+ process.exit(1);
89
+ }
90
+ // Step 4: Download page
91
+ const gameDir = path.resolve(name);
92
+ console.log(`\nDownloading ${url} into ./${name}/...`);
93
+ await downloadPage(url, gameDir);
94
+ console.log("Download complete.");
95
+ // Step 5: Detect agents
96
+ const agents = detectAgent();
97
+ const agentChoices = [
98
+ ...agents.map((a) => ({ value: a.binary, label: `${a.displayName} (${a.path})` })),
99
+ { value: "none", label: "None - I'll run the skills manually" },
100
+ ];
101
+ // Step 6: Ask about agent
102
+ let selectedAgent;
103
+ if (opts.agent) {
104
+ selectedAgent = opts.agent;
105
+ }
106
+ else if (agents.length > 0) {
107
+ selectedAgent = await askSelect("Which LLM agent do you use?", agentChoices);
108
+ }
109
+ else {
110
+ console.log("\nNo LLM agents detected (checked: claude, codex)");
111
+ selectedAgent = "none";
112
+ }
113
+ // Step 7: Login if token provided
114
+ if (opts.accessToken) {
115
+ console.log("\nLogging in...");
116
+ login(opts.accessToken);
117
+ }
118
+ // Step 8: Install skills into the game directory
119
+ if (selectedAgent !== "none") {
120
+ console.log("\nInstalling Puzzmo skills...");
121
+ const count = installSkills(selectedAgent, gameDir);
122
+ console.log(`Installed ${count} skill(s).`);
123
+ }
124
+ // Step 9: Initialize git
125
+ if (!fs.existsSync(path.join(gameDir, ".git"))) {
126
+ runCommand("git init", { cwd: gameDir });
127
+ runCommand("git add -A", { cwd: gameDir });
128
+ gitCommit("Initial game import", { cwd: gameDir });
129
+ }
130
+ // Step 10: Run skills pipeline
131
+ if (selectedAgent !== "none") {
132
+ console.log("\nStarting migration pipeline...\n");
133
+ await runSkillsPipelineTUI(selectedAgent, gameDir);
134
+ }
135
+ // Done
136
+ console.log(`\nDone! Your game is in ./${name}/`);
137
+ console.log(`\nNext steps:`);
138
+ console.log(` cd ${name}`);
139
+ console.log(` npx vite # Start development server`);
140
+ console.log(` npx vite build # Build for production`);
141
+ if (selectedAgent === "none") {
142
+ console.log(`\nTo run migration skills manually, see packages/skills/ for SKILL.md files.`);
143
+ }
144
+ };
145
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/game/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Saves a CLI token to ~/.puzzmo/config.json */
2
+ export declare const login: (token: string) => void;
@@ -0,0 +1,13 @@
1
+ import { writeConfig, readConfig } from "../lib/config.js";
2
+ /** Saves a CLI token to ~/.puzzmo/config.json */
3
+ export const login = (token) => {
4
+ if (!token.startsWith("pzt-")) {
5
+ console.error("Invalid CLI token. Generate one from studio.puzzmo.com.");
6
+ process.exit(1);
7
+ }
8
+ const config = readConfig();
9
+ config.token = token;
10
+ writeConfig(config);
11
+ console.log("Logged in successfully. Token saved to ~/.puzzmo/config.json");
12
+ };
13
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE1D,iDAAiD;AACjD,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE;IACrC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAA;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,WAAW,CAAC,MAAM,CAAC,CAAA;IAEnB,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAA;AAC7E,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ /** Uploads game build artifacts to Puzzmo */
2
+ export declare const upload: (gameSlug: string, dir: string) => Promise<void>;
@@ -0,0 +1,87 @@
1
+ import { execSync } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { uploadFiles } from "../lib/api.js";
6
+ import { getAPIURL, getToken } from "../lib/config.js";
7
+ /** Collects all files in a directory recursively */
8
+ const collectFiles = (dir) => {
9
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
10
+ const files = [];
11
+ for (const entry of entries) {
12
+ const full = path.join(dir, entry.name);
13
+ if (entry.isDirectory())
14
+ files.push(...collectFiles(full));
15
+ else
16
+ files.push(full);
17
+ }
18
+ return files;
19
+ };
20
+ /** Tries to get the current git SHA, returns null if not in a git repo */
21
+ const getGitSHA = () => {
22
+ try {
23
+ return execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ };
29
+ /** Hashes all file contents to produce a deterministic SHA */
30
+ const hashFiles = (filePaths) => {
31
+ const hash = crypto.createHash("sha256");
32
+ for (const fp of filePaths.sort()) {
33
+ hash.update(fs.readFileSync(fp));
34
+ }
35
+ return hash.digest("hex").slice(0, 12);
36
+ };
37
+ /** Formats a byte count as a human-readable string */
38
+ const formatBytes = (bytes) => {
39
+ if (bytes < 1024)
40
+ return `${bytes} B`;
41
+ if (bytes < 1024 * 1024)
42
+ return `${(bytes / 1024).toFixed(1)} KB`;
43
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
44
+ };
45
+ /** Uploads game build artifacts to Puzzmo */
46
+ export const upload = async (gameSlug, dir) => {
47
+ const token = getToken();
48
+ if (!token) {
49
+ console.error("Not logged in. Run `puzzmo login <token>` or set PUZZMO_TOKEN.");
50
+ process.exit(1);
51
+ }
52
+ const distDir = path.resolve(dir);
53
+ if (!fs.existsSync(distDir)) {
54
+ console.error(`Directory not found: ${dir}`);
55
+ process.exit(1);
56
+ }
57
+ const files = collectFiles(distDir);
58
+ if (!files.length) {
59
+ console.error(`Directory is empty: ${dir}`);
60
+ process.exit(1);
61
+ }
62
+ // Determine SHA
63
+ const sha = getGitSHA() || hashFiles(files);
64
+ console.log(`\nUploading ${gameSlug} (${sha.slice(0, 8)})`);
65
+ console.log(`Directory: ${distDir}`);
66
+ console.log("");
67
+ let totalBytes = 0;
68
+ for (const file of files) {
69
+ const size = fs.statSync(file).size;
70
+ totalBytes += size;
71
+ const rel = path.relative(distDir, file);
72
+ console.log(` ${rel} (${formatBytes(size)})`);
73
+ }
74
+ const apiURL = getAPIURL();
75
+ const defaultURL = "https://api.puzzmo.com";
76
+ console.log(`\n${files.length} file(s), ${formatBytes(totalBytes)} total`);
77
+ if (apiURL !== defaultURL)
78
+ console.log(`Uploading to ${apiURL}...`);
79
+ else
80
+ console.log("Uploading...");
81
+ const result = await uploadFiles(token, gameSlug, sha, files, distDir, (batch, totalBatches, uploaded) => {
82
+ console.log(` Batch ${batch}/${totalBatches} done (${uploaded} file(s) uploaded)`);
83
+ });
84
+ console.log(`\nDone - ${result.versionID}`);
85
+ console.log(`CDN: ${result.cdnBase}`);
86
+ };
87
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** Downloads an HTML page and its referenced assets into a src/ directory */
2
+ export declare const downloadPage: (url: string, outputDir: string) => Promise<void>;
@@ -0,0 +1,94 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import https from "node:https";
4
+ import http from "node:http";
5
+ /** Fetch that tolerates expired/self-signed SSL certificates */
6
+ const fetchPage = async (url) => {
7
+ const parsed = new URL(url);
8
+ const isHttps = parsed.protocol === "https:";
9
+ return new Promise((resolve, reject) => {
10
+ const mod = isHttps ? https : http;
11
+ const opts = isHttps ? { rejectUnauthorized: false } : {};
12
+ const req = mod.get(url, opts, (res) => {
13
+ const chunks = [];
14
+ res.on("data", (chunk) => chunks.push(chunk));
15
+ res.on("end", () => {
16
+ const buffer = Buffer.concat(chunks);
17
+ const ab = new ArrayBuffer(buffer.byteLength);
18
+ const view = new Uint8Array(ab);
19
+ view.set(buffer);
20
+ resolve({
21
+ ok: (res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300,
22
+ status: res.statusCode ?? 0,
23
+ text: async () => buffer.toString("utf-8"),
24
+ arrayBuffer: async () => ab,
25
+ });
26
+ });
27
+ });
28
+ req.on("error", reject);
29
+ });
30
+ };
31
+ /** Downloads an HTML page and its referenced assets into a src/ directory */
32
+ export const downloadPage = async (url, outputDir) => {
33
+ const srcDir = path.join(outputDir, "src");
34
+ fs.mkdirSync(srcDir, { recursive: true });
35
+ // Download the HTML
36
+ const response = await fetchPage(url);
37
+ if (!response.ok)
38
+ throw new Error(`Failed to fetch ${url}: ${response.status}`);
39
+ let html = await response.text();
40
+ const baseURL = new URL(url);
41
+ const downloaded = new Set();
42
+ // Find referenced assets (src, href, url())
43
+ const assetRefs = extractAssetRefs(html);
44
+ for (const ref of assetRefs) {
45
+ if (downloaded.has(ref))
46
+ continue;
47
+ if (ref.startsWith("data:") || ref.startsWith("#") || ref.startsWith("javascript:"))
48
+ continue;
49
+ try {
50
+ const assetURL = new URL(ref, baseURL);
51
+ // Only download same-origin assets
52
+ if (assetURL.origin !== baseURL.origin)
53
+ continue;
54
+ const assetPath = assetURL.pathname.replace(/^\//, "");
55
+ if (!assetPath)
56
+ continue;
57
+ const localPath = path.join(srcDir, assetPath);
58
+ fs.mkdirSync(path.dirname(localPath), { recursive: true });
59
+ const assetResponse = await fetchPage(assetURL.href);
60
+ if (!assetResponse.ok)
61
+ continue;
62
+ const ab = await assetResponse.arrayBuffer();
63
+ fs.writeFileSync(localPath, new Uint8Array(ab));
64
+ // Rewrite reference in HTML to local path
65
+ html = html.replaceAll(ref, assetPath);
66
+ downloaded.add(ref);
67
+ }
68
+ catch {
69
+ // Skip assets that fail to download
70
+ }
71
+ }
72
+ // Write the HTML file
73
+ fs.writeFileSync(path.join(srcDir, "index.html"), html);
74
+ console.log(` Saved index.html + ${downloaded.size} asset(s) to ${path.relative(process.cwd(), srcDir)}/`);
75
+ };
76
+ /** Extracts asset references from HTML using regex */
77
+ const extractAssetRefs = (html) => {
78
+ const refs = [];
79
+ // src="..." and href="..."
80
+ const attrRegex = /(?:src|href)=["']([^"']+)["']/gi;
81
+ let match;
82
+ while ((match = attrRegex.exec(html)) !== null) {
83
+ if (match[1])
84
+ refs.push(match[1]);
85
+ }
86
+ // url("...") in CSS
87
+ const urlRegex = /url\(["']?([^"')]+)["']?\)/gi;
88
+ while ((match = urlRegex.exec(html)) !== null) {
89
+ if (match[1])
90
+ refs.push(match[1]);
91
+ }
92
+ return [...new Set(refs)];
93
+ };
94
+ //# sourceMappingURL=page-downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-downloader.js","sourceRoot":"","sources":["../../src/download/page-downloader.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/lib/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { login } from "./commands/login.js";
3
+ import { upload } from "./commands/upload.js";
4
+ import { gameCreate } from "./commands/game/create.js";
5
+ const [command, ...args] = process.argv.slice(2);
6
+ const printUsage = () => {
7
+ console.log(`Usage:
8
+ puzzmo login <token> Save a CLI auth token
9
+ puzzmo upload <slug> <dir> Upload game build from <dir>
10
+ puzzmo game create [token] Create a new Puzzmo game project`);
11
+ };
12
+ const run = async () => {
13
+ switch (command) {
14
+ case "login": {
15
+ const token = args[0];
16
+ if (!token) {
17
+ console.error("Usage: puzzmo login <token>");
18
+ process.exit(1);
19
+ }
20
+ login(token);
21
+ break;
22
+ }
23
+ case "upload": {
24
+ const gameSlug = args[0];
25
+ const dir = args[1];
26
+ if (!gameSlug || !dir) {
27
+ console.error("Usage: puzzmo upload <gameSlug> <dir>");
28
+ process.exit(1);
29
+ }
30
+ await upload(gameSlug, dir);
31
+ break;
32
+ }
33
+ case "game": {
34
+ const [subcommand, ...subArgs] = args;
35
+ if (subcommand === "create") {
36
+ await gameCreate(subArgs);
37
+ }
38
+ else {
39
+ console.error("Usage: puzzmo game create [token] [--name <name>] [--url <url>] [--agent <agent>]");
40
+ process.exit(1);
41
+ }
42
+ break;
43
+ }
44
+ default:
45
+ printUsage();
46
+ break;
47
+ }
48
+ };
49
+ run().catch((err) => {
50
+ console.error(err.message || err);
51
+ process.exit(1);
52
+ });
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAEtD,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEhD,MAAM,UAAU,GAAG,GAAG,EAAE;IACtB,OAAO,CAAC,GAAG,CAAC;;;mEAGqD,CAAC,CAAA;AACpE,CAAC,CAAA;AAED,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;IACrB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACrB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAA;gBAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,CAAA;YACZ,MAAK;QACP,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAA;gBACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAM,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;YAC3B,MAAK;QACP,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,UAAU,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;YACrC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC5B,MAAM,UAAU,CAAC,OAAO,CAAC,CAAA;YAC3B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,mFAAmF,CAAC,CAAA;gBAClG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,MAAK;QACP,CAAC;QACD;YACE,UAAU,EAAE,CAAA;YACZ,MAAK;IACT,CAAC;AACH,CAAC,CAAA;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAClB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAA;IACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,10 @@
1
+ type CompleteResponse = {
2
+ cdnBase: string;
3
+ versionID: string;
4
+ error?: string;
5
+ };
6
+ /** Callback for reporting batch upload progress */
7
+ export type UploadProgress = (batch: number, totalBatches: number, uploaded: number) => void;
8
+ /** Multi-step upload: init -> batched file uploads -> complete */
9
+ export declare const uploadFiles: (token: string, gameSlug: string, sha: string, filePaths: string[], baseDir: string, onProgress?: UploadProgress) => Promise<CompleteResponse>;
10
+ export {};
package/lib/lib/api.js ADDED
@@ -0,0 +1,53 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { getAPIURL } from "./config.js";
4
+ const BATCH_SIZE = 10;
5
+ /** Sends a JSON POST request */
6
+ const jsonPost = async (url, token, body) => {
7
+ const res = await fetch(url, {
8
+ method: "POST",
9
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
10
+ body: JSON.stringify(body),
11
+ });
12
+ const json = await res.json();
13
+ if (!res.ok)
14
+ throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
15
+ return json;
16
+ };
17
+ /** Uploads a single file as raw binary with filename in query param */
18
+ const uploadFile = async (url, token, filePath, baseDir) => {
19
+ const relativePath = path.relative(baseDir, filePath);
20
+ const content = fs.readFileSync(filePath);
21
+ const res = await fetch(`${url}?name=${encodeURIComponent(relativePath)}`, {
22
+ method: "POST",
23
+ headers: {
24
+ Authorization: `Bearer ${token}`,
25
+ "Content-Type": "application/octet-stream",
26
+ },
27
+ body: content,
28
+ });
29
+ const json = await res.json();
30
+ if (!res.ok)
31
+ throw new Error(`Server error (${res.status}): ${json.error || "Unknown error"}`);
32
+ return json;
33
+ };
34
+ /** Multi-step upload: init -> batched file uploads -> complete */
35
+ export const uploadFiles = async (token, gameSlug, sha, filePaths, baseDir, onProgress) => {
36
+ const apiURL = getAPIURL();
37
+ // Step 1: Init session
38
+ const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha }));
39
+ // Step 2: Upload files in concurrent batches
40
+ const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`;
41
+ const totalBatches = Math.ceil(filePaths.length / BATCH_SIZE);
42
+ let totalUploaded = 0;
43
+ for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
44
+ const batch = filePaths.slice(i, i + BATCH_SIZE);
45
+ const batchNum = Math.floor(i / BATCH_SIZE) + 1;
46
+ await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir)));
47
+ totalUploaded += batch.length;
48
+ onProgress?.(batchNum, totalBatches, totalUploaded);
49
+ }
50
+ // Step 3: Complete
51
+ return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}));
52
+ };
53
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ type Config = {
2
+ token?: string;
3
+ apiURL?: string;
4
+ };
5
+ /** Reads the CLI config from ~/.puzzmo/config.json */
6
+ export declare const readConfig: () => Config;
7
+ /** Writes the CLI config to ~/.puzzmo/config.json */
8
+ export declare const writeConfig: (config: Config) => void;
9
+ /** Returns the auth token from config or PUZZMO_TOKEN env var */
10
+ export declare const getToken: () => string | undefined;
11
+ /** Returns the API base URL */
12
+ export declare const getAPIURL: () => string;
13
+ export {};
@@ -0,0 +1,29 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ const configDir = path.join(os.homedir(), ".puzzmo");
5
+ const configPath = path.join(configDir, "config.json");
6
+ /** Reads the CLI config from ~/.puzzmo/config.json */
7
+ export const readConfig = () => {
8
+ try {
9
+ const raw = fs.readFileSync(configPath, "utf-8");
10
+ return JSON.parse(raw);
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ };
16
+ /** Writes the CLI config to ~/.puzzmo/config.json */
17
+ export const writeConfig = (config) => {
18
+ fs.mkdirSync(configDir, { recursive: true });
19
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
20
+ };
21
+ /** Returns the auth token from config or PUZZMO_TOKEN env var */
22
+ export const getToken = () => {
23
+ return process.env.PUZZMO_TOKEN || readConfig().token;
24
+ };
25
+ /** Returns the API base URL */
26
+ export const getAPIURL = () => {
27
+ return process.env.PUZZMO_API_URL || readConfig().apiURL || "https://api.puzzmo.com";
28
+ };
29
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAA;AACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;AAOtD,sDAAsD;AACtD,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC,CAAA;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,EAAE;IAC5C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AACtE,CAAC,CAAA;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAuB,EAAE;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC,KAAK,CAAA;AACvD,CAAC,CAAA;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,SAAS,GAAG,GAAW,EAAE;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE,CAAC,MAAM,IAAI,wBAAwB,CAAA;AACtF,CAAC,CAAA"}