@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.
- package/dist/browser.d.ts +3 -2
- package/dist/browser.js +10 -5
- package/dist/{dist-CfBOjJ4p.js → dist-BJobG0i-.js} +30 -7
- package/dist/{dist-DuGN5x0v.js → dist-BS5OpedX.js} +181 -50
- package/dist/{dist-fZFZaf43.js → dist-D0saNPi_.js} +22 -4
- package/dist/{dist-LIFS3ZjI.js → dist-Dj6QjHBn.js} +55 -151
- package/dist/env--jJB-TSX-04klhTYi.js +143 -0
- package/dist/env-BPUBZCwJ-4jat_SVG.d.ts +38 -0
- package/dist/governance.d.ts +32 -6
- package/dist/governance.js +155 -10
- package/dist/{index-COuOu6gw.d.ts → index-CySxIvRz.d.ts} +51 -3
- package/dist/{index-Bun1aEf4.d.ts → index-Vn63HJRO.d.ts} +11 -38
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +3 -3
- package/dist/mcp.js +28 -8
- package/dist/node.d.ts +5 -4
- package/dist/node.js +5 -4
- package/dist/{testing-BmR48Pn1.js → testing-B4pq6JYa.js} +1 -1
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +3 -2
- package/dist/{types-CgNC-oQu-DEcIBJKG.d.ts → types-CKm5G_eQ-8W8FnP4u.d.ts} +13 -2
- package/dist/ui-react.d.ts +4 -2
- package/dist/ui-react.js +25 -4
- package/dist/ui-vue.d.ts +3 -2
- package/dist/ui-vue.js +44 -15
- package/dist/ui.d.ts +5 -3
- package/dist/ui.js +2 -2
- package/package.json +21 -9
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
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
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 };
|
package/dist/governance.d.ts
CHANGED
|
@@ -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-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
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: {
|