create-avocado-site 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/assets/generated-images/gen_1789981693445_284abc56.jpg +0 -0
- package/assets/generated-images/gen_1789981703140_99ea2d8b.jpg +0 -0
- package/assets/generated-images/gen_1789981712535_d49ec7bb.jpg +0 -0
- package/assets/generated-images/gen_1789981721898_119231c3.jpg +0 -0
- package/assets/generated-images/gen_1789981730383_637ea1b8.jpg +0 -0
- package/assets/generated-images/gen_1789981739789_5cc514da.jpg +0 -0
- package/assets/generated-images/gen_1789981749474_0be65523.jpg +0 -0
- package/assets/generated-images/gen_1789982485512_1932c58c.jpg +0 -0
- package/assets/generated-images/gen_1789982495151_4a0113e0.jpg +0 -0
- package/assets/generated-images/gen_1789982505374_5c12f604.jpg +0 -0
- package/assets/generated-images/gen_1789982517293_ed7fc0f2.jpg +0 -0
- package/assets/generated-images/gen_1789982525626_5468907f.jpg +0 -0
- package/assets/generated-images/gen_1789982534675_6d1b122e.jpg +0 -0
- package/assets/generated-images/gen_1789982542619_faba5835.jpg +0 -0
- package/assets/generated-images/gen_1789982552275_17891fd1.jpg +0 -0
- package/assets/generated-images/gen_1789982561072_c3631ea6.jpg +0 -0
- package/assets/generated-images/gen_1789982570680_82f806c3.jpg +0 -0
- package/assets/generated-images/gen_1789982580474_c3304b74.jpg +0 -0
- package/assets/generated-images/gen_1789982589383_87f4e381.jpg +0 -0
- package/assets/generated-images/gen_1789982599838_c8f89267.jpg +0 -0
- package/assets/generated-images/gen_1789982608948_b08d16ad.jpg +0 -0
- package/assets/generated-images/gen_1789982618063_c2d3fb42.jpg +0 -0
- package/assets/generated-images/gen_1789982626863_3c386cb6.jpg +0 -0
- package/assets/generated-images/gen_1789982636865_c9fdfa16.jpg +0 -0
- package/assets/generated-images/gen_1789982646478_0e7c60b3.jpg +0 -0
- package/assets/generated-images/gen_1789982655687_3d71a0cc.jpg +0 -0
- package/assets/generated-images/gen_1789982665153_e2ecd91f.jpg +0 -0
- package/assets/generated-images/gen_1789982674589_9bcabe11.jpg +0 -0
- package/assets/generated-images/gen_1789982683093_e92fc3ac.jpg +0 -0
- package/assets/generated-images/gen_1789982693035_b6d184c7.jpg +0 -0
- package/assets/generated-images/gen_1789982702257_3b108f7b.jpg +0 -0
- package/assets/generated-images/gen_1789982711758_9f480e3f.jpg +0 -0
- package/assets/generated-images/gen_1789982721489_4bf84186.jpg +0 -0
- package/assets/generated-images/gen_1789982730122_db36d0e4.jpg +0 -0
- package/assets/generated-images/gen_1789982740142_bea82ed7.jpg +0 -0
- package/assets/generated-images/gen_1789982748906_a97ffb7a.jpg +0 -0
- package/assets/generated-images/gen_1789982818435_30194053.jpg +0 -0
- package/assets/logos/avocado-hub.svg +7 -4
- package/dist/args.d.ts +42 -0
- package/dist/args.js +97 -0
- package/dist/index.js +77 -25
- package/dist/prompts.js +26 -6
- package/dist/templates/demo-content.d.ts +1 -1
- package/dist/templates/demo-content.js +1 -1
- package/dist/versions.d.ts +1 -1
- package/dist/versions.js +1 -1
- package/package.json +4 -4
- package/assets/generated-images/gen_1774991902955_0p1i2o.webp +0 -0
- package/assets/generated-images/gen_1788955681679_454dc316.webp +0 -0
- package/assets/generated-images/gen_1789906172816_d7d537c1.jpg +0 -0
- package/assets/generated-images/var_1773434968055_8g99nk.webp +0 -0
- package/assets/generated-images/var_1773435042084_wvet78.webp +0 -0
- package/assets/generated-images/var_1773435061562_6hf8s6.webp +0 -0
- package/assets/generated-images/var_1773435080764_6ocahu.webp +0 -0
- package/assets/generated-images/var_1773435101045_1y544z.webp +0 -0
- package/assets/generated-images/var_1776858715876_iuu72w.webp +0 -0
package/README.md
CHANGED
|
@@ -55,6 +55,30 @@ For an existing site of any size this is the smaller half of the job. The
|
|
|
55
55
|
larger half — your components, your content model, your CMS — is at
|
|
56
56
|
[docs.avocadostudio.dev/sites](https://docs.avocadostudio.dev/sites).
|
|
57
57
|
|
|
58
|
+
## Unattended runs
|
|
59
|
+
|
|
60
|
+
The demo path takes a directory name and needs no terminal, so it can be
|
|
61
|
+
scripted, run in CI, or baked into an image:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm create avocado-site@latest my-demo -- --yes --no-install
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Option | |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `-y`, `--yes` | Never ask. Implied whenever stdin is not a terminal. |
|
|
70
|
+
| `--no-install` | Write the files and stop, for callers that install their own way. |
|
|
71
|
+
| `--api-key KEY` | Write `KEY` to `.env.local`; the variable is chosen from its prefix. Also read from `AVOCADO_SETUP_API_KEY`. |
|
|
72
|
+
| `-h`, `--help` | Print the usage. |
|
|
73
|
+
|
|
74
|
+
A key is written only when you name it. A provider variable that happens to be
|
|
75
|
+
exported in your shell is left alone — it is usually your own credential, and a
|
|
76
|
+
scaffold is about to become a git repository.
|
|
77
|
+
|
|
78
|
+
Unattended runs never start the dev server; they print the command instead. A
|
|
79
|
+
run with no terminal and no directory name exits 1 and says why, because the
|
|
80
|
+
guided path has questions that nothing can answer for you.
|
|
81
|
+
|
|
58
82
|
## Requirements
|
|
59
83
|
|
|
60
84
|
- Node.js 22+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
|
|
2
2
|
<title id="title">The Avocado Hub logo</title>
|
|
3
|
-
<desc id="desc">Stylized avocado mark —
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<desc id="desc">Stylized avocado mark — brand green shell, warm cream flesh, muted brown stone.</desc>
|
|
4
|
+
<!-- One green, and it is the brand's. The mark previously used #1F7A3A over
|
|
5
|
+
#7ED957: two greens, neither of them the #0f766e on every button, and the
|
|
6
|
+
lime saturated well past anything else on the page. -->
|
|
7
|
+
<path d="M64 8C88 8 112 40 112 72C112 104 88 120 64 120C40 120 16 104 16 72C16 40 40 8 64 8Z" fill="#0f766e"/>
|
|
8
|
+
<path d="M64 18C82 18 100 44 100 72C100 100 82 110 64 110C46 110 28 100 28 72C28 44 46 18 64 18Z" fill="#e9e4d4"/>
|
|
9
|
+
<circle cx="64" cy="72" r="20" fill="#8a6a4f"/>
|
|
7
10
|
</svg>
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the command was asked to do, decided before anything can block.
|
|
3
|
+
*
|
|
4
|
+
* This file exists because the scaffolder could not be run by anything that is
|
|
5
|
+
* not a person at a keyboard. The demo path asks for an API key and then, at
|
|
6
|
+
* the end, whether to start the dev server — both unconditional, both
|
|
7
|
+
* `@clack/prompts`. With stdin closed the first one cancels and the process
|
|
8
|
+
* exits 1 having created nothing; under a pty with a newline piped in it
|
|
9
|
+
* ignores the keystroke and waits forever. So no CI job, no Dockerfile, no
|
|
10
|
+
* agent and no gate could run `npm create avocado-site`, which is the one
|
|
11
|
+
* command every new user runs first. `scaffold-serve-check.mjs` generates from
|
|
12
|
+
* the scaffolder's *templates* rather than by running it, so nothing in the
|
|
13
|
+
* repo noticed.
|
|
14
|
+
*
|
|
15
|
+
* Parsing is separated from `main()` for the reason the prompts had to be: a
|
|
16
|
+
* pure function over `argv` and `env` can be tested without a terminal.
|
|
17
|
+
*/
|
|
18
|
+
export type ParsedArgs = {
|
|
19
|
+
/** The directory to create. Its absence is what selects the guided path. */
|
|
20
|
+
target?: string;
|
|
21
|
+
/** No question may block: `--yes`, or stdin is not a terminal. */
|
|
22
|
+
nonInteractive: boolean;
|
|
23
|
+
/** `--no-install` writes the files and stops, for gates and images. */
|
|
24
|
+
install: boolean;
|
|
25
|
+
/** Only ever from an explicit flag or `AVOCADO_SETUP_API_KEY` — see below. */
|
|
26
|
+
apiKey?: {
|
|
27
|
+
variable: string;
|
|
28
|
+
value: string;
|
|
29
|
+
};
|
|
30
|
+
help: boolean;
|
|
31
|
+
unknown: string[];
|
|
32
|
+
};
|
|
33
|
+
export declare const USAGE = "Usage\n npm create avocado-site [directory] [options]\n\n With a directory name, bootstraps the demo into it. With none, asks what to\n build \u2014 which needs a terminal.\n\nOptions\n -y, --yes Never ask. Implied when stdin is not a terminal.\n --no-install Write the files, skip `npm install`.\n --api-key KEY Write KEY to .env.local. The variable is chosen from the\n key's prefix. Also read from AVOCADO_SETUP_API_KEY.\n -h, --help Show this.\n\nNon-interactive runs never start the dev server; the command to start it is\nprinted instead.";
|
|
34
|
+
export declare function parseArgs(argv: string[], env?: Record<string, string | undefined>, isTty?: boolean): ParsedArgs;
|
|
35
|
+
/**
|
|
36
|
+
* Why a run with no terminal and no directory name cannot continue.
|
|
37
|
+
*
|
|
38
|
+
* The guided path is four questions; there is no answer to guess at. What this
|
|
39
|
+
* must not do is what it used to: exit 1 with an empty directory listing and
|
|
40
|
+
* nothing on stderr, which reads as a crash rather than as a choice.
|
|
41
|
+
*/
|
|
42
|
+
export declare const NEEDS_A_NAME = "create-avocado-site: no terminal, and no directory name.\n\nThe guided setup asks what to build, which needs a terminal. To run unattended,\nname the directory to create:\n\n npm create avocado-site my-site -- --yes\n\nUsage\n npm create avocado-site [directory] [options]\n\n With a directory name, bootstraps the demo into it. With none, asks what to\n build \u2014 which needs a terminal.\n\nOptions\n -y, --yes Never ask. Implied when stdin is not a terminal.\n --no-install Write the files, skip `npm install`.\n --api-key KEY Write KEY to .env.local. The variable is chosen from the\n key's prefix. Also read from AVOCADO_SETUP_API_KEY.\n -h, --help Show this.\n\nNon-interactive runs never start the dev server; the command to start it is\nprinted instead.";
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { variableForKey } from "./prompts.js";
|
|
2
|
+
export const USAGE = `Usage
|
|
3
|
+
npm create avocado-site [directory] [options]
|
|
4
|
+
|
|
5
|
+
With a directory name, bootstraps the demo into it. With none, asks what to
|
|
6
|
+
build — which needs a terminal.
|
|
7
|
+
|
|
8
|
+
Options
|
|
9
|
+
-y, --yes Never ask. Implied when stdin is not a terminal.
|
|
10
|
+
--no-install Write the files, skip \`npm install\`.
|
|
11
|
+
--api-key KEY Write KEY to .env.local. The variable is chosen from the
|
|
12
|
+
key's prefix. Also read from AVOCADO_SETUP_API_KEY.
|
|
13
|
+
-h, --help Show this.
|
|
14
|
+
|
|
15
|
+
Non-interactive runs never start the dev server; the command to start it is
|
|
16
|
+
printed instead.`;
|
|
17
|
+
/**
|
|
18
|
+
* The key is taken only when it is named, never inherited from the shell.
|
|
19
|
+
*
|
|
20
|
+
* `--api-key` and `AVOCADO_SETUP_API_KEY` both say "put this in the project I
|
|
21
|
+
* am creating". A bare `ANTHROPIC_API_KEY` in the environment says nothing of
|
|
22
|
+
* the sort — it is usually the developer's own key, exported in a shell
|
|
23
|
+
* profile, and harvesting it would write a live credential into a new
|
|
24
|
+
* `.env.local` in a directory that is about to become a git repository. The
|
|
25
|
+
* interactive path has always required the key to be typed at it; the
|
|
26
|
+
* unattended path requires it to be named. Neither reads what happens to be
|
|
27
|
+
* lying around.
|
|
28
|
+
*/
|
|
29
|
+
const API_KEY_ENV_VAR = "AVOCADO_SETUP_API_KEY";
|
|
30
|
+
export function parseArgs(argv, env = process.env, isTty = Boolean(process.stdin.isTTY)) {
|
|
31
|
+
let target;
|
|
32
|
+
let yes = false;
|
|
33
|
+
let install = true;
|
|
34
|
+
let help = false;
|
|
35
|
+
let rawKey;
|
|
36
|
+
const unknown = [];
|
|
37
|
+
for (let i = 0; i < argv.length; i++) {
|
|
38
|
+
const arg = argv[i];
|
|
39
|
+
/*
|
|
40
|
+
* `--api-key sk-ant-…` takes the next token, which is why the old
|
|
41
|
+
* one-liner could not survive a flag that takes a value: it picked the
|
|
42
|
+
* target with `find(arg => !arg.startsWith("-"))`, so the key itself would
|
|
43
|
+
* have become the directory name — and the scaffold would have been
|
|
44
|
+
* created in a directory named after a secret.
|
|
45
|
+
*/
|
|
46
|
+
if (arg === "--api-key") {
|
|
47
|
+
rawKey = argv[++i];
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (arg.startsWith("--api-key=")) {
|
|
51
|
+
rawKey = arg.slice("--api-key=".length);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (arg === "-y" || arg === "--yes") {
|
|
55
|
+
yes = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (arg === "--no-install") {
|
|
59
|
+
install = false;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === "-h" || arg === "--help") {
|
|
63
|
+
help = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg.startsWith("-")) {
|
|
67
|
+
unknown.push(arg);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
target ??= arg;
|
|
71
|
+
}
|
|
72
|
+
const value = (rawKey ?? env[API_KEY_ENV_VAR] ?? "").trim();
|
|
73
|
+
const variable = value ? variableForKey(value) : null;
|
|
74
|
+
return {
|
|
75
|
+
target,
|
|
76
|
+
nonInteractive: yes || !isTty,
|
|
77
|
+
install,
|
|
78
|
+
apiKey: variable && value ? { variable, value } : undefined,
|
|
79
|
+
help,
|
|
80
|
+
unknown,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Why a run with no terminal and no directory name cannot continue.
|
|
85
|
+
*
|
|
86
|
+
* The guided path is four questions; there is no answer to guess at. What this
|
|
87
|
+
* must not do is what it used to: exit 1 with an empty directory listing and
|
|
88
|
+
* nothing on stderr, which reads as a crash rather than as a choice.
|
|
89
|
+
*/
|
|
90
|
+
export const NEEDS_A_NAME = `create-avocado-site: no terminal, and no directory name.
|
|
91
|
+
|
|
92
|
+
The guided setup asks what to build, which needs a terminal. To run unattended,
|
|
93
|
+
name the directory to create:
|
|
94
|
+
|
|
95
|
+
npm create avocado-site my-site -- --yes
|
|
96
|
+
|
|
97
|
+
${USAGE}`;
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,33 @@ import { resolve } from "node:path";
|
|
|
7
7
|
import { runPrompts, demoConfig, promptForApiKey, AVOCADO_INTRO } from "./prompts.js";
|
|
8
8
|
import { collectFiles, collectDemoFiles, generateFiles } from "./generator.js";
|
|
9
9
|
import { printInstructions } from "./instructions.js";
|
|
10
|
+
import { parseArgs, USAGE, NEEDS_A_NAME } from "./args.js";
|
|
10
11
|
async function main() {
|
|
11
12
|
const cwd = process.cwd();
|
|
13
|
+
const args = parseArgs(process.argv.slice(2));
|
|
14
|
+
if (args.help) {
|
|
15
|
+
console.log(USAGE);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (args.unknown.length > 0) {
|
|
19
|
+
console.error(`create-avocado-site: unknown option ${args.unknown.join(", ")}\n\n${USAGE}`);
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
/*
|
|
24
|
+
* Say so, rather than cancelling into an empty directory listing.
|
|
25
|
+
*
|
|
26
|
+
* Without a terminal the guided path's first prompt returns a cancel
|
|
27
|
+
* symbol, `main` returns, and the process exits 1 with nothing written and
|
|
28
|
+
* nothing printed. Every unattended caller that ever tried this — a CI job,
|
|
29
|
+
* an image build, an agent — saw a bare exit code and no way to tell a crash
|
|
30
|
+
* from a refusal.
|
|
31
|
+
*/
|
|
32
|
+
if (args.nonInteractive && !args.target) {
|
|
33
|
+
console.error(NEEDS_A_NAME);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
12
37
|
/*
|
|
13
38
|
* `npm create avocado-site my-demo` — a directory argument means the demo,
|
|
14
39
|
* with no *configuration* questions. Somebody who has typed a name has
|
|
@@ -24,15 +49,21 @@ async function main() {
|
|
|
24
49
|
* and nothing in the product confirms either happened. "I added the key and
|
|
25
50
|
* nothing changed" has no error message anywhere in the system, and this
|
|
26
51
|
* prompt is the cheapest way to stop most people arriving there.
|
|
52
|
+
*
|
|
53
|
+
* Unattended, that reasoning inverts: there is nobody to ask, and a question
|
|
54
|
+
* nobody can answer is not a prompt, it is a hang. `--api-key` says the same
|
|
55
|
+
* thing in advance, and skipping stays free — a scaffold with no key is a
|
|
56
|
+
* complete, working project.
|
|
27
57
|
*/
|
|
28
|
-
const target =
|
|
58
|
+
const target = args.target;
|
|
29
59
|
let config;
|
|
30
60
|
if (target) {
|
|
31
61
|
// `runPrompts` opens with this; the fast path had no intro at all, so a
|
|
32
62
|
// stranger's first interaction with the product was an unexplained request
|
|
33
63
|
// for a credential on an otherwise blank terminal.
|
|
34
64
|
p.intro(AVOCADO_INTRO);
|
|
35
|
-
|
|
65
|
+
const apiKey = args.apiKey ?? (args.nonInteractive ? undefined : await promptForApiKey());
|
|
66
|
+
config = await demoConfig(target, apiKey);
|
|
36
67
|
}
|
|
37
68
|
else {
|
|
38
69
|
config = await runPrompts(cwd);
|
|
@@ -40,7 +71,7 @@ async function main() {
|
|
|
40
71
|
if (!config)
|
|
41
72
|
return;
|
|
42
73
|
if (config.mode === "demo") {
|
|
43
|
-
await bootstrapDemo(cwd, target ?? config.siteId, config);
|
|
74
|
+
await bootstrapDemo(cwd, target ?? config.siteId, config, args);
|
|
44
75
|
return;
|
|
45
76
|
}
|
|
46
77
|
await scaffoldInto(cwd, collectFiles(config), config);
|
|
@@ -69,7 +100,7 @@ async function scaffoldInto(dir, files, config) {
|
|
|
69
100
|
* ended, and every printed step is a place for somebody evaluating the product
|
|
70
101
|
* to put it down.
|
|
71
102
|
*/
|
|
72
|
-
async function bootstrapDemo(cwd, dirName, config) {
|
|
103
|
+
async function bootstrapDemo(cwd, dirName, config, options) {
|
|
73
104
|
const dir = resolve(cwd, dirName);
|
|
74
105
|
if (existsSync(dir) && (await readdir(dir)).length > 0) {
|
|
75
106
|
p.log.error(`${dirName} already exists and is not empty.`);
|
|
@@ -81,21 +112,32 @@ async function bootstrapDemo(cwd, dirName, config) {
|
|
|
81
112
|
const files = collectDemoFiles(config);
|
|
82
113
|
await generateFiles(dir, files);
|
|
83
114
|
p.log.success(`Created ${dirName} — ${files.length} files`);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
/*
|
|
116
|
+
* `--no-install` is for the callers that install differently or not at all —
|
|
117
|
+
* a gate that overlays tarballs packed from a working tree, an image build
|
|
118
|
+
* with its own cache, a test that only cares that the files are correct.
|
|
119
|
+
* Without it, proving the command runs costs a full `npm install`.
|
|
120
|
+
*/
|
|
121
|
+
if (options.install) {
|
|
122
|
+
const spinner = p.spinner();
|
|
123
|
+
spinner.start("Installing dependencies");
|
|
124
|
+
const installed = await run("npm", ["install", "--no-audit", "--no-fund"], dir);
|
|
125
|
+
if (installed) {
|
|
126
|
+
spinner.stop("Installed dependencies");
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
/*
|
|
130
|
+
* Not fatal, and worth being explicit about rather than exiting. A failed
|
|
131
|
+
* install is usually a registry or proxy problem on the user's side, and
|
|
132
|
+
* the project on disk is complete and correct — `npm install` in it will
|
|
133
|
+
* work once that is fixed. Deleting it would throw away the good half.
|
|
134
|
+
*/
|
|
135
|
+
spinner.stop("Could not install dependencies");
|
|
136
|
+
p.log.warn(`Run \`npm install\` in ${dirName} yourself — everything else is in place.`);
|
|
137
|
+
}
|
|
89
138
|
}
|
|
90
139
|
else {
|
|
91
|
-
|
|
92
|
-
* Not fatal, and worth being explicit about rather than exiting. A failed
|
|
93
|
-
* install is usually a registry or proxy problem on the user's side, and
|
|
94
|
-
* the project on disk is complete and correct — `npm install` in it will
|
|
95
|
-
* work once that is fixed. Deleting it would throw away the good half.
|
|
96
|
-
*/
|
|
97
|
-
spinner.stop("Could not install dependencies");
|
|
98
|
-
p.log.warn(`Run \`npm install\` in ${dirName} yourself — everything else is in place.`);
|
|
140
|
+
p.log.warn(`Skipped \`npm install\` — run it in ${dirName} before starting.`);
|
|
99
141
|
}
|
|
100
142
|
p.log.message([
|
|
101
143
|
` cd ${dirName} && npm run dev`,
|
|
@@ -138,11 +180,12 @@ async function bootstrapDemo(cwd, dirName, config) {
|
|
|
138
180
|
` https://docs.avocadostudio.dev/sites`,
|
|
139
181
|
``,
|
|
140
182
|
...(config.apiKey
|
|
141
|
-
? [`
|
|
183
|
+
? [` AI editing is on \u2014 ${config.apiKey.variable} is in .env.local.`]
|
|
142
184
|
: [
|
|
143
|
-
`
|
|
144
|
-
`
|
|
145
|
-
`
|
|
185
|
+
` Visual editing works now: click a block, edit its fields, publish.`,
|
|
186
|
+
` For AI editing \u2014 describing a change and having it planned for you \u2014`,
|
|
187
|
+
` add ANTHROPIC_API_KEY, OPENAI_API_KEY or GOOGLE_GENAI_API_KEY to`,
|
|
188
|
+
` .env.local and restart.`,
|
|
146
189
|
]),
|
|
147
190
|
].join("\n"));
|
|
148
191
|
/*
|
|
@@ -158,10 +201,19 @@ async function bootstrapDemo(cwd, dirName, config) {
|
|
|
158
201
|
* script or a container wants the files and nothing else. The default is yes
|
|
159
202
|
* because the overwhelmingly common case is a person who wants to see it.
|
|
160
203
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
204
|
+
/*
|
|
205
|
+
* The second thing that could not be answered without a keyboard, and the
|
|
206
|
+
* more dangerous of the two: a `confirm` whose default is *yes* would, if it
|
|
207
|
+
* ever stopped blocking, spawn two long-running dev servers inside a CI job
|
|
208
|
+
* or an image build. Unattended, the answer is always no — the caller asked
|
|
209
|
+
* for a project, not for a process it has no way to stop.
|
|
210
|
+
*/
|
|
211
|
+
const start = options.nonInteractive
|
|
212
|
+
? false
|
|
213
|
+
: await p.confirm({
|
|
214
|
+
message: `Start it now? (runs \`npm run dev\` in ${dirName})`,
|
|
215
|
+
initialValue: true,
|
|
216
|
+
});
|
|
165
217
|
if (p.isCancel(start) || !start) {
|
|
166
218
|
p.outro(`Done. Run \`cd ${dirName} && npm run dev\` when you are ready.`);
|
|
167
219
|
return;
|
package/dist/prompts.js
CHANGED
|
@@ -64,12 +64,30 @@ export async function promptForApiKey() {
|
|
|
64
64
|
* variable is chosen from the prefix, so a reader had no way to know whether
|
|
65
65
|
* their key was one of the right ones.
|
|
66
66
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
/*
|
|
68
|
+
* Name the product's own headline feature as the headline feature.
|
|
69
|
+
*
|
|
70
|
+
* This heading read "Optional \u2014 enable chat now", which is two mistakes in
|
|
71
|
+
* five words. "Chat" names the input box rather than what it does, and
|
|
72
|
+
* "optional" is the first word a newcomer reads about the one capability
|
|
73
|
+
* the whole product exists for \u2014 describing a change in a sentence and
|
|
74
|
+
* having it planned, applied and previewed for you. Skipping has to stay
|
|
75
|
+
* available and free of friction; it does not have to be the frame.
|
|
76
|
+
*
|
|
77
|
+
* So the heading says what this is, the first paragraph says what it does
|
|
78
|
+
* with an example, and *then* it says you can skip \u2014 in that order,
|
|
79
|
+
* because a reader who skips should know what they are skipping.
|
|
80
|
+
*/
|
|
81
|
+
p.note("Describe a change and Avocado plans it, applies it and shows you the\n" +
|
|
82
|
+
"result in the live preview \u2014 \"make the hero headline shorter\",\n" +
|
|
83
|
+
"\"add a testimonials section\", \"translate this page to German\".\n" +
|
|
84
|
+
"That is the product, and it needs a model.\n\n" +
|
|
85
|
+
"You can skip and add one later. Visual editing works without a key:\n" +
|
|
86
|
+
"the preview, click-to-select, the property panel and publishing.\n\n" +
|
|
69
87
|
" ANTHROPIC_API_KEY \u2190 recommended, the best-tested planner\n" +
|
|
70
88
|
" OPENAI_API_KEY also does image generation\n" +
|
|
71
89
|
" GOOGLE_GENAI_API_KEY also does image generation\n\n" +
|
|
72
|
-
"The right variable is picked from the key itself.", "
|
|
90
|
+
"The right variable is picked from the key itself.", "AI editing \u2014 the main event");
|
|
73
91
|
const key = await p.password({
|
|
74
92
|
message: "Paste a key, or press Enter to skip",
|
|
75
93
|
mask: "\u2022",
|
|
@@ -79,9 +97,11 @@ export async function promptForApiKey() {
|
|
|
79
97
|
return undefined;
|
|
80
98
|
const value = typeof key === "string" ? key.trim() : "";
|
|
81
99
|
if (!value) {
|
|
82
|
-
p.note("
|
|
83
|
-
"
|
|
84
|
-
"
|
|
100
|
+
p.note("Visual editing works in full: the preview, click-to-select, the property\n" +
|
|
101
|
+
"panel and publishing. AI editing falls back to a built-in demo planner,\n" +
|
|
102
|
+
"which handles simple, literal edits and cannot write copy or reason\n" +
|
|
103
|
+
"about a page.\n\n" +
|
|
104
|
+
"Add a key to .env.local and restart the dev server to turn on the real thing.", "No key \u2014 that is fine");
|
|
85
105
|
return undefined;
|
|
86
106
|
}
|
|
87
107
|
const variable = variableForKey(value);
|