@aeon-ai-pay/aigateway 0.1.6 → 0.2.2
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/bin/cli.mjs +4 -1
- package/package.json +1 -1
- package/skills/aigateway/SKILL.md +72 -11
- package/src/commands/sb-tools.mjs +67 -9
- package/src/error-codes.mjs +1 -0
package/bin/cli.mjs
CHANGED
|
@@ -127,7 +127,10 @@ sb
|
|
|
127
127
|
|
|
128
128
|
sb
|
|
129
129
|
.command("tools")
|
|
130
|
-
.description("Fetch and display the AI tool catalog
|
|
130
|
+
.description("Fetch and display the AI tool catalog (with optional filters)")
|
|
131
|
+
.option("--model <id>", "Return only this model (+effectiveSchema)")
|
|
132
|
+
.option("--category <key>", "Return only this category (image / video / tts / etc.)")
|
|
133
|
+
.option("--tier <tier>", "Filter models by tier (price | quality | balanced)")
|
|
131
134
|
.action(async (opts) => {
|
|
132
135
|
const { sbTools } = await import("../src/commands/sb-tools.mjs");
|
|
133
136
|
return sbTools(opts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeon-ai-pay/aigateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "AI Agents discover, invoke, and settle paid LLMs, APIs, and Skills — starting with Skill Boss. No manual key setup. No prepayment. Pay-per-call via x402 or Agent Card.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@ description: >
|
|
|
26
26
|
emoji: "🛰️"
|
|
27
27
|
homepage: https://github.com/AEON-Project/aigateway
|
|
28
28
|
metadata:
|
|
29
|
-
version: "0.2.
|
|
29
|
+
version: "0.2.2"
|
|
30
30
|
author: AEON-Project
|
|
31
31
|
openclaw:
|
|
32
32
|
requires:
|
|
@@ -49,13 +49,29 @@ compatibility: 需要 Node.js >= 25 和 npm
|
|
|
49
49
|
|
|
50
50
|
完整工具索引(每个 `category` 含 `agentTrigger` / `defaultInputsSchema`;每个 `model` 含 `id` / `useCase` / `tier` / 可选 `inputsOverride`)由服务端集中维护,每次实时拉取。**无本地缓存** —— 服务端是 single source of truth,model 新增 / schema 改动立即生效。
|
|
51
51
|
|
|
52
|
-
**Agent 在 Phase 3.2 选 model
|
|
52
|
+
**Agent 在 Phase 3.2 选 model 时**,**优先用 CLI 自带的过滤参数**,避免自己写代码解析 list:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
55
|
+
# 推荐:CLI 端过滤,直接拿你要的
|
|
56
|
+
aigateway sb tools --model replicate/black-forest-labs/flux-schnell # 单 model + effectiveSchema
|
|
57
|
+
aigateway sb tools --category image # 单类别(含所有 models 与 defaultInputsSchema)
|
|
58
|
+
aigateway sb tools --category image --tier price # 按 tier 过滤
|
|
59
|
+
aigateway sb tools --tier quality # 所有类别中 quality 档
|
|
60
|
+
|
|
61
|
+
# 备选:全量 catalog(少用,主要用于探索)
|
|
62
|
+
aigateway sb tools
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**如果必须自己解析**(jq 推荐):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
aigateway sb tools | jq '.data.categories[] | select(.key=="image") | .models[]'
|
|
69
|
+
aigateway sb tools | jq '.data.categories[].models[] | select(.id=="<model_id>")'
|
|
56
70
|
```
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
⚠️ **不要在 Python list 上调 `.find()`** —— list 没有这个方法。用 `next(m for m in ...)` 或 dict 索引化。但通常**根本不需要**自己解析,用 `aigateway sb tools --model X` 就够了。
|
|
73
|
+
|
|
74
|
+
每次都从服务端实时获取,无本地缓存。
|
|
59
75
|
|
|
60
76
|
价格不在 catalog 里:x402 第一阶段(402 响应)会实时返回本次调用的 USDT 金额,CLI 自动展示给用户。
|
|
61
77
|
|
|
@@ -236,16 +252,58 @@ Approve: {approveTx truncated or "already approved"}
|
|
|
236
252
|
|
|
237
253
|
> 同一意图可能落到多个类别(如"做个含图的演示" = `image` + `ui_generation`),按用户**主诉求**选最匹配的一类先做。
|
|
238
254
|
|
|
239
|
-
### 3.2
|
|
255
|
+
### 3.2 列出候选 model,等用户挑
|
|
256
|
+
|
|
257
|
+
⭐ **默认模式**:**AI 不擅自选 model**,而是把候选 + 价格列给用户,让用户拍板。**推荐默认选最便宜的**(按 `tier: "price"` 优先排序)。
|
|
258
|
+
|
|
259
|
+
**例外**:用户原话已经指定了 model(`"用 flux-2-max 画"`) → 直接用,跳过列表。
|
|
260
|
+
|
|
261
|
+
#### 候选展示模板(按字面渲染)
|
|
262
|
+
|
|
263
|
+
跑 `aigateway sb tools --category <key>` 拿到该类所有 model,**按 tier 排序**(price → balanced → quality)后用以下模板渲染:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
✨ 可用 model({category 中文名})
|
|
267
|
+
|
|
268
|
+
# Model ID 价格 档位
|
|
269
|
+
1 {model_id} ${price}{unit} ← 推荐
|
|
270
|
+
2 {model_id} ${price}{unit}
|
|
271
|
+
3 {model_id} ${price}{unit}
|
|
272
|
+
...
|
|
273
|
+
|
|
274
|
+
直接回车或输入 1 用推荐项;或输入序号 / 完整 model_id 选其它。
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**字段规则**:
|
|
278
|
+
- 第 1 行**永远**是 tier=`price` 档(最便宜),并加 `← 推荐` 后缀
|
|
279
|
+
- `${price}` 取 catalog 中 `model.price`(USD 数值,保留小数)
|
|
280
|
+
- `{unit}` 按 category 推断(见下表)
|
|
281
|
+
- 用户只输序号 `2` / 完整 `model_id` / 直接回车 都接受
|
|
282
|
+
- 用户没回应 → 用 #1(推荐项)
|
|
283
|
+
|
|
284
|
+
#### Category → 价格单位映射
|
|
285
|
+
|
|
286
|
+
| Category | 价格单位 `{unit}` |
|
|
287
|
+
| --- | --- |
|
|
288
|
+
| `image` | `/张` |
|
|
289
|
+
| `video` | `/秒` |
|
|
290
|
+
| `tts` | `/1K 字符` |
|
|
291
|
+
| `stt` | `/分钟` |
|
|
292
|
+
| `embeddings` | `/M tokens` |
|
|
293
|
+
| `search` / `scraper` / `social_data` / `email` / `sms` / `document` / `ui_generation` / `financial` / `news` / `geolocation` / `utility` / `apify` | `/次` |
|
|
294
|
+
|
|
295
|
+
> 价格是**预估单价**,**最终金额**以 x402 第一阶段(402 响应)的实时数据为准(CLI 会在调用后展示 `💰 Charged`)。
|
|
240
296
|
|
|
241
|
-
|
|
297
|
+
#### 用户偏好覆盖
|
|
242
298
|
|
|
243
|
-
|
|
|
299
|
+
| 用户原话 | AI 动作 |
|
|
244
300
|
| --- | --- |
|
|
245
|
-
| "
|
|
246
|
-
| "
|
|
247
|
-
|
|
|
248
|
-
|
|
|
301
|
+
| (无偏好)"画一只猫" | 列候选 → 等用户选 → 用户选完才调 `sb invoke` |
|
|
302
|
+
| "用便宜的画一只猫" | 直接用候选 #1(最便宜),跳过等待 |
|
|
303
|
+
| "用最好的画一只猫" | 直接用 `tier: "quality"` 档第一个,跳过等待 |
|
|
304
|
+
| "用 flux-2-max 画一只猫" | 直接用 `flux-2-max`,**完全跳过候选展示** |
|
|
305
|
+
| 用户输入序号 (e.g. "2") | 用候选列表第 2 行的 model |
|
|
306
|
+
| 用户输入完整 model_id | 用该 model_id |
|
|
249
307
|
|
|
250
308
|
**查询 model 清单**:跑 `aigateway sb tools` 拿到完整 catalog,从 stdout envelope 的 `data.categories[].models[]` 中按 `tier` 挑 `model_id`。
|
|
251
309
|
|
|
@@ -531,6 +589,9 @@ aigateway clean # 卸载 skill、清缓存
|
|
|
531
589
|
| Phase 2 第一行 | `> Topping up wallet...` |
|
|
532
590
|
| Phase 2 成功 header | `✅ Wallet prepared` |
|
|
533
591
|
| Phase 4.1 sb invoke 第一行 | `> Invoking {model_id}...` |
|
|
592
|
+
| Phase 3.2 候选列表 header | `✨ 可用 model({category 中文名})` |
|
|
593
|
+
| Phase 3.2 推荐项后缀 | `← 推荐` |
|
|
594
|
+
| Phase 3.2 候选行格式 | `{#} {model_id} ${price}{unit}` |
|
|
534
595
|
| Phase 5.2 通用成功 header | `✅ Done` |
|
|
535
596
|
| Phase 5.2 Powered 行 | `🧩 Powered by Skillboss · {model_id}` |
|
|
536
597
|
| Phase 5.2 Path 行 | `📁 Path {localPath}` |
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* sb-tools: fetch and display the AI tool catalog from the server.
|
|
3
3
|
*
|
|
4
|
-
* aigateway sb tools
|
|
4
|
+
* aigateway sb tools # full catalog
|
|
5
|
+
* aigateway sb tools --model <id> # single model (+effectiveSchema)
|
|
6
|
+
* aigateway sb tools --category <key> # single category (+ all models)
|
|
7
|
+
* aigateway sb tools --tier <price|quality|balanced> # filter by tier
|
|
8
|
+
* (--category and --tier can combine)
|
|
5
9
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
10
|
+
* Filters are server-agnostic — applied client-side after fetching full catalog.
|
|
11
|
+
* Letting Agents target exactly what they need (via --model / --category) avoids
|
|
12
|
+
* the typical `.find()`-on-list type confusion when parsing the JSON manually.
|
|
8
13
|
*
|
|
9
|
-
* Endpoint: GET {serviceUrl}/open/api/skillBoss/tools-catalog
|
|
10
|
-
* (free, no x402; price fields are stripped server-side)
|
|
14
|
+
* Endpoint: GET {serviceUrl}/open/api/skillBoss/tools-catalog (no x402)
|
|
11
15
|
*/
|
|
12
16
|
import { resolve } from "../config.mjs";
|
|
13
17
|
import { emitOk, emitErr, logInfo } from "../output.mjs";
|
|
14
|
-
import { fetchCatalog } from "../catalog.mjs";
|
|
18
|
+
import { fetchCatalog, findModel } from "../catalog.mjs";
|
|
15
19
|
|
|
16
20
|
export async function sbTools(opts) {
|
|
17
21
|
const serviceUrl = resolve(opts.serviceUrl, "AIGATEWAY_SERVICE_URL", "serviceUrl");
|
|
@@ -21,9 +25,9 @@ export async function sbTools(opts) {
|
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
logInfo("Fetching tools catalog from server...");
|
|
24
|
-
let
|
|
28
|
+
let catalog;
|
|
25
29
|
try {
|
|
26
|
-
|
|
30
|
+
catalog = await fetchCatalog(serviceUrl);
|
|
27
31
|
} catch (e) {
|
|
28
32
|
emitErr("sb-tools", "CATALOG_FETCH_FAILED", {
|
|
29
33
|
message: `Failed to fetch tools catalog: ${e.message}`,
|
|
@@ -33,5 +37,59 @@ export async function sbTools(opts) {
|
|
|
33
37
|
return;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
// ─── --model <id> : return single model with effectiveSchema ────────────
|
|
41
|
+
if (opts.model) {
|
|
42
|
+
const found = findModel(catalog, opts.model);
|
|
43
|
+
if (!found) {
|
|
44
|
+
emitErr("sb-tools", "INVALID_MODEL_ID", {
|
|
45
|
+
message: `Model "${opts.model}" not found in catalog.`,
|
|
46
|
+
model: opts.model,
|
|
47
|
+
availableCategories: catalog.categories.map((c) => c.key),
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
emitOk("sb-tools", {
|
|
52
|
+
mode: "single-model",
|
|
53
|
+
category: found.category.key,
|
|
54
|
+
model: found.model,
|
|
55
|
+
effectiveSchema: found.effectiveSchema,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── --category <key> : return single category ──────────────────────────
|
|
61
|
+
if (opts.category) {
|
|
62
|
+
const cat = catalog.categories.find((c) => c.key === opts.category);
|
|
63
|
+
if (!cat) {
|
|
64
|
+
emitErr("sb-tools", "CATEGORY_NOT_FOUND", {
|
|
65
|
+
message: `Category "${opts.category}" not found.`,
|
|
66
|
+
category: opts.category,
|
|
67
|
+
availableCategories: catalog.categories.map((c) => c.key),
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const filtered = applyTierFilter(cat, opts.tier);
|
|
72
|
+
emitOk("sb-tools", { mode: "single-category", category: filtered });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── --tier alone : filter all categories ───────────────────────────────
|
|
77
|
+
if (opts.tier) {
|
|
78
|
+
const cats = catalog.categories
|
|
79
|
+
.map((c) => applyTierFilter(c, opts.tier))
|
|
80
|
+
.filter((c) => c.models.length > 0);
|
|
81
|
+
emitOk("sb-tools", { ...catalog, categories: cats, mode: "tier-filtered", tier: opts.tier });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── default: full catalog ──────────────────────────────────────────────
|
|
86
|
+
emitOk("sb-tools", catalog, { success: true, ...catalog });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function applyTierFilter(cat, tier) {
|
|
90
|
+
if (!tier) return cat;
|
|
91
|
+
return {
|
|
92
|
+
...cat,
|
|
93
|
+
models: cat.models.filter((m) => m.tier === tier),
|
|
94
|
+
};
|
|
37
95
|
}
|
package/src/error-codes.mjs
CHANGED
|
@@ -25,6 +25,7 @@ export const ERROR_CODES = {
|
|
|
25
25
|
INVALID_INPUTS: { exit: 1, message: "Inputs failed schema validation." },
|
|
26
26
|
INPUTS_FILE_NOT_FOUND: { exit: 1, message: "Inputs file (passed via --inputs @path) not found." },
|
|
27
27
|
INVALID_MODEL_ID: { exit: 1, message: "Server rejected the model id." },
|
|
28
|
+
CATEGORY_NOT_FOUND: { exit: 1, message: "Category not found in catalog." },
|
|
28
29
|
MODEL_PRICING_NOT_CONFIGURED: { exit: 1, message: "This model is not yet priced on the gateway. Ask the operator to add it to skillboss-pricing.json." },
|
|
29
30
|
INVALID_BODY: { exit: 1, message: "Server rejected the request body." },
|
|
30
31
|
TOPUP_REQUIRED: { exit: 1, message: "Wallet top-up required. Choose an amount and rerun with --topup-amount <usdt>." },
|