@neta-art/neta-search-cli 0.2.8 → 0.2.10
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 +3 -1
- package/neta-search.mjs +91 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,13 +5,15 @@ Command-line client for **neta-search**, the skill registry of the Cohub / Neta
|
|
|
5
5
|
```bash
|
|
6
6
|
npx -y @neta-art/neta-search-cli@0.2 skills find "generate video" # search (Chinese or English)
|
|
7
7
|
npx -y @neta-art/neta-search-cli@0.2 skills find "browser automation" --json
|
|
8
|
-
npx -y @neta-art/neta-search-cli@0.2 skills use <id> #
|
|
8
|
+
npx -y @neta-art/neta-search-cli@0.2 skills use <id> # print the SKILL.md (pulled into a temp dir; no project files written)
|
|
9
9
|
npx -y @neta-art/neta-search-cli@0.2 skills add <id> # install into .agents/skills/
|
|
10
10
|
npx -y @neta-art/neta-search-cli@0.2 skills init <name> # scaffold your own skill
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
`<id>` comes from `find` results, e.g. `cohub:<spaceId>/<name>`.
|
|
14
14
|
|
|
15
|
+
`use` prints the skill directory and the full SKILL.md, so an agent can follow it right away; relative paths in SKILL.md resolve against that directory. If the local entry is a pointer (frontmatter `metadata.neta-skill-id`), `use` fetches the full skill it points to.
|
|
16
|
+
|
|
15
17
|
Inside a Cohub sandbox the CLI picks up the sandbox identity automatically; elsewhere it runs anonymously.
|
|
16
18
|
|
|
17
19
|
Single file, no runtime dependencies. Node.js 20+.
|
package/neta-search.mjs
CHANGED
|
@@ -3247,13 +3247,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3247
3247
|
contextOptions = void 0;
|
|
3248
3248
|
}
|
|
3249
3249
|
const outputContext = this._getOutputContext(contextOptions);
|
|
3250
|
-
const
|
|
3250
|
+
const eventContext2 = {
|
|
3251
3251
|
error: outputContext.error,
|
|
3252
3252
|
write: outputContext.write,
|
|
3253
3253
|
command: this
|
|
3254
3254
|
};
|
|
3255
|
-
this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp",
|
|
3256
|
-
this.emit("beforeHelp",
|
|
3255
|
+
this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext2));
|
|
3256
|
+
this.emit("beforeHelp", eventContext2);
|
|
3257
3257
|
let helpInformation = this.helpInformation({ error: outputContext.error });
|
|
3258
3258
|
if (deprecatedCallback) {
|
|
3259
3259
|
helpInformation = deprecatedCallback(helpInformation);
|
|
@@ -3265,9 +3265,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3265
3265
|
if (this._getHelpOption()?.long) {
|
|
3266
3266
|
this.emit(this._getHelpOption().long);
|
|
3267
3267
|
}
|
|
3268
|
-
this.emit("afterHelp",
|
|
3268
|
+
this.emit("afterHelp", eventContext2);
|
|
3269
3269
|
this._getCommandAndAncestors().forEach(
|
|
3270
|
-
(command) => command.emit("afterAllHelp",
|
|
3270
|
+
(command) => command.emit("afterAllHelp", eventContext2)
|
|
3271
3271
|
);
|
|
3272
3272
|
}
|
|
3273
3273
|
/**
|
|
@@ -26915,7 +26915,14 @@ function detectEnv() {
|
|
|
26915
26915
|
// 隐患)。sandbox 外(本地/验收):默认 dev 公网地址,NETASEARCH_API 可覆盖。
|
|
26916
26916
|
// 不依赖平台注入 NETASEARCH_API——COHUB_SPACE_ID 就是平台已给的信号。
|
|
26917
26917
|
(spaceId !== "" ? "http://neta-search-api.recsys.svc.cluster.local" : "https://dev-search.neta.art");
|
|
26918
|
-
return {
|
|
26918
|
+
return {
|
|
26919
|
+
apiBase,
|
|
26920
|
+
actorId,
|
|
26921
|
+
spaceId,
|
|
26922
|
+
sessionId: process.env["COHUB_SESSION_ID"] ?? "",
|
|
26923
|
+
turnId: process.env["COHUB_TURN_ID"] ?? "",
|
|
26924
|
+
anonymous: actorId === "" || spaceId === ""
|
|
26925
|
+
};
|
|
26919
26926
|
}
|
|
26920
26927
|
function searchClient(env) {
|
|
26921
26928
|
const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
|
|
@@ -26925,12 +26932,27 @@ function feedbackClient(env) {
|
|
|
26925
26932
|
const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
|
|
26926
26933
|
return createClient(SkillFeedbackService, transport);
|
|
26927
26934
|
}
|
|
26928
|
-
|
|
26935
|
+
function eventContext(env, extra = {}) {
|
|
26936
|
+
const ctx = { source: "cli", ...extra };
|
|
26937
|
+
if (env.sessionId) ctx["session_id"] = env.sessionId;
|
|
26938
|
+
if (env.turnId) ctx["turn_id"] = env.turnId;
|
|
26939
|
+
return ctx;
|
|
26940
|
+
}
|
|
26941
|
+
async function reportSkillEvent(env, event, skillId, digest, skillName, extra = {}) {
|
|
26929
26942
|
if (env.anonymous) return false;
|
|
26930
26943
|
try {
|
|
26931
26944
|
await feedbackClient(env).reportSkillEvents({
|
|
26932
26945
|
events: [
|
|
26933
|
-
{
|
|
26946
|
+
{
|
|
26947
|
+
event,
|
|
26948
|
+
skillId,
|
|
26949
|
+
skillName,
|
|
26950
|
+
contentHash: digest,
|
|
26951
|
+
actorId: env.actorId,
|
|
26952
|
+
spaceId: env.spaceId,
|
|
26953
|
+
anonymous: false,
|
|
26954
|
+
context: eventContext(env, extra)
|
|
26955
|
+
}
|
|
26934
26956
|
]
|
|
26935
26957
|
});
|
|
26936
26958
|
return true;
|
|
@@ -27156,6 +27178,23 @@ function writeSkill(target, files, source) {
|
|
|
27156
27178
|
renameSync(staging, target);
|
|
27157
27179
|
if (hadOld) rmSync(trash, { recursive: true, force: true });
|
|
27158
27180
|
}
|
|
27181
|
+
var POINTER_RE = /^\s*neta-skill-id:\s*["']?(cohub:[^"'\s]+)["']?\s*$/m;
|
|
27182
|
+
function frontmatterOf(raw) {
|
|
27183
|
+
if (!raw.startsWith("---\n")) return "";
|
|
27184
|
+
const end = raw.indexOf("\n---", 4);
|
|
27185
|
+
return end < 0 ? "" : raw.slice(4, end);
|
|
27186
|
+
}
|
|
27187
|
+
function pointerTargetOf(raw) {
|
|
27188
|
+
const m = POINTER_RE.exec(frontmatterOf(raw));
|
|
27189
|
+
return m ? m[1] : null;
|
|
27190
|
+
}
|
|
27191
|
+
function pointerTarget(dir) {
|
|
27192
|
+
try {
|
|
27193
|
+
return pointerTargetOf(readFileSync2(join2(dir, "SKILL.md"), "utf8"));
|
|
27194
|
+
} catch {
|
|
27195
|
+
return null;
|
|
27196
|
+
}
|
|
27197
|
+
}
|
|
27159
27198
|
function sharedOptOut(dir) {
|
|
27160
27199
|
try {
|
|
27161
27200
|
const raw = readFileSync2(join2(dir, "SKILL.md"), "utf8");
|
|
@@ -27227,8 +27266,10 @@ async function graduate(env, name, target, opts) {
|
|
|
27227
27266
|
}
|
|
27228
27267
|
|
|
27229
27268
|
// cli/src/skills/use.ts
|
|
27230
|
-
import { existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
27269
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
27270
|
+
import { randomUUID } from "node:crypto";
|
|
27231
27271
|
import { join as join4 } from "node:path";
|
|
27272
|
+
var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
|
|
27232
27273
|
async function cmdUse(idOrName, opts) {
|
|
27233
27274
|
const env = detectEnv();
|
|
27234
27275
|
const parsed = parseId(idOrName);
|
|
@@ -27238,6 +27279,8 @@ async function cmdUse(idOrName, opts) {
|
|
|
27238
27279
|
let where = "local";
|
|
27239
27280
|
let reportId = null;
|
|
27240
27281
|
let notice = null;
|
|
27282
|
+
let fetchId = parsed && !mine ? idOrName : null;
|
|
27283
|
+
let via = opts.via ? opts.via.slice(0, 32) : null;
|
|
27241
27284
|
const installed = join4(SKILLS_DIR, name);
|
|
27242
27285
|
const draft = join4(DRAFTS_DIR, name);
|
|
27243
27286
|
if (skillDirExists(installed)) {
|
|
@@ -27263,11 +27306,32 @@ async function cmdUse(idOrName, opts) {
|
|
|
27263
27306
|
where = "mod";
|
|
27264
27307
|
}
|
|
27265
27308
|
}
|
|
27266
|
-
if (
|
|
27309
|
+
if (path) {
|
|
27310
|
+
const target = pointerTarget(path);
|
|
27311
|
+
if (target !== null) {
|
|
27312
|
+
if (!parseId(target)) {
|
|
27313
|
+
console.error(`${join4(path, "SKILL.md")} \u662F\u6307\u9488\uFF0C\u4F46\u5B83\u7684 neta-skill-id \u4E0D\u662F\u5408\u6CD5 id\uFF1A${target}`);
|
|
27314
|
+
return 1;
|
|
27315
|
+
}
|
|
27316
|
+
via = "pointer";
|
|
27317
|
+
fetchId = target;
|
|
27318
|
+
path = null;
|
|
27319
|
+
reportId = null;
|
|
27320
|
+
notice = null;
|
|
27321
|
+
}
|
|
27322
|
+
}
|
|
27323
|
+
if (!path && fetchId) {
|
|
27267
27324
|
try {
|
|
27268
|
-
const a = await fetchArchive(env,
|
|
27269
|
-
|
|
27270
|
-
if (
|
|
27325
|
+
const a = await fetchArchive(env, fetchId);
|
|
27326
|
+
const skillMd2 = a.files.find((f) => f.path === "SKILL.md");
|
|
27327
|
+
if (skillMd2 && pointerTargetOf(skillMd2.data.toString("utf8")) !== null) {
|
|
27328
|
+
console.error(`${fetchId} \u4E0B\u8F7D\u5230\u7684\u5185\u5BB9\u8FD8\u662F\u6307\u9488\uFF0C\u505C\u5728\u8FD9\u91CC\uFF08\u4E0D\u8DDF\u7B2C\u4E8C\u5C42\uFF0C\u9632\u6B62\u7ED5\u5708\uFF09\u3002`);
|
|
27329
|
+
return 1;
|
|
27330
|
+
}
|
|
27331
|
+
const p = parseId(a.id);
|
|
27332
|
+
path = join4(FETCH_ROOT, p.spaceId, p.name);
|
|
27333
|
+
const have = readSource(path);
|
|
27334
|
+
if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest });
|
|
27271
27335
|
reportId = a.id;
|
|
27272
27336
|
where = "fetched";
|
|
27273
27337
|
} catch (e) {
|
|
@@ -27279,14 +27343,21 @@ async function cmdUse(idOrName, opts) {
|
|
|
27279
27343
|
console.error(`skill "${idOrName}" \u672C\u5730\u6CA1\u6709\u2014\u2014\u5148 neta-search skills find "${name}" \u62FF\u5230 id\u3002`);
|
|
27280
27344
|
return 1;
|
|
27281
27345
|
}
|
|
27282
|
-
const
|
|
27283
|
-
|
|
27284
|
-
|
|
27346
|
+
const useId = randomUUID();
|
|
27347
|
+
const extra = { use_id: useId };
|
|
27348
|
+
if (via) extra["via"] = via;
|
|
27349
|
+
const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name, extra) : false;
|
|
27350
|
+
const skillMd = readFileSync3(join4(path, "SKILL.md"), "utf8");
|
|
27351
|
+
if (opts.json) {
|
|
27352
|
+
console.log(JSON.stringify({ skill: reportId ?? name, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
|
|
27353
|
+
} else {
|
|
27285
27354
|
const label = { local: "\u672C\u5730", mod: "\u5E73\u53F0 mod", fetched: "\u5DF2\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55" }[where];
|
|
27286
|
-
console.log(`${name} \u5C31\u7EEA\uFF08${label}\uFF09
|
|
27287
|
-
console.log(`
|
|
27355
|
+
console.log(`${reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
|
|
27356
|
+
console.log(`skill \u76EE\u5F55\uFF1A${path}`);
|
|
27357
|
+
console.log(`SKILL.md \u91CC\u7684\u76F8\u5BF9\u8DEF\u5F84\u90FD\u4ECE\u8FD9\u4E2A\u76EE\u5F55\u7B97\u8D77\u3002\u4E0B\u9762\u5C31\u662F SKILL.md \u7684\u5168\u6587\uFF0C\u7167\u7740\u505A\u5373\u53EF\uFF08\u4E0D\u7528\u518D\u8BFB\u4E00\u6B21\u6587\u4EF6\uFF09\u3002`);
|
|
27288
27358
|
console.log(`
|
|
27289
|
-
|
|
27359
|
+
----- SKILL.md -----
|
|
27360
|
+
${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}----- end -----`);
|
|
27290
27361
|
}
|
|
27291
27362
|
if (notice && reportId && !env.anonymous) shareNotice(reportId, path, notice === "draft", reported);
|
|
27292
27363
|
return 0;
|
|
@@ -27377,7 +27448,7 @@ skills.command("find").description("\u627E skill\uFF08\u751F\u6001\u8D26\u672C+\
|
|
|
27377
27448
|
skills.command("add").description("\u88C5 skill\uFF08find \u7ED3\u679C\u7684 id\uFF1A\u4E0B\u8F7D\u4F5C\u8005\u7528\u8FC7\u7684\u90A3\u4E00\u7248\uFF1B\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684\u540D\u5B57\uFF1A\u79FB\u5230 .agents/skills/\uFF09").argument("<id-or-name>", "find \u8FD4\u56DE\u7684 skill id\uFF0C\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684 skill \u540D").option("--dir <dir>", "\u5B89\u88C5\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (idOrName, opts) => {
|
|
27378
27449
|
await run(cmdAdd(idOrName, opts));
|
|
27379
27450
|
});
|
|
27380
|
-
skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u672C\u5730\u6709\u2192\
|
|
27451
|
+
skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u6253\u5370 SKILL.md \u5168\u6587\u548C skill \u76EE\u5F55\uFF1B\u672C\u5730\u6709\u2192\u7528\u672C\u5730\u7684\uFF1B\u672C\u5730\u662F\u6307\u9488\u6216\u6CA1\u6709\u2192\u6309 id \u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55\uFF09").argument("<id-or-name>", "skill id \u6216 name").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").option("--via <channel>", "\u4ECE\u54EA\u4E2A\u6E20\u9053\u6765\u7684\uFF08\u8BB0\u8FDB\u4F7F\u7528\u8BB0\u5F55\uFF1Bhub mod \u7684\u6307\u9488\u5199 pointer\uFF09").action(async (idOrName, opts) => {
|
|
27381
27452
|
await run(cmdUse(idOrName, opts));
|
|
27382
27453
|
});
|
|
27383
27454
|
skills.command("init").description("\u521B\u5EFA skill \u811A\u624B\u67B6\uFF08SKILL.md + frontmatter \u89C4\u8303\uFF09").argument("[name]", "skill \u540D\uFF08kebab-case\uFF09").option("--here", "\u5728\u5F53\u524D\u76EE\u5F55\u751F\u6210\uFF08\u9ED8\u8BA4\u751F\u6210\u5230 ./<name>/ \u5B50\u76EE\u5F55\uFF09").action(async (name, opts) => {
|