@webskill/sdk 0.1.5 → 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.
@@ -1,16 +1,15 @@
1
- import { A as validateSkills, C as parseSkillPackManifest, D as resolveArchiveLimits, O as resolveInsideRoot, S as parseSkillMarkdown, i as SKILL_MANIFEST_FILE, j as verifyManifest, k as unzipWithLimits, p as buildManifest, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, v as exportSkills, w as readResponseWithLimit, y as isValidSkillName } from "./dist-fZFZaf43.js";
2
- import { M as normalizeToolContent, N as parseBridgeRequest, S as bridgeError, c as FsArtifactStore, j as networkUrlHost, k as isNetworkAllowed, l as FsMemoryStore, o as CapabilityApproval } from "./dist-DuGN5x0v.js";
1
+ import { A as unzipWithLimits, C as parseSkillMarkdown, M as verifyManifest, O as resolveArchiveLimits, T as readResponseWithLimit, b as isValidSkillName, f as atomicWriteText, i as SKILL_MANIFEST_FILE, j as validateSkills, k as resolveInsideRoot, m as buildManifest, r as SKILLS_LOCKFILE, s as SKILL_PACK_FILE, u as WebSkillError, w as parseSkillPackManifest, y as exportSkills } from "./dist-D0saNPi_.js";
2
+ import { A as isNetworkAllowed, C as bridgeError, M as networkUrlHost, N as normalizeToolContent, P as parseBridgeRequest, c as FsArtifactStore, l as FsMemoryStore, o as CapabilityApproval } from "./dist-BS5OpedX.js";
3
+ import { createRequire } from "node:module";
3
4
  import { unzipSync, zipSync } from "fflate";
4
- import { existsSync, promises, readFileSync } from "node:fs";
5
+ import { existsSync, promises } from "node:fs";
5
6
  import path from "node:path";
6
7
  import { tmpdir } from "node:os";
7
8
  import { fileURLToPath, pathToFileURL } from "node:url";
8
9
  import { format, promisify } from "node:util";
9
10
  import { Worker } from "node:worker_threads";
10
- import { parseSync } from "oxc-parser";
11
11
  import { createInterface } from "node:readline/promises";
12
12
  import { mkdtemp } from "node:fs/promises";
13
- import * as tar from "tar";
14
13
  import { execFile } from "node:child_process";
15
14
  import { createHash } from "node:crypto";
16
15
 
@@ -122,6 +121,14 @@ var NodeFS = class {
122
121
  throw this.#mapError(e, p);
123
122
  }
124
123
  }
124
+ async rename(from, to) {
125
+ await promises.mkdir(path.dirname(toPlatform$3(to)), { recursive: true });
126
+ try {
127
+ await promises.rename(toPlatform$3(from), toPlatform$3(to));
128
+ } catch (e) {
129
+ throw this.#mapError(e, from);
130
+ }
131
+ }
125
132
  #mapError(e, p) {
126
133
  if (isEnoent(e)) return new WebSkillError("FS_NOT_FOUND", `Path not found: ${p}`);
127
134
  return e;
@@ -512,6 +519,16 @@ var SandboxedScriptExecutor = class {
512
519
  }
513
520
  }
514
521
  };
522
+ const require_ = createRequire(import.meta.url);
523
+ let parseSyncCache;
524
+ function loadParseSync() {
525
+ if (!parseSyncCache) try {
526
+ parseSyncCache = require_("oxc-parser").parseSync;
527
+ } catch (e) {
528
+ throw new WebSkillError("TOOL_UNSUPPORTED", "The \"oxc-parser\" package is required for schema inference; install it first (npm i oxc-parser)", e);
529
+ }
530
+ return parseSyncCache;
531
+ }
515
532
  const PLACEHOLDER_PREFIX = "Inferred placeholder (unsupported type: ";
