@neta-art/neta-search-cli 0.2.10 → 0.2.14

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.
Files changed (2) hide show
  1. package/neta-search.mjs +33 -13
  2. package/package.json +1 -1
package/neta-search.mjs CHANGED
@@ -26912,9 +26912,10 @@ function detectEnv() {
26912
26912
  const spaceId = process.env["COHUB_SPACE_ID"] ?? "";
26913
26913
  const apiBase = process.env["NETASEARCH_API"] ?? // sandbox 内(平台注入 COHUB_*):默认 prod Service——真实用户的事件该写
26914
26914
  // prod 账本;否则 prod sandbox 的事件会全部写进 dev 账本(review #76 抓过的
26915
- // 隐患)。sandbox 外(本地/验收):默认 dev 公网地址,NETASEARCH_API 可覆盖。
26915
+ // 隐患)。sandbox 外:默认 prod 公网地址(2026-10-09 起;此前是 dev,公网 npm 版发布前改),
26916
+ // 本地验收用 NETASEARCH_API 指 dev。
26916
26917
  // 不依赖平台注入 NETASEARCH_API——COHUB_SPACE_ID 就是平台已给的信号。
26917
- (spaceId !== "" ? "http://neta-search-api.recsys.svc.cluster.local" : "https://dev-search.neta.art");
26918
+ (spaceId !== "" ? "http://neta-search-api.recsys.svc.cluster.local" : "https://search.neta.art");
26918
26919
  return {
26919
26920
  apiBase,
26920
26921
  actorId,
@@ -27063,21 +27064,29 @@ var ArchiveNotFound = class extends Error {
27063
27064
  };
27064
27065
  async function fetchArchive(env, id) {
27065
27066
  const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27067
+ const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS) });
27068
+ if (res.status === 404) throw new ArchiveNotFound(`${id} \u4E0D\u5B58\u5728\uFF0C\u6216\u8FD8\u6CA1\u6709\u4E0A\u67B6\u7684\u7248\u672C`);
27069
+ const location = res.headers.get("location");
27070
+ if (res.status < 300 || res.status >= 400 || !location) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1AHTTP ${res.status}`);
27071
+ const digest = res.headers.get("x-skill-digest") ?? "";
27072
+ const name = res.headers.get("x-skill-name") ?? id.split("/").pop();
27073
+ if (!digest) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1A\u670D\u52A1\u7AEF\u6CA1\u6709\u7ED9\u51FA\u7248\u672C\uFF08x-skill-digest\uFF09`);
27074
+ const body = await download(new URL(location, url).toString(), id);
27075
+ const files = untarGz(body);
27076
+ const got = digestOf(new Map(files.map((f) => [f.path, f.data])));
27077
+ if (got !== digest) throw new Error(`\u4E0B\u8F7D\u7684 ${id} \u5185\u5BB9\u6821\u9A8C\u5931\u8D25\uFF08\u671F\u671B ${digest}\uFF0C\u5B9E\u9645 ${got}\uFF09`);
27078
+ return { id, name, digest, files };
27079
+ }
27080
+ async function download(url, id) {
27066
27081
  for (let attempt = 1; ; attempt++) {
27067
27082
  const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
27068
- if (res.status === 404) throw new ArchiveNotFound(`${id} \u4E0D\u5B58\u5728\uFF0C\u6216\u4F5C\u8005\u8FD8\u6CA1\u6709\u53EF\u5206\u4EAB\u7684\u7248\u672C`);
27069
- if (res.status === 503 && attempt < MAX_TRIES) {
27070
- const wait = Number(res.headers.get("retry-after") ?? "2") || 2;
27071
- await new Promise((r) => setTimeout(r, wait * 1e3));
27083
+ if (res.status === 202 && attempt < MAX_TRIES) {
27084
+ const body = await res.json().catch(() => ({}));
27085
+ await new Promise((r) => setTimeout(r, Math.min(Math.max(body.retryAfterMs ?? 2e3, 250), 5e3)));
27072
27086
  continue;
27073
27087
  }
27074
- if (!res.ok) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1AHTTP ${res.status}`);
27075
- const digest = res.headers.get("x-skill-digest") ?? "";
27076
- const name = res.headers.get("x-skill-name") ?? id.split("/").pop();
27077
- const files = untarGz(Buffer.from(await res.arrayBuffer()));
27078
- const got = digestOf(new Map(files.map((f) => [f.path, f.data])));
27079
- if (!digest || got !== digest) throw new Error(`\u4E0B\u8F7D\u7684 ${id} \u5185\u5BB9\u6821\u9A8C\u5931\u8D25\uFF08\u671F\u671B ${digest}\uFF0C\u5B9E\u9645 ${got}\uFF09`);
27080
- return { id, name, digest, files };
27088
+ if (!res.ok) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1Aregistry \u8FD4\u56DE HTTP ${res.status}`);
27089
+ return Buffer.from(await res.arrayBuffer());
27081
27090
  }
27082
27091
  }
27083
27092
  function untarGz(gz) {
@@ -27270,8 +27279,19 @@ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
27270
27279
  import { randomUUID } from "node:crypto";
27271
27280
  import { join as join4 } from "node:path";
27272
27281
  var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
27282
+ var HANDLE_RE = /^(@[a-z0-9_.-]{1,64}|[a-z0-9][a-z0-9-]{0,31})\/[a-z0-9][a-z0-9-]{0,63}$/i;
27283
+ async function resolveHandle(env, input) {
27284
+ const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
27285
+ if (!r.ok) return null;
27286
+ const out = await r.json();
27287
+ return typeof out.id === "string" && parseId(out.id) ? out.id : null;
27288
+ }
27273
27289
  async function cmdUse(idOrName, opts) {
27274
27290
  const env = detectEnv();
27291
+ if (!parseId(idOrName) && HANDLE_RE.test(idOrName)) {
27292
+ const resolved = await resolveHandle(env, idOrName).catch(() => null);
27293
+ if (resolved) idOrName = resolved;
27294
+ }
27275
27295
  const parsed = parseId(idOrName);
27276
27296
  const name = parsed?.name ?? idOrName;
27277
27297
  const mine = !parsed || parsed.spaceId === env.spaceId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/neta-search-cli",
3
- "version": "0.2.10",
3
+ "version": "0.2.14",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {