@spicyapi/skill 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,3 +20,8 @@ npx --yes --package=@spicyapi/skill spicyapi-skill path
20
20
  Use `--target <absolute-directory>` for Codex, Claude Code, Cursor, or another Agent
21
21
  Skills-compatible client when it uses a different skills directory. Use `--force` only when you
22
22
  intend to replace that exact destination.
23
+
24
+ The default workflow reads the selected model's schema once, lets CLI/MCP creation obtain and
25
+ confirm its quote, then uses waiting or a verified webhook and reads ready result URLs directly.
26
+ Health/balance checks, separate price comparisons, and download-link renewal remain available when
27
+ needed. Task creation and retry retain their billable confirmation and idempotency requirements.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spicyapi/skill",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Official installable Agent Skill for SpicyAPI",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,22 +21,28 @@ integration tests, webhook handling, credential work, or cleanup.
21
21
 
22
22
  ## Required behavior
23
23
 
24
- - Query the live catalog and fetch the selected model with its input schema before creating a task.
25
- Never invent a model ID, price, provider capability, or input field.
24
+ - Obtain the selected model's live input schema once: get a known model, or discover it in the
25
+ catalog. Reuse a complete record already returned with `includeSchema`; refresh when it is missing
26
+ or stale. Never invent a model ID, price, provider capability, or input field.
26
27
  - Pass the model-specific `input` object through unchanged. The SpicyAPI server owns schema, price,
27
28
  balance, operational API-key scope, and routing decisions.
28
- - Treat `mature` as caller-declared execution metadata. Pass the requested boolean unchanged; the
29
- platform does not inspect or gate it by account, API key, model, channel, age, consent, or a
30
- content classifier. A selected model service may still refuse under its own behavior, and the
31
- customer remains responsible for applicable law, age, consent, and end-user controls.
32
- - Treat task creation and retry as billable. Obtain explicit confirmation and preserve one
33
- idempotency key for the entire logical attempt, including recovery from an uncertain response.
29
+ - Choose model capabilities from the live catalog. No content-mode request flag or platform content
30
+ review is required.
31
+ - Treat task creation and retry as billable. CLI and MCP creation obtain and confirm the exact quote
32
+ internally; do not call the separate quote command/tool first unless comparing prices. In SDK
33
+ code, quote the exact request once, show its USD estimate and maximum charge, confirm it, and pass
34
+ that quote into creation. Preserve one idempotency key for the entire logical attempt, including
35
+ recovery from an uncertain response.
36
+ - Health/readiness and balance checks are optional diagnostics, not a per-task checklist. The server
37
+ validates availability and funds when accepting a task.
38
+ - Use ready `output.assets[].url` directly. A complete verified v2 webhook needs no extra task
39
+ lookup or download ticket. Query again for pending assets or expired links.
34
40
  - Use environment variables for API keys and webhook secrets. Never put them in arguments, source
35
41
  code, prompts, logs, support messages, or committed configuration.
36
42
  - Preserve `request_id`, task IDs, upload keys, and idempotency keys in operational results. They
37
43
  are necessary for reconciliation and support.
38
- - Do not invent task listing, cancellation, deletion, estimate, or webhook-redelivery calls. They
39
- are absent from the public developer contract.
44
+ - Do not invent task listing, cancellation, deletion or webhook-redelivery calls. They are absent
45
+ from the public developer contract.
40
46
  - Keep browser-session identity, payment, privacy, account closure, and admin operations in the
41
47
  SpicyAPI Console. Do not emulate cookie/CSRF Console routes in this server-side skill.
42
48
  - Provider/model onboarding and production enablement are outside this skill unless the user
@@ -47,7 +53,7 @@ integration tests, webhook handling, credential work, or cleanup.
47
53
  When the user asks for a real test, make a real request against the selected environment; do not
48
54
  substitute a mock and call it verified. Record every created identifier before continuing. Use the
49
55
  smallest user-approved billable scenario that the live catalog actually offers, then verify it
50
- through task retrieval or waiting.
56
+ through task retrieval, waiting, or a verified terminal webhook containing the result.
51
57
 
52
58
  Clean up only exact artifacts that the environment exposes a supported deletion mechanism for. The
53
59
  public developer API has no task or uploaded-object delete operation, so report those retained
@@ -11,63 +11,113 @@ Set credentials through the process environment or a secret manager:
11
11
 
