@puckeditor/cli 0.24.0-canary.301a0304 → 0.24.0-canary.6f40e09b
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 +2 -0
- package/dist/bin.mjs +384 -69
- package/dist/docs/cli.md +14 -4
- package/dist/templates/express/src/puck/pages.ts +13 -8
- package/dist/templates/express-ai/src/puck/pages.ts +13 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,8 +20,10 @@ Other commands:
|
|
|
20
20
|
npx @puckeditor/cli add editor # add the Puck editor
|
|
21
21
|
npx @puckeditor/cli add cloud # connect Puck Cloud
|
|
22
22
|
npx @puckeditor/cli add ai # add the Puck AI plugin
|
|
23
|
+
npx @puckeditor/cli connect # log in to Puck Cloud again for a new API key
|
|
23
24
|
npx @puckeditor/cli status # show what's set up
|
|
24
25
|
npx @puckeditor/cli doctor # diagnose problems
|
|
26
|
+
npx @puckeditor/cli frameworks # show supported frameworks and versions
|
|
25
27
|
npx @puckeditor/cli docs # read the docs for this version
|
|
26
28
|
```
|
|
27
29
|
|
package/dist/bin.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/bin.ts
|
|
4
4
|
import os from "os";
|
|
5
|
-
import
|
|
5
|
+
import path32 from "path";
|
|
6
6
|
import fs7 from "fs";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
@@ -38,6 +38,7 @@ var exitCodeByError = {
|
|
|
38
38
|
"PUCK-CLI-FRAMEWORK-MISMATCH": EXIT_CODES.unsupported,
|
|
39
39
|
"PUCK-CLI-BACKEND-REQUIRED": EXIT_CODES.usage,
|
|
40
40
|
"PUCK-CLI-BACKEND-URL-REQUIRED": EXIT_CODES.usage,
|
|
41
|
+
"PUCK-CLI-EXTERNAL-BACKEND": EXIT_CODES.unsupported,
|
|
41
42
|
"PUCK-CLI-WORKSPACE-TARGET-NOT-FOUND": EXIT_CODES.unsupported,
|
|
42
43
|
"PUCK-CLI-TYPESCRIPT-REQUIRED": EXIT_CODES.unsupported,
|
|
43
44
|
"PUCK-CLI-TARGET-DIR-NOT-EMPTY": EXIT_CODES.filesystem,
|
|
@@ -67,9 +68,6 @@ var CliError = class extends Error {
|
|
|
67
68
|
// src/args.ts
|
|
68
69
|
import { parseArgs } from "util";
|
|
69
70
|
|
|
70
|
-
// src/detect/framework.ts
|
|
71
|
-
import path3 from "path";
|
|
72
|
-
|
|
73
71
|
// src/constants.ts
|
|
74
72
|
var ENV_KEY = "PUCK_API_KEY";
|
|
75
73
|
var DEFAULT_CLOUD_URL = "https://cloud.puckeditor.com";
|
|
@@ -82,6 +80,9 @@ var CLOUD_CLIENT_PACKAGE = "@puckeditor/cloud-client";
|
|
|
82
80
|
var PLUGIN_AI_PACKAGE = "@puckeditor/plugin-ai";
|
|
83
81
|
var CANONICAL_INVOCATION = "npx @puckeditor/cli";
|
|
84
82
|
|
|
83
|
+
// src/detect/framework.ts
|
|
84
|
+
import path3 from "path";
|
|
85
|
+
|
|
85
86
|
// src/ast/parse.ts
|
|
86
87
|
import { parse } from "@babel/parser";
|
|
87
88
|
var isTsx = (filename) => /\.(tsx|jsx|js|mjs|cjs)$/.test(filename);
|
|
@@ -190,12 +191,13 @@ var resolveInstalledVersion = (vfs, fromDir, name) => {
|
|
|
190
191
|
dir = parent;
|
|
191
192
|
}
|
|
192
193
|
};
|
|
193
|
-
var
|
|
194
|
+
var versionOf = (versionOrRange) => {
|
|
194
195
|
if (!versionOrRange) return null;
|
|
195
196
|
const cleaned = versionOrRange.replace(/^(workspace:|npm:[^@]+@)/, "");
|
|
196
|
-
const match = /(\d+)(
|
|
197
|
-
return match ? Number(match[1]) : null;
|
|
197
|
+
const match = /(\d+)(?:\.(\d+))?/.exec(cleaned);
|
|
198
|
+
return match ? [Number(match[1]), Number(match[2] ?? 0)] : null;
|
|
198
199
|
};
|
|
200
|
+
var majorOf = (versionOrRange) => versionOf(versionOrRange)?.[0] ?? null;
|
|
199
201
|
|
|
200
202
|
// src/detect/server.ts
|
|
201
203
|
import path2 from "path";
|
|
@@ -283,7 +285,42 @@ var FRAMEWORK_LABELS = {
|
|
|
283
285
|
hono: "Hono",
|
|
284
286
|
express: "Express"
|
|
285
287
|
};
|
|
288
|
+
var MIN_VERSIONS = {
|
|
289
|
+
next: "15",
|
|
290
|
+
"react-router": "7",
|
|
291
|
+
// Server route handlers on createFileRoute arrived in the Start RC
|
|
292
|
+
"tanstack-start": "1.132",
|
|
293
|
+
// vinext 1.0 moved to Vite 8, which the recipe uses
|
|
294
|
+
vinext: "1",
|
|
295
|
+
// @hono/vite-dev-server is built and tested against Vite 6
|
|
296
|
+
vite: "6",
|
|
297
|
+
// @astrojs/node 11, which `astro add node` installs, needs Astro 7
|
|
298
|
+
astro: "7",
|
|
299
|
+
// @hono/node-server 2 needs Hono 4
|
|
300
|
+
hono: "4",
|
|
301
|
+
// express.json() arrived in 4.16
|
|
302
|
+
express: "4.16"
|
|
303
|
+
};
|
|
286
304
|
var SUPPORTED_FRAMEWORKS = "Next.js (App Router), React Router 7 framework mode, TanStack Start, vinext, Astro and Vite React apps, or a Hono or Express server";
|
|
305
|
+
var unsupportedVersion = (id, version, range) => {
|
|
306
|
+
const found = versionOf(version) ?? versionOf(range);
|
|
307
|
+
const [major, minor] = versionOf(MIN_VERSIONS[id]);
|
|
308
|
+
if (!found || found[0] > major || found[0] === major && found[1] >= minor)
|
|
309
|
+
return null;
|
|
310
|
+
const label2 = FRAMEWORK_LABELS[id];
|
|
311
|
+
return {
|
|
312
|
+
status: "unsupported",
|
|
313
|
+
error: {
|
|
314
|
+
code: "PUCK-CLI-UNSUPPORTED-FRAMEWORK-VERSION",
|
|
315
|
+
message: `${label2} ${version ?? range} is not supported. Upgrade to ${label2} ${MIN_VERSIONS[id]} or later, or integrate Puck manually.`,
|
|
316
|
+
details: {
|
|
317
|
+
docs: MANUAL_INTEGRATION_DOCS_URL,
|
|
318
|
+
version: version ?? range,
|
|
319
|
+
minVersion: MIN_VERSIONS[id]
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
};
|
|
287
324
|
var firstExisting = (vfs, root, candidates) => candidates.find((c) => vfs.exists(path3.join(root, c))) ?? null;
|
|
288
325
|
var CONFIG_EXTENSIONS = ["ts", "mts", "js", "mjs", "cts", "cjs"];
|
|
289
326
|
var findViteConfig = (vfs, root) => firstExisting(
|
|
@@ -338,19 +375,8 @@ var detectNext = (vfs, root, range) => {
|
|
|
338
375
|
const major = majorOf(version) ?? majorOf(range) ?? 16;
|
|
339
376
|
const layout = detectAppRouter(vfs, root, "Next.js");
|
|
340
377
|
if ("status" in layout) return layout;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
status: "unsupported",
|
|
344
|
-
error: {
|
|
345
|
-
code: "PUCK-CLI-UNSUPPORTED-FRAMEWORK-VERSION",
|
|
346
|
-
message: `Next.js ${version ?? range} is not supported. Upgrade to Next.js 15 or later, or integrate Puck manually.`,
|
|
347
|
-
details: {
|
|
348
|
-
docs: MANUAL_INTEGRATION_DOCS_URL,
|
|
349
|
-
version: version ?? range
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
}
|
|
378
|
+
const tooOld = unsupportedVersion("next", version, range);
|
|
379
|
+
if (tooOld) return tooOld;
|
|
354
380
|
return {
|
|
355
381
|
status: "detected",
|
|
356
382
|
info: {
|
|
@@ -364,6 +390,8 @@ var detectNext = (vfs, root, range) => {
|
|
|
364
390
|
};
|
|
365
391
|
var detectVinext = (vfs, root, range) => {
|
|
366
392
|
const version = resolveInstalledVersion(vfs, root, "vinext");
|
|
393
|
+
const tooOld = unsupportedVersion("vinext", version, range);
|
|
394
|
+
if (tooOld) return tooOld;
|
|
367
395
|
const layout = detectAppRouter(vfs, root, "vinext");
|
|
368
396
|
if ("status" in layout) return layout;
|
|
369
397
|
const base = layout.baseDir ? `${layout.baseDir}/` : "";
|
|
@@ -385,16 +413,8 @@ var detectVinext = (vfs, root, range) => {
|
|
|
385
413
|
var detectReactRouter = (vfs, root, range) => {
|
|
386
414
|
const version = resolveInstalledVersion(vfs, root, "@react-router/dev") ?? resolveInstalledVersion(vfs, root, "react-router");
|
|
387
415
|
const major = majorOf(version) ?? majorOf(range);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
status: "unsupported",
|
|
391
|
-
error: {
|
|
392
|
-
code: "PUCK-CLI-UNSUPPORTED-FRAMEWORK-VERSION",
|
|
393
|
-
message: `React Router ${version ?? range} is not supported. Upgrade to React Router 7 framework mode.`,
|
|
394
|
-
details: { docs: MANUAL_INTEGRATION_DOCS_URL }
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
}
|
|
416
|
+
const tooOld = unsupportedVersion("react-router", version, range);
|
|
417
|
+
if (tooOld) return tooOld;
|
|
398
418
|
const configFile = firstExisting(
|
|
399
419
|
vfs,
|
|
400
420
|
root,
|
|
@@ -456,6 +476,8 @@ var detectReactRouter = (vfs, root, range) => {
|
|
|
456
476
|
};
|
|
457
477
|
var detectTanStackStart = (vfs, root, range) => {
|
|
458
478
|
const version = resolveInstalledVersion(vfs, root, "@tanstack/react-start");
|
|
479
|
+
const tooOld = unsupportedVersion("tanstack-start", version, range);
|
|
480
|
+
if (tooOld) return tooOld;
|
|
459
481
|
const viteConfig = findViteConfig(vfs, root);
|
|
460
482
|
const code = viteConfig ? vfs.readText(path3.join(root, viteConfig)) ?? "" : "";
|
|
461
483
|
const dirs = { srcDirectory: "src", routesDirectory: "routes" };
|
|
@@ -503,6 +525,8 @@ var detectVite = (vfs, root, range, deps) => {
|
|
|
503
525
|
(f) => Boolean(f) && vfs.exists(path3.join(root, f))
|
|
504
526
|
) ?? null;
|
|
505
527
|
const version = resolveInstalledVersion(vfs, root, "vite");
|
|
528
|
+
const tooOld = unsupportedVersion("vite", version, range);
|
|
529
|
+
if (tooOld) return tooOld;
|
|
506
530
|
const viteConfig = findViteConfig(vfs, root);
|
|
507
531
|
const code = viteConfig ? vfs.readText(path3.join(root, viteConfig)) ?? "" : "";
|
|
508
532
|
return {
|
|
@@ -527,6 +551,8 @@ var ASTRO_ADAPTERS = [
|
|
|
527
551
|
];
|
|
528
552
|
var detectAstro = (vfs, root, range, deps) => {
|
|
529
553
|
const version = resolveInstalledVersion(vfs, root, "astro");
|
|
554
|
+
const tooOld = unsupportedVersion("astro", version, range);
|
|
555
|
+
if (tooOld) return tooOld;
|
|
530
556
|
const config = firstExisting(
|
|
531
557
|
vfs,
|
|
532
558
|
root,
|
|
@@ -550,6 +576,8 @@ var detectAstro = (vfs, root, range, deps) => {
|
|
|
550
576
|
};
|
|
551
577
|
var detectServer = (id) => (vfs, root, range, deps) => {
|
|
552
578
|
const version = resolveInstalledVersion(vfs, root, id);
|
|
579
|
+
const tooOld = unsupportedVersion(id, version, range);
|
|
580
|
+
if (tooOld) return tooOld;
|
|
553
581
|
return {
|
|
554
582
|
status: "detected",
|
|
555
583
|
info: {
|
|
@@ -772,17 +800,21 @@ var MUTATING = [
|
|
|
772
800
|
var COMMAND_OPTIONS = {
|
|
773
801
|
init: [...GLOBAL, ...MUTATING, "framework", "name", "ai", "no-ai"],
|
|
774
802
|
add: [...GLOBAL, ...MUTATING],
|
|
803
|
+
connect: [...GLOBAL, "api-key", "config", "wait"],
|
|
775
804
|
status: ["json", "help", "version", "cwd", "workspace", "config"],
|
|
776
805
|
doctor: ["json", "help", "version", "cwd", "workspace", "config", "offline"],
|
|
777
806
|
docs: ["json", "help", "version"],
|
|
807
|
+
frameworks: ["json", "help", "version"],
|
|
778
808
|
help: GLOBAL
|
|
779
809
|
};
|
|
780
810
|
var COMMANDS = [
|
|
781
811
|
"init",
|
|
782
812
|
"add",
|
|
813
|
+
"connect",
|
|
783
814
|
"status",
|
|
784
815
|
"doctor",
|
|
785
816
|
"docs",
|
|
817
|
+
"frameworks",
|
|
786
818
|
"help"
|
|
787
819
|
];
|
|
788
820
|
var FRAMEWORKS = FRAMEWORK_IDS;
|
|
@@ -807,7 +839,7 @@ var parseCliArgs = (argv) => {
|
|
|
807
839
|
if (!COMMANDS.includes(first)) {
|
|
808
840
|
throw new CliError(
|
|
809
841
|
"PUCK-CLI-UNKNOWN-COMMAND",
|
|
810
|
-
`Unknown command "${first}". Run
|
|
842
|
+
`Unknown command "${first}". Run \`${CANONICAL_INVOCATION} --help\` to see available commands.`
|
|
811
843
|
);
|
|
812
844
|
}
|
|
813
845
|
command = first;
|
|
@@ -818,7 +850,7 @@ var parseCliArgs = (argv) => {
|
|
|
818
850
|
if (!allowed.includes(key)) {
|
|
819
851
|
throw new CliError(
|
|
820
852
|
"PUCK-CLI-INVALID-ARGS",
|
|
821
|
-
`--${key} can't be used with \`${command ??
|
|
853
|
+
`--${key} can't be used with \`${command ?? CANONICAL_INVOCATION}\`.`
|
|
822
854
|
);
|
|
823
855
|
}
|
|
824
856
|
}
|
|
@@ -1127,11 +1159,19 @@ var COMMANDS2 = [
|
|
|
1127
1159
|
name: "add <editor|cloud|ai>",
|
|
1128
1160
|
description: "Add one capability (ai includes cloud, cloud includes editor)"
|
|
1129
1161
|
},
|
|
1162
|
+
{
|
|
1163
|
+
name: "connect",
|
|
1164
|
+
description: "Log in to Puck Cloud again and replace PUCK_API_KEY"
|
|
1165
|
+
},
|
|
1130
1166
|
{ name: "status", description: "Show what's installed and configured" },
|
|
1131
1167
|
{
|
|
1132
1168
|
name: "doctor",
|
|
1133
1169
|
description: "Diagnose problems and suggest fixes (read-only)"
|
|
1134
1170
|
},
|
|
1171
|
+
{
|
|
1172
|
+
name: "frameworks",
|
|
1173
|
+
description: "Show the supported frameworks and versions"
|
|
1174
|
+
},
|
|
1135
1175
|
{
|
|
1136
1176
|
name: "docs <command>",
|
|
1137
1177
|
description: "Read the docs for this version: ls, cat <page>, find <query>, grep <text>"
|
|
@@ -1471,7 +1511,8 @@ var templates_default = {
|
|
|
1471
1511
|
"*.tsbuildinfo",
|
|
1472
1512
|
"eslint.config.mjs",
|
|
1473
1513
|
".env*"
|
|
1474
|
-
]
|
|
1514
|
+
],
|
|
1515
|
+
allowBuilds: ["esbuild"]
|
|
1475
1516
|
};
|
|
1476
1517
|
|
|
1477
1518
|
// src/templates/source.ts
|
|
@@ -2517,10 +2558,10 @@ var analyzeRoutes = (code, filename) => {
|
|
|
2517
2558
|
const topLevel = new Set(exported.elements.filter(Boolean));
|
|
2518
2559
|
walk(exported, (node) => {
|
|
2519
2560
|
if (!isRouteCall(node, routeLocal)) return;
|
|
2520
|
-
const
|
|
2561
|
+
const path33 = stringArg(node, 0);
|
|
2521
2562
|
const isTop = topLevel.has(node);
|
|
2522
|
-
calls.push({ path:
|
|
2523
|
-
if (
|
|
2563
|
+
calls.push({ path: path33, file: stringArg(node, 1), topLevel: isTop });
|
|
2564
|
+
if (path33 === "*") {
|
|
2524
2565
|
if (isTop) splat = node;
|
|
2525
2566
|
else nestedSplat = true;
|
|
2526
2567
|
}
|
|
@@ -4671,6 +4712,26 @@ var statusTable = (result) => {
|
|
|
4671
4712
|
const width = Math.max(...rows.map(([k]) => k.length), 0) + 2;
|
|
4672
4713
|
return rows.map(([k, v]) => `${k.padEnd(width)}${v}`);
|
|
4673
4714
|
};
|
|
4715
|
+
var frameworksTable = (frameworks) => {
|
|
4716
|
+
const rows = [
|
|
4717
|
+
["Framework", "ID", "Versions", "Editor", "Puck Cloud & AI", "Notes"],
|
|
4718
|
+
...frameworks.map((f) => [
|
|
4719
|
+
f.name,
|
|
4720
|
+
f.id,
|
|
4721
|
+
`${f.minVersion}+`,
|
|
4722
|
+
f.kind === "server" ? "API only" : "\u2713",
|
|
4723
|
+
f.needsServer ? "needs a server*" : "\u2713",
|
|
4724
|
+
f.notes
|
|
4725
|
+
])
|
|
4726
|
+
];
|
|
4727
|
+
const widths = rows[0].map(
|
|
4728
|
+
(_, i) => Math.max(...rows.map((row) => row[i].length))
|
|
4729
|
+
);
|
|
4730
|
+
return rows.map((row, r) => {
|
|
4731
|
+
const line = row.map((cell, i) => i === row.length - 1 ? cell : cell.padEnd(widths[i])).join(" ").trimEnd();
|
|
4732
|
+
return r === 0 ? pc.bold(line) : line;
|
|
4733
|
+
});
|
|
4734
|
+
};
|
|
4674
4735
|
var presentHuman = (out, result, secrets) => {
|
|
4675
4736
|
const lines = [];
|
|
4676
4737
|
const write = () => out.write(secrets.scrub(lines.join("\n")) + "\n");
|
|
@@ -4704,6 +4765,18 @@ var presentHuman = (out, result, secrets) => {
|
|
|
4704
4765
|
lines.push(` ${" ".repeat(24)} ${pc.cyan(f.fix)}`);
|
|
4705
4766
|
}
|
|
4706
4767
|
lines.push("", result.message);
|
|
4768
|
+
} else if (result.command === "frameworks" && result.status !== "error") {
|
|
4769
|
+
lines.push(...frameworksTable(result.frameworks ?? []));
|
|
4770
|
+
const needServer = (result.frameworks ?? []).filter((f) => f.needsServer);
|
|
4771
|
+
if (needServer.length) {
|
|
4772
|
+
lines.push(
|
|
4773
|
+
"",
|
|
4774
|
+
`* ${needServer.map((f) => f.name).join(
|
|
4775
|
+
" and "
|
|
4776
|
+
)} need a server for Puck Cloud and Puck AI, unless they already have one. Choose one with --backend.`
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
lines.push("", pc.dim(result.message));
|
|
4707
4780
|
} else if (result.command === "docs" && result.status !== "error") {
|
|
4708
4781
|
const { page, pages, matches } = result.docs ?? {};
|
|
4709
4782
|
if (page) lines.push(page.content.trimEnd());
|
|
@@ -4765,7 +4838,7 @@ var presentHuman = (out, result, secrets) => {
|
|
|
4765
4838
|
...result.warnings.map((w) => `${pc.yellow("!")} ${w.message}`)
|
|
4766
4839
|
);
|
|
4767
4840
|
}
|
|
4768
|
-
if (result.status !== "error" && result.command !== "doctor" && result.command !== "docs" && result.nextSteps.length) {
|
|
4841
|
+
if (result.status !== "error" && result.command !== "doctor" && result.command !== "docs" && result.command !== "frameworks" && result.nextSteps.length) {
|
|
4769
4842
|
lines.push(
|
|
4770
4843
|
"",
|
|
4771
4844
|
pc.bold("Next:"),
|
|
@@ -4778,7 +4851,7 @@ var presentHuman = (out, result, secrets) => {
|
|
|
4778
4851
|
};
|
|
4779
4852
|
|
|
4780
4853
|
// src/commands/init.ts
|
|
4781
|
-
import
|
|
4854
|
+
import path28 from "path";
|
|
4782
4855
|
|
|
4783
4856
|
// src/io/vfs.ts
|
|
4784
4857
|
import fs2 from "fs";
|
|
@@ -5248,7 +5321,7 @@ var resolveTarget = async (rc, opts = {}) => {
|
|
|
5248
5321
|
};
|
|
5249
5322
|
|
|
5250
5323
|
// src/commands/mutate.ts
|
|
5251
|
-
import
|
|
5324
|
+
import path27 from "path";
|
|
5252
5325
|
|
|
5253
5326
|
// src/plan/planner.ts
|
|
5254
5327
|
import path22 from "path";
|
|
@@ -5506,6 +5579,66 @@ var planEnvWrite = (p, secret) => {
|
|
|
5506
5579
|
planGitignore(p, target);
|
|
5507
5580
|
};
|
|
5508
5581
|
|
|
5582
|
+
// src/plan/pnpm-builds.ts
|
|
5583
|
+
import path24 from "path";
|
|
5584
|
+
import { Document, isMap, isSeq, parseDocument } from "yaml";
|
|
5585
|
+
var LEGACY_LISTS = [
|
|
5586
|
+
"onlyBuiltDependencies",
|
|
5587
|
+
"neverBuiltDependencies",
|
|
5588
|
+
"ignoredBuiltDependencies"
|
|
5589
|
+
];
|
|
5590
|
+
var allowAll = (names) => Object.fromEntries(names.map((name) => [name, true]));
|
|
5591
|
+
var withAllowedBuilds = (text, names) => {
|
|
5592
|
+
const doc = parseDocument(text ?? "");
|
|
5593
|
+
if (doc.errors.length > 0) return null;
|
|
5594
|
+
if (doc.get("dangerouslyAllowAllBuilds") === true) return null;
|
|
5595
|
+
const allowBuilds = doc.get("allowBuilds");
|
|
5596
|
+
const covered = /* @__PURE__ */ new Set();
|
|
5597
|
+
if (isMap(allowBuilds)) {
|
|
5598
|
+
for (const item of allowBuilds.items) covered.add(String(item.key));
|
|
5599
|
+
}
|
|
5600
|
+
for (const list of LEGACY_LISTS) {
|
|
5601
|
+
const seq = doc.get(list);
|
|
5602
|
+
if (isSeq(seq)) for (const item of seq.toJSON()) covered.add(String(item));
|
|
5603
|
+
}
|
|
5604
|
+
const missing = names.filter((name) => !covered.has(name));
|
|
5605
|
+
if (missing.length === 0) return null;
|
|
5606
|
+
const legacy = doc.get("onlyBuiltDependencies");
|
|
5607
|
+
if (isMap(allowBuilds)) {
|
|
5608
|
+
for (const name of missing) allowBuilds.set(name, true);
|
|
5609
|
+
} else if (isSeq(legacy)) {
|
|
5610
|
+
for (const name of missing) legacy.add(name);
|
|
5611
|
+
} else if (doc.contents === null) {
|
|
5612
|
+
const created = new Document({ allowBuilds: allowAll(missing) });
|
|
5613
|
+
created.commentBefore = " Dependencies allowed to run install scripts (required by pnpm 11+)";
|
|
5614
|
+
return created.toString();
|
|
5615
|
+
} else {
|
|
5616
|
+
doc.set("allowBuilds", allowAll(missing));
|
|
5617
|
+
}
|
|
5618
|
+
return doc.toString();
|
|
5619
|
+
};
|
|
5620
|
+
var planAllowedBuilds = (planner) => {
|
|
5621
|
+
const { ctx, vfs } = planner;
|
|
5622
|
+
if (ctx.packageManager.name !== "pnpm") return;
|
|
5623
|
+
const ws = ctx.workspace;
|
|
5624
|
+
if (ws && ws.source !== "pnpm-workspace.yaml") return;
|
|
5625
|
+
const file = path24.join(ws?.root ?? ctx.root, "pnpm-workspace.yaml");
|
|
5626
|
+
const names = templates_default.allowBuilds;
|
|
5627
|
+
const list = names.join(", ");
|
|
5628
|
+
const next = withAllowedBuilds(vfs.readText(file), names);
|
|
5629
|
+
if (next === null) return;
|
|
5630
|
+
vfs.write(file, next);
|
|
5631
|
+
if (!ws) return;
|
|
5632
|
+
planner.steps.push({
|
|
5633
|
+
id: "bootstrap:allow-builds",
|
|
5634
|
+
kind: "modify_file",
|
|
5635
|
+
capability: "bootstrap",
|
|
5636
|
+
summary: `Allow ${list} build scripts in the workspace's pnpm-workspace.yaml`,
|
|
5637
|
+
path: file,
|
|
5638
|
+
edits: []
|
|
5639
|
+
});
|
|
5640
|
+
};
|
|
5641
|
+
|
|
5509
5642
|
// src/plan/backend.ts
|
|
5510
5643
|
var choicesFor = (options, wantsCloud) => [
|
|
5511
5644
|
{ value: "add", label: options.addLabel },
|
|
@@ -5575,14 +5708,14 @@ var resolveBackend = async (rc, options, capabilities, planned) => {
|
|
|
5575
5708
|
|
|
5576
5709
|
// src/auth/session-store.ts
|
|
5577
5710
|
import fs4 from "fs";
|
|
5578
|
-
import
|
|
5711
|
+
import path25 from "path";
|
|
5579
5712
|
import { createHash as createHash2 } from "crypto";
|
|
5580
5713
|
var SessionStore = class {
|
|
5581
5714
|
#file;
|
|
5582
5715
|
constructor(cacheDir, projectRoot, baseUrl) {
|
|
5583
5716
|
const key = createHash2("sha256").update(`${projectRoot}
|
|
5584
5717
|
${baseUrl}`).digest("hex").slice(0, 32);
|
|
5585
|
-
this.#file =
|
|
5718
|
+
this.#file = path25.join(cacheDir, "puck", "connect", `${key}.json`);
|
|
5586
5719
|
}
|
|
5587
5720
|
get path() {
|
|
5588
5721
|
return this.#file;
|
|
@@ -5596,14 +5729,14 @@ ${baseUrl}`).digest("hex").slice(0, 32);
|
|
|
5596
5729
|
}
|
|
5597
5730
|
}
|
|
5598
5731
|
save(session) {
|
|
5599
|
-
fs4.mkdirSync(
|
|
5732
|
+
fs4.mkdirSync(path25.dirname(this.#file), { recursive: true, mode: 448 });
|
|
5600
5733
|
fs4.writeFileSync(this.#file, JSON.stringify(session), { mode: 384 });
|
|
5601
5734
|
}
|
|
5602
5735
|
clear() {
|
|
5603
5736
|
fs4.rmSync(this.#file, { force: true });
|
|
5604
5737
|
}
|
|
5605
5738
|
};
|
|
5606
|
-
var cacheDirFor = (env2, homedir) => env2.XDG_CACHE_HOME ||
|
|
5739
|
+
var cacheDirFor = (env2, homedir) => env2.XDG_CACHE_HOME || path25.join(homedir, ".cache");
|
|
5607
5740
|
|
|
5608
5741
|
// src/auth/credentials.ts
|
|
5609
5742
|
var apiKeysUrl = (rc) => new URL(API_KEYS_PATH, rc.cloud.baseUrl).toString();
|
|
@@ -5826,7 +5959,7 @@ var promptForKey = async (rc) => {
|
|
|
5826
5959
|
warnings: []
|
|
5827
5960
|
};
|
|
5828
5961
|
};
|
|
5829
|
-
var resolveCredential = async (rc, state, info) => {
|
|
5962
|
+
var resolveCredential = async (rc, state, info, { fresh = false } = {}) => {
|
|
5830
5963
|
const warnings = [];
|
|
5831
5964
|
const fromExplicit = async (value, source) => {
|
|
5832
5965
|
const secret = secretFrom(
|
|
@@ -5852,10 +5985,15 @@ var resolveCredential = async (rc, state, info) => {
|
|
|
5852
5985
|
};
|
|
5853
5986
|
if (rc.flags.apiKey !== void 0)
|
|
5854
5987
|
return fromExplicit(rc.flags.apiKey, "flag");
|
|
5855
|
-
if (rc.deps.env[ENV_KEY])
|
|
5988
|
+
if (fresh && rc.deps.env[ENV_KEY]) {
|
|
5989
|
+
warnings.push({
|
|
5990
|
+
code: "PUCK-CLI-W-KEY-IN-PROCESS-ENV",
|
|
5991
|
+
message: `${ENV_KEY} is set in your environment and overrides the new key in the env file. Unset it to use the new key.`
|
|
5992
|
+
});
|
|
5993
|
+
} else if (rc.deps.env[ENV_KEY])
|
|
5856
5994
|
return fromExplicit(rc.deps.env[ENV_KEY], "process.env");
|
|
5857
5995
|
const existing = state.cloud.apiKey;
|
|
5858
|
-
if (existing.present && existing.value) {
|
|
5996
|
+
if (!fresh && existing.present && existing.value) {
|
|
5859
5997
|
const secret = secretFrom(
|
|
5860
5998
|
rc,
|
|
5861
5999
|
existing.value,
|
|
@@ -5916,7 +6054,7 @@ var resolveCredential = async (rc, state, info) => {
|
|
|
5916
6054
|
|
|
5917
6055
|
// src/apply/applier.ts
|
|
5918
6056
|
import fs5 from "fs";
|
|
5919
|
-
import
|
|
6057
|
+
import path26 from "path";
|
|
5920
6058
|
var tail = (text, lines = 20) => text.trim().split("\n").slice(-lines).join("\n");
|
|
5921
6059
|
var run = async (rc, spec) => {
|
|
5922
6060
|
rc.log(`$ ${formatCommand(spec)}`);
|
|
@@ -5946,14 +6084,14 @@ ${result.stderr}`))
|
|
|
5946
6084
|
}
|
|
5947
6085
|
};
|
|
5948
6086
|
var writeFile = (file, content, mode) => {
|
|
5949
|
-
fs5.mkdirSync(
|
|
6087
|
+
fs5.mkdirSync(path26.dirname(file), { recursive: true });
|
|
5950
6088
|
if (mode === void 0) {
|
|
5951
6089
|
fs5.writeFileSync(file, content);
|
|
5952
6090
|
return;
|
|
5953
6091
|
}
|
|
5954
|
-
const temp =
|
|
5955
|
-
|
|
5956
|
-
`.${
|
|
6092
|
+
const temp = path26.join(
|
|
6093
|
+
path26.dirname(file),
|
|
6094
|
+
`.${path26.basename(file)}.${process.pid}.tmp`
|
|
5957
6095
|
);
|
|
5958
6096
|
fs5.writeFileSync(temp, content, { mode });
|
|
5959
6097
|
fs5.chmodSync(temp, mode);
|
|
@@ -6154,6 +6292,7 @@ var runMutation = async (rc, input) => {
|
|
|
6154
6292
|
}
|
|
6155
6293
|
planCapabilities(planner, capabilities);
|
|
6156
6294
|
planner.finalize();
|
|
6295
|
+
if (bootstrap) planAllowedBuilds(planner);
|
|
6157
6296
|
if (scaffoldStep) planner.steps.unshift(scaffoldStep);
|
|
6158
6297
|
const wantsCloud = capabilities.includes("cloud") && planner.backend?.mode !== "external" && !state.cloud.external;
|
|
6159
6298
|
const needsConsent = planner.steps.length > 0 || wantsCloud && !state.cloud.apiKey.present;
|
|
@@ -6192,7 +6331,7 @@ var runMutation = async (rc, input) => {
|
|
|
6192
6331
|
if (wantsCloud) {
|
|
6193
6332
|
credential = await resolveCredential(rc, state, {
|
|
6194
6333
|
projectRoot: root,
|
|
6195
|
-
projectName: bootstrap?.appName ?? ctx.packageJson?.name ??
|
|
6334
|
+
projectName: bootstrap?.appName ?? ctx.packageJson?.name ?? path27.basename(root),
|
|
6196
6335
|
framework: ctx.framework.id
|
|
6197
6336
|
});
|
|
6198
6337
|
for (const w of credential.warnings) planner.warn(w.code, w.message);
|
|
@@ -6383,14 +6522,14 @@ var runInit = async (rc) => {
|
|
|
6383
6522
|
{ globs: target.workspace.globs }
|
|
6384
6523
|
);
|
|
6385
6524
|
}
|
|
6386
|
-
parentDir =
|
|
6525
|
+
parentDir = path28.join(target.workspace.root, appsDir);
|
|
6387
6526
|
workspaceRoot = target.workspace.root;
|
|
6388
6527
|
for (const pkg of target.workspace.packages) {
|
|
6389
6528
|
if (pkg.name) existingNames.add(pkg.name);
|
|
6390
|
-
existingNames.add(
|
|
6529
|
+
existingNames.add(path28.basename(pkg.dir));
|
|
6391
6530
|
}
|
|
6392
6531
|
} else {
|
|
6393
|
-
if (vfs.exists(
|
|
6532
|
+
if (vfs.exists(path28.join(target.root, "package.json"))) {
|
|
6394
6533
|
const read = readPackageJson(vfs, target.root);
|
|
6395
6534
|
const deps = depsOf(read.status === "ok" ? read.pkg : null);
|
|
6396
6535
|
const detection = detectFramework(vfs, target.root, deps);
|
|
@@ -6480,7 +6619,7 @@ var runInit = async (rc) => {
|
|
|
6480
6619
|
`A workspace package named "${name}" already exists.`
|
|
6481
6620
|
);
|
|
6482
6621
|
}
|
|
6483
|
-
const dir = inPlace ? parentDir :
|
|
6622
|
+
const dir = inPlace ? parentDir : path28.join(parentDir, name);
|
|
6484
6623
|
if (!inPlace && vfs.exists(dir) && !isEmptyDir(vfs, dir)) {
|
|
6485
6624
|
throw new CliError(
|
|
6486
6625
|
"PUCK-CLI-TARGET-DIR-NOT-EMPTY",
|
|
@@ -6499,7 +6638,7 @@ var runInit = async (rc) => {
|
|
|
6499
6638
|
bootstrap: {
|
|
6500
6639
|
framework,
|
|
6501
6640
|
dir,
|
|
6502
|
-
appName: name ?? sanitizeAppName(
|
|
6641
|
+
appName: name ?? sanitizeAppName(path28.basename(dir)),
|
|
6503
6642
|
workspaceRoot
|
|
6504
6643
|
},
|
|
6505
6644
|
workspace: target.kind === "workspace-empty" ? target.summary : null
|
|
@@ -6513,7 +6652,7 @@ var runAdd = async (rc, positionals) => {
|
|
|
6513
6652
|
"PUCK-CLI-INVALID-ARGS",
|
|
6514
6653
|
`Specify what to add: ${CAPABILITY_IDS.join(
|
|
6515
6654
|
", "
|
|
6516
|
-
)}. For example
|
|
6655
|
+
)}. For example \`${CANONICAL_INVOCATION} add cloud\`.`
|
|
6517
6656
|
);
|
|
6518
6657
|
}
|
|
6519
6658
|
const unknown = positionals.filter(
|
|
@@ -6551,6 +6690,144 @@ var runAdd = async (rc, positionals) => {
|
|
|
6551
6690
|
});
|
|
6552
6691
|
};
|
|
6553
6692
|
|
|
6693
|
+
// src/commands/connect.ts
|
|
6694
|
+
import path29 from "path";
|
|
6695
|
+
var runConnect = async (rc) => {
|
|
6696
|
+
const { deps, flags } = rc;
|
|
6697
|
+
const result = emptyResult("connect", flags.dryRun);
|
|
6698
|
+
const target = await resolveTarget(rc);
|
|
6699
|
+
if (target.kind === "action") {
|
|
6700
|
+
result.status = "action_required";
|
|
6701
|
+
result.message = target.action.message;
|
|
6702
|
+
result.actions = [target.action];
|
|
6703
|
+
return result;
|
|
6704
|
+
}
|
|
6705
|
+
if (target.kind === "workspace-empty") {
|
|
6706
|
+
throw new CliError(
|
|
6707
|
+
"PUCK-CLI-UNSUPPORTED-FRAMEWORK",
|
|
6708
|
+
`No supported app was found in this workspace. Run \`${CANONICAL_INVOCATION} init\` to create one.`,
|
|
6709
|
+
{ fix: `${CANONICAL_INVOCATION} init --name <name>` }
|
|
6710
|
+
);
|
|
6711
|
+
}
|
|
6712
|
+
const base = baseDir(rc);
|
|
6713
|
+
const root = target.root;
|
|
6714
|
+
const vfs = new Vfs();
|
|
6715
|
+
const ctx = detectProject({
|
|
6716
|
+
vfs,
|
|
6717
|
+
root,
|
|
6718
|
+
cwd: base,
|
|
6719
|
+
packageManagerFlag: flags.packageManager,
|
|
6720
|
+
env: deps.env
|
|
6721
|
+
});
|
|
6722
|
+
assertSupported(rc, ctx);
|
|
6723
|
+
const state = detectState(vfs, ctx, deps.env, { configFlag: flags.config });
|
|
6724
|
+
if (state.cloud.external) {
|
|
6725
|
+
throw new CliError(
|
|
6726
|
+
"PUCK-CLI-EXTERNAL-BACKEND",
|
|
6727
|
+
"This app sends Puck Cloud requests to a server elsewhere, and the key lives there. Run connect against that server instead.",
|
|
6728
|
+
{ fix: `${CANONICAL_INVOCATION} connect --cwd <server dir>` }
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6731
|
+
const planner = new Planner(vfs, ctx, state, deps.templates, deps.cliVersion);
|
|
6732
|
+
for (const warning of ctx.warnings)
|
|
6733
|
+
planner.warn("PUCK-CLI-W-DETECTION", warning);
|
|
6734
|
+
const cloudReady = state.cloud.clientInstalled && Boolean(state.cloud.routeFile) && state.cloud.routeRegistered !== false;
|
|
6735
|
+
const where = root === base ? "" : ` --cwd ${displayPath(base, root)}`;
|
|
6736
|
+
if (!cloudReady) {
|
|
6737
|
+
planner.warn(
|
|
6738
|
+
"PUCK-CLI-W-CLOUD-NOT-SET-UP",
|
|
6739
|
+
`Puck Cloud isn't set up in this app yet, so nothing uses ${ENV_KEY}. Run \`${CANONICAL_INVOCATION} add cloud\` to set it up.`
|
|
6740
|
+
);
|
|
6741
|
+
}
|
|
6742
|
+
const finish = (status, message) => {
|
|
6743
|
+
result.status = status;
|
|
6744
|
+
result.message = message;
|
|
6745
|
+
result.project = projectSummary(ctx, target.workspace);
|
|
6746
|
+
result.cloud = cloudSummary(state, "none");
|
|
6747
|
+
result.warnings = planner.warnings;
|
|
6748
|
+
return result;
|
|
6749
|
+
};
|
|
6750
|
+
if (!flags.dryRun && !rc.interactive && !flags.yes) {
|
|
6751
|
+
result.actions = [
|
|
6752
|
+
{
|
|
6753
|
+
id: "confirm",
|
|
6754
|
+
type: "confirm_plan",
|
|
6755
|
+
required: true,
|
|
6756
|
+
flag: "--yes",
|
|
6757
|
+
message: `Re-run with --yes to log in to Puck Cloud and replace ${ENV_KEY}.`,
|
|
6758
|
+
rerun: `${rerunCommand(rc)} --yes`
|
|
6759
|
+
}
|
|
6760
|
+
];
|
|
6761
|
+
return finish(
|
|
6762
|
+
"action_required",
|
|
6763
|
+
`Re-run with --yes to log in to Puck Cloud and replace ${ENV_KEY}.`
|
|
6764
|
+
);
|
|
6765
|
+
}
|
|
6766
|
+
const credential = await resolveCredential(
|
|
6767
|
+
rc,
|
|
6768
|
+
state,
|
|
6769
|
+
{
|
|
6770
|
+
projectRoot: root,
|
|
6771
|
+
projectName: ctx.packageJson?.name ?? path29.basename(root),
|
|
6772
|
+
framework: ctx.framework.id
|
|
6773
|
+
},
|
|
6774
|
+
{ fresh: true }
|
|
6775
|
+
);
|
|
6776
|
+
for (const w of credential.warnings) planner.warn(w.code, w.message);
|
|
6777
|
+
if (credential.kind === "action") {
|
|
6778
|
+
result.actions = [credential.action];
|
|
6779
|
+
return finish(
|
|
6780
|
+
"action_required",
|
|
6781
|
+
credential.action.type === "browser_login" ? "Puck Cloud login required. No changes have been made yet." : "A Puck API key is required. No changes have been made yet."
|
|
6782
|
+
);
|
|
6783
|
+
}
|
|
6784
|
+
if (credential.kind === "none") {
|
|
6785
|
+
planner.warn(
|
|
6786
|
+
"PUCK-CLI-W-KEY-NEEDED",
|
|
6787
|
+
"You'll be asked to log in to Puck Cloud when you run this without --dry-run."
|
|
6788
|
+
);
|
|
6789
|
+
return finish("success", "Dry run: no changes were made.");
|
|
6790
|
+
}
|
|
6791
|
+
planEnvWrite(planner, credential.secret);
|
|
6792
|
+
result.plan = { steps: serializeSteps(base, planner.steps) };
|
|
6793
|
+
if (flags.dryRun) return finish("success", "Dry run: no changes were made.");
|
|
6794
|
+
const outcome = await applyPlan(rc, vfs, planner.steps);
|
|
6795
|
+
const after = new Vfs();
|
|
6796
|
+
const verifiedCtx = detectProject({
|
|
6797
|
+
vfs: after,
|
|
6798
|
+
root,
|
|
6799
|
+
cwd: base,
|
|
6800
|
+
packageManagerFlag: flags.packageManager,
|
|
6801
|
+
env: deps.env
|
|
6802
|
+
});
|
|
6803
|
+
const verifiedState = detectState(after, verifiedCtx, deps.env, {
|
|
6804
|
+
configFlag: flags.config
|
|
6805
|
+
});
|
|
6806
|
+
const check = credential.verified;
|
|
6807
|
+
finish(
|
|
6808
|
+
"success",
|
|
6809
|
+
`Connected to Puck Cloud. ${planner.steps.length ? `Saved ${ENV_KEY} to ${displayPath(
|
|
6810
|
+
base,
|
|
6811
|
+
verifiedState.cloud.apiKey.file ?? root
|
|
6812
|
+
)}.` : `${ENV_KEY} was already set to this key.`}`
|
|
6813
|
+
);
|
|
6814
|
+
result.project = projectSummary(verifiedCtx, target.workspace);
|
|
6815
|
+
result.cloud = cloudSummary(
|
|
6816
|
+
verifiedState,
|
|
6817
|
+
check === "valid" ? "remote" : check === "invalid" ? "failed" : "local"
|
|
6818
|
+
);
|
|
6819
|
+
result.changed = planner.steps.length > 0;
|
|
6820
|
+
result.filesCreated = outcome.created.map((f) => displayPath(base, f)).sort();
|
|
6821
|
+
result.filesModified = [...outcome.created, ...outcome.modified].map((f) => displayPath(base, f)).sort();
|
|
6822
|
+
const pm = verifiedCtx.packageManager.name;
|
|
6823
|
+
const cdPrefix = root === base ? "" : `cd ${displayPath(base, root)} && `;
|
|
6824
|
+
result.nextSteps = cloudReady ? [
|
|
6825
|
+
"# Restart the dev server so it picks up the new key",
|
|
6826
|
+
`${cdPrefix}${pm} ${pm === "npm" ? "run " : ""}dev`
|
|
6827
|
+
] : [`${CANONICAL_INVOCATION} add cloud${where}`];
|
|
6828
|
+
return result;
|
|
6829
|
+
};
|
|
6830
|
+
|
|
6554
6831
|
// src/commands/status.ts
|
|
6555
6832
|
var runStatus = async (rc) => {
|
|
6556
6833
|
const base = baseDir(rc);
|
|
@@ -6624,7 +6901,7 @@ var runStatus = async (rc) => {
|
|
|
6624
6901
|
};
|
|
6625
6902
|
|
|
6626
6903
|
// src/commands/doctor.ts
|
|
6627
|
-
import
|
|
6904
|
+
import path30 from "path";
|
|
6628
6905
|
var finding = (check, status, evidence, fix = null) => ({ check, status, evidence, fix });
|
|
6629
6906
|
var runDoctor = async (rc) => {
|
|
6630
6907
|
const base = baseDir(rc);
|
|
@@ -6709,7 +6986,7 @@ var runDoctor = async (rc) => {
|
|
|
6709
6986
|
finding(
|
|
6710
6987
|
"project.package_json",
|
|
6711
6988
|
"ok",
|
|
6712
|
-
displayPath(base,
|
|
6989
|
+
displayPath(base, path30.join(ctx.root, "package.json"))
|
|
6713
6990
|
)
|
|
6714
6991
|
);
|
|
6715
6992
|
result.project = projectSummary(ctx, target.workspace);
|
|
@@ -6951,7 +7228,7 @@ var runDoctor = async (rc) => {
|
|
|
6951
7228
|
"cloud.connection",
|
|
6952
7229
|
"fail",
|
|
6953
7230
|
"Puck Cloud rejected the API key",
|
|
6954
|
-
`${CANONICAL_INVOCATION}
|
|
7231
|
+
`${CANONICAL_INVOCATION} connect${where}`
|
|
6955
7232
|
)
|
|
6956
7233
|
);
|
|
6957
7234
|
} else {
|
|
@@ -7090,6 +7367,41 @@ var runDocs = async (rc, positionals) => {
|
|
|
7090
7367
|
return result;
|
|
7091
7368
|
};
|
|
7092
7369
|
|
|
7370
|
+
// src/commands/frameworks.ts
|
|
7371
|
+
var NOTES = {
|
|
7372
|
+
next: "App Router, in app/ or src/app/",
|
|
7373
|
+
"react-router": "Framework mode",
|
|
7374
|
+
"tanstack-start": "",
|
|
7375
|
+
vinext: "App Router, in app/ or src/app/",
|
|
7376
|
+
vite: "React apps using @vitejs/plugin-react",
|
|
7377
|
+
astro: "Adds React with astro add react if needed",
|
|
7378
|
+
hono: "Serves the APIs for an editor in another app",
|
|
7379
|
+
express: "Serves the APIs for an editor in another app"
|
|
7380
|
+
};
|
|
7381
|
+
var frameworkSupport = () => FRAMEWORK_IDS.map((id) => {
|
|
7382
|
+
const adapter = ADAPTERS[id];
|
|
7383
|
+
return {
|
|
7384
|
+
id,
|
|
7385
|
+
name: FRAMEWORK_LABELS[id],
|
|
7386
|
+
minVersion: MIN_VERSIONS[id],
|
|
7387
|
+
kind: adapter.kind ?? "app",
|
|
7388
|
+
needsServer: Boolean(adapter.backend),
|
|
7389
|
+
notes: NOTES[id]
|
|
7390
|
+
};
|
|
7391
|
+
});
|
|
7392
|
+
var runFrameworks = async (_rc, positionals) => {
|
|
7393
|
+
if (positionals.length > 0) {
|
|
7394
|
+
throw new CliError(
|
|
7395
|
+
"PUCK-CLI-INVALID-ARGS",
|
|
7396
|
+
`\`frameworks\` doesn't take arguments. Run \`${CANONICAL_INVOCATION} frameworks\`.`
|
|
7397
|
+
);
|
|
7398
|
+
}
|
|
7399
|
+
const result = emptyResult("frameworks");
|
|
7400
|
+
result.frameworks = frameworkSupport();
|
|
7401
|
+
result.message = `Create a new app with \`${CANONICAL_INVOCATION} init --framework <id>\`.`;
|
|
7402
|
+
return result;
|
|
7403
|
+
};
|
|
7404
|
+
|
|
7093
7405
|
// src/cli.ts
|
|
7094
7406
|
var exitCodeFor = (result) => {
|
|
7095
7407
|
if (result.status === "error")
|
|
@@ -7143,8 +7455,11 @@ var runCli = async (argv, deps) => {
|
|
|
7143
7455
|
try {
|
|
7144
7456
|
if (command === "init") result = await runInit(rc);
|
|
7145
7457
|
else if (command === "add") result = await runAdd(rc, positionals);
|
|
7458
|
+
else if (command === "connect") result = await runConnect(rc);
|
|
7146
7459
|
else if (command === "status") result = await runStatus(rc);
|
|
7147
7460
|
else if (command === "docs") result = await runDocs(rc, positionals);
|
|
7461
|
+
else if (command === "frameworks")
|
|
7462
|
+
result = await runFrameworks(rc, positionals);
|
|
7148
7463
|
else result = await runDoctor(rc);
|
|
7149
7464
|
} catch (err) {
|
|
7150
7465
|
result = errorResult(command, err, flags.dryRun);
|
|
@@ -7248,7 +7563,7 @@ var runDevServer = (spec, onReady) => new Promise((resolve) => {
|
|
|
7248
7563
|
|
|
7249
7564
|
// src/docs/source.ts
|
|
7250
7565
|
import fs6 from "fs";
|
|
7251
|
-
import
|
|
7566
|
+
import path31 from "path";
|
|
7252
7567
|
var DirDocsSource = class {
|
|
7253
7568
|
#dir;
|
|
7254
7569
|
constructor(dir) {
|
|
@@ -7256,21 +7571,21 @@ var DirDocsSource = class {
|
|
|
7256
7571
|
}
|
|
7257
7572
|
list() {
|
|
7258
7573
|
return JSON.parse(
|
|
7259
|
-
fs6.readFileSync(
|
|
7574
|
+
fs6.readFileSync(path31.join(this.#dir, "index.json"), "utf8")
|
|
7260
7575
|
);
|
|
7261
7576
|
}
|
|
7262
7577
|
read(page) {
|
|
7263
|
-
return fs6.readFileSync(
|
|
7578
|
+
return fs6.readFileSync(path31.join(this.#dir, `${page}.md`), "utf8");
|
|
7264
7579
|
}
|
|
7265
7580
|
};
|
|
7266
7581
|
|
|
7267
7582
|
// src/bin.ts
|
|
7268
|
-
var distDir =
|
|
7269
|
-
var templatesDir =
|
|
7583
|
+
var distDir = path32.dirname(fileURLToPath(import.meta.url));
|
|
7584
|
+
var templatesDir = path32.join(distDir, "templates");
|
|
7270
7585
|
var templates = new DirTemplateSource(templatesDir, {
|
|
7271
7586
|
gitignoreName: "_gitignore",
|
|
7272
7587
|
manifest: () => JSON.parse(
|
|
7273
|
-
fs7.readFileSync(
|
|
7588
|
+
fs7.readFileSync(path32.join(templatesDir, "manifest.json"), "utf8")
|
|
7274
7589
|
)
|
|
7275
7590
|
});
|
|
7276
7591
|
var env = { ...process.env };
|
|
@@ -7280,14 +7595,14 @@ runCli(process.argv.slice(2), {
|
|
|
7280
7595
|
stdout: process.stdout,
|
|
7281
7596
|
stderr: process.stderr,
|
|
7282
7597
|
stdinIsTTY: Boolean(process.stdin.isTTY),
|
|
7283
|
-
cliVersion: "0.24.0-canary.
|
|
7598
|
+
cliVersion: "0.24.0-canary.6f40e09b",
|
|
7284
7599
|
runner: createRunner(env),
|
|
7285
7600
|
fetch: globalThis.fetch,
|
|
7286
7601
|
openUrl,
|
|
7287
7602
|
runDevServer,
|
|
7288
7603
|
createPrompter: () => createInquirerPrompter(process.stderr),
|
|
7289
7604
|
templates,
|
|
7290
|
-
docs: new DirDocsSource(
|
|
7605
|
+
docs: new DirDocsSource(path32.join(distDir, "docs")),
|
|
7291
7606
|
homedir: os.homedir(),
|
|
7292
7607
|
hostname: os.hostname(),
|
|
7293
7608
|
now: () => Date.now(),
|
package/dist/docs/cli.md
CHANGED
|
@@ -21,14 +21,16 @@ Puck AI is optional. It's the editor plugin that uses Puck Cloud as its backend,
|
|
|
21
21
|
|
|
22
22
|
- Next.js 15+ using the App Router (`app/` or `src/app/`)
|
|
23
23
|
- React Router 7+ in framework mode
|
|
24
|
-
- [TanStack Start](https://tanstack.com/start) 1.
|
|
25
|
-
- [vinext](https://github.com/cloudflare/vinext) using the App Router
|
|
26
|
-
- [Vite](https://vite.dev) React apps (see [Vite and Astro](#vite-and-astro))
|
|
24
|
+
- [TanStack Start](https://tanstack.com/start) 1.132+
|
|
25
|
+
- [vinext](https://github.com/cloudflare/vinext) 1+ using the App Router
|
|
26
|
+
- [Vite](https://vite.dev) 6+ React apps (see [Vite and Astro](#vite-and-astro))
|
|
27
27
|
- [Astro](https://astro.build) 7+ (see [Vite and Astro](#vite-and-astro))
|
|
28
|
-
- [Hono](https://hono.dev) and [Express](https://expressjs.com) servers (see [Servers](#servers))
|
|
28
|
+
- [Hono](https://hono.dev) 4+ and [Express](https://expressjs.com) 4.16+ servers (see [Servers](#servers))
|
|
29
29
|
- npm, pnpm, Yarn and Bun, including workspaces
|
|
30
30
|
- TypeScript projects
|
|
31
31
|
|
|
32
|
+
Run `npx @puckeditor/cli frameworks` to see what the CLI sets up for each framework. Older versions fail with `PUCK-CLI-UNSUPPORTED-FRAMEWORK-VERSION`.
|
|
33
|
+
|
|
32
34
|
When the CLI can't safely change something, like an existing `middleware.ts`, it leaves the file alone and tells you exactly what to add.
|
|
33
35
|
|
|
34
36
|
## Vite and Astro
|
|
@@ -157,8 +159,10 @@ npx skills add puckeditor/skills --skill puck
|
|
|
157
159
|
| `add editor` | Add the Puck editor |
|
|
158
160
|
| `add cloud` | Connect Puck Cloud (adds the editor first if needed) |
|
|
159
161
|
| `add ai` | Add the Puck AI plugin (adds Puck Cloud first if needed) |
|
|
162
|
+
| `connect` | Log in to Puck Cloud again and replace `PUCK_API_KEY` |
|
|
160
163
|
| `status` | Show what's installed and configured |
|
|
161
164
|
| `doctor` | Diagnose problems and suggest fixes. Never makes changes |
|
|
165
|
+
| `frameworks` | Show the supported frameworks and versions |
|
|
162
166
|
| `docs` | Read these docs: `ls`, `cat <page>`, `find <query>`, `grep <text>` |
|
|
163
167
|
|
|
164
168
|
## Flags
|
|
@@ -192,6 +196,12 @@ To use an existing key, prefer the environment over `--api-key`, which is visibl
|
|
|
192
196
|
PUCK_API_KEY=your-key npx @puckeditor/cli add cloud --yes
|
|
193
197
|
```
|
|
194
198
|
|
|
199
|
+
To log in again, for example to replace a revoked key, run `connect`. It always starts a new login and replaces the existing key, without changing any code:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
npx @puckeditor/cli connect
|
|
203
|
+
```
|
|
204
|
+
|
|
195
205
|
Remember to set `PUCK_API_KEY` in your production environment too. `react-router-serve` and Cloudflare Workers don't load `.env` files; on Workers, use `wrangler secret put PUCK_API_KEY`.
|
|
196
206
|
|
|
197
207
|
The `/api/puck` route and the editor are public by default. Add authentication before deploying.
|
|
@@ -27,13 +27,18 @@ puckPages.get("/api/pages", async (req, res) => {
|
|
|
27
27
|
puckPages.post(
|
|
28
28
|
"/api/pages",
|
|
29
29
|
express.json({ limit: "10mb" }),
|
|
30
|
-
async (req, res) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
async (req, res, next) => {
|
|
31
|
+
// Express 4 doesn't pass errors from async handlers to next()
|
|
32
|
+
try {
|
|
33
|
+
const { path, data } = req.body;
|
|
34
|
+
const pages = await readPages();
|
|
35
|
+
|
|
36
|
+
pages[path] = data;
|
|
37
|
+
await fs.writeFile(databasePath, JSON.stringify(pages));
|
|
38
|
+
|
|
39
|
+
res.json({ status: "ok" });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
next(error);
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
44
|
);
|
|
@@ -27,13 +27,18 @@ puckPages.get("/api/pages", async (req, res) => {
|
|
|
27
27
|
puckPages.post(
|
|
28
28
|
"/api/pages",
|
|
29
29
|
express.json({ limit: "10mb" }),
|
|
30
|
-
async (req, res) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
async (req, res, next) => {
|
|
31
|
+
// Express 4 doesn't pass errors from async handlers to next()
|
|
32
|
+
try {
|
|
33
|
+
const { path, data } = req.body;
|
|
34
|
+
const pages = await readPages();
|
|
35
|
+
|
|
36
|
+
pages[path] = data;
|
|
37
|
+
await fs.writeFile(databasePath, JSON.stringify(pages));
|
|
38
|
+
|
|
39
|
+
res.json({ status: "ok" });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
next(error);
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
44
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@puckeditor/cli",
|
|
3
|
-
"version": "0.24.0-canary.
|
|
3
|
+
"version": "0.24.0-canary.6f40e09b",
|
|
4
4
|
"description": "Set up Puck and Puck Cloud from the command line. Built for coding agents, friendly for humans.",
|
|
5
5
|
"author": "Chris Villa <chris@puckeditor.com>",
|
|
6
6
|
"repository": {
|