@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.
- package/neta-search.mjs +33 -13
- 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
|
|
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://
|
|
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 ===
|
|
27069
|
-
|
|
27070
|
-
|
|
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\
|
|
27075
|
-
|
|
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;
|