12
12
  Plain HTTP base URLs are accepted only for loopback development hosts.
13
13
 
14
+ ## API key setup and maintenance
15
+
16
+ Create and manage keys in the SpicyAPI Console, then inject `SPICY_API_KEY` into the SDK, CLI, or
17
+ MCP process. The developer Bearer API does not create, reveal, rotate, or revoke API keys. A new
18
+ plaintext key is shown once; store it in the application's secret manager. Model restrictions, IP
19
+ restrictions, spend caps, expiry, and workspace selection belong in the Console.
20
+
21
+ For planned rotation, configure and verify a replacement key before revoking the old one. If a key
22
+ has leaked, revoke it promptly in the Console and replace it. Revocation does not cancel tasks
23
+ already accepted. For authentication failures, check the configured environment and the key's
24
+ Console status; for access failures, check its model, IP, and workspace restrictions. Never include
25
+ the credential in a diagnostic message.
26
+
14
27
  ## Verified public surface
15
28
 
16
- | Purpose | CLI | MCP tool | Public route |
17
- | -------------- | --------------------------------------- | ------------------------------ | ------------------------------------ |
18
- | Service status | `spicyapi status` | `spicyapi_service_status` | `/healthz`, `/readyz` |
19
- | Search docs | `spicyapi docs search [query]` | `spicyapi_docs_search` | Bundled first-party index |
20
- | List models | `spicyapi models list --include-schema` | `spicyapi_models_list` | `GET /models` |
21
- | Get model | `spicyapi models get <model>` | `spicyapi_model_get` | `GET /models/{model}` |
22
- | Balance | `spicyapi balance` | `spicyapi_balance_get` | `GET /chat/credit` |
23
- | Create task | `spicyapi tasks create ...` | `spicyapi_task_create` | `POST /jobs/createTask` |
24
- | Get task | `spicyapi tasks get <task-id>` | `spicyapi_task_get` | `GET /jobs/recordInfo` |
25
- | Wait for task | `spicyapi tasks wait <task-id>` | `spicyapi_task_wait` | Composes task retrieval |
26
- | Retry task | `spicyapi tasks retry <task-id>` | `spicyapi_task_retry` | `POST /jobs/retry` |
27
- | Prepare upload | SDK | `spicyapi_upload_prepare` | `POST /common/upload-url` |
28
- | Upload file | `spicyapi files upload <path>` | Keep bytes outside MCP | Presigned storage `PUT`, then commit |
29
- | Commit upload | SDK | `spicyapi_upload_commit` | `POST /files/{fileId}/commit` |
30
- | Output URL | `spicyapi files download-url <task-id>` | `spicyapi_download_url_create` | `POST /common/download-url` |
31
- | Verify webhook | `spicyapi webhooks verify ...` | Local library/CLI | No network call |
32
-
33
- These are the complete public developer operations in the bundled contract. Model listing is not
34
- paginated.
29
+ | Purpose | CLI | MCP tool | Public route |
30
+ | ----------------- | -------------------------------------------------- | ------------------------------ | ------------------------------------ |
31
+ | Service status | `spicyapi status` | `spicyapi_service_status` | `/healthz`, `/readyz` |
32
+ | Search docs | `spicyapi docs search [query]` | `spicyapi_docs_search` | Bundled first-party index |
33
+ | List models | `spicyapi models list --include-schema` | `spicyapi_models_list` | `GET /models` |
34
+ | Get model | `spicyapi models get <model>` | `spicyapi_model_get` | `GET /models/{model}` |
35
+ | Balance | `spicyapi balance` | `spicyapi_balance_get` | `GET /chat/credit` |
36
+ | Current key usage | `spicyapi usage --from YYYY-MM-DD --to YYYY-MM-DD` | `spicyapi_usage_get` | `GET /usage` |
37
+ | Quote request | `spicyapi tasks quote ...` | `spicyapi_task_quote` | `POST /jobs/quote` |
38
+ | Create task | `spicyapi tasks create ...` | `spicyapi_task_create` | `POST /jobs/createTask` |
39
+ | Get task | `spicyapi tasks get <task-id>` | `spicyapi_task_get` | `GET /jobs/recordInfo` |
40
+ | Wait for task | `spicyapi tasks wait <task-id>` | `spicyapi_task_wait` | Composes task retrieval |
41
+ | Retry task | `spicyapi tasks retry <task-id>` | `spicyapi_task_retry` | `POST /jobs/retry` |
42
+ | Prepare upload | SDK | `spicyapi_upload_prepare` | `POST /common/upload-url` |
43
+ | Upload file | `spicyapi files upload <path>` | Keep bytes outside MCP | Presigned storage `PUT`, then commit |
44
+ | Commit upload | SDK | `spicyapi_upload_commit` | `POST /files/{fileId}/commit` |
45
+ | Renew output URL | `spicyapi files download-url <task-id>` | `spicyapi_download_url_create` | `POST /common/download-url` |
46
+ | Verify webhook | `spicyapi webhooks verify ...` | Local library/CLI | No network call |
47
+
48
+ These are the focused media-task SDK, CLI and MCP operations. The bundled OpenAPI also documents
49
+ `/v1` text/video compatibility and `/jobs/stream`. Model listing is not paginated.
50
+
51
+ ## Current API key usage
52
+
53
+ Use `spicyapi_usage_get` (or CLI `usage`) when asked for task usage and settled spending. Only
54
+ optional `from` and `to` dates are accepted; do not add user, key, or workspace selectors. The
55
+ configured API key determines scope. Dates use UTC `[from,to)`, including the start and excluding
56
+ the end, for at most 92 days. The default `to` is tomorrow UTC and `from` is seven days before it.
57
+ Counts are attributed to task creation day. `totalSpend` and each `spend` are exact decimal USD
58
+ strings for settled actual charges, excluding pending holds; late settlement can revise earlier
59
+ days. This is not remaining balance or API-key budget. It is not a generation precheck. Respect
60
+ `Retry-After` if reporting is rate limited; do not query each day separately when one range
61
+ suffices.
35
62
 
