@wji.ai/cli 0.1.0 → 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.
@@ -15,6 +15,18 @@ and the human table output when you are showing the user a result.
15
15
  wji auth whoami --json # confirms endpoint, credential, org context
16
16
  ```
17
17
 
18
+ - **Check for a CLI update first.** The skill documents the newest commands, so
19
+ an old binary fails in confusing ways:
20
+
21
+ ```bash
22
+ wji --version && npm view @wji.ai/cli version
23
+ ```
24
+
25
+ If the installed version is lower, tell the user to run
26
+ `npm i -g @wji.ai/cli@latest` (or `pnpm add -g` / `yarn global add`) before
27
+ continuing. A dev checkout linked into PATH updates with `pnpm build`
28
+ instead. If the registry check fails (offline, proxy), carry on — don't
29
+ block the task on it.
18
30
  - `wji: command not found`: the CLI is not on PATH. From the `wji-cli` repo,
19
31
  `npm run build`, then link it where the user can write without sudo —
20
32
  `ln -s "$PWD/dist/index.js" ~/.local/bin/wji` (if `~/.local/bin` is on
@@ -28,6 +40,9 @@ wji auth whoami --json # confirms endpoint, credential, org context
28
40
  - `wji config show` reveals which endpoint and profile are active. A local
29
41
  backend is `wji config set endpoint http://localhost:3002` — `:3000` is the
30
42
  web frontend, not the API.
43
+ - **Scan the user's message for `-auto`.** It is not a CLI flag — it is the
44
+ user telling you to generate everything this request needs without stopping
45
+ to confirm the spend. See *Rules* for what it does and does not waive.
31
46
  - **Running several `wji` commands in parallel? Prefer an API key**
32
47
  (`WJI_API_KEY`, or `wji auth key create <name> --save`). A JWT login rotates
33
48
  its refresh token, and the server revokes *every* session if a refresh token
@@ -68,6 +83,10 @@ wji ws use <workspaceId>
68
83
  After that, `wji ws get`, `wji fs`, `wji gen` and `wji preset list` all default
69
84
  to that workspace. Use `wji ws use` rather than repeating `--workspace`.
70
85
 
86
+ `wji ws create <name>` seeds the default template — Dataset, DatasetImageGeneration and
87
+ InfiniteCanvas — because the API creates no apps on its own; pass `-a` to choose apps or
88
+ `--no-apps` for an empty workspace.
89
+
71
90
  ## Reading and editing the tree
72
91
 
73
92
  ```bash
@@ -80,12 +99,15 @@ wji fs get <nodeId> --json | jq .content
80
99
  wji fs mkdir <parentId> characters --nickname 角色
81
100
  wji dataset new <folderId> "Ada" -t character -d "a tired detective"
82
101
  wji fs describe <nodeId> @description.md
83
- wji fs set <nodeId> --images <assetId> <assetId>
102
+ wji fs set <nodeId> --merge content=@body.md --merge viewMode=content # merged into content
103
+ wji fs set <nodeId> --add-images <assetId> <assetId> # appended
104
+ wji fs set <nodeId> --images <assetId> <assetId> # replaced
84
105
  ```
85
106
 
86
- `wji fs set --content` **replaces** the whole content object. To change one
87
- field, read it first (`wji fs get <id> --json | jq .content`), merge, then
88
- write — or use `wji fs describe`, which merges `content.description` for you.
107
+ `wji fs set --content` **replaces** the whole content object, and so does
108
+ `--set content.x=…`. To change fields inside `content`, use `--merge key=value`
109
+ (dotted keys, `@file` values) — it reads the node and deep-merges for you, like
110
+ `wji fs describe` does for `content.description`.
89
111
 
90
112
  Several GET endpoints are cached server-side for 10 seconds, so a plain read
91
113
  immediately after a write can come back stale. `wji fs describe` and
@@ -115,11 +137,19 @@ wji gen video -m <modelName> -p "slow pan" -d 5 --start-image <assetId> --wait -
115
137
  wji gen audio -m <modelName> -p "rain on a tin roof" --wait
