@creatica/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/cli.js +256 -0
- package/dist/cli.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anup Aglawe (Creatica)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import {
|
|
5
|
+
ENGINE_VERSION,
|
|
6
|
+
listStructures,
|
|
7
|
+
renderPng,
|
|
8
|
+
renderSvg
|
|
9
|
+
} from "@creatica/engine";
|
|
10
|
+
import { createMcpServer } from "@creatica/mcp";
|
|
11
|
+
import {
|
|
12
|
+
catalog,
|
|
13
|
+
describe,
|
|
14
|
+
listPalettes,
|
|
15
|
+
normalizeRequest,
|
|
16
|
+
styleCard,
|
|
17
|
+
suggest
|
|
18
|
+
} from "@creatica/schema";
|
|
19
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
21
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
22
|
+
import { dirname, resolve } from "path";
|
|
23
|
+
process.stdout.on("error", (e) => {
|
|
24
|
+
if (e.code === "EPIPE") process.exit(0);
|
|
25
|
+
throw e;
|
|
26
|
+
});
|
|
27
|
+
var program = new Command();
|
|
28
|
+
program.name("creatica").description(
|
|
29
|
+
"Procedural SVG/PNG backgrounds from the terminal. Renders locally; add --hosted to use the Creatica API and get a permanent image URL."
|
|
30
|
+
).version(ENGINE_VERSION).option(
|
|
31
|
+
"--api-url <url>",
|
|
32
|
+
"API origin for --hosted",
|
|
33
|
+
process.env.CREATICA_API_URL ?? "https://api.creatica.app"
|
|
34
|
+
).option(
|
|
35
|
+
"--api-key <key>",
|
|
36
|
+
"API key for --hosted (or CREATICA_API_KEY)",
|
|
37
|
+
process.env.CREATICA_API_KEY
|
|
38
|
+
);
|
|
39
|
+
program.command("list").description("List background structures").option("-q, --query <text>", "rank by a free-text brief").option("-m, --mood <word>", "filter by mood word").option("-t, --tag <tag>", "filter by tag").option("--json", "machine-readable output").action(
|
|
40
|
+
(o) => {
|
|
41
|
+
let items = catalog();
|
|
42
|
+
if (o.mood)
|
|
43
|
+
items = items.filter(
|
|
44
|
+
(c) => c.mood.some((m) => m.includes(o.mood.toLowerCase()))
|
|
45
|
+
);
|
|
46
|
+
if (o.tag)
|
|
47
|
+
items = items.filter(
|
|
48
|
+
(c) => c.tags.some((t) => t.toLowerCase().includes(o.tag.toLowerCase()))
|
|
49
|
+
);
|
|
50
|
+
if (o.query) {
|
|
51
|
+
const ranked = suggest(o.query, { limit: 100 }).map((s) => s.name);
|
|
52
|
+
items = items.filter((c) => ranked.includes(c.name)).sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));
|
|
53
|
+
}
|
|
54
|
+
if (o.json) return out(JSON.stringify(items, null, 2));
|
|
55
|
+
for (const c of items) out(`${c.name.padEnd(20)} ${c.summary}`);
|
|
56
|
+
out(
|
|
57
|
+
`
|
|
58
|
+
${items.length} structures. \`creatica describe <name>\` for options.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
program.command("describe <name>").description("Show the style card and every option a structure honours").option("--json", "machine-readable output (includes JSON Schema)").action((name, o) => {
|
|
63
|
+
try {
|
|
64
|
+
out(o.json ? JSON.stringify(describe(name), null, 2) : styleCard(name));
|
|
65
|
+
} catch (e) {
|
|
66
|
+
die(e.message);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
program.command("palettes").description("List built-in palettes").option(
|
|
70
|
+
"-m, --mood <word>",
|
|
71
|
+
"filter by mood tag (dark, pastel, vivid, warm, cool\u2026)"
|
|
72
|
+
).option("--json").action((o) => {
|
|
73
|
+
let items = listPalettes();
|
|
74
|
+
if (o.mood)
|
|
75
|
+
items = items.filter((p) => p.mood.includes(o.mood.toLowerCase()));
|
|
76
|
+
if (o.json) return out(JSON.stringify(items, null, 2));
|
|
77
|
+
for (const p of items)
|
|
78
|
+
out(
|
|
79
|
+
`${p.id.padEnd(20)} ${p.colors.join(" ").padEnd(64)} ${p.mood.join(", ")}`
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
program.command("suggest <brief...>").description(
|
|
83
|
+
"Rank structures for a brief, e.g. `creatica suggest dark aurora glow for an AI landing page`"
|
|
84
|
+
).option("-n, --limit <n>", "how many", "5").option("--json").action((brief, o) => {
|
|
85
|
+
const s = suggest(brief.join(" "), { limit: Number(o.limit) });
|
|
86
|
+
if (o.json) return out(JSON.stringify(s, null, 2));
|
|
87
|
+
if (!s.length) return out("No matches. Try `creatica list`.");
|
|
88
|
+
for (const x of s)
|
|
89
|
+
out(
|
|
90
|
+
`${x.name.padEnd(20)} ${x.summary}
|
|
91
|
+
${"".padEnd(20)} matched: ${x.matched.join(", ")}${x.palettes.length ? `; palettes: ${x.palettes.join(", ")}` : ""}`
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
program.command("gen <structure>").alias("generate").description(
|
|
95
|
+
"Render a background. Writes SVG by default; use --png or an .png output path for a raster."
|
|
96
|
+
).option("-s, --seed <seed>", "reproducible seed (any string)").option("-w, --width <px>", "width in px", int).option("-h, --height <px>", "height in px", int).option("-p, --palette <id>", "built-in palette id (see `creatica palettes`)").option("-c, --colors <hex,hex,\u2026>", "explicit colours, comma separated").option("--primary <hex>", "primary colour").option("--bg <hex>", "background colour").option("--gradient", "gradient fills").option(
|
|
97
|
+
"--set <key=value>",
|
|
98
|
+
"any structure option, repeatable (see `creatica describe`)",
|
|
99
|
+
collect,
|
|
100
|
+
[]
|
|
101
|
+
).option("--png", "output PNG instead of SVG").option(
|
|
102
|
+
"--png-width <px>",
|
|
103
|
+
"raster width for PNG (defaults to render width)",
|
|
104
|
+
int
|
|
105
|
+
).option("-o, --out <path>", "output file, or - for stdout", "-").option("--hosted", "render via the Creatica API and print a permanent URL").option(
|
|
106
|
+
"--json",
|
|
107
|
+
"print JSON metadata (seed, url, options) instead of the image; with -o, in addition to writing it"
|
|
108
|
+
).action(async (structure, o, cmd) => {
|
|
109
|
+
const globals = cmd.optsWithGlobals();
|
|
110
|
+
const input = {
|
|
111
|
+
structure,
|
|
112
|
+
seed: o.seed,
|
|
113
|
+
width: o.width,
|
|
114
|
+
height: o.height
|
|
115
|
+
};
|
|
116
|
+
const palette = {};
|
|
117
|
+
if (o.palette) palette.palette = o.palette;
|
|
118
|
+
if (o.colors) palette.colors = o.colors;
|
|
119
|
+
if (o.primary) palette.primaryColor = o.primary;
|
|
120
|
+
if (o.bg) palette.backgroundColor = o.bg;
|
|
121
|
+
if (o.gradient) palette.gradientColor = true;
|
|
122
|
+
const shape = {};
|
|
123
|
+
for (const kv of o.set) {
|
|
124
|
+
const i = kv.indexOf("=");
|
|
125
|
+
if (i < 0) die(`--set expects key=value, got "${kv}"`);
|
|
126
|
+
shape[kv.slice(0, i)] = kv.slice(i + 1);
|
|
127
|
+
}
|
|
128
|
+
if (Object.keys(palette).length) input.palette = palette;
|
|
129
|
+
if (Object.keys(shape).length) input.shape = shape;
|
|
130
|
+
let normalized;
|
|
131
|
+
try {
|
|
132
|
+
normalized = normalizeRequest(input);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
die(e.message);
|
|
135
|
+
}
|
|
136
|
+
for (const w of normalized.warnings)
|
|
137
|
+
process.stderr.write(`warning: ${w}
|
|
138
|
+
`);
|
|
139
|
+
const wantPng = o.png || o.out.toLowerCase().endsWith(".png");
|
|
140
|
+
const format = wantPng ? "png" : "svg";
|
|
141
|
+
if (o.hosted) {
|
|
142
|
+
const res = await fetch(new URL("/v1/generate", globals.apiUrl), {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: {
|
|
145
|
+
"content-type": "application/json",
|
|
146
|
+
...globals.apiKey ? { authorization: `Bearer ${globals.apiKey}` } : {}
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
...normalized.request,
|
|
150
|
+
format: o.out === "-" && !o.json ? format : "url",
|
|
151
|
+
pngWidth: o.pngWidth
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
const body = await res.json();
|
|
155
|
+
if (!res.ok)
|
|
156
|
+
die(
|
|
157
|
+
`${res.status} ${body.error}: ${body.message}${body.upgrade_url ? `
|
|
158
|
+
${body.upgrade_url}` : ""}`
|
|
159
|
+
);
|
|
160
|
+
if (o.out !== "-") {
|
|
161
|
+
const img = await fetch(
|
|
162
|
+
String(format === "png" ? body.pngUrl : body.svgUrl),
|
|
163
|
+
{
|
|
164
|
+
headers: globals.apiKey ? { authorization: `Bearer ${globals.apiKey}` } : {}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
await writeOut(o.out, Buffer.from(await img.arrayBuffer()));
|
|
168
|
+
process.stderr.write(`wrote ${o.out}
|
|
169
|
+
`);
|
|
170
|
+
}
|
|
171
|
+
if (o.json || o.out !== "-") {
|
|
172
|
+
const { svg: _s, png: _p, ...meta2 } = body;
|
|
173
|
+
return out(JSON.stringify(meta2, null, 2));
|
|
174
|
+
}
|
|
175
|
+
if (format === "png")
|
|
176
|
+
return process.stdout.write(Buffer.from(String(body.png), "base64"));
|
|
177
|
+
return process.stdout.write(String(body.svg));
|
|
178
|
+
}
|
|
179
|
+
const r = await renderSvg(normalized.request);
|
|
180
|
+
const bytes = format === "png" ? await renderPng(r.svg, { width: o.pngWidth ?? r.width }) : Buffer.from(r.svg, "utf8");
|
|
181
|
+
const meta = {
|
|
182
|
+
structure: r.structure,
|
|
183
|
+
seed: r.seed,
|
|
184
|
+
width: r.width,
|
|
185
|
+
height: r.height,
|
|
186
|
+
format,
|
|
187
|
+
bytes: bytes.length,
|
|
188
|
+
renderMs: r.durationMs,
|
|
189
|
+
request: { ...normalized.request, seed: r.seed }
|
|
190
|
+
};
|
|
191
|
+
if (o.out === "-") {
|
|
192
|
+
if (o.json) return out(JSON.stringify(meta, null, 2));
|
|
193
|
+
process.stdout.write(bytes);
|
|
194
|
+
process.stderr.write(
|
|
195
|
+
`seed=${r.seed} ${r.width}x${r.height} ${bytes.length} bytes
|
|
196
|
+
`
|
|
197
|
+
);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
await writeOut(o.out, bytes);
|
|
201
|
+
process.stderr.write(
|
|
202
|
+
`wrote ${o.out} (seed=${r.seed}, ${bytes.length} bytes)
|
|
203
|
+
`
|
|
204
|
+
);
|
|
205
|
+
if (o.json) out(JSON.stringify(meta, null, 2));
|
|
206
|
+
});
|
|
207
|
+
program.command("random").description(
|
|
208
|
+
"Render a few random structures with random seeds into a directory"
|
|
209
|
+
).option("-n, --count <n>", "how many", "4").option("-d, --dir <dir>", "output directory", "creatica-random").option("--png", "PNG instead of SVG").option("-w, --width <px>", "width", int).option("-h, --height <px>", "height", int).action(
|
|
210
|
+
async (o) => {
|
|
211
|
+
const all = listStructures().map((s) => s.name);
|
|
212
|
+
for (let i = 0; i < Number(o.count); i++) {
|
|
213
|
+
const structure = all[Math.floor(Math.random() * all.length)];
|
|
214
|
+
const r = await renderSvg({
|
|
215
|
+
structure,
|
|
216
|
+
width: o.width,
|
|
217
|
+
height: o.height
|
|
218
|
+
});
|
|
219
|
+
const file = resolve(
|
|
220
|
+
o.dir,
|
|
221
|
+
`${structure}-${r.seed}.${o.png ? "png" : "svg"}`
|
|
222
|
+
);
|
|
223
|
+
await writeOut(file, o.png ? await renderPng(r.svg) : Buffer.from(r.svg));
|
|
224
|
+
out(`${file}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
program.command("mcp").description(
|
|
229
|
+
"Run the local MCP server over stdio (same as `npx @creatica/mcp`)"
|
|
230
|
+
).action(async () => {
|
|
231
|
+
await createMcpServer().connect(new StdioServerTransport());
|
|
232
|
+
});
|
|
233
|
+
function int(v) {
|
|
234
|
+
const n = Number(v);
|
|
235
|
+
if (!Number.isInteger(n) || n <= 0)
|
|
236
|
+
throw new InvalidArgumentError("expected a positive integer");
|
|
237
|
+
return n;
|
|
238
|
+
}
|
|
239
|
+
function collect(v, prev) {
|
|
240
|
+
return [...prev, v];
|
|
241
|
+
}
|
|
242
|
+
async function writeOut(path, bytes) {
|
|
243
|
+
const p = resolve(path);
|
|
244
|
+
await mkdir(dirname(p), { recursive: true });
|
|
245
|
+
await writeFile(p, bytes);
|
|
246
|
+
}
|
|
247
|
+
function out(s) {
|
|
248
|
+
process.stdout.write(s + "\n");
|
|
249
|
+
}
|
|
250
|
+
function die(msg) {
|
|
251
|
+
process.stderr.write(`error: ${msg}
|
|
252
|
+
`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
await program.parseAsync(process.argv);
|
|
256
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport {\n ENGINE_VERSION,\n listStructures,\n renderPng,\n renderSvg,\n} from '@creatica/engine';\nimport { createMcpServer } from '@creatica/mcp';\nimport {\n catalog,\n describe,\n listPalettes,\n normalizeRequest,\n styleCard,\n suggest,\n} from '@creatica/schema';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\n\n// `creatica list | head` closes the pipe early; exit quietly instead of tracing.\nprocess.stdout.on('error', (e: NodeJS.ErrnoException) => {\n if (e.code === 'EPIPE') process.exit(0);\n throw e;\n});\n\nconst program = new Command();\nprogram\n .name('creatica')\n .description(\n 'Procedural SVG/PNG backgrounds from the terminal. Renders locally; add --hosted to use the Creatica API and get a permanent image URL.',\n )\n .version(ENGINE_VERSION)\n .option(\n '--api-url <url>',\n 'API origin for --hosted',\n process.env.CREATICA_API_URL ?? 'https://api.creatica.app',\n )\n .option(\n '--api-key <key>',\n 'API key for --hosted (or CREATICA_API_KEY)',\n process.env.CREATICA_API_KEY,\n );\n\n// ------------------------------------------------------------------ list\nprogram\n .command('list')\n .description('List background structures')\n .option('-q, --query <text>', 'rank by a free-text brief')\n .option('-m, --mood <word>', 'filter by mood word')\n .option('-t, --tag <tag>', 'filter by tag')\n .option('--json', 'machine-readable output')\n .action(\n (o: { query?: string; mood?: string; tag?: string; json?: boolean }) => {\n let items = catalog();\n if (o.mood)\n items = items.filter((c) =>\n c.mood.some((m) => m.includes(o.mood!.toLowerCase())),\n );\n if (o.tag)\n items = items.filter((c) =>\n c.tags.some((t) => t.toLowerCase().includes(o.tag!.toLowerCase())),\n );\n if (o.query) {\n const ranked = suggest(o.query, { limit: 100 }).map((s) => s.name);\n items = items\n .filter((c) => ranked.includes(c.name))\n .sort((a, b) => ranked.indexOf(a.name) - ranked.indexOf(b.name));\n }\n if (o.json) return out(JSON.stringify(items, null, 2));\n for (const c of items) out(`${c.name.padEnd(20)} ${c.summary}`);\n out(\n `\\n${items.length} structures. \\`creatica describe <name>\\` for options.`,\n );\n },\n );\n\n// -------------------------------------------------------------- describe\nprogram\n .command('describe <name>')\n .description('Show the style card and every option a structure honours')\n .option('--json', 'machine-readable output (includes JSON Schema)')\n .action((name: string, o: { json?: boolean }) => {\n try {\n out(o.json ? JSON.stringify(describe(name), null, 2) : styleCard(name));\n } catch (e) {\n die((e as Error).message);\n }\n });\n\n// -------------------------------------------------------------- palettes\nprogram\n .command('palettes')\n .description('List built-in palettes')\n .option(\n '-m, --mood <word>',\n 'filter by mood tag (dark, pastel, vivid, warm, cool…)',\n )\n .option('--json')\n .action((o: { mood?: string; json?: boolean }) => {\n let items = listPalettes();\n if (o.mood)\n items = items.filter((p) => p.mood.includes(o.mood!.toLowerCase()));\n if (o.json) return out(JSON.stringify(items, null, 2));\n for (const p of items)\n out(\n `${p.id.padEnd(20)} ${p.colors.join(' ').padEnd(64)} ${p.mood.join(', ')}`,\n );\n });\n\n// --------------------------------------------------------------- suggest\nprogram\n .command('suggest <brief...>')\n .description(\n 'Rank structures for a brief, e.g. `creatica suggest dark aurora glow for an AI landing page`',\n )\n .option('-n, --limit <n>', 'how many', '5')\n .option('--json')\n .action((brief: string[], o: { limit: string; json?: boolean }) => {\n const s = suggest(brief.join(' '), { limit: Number(o.limit) });\n if (o.json) return out(JSON.stringify(s, null, 2));\n if (!s.length) return out('No matches. Try `creatica list`.');\n for (const x of s)\n out(\n `${x.name.padEnd(20)} ${x.summary}\\n${''.padEnd(20)} matched: ${x.matched.join(', ')}${x.palettes.length ? `; palettes: ${x.palettes.join(', ')}` : ''}`,\n );\n });\n\n// ------------------------------------------------------------------- gen\nprogram\n .command('gen <structure>')\n .alias('generate')\n .description(\n 'Render a background. Writes SVG by default; use --png or an .png output path for a raster.',\n )\n .option('-s, --seed <seed>', 'reproducible seed (any string)')\n .option('-w, --width <px>', 'width in px', int)\n .option('-h, --height <px>', 'height in px', int)\n .option('-p, --palette <id>', 'built-in palette id (see `creatica palettes`)')\n .option('-c, --colors <hex,hex,…>', 'explicit colours, comma separated')\n .option('--primary <hex>', 'primary colour')\n .option('--bg <hex>', 'background colour')\n .option('--gradient', 'gradient fills')\n .option(\n '--set <key=value>',\n 'any structure option, repeatable (see `creatica describe`)',\n collect,\n [] as string[],\n )\n .option('--png', 'output PNG instead of SVG')\n .option(\n '--png-width <px>',\n 'raster width for PNG (defaults to render width)',\n int,\n )\n .option('-o, --out <path>', 'output file, or - for stdout', '-')\n .option('--hosted', 'render via the Creatica API and print a permanent URL')\n .option(\n '--json',\n 'print JSON metadata (seed, url, options) instead of the image; with -o, in addition to writing it',\n )\n .action(async (structure: string, o: GenOptions, cmd: Command) => {\n const globals = cmd.optsWithGlobals() as {\n apiUrl: string;\n apiKey?: string;\n };\n const input: Record<string, unknown> = {\n structure,\n seed: o.seed,\n width: o.width,\n height: o.height,\n };\n const palette: Record<string, unknown> = {};\n if (o.palette) palette.palette = o.palette;\n if (o.colors) palette.colors = o.colors;\n if (o.primary) palette.primaryColor = o.primary;\n if (o.bg) palette.backgroundColor = o.bg;\n if (o.gradient) palette.gradientColor = true;\n const shape: Record<string, unknown> = {};\n for (const kv of o.set) {\n const i = kv.indexOf('=');\n if (i < 0) die(`--set expects key=value, got \"${kv}\"`);\n shape[kv.slice(0, i)] = kv.slice(i + 1);\n }\n if (Object.keys(palette).length) input.palette = palette;\n if (Object.keys(shape).length) input.shape = shape;\n\n let normalized;\n try {\n normalized = normalizeRequest(input);\n } catch (e) {\n die((e as Error).message);\n }\n for (const w of normalized!.warnings)\n process.stderr.write(`warning: ${w}\\n`);\n\n const wantPng = o.png || o.out.toLowerCase().endsWith('.png');\n const format = wantPng ? 'png' : 'svg';\n\n if (o.hosted) {\n const res = await fetch(new URL('/v1/generate', globals.apiUrl), {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...(globals.apiKey\n ? { authorization: `Bearer ${globals.apiKey}` }\n : {}),\n },\n body: JSON.stringify({\n ...normalized!.request,\n format: o.out === '-' && !o.json ? format : 'url',\n pngWidth: o.pngWidth,\n }),\n });\n const body = (await res.json()) as Record<string, unknown>;\n if (!res.ok)\n die(\n `${res.status} ${body.error}: ${body.message}${body.upgrade_url ? `\\n${body.upgrade_url}` : ''}`,\n );\n if (o.out !== '-') {\n const img = await fetch(\n String(format === 'png' ? body.pngUrl : body.svgUrl),\n {\n headers: globals.apiKey\n ? { authorization: `Bearer ${globals.apiKey}` }\n : {},\n },\n );\n await writeOut(o.out, Buffer.from(await img.arrayBuffer()));\n process.stderr.write(`wrote ${o.out}\\n`);\n }\n if (o.json || o.out !== '-') {\n const { svg: _s, png: _p, ...meta } = body;\n return out(JSON.stringify(meta, null, 2));\n }\n if (format === 'png')\n return process.stdout.write(Buffer.from(String(body.png), 'base64'));\n return process.stdout.write(String(body.svg));\n }\n\n const r = await renderSvg(normalized!.request);\n const bytes =\n format === 'png'\n ? await renderPng(r.svg, { width: o.pngWidth ?? r.width })\n : Buffer.from(r.svg, 'utf8');\n const meta = {\n structure: r.structure,\n seed: r.seed,\n width: r.width,\n height: r.height,\n format,\n bytes: bytes.length,\n renderMs: r.durationMs,\n request: { ...normalized!.request, seed: r.seed },\n };\n if (o.out === '-') {\n if (o.json) return out(JSON.stringify(meta, null, 2));\n process.stdout.write(bytes);\n process.stderr.write(\n `seed=${r.seed} ${r.width}x${r.height} ${bytes.length} bytes\\n`,\n );\n return;\n }\n await writeOut(o.out, bytes);\n process.stderr.write(\n `wrote ${o.out} (seed=${r.seed}, ${bytes.length} bytes)\\n`,\n );\n if (o.json) out(JSON.stringify(meta, null, 2));\n });\n\n// ---------------------------------------------------------------- random\nprogram\n .command('random')\n .description(\n 'Render a few random structures with random seeds into a directory',\n )\n .option('-n, --count <n>', 'how many', '4')\n .option('-d, --dir <dir>', 'output directory', 'creatica-random')\n .option('--png', 'PNG instead of SVG')\n .option('-w, --width <px>', 'width', int)\n .option('-h, --height <px>', 'height', int)\n .action(\n async (o: {\n count: string;\n dir: string;\n png?: boolean;\n width?: number;\n height?: number;\n }) => {\n const all = listStructures().map((s) => s.name);\n for (let i = 0; i < Number(o.count); i++) {\n const structure = all[Math.floor(Math.random() * all.length)];\n const r = await renderSvg({\n structure,\n width: o.width,\n height: o.height,\n });\n const file = resolve(\n o.dir,\n `${structure}-${r.seed}.${o.png ? 'png' : 'svg'}`,\n );\n await writeOut(file, o.png ? await renderPng(r.svg) : Buffer.from(r.svg));\n out(`${file}`);\n }\n },\n );\n\n// ------------------------------------------------------------------- mcp\nprogram\n .command('mcp')\n .description(\n 'Run the local MCP server over stdio (same as `npx @creatica/mcp`)',\n )\n .action(async () => {\n await createMcpServer().connect(new StdioServerTransport());\n });\n\ninterface GenOptions {\n seed?: string;\n width?: number;\n height?: number;\n palette?: string;\n colors?: string;\n primary?: string;\n bg?: string;\n gradient?: boolean;\n set: string[];\n png?: boolean;\n pngWidth?: number;\n out: string;\n hosted?: boolean;\n json?: boolean;\n}\n\nfunction int(v: string): number {\n const n = Number(v);\n if (!Number.isInteger(n) || n <= 0)\n throw new InvalidArgumentError('expected a positive integer');\n return n;\n}\nfunction collect(v: string, prev: string[]): string[] {\n return [...prev, v];\n}\nasync function writeOut(path: string, bytes: Buffer): Promise<void> {\n const p = resolve(path);\n await mkdir(dirname(p), { recursive: true });\n await writeFile(p, bytes);\n}\nfunction out(s: string): void {\n process.stdout.write(s + '\\n');\n}\nfunction die(msg: string): never {\n process.stderr.write(`error: ${msg}\\n`);\n process.exit(1);\n}\n\nawait program.parseAsync(process.argv);\n"],"mappings":";;;AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B;AACrC,SAAS,SAAS,4BAA4B;AAC9C,SAAS,OAAO,iBAAiB;AACjC,SAAS,SAAS,eAAe;AAGjC,QAAQ,OAAO,GAAG,SAAS,CAAC,MAA6B;AACvD,MAAI,EAAE,SAAS,QAAS,SAAQ,KAAK,CAAC;AACtC,QAAM;AACR,CAAC;AAED,IAAM,UAAU,IAAI,QAAQ;AAC5B,QACG,KAAK,UAAU,EACf;AAAA,EACC;AACF,EACC,QAAQ,cAAc,EACtB;AAAA,EACC;AAAA,EACA;AAAA,EACA,QAAQ,IAAI,oBAAoB;AAClC,EACC;AAAA,EACC;AAAA,EACA;AAAA,EACA,QAAQ,IAAI;AACd;AAGF,QACG,QAAQ,MAAM,EACd,YAAY,4BAA4B,EACxC,OAAO,sBAAsB,2BAA2B,EACxD,OAAO,qBAAqB,qBAAqB,EACjD,OAAO,mBAAmB,eAAe,EACzC,OAAO,UAAU,yBAAyB,EAC1C;AAAA,EACC,CAAC,MAAuE;AACtE,QAAI,QAAQ,QAAQ;AACpB,QAAI,EAAE;AACJ,cAAQ,MAAM;AAAA,QAAO,CAAC,MACpB,EAAE,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,KAAM,YAAY,CAAC,CAAC;AAAA,MACtD;AACF,QAAI,EAAE;AACJ,cAAQ,MAAM;AAAA,QAAO,CAAC,MACpB,EAAE,KAAK,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,IAAK,YAAY,CAAC,CAAC;AAAA,MACnE;AACF,QAAI,EAAE,OAAO;AACX,YAAM,SAAS,QAAQ,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACjE,cAAQ,MACL,OAAO,CAAC,MAAM,OAAO,SAAS,EAAE,IAAI,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,IAAI,IAAI,OAAO,QAAQ,EAAE,IAAI,CAAC;AAAA,IACnE;AACA,QAAI,EAAE,KAAM,QAAO,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AACrD,eAAW,KAAK,MAAO,KAAI,GAAG,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE;AAC9D;AAAA,MACE;AAAA,EAAK,MAAM,MAAM;AAAA,IACnB;AAAA,EACF;AACF;AAGF,QACG,QAAQ,iBAAiB,EACzB,YAAY,0DAA0D,EACtE,OAAO,UAAU,gDAAgD,EACjE,OAAO,CAAC,MAAc,MAA0B;AAC/C,MAAI;AACF,QAAI,EAAE,OAAO,KAAK,UAAU,SAAS,IAAI,GAAG,MAAM,CAAC,IAAI,UAAU,IAAI,CAAC;AAAA,EACxE,SAAS,GAAG;AACV,QAAK,EAAY,OAAO;AAAA,EAC1B;AACF,CAAC;AAGH,QACG,QAAQ,UAAU,EAClB,YAAY,wBAAwB,EACpC;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,QAAQ,EACf,OAAO,CAAC,MAAyC;AAChD,MAAI,QAAQ,aAAa;AACzB,MAAI,EAAE;AACJ,YAAQ,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,SAAS,EAAE,KAAM,YAAY,CAAC,CAAC;AACpE,MAAI,EAAE,KAAM,QAAO,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AACrD,aAAW,KAAK;AACd;AAAA,MACE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;AAAA,IAC1E;AACJ,CAAC;AAGH,QACG,QAAQ,oBAAoB,EAC5B;AAAA,EACC;AACF,EACC,OAAO,mBAAmB,YAAY,GAAG,EACzC,OAAO,QAAQ,EACf,OAAO,CAAC,OAAiB,MAAyC;AACjE,QAAM,IAAI,QAAQ,MAAM,KAAK,GAAG,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,EAAE,CAAC;AAC7D,MAAI,EAAE,KAAM,QAAO,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AACjD,MAAI,CAAC,EAAE,OAAQ,QAAO,IAAI,kCAAkC;AAC5D,aAAW,KAAK;AACd;AAAA,MACE,GAAG,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO;AAAA,EAAK,GAAG,OAAO,EAAE,CAAC,aAAa,EAAE,QAAQ,KAAK,IAAI,CAAC,GAAG,EAAE,SAAS,SAAS,eAAe,EAAE,SAAS,KAAK,IAAI,CAAC,KAAK,EAAE;AAAA,IACxJ;AACJ,CAAC;AAGH,QACG,QAAQ,iBAAiB,EACzB,MAAM,UAAU,EAChB;AAAA,EACC;AACF,EACC,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,oBAAoB,eAAe,GAAG,EAC7C,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,sBAAsB,+CAA+C,EAC5E,OAAO,iCAA4B,mCAAmC,EACtE,OAAO,mBAAmB,gBAAgB,EAC1C,OAAO,cAAc,mBAAmB,EACxC,OAAO,cAAc,gBAAgB,EACrC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA,CAAC;AACH,EACC,OAAO,SAAS,2BAA2B,EAC3C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,oBAAoB,gCAAgC,GAAG,EAC9D,OAAO,YAAY,uDAAuD,EAC1E;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,OAAO,WAAmB,GAAe,QAAiB;AAChE,QAAM,UAAU,IAAI,gBAAgB;AAIpC,QAAM,QAAiC;AAAA,IACrC;AAAA,IACA,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,EACZ;AACA,QAAM,UAAmC,CAAC;AAC1C,MAAI,EAAE,QAAS,SAAQ,UAAU,EAAE;AACnC,MAAI,EAAE,OAAQ,SAAQ,SAAS,EAAE;AACjC,MAAI,EAAE,QAAS,SAAQ,eAAe,EAAE;AACxC,MAAI,EAAE,GAAI,SAAQ,kBAAkB,EAAE;AACtC,MAAI,EAAE,SAAU,SAAQ,gBAAgB;AACxC,QAAM,QAAiC,CAAC;AACxC,aAAW,MAAM,EAAE,KAAK;AACtB,UAAM,IAAI,GAAG,QAAQ,GAAG;AACxB,QAAI,IAAI,EAAG,KAAI,iCAAiC,EAAE,GAAG;AACrD,UAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC;AAAA,EACxC;AACA,MAAI,OAAO,KAAK,OAAO,EAAE,OAAQ,OAAM,UAAU;AACjD,MAAI,OAAO,KAAK,KAAK,EAAE,OAAQ,OAAM,QAAQ;AAE7C,MAAI;AACJ,MAAI;AACF,iBAAa,iBAAiB,KAAK;AAAA,EACrC,SAAS,GAAG;AACV,QAAK,EAAY,OAAO;AAAA,EAC1B;AACA,aAAW,KAAK,WAAY;AAC1B,YAAQ,OAAO,MAAM,YAAY,CAAC;AAAA,CAAI;AAExC,QAAM,UAAU,EAAE,OAAO,EAAE,IAAI,YAAY,EAAE,SAAS,MAAM;AAC5D,QAAM,SAAS,UAAU,QAAQ;AAEjC,MAAI,EAAE,QAAQ;AACZ,UAAM,MAAM,MAAM,MAAM,IAAI,IAAI,gBAAgB,QAAQ,MAAM,GAAG;AAAA,MAC/D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAI,QAAQ,SACR,EAAE,eAAe,UAAU,QAAQ,MAAM,GAAG,IAC5C,CAAC;AAAA,MACP;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,GAAG,WAAY;AAAA,QACf,QAAQ,EAAE,QAAQ,OAAO,CAAC,EAAE,OAAO,SAAS;AAAA,QAC5C,UAAU,EAAE;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AACD,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAI,CAAC,IAAI;AACP;AAAA,QACE,GAAG,IAAI,MAAM,IAAI,KAAK,KAAK,KAAK,KAAK,OAAO,GAAG,KAAK,cAAc;AAAA,EAAK,KAAK,WAAW,KAAK,EAAE;AAAA,MAChG;AACF,QAAI,EAAE,QAAQ,KAAK;AACjB,YAAM,MAAM,MAAM;AAAA,QAChB,OAAO,WAAW,QAAQ,KAAK,SAAS,KAAK,MAAM;AAAA,QACnD;AAAA,UACE,SAAS,QAAQ,SACb,EAAE,eAAe,UAAU,QAAQ,MAAM,GAAG,IAC5C,CAAC;AAAA,QACP;AAAA,MACF;AACA,YAAM,SAAS,EAAE,KAAK,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC,CAAC;AAC1D,cAAQ,OAAO,MAAM,SAAS,EAAE,GAAG;AAAA,CAAI;AAAA,IACzC;AACA,QAAI,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAC3B,YAAM,EAAE,KAAK,IAAI,KAAK,IAAI,GAAGA,MAAK,IAAI;AACtC,aAAO,IAAI,KAAK,UAAUA,OAAM,MAAM,CAAC,CAAC;AAAA,IAC1C;AACA,QAAI,WAAW;AACb,aAAO,QAAQ,OAAO,MAAM,OAAO,KAAK,OAAO,KAAK,GAAG,GAAG,QAAQ,CAAC;AACrE,WAAO,QAAQ,OAAO,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,EAC9C;AAEA,QAAM,IAAI,MAAM,UAAU,WAAY,OAAO;AAC7C,QAAM,QACJ,WAAW,QACP,MAAM,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,IACvD,OAAO,KAAK,EAAE,KAAK,MAAM;AAC/B,QAAM,OAAO;AAAA,IACX,WAAW,EAAE;AAAA,IACb,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV;AAAA,IACA,OAAO,MAAM;AAAA,IACb,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE,GAAG,WAAY,SAAS,MAAM,EAAE,KAAK;AAAA,EAClD;AACA,MAAI,EAAE,QAAQ,KAAK;AACjB,QAAI,EAAE,KAAM,QAAO,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACpD,YAAQ,OAAO,MAAM,KAAK;AAC1B,YAAQ,OAAO;AAAA,MACb,QAAQ,EAAE,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,IAAI,MAAM,MAAM;AAAA;AAAA,IACvD;AACA;AAAA,EACF;AACA,QAAM,SAAS,EAAE,KAAK,KAAK;AAC3B,UAAQ,OAAO;AAAA,IACb,SAAS,EAAE,GAAG,UAAU,EAAE,IAAI,KAAK,MAAM,MAAM;AAAA;AAAA,EACjD;AACA,MAAI,EAAE,KAAM,KAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC/C,CAAC;AAGH,QACG,QAAQ,QAAQ,EAChB;AAAA,EACC;AACF,EACC,OAAO,mBAAmB,YAAY,GAAG,EACzC,OAAO,mBAAmB,oBAAoB,iBAAiB,EAC/D,OAAO,SAAS,oBAAoB,EACpC,OAAO,oBAAoB,SAAS,GAAG,EACvC,OAAO,qBAAqB,UAAU,GAAG,EACzC;AAAA,EACC,OAAO,MAMD;AACJ,UAAM,MAAM,eAAe,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAC9C,aAAS,IAAI,GAAG,IAAI,OAAO,EAAE,KAAK,GAAG,KAAK;AACxC,YAAM,YAAY,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,MAAM,CAAC;AAC5D,YAAM,IAAI,MAAM,UAAU;AAAA,QACxB;AAAA,QACA,OAAO,EAAE;AAAA,QACT,QAAQ,EAAE;AAAA,MACZ,CAAC;AACD,YAAM,OAAO;AAAA,QACX,EAAE;AAAA,QACF,GAAG,SAAS,IAAI,EAAE,IAAI,IAAI,EAAE,MAAM,QAAQ,KAAK;AAAA,MACjD;AACA,YAAM,SAAS,MAAM,EAAE,MAAM,MAAM,UAAU,EAAE,GAAG,IAAI,OAAO,KAAK,EAAE,GAAG,CAAC;AACxE,UAAI,GAAG,IAAI,EAAE;AAAA,IACf;AAAA,EACF;AACF;AAGF,QACG,QAAQ,KAAK,EACb;AAAA,EACC;AACF,EACC,OAAO,YAAY;AAClB,QAAM,gBAAgB,EAAE,QAAQ,IAAI,qBAAqB,CAAC;AAC5D,CAAC;AAmBH,SAAS,IAAI,GAAmB;AAC9B,QAAM,IAAI,OAAO,CAAC;AAClB,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,KAAK;AAC/B,UAAM,IAAI,qBAAqB,6BAA6B;AAC9D,SAAO;AACT;AACA,SAAS,QAAQ,GAAW,MAA0B;AACpD,SAAO,CAAC,GAAG,MAAM,CAAC;AACpB;AACA,eAAe,SAAS,MAAc,OAA8B;AAClE,QAAM,IAAI,QAAQ,IAAI;AACtB,QAAM,MAAM,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,UAAU,GAAG,KAAK;AAC1B;AACA,SAAS,IAAI,GAAiB;AAC5B,UAAQ,OAAO,MAAM,IAAI,IAAI;AAC/B;AACA,SAAS,IAAI,KAAoB;AAC/B,UAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,CAAI;AACtC,UAAQ,KAAK,CAAC;AAChB;AAEA,MAAM,QAAQ,WAAW,QAAQ,IAAI;","names":["meta"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@creatica/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generate procedural SVG/PNG backgrounds from the terminal. Local by default; --hosted uses the Creatica API.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"creatica": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"start": "node dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@creatica/engine": "0.1.0",
|
|
21
|
+
"@creatica/mcp": "0.1.0",
|
|
22
|
+
"@creatica/schema": "0.1.0",
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
24
|
+
"commander": "^15.0.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/anup-a/web-backgrounds",
|
|
32
|
+
"directory": "packages/cli"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://creatica.app",
|
|
35
|
+
"keywords": [
|
|
36
|
+
"svg",
|
|
37
|
+
"background",
|
|
38
|
+
"generator",
|
|
39
|
+
"mcp",
|
|
40
|
+
"model-context-protocol",
|
|
41
|
+
"ai",
|
|
42
|
+
"design",
|
|
43
|
+
"procedural"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
}
|
|
48
|
+
}
|