36
63
  ## Generation sequence
37
64
 
38
- 1. Check readiness and authenticated balance.
39
- 2. List models with `--include-schema` and get the exact candidate model.
40
- 3. Build only the `input` object described by the returned live schema. Preserve unknown future
41
- fields rather than remapping them.
42
- 4. If the caller requests mature execution, send `mature: true` unchanged. It is recorded and
43
- forwarded as execution metadata; it is not checked against an account, API key, catalog label,
44
- channel, or platform content classifier. The selected model service may still refuse.
45
- 5. Upload local media first when the model input requires a hosted asset. Use the returned committed
46
- file reference exactly as the model schema specifies.
47
- 6. Choose one idempotency key for the logical creation attempt.
48
- 7. Confirm the current action and create the task. CLI confirmation is interactive unless `--yes` is
49
- explicitly supplied; MCP uses protocol elicitation.
50
- 8. Save the returned task ID, request ID when present, and idempotency key.
51
- 9. Poll with `tasks wait`, or retrieve the task after a signed terminal webhook.
52
- 10. Create a short-lived download URL for a successful task output. Do not persist the presigned URL
53
- as a durable asset identifier.
65
+ 1. Use the selected model's live input schema. Get a known model directly, or discover models first.
66
+ If a list response already includes its full schema, reuse it instead of fetching it again.
67
+ 2. Build only the `input` described by that schema. Pass supported public HTTPS media URLs directly;
68
+ upload local files with SDK `uploadFile` or CLI `files upload` and use the committed `spicy://`
69
+ URI.
70
+ 3. Preserve one idempotency key for the logical creation attempt. CLI `tasks create` and MCP
71
+ `spicyapi_task_create` obtain the exact quote and request confirmation internally. Do not run
72
+ `tasks quote` or `spicyapi_task_quote` first unless comparing prices independently. SDK code
73
+ obtains one quote, shows `estimatedCost`, `maxCharge` and `expiresAt`, then sends its `quoteId`
74
+ and `expectedCost` with the unchanged confirmed request. CLI `--yes` is explicit noninteractive
75
+ authorization; it does not remove the price confirmation bound into the request. MCP retains
76
+ protocol elicitation and signed request state.
77
+ 4. Save the accepted task ID, request ID when present, and idempotency key. Use a verified webhook
78
+ for a production receiver, or bounded waiting with `tasks create --wait` / `spicyapi_task_wait`.
79
+ A complete verified v2 webhook already contains the task result; no extra lookup is required.
80
+ 5. Use ready `output.assets[].url` directly, without attaching the SpicyAPI key. Query the same task
81
+ again when assets are pending or URLs expire. Save a durable copy in storage the user controls; a
82
+ temporary URL is not a durable asset identifier.
83
+
84
+ Health/readiness and balance tools are for explicit status requests or diagnostics, not
85
+ prerequisites for generation. The server validates availability and funds at acceptance. The
86
+ separate quote tool is useful for price comparisons; download-url remains available for legacy
87
+ clients and explicit link renewal. A v1 callback or an incomplete payload may require task
88
+ retrieval. Choose webhook or waiting as the normal completion path instead of running both
89
+ routinely.
54
90
 