116
138
  ```
117
139
 
140
+ - **Check the spending policy in *Rules* before you submit:** a single image
141
+ just runs; video, audio and anything with `-n` above 1 need a yes in the
142
+ chat first — unless the user's message carried `-auto`.
118
143
  - References are `assetId[:label[:weight]]`: `--ref 65f0…:hero:0.6`. Reference
119
144
  *assets*, not file paths — upload first with `wji asset upload`.
120
145
  - Model-specific parameters go through `--set` (`--set quality=high`); read the
121
146
  valid keys from `wji models get`.
122
- - `--fsnode <id>` files the result onto a node; `--tags` labels the history.
147
+ - `--fsnode <id>` files the *history entry* under a node — it does **not** add
148
+ the asset to that node's `images`. Recover them later with
149
+ `wji history list --fsnode <id>`. To make a generated image part of a dataset
150
+ you must attach it yourself, and `fs set --images` replaces the whole array:
151
+ read the current ids first, then write old + new together.
152
+ - `--tags` labels the history entry.
123
153
  - Video can take minutes. Without `--wait` you get a history id back:
124
154
  `wji history get <id> --wait` resumes polling, and `--timeout` is in seconds.
125
155
  - On timeout the task is usually still running — re-poll rather than
@@ -142,6 +172,18 @@ confirm with `wji models list --feature <image|video>` and `wji models get`.
142
172
  and big changes such as character sheets; `gpt-image-2.5-flare` for edits that
143
173
  keep the composition), **Nano Banana 2** or **Nano Banana Pro**.
144
174
 
175
+ Two image jobs have one right model each:
176
+
177
+ - **Transparent background** (logos, cut-outs, overlays, sprites) → GPT Image
178
+ 2.5 with `--set background=transparent`; the result comes back as PNG with
179
+ alpha. `opaque` forces a filled background, `auto` lets the model decide.
180
+ - **Split a picture into layers** (subject separated from background, for
181
+ compositing) → `bytedance-seedream-5-pro` with
182
+ `--set layer_decomposition=true`. It needs **exactly one** reference image,
183
+ a size tier (`--set size=1K|2K`, not pixel dimensions), and returns several
184
+ PNG layers as separate assets. On a canvas, run it in the browser's layer
185
+ editor instead — a CLI generate node keeps only one asset per node.
186
+
145
187
  **Video — reference models over first/last-frame models.** A family usually
146
188
  has both:
147
189
 
@@ -207,11 +249,11 @@ a Ref model.
207
249
 
208
250
  ```bash
209
251
  wji fs get <datasetId> --scope full --json | jq '{images: [.images[]? | {id, type}], icon, primaryAudio}'
