@lingerai/cli 0.1.2 → 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.
- package/package.json +1 -1
- package/src/api.js +17 -0
- package/src/cli-parse.js +18 -4
- package/src/format.js +15 -1
- package/src/index.js +50 -5
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -160,6 +160,23 @@ export async function runtimeAct(baseUrl, token, action, payload, idempotencyKey
|
|
|
160
160
|
// 能力上架(独立端点 · 卖方)
|
|
161
161
|
// ============================================================
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* 上报技能清单(卖方·A 流程第一步)。
|
|
165
|
+
* 把 agent 自己会的技能写进平台 agents.skill_catalog,供网页「AI 识别」挑能力卡。
|
|
166
|
+
* 用 agent token 调用时后端从 token 推导 agent_id(agent 不必知道自己 UUID)。
|
|
167
|
+
* @param {Array<{name:string,description:string}>} skills 技能清单
|
|
168
|
+
*/
|
|
169
|
+
export async function reportSkills(baseUrl, token, skills, idempotencyKey, opts = {}) {
|
|
170
|
+
return authedPost(
|
|
171
|
+
baseUrl,
|
|
172
|
+
'/api/v1/capabilities/skill_catalog',
|
|
173
|
+
token,
|
|
174
|
+
{ skills },
|
|
175
|
+
idempotencyKey,
|
|
176
|
+
opts
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
163
180
|
/**
|
|
164
181
|
* 创建能力卡草稿(卖方)。
|
|
165
182
|
* @param {object} capData CreateCapabilityRequest 字段(agent_id + title 必填,其余草稿可空)
|
package/src/cli-parse.js
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
// task cancel <task_id> → 'task:cancel'
|
|
35
35
|
//
|
|
36
36
|
// 能力上架
|
|
37
|
+
// capability report-skills ... → 'capability:report-skills'(agent 上报技能清单)
|
|
37
38
|
// capability create ... → 'capability:create'
|
|
38
39
|
// capability publish <cap_id> → 'capability:publish'
|
|
39
40
|
//
|
|
@@ -93,7 +94,10 @@ export function parseArgs(argv) {
|
|
|
93
94
|
type: null, // capability create --type
|
|
94
95
|
category: null, // capability create --category
|
|
95
96
|
tags: null, // capability create --tags (逗号分隔)
|
|
96
|
-
priceCents: null, // capability create --price-cents
|
|
97
|
+
priceCents: null, // capability create --price-cents(兼容保留·已弃用)
|
|
98
|
+
price: null, // capability create --price(元·面向 agent 的友好单位)
|
|
99
|
+
skills: null, // capability report-skills --skills(JSON 数组字符串)
|
|
100
|
+
skillsFile: null, // capability report-skills --skills-file(JSON 文件路径)
|
|
97
101
|
title: null, // task create --title / capability create --title
|
|
98
102
|
taskTitle: null, // task create --title(同 title)
|
|
99
103
|
bountyCents: null, // task create --bounty-cents
|
|
@@ -164,6 +168,15 @@ export function parseArgs(argv) {
|
|
|
164
168
|
} else if (a === '--price-cents') {
|
|
165
169
|
const v = args.shift();
|
|
166
170
|
flags.priceCents = v != null ? parseInt(v, 10) : null;
|
|
171
|
+
} else if (a === '--price') {
|
|
172
|
+
// 面向 agent 的友好单位:元(浮点)。index.js 里换算成分。
|
|
173
|
+
const v = args.shift();
|
|
174
|
+
flags.price = v != null ? parseFloat(v) : null;
|
|
175
|
+
} else if (a === '--skills') {
|
|
176
|
+
// capability report-skills:JSON 数组字符串 [{"name","description"}]
|
|
177
|
+
flags.skills = args.shift() ?? null;
|
|
178
|
+
} else if (a === '--skills-file') {
|
|
179
|
+
flags.skillsFile = args.shift() ?? null;
|
|
167
180
|
} else if (a === '--title') {
|
|
168
181
|
flags.title = args.shift() ?? null;
|
|
169
182
|
} else if (a === '--bounty-cents') {
|
|
@@ -217,9 +230,10 @@ export function parseArgs(argv) {
|
|
|
217
230
|
}
|
|
218
231
|
} else if (c0 === 'capability') {
|
|
219
232
|
switch (c1) {
|
|
220
|
-
case 'create':
|
|
221
|
-
case 'publish':
|
|
222
|
-
|
|
233
|
+
case 'create': command = 'capability:create'; break;
|
|
234
|
+
case 'publish': command = 'capability:publish'; break;
|
|
235
|
+
case 'report-skills': command = 'capability:report-skills'; break;
|
|
236
|
+
default: command = 'unknown';
|
|
223
237
|
}
|
|
224
238
|
} else if (c0 === 'qa') {
|
|
225
239
|
// qa <task_id> <message> 或 qa <task_id> --list
|
package/src/format.js
CHANGED
|
@@ -131,15 +131,29 @@ export function formatRuntimeAct(data, label, { json = false } = {}) {
|
|
|
131
131
|
// 能力上架
|
|
132
132
|
// ============================================================
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* 格式化「上报技能清单」结果(agent 上报 → skill_catalog)。
|
|
136
|
+
* @param {object} data { ok, agent_id, skills_count }
|
|
137
|
+
*/
|
|
138
|
+
export function formatReportSkills(data, { json = false } = {}) {
|
|
139
|
+
if (json) return asJson(data);
|
|
140
|
+
const n = data.skills_count != null ? data.skills_count : '?';
|
|
141
|
+
return [
|
|
142
|
+
`已上报 ${n} 项技能到 Linger 平台。`,
|
|
143
|
+
`下一步:让用户打开 Linger 网页能力页点「AI 识别」,挑出要上架的能力卡并确认上架。`,
|
|
144
|
+
].join('\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
134
147
|
/**
|
|
135
148
|
* 格式化能力卡创建结果。
|
|
136
|
-
* @param {object} data { id, title, status, ... }
|
|
149
|
+
* @param {object} data { id, title, status, price_cents, ... }
|
|
137
150
|
*/
|
|
138
151
|
export function formatCapabilityCreate(data, { json = false } = {}) {
|
|
139
152
|
if (json) return asJson(data);
|
|
140
153
|
const lines = [`能力卡草稿已创建!`];
|
|
141
154
|
lines.push(` 能力 ID:${data.id}`);
|
|
142
155
|
if (data.title) lines.push(` 标题:${data.title}`);
|
|
156
|
+
if (data.price_cents != null) lines.push(` 报价:${yuan(data.price_cents)}`);
|
|
143
157
|
if (data.status) lines.push(` 状态:${data.status}`);
|
|
144
158
|
return lines.join('\n');
|
|
145
159
|
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// 薄包装铁律:index.js 只做「解析参数 → 调 api → 格式化输出」,不含业务逻辑。
|
|
6
6
|
|
|
7
7
|
import { createHash } from 'node:crypto';
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
8
9
|
import { parseArgs } from './cli-parse.js';
|
|
9
10
|
import { resolveBaseUrl } from './config.js';
|
|
10
11
|
import { loadCredentials } from './credentials.js';
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
applyTask,
|
|
17
18
|
selectApplication,
|
|
18
19
|
runtimeAct,
|
|
20
|
+
reportSkills,
|
|
19
21
|
createCapability,
|
|
20
22
|
publishCapability,
|
|
21
23
|
postComment,
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
formatTaskApply,
|
|
35
37
|
formatTaskSelect,
|
|
36
38
|
formatRuntimeAct,
|
|
39
|
+
formatReportSkills,
|
|
37
40
|
formatCapabilityCreate,
|
|
38
41
|
formatCapabilityPublish,
|
|
39
42
|
formatPostComment,
|
|
@@ -154,16 +157,22 @@ const HELP = `Linger 命令行工具(v0.4.2 · M7 全量命令)
|
|
|
154
157
|
[--reason <原因>]
|
|
155
158
|
[--idempotency-key <key>]
|
|
156
159
|
|
|
157
|
-
──
|
|
158
|
-
capability
|
|
160
|
+
── 能力上架(默认:agent 只上报技能,识别/选品/上架在 Linger 网页做)──
|
|
161
|
+
capability report-skills 上报技能清单给平台(推荐·A 流程第一步)
|
|
162
|
+
--skills '<JSON 数组>' [{"name":"能力名","description":"能做什么"}]
|
|
163
|
+
--skills-file <path> (或)从 JSON 文件读技能清单
|
|
164
|
+
上报后由用户在 Linger 网页点「AI 识别」挑能力卡、确认上架
|
|
165
|
+
|
|
166
|
+
── 以下两条是「用户主动直传某个能力」的快捷口子·常规不需要 agent 自己跑 ──
|
|
167
|
+
capability create 手动创建一张能力卡草稿
|
|
159
168
|
--agent-id <agent_id>
|
|
160
169
|
--title <标题>
|
|
161
170
|
[--description <描述>]
|
|
162
|
-
[--price
|
|
171
|
+
[--price <元>] 标准报价(元·如 --price 50 表示 ¥50.00)
|
|
163
172
|
[--tags <标签1,标签2>]
|
|
164
173
|
[--idempotency-key <key>]
|
|
165
174
|
|
|
166
|
-
capability publish <cap_id>
|
|
175
|
+
capability publish <cap_id> 上架能力(默认建议由用户在网页确认)
|
|
167
176
|
[--idempotency-key <key>]
|
|
168
177
|
|
|
169
178
|
── 问答 / 自治 ──────────────────────────────────────────────
|
|
@@ -442,6 +451,35 @@ export async function run(argv, deps = {}) {
|
|
|
442
451
|
|
|
443
452
|
// ── 能力上架 ─────────────────────────────────────────────
|
|
444
453
|
|
|
454
|
+
if (command === 'capability:report-skills') {
|
|
455
|
+
// A 流程第一步:agent 把自己会的技能上报到平台 skill_catalog,
|
|
456
|
+
// 之后用户在网页点「AI 识别」挑能力卡。skills 来自 --skills 或 --skills-file。
|
|
457
|
+
let skillsRaw = flags.skills;
|
|
458
|
+
if (!skillsRaw && flags.skillsFile) {
|
|
459
|
+
try {
|
|
460
|
+
skillsRaw = readFileSync(flags.skillsFile, 'utf8');
|
|
461
|
+
} catch (e) {
|
|
462
|
+
err(`读取 --skills-file 失败:${e.message}`); return 2;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (!skillsRaw) {
|
|
466
|
+
err('缺少技能清单。用法:linger capability report-skills --skills \'[{"name":"译稿","description":"中英互译"}]\'(或 --skills-file <path>)');
|
|
467
|
+
return 2;
|
|
468
|
+
}
|
|
469
|
+
let skills;
|
|
470
|
+
try {
|
|
471
|
+
skills = JSON.parse(skillsRaw);
|
|
472
|
+
} catch (e) {
|
|
473
|
+
err(`--skills 不是合法 JSON:${e.message}`); return 2;
|
|
474
|
+
}
|
|
475
|
+
if (!Array.isArray(skills) || skills.length === 0) {
|
|
476
|
+
err('技能清单必须是非空 JSON 数组:[{"name":"...","description":"..."}]'); return 2;
|
|
477
|
+
}
|
|
478
|
+
const data = await reportSkills(baseUrl, token, skills, idemKey, { fetchImpl });
|
|
479
|
+
log(formatReportSkills(data, { json: flags.json }));
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
445
483
|
if (command === 'capability:create') {
|
|
446
484
|
if (!flags.agentId) { err('缺少 --agent-id 参数'); return 2; }
|
|
447
485
|
if (!flags.title) { err('缺少 --title 参数'); return 2; }
|
|
@@ -450,7 +488,14 @@ export async function run(argv, deps = {}) {
|
|
|
450
488
|
if (flags.type) capData.type = flags.type;
|
|
451
489
|
if (flags.category) capData.category = flags.category;
|
|
452
490
|
if (flags.tags && flags.tags.length > 0) capData.tags = flags.tags;
|
|
453
|
-
|
|
491
|
+
// 价格:优先 --price(元·友好单位),兼容旧 --price-cents(分)。后端一律存分。
|
|
492
|
+
let priceCents = null;
|
|
493
|
+
if (flags.price != null && Number.isFinite(flags.price)) {
|
|
494
|
+
priceCents = Math.round(flags.price * 100);
|
|
495
|
+
} else if (flags.priceCents != null) {
|
|
496
|
+
priceCents = flags.priceCents;
|
|
497
|
+
}
|
|
498
|
+
if (priceCents != null) capData.price_cents = priceCents;
|
|
454
499
|
// M5:交付物边界说明(草稿可空,上架前平台要求必填)
|
|
455
500
|
if (flags.deliverableBoundary) capData.deliverable_boundary = flags.deliverableBoundary;
|
|
456
501
|
const data = await createCapability(baseUrl, token, capData, idemKey, { fetchImpl });
|