55
91
  Example CLI shape, after inspecting the live schema:
56
92
 
57
93
  ```bash
58
- spicyapi --json models get provider/model
94
+ spicyapi --json models get family/version/task
59
95
  spicyapi --json tasks create \
60
- --model provider/model \
96
+ --model family/version/task \
61
97
  --input-file ./input.json \
62
- --mature \
63
98
  --idempotency-key 7b5a89dd-1ec3-4ec8-8246-6c76cc863665 \
64
99
  --yes \
65
100
  --wait
66
101
  ```
67
102
 
68
- The model and fields above are structural placeholders, not claims that a model exists. Replace them
69
- only with values returned by the live catalog. Omit `--mature` when the caller did not request that
70
- execution mode.
103
+ The model and fields above are structural placeholders, not claims that a model exists. Replace the
104
+ placeholder only with a value returned by the live catalog; never assemble a model ID from a
105
+ publisher, family name, version, or task you guessed.
106
+
107
+ ## Discovery and bounded batches
108
+
109
+ The model list is not paginated: do not invent cursor, page, or next-page calls. Narrow discovery
110
+ with `search`, `modality`, `task`, or the public model creator in `provider`. Fetch schema and
111
+ examples only when needed; an existing complete selected-model record can serve multiple inputs. Use
112
+ enum values, required fields, defaults, ranges, and examples from that record, without changing an
113
+ uploaded reference or guessing an unsupported parameter.
114
+
115
+ There is no batch-create API. For an authorized batch, reuse the selected schema, bound the number
116
+ of concurrent requests, and assign one persistent idempotency key per item. Each different input
117
+ requires its own exact quote and authorized charge; a schema cache is not a price lock. Record each
118
+ accepted task ID as it arrives, handle each result separately, and respect `Retry-After` on rate
119
+ limits. Do not submit the entire batch again because one item timed out. Keep file bytes outside
120
+ MCP; use the upload helper or CLI for local media.
71
121
 
72
122
  ## Uncertain responses and retries
73
123
 
@@ -77,8 +127,18 @@ execution mode.
77
127
  merely because the first response was uncertain.
78
128
  - A task retry creates a new task and may reserve funds again. It is valid only for server-accepted
79
129
  source states such as `failed` or `expired`; the server remains authoritative.
80
- - Use `request_id` and `Retry-After` metadata from structured errors. Do not expose the API key
81
- while reporting an error.
130
+ - Once a task ID is known, continue waiting or retrieve that task; a local timeout does not cancel
131
+ it. Do not call task retry merely to resume waiting.
132
+ - Business code `40901` means the price confirmation needs attention: obtain a fresh exact quote and
133
+ confirm it. Do not silently discard `quoteId` or `expectedCost`. An idempotency conflict is
134
+ different: keep the original request associated with its key instead of overwriting it.
135
+ - After input-validation errors, correct fields against the selected live schema; after insufficient
136
+ funds, use the Console or inspect balance. Repeatedly sending the same rejected request is not
137
+ recovery.
138
+ - Inspect `state` after get/wait; successfully retrieving a task does not mean generation succeeded.
139
+ Only claim usable media when the asset has a ready URL; pending assets require another lookup.
140
+ - Use `request_id` when available and `Retry-After` metadata from structured errors. Do not expose
141
+ the API key while reporting an error.
82
142
 
83
143
  ## SDK outline
84
144
 
@@ -86,10 +146,15 @@ execution mode.
86
146
  import { SpicyClient } from "@spicyapi/sdk";
87
147
 
88
148
  const client = new SpicyClient();
