@miketromba/ploof 0.1.1 → 0.1.3

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 CHANGED
@@ -1,9 +1,5 @@
1
1
  <p align="center">
2
- <strong>Ploof</strong>
3
- </p>
4
-
5
- <p align="center">
6
- AI asset generation from the command line.
2
+ <img src="assets/brand/ploof-banner.png" alt="Ploof - AI asset generation from the command line." width="100%" />
7
3
  </p>
8
4
 
9
5
  <p align="center">
@@ -62,7 +58,7 @@ ploof login openai --api-key <your-api-key>
62
58
  ploof image generate \
63
59
  --prompt "Studio product photo of a matte black water bottle" \
64
60
  --out assets/hero.png \
65
- --model gpt-image-1 \
61
+ --model gpt-image-2 \
66
62
  --size 1024x1024
67
63
 
68
64
  # Edit an image with context
@@ -118,7 +114,7 @@ ploof image generate \
118
114
  --profile default \
119
115
  --prompt "Editorial portrait, dramatic side light" \
120
116
  --out assets/portrait.png \
121
- --model gpt-image-1 \
117
+ --model gpt-image-2 \
122
118
  --size 1024x1024 \
123
119
  --quality high \
124
120
  --format png
@@ -165,7 +161,7 @@ tasks:
165
161
  provider: openai
166
162
  prompt: "Studio product photo"
167
163
  params:
168
- model: gpt-image-1
164
+ model: gpt-image-2
169
165
  size: 1024x1024
170
166
  quality: high
171
167
  output: assets/base.png
@@ -269,10 +265,33 @@ PLOOF_OPENAI_API_KEY=sk-... bun test tests/e2e
269
265
  Optional live-test overrides:
270
266
 
271
267
  ```bash
272
- PLOOF_OPENAI_LIVE_MODEL=gpt-image-1
268
+ PLOOF_OPENAI_LIVE_MODEL=gpt-image-2
273
269
  PLOOF_OPENAI_LIVE_SIZE=1024x1024
274
270
  ```
275
271
 
272
+ ## Publishing
273
+
274
+ Local release verification stops at packaging:
275
+
276
+ ```bash
277
+ bun run release
278
+ ```
279
+
280
+ Publishing should happen from GitHub Actions by pushing a `v*` tag. The npm
281
+ package must have a Trusted Publisher configured with:
282
+
283
+ | Field | Value |
284
+ | --- | --- |
285
+ | Provider | GitHub Actions |
286
+ | Organization or user | `miketromba` |
287
+ | Repository | `ploof` |
288
+ | Workflow filename | `publish.yml` |
289
+ | Environment | blank |
290
+ | Allowed action | `npm publish` |
291
+
292
+ Do not publish from a local terminal unless intentionally doing a manual
293
+ emergency release.
294
+
276
295
  ## License
277
296
 
278
297
  MIT
package/SPEC.md CHANGED
@@ -51,9 +51,21 @@ Conventions to preserve:
51
51
  - Published package runs on Node 18+ and does not require Bun.
52
52
  - Published files are constrained to runtime artifacts, README, LICENSE, spec, and skill files.
53
53
  - GitHub Actions CI runs typecheck, tests, and build.
54
- - GitHub Actions publish runs on `v*` tags and supports npm trusted publishing/OIDC.
54
+ - GitHub Actions publish runs on `v*` tags through npm trusted publishing/OIDC.
55
55
  - Human-readable output in TTY, compact output when piped, JSON/JSONL for agents and scripts.
56
56
 
57
+ NPM trusted publisher settings for `@miketromba/ploof`:
58
+
59
+ - Provider: GitHub Actions.
60
+ - Organization or user: `miketromba`.
61
+ - Repository: `ploof`.
62
+ - Workflow filename: `publish.yml`.
63
+ - Environment: blank.
64
+ - Allowed action: `npm publish`.
65
+
66
+ Local release verification must stop at `npm pack --dry-run`; do not run local
67
+ `npm publish` for normal releases.
68
+
57
69
  ## Core Goals
58
70
 
59
71
  1. Authenticate with multiple asset generation providers.