210
- wji fs set <datasetId> --images <headshot> <sheet> <voice> --icon <headshot> --set primaryAudio=<voice>
252
+ wji fs set <datasetId> --add-images <headshot> <sheet> <voice> --icon <headshot> --set primaryAudio=<voice>
211
253
  ```
212
254
 
213
- `--images` replaces the whole list — read the current list first and keep
214
- what is already there.
255
+ `--add-images` appends; `--images` replaces the whole list — use it only when
256
+ the user wants the list rewritten, after reading what is there.
215
257
 
216
258
  ## Assets
217
259
 
@@ -267,12 +309,19 @@ use `skill push` for large files.
267
309
 
268
310
  ## Canvas
269
311
 
270
- An Infinite Canvas holds **nodes** (text, title, comment, image, video, audio…)
271
- and **edges** between them. An edge into a media node makes the source one of
272
- its **references**, numbered per kind in edge order: `@image1`, `@image2`,
273
- `@video1`. A media node is either a **display** node (shows an existing asset)
274
- or a **generate** node (prompt + model + settings; generating fills in its
275
- asset).
312
+ An Infinite Canvas holds **nodes** (text, title, comment, image, video, audio,
313
+ dataset…) and **edges** between them. An edge into a media node makes the
314
+ source one of its **references**, numbered per kind in edge order: `@image1`,
315
+ `@image2`, `@video1`. A media node is either a **display** node (shows an
316
+ existing asset) or a **generate** node (prompt + model + settings; generating
317
+ fills in its asset). A **dataset** node is a card bound to a workspace dataset
318
+ (`canvas add dataset --dataset <fsnodeId>`) — good for showing a character
319
+ or scene next to its sheets, but `canvas gen` cannot take it as a reference:
320
+ reference the image display node that shows the asset instead.
321
+
322
+ A workspace's InfiniteCanvas app can hold several canvases (`wji canvas list`);
323
+ `wji canvas create <name>` adds an empty one — e.g. one canvas per episode or
324
+ per character set.
276
325
 
277
326
  **The link.** The user opens a canvas in the browser and presses the link
278
327
  button in its top bar. That stores the open workspace, canvas and **the nodes
@@ -286,8 +335,11 @@ wji canvas nodes # every node; * marks generate nodes
286
335
  wji canvas get <nodeId> # full detail incl. references
287
336
  wji asset download <assetId…> -o <dir> # then look at the images yourself
288
337
 
338
+ wji canvas create E01 -w <workspaceId> # new empty canvas; then --canvas <id>
289
339
  wji canvas add video --from <imgNode> <imgNode> -m <model> -r 16:9 -d 8 -p "@image1 … @image2 …"
290
340
  wji canvas add text -t "shot list…" --at 0,600
341
+ wji canvas add image --asset <assetId> -n "Ada sheet" # display node for an existing asset
342
+ wji canvas add dataset --dataset <fsnodeId> # card bound to a workspace dataset
291
343
  wji canvas set <nodeId> -p "new prompt" # merges: other settings stay
292
344
  wji canvas set <nodeId> -m <model> --resolution 720p --set seed=42
293
345
  wji canvas connect <from> <to> / wji canvas disconnect <from> <to>
@@ -377,14 +429,37 @@ wji org list && wji org use <orgId>
377
429
  anything you find afterwards — new targets need a new question.
378
430
  - **Overwrites don't prompt at all.** `fs set --content`, `--images` and
379
431
  `--tags`, and the update commands' tag options, replace the whole value.
380
- Read the current value first, and check with the user before replacing
381
- anything they did not ask to change.
382
- - **Generation costs credits.** Run `wji gen cost` first for anything large
383
- (high `-n`, long video), show the user the number, and get agreement before
384
- submitting a batch. `wji canvas gen` prints its exact cost and refuses
385
- without `--yes`: run it once without `--yes` to get the number, tell the
386
- user, and pass `--yes` only after they agree. Creating or editing a generate
387
- node (`canvas add` / `canvas set`) costs nothing.
432
+ Prefer the merging forms (`--merge`, `--add-images`, `fs describe`); when
433
+ you must replace, read the current value first and check with the user
434
+ before replacing anything they did not ask to change.
435
+ - **Generation costs credits — one image just runs; video and batches ask
436
+ first.** This is the user's standing policy. Follow it without asking them
437
+ to restate it:
438
+
439
+ | About to run | Do this |
440
+ | --- | --- |
441
+ | **One image** — `gen image` with `-n 1`, or `canvas gen` on an image node | **Go ahead.** Pass `--yes`, then report the `credits` line from the result. |
442
+ | **Any video or audio** | **Ask first.** Get the number, tell the user, wait for a yes. |
443
+ | **More than one generation in one step** — `-n 2` or higher, several nodes, a storyboard fan-out, an A×B matrix | **Ask first, with the total** — not the per-item price. |
444
+
445
+ Splitting a batch does not make it single: what needs approval is the total
446
+ you are about to spend in this step, however many commands it takes.
447
+
448
+ To get the number before asking, both of these are free: `wji gen cost -t
449
+ video -m <model> -d 8`, or run `wji canvas gen <nodeId>` *without* `--yes` —
450
+ it refuses to run and the error names the exact credits.
451
+
452
+ **`-auto` in the user's message overrides the table.** Generate everything
453
+ that request needs without asking — video and batches included — and report
454
+ the total actually charged when you are done. It covers that request and the
455
+ follow-ups continuing it; when the user moves to a different task, the table
456
+ above applies again. `-auto` never waives the confirmations for *destructive*
457
+ commands above, only the spending ones.
458
+
459
+ Creating or editing a generate node (`canvas add` / `canvas set`) costs
460
+ nothing, and neither does `gen cost`. Actual spend is in the result's
461
+ `credits` field (`history.chargedCredits`), so quote that rather than the
462
+ estimate once a job has finished.
388
463
  - **Never invent ids.** Look up workspace, node, asset and model ids with the
389
464
  list commands; ids are Mongo ObjectIds, not names.
390
465
  - **Stay inside the generation queue.** See *Limits* above — batch to the cap
@@ -110,6 +110,18 @@ its defaults; check with `wji canvas get <node>` and tell the user what changed.
110
110
 
111
111
  > "Go ahead and generate it."
112
112
 
113
+ **An image node just runs** (see the spending policy in SKILL.md *Rules*):
114
+
115
+ ```bash
116
+ wji canvas gen <node> --yes --wait
117
+ ```
118
+
119
+ Report the `credits` line from the result so the user sees what it actually
120
+ cost.
121
+
122
+ **A video or audio node needs a yes first** — so does any batch of nodes, and
123
+ `-auto` in the user's message waives both:
124
+
113
125
  ```bash