89
- const model = await client.getModel("live/catalog-id");
149
+ const model = await client.getModel("family/version/task");
150
+ const payload = { model: model.model, input: {/* fields from model.inputSchema */} };
151
+ const quote = await client.quoteTask(payload);
152
+ console.log(quote.estimatedCost, quote.maxCharge, quote.expiresAt);
153
+ // 在用户确认这份报价后继续;持久化同一幂等键以恢复响应丢失。
154
+ const idempotencyKey = crypto.randomUUID();
90
155
  const accepted = await client.createTask(
91
- { model: model.model, input: {/* fields from model.inputSchema */} },
92
- { idempotencyKey: crypto.randomUUID() },
156
+ { ...payload, quoteId: quote.quoteId, expectedCost: quote.estimatedCost },
157
+ { idempotencyKey },
93
158
  );
94
159
  const terminal = await client.waitForTask(accepted.taskId);
95
160
  ```
@@ -105,4 +170,20 @@ taskId.timestamp.hex(sha256(raw_body))
105
170
 
106
171
  Payload version 1 reads `task_id`; version 2 reads `data.taskId`. Perform constant-time signature
107
172
  comparison before checking timestamp tolerance. For v2, use `request_id` as the stable delivery
108
- identifier when present.
173
+ identifier when present. Read ready `data.output.assets[].url` directly from a complete verified v2
174
+ payload. Only retrieve the task if the payload is incomplete, assets are pending, or the URL has
175
+ expired. Delivery retries can refresh URLs while retaining the same `request_id`; deduplicate by
176
+ that identifier rather than hashing the whole body. Never send the SpicyAPI key when downloading a
177
+ signed asset URL.
178
+
179
+ ## Recover task history
180
+
181
+ Use `client.listTasks`, `spicyapi tasks list`, or read-only `spicyapi_tasks_list` to find
182
+ current-key tasks after a restart or missed callback. Results contain metadata only. Pass fixed UTC
183
+ `from`/`to` dates and unchanged state/model filters while following `nextCursor`; use the
184
+ task-detail tool only for a selected result. Defaults are seven days and 20 items; limits are 92
185
+ days and 100 items.
186
+
187
+ This is a recovery tool, not a mandatory generation preflight. If acceptance of a submitted request
188
+ is uncertain, first retry unchanged input with its original Idempotency-Key. An empty page is not
189
+ proof that no task was accepted and must not trigger a second billable submission with a new key.
@@ -15,25 +15,23 @@
15
15
 
16
16
  Task creation and retry can reserve funds. Before execution:
17
17
 
18
- 1. Fetch the live model record and balance.
19
- 2. Explain the exact model ID and that live price, operational API-key scope, balance, and routing
20
- are decided on acceptance.
18
+ 1. Use the selected model's live schema; reuse a complete record already read in this workflow.
19
+ Check balance only when requested or diagnosing funds. The server enforces funds at acceptance.
20
+ 2. Explain the exact request's model ID, USD estimate, maximum charge and expiry. CLI and MCP create
21
+ obtain the quote internally; do not quote separately first. SDK code obtains one quote and passes
22
+ it unchanged into creation after confirmation.
21
23
  3. Obtain explicit user confirmation, unless the user's current request already explicitly
22
24
  authorizes that exact billable test.
23
25
  4. Mint or preserve one idempotency key and surface it to the user.
24
26
 
25
27
  Decline, cancellation of confirmation, schema mismatch, or missing confirmation must result in zero
26
- API calls.
28
+ billable create calls; a read-only quote may already have been fetched.
27
29
 
28
- ## Mature request metadata
30
+ ## Model capabilities
29
31
 
30
- - `mature` is a caller-declared request value, not an account or API-key entitlement.
31
- - Forward the boolean unchanged. Do not preflight it against the catalog capability, selected
32
- channel, age, consent, or a platform content policy, and do not claim the platform approved it.
33
- - A model service may still return its own refusal. Report that observed task failure normally;
34
- never invent a platform gate or retry under a different mode without user direction.
35
- - Applicable legal, age, consent, real-person, and end-user access responsibilities remain with the
36
- customer and are not established by technical success.
32
+ Select the model that supports the intended use. SpicyAPI does not require a content-mode flag or
33
+ perform per-request content review. A model can still fail according to its own implementation;
34
+ report the observed normalized failure.
37
35
 
38
36
  ## Real integration tests
39
37