@@ -170,7 +182,7 @@ ploof image generate \
170
182
  --profile default \
171
183
  --prompt "Studio product photo" \
172
184
  --out assets/hero.png \
173
- --model gpt-image-1 \
185
+ --model gpt-image-2 \
174
186
  --size 1024x1024 \
175
187
  --quality high \
176
188
  --format png
@@ -214,7 +226,7 @@ tasks:
214
226
  provider: openai
215
227
  prompt: "Studio product photo"
216
228
  params:
217
- model: gpt-image-1
229
+ model: gpt-image-2
218
230
  size: 1024x1024
219
231
  quality: high
220
232
  output: assets/base.png
@@ -313,7 +325,7 @@ Asset-producing commands should write the asset to disk and print structured met
313
325
  "provider": "openai",
314
326
  "outputs": ["assets/hero.png"],
315
327
  "metadata": {
316
- "model": "gpt-image-1"
328
+ "model": "gpt-image-2"
317
329
  }
318
330
  }
319
331
  ```
Binary file
package/dist/ploof.js CHANGED
@@ -259,7 +259,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
259
259
  `,z)}while(J!==-1);return X+=$.slice(z),X}var{stdout:GV,stderr:NV}=zV,i3=Symbol("GENERATOR"),dU=Symbol("STYLER"),H0=Symbol("IS_EMPTY"),_V=["ansi","ansi","ansi256","ansi16m"],nU=Object.create(null),MT=($,U={})=>{if(U.level&&!(Number.isInteger(U.level)&&U.level>=0&&U.level<=3))throw Error("The `level` option should be an integer from 0 to 3");let w=GV?GV.level:0;$.level=U.level===void 0?w:U.level};var vT=($)=>{let U=(...w)=>w.join(" ");return MT(U,$),Object.setPrototypeOf(U,K0.prototype),U};function K0($){return vT($)}Object.setPrototypeOf(K0.prototype,Function.prototype);for(let[$,U]of Object.entries(t$))nU[$]={get(){let w=$J(this,m3(U.open,U.close,this[dU]),this[H0]);return Object.defineProperty(this,$,{value:w}),w}};nU.visible={get(){let $=$J(this,this[dU],!0);return Object.defineProperty(this,"visible",{value:$}),$}};var h3=($,U,w,...J)=>{if($==="rgb"){if(U==="ansi16m")return t$[w].ansi16m(...J);if(U==="ansi256")return t$[w].ansi256(t$.rgbToAnsi256(...J));return t$[w].ansi(t$.rgbToAnsi(...J))}if($==="hex")return h3("rgb",U,w,...t$.hexToRgb(...J));return t$[w][$](...J)},ST=["rgb","hex","ansi256"];for(let $ of ST){nU[$]={get(){let{level:w}=this;return function(...J){let z=m3(h3($,_V[w],"color",...J),t$.color.close,this[dU]);return $J(this,z,this[H0])}}};let U="bg"+$[0].toUpperCase()+$.slice(1);nU[U]={get(){let{level:w}=this;return function(...J){let z=m3(h3($,_V[w],"bgColor",...J),t$.bgColor.close,this[dU]);return $J(this,z,this[H0])}}}}var bT=Object.defineProperties(()=>{},{...nU,level:{enumerable:!0,get(){return this[i3].level},set($){this[i3].level=$}}}),m3=($,U,w)=>{let J,z;if(w===void 0)J=$,z=U;else J=w.openAll+$,z=U+w.closeAll;return{open:$,close:U,openAll:J,closeAll:z,parent:w}},$J=($,U,w)=>{let J=(...z)=>ET(J,z.length===1?""+z[0]:z.join(" "));return Object.setPrototypeOf(J,bT),J[i3]=$,J[dU]=U,J[H0]=w,J},ET=($,U)=>{if($.level<=0||!U)return $[H0]?"":U;let w=$[dU];if(w===void 0)return U;let{openAll:J,closeAll:z}=w;if(U.includes("\x1B"))while(w!==void 0)U=JV(U,w.close,w.open),w=w.parent;let X=U.indexOf(`
260
260
  `);if(X!==-1)U=XV(U,z,J,X);return J+U+z};Object.defineProperties(K0.prototype,nU);var AT=K0(),vr=K0({level:NV?NV.level:0});var UJ=AT;class y$ extends Error{code;constructor($,U=1){super($);this.code=U;this.name="CliError"}}function YV($,U=!1){let w=$ instanceof Error?$.message:String($);if(U)return`Error: ${w}`;return`${UJ.red("Error:")} ${w}`}function QV($){return`${$.join(`
261
261
  `).trim()}
262
- `}var RT=QV(["# Generate assets with the ploof CLI","","Ploof is an AI asset generation CLI. Use it when a task needs generated or edited images, batch asset creation, provider authentication, image context inputs, or reusable generation manifests.","","Package name: `@miketromba/ploof`. Command name: `ploof`. Supported today: OpenAI image generation and image editing.","","## Core workflow","","1. Run `ploof whoami openai`. If unauthenticated, ask the user to run `ploof login openai` or provide `PLOOF_OPENAI_API_KEY` in their shell.","2. For one asset, use `ploof image generate` or `ploof image edit` with an explicit `--out` path.","3. For multiple assets, dependencies, or parallel work, write a YAML manifest and run `ploof run <manifest.yaml>`.","4. Use `--output json` for commands another tool or agent must parse. Use `--output jsonl` for batch streams.","5. After a command completes, verify every path in `outputs` exists before telling the user generation succeeded.","","## Authentication","","```bash","ploof login openai --api-key <key>","ploof whoami openai","ploof profiles openai","```","","There is no `ploof auth` namespace. The authentication commands are top-level: `login`, `whoami`, `profiles`, and `logout`.","","Environment variables override stored credentials:","","```bash","PLOOF_OPENAI_API_KEY=...","OPENAI_API_KEY=...","```","","Use `--profile <name>` on `login`, `whoami`, `image generate`, `image edit`, and manifest tasks when the user has multiple OpenAI credentials. Never print or store secrets in generated project files.","","## Image generation","","```bash","ploof image generate \\"," --provider openai \\",' --prompt "Studio product photo" \\'," --out assets/hero.png \\"," --model gpt-image-1 \\"," --size 1024x1024 \\"," --quality high \\"," --format png \\"," --output json","```","","Important: `--format png` controls the generated asset format. `--output json` controls CLI output formatting.","","First-class OpenAI image flags:","","- `--model <model>`: for example `gpt-image-1`.","- `--size <size>`: provider size such as `1024x1024`.","- `--quality <quality>`: provider quality such as `low`, `medium`, or `high` when supported.","- `--format <format>` or `--output-format <format>`: provider image format, usually `png`, `jpeg`, or `webp` when supported.","- `--background <value>`, `--moderation <value>`, `--n <count>`, `--output-compression <number>`, `--partial-images <number>`, `--response-format <format>`, `--style <style>`, `--user <id>`, `--stream`.","- `--param key=value`: pass one provider-specific parameter. Nested keys work, such as `--param extra.foo=bar`.","- `--json '{...}'`: merge a provider-specific JSON object into the request. Explicit first-class flags override overlapping JSON keys.","","Use `--quality low` for cheap smoke tests. Use the user's requested quality and size for final assets.","","## Image editing and context images","","```bash","ploof image edit \\"," --provider openai \\"," --image input.png \\"," --image reference.png \\"," --mask mask.png \\",' --prompt "Replace the background" \\'," --out assets/edited.png \\"," --model gpt-image-1 \\"," --format png \\"," --output json","```","","Pass every source image the provider should see with repeated `--image`. Use `--mask` only when the provider/model supports masked edits. Edit also supports `--input-fidelity <value>`.","","Input assets can be local paths, `http://` or `https://` URLs, or `-` for stdin. Ploof infers common image MIME types from file extensions and preserves generated files locally.","","## Outputs and verification","","`--out <path>` can be a file path or directory. When generating multiple images with `--n`, a file path is expanded with `-1`, `-2`, etc. If `--out` is omitted, Ploof writes in the current directory using default names like `image.png` or `edited-image.png`.","","Default sidecar metadata is enabled. For each output file, Ploof writes `<output>.json` with the operation, prompt, params, outputs, provider metadata, and timestamp.","","Parseable result shape:","","```json","{",' "kind": "image.generate",',' "provider": "openai",',' "profile": "default",',' "outputs": ["assets/hero.png"],',' "metadata": { "model": "gpt-image-1" }',"}","```","","Useful global output flags:","","- `--output json|jsonl|compact|table`: choose CLI output format.","- `--fields outputs,metadata.usage.total_tokens`: select fields for parseable outputs.","- `--detail`, `--quiet`, `--no-color`, `--verbose`.","","## Batch manifests","","```yaml","version: 1","parallel: 4","tasks:"," - id: base"," kind: image.generate"," provider: openai"," profile: default",' prompt: "Studio product photo"'," output: assets/base.png"," params:"," model: gpt-image-1"," size: 1024x1024"," quality: high"," output_format: png",""," - id: edit"," kind: image.edit"," provider: openai"," needs: [base]"," inputs:"," images:"," - task: base"," - source: ./reference.png"," mask:"," source: ./mask.png",' prompt: "Add a premium background"'," output: assets/final.png"," params:"," model: gpt-image-1"," size: 1024x1024","```","","Run it with:","","```bash","ploof run assets.yaml --parallel 4 --output json","```","","Manifest notes:","","- `version: 1`, `parallel`, and `tasks` are supported.","- Task kinds are `image.generate` and `image.edit`.","- Task fields: `id`, `kind`, `provider`, `profile`, `needs`, `prompt`, `output`, `params`, `sidecar`, `inputs`.","- Relative paths are resolved from the manifest file location.","- `inputs.images` accepts strings, `{ source }`, or `{ task }`. `{ task: base }` uses the first output from that completed task.","- Run `ploof run assets.yaml --dry-run --output json` before expensive batches.","","## Configuration","","```bash","ploof config list","ploof config set output json","ploof config set defaultParallel 4","ploof config set sidecar true","ploof config reset","```","","Config is stored at `~/.ploof/config.json`. Credentials are stored separately at `~/.ploof/credentials.json`.","","## Agent guidance","","- Prefer exact user-provided prompts, dimensions, source images, and output paths. Ask only when a missing choice would change the intended asset.","- Keep prompts and params explicit in commands or manifests.","- Use deterministic output paths, usually under the user's requested asset directory or a temp directory for tests.","- Use manifests for parallel generation, dependency chains, or more than one related asset.","- Keep `--parallel` modest unless the user requests volume; generation costs money and provider rate limits may apply.","- Report generated file paths and any relevant metadata such as model and token usage.","- Do not claim assets were generated unless the command completed successfully and output files exist.","- If a command fails, report the command goal, the error, and the next concrete fix. Do not fabricate assets.","- Run `ploof <command> --help` when a detail is unclear; prefer the installed CLI help over memory."]);function WV(){return QV(["Usage: ploof learn [options]","","Print AI-agent instructions for using the installed ploof CLI.","","Options:"," --help, -h Show this help","","Examples:"," ploof learn"])}function VV($=[]){if($.includes("--help")||$.includes("-h"))return WV();let U=$.find((w)=>w.trim()!=="");if(U)throw Error(`Unknown learn argument: ${U}
262
+ `}var RT=QV(["# Generate assets with the ploof CLI","","Ploof is an AI asset generation CLI. Use it when a task needs generated or edited images, batch asset creation, provider authentication, image context inputs, or reusable generation manifests.","","Package name: `@miketromba/ploof`. Command name: `ploof`. Supported today: OpenAI image generation and image editing.","","## Core workflow","","1. Run `ploof whoami openai`. If unauthenticated, ask the user to run `ploof login openai` or provide `PLOOF_OPENAI_API_KEY` in their shell.","2. For one asset, use `ploof image generate` or `ploof image edit` with an explicit `--out` path.","3. For multiple assets, dependencies, or parallel work, write a YAML manifest and run `ploof run <manifest.yaml>`.","4. Use `--output json` for commands another tool or agent must parse. Use `--output jsonl` for batch streams.","5. After a command completes, verify every path in `outputs` exists before telling the user generation succeeded.","","## Authentication","","```bash","ploof login openai --api-key <key>","ploof whoami openai","ploof profiles openai","```","","There is no `ploof auth` namespace. The authentication commands are top-level: `login`, `whoami`, `profiles`, and `logout`.","","Environment variables override stored credentials:","","```bash","PLOOF_OPENAI_API_KEY=...","OPENAI_API_KEY=...","```","","Use `--profile <name>` on `login`, `whoami`, `image generate`, `image edit`, and manifest tasks when the user has multiple OpenAI credentials. Never print or store secrets in generated project files.","","## Image generation","","```bash","ploof image generate \\"," --provider openai \\",' --prompt "Studio product photo" \\'," --out assets/hero.png \\"," --model gpt-image-2 \\"," --size 1024x1024 \\"," --quality high \\"," --format png \\"," --output json","```","","Important: `--format png` controls the generated asset format. `--output json` controls CLI output formatting.","","First-class OpenAI image flags:","","- `--model <model>`: for example `gpt-image-2`.","- `--size <size>`: provider size such as `1024x1024`.","- `--quality <quality>`: provider quality such as `low`, `medium`, or `high` when supported.","- `--format <format>` or `--output-format <format>`: provider image format, usually `png`, `jpeg`, or `webp` when supported.","- `--background <value>`, `--moderation <value>`, `--n <count>`, `--output-compression <number>`, `--partial-images <number>`, `--response-format <format>`, `--style <style>`, `--user <id>`, `--stream`.","- `--param key=value`: pass one provider-specific parameter. Nested keys work, such as `--param extra.foo=bar`.","- `--json '{...}'`: merge a provider-specific JSON object into the request. Explicit first-class flags override overlapping JSON keys.","","Use `--quality low` for cheap smoke tests. Use the user's requested quality and size for final assets.","","## Image editing and context images","","```bash","ploof image edit \\"," --provider openai \\"," --image input.png \\"," --image reference.png \\"," --mask mask.png \\",' --prompt "Replace the background" \\'," --out assets/edited.png \\"," --model gpt-image-2 \\"," --format png \\"," --output json","```","","Pass every source image the provider should see with repeated `--image`. Use `--mask` only when the provider/model supports masked edits. Edit also supports `--input-fidelity <value>`.","","Input assets can be local paths, `http://` or `https://` URLs, or `-` for stdin. Ploof infers common image MIME types from file extensions and preserves generated files locally.","","## Outputs and verification","","`--out <path>` can be a file path or directory. When generating multiple images with `--n`, a file path is expanded with `-1`, `-2`, etc. If `--out` is omitted, Ploof writes in the current directory using default names like `image.png` or `edited-image.png`.","","Default sidecar metadata is enabled. For each output file, Ploof writes `<output>.json` with the operation, prompt, params, outputs, provider metadata, and timestamp.","","Parseable result shape:","","```json","{",' "kind": "image.generate",',' "provider": "openai",',' "profile": "default",',' "outputs": ["assets/hero.png"],',' "metadata": { "model": "gpt-image-2" }',"}","```","","Useful global output flags:","","- `--output json|jsonl|compact|table`: choose CLI output format.","- `--fields outputs,metadata.usage.total_tokens`: select fields for parseable outputs.","- `--detail`, `--quiet`, `--no-color`, `--verbose`.","","## Batch manifests","","```yaml","version: 1","parallel: 4","tasks:"," - id: base"," kind: image.generate"," provider: openai"," profile: default",' prompt: "Studio product photo"'," output: assets/base.png"," params:"," model: gpt-image-2"," size: 1024x1024"," quality: high"," output_format: png",""," - id: edit"," kind: image.edit"," provider: openai"," needs: [base]"," inputs:"," images:"," - task: base"," - source: ./reference.png"," mask:"," source: ./mask.png",' prompt: "Add a premium background"'," output: assets/final.png"," params:"," model: gpt-image-2"," size: 1024x1024","```","","Run it with:","","```bash","ploof run assets.yaml --parallel 4 --output json","```","","Manifest notes:","","- `version: 1`, `parallel`, and `tasks` are supported.","- Task kinds are `image.generate` and `image.edit`.","- Task fields: `id`, `kind`, `provider`, `profile`, `needs`, `prompt`, `output`, `params`, `sidecar`, `inputs`.","- Relative paths are resolved from the manifest file location.","- `inputs.images` accepts strings, `{ source }`, or `{ task }`. `{ task: base }` uses the first output from that completed task.","- Run `ploof run assets.yaml --dry-run --output json` before expensive batches.","","## Configuration","","```bash","ploof config list","ploof config set output json","ploof config set defaultParallel 4","ploof config set sidecar true","ploof config reset","```","","Config is stored at `~/.ploof/config.json`. Credentials are stored separately at `~/.ploof/credentials.json`.","","## Agent guidance","","- Prefer exact user-provided prompts, dimensions, source images, and output paths. Ask only when a missing choice would change the intended asset.","- Keep prompts and params explicit in commands or manifests.","- Use deterministic output paths, usually under the user's requested asset directory or a temp directory for tests.","- Use manifests for parallel generation, dependency chains, or more than one related asset.","- Keep `--parallel` modest unless the user requests volume; generation costs money and provider rate limits may apply.","- Report generated file paths and any relevant metadata such as model and token usage.","- Do not claim assets were generated unless the command completed successfully and output files exist.","- If a command fails, report the command goal, the error, and the next concrete fix. Do not fabricate assets.","- Run `ploof <command> --help` when a detail is unclear; prefer the installed CLI help over memory."]);function WV(){return QV(["Usage: ploof learn [options]","","Print AI-agent instructions for using the installed ploof CLI.","","Options:"," --help, -h Show this help","","Examples:"," ploof learn"])}function VV($=[]){if($.includes("--help")||$.includes("-h"))return WV();let U=$.find((w)=>w.trim()!=="");if(U)throw Error(`Unknown learn argument: ${U}
263
263
 
264
264
  ${WV()}`);return RT}import{readFile as RE}from"node:fs/promises";import{dirname as kE,extname as fE,resolve as Eq}from"node:path";var lb=hY(),rb=k0(),db=FY(),sY=f0(),nb=P0(),e4=p(),pb=n4(),tb=N$(),ab=t4(),ob=a4(),td=iJ(),eb=aY(),sb=oY(),$E=eY(),lJ=Zq(),Tq=L0();var UE=lb.Composer,wE=rb.Document,zE=db.Schema,JE=sY.YAMLError,XE=sY.YAMLParseError,GE=sY.YAMLWarning,NE=nb.Alias,_E=e4.isAlias,YE=e4.isCollection,WE=e4.isDocument,QE=e4.isMap,VE=e4.isNode,BE=e4.isPair,qE=e4.isScalar,HE=e4.isSeq,KE=pb.Pair,LE=tb.Scalar,IE=ab.YAMLMap,PE=ob.YAMLSeq;var ZE=eb.Lexer,TE=sb.LineCounter,gE=$E.Parser,$W=lJ.parse,DE=lJ.parseAllDocuments,OE=lJ.parseDocument,FE=lJ.stringify,ME=Tq.visit,vE=Tq.visitAsync;class UW{id="openai";capabilities=["image.generate","image.edit"];async runImageGenerate($,U){let w=gq(U),J={...$.params,prompt:$.prompt},z=await Dq(w).generate(J),X=await Oq({response:z,output:$.output,format:Mq($.params),defaultName:$.id??"image"}),G={id:$.id,kind:"image.generate",provider:this.id,profile:U.credential.profile,outputs:X,metadata:Fq(z,$.params)};if($.sidecar??U.sidecar??!0)await iX(G,$,"image.generate");return G}async runImageEdit($,U){let w=gq(U),J=$.inputs.filter((V)=>V.role==="image"),z=$.inputs.find((V)=>V.role==="mask");if(J.length===0)throw Error("At least one --image input is required for image edits.");let X=await Promise.all(J.map(yX)),G=z?await yX(z):void 0,N=X.length===1?X[0]:X,_={...$.params,prompt:$.prompt,image:N,...G?{mask:G}:{}},Y=await Dq(w).edit(_),W=await Oq({response:Y,output:$.output,format:Mq($.params),defaultName:$.id??"edited-image"}),Q={id:$.id,kind:"image.edit",provider:this.id,profile:U.credential.profile,outputs:W,metadata:Fq(Y,$.params)};if($.sidecar??U.sidecar??!0)await iX(Q,$,"image.edit");return Q}}function gq($){let U=$.credential;if(!U.apiKey)throw Error("No OpenAI API key found. Run 'ploof login openai --api-key <key>' or set PLOOF_OPENAI_API_KEY.");return new i({apiKey:U.apiKey,organization:U.organization,project:U.project,baseURL:U.baseURL})}function Dq($){return $.images}async function Oq($){if(EE($.response))return SE($);let U=vq($.response),w=U.length,J=[];for(let z=0;z<U.length;z++){let X=U[z];if(X.b64_json)J.push(await Pz({data:X.b64_json,output:$.output,index:z,total:w,format:$.format,defaultName:$.defaultName}));else if(X.url)J.push(await dQ({url:X.url,output:$.output,index:z,total:w,format:$.format,defaultName:$.defaultName}))}if(J.length===0)throw Error("OpenAI response did not include image data or URLs.");return J}async function SE($){let U=[],w=0;for await(let J of $.response)for(let z of bE(J))U.push(await Pz({data:z,output:$.output,index:w,total:2,format:$.format,defaultName:$.defaultName})),w+=1;return U}function Fq($,U){let w=$&&typeof $==="object"?$:{};return{model:U?.model,created:w.created,usage:w.usage,revisedPrompts:vq($).map((J)=>J.revised_prompt).filter(Boolean)}}function vq($){if(!$||typeof $!=="object")return[];let U=$.data;return Array.isArray(U)?U:[]}function bE($){if(!$||typeof $!=="object")return[];let U=[],w=(J)=>{if(!J||typeof J!=="object")return;for(let[z,X]of Object.entries(J))if(typeof X==="string"&&(z==="b64_json"||z==="partial_image_b64"||z==="image_b64"))U.push(X);else if(typeof X==="object")w(X)};return w($),U}function Mq($){return $?.output_format??$?.format}function EE($){return Boolean($&&typeof $==="object"&&Symbol.asyncIterator in $)}var AE=[new UW];function rJ($){let U=AE.find((w)=>w.id===$);if(!U)throw Error(`Unknown provider: ${$}`);return U}var Sq=y.union([y.string(),y.object({source:y.string().optional(),task:y.string().optional(),mime:y.string().optional(),name:y.string().optional()})]),jE=y.object({id:y.string(),kind:y.enum(["image.generate","image.edit"]),provider:y.string().default("openai"),profile:y.string().optional(),needs:y.array(y.string()).default([]),prompt:y.string(),params:y.record(y.string(),y.unknown()).default({}),output:y.string().optional(),sidecar:y.boolean().optional(),inputs:y.object({images:y.array(Sq).optional(),mask:Sq.optional()}).optional()}),CE=y.object({version:y.union([y.literal(1),y.string()]).default(1),parallel:y.number().int().positive().optional(),tasks:y.array(jE).min(1)});async function uE($){let U=await RE($,"utf-8"),J=fE($).toLowerCase()===".json"?JSON.parse(U):$W(U),z=CE.parse(J);return xE(z),z}async function Aq($,U={}){let w=await uE($),J=U.parallel??w.parallel??4,z=kE(Eq($));if(U.dryRun)return w.tasks.map((Q)=>({id:Q.id,kind:Q.kind,provider:Q.provider,profile:Q.profile,outputs:Q.output?[dJ(z,Q.output)]:[],metadata:{dryRun:!0,needs:Q.needs}}));let X=U.auth??new g4,G=new Map,N=new Map,_=new Map(w.tasks.map((Q)=>[Q.id,Q])),Y=[],W;while(_.size>0||N.size>0){if(W)throw W;for(let[Q,V]of[..._]){if(N.size>=J)break;if(!V.needs.every((K)=>G.has(K)))continue;_.delete(Q);let B=yE(V,{auth:X,baseDir:z,completed:G,verbose:U.verbose,sidecar:U.config?.get("sidecar")??!0}).then((K)=>{G.set(Q,K),Y.push(K)}).catch((K)=>{W=K}).finally(()=>{N.delete(Q)});N.set(Q,B)}if(N.size===0&&_.size>0)throw Error("Manifest has unresolved dependencies.");if(N.size>0)await Promise.race(N.values())}if(W)throw W;return Y}function xE($){let U=new Set;for(let w of $.tasks){if(U.has(w.id))throw Error(`Duplicate task id: ${w.id}`);U.add(w.id)}for(let w of $.tasks)for(let J of w.needs)if(!U.has(J))throw Error(`Task ${w.id} depends on unknown task ${J}.`)}async function yE($,U){let w=rJ($.provider),J=U.auth.getCredential($.provider,$.profile);if(!J?.apiKey)throw Error(`No credentials found for ${$.provider}. Run 'ploof login ${$.provider}'.`);let z={id:$.id,kind:$.kind,provider:$.provider,profile:$.profile,prompt:$.prompt,params:$.params,output:$.output?dJ(U.baseDir,$.output):void 0,sidecar:$.sidecar??U.sidecar};if($.kind==="image.generate")return w.runImageGenerate({...z,kind:"image.generate"},{credential:J,verbose:U.verbose,sidecar:z.sidecar});let X=iE($,U);return w.runImageEdit({...z,kind:"image.edit",inputs:X},{credential:J,verbose:U.verbose,sidecar:z.sidecar})}function iE($,U){let w=[];for(let J of $.inputs?.images??[])w.push({role:"image",source:bq(J,U)});if($.inputs?.mask)w.push({role:"mask",source:bq($.inputs.mask,U)});return w}function bq($,U){if(typeof $==="string")return dJ(U.baseDir,$);if($.task){let J=U.completed.get($.task)?.outputs[0];if(!J)throw Error(`Task output not available: ${$.task}`);return J}if($.source)return dJ(U.baseDir,$.source);throw Error("Input reference must include source or task.")}function dJ($,U){if(U.startsWith("/")||U.startsWith("http://")||U.startsWith("https://")||U==="-")return U;return Eq($,U)}function Rq($,U,w,J){let z=$??w??U;if(z&&z!=="auto"){if(rE(z))return z;throw Error(`Invalid output format: ${z}`)}return J?"table":"compact"}function kq($,U){let w=Array.isArray($)?$:[$],J=U.fields?.length?w.map((z)=>cE(z,U.fields)):w;switch(U.format){case"json":return JSON.stringify(Array.isArray($)?J:J[0],null,2);case"jsonl":return J.map((z)=>JSON.stringify(z)).join(`
265
265
  `);case"table":return mE(w,U);case"compact":return w.map(hE).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miketromba/ploof",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "AI asset generation CLI for OpenAI images today, designed for multi-provider creative workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "dist",
11
11
  "skills",
12
+ "assets/brand/ploof-banner.png",
12
13
  "README.md",
13
14
  "LICENSE",
14
15
  "SPEC.md"
@@ -26,7 +27,7 @@
26
27
  "format": "bunx biome format --write .",
27
28
  "typecheck": "bunx tsc --noEmit",
28
29
  "prepublishOnly": "bun run lint && bun run typecheck && bun test && bun run build",
29
- "release": "bun run lint && bun run typecheck && bun test && bun run build && npm pack --dry-run && npm publish --access public",
30
+ "release": "bun run lint && bun run typecheck && bun test && bun run build && npm pack --dry-run",
30
31
  "ploof": "bun run bin/ploof.ts",
31
32
  "prepare": "husky || true"
32
33
  },