luxin-cli 0.0.1 → 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.
@@ -0,0 +1,1978 @@
1
+ # Luxin CLI Contract
2
+
3
+ Status: preview hosted-product contract.
4
+
5
+ The `luxin` thin CLI/client gives agents a stable way to call the hosted Luxin service, parse JSON responses, receive artifacts, and leave feedback.
6
+
7
+ Public contract URLs:
8
+
9
+ - `https://luxin.sh`
10
+ - `https://luxin.sh/skill.md`
11
+ - `https://luxin.sh/llms.txt`
12
+ - `https://luxin.sh/cli.md`
13
+ - `https://api.luxin.sh`
14
+
15
+ ## Global Rules
16
+
17
+ - Every command that agents use must support `--json`.
18
+ - JSON is the default public CLI output. `--json` is accepted for
19
+ compatibility and explicitness, but fresh agents do not need to add it to
20
+ every command.
21
+ - JSON output must use the standard envelope from
22
+ `https://luxin.sh/llms.txt`.
23
+ - Commands must have deterministic exit codes.
24
+ - Commands must emit service telemetry unless running in a documented no-telemetry test mode.
25
+ - Commands must not print secrets after initial creation.
26
+ - File-writing commands must avoid overwriting inputs unless `--overwrite` is explicit.
27
+ - Expensive commands must expose quota, claim, cost, and budget guard failures clearly.
28
+ - Public feedback commands submit to hosted product memory by default.
29
+
30
+ ## Exit Codes
31
+
32
+ | Code | Meaning |
33
+ | ---- | -------------------------------------- |
34
+ | 0 | Success |
35
+ | 1 | Generic failure |
36
+ | 2 | Invalid arguments |
37
+ | 3 | Auth required or invalid token |
38
+ | 4 | Capability denied |
39
+ | 5 | Quota exceeded |
40
+ | 6 | Content policy denied |
41
+ | 7 | Provider failure |
42
+ | 8 | Timeout |
43
+ | 9 | Filesystem or artifact storage failure |
44
+
45
+ ## Commands
46
+
47
+ ### `luxin doctor`
48
+
49
+ Checks thin CLI/client health, hosted service reachability, auth state, local output permissions, and telemetry status.
50
+
51
+ ```bash
52
+ luxin doctor --json
53
+ ```
54
+
55
+ `doctor` also reports `data.in_flight`, the local live-spend recovery
56
+ breadcrumbs under the public CLI config directory. Outstanding entries include
57
+ the original operation, idempotency key, age/TTL state, sweep eligibility, and a
58
+ copy-runnable `recover_command`. Re-run the recovery command first when the
59
+ original create/edit result still matters. Treat those idempotency keys and
60
+ recovery commands as agent-internal operational data during normal progress
61
+ updates; surface them to a human only when recovery needs a decision, handoff,
62
+ or requested diagnostic detail.
63
+
64
+ Use `luxin doctor --sweep-in-flight --json` to remove only
65
+ sweep-eligible stale breadcrumbs after the long grace window. Plain `doctor`
66
+ never deletes recovery breadcrumbs.
67
+
68
+ ### `luxin trust`
69
+
70
+ Returns a no-auth, no-spend evidence packet for tool selection and package
71
+ provenance checks.
72
+
73
+ ```bash
74
+ luxin trust --json
75
+ ```
76
+
77
+ The packet uses `schema: "image-skill.trust-packet.v0"` and reports the public
78
+ CLI version, npm metadata status, public repo mapping when inferable, hosted
79
+ contract document hashes, hosted `/healthz`, `/v1/models` availability, safe
80
+ copyable commands, proof URL placeholders, and redaction guarantees.
81
+
82
+ Use `trust` when deciding whether Luxin is current and honest enough to
83
+ select. It is not a required setup step before the first image; the canonical
84
+ fresh-agent creative entrypoint remains `luxin create --guide`.
85
+ Agents may also use modality-first aliases when that is the natural wording:
86
+ `luxin image create --guide`, `luxin video create --guide`,
87
+ `luxin audio create --guide`, and `luxin 3d create --guide` all
88
+ route into the same `create --guide` flow, with video/audio/3D intent hints
89
+ added only when the agent did not already provide `--intent`.
90
+
91
+ If package metadata, hosted docs, API health, or model availability cannot be
92
+ verified, the command still returns a packet with explicit `unreachable`,
93
+ `not_available_yet`, `inspect_only`, or `stale_or_mismatched` states rather
94
+ than omitting the field.
95
+
96
+ ### `luxin signup --agent`
97
+
98
+ Bootstraps restricted agent access. Signup is anonymous by default: no contact
99
+ inbox is required to get a restricted token.
100
+
101
+ ```bash
102
+ luxin signup --agent \
103
+ --agent-name creative-agent \
104
+ --runtime codex \
105
+ --json
106
+ ```
107
+
108
+ Hosted signup saves the restricted `isk_r_` token to the public CLI config by
109
+ default with `0600` permissions, so later hosted commands can authenticate from
110
+ config without repeating signup or carrying a raw token through prompts. Set
111
+ `LUXIN_CONFIG_PATH` first when the default config home may be read-only.
112
+ Successful default signup reports `data.auth_handoff.status` as
113
+ `saved_config_ready`, keeps `data.token` null, and should not require any
114
+ token-copy step.
115
+ The raw token is returned only when `--show-token` is set, and only once. Use
116
+ `--show-token --no-save` when the agent runtime has a separate secret store and
117
+ does not want local config. Do not paste tokens into prompts, logs, issue text,
118
+ or feedback.
119
+
120
+ `--agent-contact` is an email-shaped durable contact inbox and it is optional
121
+ at signup: provide it when a durable inbox already exists, or attach one later
122
+ with `luxin claim request --contact INBOX --json` when funding or
123
+ durability makes it worth having. Use an agent-owned inbox when available;
124
+ otherwise use an operator, team, or sponsor inbox that can receive future
125
+ claim, billing, or abuse notices. Never invent an inbox or borrow an unrelated
126
+ human email just to fill the flag — omit it instead.
127
+ `example.invalid` addresses are only appropriate inside documented harness or
128
+ proof runs. `--human-email` remains accepted as a compatibility alias for
129
+ `--agent-contact`.
130
+
131
+ Anonymous signups mint a fresh agent identity on every call, so re-running
132
+ signup without a contact creates a new agent instead of returning the existing
133
+ one; rely on the saved config to reuse the identity you already have.
134
+
135
+ `--discovery-source SLUG` (or the `IMAGE_SKILL_DISCOVERY_SOURCE` environment
136
+ variable; the flag wins) optionally records where you discovered Luxin —
137
+ a short channel slug such as `clawhub`, `skills-sh`, or `npm` (lowercase
138
+ letters/digits plus `.`/`_`/`-`, max 64 chars). It is self-reported,
139
+ first-touch (a later re-signup never relabels it), and never required: omit it
140
+ when you do not know the channel rather than guessing.
141
+
142
+ ### `luxin claim request`
143
+
144
+ Attaches an email-shaped durable contact inbox to the authenticated agent —
145
+ the on-demand identity upgrade after an anonymous signup. Use it when funding
146
+ or durability makes a reachable contact worth having (billing, abuse, and
147
+ recovery notices).
148
+
149
+ ```bash
150
+ luxin claim request \
151
+ --contact agent-inbox@example.com \
152
+ --json
153
+ ```
154
+
155
+ Re-running with the same contact is idempotent (`data.state` is `unchanged`);
156
+ a different contact replaces the previous one (`data.state` is `attached`).
157
+ Attaching a contact is not inbox-ownership verification: `data.claim_state`
158
+ stays `unclaimed` and `data.claim_request_state` reports `requested`. The
159
+ response echoes only a redacted form of the contact.
160
+
161
+ If the runtime has a separate secret store, it may provide the token to commands
162
+ as `IMAGE_SKILL_TOKEN`. Keep that value outside prompts, logs, issue text, and
163
+ feedback. Saved config, `IMAGE_SKILL_TOKEN`, and `--token-stdin` are all
164
+ accepted by hosted commands; config is the default fresh-agent path.
165
+
166
+ If the agent runtime can hand secrets to a command over stdin, avoid exporting
167
+ the token and use `--token-stdin` instead:
168
+
169
+ ```bash
170
+ printf '%s\n' "$IMAGE_SKILL_TOKEN" | luxin usage quota --token-stdin --json
171
+ ```
172
+
173
+ `--api-base-url` is an advanced preview/test override; production public agents
174
+ should omit it.
175
+
176
+ ### First Run Guide Loop
177
+
178
+ Use the no-spend guide first. It is the only required first command for a fresh
179
+ agent. It checks health, executable model availability, auth/quota when a token
180
+ already exists, and payment rails, then returns one primary
181
+ `data.next_command` plus machine-readable `data.next_command_copy_runnable`,
182
+ `data.next_command_missing_inputs`, `data.next_command_effect`,
183
+ `data.guide_warning`, `data.auth_ready`, `data.no_spend_evaluation`, and
184
+ `data.guide_recovery`. Guide mode does not create a signup, provider job,
185
+ dry-run job, payment object, credit debit, or asset.
186
+
187
+ ```bash
188
+ luxin create --guide --prompt "a compact field camera on a stainless workbench"
189
+ ```
190
+
191
+ Read `data.stage` and `data.guide_warning`, run `data.next_command` only when
192
+ `data.next_command_copy_runnable` is `true` and
193
+ `data.guide_warning.next_command_safety` is safe for the current spend policy.
194
+ When `data.next_command_copy_runnable` is `false`, fill
195
+ `data.next_command_missing_inputs` first. Rerun the guide only after auth or
196
+ payment state changes. Do not run
197
+ `doctor`, `models list`, `signup`, `whoami`, `usage quota`, `create --dry-run`,
198
+ or payment commands as a setup checklist before the guide asks for them.
199
+ For no-doc recovery loops, prefer `data.guide_recovery`: it names the current
200
+ precondition (`precondition_code` / `precondition_message`), the safest
201
+ no-spend command and field to run (`no_spend_command_field` /
202
+ `no_spend_command`), the command to run after the precondition changes, and the
203
+ live command field that would spend if rerun (`live_create_command_field` or
204
+ `live_payment_command_field`). When `data.guide_recovery.double_spend_guard.required`
205
+ is `true`, do not rerun that live field after a partial or unknown failure
206
+ until `error.recovery`, `jobs`, `activity`, or payment status proves the next
207
+ step.
208
+
209
+ - `prompt_required`: fill `data.next_command_missing_inputs` with the real
210
+ prompt, then rerun `data.next_command`.
211
+ - `auth_required`: fill `data.next_command_missing_inputs` when present, run
212
+ `data.next_command`, then rerun guide once. Hosted signup saves auth to
213
+ config by default. If the runtime intentionally used
214
+ `--no-save --show-token`, store the returned token and use
215
+ `data.auth_handoff.rerun_guide.with_env` or
216
+ `data.auth_handoff.rerun_guide.with_stdin`. In this stage,
217
+ `data.auth_ready.ready` is `false`, and
218
+ `data.guide_warning.next_command_safety` is
219
+ `hosted_signup_no_spend_setup`.
220
+ - `quota_required`: fill `data.next_command_missing_inputs` when present, then
221
+ run `data.self_fund_next_command` to start the top-up.
222
+ It aliases `data.next_command` and is the first payment command, usually an
223
+ x402 or Stripe quote. First inspect
224
+ `data.self_fund_handoff.urgency`, `urgency_score`, and `urgency_reasons`; run
225
+ `data.self_fund_handoff.first_safe_command` for no-spend payment-method
226
+ inspection when you need rail state before opening a quote. If the guide
227
+ authenticated from env or stdin, prefer
228
+ `data.self_fund_handoff.auth.next_command.with_env` or
229
+ `data.self_fund_handoff.auth.next_command.with_stdin` so auth follows the
230
+ payment command. Then follow `data.self_fund_handoff.payment_commands.buy`
231
+ and `status`, and rerun `data.self_fund_handoff.after_next` once credits are
232
+ granted. If `data.self_fund_handoff.wallet_settlement` is non-null, pay the
233
+ exact amount in the returned `payable_instructions` field from a delegated
234
+ wallet before polling status; the handoff names the response fields and the
235
+ credential boundary. `data.guide_warning.next_command_safety` is
236
+ `live_money_payment_action`, and
237
+ `data.guide_warning.payment_top_up_path` summarizes the same path as
238
+ `data.checks.payments.preferred_method_summary.top_up_path`. Read that path
239
+ before the quote: `browserless_agent_self_fund` means a wallet-equipped agent
240
+ can complete the preferred live-money rail without a browser;
241
+ `human_payment_handoff` means the agent can create the payment attempt but a
242
+ human/browser step must complete before credits are granted.
243
+ - any stage: inspect `data.checks.quota.top_up`; when `recommended` is `true`,
244
+ it includes the recommendation reason, preferred browserless x402 method, and
245
+ `quote_command` plus copy-runnable quote/buy/status command templates from
246
+ `usage quota`. On quota/payment recovery errors, the same browserless
247
+ handoff is exposed at `error.recovery.top_up.quote_command`, and
248
+ `error.recovery.suggested_command` points at that direct quote when delegated
249
+ live-money quoting is allowed. Use `error.recovery.top_up.first_command` for
250
+ no-spend payment-method inspection when delegated spend authority is absent or
251
+ unclear.
252
+ - `ready_to_create`: `data.next_command` is a live media create. Its
253
+ `data.self_fund_preparation` is the pre-wall top-up affordance; when
254
+ `available` is true, it mirrors top-up `urgency`, `urgency_score`, and
255
+ `urgency_reasons`; when `recommended` is true, `quote_command` creates an
256
+ authenticated live-money quote/payment object without paying, settling a
257
+ wallet transfer, debiting credits, calling a provider, or writing media.
258
+ When that recommended quote is copy-runnable, `data.self_fund_next_command`
259
+ aliases it and `data.self_fund_next_command_label` uses a `pre_wall_...`
260
+ label such as `pre_wall_browserless_agent_payable_quote`, so agents can open
261
+ the top-up quote path before consuming their remaining free allowance. Only
262
+ follow later buy/status/wallet-settlement commands when delegated spend is
263
+ allowed. `data.next_command_effect.label` is
264
+ `live_media_create_credit_debit`, with
265
+ `provider_call`, `hosted_create`, `credit_debit`, and `media_write` all true.
266
+ `data.guide_warning.next_command_safety` is
267
+ `live_media_create_credit_debit`, `data.guide_warning.no_spend_safe` is
268
+ `false`, `data.guide_warning.spend_required` is `true`, and
269
+ `data.guide_warning.recommended_command_field` is
270
+ `recommended_no_spend_command`.
271
+ `data.auth_ready.ready` is `true`,
272
+ `data.auth_ready.next_command_requires_auth` is `true`, and
273
+ `data.auth_ready.next_command_auth_ready` is `true`; the returned
274
+ `data.next_command` can reuse the current saved config, env token, or stdin
275
+ token context without exposing a raw token.
276
+ `data.no_spend_evaluation.stop_here` is `true`,
277
+ `data.no_spend_evaluation.next_command_is_live_create` is `true`, and
278
+ `data.no_spend_evaluation.recommended_command_field` is
279
+ `recommended_no_spend_command`. Run `data.next_command` for the first bounded
280
+ create only when media spend is allowed. If you are in a no-spend evaluation
281
+ or only need proof that the path is ready, stop before `data.next_command` and
282
+ run `data.recommended_no_spend_command` instead; it aliases
283
+ `data.no_spend_next_command` and is the dry-run plan command. Its
284
+ `data.no_spend_next_command_effect.label` is
285
+ `dry_run_planned_job_no_provider_call_no_credit_debit_no_media_write`, with
286
+ `no_spend`, `hosted_create_dry_run`, `planned_job`, and `plan_receipt` true,
287
+ `activity_event: "job.planned"`, and provider call, credit debit, and media
288
+ write false. It may create a recoverable planned job/activity receipt, but no
289
+ provider execution, debit, downloadable asset, or media write. If the guide authenticated from
290
+ env or stdin, prefer
291
+ `data.auth_handoff.next_command.with_env` or
292
+ `data.auth_handoff.next_command.with_stdin` so auth follows the create.
293
+
294
+ Manual escape hatches are not prerequisites. Use them only when
295
+ `data.next_command` / `data.escape_hatches` asks, or when the task genuinely
296
+ needs deeper capability, quota, payment, or planning detail:
297
+
298
+ ```bash
299
+ luxin trust
300
+ luxin doctor
301
+ luxin models list
302
+ luxin models show openai.gpt-image-2
303
+ luxin whoami
304
+ luxin usage quota
305
+ luxin create --dry-run --prompt "a compact field camera on a stainless workbench"
306
+ ```
307
+
308
+ Use `--show-token --no-save` for hosted signup only when the runtime can
309
+ immediately store the raw token once outside local config. For later commands,
310
+ saved config is the default; `IMAGE_SKILL_TOKEN` and `--token-stdin` remain
311
+ available for runtimes with a separate secret store.
312
+ `create --guide` also returns `data.auth_handoff` with copy-safe env/stdin
313
+ templates when auth is required or when the returned create command needs the
314
+ same auth context.
315
+
316
+ ### Local Config And Install
317
+
318
+ Prefer package execution in fresh agent sandboxes:
319
+
320
+ ```bash
321
+ npm_config_update_notifier=false npx -y luxin-cli@latest create --guide --prompt "a compact field camera on a stainless workbench" --json
322
+ ```
323
+
324
+ Global install is optional, not the primary path. If `npm install -g luxin-cli`
325
+ or `npx luxin-cli@latest ...` hits prefix/cache `EACCES`, retry with writable
326
+ package-manager paths instead of cloning private source:
327
+
328
+ ```bash
329
+ export npm_config_cache="${npm_config_cache:-$PWD/.npm-cache}"
330
+ export npm_config_prefix="${npm_config_prefix:-$PWD/.npm-global}"
331
+ export PATH="$npm_config_prefix/bin:$PATH"
332
+ npm_config_update_notifier=false npx -y luxin-cli@latest create --guide --prompt "a compact field camera on a stainless workbench" --json
333
+ ```
334
+
335
+ For `npx skills add ... -g -a codex` installs, the writable target is Codex
336
+ agent home rather than the npm prefix. If the Codex/global skill target is
337
+ read-only or cannot create its default directory, keep the tracked slug install
338
+ and point agent skill state at a writable workspace home. The skills.sh Codex
339
+ adapter writes to `$HOME/.agents`; `CODEX_HOME` keeps Codex profile state on the
340
+ same writable path:
341
+
342
+ ```bash
343
+ export HOME="$PWD/.agent-home"
344
+ export CODEX_HOME="$HOME/.codex"
345
+ mkdir -p "$HOME" "$CODEX_HOME"
346
+ npx skills add danielgwilson/luxin --full-depth --skill luxin -g -a codex -y
347
+ ```
348
+
349
+ Hosted signup saves auth state to the public CLI config by default. If the
350
+ runtime needs a writable compatibility config path, set
351
+ `LUXIN_CONFIG_PATH` before `signup`:
352
+
353
+ ```bash
354
+ export LUXIN_CONFIG_PATH="$PWD/.luxin/config.json"
355
+ # legacy name IMAGE_SKILL_CONFIG_PATH remains honored
356
+ npm_config_update_notifier=false npx -y luxin-cli@latest signup --agent \
357
+ --agent-name creative-agent \
358
+ --runtime codex \
359
+ --json
360
+ ```
361
+
362
+ Config write failures return `PUBLIC_CLI_CONFIG_WRITE_FAILED` with a structured
363
+ `error.recovery.suggested_command`. Agents should follow that recovery field,
364
+ then rerun `create --guide` for the requested creative flow.
365
+ When `create --guide` reaches `auth_required`, it probes the configured auth
366
+ path first. If local config cannot be written, `data.next_command` prefixes the
367
+ normal saved-config signup with
368
+ `LUXIN_CONFIG_PATH="$PWD/.luxin/config.json"`, so the first recovery
369
+ step still saves auth locally without exposing the one-time token.
370
+ `--show-token --no-save` and `data.auth_handoff.rerun_guide.with_stdin` remain
371
+ structured fallback paths for runtimes that intentionally use a separate secret
372
+ store.
373
+
374
+ ### `luxin whoami`
375
+
376
+ Shows current actor, organization, claim state, token class, and grants.
377
+ The JSON includes `session_origin` so a fresh agent can tell whether it is
378
+ unauthenticated, loaded a saved config, created the token in this process, or
379
+ is using hosted-session auth. `token_created_at` is the active token creation
380
+ timestamp when Luxin can prove it, otherwise `null`.
381
+
382
+ ```bash
383
+ luxin whoami --json
384
+ ```
385
+
386
+ Minimum success data:
387
+
388
+ ```json
389
+ {
390
+ "authenticated": true,
391
+ "session_origin": "loaded_from_config",
392
+ "token_status": "active",
393
+ "token_created_at": "2026-05-08T12:00:00.000Z"
394
+ }
395
+ ```
396
+
397
+ ### `luxin usage quota`
398
+
399
+ Canonical pre-spend check. Shows remaining credits, job limits, model limits,
400
+ and reset windows before create/edit.
401
+
402
+ ```bash
403
+ luxin usage quota --json
404
+ ```
405
+
406
+ `luxin quota --json` remains a compatibility alias.
407
+
408
+ Hosted API equivalent:
409
+
410
+ ```bash
411
+ curl -sS https://api.luxin.sh/v1/quota \
412
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN"
413
+ ```
414
+
415
+ ### `luxin credits methods`
416
+
417
+ Machine-readable payment rail discovery. Use this before quoting or buying so
418
+ agents can tell which rails are available, whether live money can move, whether
419
+ browser/human action is required, and which command to try next.
420
+
421
+ ```bash
422
+ luxin credits methods --json
423
+ ```
424
+
425
+ Minimum success data shape:
426
+
427
+ ```json
428
+ {
429
+ "contract_version": "image-skill.payment-methods.v1",
430
+ "credit_unit_cents": 1,
431
+ "currency": "USD",
432
+ "quote_endpoint": "/v1/credit-quotes",
433
+ "packs_endpoint": "/v1/credit-packs",
434
+ "status_endpoint": "/v1/credit-purchases/status",
435
+ "next_actions": {
436
+ "recommended_quote": {
437
+ "purpose": "quote_credit_top_up",
438
+ "recommendation_reason": "browserless_agent_self_fund",
439
+ "method_id": "stripe_x402.exact.usdc",
440
+ "pack_id": "starter-500",
441
+ "command": "luxin credits quote --pack starter-500 --payment-method stripe_x402.exact.usdc --json",
442
+ "quote_command_copy_runnable": true,
443
+ "buy_command": "luxin credits buy --provider stripe_x402 --quote-id QUOTE_ID --idempotency-key KEY --json",
444
+ "status_command": "luxin credits status --payment-attempt-id PAYMENT_ATTEMPT_ID --json",
445
+ "requires_auth": true,
446
+ "live_money": true,
447
+ "requires_browser": false,
448
+ "agent_settleable": true,
449
+ "command_effect": {
450
+ "label": "live_money_quote_no_charge",
451
+ "no_spend": true,
452
+ "payment_object": true,
453
+ "credit_debit": false,
454
+ "media_write": false
455
+ }
456
+ }
457
+ },
458
+ "methods": [
459
+ {
460
+ "method_id": "stripe_checkout",
461
+ "status": "available",
462
+ "available": true,
463
+ "quoteable": true,
464
+ "purchasable": true,
465
+ "live_money": true,
466
+ "buyer_modes": ["hybrid", "human_only"],
467
+ "requires_browser": true,
468
+ "agent_initiated": true,
469
+ "agent_settleable": false,
470
+ "settlement_blocker": "requires human browser checkout completion",
471
+ "default_pack_id": "starter-500",
472
+ "purchase_endpoint": "/v1/credit-purchases/stripe-checkout-sessions"
473
+ },
474
+ {
475
+ "method_id": "stripe_x402.exact.usdc",
476
+ "status": "available",
477
+ "available": true,
478
+ "quoteable": true,
479
+ "purchasable": true,
480
+ "live_money": true,
481
+ "buyer_modes": ["agent_only", "hybrid"],
482
+ "requires_browser": false,
483
+ "agent_initiated": true,
484
+ "agent_settleable": true,
485
+ "settlement_blocker": null,
486
+ "default_pack_id": "starter-500",
487
+ "purchase_endpoint": "/v1/credit-purchases/stripe-x402-deposits"
488
+ }
489
+ ]
490
+ }
491
+ ```
492
+
493
+ Public payment discovery is intentionally action-first. Limited-rollout rails
494
+ may be returned with `available:false`, `quoteable:false`, `purchasable:false`,
495
+ and a non-null `unavailable_reason` so headless agents can understand the path
496
+ without trying it. Use a method only when it is returned with `available:true`,
497
+ `quoteable:true`, and `purchasable:true`. When
498
+ `next_actions.recommended_quote` is present, prefer its `command` as the next
499
+ authenticated quote step; it creates a live-money payment object but does not
500
+ move money, grant credits, debit credits, call a provider, or write media.
501
+
502
+ Hosted API equivalent:
503
+
504
+ ```bash
505
+ curl -sS https://api.luxin.sh/v1/payment-methods
506
+ ```
507
+
508
+ ### `luxin credits packs list`
509
+
510
+ Lists the recommended Luxin credit packs. Packs are the default
511
+ live-money buying UX because agents get obvious starter choices and avoid tiny
512
+ fee traps. Use the payment method catalog to choose the rail:
513
+ `stripe_checkout` when a human sponsor can complete Checkout, or
514
+ `stripe_x402.exact.usdc` when a wallet-equipped agent can settle a browserless
515
+ live crypto deposit attempt from returned pay-to instructions.
516
+ Exact custom quotes are still supported when an agent already knows the
517
+ required credit budget.
518
+
519
+ ```bash
520
+ luxin credits packs list --json
521
+ ```
522
+
523
+ Minimum success data:
524
+
525
+ ```json
526
+ {
527
+ "credit_unit_cents": 1,
528
+ "credit_unit_usd": 0.01,
529
+ "currency": "USD",
530
+ "default_pack_id": "starter-500",
531
+ "packs": [
532
+ {
533
+ "pack_id": "starter-500",
534
+ "name": "Starter",
535
+ "display_name": "Starter (500 credits)",
536
+ "credits": 500,
537
+ "amount_cents": 500,
538
+ "amount_usd": "5.00",
539
+ "price_usd": "5.00",
540
+ "currency": "USD"
541
+ }
542
+ ],
543
+ "custom_quotes": {
544
+ "supported": true,
545
+ "min_credits": 1,
546
+ "max_credits": 5000
547
+ }
548
+ }
549
+ ```
550
+
551
+ Hosted API equivalent:
552
+
553
+ ```bash
554
+ curl -sS https://api.luxin.sh/v1/credit-packs
555
+ ```
556
+
557
+ ### `luxin credits quote`
558
+
559
+ Requests a bounded credit quote from the hosted service. Public top-ups use the
560
+ payment method returned by `credits methods --json`: `stripe_checkout` for the
561
+ human Checkout path, or `stripe_x402.exact.usdc` for a browserless
562
+ action-required deposit attempt. A quote never grants credits.
563
+ One Luxin credit is a stable user-facing value unit worth `$0.01`.
564
+ Creative operations can consume more than one credit based on the selected
565
+ model's provider cost and Luxin's margin policy; inspect
566
+ `models show MODEL_ID --json` and operation `cost.credit_pricing` for the exact
567
+ debit before spending.
568
+
569
+ ```bash
570
+ luxin credits quote --credits 10 --payment-method stripe_x402.exact.usdc --json
571
+ ```
572
+
573
+ Always pass the payment method from `credits methods --json`; the public CLI
574
+ does not infer one. For retry-stable automation, provide an explicit non-secret
575
+ idempotency key:
576
+
577
+ ```bash
578
+ luxin credits quote \
579
+ --credits 10 \
580
+ --payment-method stripe_x402.exact.usdc \
581
+ --idempotency-key quote-run-001 \
582
+ --json
583
+ ```
584
+
585
+ Idempotency keys are scoped to the current hosted agent identity and exact
586
+ quote request. Reusing a key with different credits, pack, or payment method
587
+ returns a structured `error.recovery.suggested_command` with a fresh
588
+ idempotency key for the attempted quote terms.
589
+
590
+ For Stripe Checkout terms, prefer a named pack:
591
+
592
+ ```bash
593
+ luxin credits quote \
594
+ --pack starter-500 \
595
+ --payment-method stripe_checkout \
596
+ --idempotency-key stripe-pack-quote-run-001 \
597
+ --json
598
+ ```
599
+
600
+ For the browserless agent x402 rail, quote the exact method id returned by
601
+ `credits methods --json`:
602
+
603
+ ```bash
604
+ luxin credits quote \
605
+ --pack starter-500 \
606
+ --payment-method stripe_x402.exact.usdc \
607
+ --idempotency-key agent-x402-quote-run-001 \
608
+ --json
609
+ ```
610
+
611
+ For exact custom terms, keep the same rail choice. Use
612
+ `stripe_x402.exact.usdc` for an agent-settleable browserless rail, or use
613
+ `stripe_checkout` only for a human Checkout fallback:
614
+
615
+ ```bash
616
+ luxin credits quote \
617
+ --credits 137 \
618
+ --payment-method stripe_checkout \
619
+ --idempotency-key exact-quote-run-001 \
620
+ --json
621
+ ```
622
+
623
+ Minimum success data:
624
+
625
+ ```json
626
+ {
627
+ "quote_id": "quote_...",
628
+ "state": "created",
629
+ "credits": 10,
630
+ "price_amount_cents": 10,
631
+ "currency": "USD",
632
+ "expires_at": "2026-05-08T20:00:00.000Z",
633
+ "accepted_payment_method": "stripe_checkout",
634
+ "idempotency_key": "quote-run-001",
635
+ "pack_id": null,
636
+ "pack": null,
637
+ "live_money": true,
638
+ "next_actions": {
639
+ "recommended_buy": {
640
+ "purpose": "complete_credit_top_up",
641
+ "recommendation_reason": "human_checkout_handoff",
642
+ "quote_id": "quote_...",
643
+ "method_id": "stripe_checkout",
644
+ "provider": "stripe",
645
+ "command": "luxin credits buy --provider stripe --quote-id quote_... --idempotency-key purchase:quote_... --json",
646
+ "buy_command_copy_runnable": true,
647
+ "purchase_idempotency_key": "purchase:quote_...",
648
+ "status_command": "luxin credits status --quote-id quote_... --json",
649
+ "status_command_copy_runnable": true,
650
+ "status_command_after_payment": "luxin credits status --payment-attempt-id PAYMENT_ATTEMPT_ID --json",
651
+ "status_command_after_payment_copy_runnable": false,
652
+ "requires_auth": true,
653
+ "live_money": true,
654
+ "requires_browser": true,
655
+ "agent_settleable": false,
656
+ "human_handoff_required": true,
657
+ "command_effect": {
658
+ "label": "live_money_payment_attempt_requires_completion",
659
+ "no_spend": false,
660
+ "payment_attempt": true,
661
+ "credit_debit": false,
662
+ "media_write": false,
663
+ "wallet_settlement": false
664
+ }
665
+ }
666
+ }
667
+ }
668
+ ```
669
+
670
+ For x402 quotes, `accepted_payment_method` is
671
+ `"stripe_x402.exact.usdc"`. The quote does not grant credits or include pay-to
672
+ instructions; `data.next_actions.recommended_buy.command` carries the returned
673
+ `quote_id` and a stable non-secret purchase idempotency key for the
674
+ action-required deposit attempt. Run it only when delegated spend is allowed.
675
+ Use `data.next_actions.recommended_buy.status_command` to inspect the quote or
676
+ payment state by `quote_id`; after buy returns a `payment_attempt_id`, prefer
677
+ `status_command_after_payment`.
678
+
679
+ When `credits status --quote-id quote_... --json` sees an open
680
+ `stripe_x402.exact.usdc` or `stripe_checkout` quote, it repeats the exact
681
+ `data.next_actions.recommended_buy` command with the real `quote_id` and
682
+ `purchase:quote_...` idempotency key so agents do not need to reconstruct the
683
+ buy step from placeholders.
684
+
685
+ Hosted API equivalent:
686
+
687
+ ```bash
688
+ curl -sS https://api.luxin.sh/v1/credit-quotes \
689
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN" \
690
+ -H "content-type: application/json" \
691
+ -d '{"pack_id":"starter-500","payment_method":"stripe_x402.exact.usdc","idempotency_key":"agent-x402-quote-run-001"}'
692
+ ```
693
+
694
+ ### `luxin credits buy`
695
+
696
+ Creates a payment action for a previously returned quote. Choose the provider
697
+ that matches the quote's `accepted_payment_method`.
698
+
699
+ For a `stripe_x402.exact.usdc` quote, `--provider stripe_x402` creates a
700
+ browserless action-required USDC deposit attempt. When the response includes
701
+ `stripe_x402.payable_instructions`, a wallet-equipped agent may pay the exact
702
+ USDC amount to `deposit_address` on Base without using a browser. The response
703
+ is live money when `live_money:true`. Credits are granted only after verified
704
+ settlement and webhook fulfillment succeeds. Deposit challenge creation itself
705
+ must not mutate credit balances. Stay within the delegated cap and never pass
706
+ wallet private keys, seed phrases, x402 payment headers, deposit client
707
+ secrets, card data, Stripe secrets, or provider receipts to Luxin.
708
+ When `create --guide` enters `quota_required` for this rail, it returns
709
+ `data.self_fund_handoff.wallet_settlement` with the buy/status response fields
710
+ to inspect, plus `data.self_fund_handoff.first_safe_command` for no-spend rail
711
+ inspection before quote/buy: `data.stripe_x402.payable_instructions` after
712
+ `credits buy`, or
713
+ `data.payment_attempt.stripe_x402.payable_instructions` after `credits status`.
714
+
715
+ ```bash
716
+ luxin credits buy \
717
+ --provider stripe_x402 \
718
+ --quote-id quote_... \
719
+ --idempotency-key agent-x402-buy-run-001 \
720
+ --json
721
+ ```
722
+
723
+ Minimum x402 action-required data:
724
+
725
+ ```json
726
+ {
727
+ "state": "action_required",
728
+ "quote_id": "quote_...",
729
+ "payment_attempt_id": "payatt_...",
730
+ "provider": "stripe",
731
+ "accepted_payment_method": "stripe_x402.exact.usdc",
732
+ "credits": 500,
733
+ "amount_cents": 500,
734
+ "currency": "USD",
735
+ "live_money": true,
736
+ "stripe_x402": {
737
+ "method_id": "stripe_x402.exact.usdc",
738
+ "scheme": "exact",
739
+ "network": "base",
740
+ "token_currency": "usdc",
741
+ "deposit_address_present": true,
742
+ "payable_instructions": {
743
+ "kind": "stripe_crypto_deposit",
744
+ "network": "base",
745
+ "token_currency": "usdc",
746
+ "token_decimals": 6,
747
+ "token_amount": "5.00",
748
+ "token_amount_atomic": "5000000",
749
+ "amount_cents": 500,
750
+ "amount_usd": "5.00",
751
+ "deposit_address": "0x...",
752
+ "token_contract_address": "0x...",
753
+ "supported_token_currencies": ["usdc"],
754
+ "expires_at": "2026-05-08T20:00:00.000Z",
755
+ "exact_amount_required": true
756
+ },
757
+ "redacted": {
758
+ "payment_intent_id": "[redacted-stripe-payment-intent]",
759
+ "deposit_address": "[redacted-stripe-crypto-deposit-address]",
760
+ "client_secret": "[redacted-stripe-client-secret]"
761
+ }
762
+ },
763
+ "next_actions": {
764
+ "recommended_settlement": {
765
+ "purpose": "settle_stripe_x402_credit_top_up",
766
+ "quote_id": "quote_...",
767
+ "payment_attempt_id": "payatt_...",
768
+ "method_id": "stripe_x402.exact.usdc",
769
+ "payable_instructions_path": "data.stripe_x402.payable_instructions",
770
+ "status_command": "luxin credits status --payment-attempt-id payatt_... --json",
771
+ "quota_command": "luxin usage quota --json",
772
+ "command_effect": {
773
+ "wallet_settlement": true,
774
+ "credit_debit": false,
775
+ "media_write": false
776
+ }
777
+ }
778
+ },
779
+ "next": {
780
+ "agent_action": "pay_stripe_crypto_deposit",
781
+ "suggested_commands": [
782
+ "luxin credits status --payment-attempt-id payatt_... --json"
783
+ ]
784
+ }
785
+ }
786
+ ```
787
+
788
+ For a `stripe_checkout` quote, `--provider stripe` creates a hosted Stripe
789
+ Checkout Session and returns an `action_required` response with
790
+ `checkout_handoff_url`.
791
+
792
+ Agents should present or open `checkout_handoff_url` for humans. It is a short
793
+ Luxin URL that redirects to Stripe Checkout and is safe to copy from
794
+ mobile terminals, SSH clients, and wrapped chat output. `checkout_compact_url`
795
+ is also copy-safe and equals the Luxin handoff when the hosted API can
796
+ provide one. `checkout_url` is the raw Stripe compatibility fallback only; do
797
+ not present it unless no handoff URL is available. Do not trim Stripe Checkout
798
+ URLs: the long `#...` fragment is required by Stripe Checkout in the browser.
799
+ Present any fallback Stripe URL in a fenced code block so terminal wrapping does
800
+ not corrupt it. Stripe-hosted Checkout may also show a promotion-code field for
801
+ operator-provided codes; agents should let the human enter those codes on
802
+ Stripe, never collect promo codes, card details, or wallet credentials in the
803
+ Luxin CLI.
804
+
805
+ ```bash
806
+ luxin credits buy \
807
+ --provider stripe \
808
+ --quote-id quote_... \
809
+ --idempotency-key stripe-buy-run-001 \
810
+ --json
811
+ ```
812
+
813
+ Minimum success data:
814
+
815
+ ```json
816
+ {
817
+ "state": "action_required",
818
+ "quote_id": "quote_...",
819
+ "payment_attempt_id": "payatt_...",
820
+ "provider": "stripe",
821
+ "accepted_payment_method": "stripe_checkout",
822
+ "checkout_session_id": "cs_...",
823
+ "checkout_handoff_url": "https://api.luxin.sh/pay/payatt_...",
824
+ "checkout_compact_url": "https://api.luxin.sh/pay/payatt_...",
825
+ "checkout_url": "https://checkout.stripe.com/c/pay/cs_...#fid...",
826
+ "credits": 500,
827
+ "amount_cents": 500,
828
+ "currency": "USD",
829
+ "live_money": true,
830
+ "next": {
831
+ "human_action": "open_checkout_url",
832
+ "checkout_handoff_url": "https://api.luxin.sh/pay/payatt_...",
833
+ "checkout_compact_url": "https://api.luxin.sh/pay/payatt_...",
834
+ "fallback_checkout_url": "https://checkout.stripe.com/c/pay/cs_...#fid...",
835
+ "after_payment": "open checkout_handoff_url or checkout_compact_url; use the full checkout_url only if no Luxin handoff URL is available, and preserve its Stripe # fragment. Then poll luxin credits status --payment-attempt-id PAYMENT_ATTEMPT_ID --json or luxin usage quota --json; credits are granted only after verified webhook fulfillment"
836
+ }
837
+ }
838
+ ```
839
+
840
+ Hosted API equivalent:
841
+
842
+ ```bash
843
+ curl -sS https://api.luxin.sh/v1/credit-purchases/stripe-checkout-sessions \
844
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN" \
845
+ -H "content-type: application/json" \
846
+ -d '{"quote_id":"quote_...","idempotency_key":"stripe-buy-run-001"}'
847
+ ```
848
+
849
+ x402 hosted API equivalent:
850
+
851
+ ```bash
852
+ curl -sS https://api.luxin.sh/v1/credit-purchases/stripe-x402-deposits \
853
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN" \
854
+ -H "content-type: application/json" \
855
+ -d '{"quote_id":"quote_...","idempotency_key":"agent-x402-buy-run-001"}'
856
+ ```
857
+
858
+ ### `luxin credits status`
859
+
860
+ Without a payment reference, shows the current credit balance using the same
861
+ quota data as `luxin usage quota --json`. With a payment reference,
862
+ shows the durable state of a quote, x402 deposit attempt, Stripe Checkout
863
+ attempt, Checkout Session, or receipt. Use the referenced form after
864
+ `credits buy` so agents do not have to infer payment state from quota deltas
865
+ or activity text.
866
+
867
+ ```bash
868
+ luxin credits status --json
869
+ luxin credits status \
870
+ --payment-attempt-id payatt_... \
871
+ --json
872
+ ```
873
+
874
+ At most one reference flag is allowed: `--quote-id`,
875
+ `--payment-attempt-id`, `--checkout-session-id`, or `--receipt-id`. Passing no
876
+ reference returns the balance/quota state.
877
+
878
+ For Stripe x402 attempts that are still `action_required`, status responses
879
+ include `data.next_actions.recommended_settlement` with the same exact Base/USDC
880
+ payable instructions, a copy-runnable status command for the real
881
+ `payment_attempt_id`, and explicit effect flags showing this is wallet
882
+ settlement, not credit debit, media write, or provider generation work.
883
+
884
+ Minimum action-required data:
885
+
886
+ ```json
887
+ {
888
+ "state": "action_required",
889
+ "quote": {
890
+ "quote_id": "quote_...",
891
+ "credits": 500,
892
+ "price_amount_cents": 500,
893
+ "accepted_payment_method": "stripe_checkout",
894
+ "pack_id": "starter-500",
895
+ "x402": null
896
+ },
897
+ "payment_attempt": {
898
+ "payment_attempt_id": "payatt_...",
899
+ "checkout_session_id": "cs_...",
900
+ "checkout_handoff_url": "https://api.luxin.sh/pay/payatt_...",
901
+ "checkout_compact_url": "https://api.luxin.sh/pay/payatt_...",
902
+ "checkout_url": "https://checkout.stripe.com/c/pay/cs_...#fid...",
903
+ "attempt_status": "requires_action"
904
+ },
905
+ "receipt": null,
906
+ "credit_event": null,
907
+ "next": {
908
+ "retry_after_seconds": 10,
909
+ "human_action": "open_checkout_url",
910
+ "checkout_handoff_url": "https://api.luxin.sh/pay/payatt_...",
911
+ "checkout_compact_url": "https://api.luxin.sh/pay/payatt_..."
912
+ }
913
+ }
914
+ ```
915
+
916
+ Minimum success data includes `state: "succeeded"`, `receipt`,
917
+ `credit_event`, and the updated hosted `limits`.
918
+
919
+ Hosted API equivalent:
920
+
921
+ ```bash
922
+ curl -sS "https://api.luxin.sh/v1/credit-purchases/status?payment_attempt_id=payatt_..." \
923
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN"
924
+ ```
925
+
926
+ Do not pass card data, wallet secrets, provider receipts, Stripe secrets, MPP
927
+ tokens, SPTs, live x402 payment headers, deposit client secrets, wallet
928
+ private keys, seed phrases, or any payment credential to credits commands.
929
+ Stripe Checkout collects payment details only on Stripe-hosted pages; x402
930
+ settlement is handled by the agent/wallet against the returned redacted deposit
931
+ challenge, not by pasting credentials into Luxin. The public request
932
+ fields are `credits`, `pack_id`, `payment_method`, `quote_id`, status reference
933
+ IDs, and `idempotency_key`.
934
+
935
+ ### `luxin models`
936
+
937
+ First-run creative discovery. Lists public models and shows the full
938
+ capability-preserving schema for one model.
939
+
940
+ ```bash
941
+ luxin models --json
942
+ luxin models list --json
943
+ luxin models list --details --json
944
+ luxin models list --available --operation image.generate --json
945
+ luxin models list --available --operation image.edit --json
946
+ luxin models list --available --modality video --operation video.generate --json
947
+ luxin models list --query nano-banana --json
948
+ luxin models list --catalog-only --provider fal --json
949
+ luxin models show MODEL_ID --json
950
+ luxin models show nano-banana --json
951
+ luxin models show default --json
952
+ ```
953
+
954
+ Hosted API equivalents:
955
+
956
+ ```bash
957
+ curl -sS https://api.luxin.sh/v1/models
958
+ curl -sS 'https://api.luxin.sh/v1/models?available=true&modality=video&operation=video.generate'
959
+ curl -sS 'https://api.luxin.sh/v1/models?query=nano-banana'
960
+ curl -sS https://api.luxin.sh/v1/models/xai.grok-imagine-image
961
+ ```
962
+
963
+ `models show` exposes operation support, media input/output types, parameter
964
+ schemas, defaults and fixed controls, cost and latency class, safety behavior,
965
+ and migration hints. Agents should inspect it before assuming a model supports
966
+ seeds, masks, reference images, transparent backgrounds, arbitrary aspect
967
+ ratios, image-size presets, output counts, resolution controls, safety
968
+ controls, or provider-native options.
969
+
970
+ `models list` is executable-first by default and returns one
971
+ `default: true` row when the default executable create model is included.
972
+ `models show default --json` resolves to that model so first-run agents can
973
+ inspect the recommended create surface without knowing a provider-specific
974
+ model id. `models show` also resolves common migration aliases such as
975
+ `nano-banana` to `fal.nano-banana-2`, while `models list --query QUERY --json`
976
+ returns fuzzy provider/model-family matches before an agent commits to one
977
+ model id. The list response also returns `summary` with total, returned,
978
+ available, executable, catalog-only, provider split,
979
+ `execution_availability`, first actionable model ids, recommended filter
980
+ commands, alias hints, provider/category discovery groups, and
981
+ catalog-inclusion flags. Default list output is a compact, sortable model menu
982
+ and excludes catalog-only rows so fresh agents see executable candidates first.
983
+ Each row keeps the model id, flat
984
+ `estimated_usd_per_image`, `credits_required`, lightweight `task_tags`, status,
985
+ provider, max output count/resolution, storage, and `show_command`, while
986
+ omitting full parameter schemas. Use `models show MODEL_ID --json` for one
987
+ model's full capability schema, or `models list --details --json` only when you
988
+ intentionally need the full list with capability schemas for compatibility or
989
+ offline analysis. `--summary` is still accepted as a compatibility alias for the
990
+ default compact list. Use `--available` for currently usable executable rows,
991
+ `--modality image|video|audio|3d` for media type, `--operation
992
+ image.generate`, `--operation image.edit`, `--operation video.generate`,
993
+ `--operation audio.generate`, or `--operation 3d.generate` for the task,
994
+ `--provider fal|xai|openai` to narrow by provider, `--query QUERY` to search
995
+ ids, display names, provider namespaces, operations, tags, intents, upstream
996
+ Fal endpoint ids, and known aliases, and `--catalog-only` when you intentionally
997
+ want source-backed rows that are inspectable but not runnable yet.
998
+ Provider-level availability is not the same thing as model executability; for
999
+ runnable choices require both `status:"available"` and
1000
+ `execution.model_execution_status:"executable"`. If a reachable provider has no
1001
+ runnable model for the requested operation, `summary.execution_availability`
1002
+ says so directly and includes the fastest `--available --operation ...`
1003
+ recovery command.
1004
+
1005
+ Luxin standardizes common controls so agents can work quickly, but it
1006
+ must not flatten rich model capabilities into coarse universal categories.
1007
+ Use `model_parameters` for rare or model-specific parameters advertised by the
1008
+ capability schema.
1009
+
1010
+ Current executable provider-native controls include:
1011
+
1012
+ - Fal FLUX.1 dev: `model_parameters.image_size` for presets such as
1013
+ `square_hd`, plus `seed`.
1014
+ - Fal FLUX Pro 1.1 Ultra Create: `model_parameters.seed` and
1015
+ `model_parameters.raw`; optional reference-image controls remain cataloged
1016
+ for inspection but are not executable on the create-only path.
1017
+ - Fal Z-Image Turbo Create/Edit: `model_parameters.image_size` for
1018
+ `square_hd`, `square`, portrait/landscape presets, and `auto` on edit; costs
1019
+ are quoted from requested megapixels when the output size is explicit.
1020
+ - Fal Nano Banana 2 Edit: `model_parameters.resolution` for `0.5K`, `1K`,
1021
+ `2K`, and `4K`, plus `seed`.
1022
+ - Fal Ideogram V2 Edit: `model_parameters.expand_prompt`, `seed`, and
1023
+ `style`; pass masks as top-level `--mask` / `mask_asset_id`, not as
1024
+ provider `mask_url`.
1025
+ - Fal Gemini 3 Pro Image Preview Create/Edit:
1026
+ `model_parameters.resolution` for `1K`, `2K`, and `4K`, plus `seed`; 4K is
1027
+ quoted as the higher-priced provider tier.
1028
+ - Fal Nano Banana Pro Create/Edit: `model_parameters.resolution` for `1K`,
1029
+ `2K`, and `4K`, plus `seed`; 4K is quoted as the higher-priced provider tier.
1030
+ - Fal FLUX Pro Kontext Pro/Max Edit: `model_parameters.seed`; guidance scale
1031
+ and aspect-ratio controls remain cataloged for inspection but are not
1032
+ executable until their UX and receipt behavior are represented.
1033
+ - Fal Bytedance Seedream 4.5 Create/Edit: `model_parameters.image_size` for
1034
+ `square_hd`, `square`, portrait/landscape presets, `auto_2K`, and
1035
+ `auto_4K`, plus `seed`; multi-output and multi-reference controls remain
1036
+ cataloged but fixed for hosted accounting.
1037
+ - Fal Bytedance Seedream 5.0 Lite Create/Edit:
1038
+ `model_parameters.image_size` for `square_hd`, `square`, portrait/landscape
1039
+ presets, `auto_2K`, and `auto_3K`; multi-output and multi-reference controls
1040
+ remain cataloged but fixed for hosted accounting.
1041
+ - xAI Grok Imagine Image Quality: `model_parameters.resolution` for `1k` and
1042
+ `2k`; 2k is priced from the higher provider tier. Create supports top-level
1043
+ `--output-count` up to the model's advertised `max_outputs_per_request`,
1044
+ currently mapped to xAI's documented `n` batch parameter.
1045
+ - GPT Image 1.5 create/edit: documented fixed sizes `1024x1024`,
1046
+ `1024x1536`, and `1536x1024`, output format, compression, transparent or
1047
+ opaque background, moderation, and the upstream provider-native quality
1048
+ parameter. GPT Image 1.5 create quotes output-token estimates when quality
1049
+ and concrete size are known; GPT Image 1.5 create supports top-level
1050
+ `--output-count` up to the model's advertised `max_outputs_per_request`,
1051
+ currently mapped to OpenAI's `n` parameter. GPT Image 1.5 edit accepts
1052
+ low/high `input_fidelity` and remains preflight unknown-cost until usage is
1053
+ returned.
1054
+ - GPT Image 2 create/edit: size, output format, compression, background,
1055
+ moderation, and the upstream provider-native quality parameter. GPT Image 2
1056
+ create quotes request-aware output-token estimates when quality and concrete
1057
+ size are known; GPT Image 2 create supports top-level `--output-count` up to
1058
+ the model's advertised `max_outputs_per_request`, currently mapped to
1059
+ OpenAI's `n` parameter. GPT Image 2 edit remains preflight unknown-cost, then
1060
+ records usage-priced provider cost when OpenAI returns token usage.
1061
+
1062
+ Inspect each model before use; provider-native controls are available only
1063
+ through validated `model_parameters`.
1064
+
1065
+ ### `luxin capabilities`
1066
+
1067
+ Schema-language view over the same capability catalog. Use this when you need
1068
+ the capability abstraction directly rather than starting from a model.
1069
+
1070
+ ```bash
1071
+ luxin capabilities --json
1072
+ luxin capabilities list --json
1073
+ luxin capabilities show CAPABILITY_ID --json
1074
+ ```
1075
+
1076
+ ### `luxin create`
1077
+
1078
+ Guides, creates, or plans a zero-cost dry run.
1079
+
1080
+ Guide the first image path without mutation:
1081
+
1082
+ ```bash
1083
+ luxin create --guide --prompt "A compact field camera on a stainless workbench" --json
1084
+ ```
1085
+
1086
+ `create --guide` returns `schema: image-skill.create-guide.v1`,
1087
+ `stage`, `next_command`, `next_command_copy_runnable`,
1088
+ `next_command_missing_inputs`, `guide_warning`, `auth_ready`,
1089
+ `no_spend_evaluation`, `recommended_no_spend_command`,
1090
+ `self_fund_next_command`, `self_fund_handoff`, `self_fund_preparation`,
1091
+ `escape_hatches`, selected executable model and cost, auth/quota/payment
1092
+ blockers, and mutation flags. All
1093
+ mutation flags must be false in guide mode: no provider call, hosted create,
1094
+ signup, payment object, credit debit, or media write.
1095
+ For next-command safety, read `next_command_copy_runnable`,
1096
+ `next_command_missing_inputs`, and `guide_warning.next_command_safety` before
1097
+ acting: `hosted_signup_no_spend_setup` is no-spend auth setup,
1098
+ `live_money_payment_action` is top-up/payment work, and
1099
+ `live_media_create_credit_debit` is a live create that can call a provider,
1100
+ debit credits, and write media.
1101
+ For payment state, read
1102
+ `checks.payments.preferred_method_summary.top_up_path` instead of inferring
1103
+ from several arrays. It is `browserless_agent_self_fund` when the preferred
1104
+ rail is live money, browserless, agent initiated, and agent settleable;
1105
+ `human_payment_handoff` when a human/browser completion step is required; and
1106
+ `payment_method_inspection` when the guide cannot classify a direct top-up path.
1107
+ In guide cost output, `cost.estimated_usd_per_image` is the estimated Image
1108
+ Skill debit in dollars for one output, matching
1109
+ `cost.estimated_debit_usd_per_image` and
1110
+ `cost.estimated_credits * cost.credit_unit_usd` when credit pricing is known.
1111
+ `cost.estimated_provider_usd_per_image` is the upstream provider estimate for
1112
+ transparency; do not use it as the amount the agent needs to fund.
1113
+
1114
+ ```bash
1115
+ luxin create \
1116
+ --prompt "A compact field camera on a stainless workbench" \
1117
+ --intent explore \
1118
+ --aspect-ratio 1:1 \
1119
+ --max-estimated-usd-per-image 0.07 \
1120
+ --json
1121
+ ```
1122
+
1123
+ Hosted defaults are quality-first. If an agent does not choose a model, Image
1124
+ Skill selects the strongest available create capability for the requested
1125
+ intent and budget, then records the decision in `request.selection`. Explicit
1126
+ `--provider`, `--model`, namespaced model ids, and validated
1127
+ `model_parameters` always take precedence. For final/product/hero-style
1128
+ intents, Luxin may default an eligible quality-capability request to a
1129
+ higher output tier only when `--max-estimated-usd-per-image` is high enough for
1130
+ that tier; otherwise it stays on a lower-cost quality tier or chooses a cheaper
1131
+ capability within the budget and tells agents what happened in the selection
1132
+ receipt. Use the `--max-estimated-usd-per-image` value returned by
1133
+ `create --guide`; it is sized to the Luxin credit debit, not only the
1134
+ upstream provider estimate.
1135
+
1136
+ Preview-compatible richer shape:
1137
+
1138
+ ```bash
1139
+ luxin create \
1140
+ --prompt "Campaign-ready product image of a compact field camera" \
1141
+ --intent finalize \
1142
+ --model MODEL_ID \
1143
+ --aspect-ratio 1:1 \
1144
+ --output-count 2 \
1145
+ --max-estimated-usd-per-image 0.07 \
1146
+ --model-parameters-json '{"seed":1234}' \
1147
+ --json
1148
+ ```
1149
+
1150
+ Use `--output-count N` only when `models show MODEL_ID --json` advertises
1151
+ `media.output.max_outputs_per_request` greater than `1`. `--output-count` is a
1152
+ top-level Luxin create control; do not pass provider-native `n` through
1153
+ `model_parameters` unless the selected model schema explicitly advertises that
1154
+ field. Credit pricing and `cost.credit_pricing.credits_required` are total
1155
+ operation debits across all requested outputs. `--max-estimated-usd-per-image`
1156
+ and raw API `max_estimated_usd_per_image` are per-image Luxin debit
1157
+ budget guards.
1158
+
1159
+ Generate video through the same `create` command and durable-media loop. For
1160
+ video prompts, run `luxin create --guide --prompt "..." --json`; the guide
1161
+ can select the executable video model, suggest `--aspect-ratio 16:9`, and emit
1162
+ the next create command. Plain `create` without a model still defaults to an
1163
+ image model, so use the guide or pass the video model id directly. The response
1164
+ returns a durable owned `video_...` mp4 asset URL, a `job_id`, and a
1165
+ `cost.credit_pricing` receipt just like an image create.
1166
+
1167
+ ```bash
1168
+ luxin create \
1169
+ --model fal.ltx-video-13b-distilled \
1170
+ --prompt "A slow dolly push-in on a steaming espresso cup on a cafe counter, morning light" \
1171
+ --aspect-ratio 16:9 \
1172
+ --json
1173
+ ```
1174
+
1175
+ Discover runnable video models with `luxin models list --available
1176
+ --modality video --operation video.generate --json`. Inspect parameters, output
1177
+ media type, and cost first with `luxin models show
1178
+ fal.ltx-video-13b-distilled --json`. Video runs synchronously through the same
1179
+ create call and can take longer than an image; the returned `assets[].url` is an
1180
+ owned `video/mp4`.
1181
+
1182
+ Generate audio (music, sound) through the same `create` command and
1183
+ durable-media loop. Request an audio model by id; the response returns a durable
1184
+ owned `audio_...` wav asset URL, a `job_id`, and a `cost.credit_pricing` receipt
1185
+ just like an image create. Audio has no aspect ratio, so do not pass
1186
+ `--aspect-ratio`.
1187
+
1188
+ ```bash
1189
+ luxin create \
1190
+ --model fal.stable-audio-25-text-to-audio \
1191
+ --prompt "A warm lo-fi hip-hop loop with vinyl crackle and a mellow Rhodes piano" \
1192
+ --json
1193
+ ```
1194
+
1195
+ `fal.stable-audio-25-text-to-audio` (Stable Audio 2.5) is text-to-audio at a flat
1196
+ $0.20/clip (about 34 credits, quoted before spend) and returns an owned
1197
+ `audio/wav` clip. The first slice is defaults-only (no tunable
1198
+ `model_parameters`); duration/steps controls are a later milestone. Inspect
1199
+ parameters, output media type, and cost first with `luxin models show
1200
+ fal.stable-audio-25-text-to-audio --json`. Audio runs synchronously through the
1201
+ same create call and can take longer than an image.
1202
+
1203
+ For create models with wired reference support, pass owned reference assets
1204
+ with the model's advertised reference role. Kling element routes use
1205
+ `--element-frontal IMAGE[@ELEMENT_INDEX]` and
1206
+ `--element-reference IMAGE[@ELEMENT_INDEX[:REFERENCE_INDEX]]`; flat
1207
+ reference-image routes use `--reference-image IMAGE[@INDEX]`; Fal DreamO also
1208
+ accepts `:TASK` where `TASK` is `ip`, `id`, or `style`. The public CLI uploads
1209
+ local paths and external URLs first, then
1210
+ sends top-level `references[]` entries with Luxin `asset_id` values to
1211
+ `/v1/create`. Do not pass provider-native `elements`, `frontal_image_url`,
1212
+ `reference_image_urls`, `first_image_url`, `second_image_url`, `images`, or
1213
+ `*_reference_task` through `model_parameters`; provider-private URLs are
1214
+ resolved server-side after ownership and media-policy validation.
1215
+
1216
+ ```bash
1217
+ luxin create \
1218
+ --model fal.kling-image-o3-text-to-image \
1219
+ --prompt "Place the same character in a clean studio campaign" \
1220
+ --element-frontal ./character-front.png@0 \
1221
+ --element-reference ./character-side.webp@0:0 \
1222
+ --output-count 2 \
1223
+ --max-estimated-usd-per-image 0.06 \
1224
+ --json
1225
+ ```
1226
+
1227
+ ```bash
1228
+ luxin create \
1229
+ --model fal.dreamo \
1230
+ --prompt "Studio portrait preserving identity with a bolder editorial style" \
1231
+ --reference-image ./identity.png@0:id \
1232
+ --reference-image ./style.webp@1:style \
1233
+ --model-parameters-json '{"image_size":{"width":1280,"height":720}}' \
1234
+ --max-estimated-usd-per-image 0.06 \
1235
+ --json
1236
+ ```
1237
+
1238
+ High-resolution examples:
1239
+
1240
+ ```bash
1241
+ luxin create \
1242
+ --prompt "Campaign-ready product image of a compact field camera" \
1243
+ --intent final \
1244
+ --max-estimated-usd-per-image 0.07 \
1245
+ --json
1246
+
1247
+ luxin create \
1248
+ --prompt "Campaign-ready product image of a compact field camera" \
1249
+ --model fal.gemini-3-pro-image-preview \
1250
+ --model-parameters-json '{"resolution":"4K"}' \
1251
+ --max-estimated-usd-per-image 0.30 \
1252
+ --json
1253
+
1254
+ luxin create \
1255
+ --prompt "Campaign-ready product image of a compact field camera" \
1256
+ --model xai.grok-imagine-image-quality \
1257
+ --model-parameters-json '{"resolution":"2k"}' \
1258
+ --max-estimated-usd-per-image 0.07 \
1259
+ --json
1260
+
1261
+ luxin edit \
1262
+ --input-asset-id image_... \
1263
+ --prompt "preserve the subject and make this campaign-ready" \
1264
+ --model fal.nano-banana-2-edit \
1265
+ --model-parameters-json '{"resolution":"4K"}' \
1266
+ --accept-unknown-cost \
1267
+ --json
1268
+ ```
1269
+
1270
+ `model_parameters` must be validated against the selected model/capability
1271
+ schema before any provider call or paid reservation. Unknown fields fail closed
1272
+ unless the capability explicitly allows additional properties. This is how
1273
+ Luxin preserves rare model controls without turning every
1274
+ provider-specific parameter into a top-level flag.
1275
+ In the current preview, Fal create/edit, xAI quality generation, and OpenAI GPT
1276
+ Image 2 expose the executable provider-native controls listed in the selected
1277
+ model schema. GPT Image 2 create has request-aware output-token credit quotes
1278
+ for concrete quality/size requests; GPT Image 2 edit still requires
1279
+ unknown-cost acceptance before execution, but records usage-priced provider cost
1280
+ after execution when OpenAI returns token usage. Provider-native controls remain
1281
+ visible for planning and fail closed until their capability schema marks them
1282
+ executable. Hosted `create --dry-run` validates `model_parameters` against the
1283
+ selected model, returns accepted keys/provenance and request-aware credit
1284
+ pricing for planning, and never executes provider controls or consumes credits.
1285
+ Hosted `edit --dry-run` validates the same owned input, mask/reference,
1286
+ prompt-policy, budget, and `model_parameters` checks as a live edit, then
1287
+ returns planned outputs without storage, provider, or billing side effects.
1288
+ For dry-run responses, `cost.credit_pricing.credits_required` is the planned
1289
+ live execution debit for the selected model. The actual debit for the dry run is
1290
+ `quota.consumed_credits: 0`.
1291
+ Authenticated hosted dry-runs also create a recoverable planned job:
1292
+ `jobs show` returns `status: "planned"` with `plan_receipt`, and `activity`
1293
+ emits `job.planned`. Planned receipts do not create downloadable media assets or
1294
+ usage debits, media writes, or provider execution. In the first-run guide, this
1295
+ exact no-spend command behavior is exposed as
1296
+ `data.no_spend_next_command_effect` and
1297
+ `data.recommended_no_spend_command_effect`; the evaluator stop policy is exposed
1298
+ as `data.no_spend_evaluation`.
1299
+
1300
+ Minimum success data:
1301
+
1302
+ ```json
1303
+ {
1304
+ "job_id": "job_...",
1305
+ "capability": {
1306
+ "id": "is.image.generate.xai-grok-imagine-image-quality.v1"
1307
+ },
1308
+ "assets": [
1309
+ {
1310
+ "asset_id": "image_...",
1311
+ "path": "https://media.image-skill.com/a/image_abc123.png",
1312
+ "mime_type": "image/png",
1313
+ "url": "https://media.image-skill.com/a/image_abc123.png",
1314
+ "content_length": 333444,
1315
+ "width": 2048,
1316
+ "height": 2048
1317
+ }
1318
+ ],
1319
+ "cost": {
1320
+ "estimated_usd": 0.07,
1321
+ "credit_pricing": {
1322
+ "credit_unit_usd": 0.01,
1323
+ "credits_required": 12,
1324
+ "estimated_provider_cost_usd": 0.07,
1325
+ "estimated_revenue_usd": 0.12,
1326
+ "pricing_confidence": "known"
1327
+ }
1328
+ },
1329
+ "request": {
1330
+ "output_count": 1,
1331
+ "selection": {
1332
+ "policy": "hosted_default_create_v1",
1333
+ "reason": "hosted default selected the strongest currently available quality-first create model",
1334
+ "intent": "explore",
1335
+ "capability": {
1336
+ "id": "is.image.generate.xai-grok-imagine-image-quality.v1"
1337
+ },
1338
+ "model_parameters": {
1339
+ "keys": ["resolution"],
1340
+ "defaults_applied": ["resolution=2k"],
1341
+ "source": "default_policy"
1342
+ },
1343
+ "output": {
1344
+ "resolution_class": "2k",
1345
+ "expected_width": null,
1346
+ "expected_height": null,
1347
+ "expected_min_short_edge": 2048
1348
+ }
1349
+ }
1350
+ },
1351
+ "safety": {
1352
+ "status": "allowed"
1353
+ }
1354
+ }
1355
+ ```
1356
+
1357
+ When hosted artifact storage is configured, `url` is an Luxin-owned URL.
1358
+ Agents should prefer `assets[].url` over provider-origin URLs and should not
1359
+ need provider account access to fetch outputs.
1360
+
1361
+ Hosted create does not accept `--output-dir`. A future download/fetch command
1362
+ may add CLI-side local file convenience while preserving hosted artifact URLs as
1363
+ the source of truth.
1364
+
1365
+ If provider generation succeeds but artifact storage fails, the command returns
1366
+ `ARTIFACT_STORAGE_WRITE_FAILED` with exit `9` and `retryable: false`. Agents
1367
+ should not retry the whole create blindly, because that may duplicate paid
1368
+ provider spend.
1369
+
1370
+ For retry-safe create automation, pass an explicit non-secret
1371
+ `--idempotency-key`. A retry that reuses the same key does not create a second
1372
+ credit reservation, so a transient `502`/`PROVIDER_FAILURE` that already
1373
+ reserved a credit cannot double-charge on retry. `create --guide` bakes a
1374
+ generated `--idempotency-key` into its advertised create `next_command`, and a
1375
+ retryable create error returns an `error.recovery.idempotency_key` plus an
1376
+ `error.recovery.suggested_command` that re-runs the same create with that key.
1377
+
1378
+ Live non-dry-run create/edit emits one JSON diagnostic line to stderr before
1379
+ the blocking hosted request:
1380
+
1381
+ ```json
1382
+ {
1383
+ "in_flight": {
1384
+ "command": "luxin create",
1385
+ "idempotency_key": "create-...",
1386
+ "recover_command": "luxin create --idempotency-key create-... <same arguments> --json"
1387
+ }
1388
+ }
1389
+ ```
1390
+
1391
+ stdout remains the command JSON envelope. If an agent combines streams with
1392
+ `2>&1`, split stderr diagnostics from the stdout envelope before parsing. The
1393
+ same recovery breadcrumb is stored under `<config-dir>/in-flight/` and appears
1394
+ in `luxin doctor --json` at `data.in_flight`. Keep the in-flight
1395
+ `idempotency_key` and `recover_command` inside the agent workflow unless a
1396
+ failed or uncertain operation needs user-visible recovery.
1397
+
1398
+ ```bash
1399
+ luxin create \
1400
+ --prompt "A compact field camera on a stainless workbench" \
1401
+ --idempotency-key create-run-001 \
1402
+ --json
1403
+ ```
1404
+
1405
+ Hosted free-preview API equivalent:
1406
+
1407
+ ```bash
1408
+ curl -sS https://api.luxin.sh/v1/create \
1409
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN" \
1410
+ -H "content-type: application/json" \
1411
+ -d '{
1412
+ "prompt": "A compact field camera on a stainless workbench",
1413
+ "intent": "explore",
1414
+ "aspect_ratio": "1:1",
1415
+ "output_count": 1,
1416
+ "max_estimated_usd_per_image": 0.07
1417
+ }'
1418
+ ```
1419
+
1420
+ Hosted free-preview create currently requires owned artifact storage and returns
1421
+ one `assets[]` entry per output with `assets[].url` under
1422
+ `https://media.image-skill.com/...` on success.
1423
+
1424
+ ### `luxin upload`
1425
+
1426
+ Normalizes a local image path or remote image URL into an Luxin-owned
1427
+ input asset for later edit workflows.
1428
+
1429
+ ```bash
1430
+ luxin upload ./source.png --json
1431
+ luxin upload https://example.com/source.png --json
1432
+ ```
1433
+
1434
+ The CLI reads local files and remote URLs client-side, then sends image bytes to
1435
+ `POST /v1/upload`. The hosted API does not fetch arbitrary remote URLs in this
1436
+ preview. This keeps server-side URL fetching out of the public upload path.
1437
+
1438
+ Minimum success data:
1439
+
1440
+ ```json
1441
+ {
1442
+ "request": {
1443
+ "source_kind": "local_path",
1444
+ "filename": "source.png",
1445
+ "remote_origin": null
1446
+ },
1447
+ "asset": {
1448
+ "asset_id": "image_...",
1449
+ "job_id": "job_...",
1450
+ "kind": "uploaded",
1451
+ "url": "https://media.image-skill.com/a/image_abc123.png",
1452
+ "mime_type": "image/png",
1453
+ "content_length": 12345
1454
+ },
1455
+ "upload": {
1456
+ "bytes": 12345,
1457
+ "mime_type": "image/png",
1458
+ "sha256": "...",
1459
+ "policy": {
1460
+ "status": "allowed"
1461
+ }
1462
+ }
1463
+ }
1464
+ ```
1465
+
1466
+ Supported preview MIME types are `image/png`, `image/jpeg`, `image/webp`,
1467
+ `image/gif`, and `image/avif`. Unsupported input returns
1468
+ `INPUT_POLICY_DENIED` with exit `6`. Responses never include local paths, raw
1469
+ bytes, base64 payloads, full remote URLs, bucket names, or object keys.
1470
+
1471
+ ### `luxin edit`
1472
+
1473
+ Edits an Luxin-owned input asset or client-normalized local/remote image
1474
+ with one hosted provider-backed edit model.
1475
+
1476
+ ```bash
1477
+ luxin edit \
1478
+ --input ASSET_ID_OR_PATH_OR_URL \
1479
+ --mask MASK_ASSET_ID_OR_PATH_OR_URL \
1480
+ --prompt "Remove the background and keep natural object shadows" \
1481
+ --accept-unknown-cost \
1482
+ --json
1483
+ ```
1484
+
1485
+ If `--input` is a local path or external URL, the public CLI first normalizes it
1486
+ through the same upload resolver as `luxin upload`, then sends only the
1487
+ resulting `asset_id` to `POST /v1/edit`. If `--input` is an Luxin asset id
1488
+ or owned asset URL, edit uses that owned asset directly.
1489
+ Add `--dry-run` to plan the edit after owned-asset, prompt-policy,
1490
+ `model_parameters`, and budget validation. Dry-run edit responses return planned
1491
+ assets and `quota.consumed_credits: 0`, store a recoverable `job.planned`
1492
+ receipt, and do not call the provider, debit credits, or write media.
1493
+ For models with wired mask support, `--mask` follows the same upload/asset-id
1494
+ resolver and sends only `mask_asset_id`; never pass provider-native `mask_url`
1495
+ through `model_parameters`.
1496
+ For models with wired reference support, pass owned reference assets with the
1497
+ model's advertised reference role. Kling element routes use
1498
+ `--element-frontal IMAGE[@ELEMENT_INDEX]` and
1499
+ `--element-reference IMAGE[@ELEMENT_INDEX[:REFERENCE_INDEX]]`; flat
1500
+ reference-image routes use `--reference-image IMAGE[@INDEX[:TASK]]`. The
1501
+ public CLI uploads local paths and external URLs first, then sends top-level
1502
+ `references[]` entries with Luxin `asset_id` values. For Kling element
1503
+ routes, `--element-frontal ./front.png@0` becomes role `element_frontal` for
1504
+ element index `0`, and `--element-reference ./side.webp@0:0` becomes role
1505
+ `element_reference` for the same element with reference slot `0`. For DreamO
1506
+ create, `--reference-image ./identity.png@0:id` becomes role
1507
+ `reference_image`, index `0`, and `reference_task` `id`. For xAI edit,
1508
+ `--reference-image ./reference.png@0` becomes the second ordered source image;
1509
+ the primary `--input` asset remains the first source image. Do not pass
1510
+ provider-native `elements`, `image_url`, `image_urls`, `frontal_image_url`,
1511
+ `reference_image_urls`, `first_image_url`, `second_image_url`, `images`, or
1512
+ `*_reference_task` through `model_parameters`; provider-private URLs are
1513
+ resolved server-side after ownership and media-policy validation.
1514
+ Current public `references[]` support covers Kling Image O1, Kling Image O3
1515
+ image-to-image/text-to-image, Kling Image v3 image-to-image/text-to-image, and
1516
+ Fal DreamO create plus xAI Grok Imagine image edit/quality edit. Kling requests
1517
+ may contain at most 40 reference entries across at most 10 contiguous element
1518
+ indexes starting at `0`; each referenced element requires one frontal image and
1519
+ may include up to three additional reference images. DreamO accepts up to two
1520
+ contiguous `reference_image` indexes starting at `0`, each with optional
1521
+ `reference_task` `ip`, `id`, or `style`. xAI edit accepts up to two contiguous
1522
+ `reference_image` indexes starting at `0` and does not accept `reference_task`.
1523
+ Reference assets must be Luxin-owned PNG, JPEG, or WebP images with
1524
+ known non-empty byte length up to 10MB, known width and height of at least
1525
+ 300px, and aspect ratio from 0.40 to 2.50.
1526
+
1527
+ ```bash
1528
+ luxin edit \
1529
+ --model fal.kling-image-o3-image-to-image \
1530
+ --input ./starting-frame.png \
1531
+ --element-frontal ./character-front.png@0 \
1532
+ --element-reference ./character-side.webp@0:0 \
1533
+ --prompt "Place the same character in a clean studio product portrait" \
1534
+ --accept-unknown-cost \
1535
+ --json
1536
+ ```
1537
+
1538
+ Direct `/v1/edit` callers use the same owned-asset contract:
1539
+
1540
+ ```json
1541
+ {
1542
+ "input_asset_id": "image_starting_frame",
1543
+ "model": "fal.kling-image-o3-image-to-image",
1544
+ "prompt": "Place the same character in a clean studio product portrait",
1545
+ "references": [
1546
+ {
1547
+ "asset_id": "image_character_front",
1548
+ "role": "element_frontal",
1549
+ "index": 0
1550
+ },
1551
+ {
1552
+ "asset_id": "image_character_side",
1553
+ "role": "element_reference",
1554
+ "index": 0,
1555
+ "reference_index": 0
1556
+ }
1557
+ ]
1558
+ }
1559
+ ```
1560
+
1561
+ Set `dry_run: true` on `/v1/edit` to get the same no-spend plan the CLI returns.
1562
+ The server still validates ownership, prompt policy, references, masks,
1563
+ `model_parameters`, and budget guards before returning the planned job.
1564
+
1565
+ Create a 3D asset from an image through the same `edit` command and
1566
+ durable-media loop. Image-to-3D is promptless and image-conditioned, so it ships
1567
+ as a variation transform: pass exactly one owned input image (no prompt) to a 3D
1568
+ model by id and the response returns a durable owned `.glb` mesh asset URL (in
1569
+ `assets[].url`), a `job_id`, and a `cost.credit_pricing` receipt. A 3D mesh has
1570
+ no aspect ratio.
1571
+
1572
+ ```bash
1573
+ luxin edit \
1574
+ --input image_... \
1575
+ --model fal.trellis-image-to-3d \
1576
+ --json
1577
+ ```
1578
+
1579
+ `fal.trellis-image-to-3d` (Trellis) is image-to-3D at a flat $0.02/asset (about 4
1580
+ credits, quoted before spend) and returns an owned `model/gltf-binary` (`.glb`)
1581
+ textured mesh. The first slice is defaults-only (no tunable `model_parameters`);
1582
+ guidance/steps/mesh_simplify/texture_size controls are a later milestone. Inspect
1583
+ parameters, output media type, and cost first with `luxin models show
1584
+ fal.trellis-image-to-3d --json`. The input must be one Luxin-owned image;
1585
+ 3D runs synchronously through the same edit call and can take longer than an
1586
+ image.
1587
+
1588
+ Preview hosted create/edit supports model-specific provider-backed paths such
1589
+ as Fal Gemini 3 Pro Image Preview Create (`fal.gemini-3-pro-image-preview`),
1590
+ Fal Nano Banana 2 Edit (`fal.nano-banana-2-edit`), Fal Ideogram V2 Edit
1591
+ (`fal.ideogram-v2-edit`), Fal Gemini 3 Pro Image
1592
+ Preview Edit (`fal.gemini-3-pro-image-preview-edit`), Fal FLUX Pro 1.1 Ultra
1593
+ Create (`fal.flux-pro-v1-1-ultra`), Fal FLUX Pro Kontext Edit
1594
+ (`fal.flux-pro-kontext`), Fal FLUX Pro Kontext Max Edit
1595
+ (`fal.flux-pro-kontext-max`), Fal Seedream 5.0 Lite Create
1596
+ (`fal.bytedance-seedream-v5-lite-text-to-image`), Fal Seedream 5.0 Lite Edit
1597
+ (`fal.bytedance-seedream-v5-lite-edit`), Fal Seedream 4.5 Create
1598
+ (`fal.bytedance-seedream-v4-5-text-to-image`), Fal Seedream 4.5 Edit
1599
+ (`fal.bytedance-seedream-v4-5-edit`), Fal Nano Banana Pro Create
1600
+ (`fal.nano-banana-pro`), Fal Nano Banana Pro Edit
1601
+ (`fal.nano-banana-pro-edit`), GPT Image 1.5 Create
1602
+ (`openai.gpt-image-1.5`), GPT Image 1.5 Edit
1603
+ (`openai.gpt-image-1.5-edit`), and GPT Image 2 Edit
1604
+ (`openai.gpt-image-2-edit`) when their provider credentials are configured.
1605
+ Fal Gemini 3 Pro Image Preview create/edit has known per-image pricing: 1K/2K
1606
+ requests quote `$0.15` provider cost and 4K quotes the doubled provider tier.
1607
+ Fal Nano Banana Pro create/edit uses the same `$0.15` standard and doubled 4K
1608
+ provider tier. Fal FLUX Pro 1.1 Ultra Create quotes `$0.06` provider cost per
1609
+ image. Fal FLUX Pro Kontext Edit quotes `$0.04` provider cost per image, and
1610
+ Fal FLUX Pro Kontext Max Edit quotes `$0.08` provider cost per image. Fal
1611
+ Seedream 4.5 create/edit quotes `$0.04` provider cost per image.
1612
+ Fal Seedream 5.0 Lite create/edit quotes `$0.035` provider cost per image. Fal
1613
+ Z-Image Turbo create/edit quotes `$0.005/MP` when `image_size` is explicit or
1614
+ derived from aspect ratio; edit `auto` remains unknown-cost. GPT Image 1.5
1615
+ create quotes output-token estimates for concrete quality/size requests using
1616
+ OpenAI's fixed-size token table; GPT Image 1.5 edit remains preflight
1617
+ unknown-cost because edit input image/text tokens are provider-metered, then
1618
+ records usage-priced provider cost when OpenAI returns token usage. GPT Image 2
1619
+ create quotes output-token estimates for concrete quality/size requests. GPT
1620
+ Image 2 edit remains preflight unknown-cost because edit input image/text tokens
1621
+ are provider-metered, then records usage-priced provider cost when OpenAI
1622
+ returns token usage. Other edit paths without machine-readable pricing require
1623
+ `--accept-unknown-cost` until a stable price source is captured. Responses
1624
+ include a new generated asset URL, job id, safety state, quota consumption, and
1625
+ input asset metadata where
1626
+ applicable. Responses do not include raw prompts, source bytes, base64
1627
+ payloads, local paths, full external URLs, bucket names, or object keys.
1628
+ When `quota.top_up.available` is true, `next_actions.self_fund.quote_command`
1629
+ is a copy-runnable top-up quote command. `next_actions.self_fund.recommended`
1630
+ and the mirrored top-up `urgency`, `urgency_score`, and `urgency_reasons` say
1631
+ whether the agent should act now. The handoff exposes `first_safe_command`,
1632
+ `first_safe_command_effect`, `inspect_methods_command`, and
1633
+ `inspect_packs_command` for no-spend payment rail inspection before quote/buy.
1634
+ The quote command omits `--idempotency-key` so the public CLI generates and
1635
+ returns one for retry safety before the agent follows the quote response into
1636
+ `credits buy` and `credits status`.
1637
+
1638
+ Provider/model names in this paragraph are preview provenance, not the primary
1639
+ public UX. The public selection surface should be Luxin capabilities and
1640
+ model-parameter schemas; provider/model details belong in explicit
1641
+ provenance/debug output.
1642
+
1643
+ Edit accepts the same retry-safe `--idempotency-key` as create. A retry that
1644
+ reuses the same key does not create a second credit reservation, so a transient
1645
+ `502`/`PROVIDER_FAILURE` after a reservation cannot double-charge; a retryable
1646
+ edit error returns an `error.recovery.idempotency_key` and an
1647
+ `error.recovery.suggested_command` that re-runs the same edit with that key.
1648
+ Live non-dry-run edit emits the same stderr `in_flight` diagnostic and local
1649
+ doctor-visible recovery breadcrumb as create.
1650
+
1651
+ ### `luxin assets show`
1652
+
1653
+ Inspects an Luxin-owned asset URL or hosted asset id.
1654
+
1655
+ ```bash
1656
+ luxin assets show \
1657
+ https://media.image-skill.com/a/image_abc123.png \
1658
+ --json
1659
+ ```
1660
+
1661
+ For asset-id lookup, use hosted auth:
1662
+
1663
+ ```bash
1664
+ luxin assets show image_... --json
1665
+ ```
1666
+
1667
+ Minimum success data:
1668
+
1669
+ ```json
1670
+ {
1671
+ "request": {
1672
+ "reference": "image_...",
1673
+ "reference_type": "asset_id"
1674
+ },
1675
+ "asset": {
1676
+ "asset_id": "image_...",
1677
+ "job_id": "job_...",
1678
+ "url": "https://media.image-skill.com/a/image_abc123.png",
1679
+ "mime_type": "image/png",
1680
+ "content_length": 12345,
1681
+ "width": 1024,
1682
+ "height": 1024,
1683
+ "source": "hosted_metadata"
1684
+ }
1685
+ }
1686
+ ```
1687
+
1688
+ External URLs are rejected. Older assets created before hosted asset metadata
1689
+ was recorded may still be inspectable by Luxin-owned URL.
1690
+
1691
+ For hosted generated assets, when quota exposes an available top-up path,
1692
+ `data.next_actions.self_fund` mirrors the top-up recommendation, urgency, and
1693
+ no-spend payment rail inspection handoff used by create/edit, jobs, and
1694
+ activity responses. On successful create/edit, when that self-fund action is
1695
+ recommended and copy-runnable, `data.self_fund_next_command` aliases
1696
+ `data.next_actions.self_fund.quote_command`; its effect object proves the quote
1697
+ does not buy, settle a wallet transfer, debit credits, call a provider, create a
1698
+ hosted job, or write media.
1699
+
1700
+ ### `luxin assets get`
1701
+
1702
+ Downloads an Luxin-owned asset URL or hosted asset id to a local file.
1703
+
1704
+ ```bash
1705
+ luxin assets get \
1706
+ https://media.image-skill.com/a/image_abc123.png \
1707
+ --output ./result.png \
1708
+ --json
1709
+ ```
1710
+
1711
+ The command refuses to overwrite existing files unless `--overwrite` is
1712
+ explicit. It verifies byte length when the asset server provides a
1713
+ `content-length` header. For hosted asset-id downloads, `assets get` preserves
1714
+ `data.next_actions.self_fund` from the asset metadata response after a
1715
+ successful download.
1716
+
1717
+ ### `luxin jobs show`
1718
+
1719
+ Inspects a hosted Luxin job visible to the authenticated agent.
1720
+
1721
+ ```bash
1722
+ luxin jobs show job_... --json
1723
+ ```
1724
+
1725
+ Output includes public job status, trace id, timestamps, capability id, cost
1726
+ summary, safety status, and Luxin-owned asset metadata. For fresh-agent
1727
+ parsing, `status`, `state`, `assets`, and `cost` are mirrored at top level and
1728
+ match `job.status`, `job.assets`, and `job.cost`. Provider/model provenance is
1729
+ available only through explicit provenance/debug affordances for authorized
1730
+ actors. Default output does not include raw prompts, generated bytes, provider
1731
+ credentials, DB/storage keys, bucket names, or local paths.
1732
+
1733
+ Minimum success data:
1734
+
1735
+ ```json
1736
+ {
1737
+ "request": {
1738
+ "job_id": "job_..."
1739
+ },
1740
+ "status": "completed",
1741
+ "state": "completed",
1742
+ "assets": [],
1743
+ "cost": {
1744
+ "estimated_usd": 0.05,
1745
+ "provider_reported_usd_ticks": null
1746
+ },
1747
+ "job": {
1748
+ "job_id": "job_...",
1749
+ "status": "completed",
1750
+ "assets": [],
1751
+ "cost": {
1752
+ "estimated_usd": 0.05,
1753
+ "provider_reported_usd_ticks": null
1754
+ }
1755
+ }
1756
+ }
1757
+ ```
1758
+
1759
+ ### `luxin jobs wait`
1760
+
1761
+ Waits for a hosted Luxin job to reach a terminal status.
1762
+
1763
+ ```bash
1764
+ luxin jobs wait job_... --timeout-ms 30000 --poll-interval-ms 1000 --json
1765
+ ```
1766
+
1767
+ Completed jobs return immediately. Non-terminal jobs poll until completion,
1768
+ failure, cancellation, or deterministic timeout. Terminal success uses the same
1769
+ top-level `status`, `state`, `assets`, and `cost` mirrors as `jobs show`, with
1770
+ `request.timeout_ms` and `request.poll_interval_ms` added for provenance.
1771
+
1772
+ ### `luxin activity list`
1773
+
1774
+ Lists recent hosted activity ledger events visible to the authenticated agent.
1775
+
1776
+ ```bash
1777
+ luxin activity list --limit 20 --json
1778
+ luxin activity list --subject job_... --json
1779
+ ```
1780
+
1781
+ Activity is the ledger, not the work queue. Use it to find recent event IDs,
1782
+ related job IDs, asset IDs, usage IDs, feedback IDs, trace IDs, status changes,
1783
+ and product-memory writes. Use `jobs show` or `jobs wait` when you need
1784
+ operational recovery, polling, retry judgment, or final job assets.
1785
+
1786
+ When the ledger proves generated work and current quota exposes an available
1787
+ top-up path, `data.next_actions.self_fund` mirrors the same recommendation,
1788
+ urgency, and no-spend payment-method inspection handoff returned by successful
1789
+ create/edit and `jobs show`.
1790
+
1791
+ Minimum success data:
1792
+
1793
+ ```json
1794
+ {
1795
+ "events": [
1796
+ {
1797
+ "event_id": "evt_...",
1798
+ "type": "job.completed",
1799
+ "occurred_at": "2026-05-05T19:00:23.000Z",
1800
+ "summary": "Create job completed",
1801
+ "operation": "create",
1802
+ "subject": {
1803
+ "type": "job",
1804
+ "id": "job_..."
1805
+ },
1806
+ "links": {
1807
+ "job_id": "job_...",
1808
+ "asset_ids": ["image_..."],
1809
+ "feedback_id": null,
1810
+ "usage_event_id": "usage_..."
1811
+ },
1812
+ "status": "completed",
1813
+ "cost": {
1814
+ "estimated_usd": 0.025
1815
+ }
1816
+ }
1817
+ ],
1818
+ "source": "hosted_activity_ledger"
1819
+ }
1820
+ ```
1821
+
1822
+ The ledger hides provider and storage implementation details by default. It is
1823
+ safe to cite `evt_...`, `job_...`, `image_...`, `usage_...`, `feedback_id`,
1824
+ and `trace_id` values in feedback.
1825
+
1826
+ Hosted API equivalent:
1827
+
1828
+ ```bash
1829
+ curl -sS "https://api.luxin.sh/v1/activity?limit=20&subject=job_..." \
1830
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN"
1831
+ ```
1832
+
1833
+ ### `luxin activity show`
1834
+
1835
+ Shows one hosted activity event or the latest events related to one subject.
1836
+
1837
+ ```bash
1838
+ luxin activity show evt_... --json
1839
+ luxin activity show job_... --json
1840
+ luxin activity show image_... --json
1841
+ luxin activity show sig_... --json
1842
+ ```
1843
+
1844
+ `activity show` accepts activity event IDs plus job, asset, usage, feedback, and
1845
+ trace references. When the reference is a subject rather than one exact event,
1846
+ the response includes matching ledger events so an agent can cite the right
1847
+ event without reading telemetry logs. When current quota exposes top-up
1848
+ availability after generated work,
1849
+ `data.next_actions.self_fund.first_safe_command` is the no-spend rail
1850
+ inspection command to run before any quote/buy step.
1851
+
1852
+ Hosted API equivalent:
1853
+
1854
+ ```bash
1855
+ curl -sS https://api.luxin.sh/v1/activity/evt_... \
1856
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN"
1857
+ ```
1858
+
1859
+ ### Activity Event Registry
1860
+
1861
+ Activity `type` values are stable public contract values. Do not infer new
1862
+ event names from provider responses or telemetry logs; use only the registry
1863
+ below.
1864
+
1865
+ | Event type | Subject | Operation | Emitted when | Stable links |
1866
+ | ------------------------------------------ | ---------- | ----------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
1867
+ | `job.completed` | `job` | create/edit | A hosted create or edit job reaches a terminal state. | `job_id`, `asset_ids`, `usage_event_id` |
1868
+ | `job.planned` | `job` | create/edit | An authenticated hosted dry-run stores a recoverable plan receipt. | `job_id` |
1869
+ | `asset.created` | `asset` | create/edit | A hosted create or edit produces an output asset. | `job_id`, `asset_ids`, `usage_event_id` |
1870
+ | `asset.uploaded` | `asset` | upload | A public edit workflow uploads or imports input media. | `job_id`, `asset_ids`, `usage_event_id` |
1871
+ | `usage.credit_consumed` | `usage` | usage | A creative operation records a preview-credit entry. | `job_id`, `usage_event_id` |
1872
+ | `feedback.created` | `feedback` | feedback | Hosted agent feedback is accepted into product memory. | `feedback_id` |
1873
+ | `feedback.github_queue.processed` | `feedback` | feedback | Feedback is processed by the GitHub implementation queue handoff. | `feedback_id` |
1874
+ | `payment.checkout_session.created` | `payment` | payment | A Stripe Checkout session is created and awaits external action. | `quote_id`, `payment_attempt_id`, `checkout_session_id` |
1875
+ | `credits.payment_backed_granted` | `credit` | credits | Verified payment fulfillment grants paid credits. | `quote_id`, `receipt_id`, `credit_event_id` |
1876
+ | `credits.payment_backed_refunded` | `credit` | credits | A Stripe refund debits payment-backed credits. | `quote_id`, `receipt_id`, `payment_reversal_id`, `credit_event_id` |
1877
+ | `credits.payment_backed_disputed` | `credit` | credits | A Stripe dispute debit applies to payment-backed credits. | `quote_id`, `receipt_id`, `payment_reversal_id`, `credit_event_id` |
1878
+ | `credits.payment_backed_reinstated` | `credit` | credits | Stripe dispute funds were reinstated and recorded. | `quote_id`, `receipt_id`, `payment_reversal_id` |
1879
+ | `credits.payment_backed_reversal_pending` | `credit` | credits | A reversal was recorded but could not be fully applied. | `quote_id`, `receipt_id`, `payment_reversal_id` |
1880
+ | `credits.payment_backed_reversal_rejected` | `credit` | credits | A reversal was rejected because it could not safely reconcile. | `quote_id`, `receipt_id`, `payment_reversal_id` |
1881
+
1882
+ `feedback.github_queue.processed` includes `details.github_queue` with
1883
+ machine-readable lifecycle fields such as `state`, `reason`, `issue_urls`,
1884
+ `issue_numbers`, `mode`, and `github_mutation`. Agents should use it to learn
1885
+ whether submitted feedback was promoted, skipped, deduped, blocked, or already
1886
+ mirrored without reading private repository artifacts.
1887
+ `job.planned` includes `details.plan_receipt` for authenticated hosted
1888
+ dry-runs. It is a recoverable planning receipt, not completed media work:
1889
+ planned outputs do not have durable asset IDs, download URLs, usage debits, or
1890
+ provider execution.
1891
+
1892
+ If a response includes an event type outside this registry, treat it as a
1893
+ contract bug and submit `luxin feedback create --json` with the event ID
1894
+ and trace ID.
1895
+
1896
+ ### `luxin feedback create`
1897
+
1898
+ Leaves structured product feedback in hosted Luxin product memory.
1899
+ At minimum, provide `--title` and `--body`; Luxin accepts narrative
1900
+ feedback and adds quality guidance server-side. Use the structured fields below
1901
+ when the agent already knows them.
1902
+
1903
+ ```bash
1904
+ luxin feedback create \
1905
+ --type user_feedback \
1906
+ --title "Short concrete title" \
1907
+ --body "What happened, what was expected, and why it matters" \
1908
+ --command "Command or workflow observed" \
1909
+ --expected "Expected result" \
1910
+ --actual "Actual result" \
1911
+ --proof-needed "What would prove this is handled" \
1912
+ --surface cli,docs \
1913
+ --evidence trace:TRACE_ID \
1914
+ --severity medium \
1915
+ --confidence high \
1916
+ --next-state watch \
1917
+ --json
1918
+ ```
1919
+
1920
+ Hosted feedback authenticates the same way as other hosted commands: saved
1921
+ public CLI config from default signup, `IMAGE_SKILL_TOKEN`, or
1922
+ `--token-stdin`. If guide/signup already saved config, run `feedback create`
1923
+ normally; no raw token copy step is required. If the runtime uses its own
1924
+ secret store, prefer `IMAGE_SKILL_TOKEN` or `--token-stdin`. Do not paste tokens
1925
+ into feedback title, body, evidence, issues, or logs. Feedback persists through
1926
+ `https://api.luxin.sh/v1/feedback`. The hosted API fails closed if
1927
+ durable hosted feedback storage is unavailable.
1928
+
1929
+ JSON errors may include `error.recovery` with machine-readable fields such as
1930
+ `required_flag`, `suggested_command`, `docs_url`, or `retry_after_seconds`.
1931
+ Agents should prefer those fields over parsing prose error messages. For
1932
+ example, `BUDGET_REQUIRES_CONFIRMATION` returns
1933
+ `required_flag: "--accept-unknown-cost"`.
1934
+ Quota/payment recovery also includes `error.recovery.top_up`: its
1935
+ `quote_command` is copy-runnable and creates an authenticated live-money
1936
+ quote/payment object without paying, settling a wallet transfer, debiting
1937
+ credits, calling a provider, or writing media. Only the later buy/status and
1938
+ wallet/checkout completion steps can spend money.
1939
+
1940
+ `whoami`, `usage quota`, `quota`, `credits quote`, `credits buy`,
1941
+ `credits status`, `create`, `activity list`,
1942
+ `activity show`, and `feedback create` accept `--token-stdin` for stdin-based
1943
+ secret handoff.
1944
+ `credits methods` and `credits packs list` do not require auth.
1945
+
1946
+ Feedback should avoid raw prompts, hosted tokens, provider keys, generated
1947
+ image bytes, source image bytes, and private user data.
1948
+
1949
+ Hosted API equivalent:
1950
+
1951
+ ```bash
1952
+ curl -sS https://api.luxin.sh/v1/feedback \
1953
+ -H "authorization: Bearer $IMAGE_SKILL_TOKEN" \
1954
+ -H "content-type: application/json" \
1955
+ -d '{
1956
+ "type": "user_feedback",
1957
+ "title": "Short concrete title",
1958
+ "body": "What happened, what was expected, and why it matters",
1959
+ "command": "Command or workflow observed",
1960
+ "expected": "Expected result",
1961
+ "actual": "Actual result",
1962
+ "proof_needed": "What would prove this is handled",
1963
+ "surface": ["cli", "docs"],
1964
+ "evidence": ["trace:TRACE_ID"],
1965
+ "severity": "medium",
1966
+ "confidence": "high",
1967
+ "next_state": "watch"
1968
+ }'
1969
+ ```
1970
+
1971
+ ### Planned Resource Commands
1972
+
1973
+ `jobs list`, `assets list`, `assets delete`, and async job cancellation are
1974
+ planned public resource commands. They are not part of the current public
1975
+ allowlist until the hosted service backs them and this contract lists their
1976
+ exact command shapes. `activity list/show` is available now for ledger
1977
+ readback, but it is not a substitute for future job listing, cancellation, or
1978
+ retry controls.