pixelmuse 0.2.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 +88 -0
- package/README.md +274 -0
- package/dist/chunk-743CKPHW.js +60 -0
- package/dist/chunk-7ARYEFAH.js +52 -0
- package/dist/chunk-MZZY4JXW.js +63 -0
- package/dist/chunk-ZVJQFWUI.js +272 -0
- package/dist/cli.js +531 -0
- package/dist/config-RMVFR3GN.js +13 -0
- package/dist/image-2H3GUQI6.js +16 -0
- package/dist/mcp/server.js +133 -0
- package/dist/tui.js +1274 -0
- package/package.json +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: StarMorph LLC
|
|
6
|
+
Licensed Work: Pixelmuse CLI 0.2.0
|
|
7
|
+
The Licensed Work is (c) 2025 StarMorph LLC.
|
|
8
|
+
Additional Use Grant: You may make production use of the Licensed Work,
|
|
9
|
+
provided that you do not use the Licensed Work to
|
|
10
|
+
offer a commercial image generation service that
|
|
11
|
+
competes with or substitutes for the Licensor's
|
|
12
|
+
image generation products and services.
|
|
13
|
+
Change Date: 2029-03-01
|
|
14
|
+
Change License: GNU General Public License v2.0 or later
|
|
15
|
+
|
|
16
|
+
Terms
|
|
17
|
+
|
|
18
|
+
The Licensor hereby grants you the right to copy, modify, create
|
|
19
|
+
derivative works, redistribute, and make non-production use of the
|
|
20
|
+
Licensed Work. The Licensor may make an Additional Use Grant, above,
|
|
21
|
+
permitting limited production use.
|
|
22
|
+
|
|
23
|
+
Effective on the Change Date, or the fourth anniversary of the first
|
|
24
|
+
publicly available distribution of a specific version of the Licensed
|
|
25
|
+
Work under this License, whichever comes first, the Licensor hereby
|
|
26
|
+
grants you rights under the terms of the Change License, and the rights
|
|
27
|
+
granted in the paragraph above terminate.
|
|
28
|
+
|
|
29
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
30
|
+
currently in effect as described in this License, you must purchase a
|
|
31
|
+
commercial license from the Licensor, its affiliated entities, or
|
|
32
|
+
authorized resellers, or you must refrain from using the Licensed Work.
|
|
33
|
+
|
|
34
|
+
All copies of the original and modified Licensed Work, and derivative
|
|
35
|
+
works of the Licensed Work, are subject to this License. This License
|
|
36
|
+
applies separately for each version of the Licensed Work and the Change
|
|
37
|
+
Date may vary for each version of the Licensed Work released by
|
|
38
|
+
Licensor.
|
|
39
|
+
|
|
40
|
+
You must conspicuously display this License on each original or modified
|
|
41
|
+
copy of the Licensed Work. If you receive the Licensed Work in original
|
|
42
|
+
or modified form from a third party, the terms and conditions set forth
|
|
43
|
+
in this License apply to your use of that work.
|
|
44
|
+
|
|
45
|
+
Any use of the Licensed Work in violation of this License will
|
|
46
|
+
automatically terminate your rights under this License for the current
|
|
47
|
+
and all other versions of the Licensed Work.
|
|
48
|
+
|
|
49
|
+
This License does not grant you any right in any trademark or logo of
|
|
50
|
+
Licensor or its affiliates (provided that you may use a trademark or
|
|
51
|
+
logo of Licensor as expressly required by this License).
|
|
52
|
+
|
|
53
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS
|
|
54
|
+
PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES
|
|
55
|
+
AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION)
|
|
56
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
57
|
+
NON-INFRINGEMENT, AND TITLE.
|
|
58
|
+
|
|
59
|
+
MariaDB hereby grants you permission to use this License's text to
|
|
60
|
+
license your works, and to refer to it using the trademark "Business
|
|
61
|
+
Source License", as long as you comply with the Covenants of Licensor
|
|
62
|
+
below.
|
|
63
|
+
|
|
64
|
+
Covenants of Licensor
|
|
65
|
+
|
|
66
|
+
In consideration of the right to use this License's text and the
|
|
67
|
+
"Business Source License" name and trademark, Licensor covenants to
|
|
68
|
+
MariaDB, and to all other recipients of the licensed work to be provided
|
|
69
|
+
by Licensor:
|
|
70
|
+
|
|
71
|
+
1. To specify as the Change License the GPL Version 2.0 or any later
|
|
72
|
+
version, or a license that is compatible with GPL Version 2.0 or a
|
|
73
|
+
later version, where "compatible" means that software provided under
|
|
74
|
+
the Change License can be included in a program with software
|
|
75
|
+
provided under GPL Version 2.0 or a later version. Licensor may
|
|
76
|
+
specify additional Change Licenses without limitation.
|
|
77
|
+
|
|
78
|
+
2. To either: (a) specify an additional grant of rights to use that does
|
|
79
|
+
not impose any additional restriction on the right granted in this
|
|
80
|
+
License, as the Additional Use Grant; or (b) insert the text "None".
|
|
81
|
+
|
|
82
|
+
3. To specify a Change Date.
|
|
83
|
+
|
|
84
|
+
4. Not to modify this License in any other way.
|
|
85
|
+
|
|
86
|
+
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights
|
|
87
|
+
Reserved. "Business Source License" is a trademark of MariaDB
|
|
88
|
+
Corporation Ab.
|
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./assets/banner.png" alt="pixelmuse" width="700" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>AI image generation from the terminal.</strong><br/>
|
|
7
|
+
CLI, interactive TUI, and MCP server — powered by the <a href="https://pixelmuse.studio">Pixelmuse</a> API.
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://www.npmjs.com/package/pixelmuse"><img src="https://img.shields.io/npm/v/pixelmuse.svg" alt="npm version" /></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/pixelmuse"><img src="https://img.shields.io/npm/dm/pixelmuse.svg" alt="npm downloads" /></a>
|
|
13
|
+
<a href="https://github.com/starmorph/pixelmuse-cli/actions/workflows/ci.yml"><img src="https://github.com/starmorph/pixelmuse-cli/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
14
|
+
<a href="https://github.com/starmorph/pixelmuse-cli/actions/workflows/security.yml"><img src="https://github.com/starmorph/pixelmuse-cli/actions/workflows/security.yml/badge.svg" alt="Security" /></a>
|
|
15
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/License-BSL%201.1-blue.svg" alt="License: BSL 1.1" /></a>
|
|
16
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg" alt="Node.js" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<!-- GIF: Hero — basic generation. Record: pixelmuse "a cyberpunk city at sunset, neon lights reflecting on wet streets" -->
|
|
22
|
+
<!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Pixelmuse CLI demo" width="600" /></p> -->
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Why Pixelmuse?
|
|
27
|
+
|
|
28
|
+
- **One command, any model.** Flux, Imagen 3, Recraft V4, and more — switch models with a flag, no separate accounts or API keys.
|
|
29
|
+
- **Built for developer workflows.** Pipe from stdin, JSON output for scripting, watch mode for prompt iteration, MCP server for AI agents.
|
|
30
|
+
- **Predictable credit pricing.** 1-3 credits per generation, no surprises. Free credits on signup.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g pixelmuse
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then authenticate:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pixelmuse login
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Sign up at [pixelmuse.studio/sign-up](https://www.pixelmuse.studio/sign-up) — new accounts include free credits.
|
|
45
|
+
|
|
46
|
+
> Requires Node.js 20+. For terminal image previews, install [chafa](https://hpjansson.org/chafa/) (`brew install chafa` / `sudo apt install chafa`).
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Generate an image
|
|
52
|
+
pixelmuse "a cat floating through space"
|
|
53
|
+
|
|
54
|
+
# Choose model and aspect ratio
|
|
55
|
+
pixelmuse "neon cityscape at night" -m recraft-v4 -a 16:9
|
|
56
|
+
|
|
57
|
+
# Apply a style
|
|
58
|
+
pixelmuse "mountain landscape" -s anime -a 21:9
|
|
59
|
+
|
|
60
|
+
# Save to specific path
|
|
61
|
+
pixelmuse "app icon, minimal" -o icon.png
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
<!-- GIF: Model selection. Record: pixelmuse "a cat astronaut" -m recraft-v4 --open -->
|
|
65
|
+
<!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Model selection" width="600" /></p> -->
|
|
66
|
+
|
|
67
|
+
## Prompt Templates
|
|
68
|
+
|
|
69
|
+
Save reusable prompts as YAML files with variables and default settings.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Scaffold a new template
|
|
73
|
+
pixelmuse template init product-shot
|
|
74
|
+
|
|
75
|
+
# Generate with a template
|
|
76
|
+
pixelmuse template use blog-thumbnail --var subject="React hooks guide"
|
|
77
|
+
|
|
78
|
+
# List all templates
|
|
79
|
+
pixelmuse template list
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
<!-- GIF: Template system. Record: pixelmuse template use blog-thumbnail --var subject="TypeScript generics" -->
|
|
83
|
+
<!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Template system" width="600" /></p> -->
|
|
84
|
+
|
|
85
|
+
Templates are stored at `~/.config/pixelmuse-cli/prompts/`:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
# blog-thumbnail.yaml
|
|
89
|
+
name: Blog Thumbnail
|
|
90
|
+
description: Dark-themed blog post thumbnail
|
|
91
|
+
prompt: >
|
|
92
|
+
A cinematic {{subject}} on a dark gradient background,
|
|
93
|
+
dramatic lighting, 8K resolution
|
|
94
|
+
defaults:
|
|
95
|
+
model: nano-banana-2
|
|
96
|
+
aspect_ratio: "16:9"
|
|
97
|
+
variables:
|
|
98
|
+
subject: "code editor with syntax highlighting"
|
|
99
|
+
tags: [blog, thumbnail, dark]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Interactive TUI
|
|
103
|
+
|
|
104
|
+
A full terminal UI for visual browsing, generation wizards, gallery, and account management:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pixelmuse ui
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
<!-- GIF: TUI. Record: pixelmuse ui → navigate Generate → pick model → type prompt → generate -->
|
|
111
|
+
<!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Interactive TUI" width="600" /></p> -->
|
|
112
|
+
|
|
113
|
+
## Scripting & Pipes
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Pipe prompt from stdin
|
|
117
|
+
echo "hero banner for SaaS landing page" | pixelmuse -o hero.png
|
|
118
|
+
cat prompt.txt | pixelmuse -m recraft-v4
|
|
119
|
+
|
|
120
|
+
# JSON output for scripting
|
|
121
|
+
pixelmuse --json "logo concept" | jq .output_path
|
|
122
|
+
|
|
123
|
+
# Watch mode — regenerates when prompt file changes
|
|
124
|
+
pixelmuse --watch prompt.txt -o output.png
|
|
125
|
+
|
|
126
|
+
# Skip preview, copy to clipboard
|
|
127
|
+
pixelmuse "avatar" --no-preview --clipboard
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
<!-- GIF: Pipe/scripting. Record: echo "minimalist logo for a coffee shop" | pixelmuse --json -m flux-schnell -->
|
|
131
|
+
<!-- Replace this comment with: <p align="center"><img src="YOUR_GIF_URL" alt="Scripting and pipes" width="600" /></p> -->
|
|
132
|
+
|
|
133
|
+
## MCP Server (Claude Code, Cursor, Windsurf)
|
|
134
|
+
|
|
135
|
+
The MCP server lets AI agents generate images, list models, and check your balance — no manual CLI steps needed.
|
|
136
|
+
|
|
137
|
+
**Claude Code** — add to `~/.claude/.mcp.json`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"pixelmuse": {
|
|
143
|
+
"command": "npx",
|
|
144
|
+
"args": ["-y", "pixelmuse-mcp"],
|
|
145
|
+
"env": {
|
|
146
|
+
"PIXELMUSE_API_KEY": "pm_live_your_key_here"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Cursor / Windsurf** — add to your MCP settings:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"pixelmuse": {
|
|
158
|
+
"command": "npx",
|
|
159
|
+
"args": ["-y", "pixelmuse-mcp"],
|
|
160
|
+
"env": {
|
|
161
|
+
"PIXELMUSE_API_KEY": "pm_live_your_key_here"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The agent gets three tools:
|
|
168
|
+
|
|
169
|
+
| Tool | What it does |
|
|
170
|
+
|------|-------------|
|
|
171
|
+
| `generate_image` | Generate an image from a prompt with model, aspect ratio, style, and output path. |
|
|
172
|
+
| `list_models` | List all available models with credit costs. |
|
|
173
|
+
| `check_balance` | Check your credit balance and plan info. |
|
|
174
|
+
|
|
175
|
+
**Example prompts for your AI agent:**
|
|
176
|
+
|
|
177
|
+
- "Generate a hero image for my landing page, 16:9, save to `./public/hero.png`"
|
|
178
|
+
- "Create a blog thumbnail about React hooks using the anime style"
|
|
179
|
+
- "What Pixelmuse models are available?"
|
|
180
|
+
|
|
181
|
+
## Which Interface?
|
|
182
|
+
|
|
183
|
+
Pixelmuse ships four interfaces. Pick the one that fits your workflow — they all use the same API and credentials.
|
|
184
|
+
|
|
185
|
+
| | **CLI** | **TUI** | **MCP Server** | **Claude Skill** |
|
|
186
|
+
|---|---|---|---|---|
|
|
187
|
+
| **What it is** | Command-line tool | Interactive terminal UI | AI agent tool server | Claude Code prompt template |
|
|
188
|
+
| **Launch** | `pixelmuse "prompt"` | `pixelmuse ui` | Auto-starts with Claude/Cursor | Auto-triggers on keywords |
|
|
189
|
+
| **Best for** | Scripting, automation, CI/CD | Visual browsing, exploring models | Letting AI agents generate images | Generating images mid-conversation |
|
|
190
|
+
| **Input** | Flags, stdin, pipe, watch mode | Guided wizard with menus | AI decides params from natural language | Natural language to Claude |
|
|
191
|
+
|
|
192
|
+
**Rule of thumb:**
|
|
193
|
+
- **You type the prompt** → CLI or TUI
|
|
194
|
+
- **AI types the prompt** → MCP Server or Claude Skill
|
|
195
|
+
- **Quick one-off** → CLI
|
|
196
|
+
- **Browsing/exploring** → TUI
|
|
197
|
+
|
|
198
|
+
## Models
|
|
199
|
+
|
|
200
|
+
| Model | Credits | Best For |
|
|
201
|
+
|-------|---------|----------|
|
|
202
|
+
| **Nano Banana 2** (default) | 1 | Speed, text rendering, world knowledge |
|
|
203
|
+
| Nano Banana Pro | 4 | Text rendering, real-time info, multi-image editing |
|
|
204
|
+
| Flux Schnell | 1 | Quick mockups, ideation |
|
|
205
|
+
| Google Imagen 3 | 1 | Realistic photos, complex compositions |
|
|
206
|
+
| Recraft V4 | 1 | Typography, design, composition |
|
|
207
|
+
| Recraft V4 Pro | 7 | High-res design, art direction |
|
|
208
|
+
|
|
209
|
+
## CLI Reference
|
|
210
|
+
|
|
211
|
+
### Commands
|
|
212
|
+
|
|
213
|
+
| Command | Description |
|
|
214
|
+
|---------|-------------|
|
|
215
|
+
| `pixelmuse "prompt"` | Generate an image (default command) |
|
|
216
|
+
| `pixelmuse models` | List available models with costs |
|
|
217
|
+
| `pixelmuse account` | Account balance and usage stats |
|
|
218
|
+
| `pixelmuse history` | Recent generations table |
|
|
219
|
+
| `pixelmuse open <id>` | Open a generation in system viewer |
|
|
220
|
+
| `pixelmuse login` | Authenticate with API key |
|
|
221
|
+
| `pixelmuse logout` | Remove stored credentials |
|
|
222
|
+
| `pixelmuse template <cmd>` | Manage prompt templates |
|
|
223
|
+
| `pixelmuse ui` | Launch interactive TUI |
|
|
224
|
+
|
|
225
|
+
### Flags
|
|
226
|
+
|
|
227
|
+
| Flag | Description |
|
|
228
|
+
|------|-------------|
|
|
229
|
+
| `-m, --model` | Model ID (default: `nano-banana-2`) |
|
|
230
|
+
| `-a, --aspect-ratio` | Aspect ratio (default: `1:1`) |
|
|
231
|
+
| `-s, --style` | `realistic`, `anime`, `artistic`, `none` |
|
|
232
|
+
| `-o, --output` | Output file path |
|
|
233
|
+
| `--json` | Machine-readable JSON output |
|
|
234
|
+
| `--no-preview` | Skip terminal image preview |
|
|
235
|
+
| `--open` | Open result in system viewer |
|
|
236
|
+
| `--clipboard` | Copy image to clipboard |
|
|
237
|
+
| `--watch <file>` | Watch prompt file, regenerate on save |
|
|
238
|
+
| `--no-save` | Don't save image to disk |
|
|
239
|
+
|
|
240
|
+
## Configuration
|
|
241
|
+
|
|
242
|
+
Settings at `~/.config/pixelmuse-cli/config.yaml`:
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
defaultModel: nano-banana-2
|
|
246
|
+
defaultAspectRatio: "1:1"
|
|
247
|
+
defaultStyle: none
|
|
248
|
+
autoPreview: true
|
|
249
|
+
autoSave: true
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
| Path | Contents |
|
|
253
|
+
|------|----------|
|
|
254
|
+
| `~/.config/pixelmuse-cli/config.yaml` | User settings |
|
|
255
|
+
| `~/.config/pixelmuse-cli/auth.json` | API key (fallback if keychain unavailable) |
|
|
256
|
+
| `~/.config/pixelmuse-cli/prompts/` | Prompt template YAML files |
|
|
257
|
+
| `~/.local/share/pixelmuse-cli/generations/` | Auto-saved generation images |
|
|
258
|
+
|
|
259
|
+
## Links
|
|
260
|
+
|
|
261
|
+
- [Pixelmuse](https://www.pixelmuse.studio) — Platform home
|
|
262
|
+
- [Sign up](https://www.pixelmuse.studio/sign-up) — Create an account (free credits included)
|
|
263
|
+
- [API keys](https://www.pixelmuse.studio/settings/api-keys) — Manage API keys
|
|
264
|
+
- [API docs](https://www.pixelmuse.studio/developers) — Full API reference (Scalar)
|
|
265
|
+
- [Get credits](https://www.pixelmuse.studio/get-credits) — Buy credit packs
|
|
266
|
+
<!-- - [YouTube demo](YOUR_YOUTUBE_URL) — Full walkthrough video -->
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
Business Source License 1.1 (BSL 1.1). See [LICENSE](./LICENSE) for details.
|
|
271
|
+
|
|
272
|
+
Free for any use except offering a competing image generation API or platform. Converts to GPL-2.0 on 2029-03-01.
|
|
273
|
+
|
|
274
|
+
Copyright 2025 StarMorph LLC.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
pollGeneration,
|
|
4
|
+
slugify
|
|
5
|
+
} from "./chunk-ZVJQFWUI.js";
|
|
6
|
+
import {
|
|
7
|
+
autoSave,
|
|
8
|
+
imageToBuffer,
|
|
9
|
+
saveImage
|
|
10
|
+
} from "./chunk-MZZY4JXW.js";
|
|
11
|
+
|
|
12
|
+
// src/core/generate.ts
|
|
13
|
+
import { existsSync } from "fs";
|
|
14
|
+
import { join, resolve } from "path";
|
|
15
|
+
function resolveOutputPath(prompt, output) {
|
|
16
|
+
if (output) return resolve(output);
|
|
17
|
+
const base = slugify(prompt).slice(0, 60) || "generation";
|
|
18
|
+
let filename = `${base}.png`;
|
|
19
|
+
let counter = 2;
|
|
20
|
+
while (existsSync(join(process.cwd(), filename))) {
|
|
21
|
+
filename = `${base}-${counter}.png`;
|
|
22
|
+
counter++;
|
|
23
|
+
}
|
|
24
|
+
return join(process.cwd(), filename);
|
|
25
|
+
}
|
|
26
|
+
async function generateImage(client, options) {
|
|
27
|
+
const {
|
|
28
|
+
prompt,
|
|
29
|
+
model = "nano-banana-2",
|
|
30
|
+
aspectRatio = "1:1",
|
|
31
|
+
style,
|
|
32
|
+
output,
|
|
33
|
+
noSave = false,
|
|
34
|
+
onProgress
|
|
35
|
+
} = options;
|
|
36
|
+
const start = Date.now();
|
|
37
|
+
let gen = await client.generate({
|
|
38
|
+
prompt,
|
|
39
|
+
model,
|
|
40
|
+
aspect_ratio: aspectRatio,
|
|
41
|
+
style: style === "none" ? void 0 : style
|
|
42
|
+
});
|
|
43
|
+
if (gen.status === "processing" || gen.status === "pending") {
|
|
44
|
+
gen = await pollGeneration(client, gen.id, { onProgress });
|
|
45
|
+
}
|
|
46
|
+
const elapsed = (Date.now() - start) / 1e3;
|
|
47
|
+
let imagePath = null;
|
|
48
|
+
if (gen.output?.[0] && !noSave) {
|
|
49
|
+
const buf = await imageToBuffer(gen.output[0]);
|
|
50
|
+
const outPath = resolveOutputPath(prompt, output);
|
|
51
|
+
saveImage(buf, outPath);
|
|
52
|
+
imagePath = outPath;
|
|
53
|
+
autoSave(gen.id, buf);
|
|
54
|
+
}
|
|
55
|
+
return { generation: gen, imagePath, elapsed };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
generateImage
|
|
60
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/config.ts
|
|
4
|
+
import envPaths from "env-paths";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import YAML from "yaml";
|
|
8
|
+
var paths = envPaths("pixelmuse-cli");
|
|
9
|
+
var PATHS = {
|
|
10
|
+
config: paths.config,
|
|
11
|
+
data: paths.data,
|
|
12
|
+
auth: join(paths.config, "auth.json"),
|
|
13
|
+
settings: join(paths.config, "config.yaml"),
|
|
14
|
+
prompts: join(paths.config, "prompts"),
|
|
15
|
+
generations: join(paths.data, "generations")
|
|
16
|
+
};
|
|
17
|
+
var DEFAULT_SETTINGS = {
|
|
18
|
+
defaultModel: "nano-banana-2",
|
|
19
|
+
defaultAspectRatio: "1:1",
|
|
20
|
+
defaultStyle: "none",
|
|
21
|
+
autoPreview: true,
|
|
22
|
+
autoSave: true
|
|
23
|
+
};
|
|
24
|
+
function ensureDirs() {
|
|
25
|
+
for (const dir of [PATHS.config, PATHS.data, PATHS.prompts, PATHS.generations]) {
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function readSettings() {
|
|
30
|
+
ensureDirs();
|
|
31
|
+
if (!existsSync(PATHS.settings)) {
|
|
32
|
+
writeSettings(DEFAULT_SETTINGS);
|
|
33
|
+
return DEFAULT_SETTINGS;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const raw = readFileSync(PATHS.settings, "utf-8");
|
|
37
|
+
return { ...DEFAULT_SETTINGS, ...YAML.parse(raw) };
|
|
38
|
+
} catch {
|
|
39
|
+
return DEFAULT_SETTINGS;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function writeSettings(settings) {
|
|
43
|
+
ensureDirs();
|
|
44
|
+
writeFileSync(PATHS.settings, YAML.stringify(settings), "utf-8");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
PATHS,
|
|
49
|
+
ensureDirs,
|
|
50
|
+
readSettings,
|
|
51
|
+
writeSettings
|
|
52
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
PATHS
|
|
4
|
+
} from "./chunk-7ARYEFAH.js";
|
|
5
|
+
|
|
6
|
+
// src/core/image.ts
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
var _chafaAvailable = null;
|
|
11
|
+
function hasChafa() {
|
|
12
|
+
if (_chafaAvailable !== null) return _chafaAvailable;
|
|
13
|
+
try {
|
|
14
|
+
execSync("chafa --version", { stdio: "ignore" });
|
|
15
|
+
_chafaAvailable = true;
|
|
16
|
+
} catch {
|
|
17
|
+
_chafaAvailable = false;
|
|
18
|
+
}
|
|
19
|
+
return _chafaAvailable;
|
|
20
|
+
}
|
|
21
|
+
async function imageToBuffer(output) {
|
|
22
|
+
if (output.startsWith("data:")) {
|
|
23
|
+
const base64 = output.split(",", 2)[1];
|
|
24
|
+
if (!base64) throw new Error("Invalid data URI: missing base64 content");
|
|
25
|
+
return Buffer.from(base64, "base64");
|
|
26
|
+
}
|
|
27
|
+
const res = await fetch(output);
|
|
28
|
+
if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`);
|
|
29
|
+
return Buffer.from(await res.arrayBuffer());
|
|
30
|
+
}
|
|
31
|
+
function saveImage(buffer, path) {
|
|
32
|
+
const dir = dirname(path);
|
|
33
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
34
|
+
writeFileSync(path, buffer);
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
function autoSave(id, buffer) {
|
|
38
|
+
const dir = PATHS.generations;
|
|
39
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
40
|
+
const path = join(dir, `${id}.png`);
|
|
41
|
+
writeFileSync(path, buffer);
|
|
42
|
+
return path;
|
|
43
|
+
}
|
|
44
|
+
function renderImageDirect(path) {
|
|
45
|
+
if (!hasChafa()) return false;
|
|
46
|
+
try {
|
|
47
|
+
execSync(`chafa --animate off "${path}"`, {
|
|
48
|
+
stdio: "inherit",
|
|
49
|
+
maxBuffer: 10 * 1024 * 1024
|
|
50
|
+
});
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
hasChafa,
|
|
59
|
+
imageToBuffer,
|
|
60
|
+
saveImage,
|
|
61
|
+
autoSave,
|
|
62
|
+
renderImageDirect
|
|
63
|
+
};
|