@puckeditor/cli 0.24.0-canary.55558da4 → 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 +1 -0
- package/dist/bin.mjs +255 -42
- package/dist/docs/cli.md +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ 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
|
|
25
26
|
npx @puckeditor/cli frameworks # show supported frameworks and versions
|
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);
|
|
@@ -799,6 +800,7 @@ var MUTATING = [
|
|
|
799
800
|
var COMMAND_OPTIONS = {
|
|
800
801
|
init: [...GLOBAL, ...MUTATING, "framework", "name", "ai", "no-ai"],
|
|
801
802
|
add: [...GLOBAL, ...MUTATING],
|
|
803
|
+
connect: [...GLOBAL, "api-key", "config", "wait"],
|
|
802
804
|
status: ["json", "help", "version", "cwd", "workspace", "config"],
|
|
803
805
|
doctor: ["json", "help", "version", "cwd", "workspace", "config", "offline"],
|
|
804
806
|
docs: ["json", "help", "version"],
|
|
@@ -808,6 +810,7 @@ var COMMAND_OPTIONS = {
|
|
|
808
810
|
var COMMANDS = [
|
|
809
811
|
"init",
|
|
810
812
|
"add",
|
|
813
|
+
"connect",
|
|
811
814
|
"status",
|
|
812
815
|
"doctor",
|
|
813
816
|
"docs",
|
|
@@ -836,7 +839,7 @@ var parseCliArgs = (argv) => {
|
|
|
836
839
|
if (!COMMANDS.includes(first)) {
|
|
837
840
|
throw new CliError(
|
|
838
841
|
"PUCK-CLI-UNKNOWN-COMMAND",
|
|
839
|
-
`Unknown command "${first}". Run
|
|
842
|
+
`Unknown command "${first}". Run \`${CANONICAL_INVOCATION} --help\` to see available commands.`
|
|
840
843
|
);
|
|
841
844
|
}
|
|
842
845
|
command = first;
|
|
@@ -847,7 +850,7 @@ var parseCliArgs = (argv) => {
|
|
|
847
850
|
if (!allowed.includes(key)) {
|
|
848
851
|
throw new CliError(
|
|
849
852
|
"PUCK-CLI-INVALID-ARGS",
|
|
850
|
-
`--${key} can't be used with \`${command ??
|
|
853
|
+
`--${key} can't be used with \`${command ?? CANONICAL_INVOCATION}\`.`
|
|
851
854
|
);
|
|
852
855
|
}
|
|
853
856
|
}
|
|
@@ -1156,6 +1159,10 @@ var COMMANDS2 = [
|
|
|
1156
1159
|
name: "add <editor|cloud|ai>",
|
|
1157
1160
|
description: "Add one capability (ai includes cloud, cloud includes editor)"
|
|
1158
1161
|
},
|
|
1162
|
+
{
|
|
1163
|
+
name: "connect",
|
|
1164
|
+
description: "Log in to Puck Cloud again and replace PUCK_API_KEY"
|
|
1165
|
+
},
|
|
1159
1166
|
{ name: "status", description: "Show what's installed and configured" },
|
|
1160
1167
|
{
|
|
1161
1168
|
name: "doctor",
|
|
@@ -1504,7 +1511,8 @@ var templates_default = {
|
|
|
1504
1511
|
"*.tsbuildinfo",
|
|
1505
1512
|
"eslint.config.mjs",
|
|
1506
1513
|
".env*"
|
|
1507
|
-
]
|
|
1514
|
+
],
|
|
1515
|
+
allowBuilds: ["esbuild"]
|
|
1508
1516
|
};
|
|
1509
1517
|
|
|
1510
1518
|
// src/templates/source.ts
|
|
@@ -2550,10 +2558,10 @@ var analyzeRoutes = (code, filename) => {
|
|
|
2550
2558
|
const topLevel = new Set(exported.elements.filter(Boolean));
|
|
2551
2559
|
walk(exported, (node) => {
|
|
2552
2560
|
if (!isRouteCall(node, routeLocal)) return;
|
|
2553
|
-
const
|
|
2561
|
+
const path33 = stringArg(node, 0);
|
|
2554
2562
|
const isTop = topLevel.has(node);
|
|
2555
|
-
calls.push({ path:
|
|
2556
|
-
if (
|
|
2563
|
+
calls.push({ path: path33, file: stringArg(node, 1), topLevel: isTop });
|
|
2564
|
+
if (path33 === "*") {
|
|
2557
2565
|
if (isTop) splat = node;
|
|
2558
2566
|
else nestedSplat = true;
|
|
2559
2567
|
}
|
|
@@ -4843,7 +4851,7 @@ var presentHuman = (out, result, secrets) => {
|
|
|
4843
4851
|
};
|
|
4844
4852
|
|
|
4845
4853
|
// src/commands/init.ts
|
|
4846
|
-
import
|
|
4854
|
+
import path28 from "path";
|
|
4847
4855
|
|
|
4848
4856
|
// src/io/vfs.ts
|
|
4849
4857
|
import fs2 from "fs";
|
|
@@ -5313,7 +5321,7 @@ var resolveTarget = async (rc, opts = {}) => {
|
|
|
5313
5321
|
};
|
|
5314
5322
|
|
|
5315
5323
|
// src/commands/mutate.ts
|
|
5316
|
-
import
|
|
5324
|
+
import path27 from "path";
|
|
5317
5325
|
|
|
5318
5326
|
// src/plan/planner.ts
|
|
5319
5327
|
import path22 from "path";
|
|
@@ -5571,6 +5579,66 @@ var planEnvWrite = (p, secret) => {
|
|
|
5571
5579
|
planGitignore(p, target);
|
|
5572
5580
|
};
|
|
5573
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
|
+
|
|
5574
5642
|
// src/plan/backend.ts
|
|
5575
5643
|
var choicesFor = (options, wantsCloud) => [
|
|
5576
5644
|
{ value: "add", label: options.addLabel },
|
|
@@ -5640,14 +5708,14 @@ var resolveBackend = async (rc, options, capabilities, planned) => {
|
|
|
5640
5708
|
|
|
5641
5709
|
// src/auth/session-store.ts
|
|
5642
5710
|
import fs4 from "fs";
|
|
5643
|
-
import
|
|
5711
|
+
import path25 from "path";
|
|
5644
5712
|
import { createHash as createHash2 } from "crypto";
|
|
5645
5713
|
var SessionStore = class {
|
|
5646
5714
|
#file;
|
|
5647
5715
|
constructor(cacheDir, projectRoot, baseUrl) {
|
|
5648
5716
|
const key = createHash2("sha256").update(`${projectRoot}
|
|
5649
5717
|
${baseUrl}`).digest("hex").slice(0, 32);
|
|
5650
|
-
this.#file =
|
|
5718
|
+
this.#file = path25.join(cacheDir, "puck", "connect", `${key}.json`);
|
|
5651
5719
|
}
|
|
5652
5720
|
get path() {
|
|
5653
5721
|
return this.#file;
|
|
@@ -5661,14 +5729,14 @@ ${baseUrl}`).digest("hex").slice(0, 32);
|
|
|
5661
5729
|
}
|
|
5662
5730
|
}
|
|
5663
5731
|
save(session) {
|
|
5664
|
-
fs4.mkdirSync(
|
|
5732
|
+
fs4.mkdirSync(path25.dirname(this.#file), { recursive: true, mode: 448 });
|
|
5665
5733
|
fs4.writeFileSync(this.#file, JSON.stringify(session), { mode: 384 });
|
|
5666
5734
|
}
|
|
5667
5735
|
clear() {
|
|
5668
5736
|
fs4.rmSync(this.#file, { force: true });
|
|
5669
5737
|
}
|
|
5670
5738
|
};
|
|
5671
|
-
var cacheDirFor = (env2, homedir) => env2.XDG_CACHE_HOME ||
|
|
5739
|
+
var cacheDirFor = (env2, homedir) => env2.XDG_CACHE_HOME || path25.join(homedir, ".cache");
|
|
5672
5740
|
|
|
5673
5741
|
// src/auth/credentials.ts
|
|
5674
5742
|
var apiKeysUrl = (rc) => new URL(API_KEYS_PATH, rc.cloud.baseUrl).toString();
|
|
@@ -5891,7 +5959,7 @@ var promptForKey = async (rc) => {
|
|
|
5891
5959
|
warnings: []
|
|
5892
5960
|
};
|
|
5893
5961
|
};
|
|
5894
|
-
var resolveCredential = async (rc, state, info) => {
|
|
5962
|
+
var resolveCredential = async (rc, state, info, { fresh = false } = {}) => {
|
|
5895
5963
|
const warnings = [];
|
|
5896
5964
|
const fromExplicit = async (value, source) => {
|
|
5897
5965
|
const secret = secretFrom(
|
|
@@ -5917,10 +5985,15 @@ var resolveCredential = async (rc, state, info) => {
|
|
|
5917
5985
|
};
|
|
5918
5986
|
if (rc.flags.apiKey !== void 0)
|
|
5919
5987
|
return fromExplicit(rc.flags.apiKey, "flag");
|
|
5920
|
-
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])
|
|
5921
5994
|
return fromExplicit(rc.deps.env[ENV_KEY], "process.env");
|
|
5922
5995
|
const existing = state.cloud.apiKey;
|
|
5923
|
-
if (existing.present && existing.value) {
|
|
5996
|
+
if (!fresh && existing.present && existing.value) {
|
|
5924
5997
|
const secret = secretFrom(
|
|
5925
5998
|
rc,
|
|
5926
5999
|
existing.value,
|
|
@@ -5981,7 +6054,7 @@ var resolveCredential = async (rc, state, info) => {
|
|
|
5981
6054
|
|
|
5982
6055
|
// src/apply/applier.ts
|
|
5983
6056
|
import fs5 from "fs";
|
|
5984
|
-
import
|
|
6057
|
+
import path26 from "path";
|
|
5985
6058
|
var tail = (text, lines = 20) => text.trim().split("\n").slice(-lines).join("\n");
|
|
5986
6059
|
var run = async (rc, spec) => {
|
|
5987
6060
|
rc.log(`$ ${formatCommand(spec)}`);
|
|
@@ -6011,14 +6084,14 @@ ${result.stderr}`))
|
|
|
6011
6084
|
}
|
|
6012
6085
|
};
|
|
6013
6086
|
var writeFile = (file, content, mode) => {
|
|
6014
|
-
fs5.mkdirSync(
|
|
6087
|
+
fs5.mkdirSync(path26.dirname(file), { recursive: true });
|
|
6015
6088
|
if (mode === void 0) {
|
|
6016
6089
|
fs5.writeFileSync(file, content);
|
|
6017
6090
|
return;
|
|
6018
6091
|
}
|
|
6019
|
-
const temp =
|
|
6020
|
-
|
|
6021
|
-
`.${
|
|
6092
|
+
const temp = path26.join(
|
|
6093
|
+
path26.dirname(file),
|
|
6094
|
+
`.${path26.basename(file)}.${process.pid}.tmp`
|
|
6022
6095
|
);
|
|
6023
6096
|
fs5.writeFileSync(temp, content, { mode });
|
|
6024
6097
|
fs5.chmodSync(temp, mode);
|
|
@@ -6219,6 +6292,7 @@ var runMutation = async (rc, input) => {
|
|
|
6219
6292
|
}
|
|
6220
6293
|
planCapabilities(planner, capabilities);
|
|
6221
6294
|
planner.finalize();
|
|
6295
|
+
if (bootstrap) planAllowedBuilds(planner);
|
|
6222
6296
|
if (scaffoldStep) planner.steps.unshift(scaffoldStep);
|
|
6223
6297
|
const wantsCloud = capabilities.includes("cloud") && planner.backend?.mode !== "external" && !state.cloud.external;
|
|
6224
6298
|
const needsConsent = planner.steps.length > 0 || wantsCloud && !state.cloud.apiKey.present;
|
|
@@ -6257,7 +6331,7 @@ var runMutation = async (rc, input) => {
|
|
|
6257
6331
|
if (wantsCloud) {
|
|
6258
6332
|
credential = await resolveCredential(rc, state, {
|
|
6259
6333
|
projectRoot: root,
|
|
6260
|
-
projectName: bootstrap?.appName ?? ctx.packageJson?.name ??
|
|
6334
|
+
projectName: bootstrap?.appName ?? ctx.packageJson?.name ?? path27.basename(root),
|
|
6261
6335
|
framework: ctx.framework.id
|
|
6262
6336
|
});
|
|
6263
6337
|
for (const w of credential.warnings) planner.warn(w.code, w.message);
|
|
@@ -6448,14 +6522,14 @@ var runInit = async (rc) => {
|
|
|
6448
6522
|
{ globs: target.workspace.globs }
|
|
6449
6523
|
);
|
|
6450
6524
|
}
|
|
6451
|
-
parentDir =
|
|
6525
|
+
parentDir = path28.join(target.workspace.root, appsDir);
|
|
6452
6526
|
workspaceRoot = target.workspace.root;
|
|
6453
6527
|
for (const pkg of target.workspace.packages) {
|
|
6454
6528
|
if (pkg.name) existingNames.add(pkg.name);
|
|
6455
|
-
existingNames.add(
|
|
6529
|
+
existingNames.add(path28.basename(pkg.dir));
|
|
6456
6530
|
}
|
|
6457
6531
|
} else {
|
|
6458
|
-
if (vfs.exists(
|
|
6532
|
+
if (vfs.exists(path28.join(target.root, "package.json"))) {
|
|
6459
6533
|
const read = readPackageJson(vfs, target.root);
|
|
6460
6534
|
const deps = depsOf(read.status === "ok" ? read.pkg : null);
|
|
6461
6535
|
const detection = detectFramework(vfs, target.root, deps);
|
|
@@ -6545,7 +6619,7 @@ var runInit = async (rc) => {
|
|
|
6545
6619
|
`A workspace package named "${name}" already exists.`
|
|
6546
6620
|
);
|
|
6547
6621
|
}
|
|
6548
|
-
const dir = inPlace ? parentDir :
|
|
6622
|
+
const dir = inPlace ? parentDir : path28.join(parentDir, name);
|
|
6549
6623
|
if (!inPlace && vfs.exists(dir) && !isEmptyDir(vfs, dir)) {
|
|
6550
6624
|
throw new CliError(
|
|
6551
6625
|
"PUCK-CLI-TARGET-DIR-NOT-EMPTY",
|
|
@@ -6564,7 +6638,7 @@ var runInit = async (rc) => {
|
|
|
6564
6638
|
bootstrap: {
|
|
6565
6639
|
framework,
|
|
6566
6640
|
dir,
|
|
6567
|
-
appName: name ?? sanitizeAppName(
|
|
6641
|
+
appName: name ?? sanitizeAppName(path28.basename(dir)),
|
|
6568
6642
|
workspaceRoot
|
|
6569
6643
|
},
|
|
6570
6644
|
workspace: target.kind === "workspace-empty" ? target.summary : null
|
|
@@ -6578,7 +6652,7 @@ var runAdd = async (rc, positionals) => {
|
|
|
6578
6652
|
"PUCK-CLI-INVALID-ARGS",
|
|
6579
6653
|
`Specify what to add: ${CAPABILITY_IDS.join(
|
|
6580
6654
|
", "
|
|
6581
|
-
)}. For example
|
|
6655
|
+
)}. For example \`${CANONICAL_INVOCATION} add cloud\`.`
|
|
6582
6656
|
);
|
|
6583
6657
|
}
|
|
6584
6658
|
const unknown = positionals.filter(
|
|
@@ -6616,6 +6690,144 @@ var runAdd = async (rc, positionals) => {
|
|
|
6616
6690
|
});
|
|
6617
6691
|
};
|
|
6618
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
|
+
|
|
6619
6831
|
// src/commands/status.ts
|
|
6620
6832
|
var runStatus = async (rc) => {
|
|
6621
6833
|
const base = baseDir(rc);
|
|
@@ -6689,7 +6901,7 @@ var runStatus = async (rc) => {
|
|
|
6689
6901
|
};
|
|
6690
6902
|
|
|
6691
6903
|
// src/commands/doctor.ts
|
|
6692
|
-
import
|
|
6904
|
+
import path30 from "path";
|
|
6693
6905
|
var finding = (check, status, evidence, fix = null) => ({ check, status, evidence, fix });
|
|
6694
6906
|
var runDoctor = async (rc) => {
|
|
6695
6907
|
const base = baseDir(rc);
|
|
@@ -6774,7 +6986,7 @@ var runDoctor = async (rc) => {
|
|
|
6774
6986
|
finding(
|
|
6775
6987
|
"project.package_json",
|
|
6776
6988
|
"ok",
|
|
6777
|
-
displayPath(base,
|
|
6989
|
+
displayPath(base, path30.join(ctx.root, "package.json"))
|
|
6778
6990
|
)
|
|
6779
6991
|
);
|
|
6780
6992
|
result.project = projectSummary(ctx, target.workspace);
|
|
@@ -7016,7 +7228,7 @@ var runDoctor = async (rc) => {
|
|
|
7016
7228
|
"cloud.connection",
|
|
7017
7229
|
"fail",
|
|
7018
7230
|
"Puck Cloud rejected the API key",
|
|
7019
|
-
`${CANONICAL_INVOCATION}
|
|
7231
|
+
`${CANONICAL_INVOCATION} connect${where}`
|
|
7020
7232
|
)
|
|
7021
7233
|
);
|
|
7022
7234
|
} else {
|
|
@@ -7243,6 +7455,7 @@ var runCli = async (argv, deps) => {
|
|
|
7243
7455
|
try {
|
|
7244
7456
|
if (command === "init") result = await runInit(rc);
|
|
7245
7457
|
else if (command === "add") result = await runAdd(rc, positionals);
|
|
7458
|
+
else if (command === "connect") result = await runConnect(rc);
|
|
7246
7459
|
else if (command === "status") result = await runStatus(rc);
|
|
7247
7460
|
else if (command === "docs") result = await runDocs(rc, positionals);
|
|
7248
7461
|
else if (command === "frameworks")
|
|
@@ -7350,7 +7563,7 @@ var runDevServer = (spec, onReady) => new Promise((resolve) => {
|
|
|
7350
7563
|
|
|
7351
7564
|
// src/docs/source.ts
|
|
7352
7565
|
import fs6 from "fs";
|
|
7353
|
-
import
|
|
7566
|
+
import path31 from "path";
|
|
7354
7567
|
var DirDocsSource = class {
|
|
7355
7568
|
#dir;
|
|
7356
7569
|
constructor(dir) {
|
|
@@ -7358,21 +7571,21 @@ var DirDocsSource = class {
|
|
|
7358
7571
|
}
|
|
7359
7572
|
list() {
|
|
7360
7573
|
return JSON.parse(
|
|
7361
|
-
fs6.readFileSync(
|
|
7574
|
+
fs6.readFileSync(path31.join(this.#dir, "index.json"), "utf8")
|
|
7362
7575
|
);
|
|
7363
7576
|
}
|
|
7364
7577
|
read(page) {
|
|
7365
|
-
return fs6.readFileSync(
|
|
7578
|
+
return fs6.readFileSync(path31.join(this.#dir, `${page}.md`), "utf8");
|
|
7366
7579
|
}
|
|
7367
7580
|
};
|
|
7368
7581
|
|
|
7369
7582
|
// src/bin.ts
|
|
7370
|
-
var distDir =
|
|
7371
|
-
var templatesDir =
|
|
7583
|
+
var distDir = path32.dirname(fileURLToPath(import.meta.url));
|
|
7584
|
+
var templatesDir = path32.join(distDir, "templates");
|
|
7372
7585
|
var templates = new DirTemplateSource(templatesDir, {
|
|
7373
7586
|
gitignoreName: "_gitignore",
|
|
7374
7587
|
manifest: () => JSON.parse(
|
|
7375
|
-
fs7.readFileSync(
|
|
7588
|
+
fs7.readFileSync(path32.join(templatesDir, "manifest.json"), "utf8")
|
|
7376
7589
|
)
|
|
7377
7590
|
});
|
|
7378
7591
|
var env = { ...process.env };
|
|
@@ -7382,14 +7595,14 @@ runCli(process.argv.slice(2), {
|
|
|
7382
7595
|
stdout: process.stdout,
|
|
7383
7596
|
stderr: process.stderr,
|
|
7384
7597
|
stdinIsTTY: Boolean(process.stdin.isTTY),
|
|
7385
|
-
cliVersion: "0.24.0-canary.
|
|
7598
|
+
cliVersion: "0.24.0-canary.6f40e09b",
|
|
7386
7599
|
runner: createRunner(env),
|
|
7387
7600
|
fetch: globalThis.fetch,
|
|
7388
7601
|
openUrl,
|
|
7389
7602
|
runDevServer,
|
|
7390
7603
|
createPrompter: () => createInquirerPrompter(process.stderr),
|
|
7391
7604
|
templates,
|
|
7392
|
-
docs: new DirDocsSource(
|
|
7605
|
+
docs: new DirDocsSource(path32.join(distDir, "docs")),
|
|
7393
7606
|
homedir: os.homedir(),
|
|
7394
7607
|
hostname: os.hostname(),
|
|
7395
7608
|
now: () => Date.now(),
|
package/dist/docs/cli.md
CHANGED
|
@@ -159,6 +159,7 @@ npx skills add puckeditor/skills --skill puck
|
|
|
159
159
|
| `add editor` | Add the Puck editor |
|
|
160
160
|
| `add cloud` | Connect Puck Cloud (adds the editor first if needed) |
|
|
161
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` |
|
|
162
163
|
| `status` | Show what's installed and configured |
|
|
163
164
|
| `doctor` | Diagnose problems and suggest fixes. Never makes changes |
|
|
164
165
|
| `frameworks` | Show the supported frameworks and versions |
|
|
@@ -195,6 +196,12 @@ To use an existing key, prefer the environment over `--api-key`, which is visibl
|
|
|
195
196
|
PUCK_API_KEY=your-key npx @puckeditor/cli add cloud --yes
|
|
196
197
|
```
|
|
197
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
|
+
|
|
198
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`.
|
|
199
206
|
|
|
200
207
|
The `/api/puck` route and the editor are public by default. Add authentication before deploying.
|
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": {
|