516
533
  const placeholder = (text) => ({
517
534
  type: "string",
@@ -561,7 +578,7 @@ var TypeMapper = class {
561
578
  };
562
579
  return placeholder(this.#text(t));
563
580
  }
564
- #union(t, seen) {
581
+ #union(t, _seen) {
565
582
  const values = [];
566
583
  for (const member of t.types ?? []) {
567
584
  const value = member.literal?.value ?? member.value;
@@ -661,7 +678,7 @@ var OxcSchemaInferer = class {
661
678
  let program;
662
679
  let comments;
663
680
  try {
664
- const result = parseSync(options?.fileName ?? "script.ts", source);
681
+ const result = loadParseSync()(options?.fileName ?? "script.ts", source);
665
682
  if (result.errors?.length > 0) return void 0;
666
683
  program = result.program;
667
684
  comments = result.comments ?? [];
@@ -677,7 +694,7 @@ var OxcSchemaInferer = class {
677
694
  const jsdoc = comments.filter((c) => c.type === "Block" && typeof c.value === "string" && c.value.includes("@param") && typeof c.end === "number" && c.end <= runStart).sort((a, b) => b.end - a.end)[0];
678
695
  if (!jsdoc) return void 0;
679
696
  const reparse = (typeText) => {
680
- const r = parseSync("__t.ts", `type __T = ${typeText};`);
697
+ const r = loadParseSync()("__t.ts", `type __T = ${typeText};`);
681
698
  if (r.errors?.length > 0) return void 0;
682
699
  return r.program.body.find((n) => n.type === "TSTypeAliasDeclaration")?.typeAnnotation;
683
700
  };
@@ -833,6 +850,13 @@ var CliUiBridge = class {
833
850
  const messageOf$4 = (e) => e instanceof Error ? e.message : String(e);
834
851
  const toPlatform$1 = (p) => p.split("/").join(path.sep);
835
852
  const isZipArchive = (data) => data.length > 1 && data[0] === 80 && data[1] === 75;
853
+ let tarModule$1;
854
+ async function loadTar$1() {
855
+ tarModule$1 ??= await import("tar").catch((e) => {
856
+ throw new WebSkillError("TOOL_UNSUPPORTED", "The \"tar\" package is required for tar/tar.gz archives; install it first (npm i tar)", e);
857
+ });
858
+ return tarModule$1;
859
+ }
836
860
  /**
837
861
  * zip 解包(fflate 流式,单条目/总解压体积上限)。每个条目路径过 resolveInsideRoot:
838
862
  * 拒绝 `..` 段、绝对路径、反斜杠穿越(zip-slip 防护)。
@@ -853,7 +877,8 @@ async function extractTarFile(archivePath, destRoot, limits) {
853
877
  try {
854
878
  const entryPaths = [];
855
879
  let total = 0;
856
- await tar.t({
880
+ const tarMod = await loadTar$1();
881
+ await tarMod.t({
857
882
  file: toPlatform$1(archivePath),
858
883
  onentry: (entry) => {
859
884
  entryPaths.push(entry.path);
@@ -864,7 +889,7 @@ async function extractTarFile(archivePath, destRoot, limits) {
864
889
  }
865
890
  });
866
891
  for (const p of entryPaths) if (!p.replace(/\\/g, "/").split("/").every((s) => s === "" || s === ".")) resolveInsideRoot(destRoot, p);
867
- await tar.x({
892
+ await tarMod.x({
868
893
  file: toPlatform$1(archivePath),
869
894
  cwd: toPlatform$1(destRoot),
870
895
  filter: (_p, entry) => !("type" in entry) || entry.type !== "SymbolicLink" && entry.type !== "Link"
@@ -916,6 +941,13 @@ async function removeDirQuiet(fs, dir) {
916
941
  } catch {}
917
942
  }
918
943
  const messageOf$3 = (e) => e instanceof Error ? e.message : String(e);
944
+ let tarModule;
945
+ async function loadTar() {
946
+ tarModule ??= await import("tar").catch((e) => {
947
+ throw new WebSkillError("TOOL_UNSUPPORTED", "The \"tar\" package is required for tar/tar.gz archives; install it first (npm i tar)", e);
948
+ });
949
+ return tarModule;
950
+ }
919
951
  /** 导出技能目录全部文件(含 manifest)为 zip/tar 归档,返回 outPath */
920
952
  async function exportArchive(fs, skillRoot, options) {
921
953
  try {
@@ -923,7 +955,7 @@ async function exportArchive(fs, skillRoot, options) {
923
955
  const files = {};
924
956
  for (const rel of await listFiles(fs, skillRoot)) files[rel] = await fs.readBinary(`${skillRoot}/${rel}`);
925
957
  await fs.writeBinary(options.outPath, zipSync(files, { level: 6 }));
926
- } else await tar.c({
958
+ } else await (await loadTar()).c({
927
959
  file: toPlatform$1(options.outPath),
928
960
  cwd: toPlatform$1(skillRoot)
929
961
  }, ["."]);
@@ -945,7 +977,7 @@ async function readArchiveManifest(fs, archivePath) {
945
977
  }
946
978
  let manifestText;
947
979
  try {
948
- await tar.t({
980
+ await (await loadTar()).t({
949
981
  file: toPlatform$1(archivePath),
950
982
  onentry: (entry) => {
951
983
  if (entry.path === "webskill.skill-manifest.json" || entry.path.endsWith(`/${"webskill.skill-manifest.json"}`)) {
@@ -1151,15 +1183,19 @@ async function readLockfile(fs, managedRoot) {
1151
1183
  async function upsertLockEntry(fs, managedRoot, name, entry) {
1152
1184
  const lockfile = await readLockfile(fs, managedRoot);
1153
1185
  lockfile.skills[name] = entry;
1154
- await fs.writeText(`${managedRoot}/${SKILLS_LOCKFILE}`, JSON.stringify(lockfile, null, 2));
1186
+ await writeLockfileAtomic(fs, managedRoot, lockfile);
1155
1187
  return lockfile;
1156
1188
  }
1157
1189
  async function removeLockEntry(fs, managedRoot, name) {
1158
1190
  const lockfile = await readLockfile(fs, managedRoot);
1159
1191
  delete lockfile.skills[name];
1160
- await fs.writeText(`${managedRoot}/${SKILLS_LOCKFILE}`, JSON.stringify(lockfile, null, 2));
1192
+ await writeLockfileAtomic(fs, managedRoot, lockfile);
1161
1193
  return lockfile;
1162
1194
  }
1195
+ /** 原子写:先写临时文件再 rename(进程崩溃不留下半写 lockfile) */
1196
+ async function writeLockfileAtomic(fs, managedRoot, lockfile) {
1197
+ await atomicWriteText(fs, `${managedRoot}/${SKILLS_LOCKFILE}`, JSON.stringify(lockfile, null, 2));
1198
+ }
1163
1199
  const EXCLUDED_FILES = /* @__PURE__ */ new Set([SKILL_MANIFEST_FILE, SKILLS_LOCKFILE]);
1164
1200
  /** 递归收集技能目录内的文件(相对路径,posix 分隔) */
1165
1201
  async function walkFiles(fs, root, prefix = "") {
@@ -1235,6 +1271,10 @@ var SkillManager = class {
1235
1271
  this.#archiveLimits = deps.archiveLimits;
1236
1272
  this.#onChanged = deps.onChanged;
1237
1273
  }
1274
+ /** 托管根目录(治理发布归档捕获等只读场景) */
1275
+ get managedRoot() {
1276
+ return this.#managedRoot;
1277
+ }
1238
1278
  async install(source, options) {
1239
1279
  const fs = this.#fs;
1240
1280
  const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-install-"))).split(path.sep).join("/");
@@ -1458,142 +1498,6 @@ var SkillManager = class {
1458
1498
  return options.outPath;
1459
1499
  }
1460
1500
  };
1461
- /** 解析 .env(简单 KEY=VALUE 行解析,不引依赖);文件缺失返回 undefined */
1462
- function readEnvVars(dotenvPath) {
1463
- let raw;
1464
- try {
1465
- raw = readFileSync(dotenvPath, "utf8");
1466
- } catch {
1467
- return;
1468
- }
1469
- const vars = /* @__PURE__ */ new Map();
1470
- for (const line of raw.split(/\r?\n/)) {
1471
- const trimmed = line.trim();
1472
- if (trimmed === "" || trimmed.startsWith("#")) continue;
1473
- const eq = trimmed.indexOf("=");
1474
- if (eq <= 0) continue;
1475
- const key = trimmed.slice(0, eq).trim();
1476
- let value = trimmed.slice(eq + 1).trim();
1477
- if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1478
- vars.set(key, value);
1479
- }
1480
- return vars;
1481
- }
1482
- /**
1483
- * 读取 VITE_BASE_URL / VITE_API_KEY / VITE_MODEL_NAME;缺项返回 undefined 供测试 skip 判断。
1484
- */
1485
- function loadLlmConfigFromEnv(dotenvPath = ".env") {
1486
- const vars = readEnvVars(dotenvPath);
1487
- if (!vars) return void 0;
1488
- const baseUrl = vars.get("VITE_BASE_URL");
1489
- const apiKey = vars.get("VITE_API_KEY");
1490
- const model = vars.get("VITE_MODEL_NAME");
1491
- if (!baseUrl || !apiKey || !model) return void 0;
1492
- return {
1493
- baseUrl,
1494
- apiKey,
1495
- model
1496
- };
1497
- }
1498
- /** ANTHROPIC_API_KEY / ANTHROPIC_MODEL(可选 ANTHROPIC_BASE_URL);缺项返回 undefined */
1499
- function loadAnthropicConfigFromEnv(dotenvPath = ".env") {
1500
- const vars = readEnvVars(dotenvPath);
1501
- if (!vars) return void 0;
1502
- const apiKey = vars.get("ANTHROPIC_API_KEY");
1503
- const model = vars.get("ANTHROPIC_MODEL");
1504
- if (!apiKey || !model) return void 0;
1505
- const baseUrl = vars.get("ANTHROPIC_BASE_URL");
1506
- return {
1507
- apiKey,
1508
- model,
1509
- ...baseUrl ? { baseUrl } : {}
1510
- };
1511
- }
1512
- /** GOOGLE_API_KEY / GOOGLE_MODEL(可选 GOOGLE_BASE_URL);缺项返回 undefined */
1513
- function loadGoogleConfigFromEnv(dotenvPath = ".env") {
1514
- const vars = readEnvVars(dotenvPath);
1515
- if (!vars) return void 0;
1516
- const apiKey = vars.get("GOOGLE_API_KEY");
1517
- const model = vars.get("GOOGLE_MODEL");
1518
- if (!apiKey || !model) return void 0;
1519
- const baseUrl = vars.get("GOOGLE_BASE_URL");
1520
- return {
1521
- apiKey,
1522
- model,
1523
- ...baseUrl ? { baseUrl } : {}
1524
- };
1525
- }
1526
- let capabilitiesCache;
1527
- /**
1528
- * 探测 LLM 能力(结果模块级缓存):
1529
- * - available:GET /models 可达(与 checkAvailability 同语义)
1530
- * - nonStreaming:最小非流式 chat completion(max_tokens=1,短超时);
1531
- * HTTP 400/404 或明确不支持非流式的错误 → false
1532
- * - streaming 能力由 available 推断(SSE 解析在 SDK 侧实现)
1533
- * 环境变量 VITE_LLM_NON_STREAMING=0 可显式强制 nonStreaming=false(模拟流式-only 回归)。
1534
- */
1535
- function probeLlmCapabilities(config) {
1536
- capabilitiesCache ??= doProbeLlmCapabilities(config);
1537
- return capabilitiesCache;
1538
- }
1539
- async function doProbeLlmCapabilities(config) {
1540
- const baseUrl = config.baseUrl.replace(/\/+$/, "");
1541
- const headers = { authorization: `Bearer ${config.apiKey}` };
1542
- let available = false;
1543
- try {
1544
- available = (await fetch(`${baseUrl}/models`, {
1545
- headers,
1546
- signal: AbortSignal.timeout(1e4)
1547
- })).ok;
1548
- } catch {
1549
- available = false;
1550
- }
1551
- if (!available) return {
1552
- available: false,
1553
- nonStreaming: false,
1554
- reason: "endpoint unreachable or unauthorized"
1555
- };
1556
- if (process.env["VITE_LLM_NON_STREAMING"] === "0") return {
1557
- available: true,
1558
- nonStreaming: false,
1559
- reason: "non-streaming disabled via VITE_LLM_NON_STREAMING=0 (simulated)"
1560
- };
1561
- try {
1562
- const res = await fetch(`${baseUrl}/chat/completions`, {
1563
- method: "POST",
1564
- headers: {
1565
- ...headers,
1566
- "content-type": "application/json"
1567
- },
1568
- body: JSON.stringify({
1569
- model: config.model,
1570
- messages: [{
1571
- role: "user",
1572
- content: "OK"
1573
- }],
1574
- max_tokens: 1
1575
- }),
1576
- signal: AbortSignal.timeout(15e3)
1577
- });
1578
- const contentType = res.headers.get("content-type") ?? "";
1579
- if (res.ok && !contentType.includes("text/event-stream")) return {
1580
- available: true,
1581
- nonStreaming: true
1582
- };
1583
- const detail = (await res.text().catch(() => "")).slice(0, 200);
1584
- return {
1585
- available: true,
1586
- nonStreaming: false,
1587
- reason: `endpoint rejects non-streaming chat completions (HTTP ${res.status})${detail ? `: ${detail}` : ""}`
1588
- };
1589
- } catch (e) {
1590
- return {
1591
- available: true,
1592
- nonStreaming: false,
1593
- reason: `non-streaming probe failed: ${e instanceof Error ? e.message : String(e)}`
1594
- };
1595
- }
1596
- }
1597
1501
 
1598
1502
  //#endregion
1599
- export { NodeScriptExecutor as a, SkillManager as c, loadLlmConfigFromEnv as d, probeLlmCapabilities as f, NodeFS as i, loadAnthropicConfigFromEnv as l, FileArtifactStore as n, OxcSchemaInferer as o, readArchiveManifest as p, FileMemoryStore as r, SandboxedScriptExecutor as s, CliUiBridge as t, loadGoogleConfigFromEnv as u };
1503
+ export { NodeScriptExecutor as a, SkillManager as c, NodeFS as i, exportArchive as l, FileArtifactStore as n, OxcSchemaInferer as o, FileMemoryStore as r, SandboxedScriptExecutor as s, CliUiBridge as t, readArchiveManifest as u };
@@ -0,0 +1,143 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ //#region ../node/dist/env--jJB-TSX.js
4
+ /** 解析 .env(简单 KEY=VALUE 行解析,不引依赖);文件缺失返回 undefined */
5
+ function readEnvVars(dotenvPath) {
6
+ let raw;
7
+ try {
8
+ raw = readFileSync(dotenvPath, "utf8");
9
+ } catch {
10
+ return;
11
+ }
12
+ const vars = /* @__PURE__ */ new Map();
13
+ for (const line of raw.split(/\r?\n/)) {
14
+ const trimmed = line.trim();
15
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
16
+ const eq = trimmed.indexOf("=");
17
+ if (eq <= 0) continue;
18
+ const key = trimmed.slice(0, eq).trim();
19
+ let value = trimmed.slice(eq + 1).trim();
20
+ if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
21
+ vars.set(key, value);
22
+ }
23
+ return vars;
24
+ }
25
+ /**
26
+ * 读取 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL(优先),
27
+ * 向后兼容 VITE_BASE_URL / VITE_API_KEY / VITE_MODEL_NAME;缺项返回 undefined 供测试 skip 判断。
28
+ */
29
+ function loadLlmConfigFromEnv(dotenvPath = ".env") {
30
+ const vars = readEnvVars(dotenvPath);
31
+ if (!vars) return void 0;
32
+ const baseUrl = vars.get("LLM_BASE_URL") ?? vars.get("VITE_BASE_URL");
33
+ const apiKey = vars.get("LLM_API_KEY") ?? vars.get("VITE_API_KEY");
34
+ const model = vars.get("LLM_MODEL") ?? vars.get("VITE_MODEL_NAME");
35
+ if (!baseUrl || !apiKey || !model) return void 0;
36
+ return {
37
+ baseUrl,
38
+ apiKey,
39
+ model
40
+ };
41
+ }
42
+ /** ANTHROPIC_API_KEY / ANTHROPIC_MODEL(可选 ANTHROPIC_BASE_URL);缺项返回 undefined */
43
+ function loadAnthropicConfigFromEnv(dotenvPath = ".env") {
44
+ const vars = readEnvVars(dotenvPath);
45
+ if (!vars) return void 0;
46
+ const apiKey = vars.get("ANTHROPIC_API_KEY");
47
+ const model = vars.get("ANTHROPIC_MODEL");
48
+ if (!apiKey || !model) return void 0;
49
+ const baseUrl = vars.get("ANTHROPIC_BASE_URL");
50
+ return {
51
+ apiKey,
52
+ model,
53
+ ...baseUrl ? { baseUrl } : {}
54
+ };
55
+ }
56
+ /** GOOGLE_API_KEY / GOOGLE_MODEL(可选 GOOGLE_BASE_URL);缺项返回 undefined */
57
+ function loadGoogleConfigFromEnv(dotenvPath = ".env") {
58
+ const vars = readEnvVars(dotenvPath);
59
+ if (!vars) return void 0;
60
+ const apiKey = vars.get("GOOGLE_API_KEY");
61
+ const model = vars.get("GOOGLE_MODEL");
62
+ if (!apiKey || !model) return void 0;
63
+ const baseUrl = vars.get("GOOGLE_BASE_URL");
64
+ return {
65
+ apiKey,
66
+ model,
67
+ ...baseUrl ? { baseUrl } : {}
68
+ };
69
+ }
70
+ let capabilitiesCache;
71
+ /**
72
+ * 探测 LLM 能力(结果模块级缓存):
73
+ * - available:GET /models 可达(与 checkAvailability 同语义)
74
+ * - nonStreaming:最小非流式 chat completion(max_tokens=1,短超时);
75
+ * HTTP 400/404 或明确不支持非流式的错误 → false
76
+ * - streaming 能力由 available 推断(SSE 解析在 SDK 侧实现)
77
+ * 环境变量 VITE_LLM_NON_STREAMING=0 可显式强制 nonStreaming=false(模拟流式-only 回归)。
78
+ */
79
+ function probeLlmCapabilities(config) {
80
+ capabilitiesCache ??= doProbeLlmCapabilities(config);
81
+ return capabilitiesCache;
82
+ }
83
+ async function doProbeLlmCapabilities(config) {
84
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
85
+ const headers = { authorization: `Bearer ${config.apiKey}` };
86
+ let available;
87
+ try {
88
+ available = (await fetch(`${baseUrl}/models`, {
89
+ headers,
90
+ signal: AbortSignal.timeout(1e4)
91
+ })).ok;
92
+ } catch {
93
+ available = false;
94
+ }
95
+ if (!available) return {
96
+ available: false,
97
+ nonStreaming: false,
98
+ reason: "endpoint unreachable or unauthorized"
99
+ };
100
+ if (process.env["VITE_LLM_NON_STREAMING"] === "0") return {
101
+ available: true,
102
+ nonStreaming: false,
103
+ reason: "non-streaming disabled via VITE_LLM_NON_STREAMING=0 (simulated)"
104
+ };
105
+ try {
106
+ const res = await fetch(`${baseUrl}/chat/completions`, {
107
+ method: "POST",
108
+ headers: {
109
+ ...headers,
110
+ "content-type": "application/json"
111
+ },
112
+ body: JSON.stringify({
113
+ model: config.model,
114
+ messages: [{
115
+ role: "user",
116
+ content: "OK"
117
+ }],
118
+ max_tokens: 1
119
+ }),
120
+ signal: AbortSignal.timeout(15e3)
121
+ });
122
+ const contentType = res.headers.get("content-type") ?? "";
123
+ if (res.ok && !contentType.includes("text/event-stream")) return {
124
+ available: true,
125
+ nonStreaming: true
126
+ };
127
+ const detail = (await res.text().catch(() => "")).slice(0, 200);
128
+ return {
129
+ available: true,
130
+ nonStreaming: false,
131
+ reason: `endpoint rejects non-streaming chat completions (HTTP ${res.status})${detail ? `: ${detail}` : ""}`
132
+ };
133
+ } catch (e) {
134
+ return {
135
+ available: true,
136
+ nonStreaming: false,
137
+ reason: `non-streaming probe failed: ${e instanceof Error ? e.message : String(e)}`
138
+ };
139
+ }
140
+ }
141
+
142
+ //#endregion
143
+ export { probeLlmCapabilities as i, loadGoogleConfigFromEnv as n, loadLlmConfigFromEnv as r, loadAnthropicConfigFromEnv as t };
@@ -0,0 +1,38 @@
1
+ //#region ../node/dist/env-BPUBZCwJ.d.ts
2
+ //#region src/env.d.ts
3
+ interface LlmEnvConfig {
4
+ baseUrl: string;
5
+ apiKey: string;
6
+ model: string;
7
+ }
8
+ /** per-provider env 配置(baseUrl 可选,客户端自带默认) */
9
+ interface ProviderEnvConfig {
10
+ apiKey: string;
11
+ model: string;
12
+ baseUrl?: string;
13
+ }
14
+ /**
15
+ * 读取 LLM_BASE_URL / LLM_API_KEY / LLM_MODEL(优先),
16
+ * 向后兼容 VITE_BASE_URL / VITE_API_KEY / VITE_MODEL_NAME;缺项返回 undefined 供测试 skip 判断。
17
+ */
18
+ declare function loadLlmConfigFromEnv(dotenvPath?: string): LlmEnvConfig | undefined;
19
+ /** ANTHROPIC_API_KEY / ANTHROPIC_MODEL(可选 ANTHROPIC_BASE_URL);缺项返回 undefined */
20
+ declare function loadAnthropicConfigFromEnv(dotenvPath?: string): ProviderEnvConfig | undefined;
21
+ /** GOOGLE_API_KEY / GOOGLE_MODEL(可选 GOOGLE_BASE_URL);缺项返回 undefined */
22
+ declare function loadGoogleConfigFromEnv(dotenvPath?: string): ProviderEnvConfig | undefined;
23
+ interface LlmCapabilities {
24
+ available: boolean;
25
+ nonStreaming: boolean;
26
+ reason?: string;
27
+ }
28
+ /**
29
+ * 探测 LLM 能力(结果模块级缓存):
30
+ * - available:GET /models 可达(与 checkAvailability 同语义)
31
+ * - nonStreaming:最小非流式 chat completion(max_tokens=1,短超时);
32
+ * HTTP 400/404 或明确不支持非流式的错误 → false
33
+ * - streaming 能力由 available 推断(SSE 解析在 SDK 侧实现)
34
+ * 环境变量 VITE_LLM_NON_STREAMING=0 可显式强制 nonStreaming=false(模拟流式-only 回归)。
35
+ */
36
+ declare function probeLlmCapabilities(config: LlmEnvConfig): Promise<LlmCapabilities>;
37
+ //#endregion
38
+ export { loadGoogleConfigFromEnv as a, loadAnthropicConfigFromEnv as i, LlmEnvConfig as n, loadLlmConfigFromEnv as o, ProviderEnvConfig as r, probeLlmCapabilities as s, LlmCapabilities as t };
@@ -1,6 +1,6 @@
1
- import { F as SkillDocument, N as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider, z as SkillManifest } from "./types-CgNC-oQu-DEcIBJKG.js";
2
- import { $ as SkillStateGuard, lt as WebSkillRuntime, q as RuntimeRun } from "./index-COuOu6gw.js";
3
- import { f as SkillManager } from "./index-Bun1aEf4.js";
1
+ import { F as SkillDocument, N as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider, z as SkillManifest } from "./types-CKm5G_eQ-8W8FnP4u.js";
2
+ import { dt as WebSkillRuntime, q as RuntimeRun, tt as SkillStateGuard } from "./index-CySxIvRz.js";
3
+ import { l as SkillManager } from "./index-Vn63HJRO.js";
4
4
  //#region ../governance/dist/index.d.ts
5
5
  //#region src/types.d.ts
6
6
  type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
@@ -32,6 +32,10 @@ interface AuditEvent {
32
32
  actor?: string;
33
33
  ts: string;
34
34
  data?: Record<string, unknown>;
35
+ /** hash 链:上一条事件的 hash(首条为 GENESIS) */
36
+ prevHash?: string;
37
+ /** 本事件规范化载荷 + prevHash 的 sha256 */
38
+ hash?: string;
35
39
  }
36
40
  interface AuditLog {
37
41
  append(event: Omit<AuditEvent, 'id' | 'ts'> & {
@@ -51,6 +55,8 @@ interface SkillVersion {
51
55
  reason: string;
52
56
  manifest: SkillManifest;
53
57
  auditEventId?: string;
58
+ /** 发布时捕获的技能归档(zip 相对路径,applyRollback 恢复用;0.2.0 起记录) */
59
+ archivePath?: string;
54
60
  }
55
61
  type SkillState = 'active' | 'quarantined' | 'deprecated' | 'disabled';
56
62
  //#endregion
@@ -151,7 +157,10 @@ declare class SkillVersionStore {
151
157
  reason: string;
152
158
  manifest: SkillManifest;
153
159
  parentVersionId?: string;
160
+ archive?: Uint8Array;
154
161
  }): Promise<SkillVersion>;
162
+ /** 读取版本归档字节(applyRollback 用;未捕获归档的旧版本 → GOVERNANCE_FAILED) */
163
+ readArchive(skillName: string, versionId: string): Promise<Uint8Array>;
155
164
  list(skillName: string): Promise<SkillVersion[]>;
156
165
  get(skillName: string, versionId: string): Promise<SkillVersion>;
157
166
  /** 回滚:基于旧 manifest 追加新版本 + skill.rolled_back 审计 */
@@ -182,10 +191,25 @@ declare class ApprovalWorkflow {
182
191
  publish(candidateId: string, input: {
183
192
  actor: string;
184
193
  }): Promise<SkillManifest>;
194
+ /**
195
+ * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
196
+ * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
197
+ * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
198
+ */
199
+ applyRollback(skillName: string, versionId: string, input: {
200
+ actor: string;
201
+ reason?: string;
202
+ }): Promise<SkillManifest>;
185
203
  }
186
204
  //#endregion
187
205
  //#region src/audit/fsAuditLog.d.ts
188
- /** JSONL 追加的审计日志:<managedRoot>/.webskill/audit.jsonl;跨实例可恢复查询 */
206
+ interface AuditChainVerification {
207
+ ok: boolean;
208
+ /** 首个断链位置(行号,0 起;ok 时 undefined) */
209
+ brokenAt?: number;
210
+ reason?: string;
211
+ }
212
+ /** JSONL 追加的审计日志:<managedRoot>/.webskill/audit.jsonl;跨实例可恢复查询;prevHash 链可校验完整性 */
189
213
  declare class FsAuditLog implements AuditLog {
190
214
  #private;
191
215
  constructor(deps: {
@@ -194,7 +218,7 @@ declare class FsAuditLog implements AuditLog {
194
218
  now?: () => string;
195
219
  createId?: () => string;
196
220
  });
197
- append(event: Omit<AuditEvent, 'id' | 'ts'> & {
221
+ append(event: Omit<AuditEvent, 'id' | 'ts' | 'prevHash' | 'hash'> & {
198
222
  id?: string;
199
223
  ts?: string;
200
224
  }): Promise<AuditEvent>;
@@ -203,6 +227,8 @@ declare class FsAuditLog implements AuditLog {
203
227
  type?: string;
204
228
  since?: string;
205
229
  }): Promise<AuditEvent[]>;
230
+ /** hash 链完整性校验:逐行重算 hash 并核对 prevHash 链接 */
231
+ verifyChain(): Promise<AuditChainVerification>;
206
232
  }
207
233
  //#endregion
208
234
  //#region src/repair/failureAnalyzer.d.ts
@@ -234,7 +260,7 @@ interface RepairOption {
234
260
  }
235
261
  /**
236
262
  * 修订选项规划:patch(按诊断给具体文件变更)/ rollback(有旧版本时)/ quarantine。
237
- * 注意:任何 option 的 apply 都必须经 ApprovalWorkflow 审批后执行。
263
+ * 注意:任何 option 的 apply 都必须经 ApprovalWorkflow 审批后执行;rollback 选项(targetVersionId)经 ApprovalWorkflow.applyRollback 真实恢复文件。
238
264
  */
239
265
  declare class RepairPlanner {
240
266
  plan(input: {