114
126
  wji canvas gen <node> # prints the cost and refuses (no terminal)
115
127
  ```
@@ -147,8 +159,10 @@ Never resubmit a node whose status is `generating` — that charges twice.
147
159
  wji canvas add video -n "Shot 2" --from <char1> <char2> -m <model> -r 16:9 -d 5 -p "…shot 2…"
148
160
  ```
149
161
 
150
- 4. Total the cost (`wji canvas gen <node>` for each, without `--yes`) before
151
- offering to generate them all.
162
+ 4. Total the cost (`wji canvas gen <node>` for each, without `--yes`) and get a
163
+ yes on that total before generating any of them. A fan-out is a batch, so
164
+ this applies even when every node is an image — unless the request carried
165
+ `-auto`, in which case generate them all and report the total charged.
152
166
 
153
167
  ## 6. Fix which image is `@image1`
154
168
 
@@ -67,7 +67,7 @@ switch endpoint.
67
67
  | `ws list [-k keyword] [-s all\|me\|shared\|favorite] [--page] [--limit]` | `GET /workspace/search` — `--limit` max 100, `-1` returns at most 500 |
68
68
  | `ws get [id]` | `GET /workspace/list/:id` |
69
69
  | `ws use <id>` | stores the default workspace locally |
70
- | `ws create <name> [-d] [-r ratio] [-l lang] [-t type] [-a app...]` | `POST /workspace/create` |
70
+ | `ws create <name> [-d] [-r ratio] [-l lang] [-t type] [-a app...] [--no-apps]` | `POST /workspace/create` — without `-a` the workspace gets the default template `dataset datasetimagegeneration canvas`; `--no-apps` creates it empty (the API itself adds nothing) |
71
71
  | `ws update [id] [-n] [-d] [-r] [-l] [--set]` | `PUT /workspace/list/:id` |
72
72
  | `ws rm <id> [-y]` | `DELETE /workspace/list/:id` (owner only) |
73
73
  | `ws favorite <id> [--off]` | `PUT /workspace/list/:id/favorite` |
@@ -99,7 +99,7 @@ Restriction from the backend: `Dataset`, `Draw`, `DatasetImageGeneration` and
99
99
  | `fs path <id>` | `GET /fsnode/list/:id/path` |
100
100
  | `fs mkdir <parent> <name> [--nickname] [--content]` | `POST /fsnode/create` type=folder |
101
101
  | `fs touch <parent> <name> [--mimetype] [--asset] [--content]` | `POST /fsnode/create` type=file |
102
- | `fs set <id> [-n] [--nickname] [--content] [--tags] [--images] [--icon] [--asset] [--sort] [--set]` | `PATCH /fsnode/list/:id` |
102
+ | `fs set <id> [-n] [--nickname] [--content] [--tags] [--images] [--add-images] [--icon] [--asset] [--sort] [--set] [--merge]` | `PATCH /fsnode/list/:id` — `--merge key=value` deep-merges into `content` (reads first; `@file` values), `--add-images` appends to `images`; `--content`, `--images`, `--set content.*` replace |
103
103
  | `fs describe <id> <text>` | merges `content.description` |
104
104
  | `fs rm <id> [-y]` | `DELETE /fsnode/list/:id` (soft delete, whole subtree) |
105
105
  | `fs sort <parent> <ids...>` | `PATCH /fsnode/list/:id/children/sort` |
@@ -142,6 +142,11 @@ Semantic dataset folders created with the Dataset app: `story`, `character`,
142
142
 
143
143
  Asset states: `0` pending, `1` ready, `-1` cancelled.
144
144
 
145
+ Model parameters worth knowing (`--set`, see `wji models get`):
146
+ `gpt-image-2.5-*` `background=transparent` returns a PNG with alpha;
147
+ `bytedance-seedream-5-pro` `layer_decomposition=true` splits one reference
148
+ image into separate PNG layer assets (exactly 1 reference, `size=1K|2K`).
149
+
145
150
  Uploads of `.html`, `.xhtml`, `.svg`, `.js` and `.xml` are stored as
