@spicyapi/skill 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/SECURITY.md +4 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +51 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/skill/index.d.ts +13 -0
- package/dist/src/skill/index.d.ts.map +1 -0
- package/dist/src/skill/index.js +66 -0
- package/dist/src/skill/index.js.map +1 -0
- package/package.json +44 -0
- package/skills/spicyapi/SKILL.md +61 -0
- package/skills/spicyapi/agents/openai.yaml +8 -0
- package/skills/spicyapi/references/api-workflows.md +108 -0
- package/skills/spicyapi/references/safety.md +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SpicyAPI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @spicyapi/skill
|
|
2
|
+
|
|
3
|
+
Official standalone Agent Skill for SpicyAPI. It follows the portable Agent Skills directory format
|
|
4
|
+
and contains the Skill assets plus an atomic installer; it does not include the SDK, CLI, or MCP
|
|
5
|
+
server. Codex is one supported client, not the package's scope or identity.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx --yes --package=@spicyapi/skill spicyapi-skill install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The default destination is `~/.agents/skills/spicyapi`. Set `AGENTS_SKILLS_DIR` to another shared
|
|
12
|
+
skills directory, or pass the exact client-specific directory with `--target`.
|
|
13
|
+
|
|
14
|
+
Inspect the packaged source directory with:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx --yes --package=@spicyapi/skill spicyapi-skill path
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use `--target <absolute-directory>` for Codex, Claude Code, Cursor, or another Agent
|
|
21
|
+
Skills-compatible client when it uses a different skills directory. Use `--force` only when you
|
|
22
|
+
intend to replace that exact destination.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { getBundledSkillPath, installSpicySkill } from "./skill/index.js";
|
|
4
|
+
function usage() {
|
|
5
|
+
return [
|
|
6
|
+
"Usage: spicyapi-skill [path | install] [--target <directory>] [--force] [--json]",
|
|
7
|
+
"",
|
|
8
|
+
"Commands:",
|
|
9
|
+
" path Show the packaged Skill directory",
|
|
10
|
+
" install Install the Skill into the shared Agent Skills directory (default)",
|
|
11
|
+
].join("\n");
|
|
12
|
+
}
|
|
13
|
+
function optionValue(args, name) {
|
|
14
|
+
const index = args.indexOf(name);
|
|
15
|
+
if (index < 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
const value = args[index + 1];
|
|
18
|
+
if (!value || value.startsWith("--"))
|
|
19
|
+
throw new Error(`${name} requires a value`);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
async function main() {
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
25
|
+
process.stdout.write(`${usage()}\n`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const command = args[0]?.startsWith("--") ? "install" : (args[0] ?? "install");
|
|
29
|
+
const json = args.includes("--json");
|
|
30
|
+
let result;
|
|
31
|
+
if (command === "path") {
|
|
32
|
+
result = { path: await getBundledSkillPath() };
|
|
33
|
+
}
|
|
34
|
+
else if (command === "install") {
|
|
35
|
+
const target = optionValue(args, "--target");
|
|
36
|
+
result = await installSpicySkill({
|
|
37
|
+
...(target === undefined ? {} : { destination: target }),
|
|
38
|
+
force: args.includes("--force"),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error(`unknown command: ${command}`);
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write(json ? `${JSON.stringify(result)}\n` : `${JSON.stringify(result, null, 2)}\n`);
|
|
45
|
+
}
|
|
46
|
+
main().catch((error) => {
|
|
47
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
48
|
+
process.stderr.write(`${message}\n`);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,SAAS,KAAK;IACZ,OAAO;QACL,kFAAkF;QAClF,EAAE;QACF,WAAW;QACX,gDAAgD;QAChD,iFAAiF;KAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,IAAc,EAAE,IAAY;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mBAAmB,CAAC,CAAC;IAClF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,MAAe,CAAC;IACpB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,mBAAmB,EAAE,EAAE,CAAC;IACjD,CAAC;SAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC7C,MAAM,GAAG,MAAM,iBAAiB,CAAC;YAC/B,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;YACxD,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAC9E,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;IACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface InstallSpicySkillOptions {
|
|
2
|
+
destination?: string;
|
|
3
|
+
force?: boolean;
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
}
|
|
6
|
+
export interface InstalledSpicySkill {
|
|
7
|
+
destination: string;
|
|
8
|
+
replaced: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function getBundledSkillPath(): Promise<string>;
|
|
11
|
+
export declare function getDefaultAgentSkillPath(env?: NodeJS.ProcessEnv): string;
|
|
12
|
+
export declare function installSpicySkill(options?: InstallSpicySkillOptions): Promise<InstalledSpicySkill>;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/skill/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAYD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU3D;AAED,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGrF;AAED,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,mBAAmB,CAAC,CA+B9B"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { access, cp, mkdir, rename, rm } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
async function exists(target) {
|
|
7
|
+
try {
|
|
8
|
+
await access(target);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code === "ENOENT")
|
|
13
|
+
return false;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function getBundledSkillPath() {
|
|
18
|
+
const candidates = [
|
|
19
|
+
new URL("../../../skills/spicyapi/", import.meta.url),
|
|
20
|
+
new URL("../../skills/spicyapi/", import.meta.url),
|
|
21
|
+
];
|
|
22
|
+
for (const candidate of candidates) {
|
|
23
|
+
const directory = fileURLToPath(candidate);
|
|
24
|
+
if (await exists(path.join(directory, "SKILL.md")))
|
|
25
|
+
return directory;
|
|
26
|
+
}
|
|
27
|
+
throw new Error("the packaged SpicyAPI Skill could not be found");
|
|
28
|
+
}
|
|
29
|
+
export function getDefaultAgentSkillPath(env = process.env) {
|
|
30
|
+
const skillsRoot = env.AGENTS_SKILLS_DIR?.trim() || path.join(homedir(), ".agents", "skills");
|
|
31
|
+
return path.resolve(skillsRoot, "spicyapi");
|
|
32
|
+
}
|
|
33
|
+
export async function installSpicySkill(options = {}) {
|
|
34
|
+
const source = await getBundledSkillPath();
|
|
35
|
+
const destination = path.resolve(options.destination ?? getDefaultAgentSkillPath(options.env));
|
|
36
|
+
const destinationExists = await exists(destination);
|
|
37
|
+
if (destinationExists && !options.force) {
|
|
38
|
+
throw new Error(`skill already exists at ${destination}; pass --force to replace that exact directory`);
|
|
39
|
+
}
|
|
40
|
+
const parent = path.dirname(destination);
|
|
41
|
+
const nonce = randomUUID();
|
|
42
|
+
const staging = path.join(parent, `.spicyapi-install-${nonce}`);
|
|
43
|
+
const backup = path.join(parent, `.spicyapi-backup-${nonce}`);
|
|
44
|
+
await mkdir(parent, { recursive: true });
|
|
45
|
+
try {
|
|
46
|
+
await cp(source, staging, { recursive: true, errorOnExist: true, force: false });
|
|
47
|
+
if (destinationExists)
|
|
48
|
+
await rename(destination, backup);
|
|
49
|
+
try {
|
|
50
|
+
await rename(staging, destination);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (destinationExists && (await exists(backup)))
|
|
54
|
+
await rename(backup, destination);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (destinationExists)
|
|
58
|
+
await rm(backup, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
await rm(staging, { recursive: true, force: true });
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
return { destination, replaced: destinationExists };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/skill/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spicyapi/skill",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official installable Agent Skill for SpicyAPI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/SpicyAPI/spicy-devkit.git",
|
|
10
|
+
"directory": "packages/skill"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://spicyapi.ai",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://spicyapi.ai/contact"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.13.0"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/src/skill/index.js",
|
|
23
|
+
"types": "./dist/src/skill/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/src/skill/index.d.ts",
|
|
27
|
+
"import": "./dist/src/skill/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"spicyapi-skill": "dist/src/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/src",
|
|
35
|
+
"skills/spicyapi",
|
|
36
|
+
"README.md",
|
|
37
|
+
"SECURITY.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spicyapi
|
|
3
|
+
description:
|
|
4
|
+
Use the SpicyAPI public developer API through the official SDK, CLI, or MCP server to inspect live
|
|
5
|
+
models, create, retry, and wait for media tasks, upload inputs, retrieve outputs, check balances,
|
|
6
|
+
and verify webhooks. Use for SpicyAPI integration and API operations; not for browser-account
|
|
7
|
+
administration, payments, or publishing provider models.
|
|
8
|
+
metadata:
|
|
9
|
+
short-description: Operate SpicyAPI through its SDK, CLI, or MCP server
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# SpicyAPI
|
|
13
|
+
|
|
14
|
+
Use the packaged `spicyapi_*` MCP tools when they are connected. Otherwise use the `spicyapi` CLI
|
|
15
|
+
for operations or `SpicyClient` for application code. Treat the live model catalog and the bundled
|
|
16
|
+
OpenAPI contract as authoritative.
|
|
17
|
+
|
|
18
|
+
Read [references/api-workflows.md](references/api-workflows.md) before composing API, CLI, or MCP
|
|
19
|
+
operations. Read [references/safety.md](references/safety.md) before billable calls, real
|
|
20
|
+
integration tests, webhook handling, credential work, or cleanup.
|
|
21
|
+
|
|
22
|
+
## Required behavior
|
|
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.
|
|
26
|
+
- Pass the model-specific `input` object through unchanged. The SpicyAPI server owns schema, price,
|
|
27
|
+
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.
|
|
34
|
+
- Use environment variables for API keys and webhook secrets. Never put them in arguments, source
|
|
35
|
+
code, prompts, logs, support messages, or committed configuration.
|
|
36
|
+
- Preserve `request_id`, task IDs, upload keys, and idempotency keys in operational results. They
|
|
37
|
+
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.
|
|
40
|
+
- Keep browser-session identity, payment, privacy, account closure, and admin operations in the
|
|
41
|
+
SpicyAPI Console. Do not emulate cookie/CSRF Console routes in this server-side skill.
|
|
42
|
+
- Provider/model onboarding and production enablement are outside this skill unless the user
|
|
43
|
+
explicitly supplies a separate supported contract.
|
|
44
|
+
|
|
45
|
+
## Real validation
|
|
46
|
+
|
|
47
|
+
When the user asks for a real test, make a real request against the selected environment; do not
|
|
48
|
+
substitute a mock and call it verified. Record every created identifier before continuing. Use the
|
|
49
|
+
smallest user-approved billable scenario that the live catalog actually offers, then verify it
|
|
50
|
+
through task retrieval or waiting.
|
|
51
|
+
|
|
52
|
+
Clean up only exact artifacts that the environment exposes a supported deletion mechanism for. The
|
|
53
|
+
public developer API has no task or uploaded-object delete operation, so report those retained
|
|
54
|
+
artifacts and their identifiers honestly instead of claiming deletion or reaching into unrelated
|
|
55
|
+
data stores.
|
|
56
|
+
|
|
57
|
+
## Completion
|
|
58
|
+
|
|
59
|
+
Report the surface used (SDK, CLI, or MCP), live model ID when applicable, idempotency key,
|
|
60
|
+
task/request identifiers, final observed state, and any artifact that could not be removed through a
|
|
61
|
+
supported API. Never report a provider call or cleanup as successful without observing it.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# SpicyAPI API workflows
|
|
2
|
+
|
|
3
|
+
## Runtime configuration
|
|
4
|
+
|
|
5
|
+
Set credentials through the process environment or a secret manager:
|
|
6
|
+
|
|
7
|
+
- `SPICY_API_KEY`: required for authenticated public API operations.
|
|
8
|
+
- `SPICY_API_BASE_URL`: defaults to `https://api.spicyapi.ai/api/v1`.
|
|
9
|
+
- `SPICY_SERVICE_BASE_URL`: defaults to `https://api.spicyapi.ai` and is used for health/readiness.
|
|
10
|
+
- `SPICY_WEBHOOK_SECRET`: used only for local webhook verification.
|
|
11
|
+
|
|
12
|
+
Plain HTTP base URLs are accepted only for loopback development hosts.
|
|
13
|
+
|
|
14
|
+
## Verified public surface
|
|
15
|
+
|
|
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.
|
|
35
|
+
|
|
36
|
+
## Generation sequence
|
|
37
|
+
|
|
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.
|
|
54
|
+
|
|
55
|
+
Example CLI shape, after inspecting the live schema:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
spicyapi --json models get provider/model
|
|
59
|
+
spicyapi --json tasks create \
|
|
60
|
+
--model provider/model \
|
|
61
|
+
--input-file ./input.json \
|
|
62
|
+
--mature \
|
|
63
|
+
--idempotency-key 7b5a89dd-1ec3-4ec8-8246-6c76cc863665 \
|
|
64
|
+
--yes \
|
|
65
|
+
--wait
|
|
66
|
+
```
|
|
67
|
+
|
|
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.
|
|
71
|
+
|
|
72
|
+
## Uncertain responses and retries
|
|
73
|
+
|
|
74
|
+
- Read-only requests can use bounded automatic retries for transient failures.
|
|
75
|
+
- Task creation and retry are automatically retryable only when an idempotency key is present.
|
|
76
|
+
- After a timeout or lost response, reuse the same idempotency key. Never generate a second key
|
|
77
|
+
merely because the first response was uncertain.
|
|
78
|
+
- A task retry creates a new task and may reserve funds again. It is valid only for server-accepted
|
|
79
|
+
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.
|
|
82
|
+
|
|
83
|
+
## SDK outline
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { SpicyClient } from "@spicyapi/sdk";
|
|
87
|
+
|
|
88
|
+
const client = new SpicyClient();
|
|
89
|
+
const model = await client.getModel("live/catalog-id");
|
|
90
|
+
const accepted = await client.createTask(
|
|
91
|
+
{ model: model.model, input: {/* fields from model.inputSchema */} },
|
|
92
|
+
{ idempotencyKey: crypto.randomUUID() },
|
|
93
|
+
);
|
|
94
|
+
const terminal = await client.waitForTask(accepted.taskId);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Webhook verification
|
|
98
|
+
|
|
99
|
+
Verify the exact raw request bytes before parsing. Current signatures are Base64-encoded HMAC-SHA256
|
|
100
|
+
over:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
taskId.timestamp.hex(sha256(raw_body))
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Payload version 1 reads `task_id`; version 2 reads `data.taskId`. Perform constant-time signature
|
|
107
|
+
comparison before checking timestamp tolerance. For v2, use `request_id` as the stable delivery
|
|
108
|
+
identifier when present.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# SpicyAPI safety and cleanup
|
|
2
|
+
|
|
3
|
+
## Credentials and privacy
|
|
4
|
+
|
|
5
|
+
- Load `SPICY_API_KEY`, webhook secrets, and local MCP tokens from environment variables or a secret
|
|
6
|
+
manager.
|
|
7
|
+
- Never accept an API key through a CLI flag. Do not serialize secrets into JSON output, shell
|
|
8
|
+
history, MCP prompts, screenshots, telemetry, test snapshots, or error reports.
|
|
9
|
+
- Treat generation prompts, uploaded media, outputs, callbacks, and task metadata as private
|
|
10
|
+
customer content. Log identifiers and correlation metadata, not payload bodies or presigned URLs.
|
|
11
|
+
- A presigned upload or download URL is a temporary credential. Redact it and do not send the Spicy
|
|
12
|
+
API key to its storage host.
|
|
13
|
+
|
|
14
|
+
## Billable operations
|
|
15
|
+
|
|
16
|
+
Task creation and retry can reserve funds. Before execution:
|
|
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.
|
|
21
|
+
3. Obtain explicit user confirmation, unless the user's current request already explicitly
|
|
22
|
+
authorizes that exact billable test.
|
|
23
|
+
4. Mint or preserve one idempotency key and surface it to the user.
|
|
24
|
+
|
|
25
|
+
Decline, cancellation of confirmation, schema mismatch, or missing confirmation must result in zero
|
|
26
|
+
API calls.
|
|
27
|
+
|
|
28
|
+
## Mature request metadata
|
|
29
|
+
|
|
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.
|
|
37
|
+
|
|
38
|
+
## Real integration tests
|
|
39
|
+
|
|
40
|
+
- Use a dedicated test account/key and a unique marker in permissible metadata or prompt text when
|
|
41
|
+
the live model schema supports it.
|
|
42
|
+
- Record start time, model ID, idempotency key, request IDs, task IDs, uploaded file IDs/keys, and
|
|
43
|
+
output keys immediately.
|
|
44
|
+
- Bound cost and count before starting. Stop after the first representative success for each
|
|
45
|
+
distinct route unless more cases are explicitly required.
|
|
46
|
+
- Verify boundaries with real server responses: unauthorized, invalid schema, insufficient balance,
|
|
47
|
+
idempotent replay, terminal polling, ownership-safe 404, and webhook tampering where the
|
|
48
|
+
environment safely permits them.
|
|
49
|
+
- Never manufacture a success when provider credentials, model availability, funds, or policy
|
|
50
|
+
prevent the route from completing. Preserve the observed error and request ID.
|
|
51
|
+
|
|
52
|
+
## Cleanup
|
|
53
|
+
|
|
54
|
+
Build an exact artifact ledger during testing. Delete or revoke only entries from that ledger and
|
|
55
|
+
verify each deletion through the same supported control plane.
|
|
56
|
+
|
|
57
|
+
The public SpicyAPI developer contract does not expose task cancellation, task deletion,
|
|
58
|
+
uploaded-object deletion, output deletion, or webhook redelivery. Therefore:
|
|
59
|
+
|
|
60
|
+
- Do not claim public-API cleanup for those records.
|
|
61
|
+
- Do not delete adjacent account data or reach into production databases as a workaround.
|
|
62
|
+
- If an authorized isolated local environment exposes direct cleanup, match exact unique IDs,
|
|
63
|
+
confirm the environment, remove only those rows/objects, and verify zero remaining matches.
|
|
64
|
+
- Otherwise report retained task/file IDs and their documented retention behavior to the user.
|
|
65
|
+
|
|
66
|
+
## MCP boundaries
|
|
67
|
+
|
|
68
|
+
- Prefer stdio for local clients.
|
|
69
|
+
- The packaged HTTP server binds only to loopback and requires a separate `SPICY_MCP_HTTP_TOKEN` of
|
|
70
|
+
at least 32 bytes. It must not equal `SPICY_API_KEY`.
|
|
71
|
+
- Never expose the local static-token HTTP mode to a remote interface. Internet-facing MCP requires
|
|
72
|
+
a standards-compliant OAuth resource server with audience-bound tokens and is intentionally not
|
|
73
|
+
provided here.
|
|
74
|
+
- MCP tool annotations are hints, not authorization. Billable execution remains gated by protocol
|
|
75
|
+
elicitation and signed request state.
|
|
76
|
+
|
|
77
|
+
## Account and payment boundaries
|
|
78
|
+
|
|
79
|
+
Registration, login, password recovery, team membership, checkout, saved payment methods, automatic
|
|
80
|
+
recharge, privacy export/deletion, account closure, support, and admin operations use
|
|
81
|
+
browser-session Console contracts. Keep those flows in the product UI unless a first-class
|
|
82
|
+
non-browser authorization contract is explicitly added.
|