146
151
  `application/octet-stream` with `Content-Disposition: attachment`.
147
152
 
@@ -156,10 +161,11 @@ updates live.
156
161
  | `canvas status` (`link`) | `GET /canvas-link`, then the selected nodes read fresh from the canvas |
157
162
  | `canvas unlink` | `DELETE /canvas-link`; the tab turns its button off at its next heartbeat (≤ 60s) |
158
163
  | `canvas list [-w]` | canvases under each InfiniteCanvas app: `GET /workspace/list/:id/fs/:appId` → `workspace-v2/canvases` children |
164
+ | `canvas create <name> [-w] [--app]` | `POST /fsnode/create` a `workspace-v2/canvas` folder under that `canvases` folder with the web's initial content (`{data:{viewport, backgroundMode:'dots'}, metadata:{}}`) |
159
165
  | `canvas nodes [-c] [-k kind]` | `GET /fsnode/list/:canvasId/children?limit=-1` + `POST /assets/check-status` |
160
166
  | `canvas get <nodeId> [-c]` | same load; one node with references (`@image1 ← id`) and frame slots |
161
167
  | `canvas edges [-c] [--node]` | the `canvas/edge` children |
162
- | `canvas add <kind> [-c] [-n] [-t\|--text-file] [--asset] [-p\|--prompt-file] [-m] [-r] [--resolution] [-d] [--set] [--from ids…] [--at x,y]` | `POST /fsnode/create` (`canvas/node/<kind>`), then one `canvas/edge` per `--from`. Kinds: text, title, comment, image, video, audio |
168
+ | `canvas add <kind> [-c] [-n] [-t\|--text-file] [--asset] [--dataset] [-p\|--prompt-file] [-m] [-r] [--resolution] [-d] [--set] [--from ids…] [--at x,y]` | `POST /fsnode/create` (`canvas/node/<kind>`), then one `canvas/edge` per `--from`. Kinds: text, title, comment, image, video, audio, dataset. `add dataset --dataset <fsnodeId>` writes `content {datasetId, datasetName, datasetType, datasetParentName, description}` from the dataset node (must be `workspace/dataset`); it takes no `--from` |
163
169
  | `canvas set <nodeId> [-c] [-n] [-t] [--move x,y] [-p] [-m] [-r] [--resolution] [-d] [--set]` | read → merge → `PATCH /fsnode/list/:id` |
164
170
  | `canvas connect <from> <to> [-c]` | `POST /fsnode/create` (`canvas/edge`); a first/last-frame video model gets `videoFrameSlot` start, then end |
165
171
  | `canvas disconnect <from> <to> [-c]` | `DELETE /fsnode/list/:edgeId` |
@@ -200,7 +206,7 @@ viewport, url}` on toggle (`takeover: true`), on selection change and every
200
206
 
201
207
  Shared flags: `-m/--model` (required), `-p/--prompt`, `-n/--n`, `-w/--workspace`,
202
208
  `--fsnode`, `--scope`, `--scope-id`, `-r/--ratio`, `--tags`, `--values`,
203
- `--set`, `--no-history`, `--wait`, `--timeout` (seconds, default 900),
209
+ `--set`, `--wait`, `--timeout` (seconds, default 900),
204
210
  `--interval` (seconds, default 5).
205
211
 
206
212
  | Command | Extra flags | API |
@@ -210,8 +216,11 @@ Shared flags: `-m/--model` (required), `-p/--prompt`, `-n/--n`, `-w/--workspace`
210
216
  | `gen audio` | `-d/--duration`, `--ref-audio` | `POST /ai/assets/history/audio/generations` |
211
217
  | `gen cost -t <type> -m <model>` | `-n`, `-p`, `-d`, `-r`, `--refs`, `--values` | `POST /ai/assets/preComputeCost` |
212
218
 
213
- `--no-history` calls `/ai/assets/<type>/generations`, which returns assets
214
- directly and cannot be polled by history id.
219
+ Every generation goes through the history endpoint — there is no raw mode. The
220
+ backend also exposes `/ai/assets/<type>/generations`, which skips the history
221
+ record, but the CLI never calls it: without a history there is no id to poll,
222
+ and `fsnode`, `scope`, `scopeId` and `tags` are silently dropped (they are
223
+ written only inside `if (toHistory)`).
215
224
 
216
225
  Admission limits, per user and API key (changing IP does not reset them):
217
226