@webskill/sdk 0.2.6 → 0.2.8

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,85 +1,37 @@
1
- import { B as SkillManifest, I as SkillDocument, P as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge, w as FileSystemProvider } from "./types-CKm5G_eQ-krKWW8WV.js";
2
- import { Q as ScriptExecutor, dt as WebSkillRuntime, ft as WebSkillRuntimeDeps, q as RuntimeRun, tt as SkillStateGuard } from "./index-DJOha4b6.js";
3
- import { d as SkillManager } from "./index-DRlYzdr2.js";
4
- //#region ../governance/dist/documentSource-9dhqKIVQ.d.ts
5
- //#region src/types.d.ts
6
- type CandidateStatus = 'draft' | 'pending-review' | 'approved' | 'published' | 'rejected';
7
- type CandidateSource = 'runtime-miss' | 'document' | 'manual';
8
- type CandidateRisk = 'low' | 'medium' | 'high';
9
- interface CandidateFile {
1
+ import { G as SkillDocument, N as FileSystemProvider, U as SkillCatalogEntry, c as LlmClient, u as LlmMessage, v as UiBridge } from "./types-AmKCKJn_-BogJPQHU.js";
2
+ import { dt as WebSkillRuntime, q as RuntimeRun, tt as SkillStateGuard } from "./index-QrHtAudz.js";
3
+ import { D as SkillManager, a as AuditLog, c as CandidateSkill, d as CandidateStore, f as CompositeApprovalPolicy, g as candidateToCatalogEntry, h as SkillVersionStore, i as AuditEvent, l as CandidateSource, m as SkillVersion, n as ApprovalDecision, o as CandidateFile, p as SkillState, r as ApprovalPolicy, s as CandidateRisk, t as AlwaysHumanApprovalPolicy, u as CandidateStatus } from "./skillVersionStore-B7rGjtMi-BgnQho9v.js";
4
+ //#region ../governance/dist/index.d.ts
5
+ //#region src/candidate/candidateNormalizer.d.ts
6
+ /** markdown fence <think> 块、截取首尾 {};非对象 CANDIDATE_INVALID */
7
+ declare function parseJsonObject(raw: string): Record<string, unknown>;
8
+ /** 小写连字符化、≤64、过 isValidSkillName */
9
+ declare function sanitizeCandidateName(raw: string): string;
10
+ /**
11
+ * 文件归一:补 SKILL.md;路径含 `..` / 以 `/` 开头 / 含 `\` → CANDIDATE_INVALID;
12
+ * scripts/ 下必须是 .ts/.js。
13
+ */
14
+ declare function normalizeCandidateFiles(files: Array<{
10
15
  path: string;
11
- kind: 'skill-md' | 'script' | 'reference' | 'asset';
12
16
  content: string;
13
- }
14
- interface CandidateSkill {
15
- id: string;
16
- name: string;
17
- description: string;
18
- status: CandidateStatus;
19
- source: CandidateSource;
17
+ }>, name: string, description: string): CandidateFile[];
18
+ /** 含脚本且 LLM 自报 low → 强制 medium 并追加"必须人工审批"原因 */
19
+ declare function normalizeRisk(declared: unknown, files: CandidateFile[], reasons: string[]): {
20
20
  risk: CandidateRisk;
21
- riskReasons: string[];
22
- files: CandidateFile[];
23
- suggestedTests?: string[];
24
- createdAt: string;
25
- updatedAt: string;
26
- metadata?: Record<string, unknown>;
27
- }
28
- interface AuditEvent {
29
- id: string;
30
- type: string;
31
- target: string;
32
- actor?: string;
33
- ts: string;
34
- data?: Record<string, unknown>;
35
- /** hash 链:上一条事件的 hash(首条为 GENESIS) */
36
- prevHash?: string;
37
- /** 本事件规范化载荷 + prevHash 的 sha256 */
38
- hash?: string;
39
- }
40
- interface AuditLog {
41
- append(event: Omit<AuditEvent, 'id' | 'ts'> & {
42
- id?: string;
43
- ts?: string;
44
- }): Promise<AuditEvent>;
45
- query(filter: {
46
- target?: string;
47
- type?: string;
48
- since?: string;
49
- }): Promise<AuditEvent[]>;
50
- }
51
- interface SkillVersion {
52
- versionId: string;
53
- parentVersionId?: string;
54
- createdAt: string;
55
- reason: string;
56
- manifest: SkillManifest;
57
- auditEventId?: string;
58
- /** 发布时捕获的技能归档(zip 相对路径,applyRollback 恢复用;0.2.0 起记录) */
59
- archivePath?: string;
60
- }
61
- type SkillState = 'active' | 'quarantined' | 'deprecated' | 'disabled';
21
+ reasons: string[];
22
+ };
23
+ /** 不信任归一化管线全链路;状态恒为 draft */
24
+ declare function normalizeCandidate(input: {
25
+ raw: Record<string, unknown>;
26
+ source: CandidateSource;
27
+ now?: () => string;
28
+ createId?: () => string;
29
+ }): CandidateSkill;
62
30
  //#endregion
63
31
  //#region src/candidate/candidateValidator.d.ts
64
32
  /** 候选校验:缺 SKILL.md / 非法扩展名 → CANDIDATE_INVALID */
65
33
  declare function validateCandidate(candidate: CandidateSkill): void;
66
34
  //#endregion
67
- //#region src/candidate/candidateStore.d.ts
68
- /** 逐文件持久化的候选存储:<managedRoot>/.webskill/candidates/<id>.json */
69
- declare class CandidateStore {
70
- #private;
71
- constructor(deps: {
72
- root: string;
73
- fs: FileSystemProvider;
74
- });
75
- save(candidate: CandidateSkill): Promise<void>;
76
- get(id: string): Promise<CandidateSkill>;
77
- list(): Promise<CandidateSkill[]>;
78
- updateStatus(id: string, status: CandidateStatus, now?: string): Promise<CandidateSkill>;
79
- }
80
- /** 硬门禁:仅 published 可转换为 Catalog 条目,否则 APPROVAL_REQUIRED */
81
- declare function candidateToCatalogEntry(candidate: CandidateSkill): SkillCatalogEntry;
82
- //#endregion
83
35
  //#region src/candidate/llmCandidateGenerator.d.ts
84
36
  /** LLM 候选生成器:prompt 强制约束 + 不信任归一化管线 + candidate.created 审计 */
85
37
  declare class LlmCandidateGenerator {
@@ -97,37 +49,6 @@ declare class LlmCandidateGenerator {
97
49
  }): Promise<CandidateSkill>;
98
50
  }
99
51
  //#endregion
100
- //#region src/versioning/skillVersionStore.d.ts
101
- /** 版本存储:manifest 快照 + parentVersionId 链;回滚 = 追加新版本(谱系不断)。
102
- * 保留策略:maxArchivesPerSkill(默认 5)超出时清理最旧版本(json + zip 归档一并删除)。 */
103
- declare class SkillVersionStore {
104
- #private;
105
- constructor(deps: {
106
- root: string;
107
- fs: FileSystemProvider;
108
- now?: () => string;
109
- createId?: () => string;
110
- audit?: AuditLog;
111
- /** 每技能保留的版本/归档上限(默认 5,超出清理最旧) */
112
- maxArchivesPerSkill?: number;
113
- });
114
- add(skillName: string, input: {
115
- reason: string;
116
- manifest: SkillManifest;
117
- parentVersionId?: string;
118
- archive?: Uint8Array;
119
- }): Promise<SkillVersion>;
120
- /** 读取版本归档字节(applyRollback 用;未捕获归档的旧版本 → GOVERNANCE_FAILED) */
121
- readArchive(skillName: string, versionId: string): Promise<Uint8Array>;
122
- list(skillName: string): Promise<SkillVersion[]>;
123
- get(skillName: string, versionId: string): Promise<SkillVersion>;
124
- /** 回滚:基于旧 manifest 追加新版本 + skill.rolled_back 审计 */
125
- rollback(skillName: string, targetVersionId: string, input: {
126
- actor?: string;
127
- reason?: string;
128
- }): Promise<SkillVersion>;
129
- }
130
- //#endregion
131
52
  //#region src/audit/fsAuditLog.d.ts
132
53
  interface AuditChainVerification {
133
54
  ok: boolean;
@@ -167,6 +88,8 @@ interface FailureDiagnosis {
167
88
  cause: string;
168
89
  suggestedFix: string;
169
90
  errorCode?: string;
91
+ /** 诊断来源:'llm' 为模型输出,'rules' 为错误码归类兑底 */
92
+ source: 'llm' | 'rules';
170
93
  }
171
94
  /** 失败诊断:trace 摘要 → LLM 诊断(JSON);无 LLM 时规则版(错误码归类) */
172
95
  declare class FailureAnalyzer {
@@ -359,96 +282,6 @@ interface SourceDocument {
359
282
  /** 读文档 + sha256 hash(变更检测用) */
360
283
  declare function readDocument(fs: FileSystemProvider, path: string): Promise<SourceDocument>;
361
284
  //#endregion
362
- //#region ../governance/dist/index.d.ts
363
- //#region src/candidate/candidateNormalizer.d.ts
364
- /** 剥 markdown fence 与 <think> 块、截取首尾 {};非对象 → CANDIDATE_INVALID */
365
- declare function parseJsonObject(raw: string): Record<string, unknown>;
366
- /** 小写连字符化、≤64、过 isValidSkillName */
367
- declare function sanitizeCandidateName(raw: string): string;
368
- /**
369
- * 文件归一:补 SKILL.md;路径含 `..` / 以 `/` 开头 / 含 `\` → CANDIDATE_INVALID;
370
- * scripts/ 下必须是 .ts/.js。
371
- */
372
- declare function normalizeCandidateFiles(files: Array<{
373
- path: string;
374
- content: string;
375
- }>, name: string, description: string): CandidateFile[];
376
- /** 含脚本且 LLM 自报 low → 强制 medium 并追加"必须人工审批"原因 */
377
- declare function normalizeRisk(declared: unknown, files: CandidateFile[], reasons: string[]): {
378
- risk: CandidateRisk;
379
- reasons: string[];
380
- };
381
- /** 不信任归一化管线全链路;状态恒为 draft */
382
- declare function normalizeCandidate(input: {
383
- raw: Record<string, unknown>;
384
- source: CandidateSource;
385
- now?: () => string;
386
- createId?: () => string;
387
- }): CandidateSkill;
388
- //#endregion
389
- //#region src/approval/policies.d.ts
390
- interface ApprovalDecision {
391
- needsHuman: boolean;
392
- reason: string;
393
- }
394
- interface ApprovalPolicy {
395
- evaluate(candidate: CandidateSkill): ApprovalDecision;
396
- }
397
- /** 默认策略:任何候选都必须人工审批 */
398
- declare class AlwaysHumanApprovalPolicy implements ApprovalPolicy {
399
- evaluate(candidate: CandidateSkill): ApprovalDecision;
400
- }
401
- /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
402
- declare class CompositeApprovalPolicy implements ApprovalPolicy {
403
- #private;
404
- constructor(rules: Array<(candidate: CandidateSkill) => ApprovalDecision | undefined>, fallback?: ApprovalPolicy);
405
- evaluate(candidate: CandidateSkill): ApprovalDecision;
406
- }
407
- //#endregion
408
- //#region src/approval/approvalWorkflow.d.ts
409
- /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
410
- declare class ApprovalWorkflow {
411
- #private;
412
- constructor(deps: {
413
- policy: ApprovalPolicy;
414
- audit: AuditLog;
415
- store: CandidateStore;
416
- skillManager: SkillManager;
417
- versions: SkillVersionStore;
418
- fs?: FileSystemProvider;
419
- });
420
- /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
421
- review(candidateId: string, input: {
422
- actor: string;
423
- uiBridge?: UiBridge;
424
- }): Promise<CandidateSkill>;
425
- /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
426
- publish(candidateId: string, input: {
427
- actor: string;
428
- }): Promise<SkillManifest>;
429
- /**
430
- * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
431
- * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
432
- * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
433
- */
434
- applyRollback(skillName: string, versionId: string, input: {
435
- actor: string;
436
- reason?: string;
437
- }): Promise<SkillManifest>;
438
- }
439
- //#endregion
440
- //#region src/evaluation/evaluationRuntime.d.ts
441
- /**
442
- * 治理评估专用 runtime 装配(不可信技能试用路径):
443
- * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
444
- * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
445
- * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
446
- * 非安全边界;envWhitelist 同样适用于该执行器)。
447
- */
448
- declare function createEvaluationRuntime(deps: WebSkillRuntimeDeps & {
449
- executor?: ScriptExecutor;
450
- }): WebSkillRuntime;
451
- //#endregion
452
285
  //#region src/documents/documentSkillExtractor.d.ts
453
286
  /** 文档 → LLM 抽取 → 完整防御管线 → draft 候选(source: document) */
454
287
  declare class DocumentSkillExtractor {
@@ -495,4 +328,4 @@ declare function createMissHook(deps: {
495
328
  */
496
329
  declare function completeText(llm: LlmClient, messages: LlmMessage[]): Promise<string>;
497
330
  //#endregion
498
- export { AlwaysHumanApprovalPolicy, type ApprovalDecision, type ApprovalPolicy, ApprovalWorkflow, type AuditEvent, type AuditLog, type CandidateFile, type CandidateRisk, type CandidateSkill, type CandidateSource, type CandidateStatus, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, type EvaluationReport, type EvaluationResult, EvaluationRunner, type EvaluationTask, FailureAnalyzer, type FailureDiagnosis, FsAuditLog, LlmCandidateGenerator, type RepairOption, RepairPlanner, SCORING_WEIGHTS, type ScoreInput, SimilarityDetector, SkillScorer, type SkillState, SkillStatePolicy, type SkillVersion, SkillVersionStore, type SourceDocument, candidateToCatalogEntry, completeText, createEvaluationRuntime, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
331
+ export { AlwaysHumanApprovalPolicy, type ApprovalDecision, type ApprovalPolicy, type AuditEvent, type AuditLog, type CandidateFile, type CandidateRisk, type CandidateSkill, type CandidateSource, type CandidateStatus, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, type EvaluationReport, type EvaluationResult, EvaluationRunner, type EvaluationTask, FailureAnalyzer, type FailureDiagnosis, FsAuditLog, LlmCandidateGenerator, type RepairOption, RepairPlanner, SCORING_WEIGHTS, type ScoreInput, SimilarityDetector, SkillScorer, type SkillState, SkillStatePolicy, type SkillVersion, SkillVersionStore, type SourceDocument, candidateToCatalogEntry, completeText, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
@@ -1,11 +1,6 @@
1
- import { C as messageOf, M as unzipWithLimits, N as validateSkills, f as assertSafePathSegment, j as resolveInsideRoot, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
2
- import { S as WebSkillRuntime } from "./dist-BXpDDZpR.js";
3
- import { i as NodeFS, s as ProcessSandboxExecutor, u as exportArchive } from "./dist-DWFDb1Ww.js";
4
- import path from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { mkdtemp } from "node:fs/promises";
1
+ import { C as messageOf, f as assertSafePathSegment, u as WebSkillError, x as isValidSkillName } from "./dist-BQzncxXg.js";
7
2
 
8
- //#region ../governance/dist/documentSource-C6gq6pbk.js
3
+ //#region ../governance/dist/index.js
9
4
  const invalid = (message, details) => {
10
5
  throw new WebSkillError("CANDIDATE_INVALID", message, details);
11
6
  };
@@ -216,6 +211,31 @@ var LlmCandidateGenerator = class {
216
211
  return candidate;
217
212
  }
218
213
  };
214
+ /** 默认策略:任何候选都必须人工审批 */
215
+ var AlwaysHumanApprovalPolicy = class {
216
+ evaluate(candidate) {
217
+ return {
218
+ needsHuman: true,
219
+ reason: `Candidate "${candidate.name}" requires human approval (risk: ${candidate.risk})`
220
+ };
221
+ }
222
+ };
223
+ /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
224
+ var CompositeApprovalPolicy = class {
225
+ #rules;
226
+ #fallback;
227
+ constructor(rules, fallback) {
228
+ this.#rules = rules;
229
+ this.#fallback = fallback ?? new AlwaysHumanApprovalPolicy();
230
+ }
231
+ evaluate(candidate) {
232
+ for (const rule of this.#rules) {
233
+ const decision = rule(candidate);
234
+ if (decision) return decision;
235
+ }
236
+ return this.#fallback.evaluate(candidate);
237
+ }
238
+ };
219
239
  const fileOf$1 = (root) => `${root}/.webskill/audit.jsonl`;
220
240
  /** 环境无关 sha256(WebCrypto;Node ≥17 与浏览器均有 globalThis.crypto.subtle) */
221
241
  const sha256Hex$1 = async (text) => {
@@ -289,12 +309,14 @@ var FsAuditLog = class {
289
309
  if (!await this.#fs.exists(path)) return [];
290
310
  const raw = await this.#fs.readText(path);
291
311
  const events = [];
312
+ let skippedLines = 0;
292
313
  for (const line of raw.split("\n")) {
293
314
  if (line.trim() === "") continue;
294
315
  let event;
295
316
  try {
296
317
  event = JSON.parse(line);
297
318
  } catch {
319
+ skippedLines += 1;
298
320
  continue;
299
321
  }
300
322
  if (filter.target !== void 0 && event.target !== filter.target) continue;
@@ -302,6 +324,7 @@ var FsAuditLog = class {
302
324
  if (filter.since !== void 0 && event.ts < filter.since) continue;
303
325
  events.push(event);
304
326
  }
327
+ if (skippedLines > 0) console.warn(`[webskill] Audit log at ${path} contains ${skippedLines} unparsable line(s) skipped by query; run verifyChain() to check integrity`);
305
328
  return events;
306
329
  }
307
330
  /** hash 链完整性校验:逐行重算 hash 并核对 prevHash 链接 */
@@ -461,14 +484,18 @@ var FailureAnalyzer = class {
461
484
  return {
462
485
  cause: String(parsed["cause"] ?? "Unknown cause"),
463
486
  suggestedFix: String(parsed["suggestedFix"] ?? "Manual inspection required"),
464
- ...typeof parsed["errorCode"] === "string" ? { errorCode: parsed["errorCode"] } : firstCode ? { errorCode: firstCode } : {}
487
+ ...typeof parsed["errorCode"] === "string" ? { errorCode: parsed["errorCode"] } : firstCode ? { errorCode: firstCode } : {},
488
+ source: "llm"
465
489
  };
466
- } catch {}
490
+ } catch (e) {
491
+ console.warn(`[webskill] LLM diagnosis output was not valid JSON, falling back to rule-based diagnosis: ${e instanceof Error ? e.message : String(e)}`);
492
+ }
467
493
  }
468
494
  return {
469
495
  cause: firstCode ? ERROR_CAUSES[firstCode] ?? `Failure with code ${firstCode}` : "Unknown failure (no error code in trace)",
470
496
  suggestedFix: firstCode ? `Inspect the component responsible for ${firstCode}` : "Inspect the run trace manually",
471
- ...firstCode ? { errorCode: firstCode } : {}
497
+ ...firstCode ? { errorCode: firstCode } : {},
498
+ source: "rules"
472
499
  };
473
500
  }
474
501
  };
@@ -809,194 +836,6 @@ async function readDocument(fs, path) {
809
836
  hash: await sha256Hex(content)
810
837
  };
811
838
  }
812
-
813
- //#endregion
814
- //#region ../governance/dist/index.js
815
- /** 默认策略:任何候选都必须人工审批 */
816
- var AlwaysHumanApprovalPolicy = class {
817
- evaluate(candidate) {
818
- return {
819
- needsHuman: true,
820
- reason: `Candidate "${candidate.name}" requires human approval (risk: ${candidate.risk})`
821
- };
822
- }
823
- };
824
- /** 规则组合策略:首个命中的规则胜出,全部未命中走 fallback(默认 AlwaysHuman) */
825
- var CompositeApprovalPolicy = class {
826
- #rules;
827
- #fallback;
828
- constructor(rules, fallback) {
829
- this.#rules = rules;
830
- this.#fallback = fallback ?? new AlwaysHumanApprovalPolicy();
831
- }
832
- evaluate(candidate) {
833
- for (const rule of this.#rules) {
834
- const decision = rule(candidate);
835
- if (decision) return decision;
836
- }
837
- return this.#fallback.evaluate(candidate);
838
- }
839
- };
840
- /** 审批工作流:review(UiBridge confirm 真实接线)/ publish(校验→安装→版本→审计) */
841
- var ApprovalWorkflow = class {
842
- #policy;
843
- #audit;
844
- #store;
845
- #skillManager;
846
- #versions;
847
- #fs;
848
- constructor(deps) {
849
- this.#policy = deps.policy;
850
- this.#audit = deps.audit;
851
- this.#store = deps.store;
852
- this.#skillManager = deps.skillManager;
853
- this.#versions = deps.versions;
854
- this.#fs = deps.fs ?? new NodeFS();
855
- }
856
- /** 策略评估;needs-human 时经 UiBridge confirm 真实询问,按应答迁移状态 */
857
- async review(candidateId, input) {
858
- const candidate = await this.#store.get(candidateId);
859
- if (candidate.status !== "draft" && candidate.status !== "pending-review") throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidateId}" cannot be reviewed from status "${candidate.status}"`);
860
- const decision = this.#policy.evaluate(candidate);
861
- let approved;
862
- if (decision.needsHuman) {
863
- if (!input.uiBridge) {
864
- await this.#store.updateStatus(candidateId, "pending-review");
865
- throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" requires human approval: ${decision.reason}`);
866
- }
867
- await this.#store.updateStatus(candidateId, "pending-review");
868
- const response = await input.uiBridge.request({
869
- type: "confirm",
870
- id: `approval-${candidateId}`,
871
- message: `Approve candidate "${candidate.name}" (risk: ${candidate.risk})? ${decision.reason}`,
872
- defaultValue: false
873
- });
874
- approved = response.cancelled !== true && response.value === true;
875
- } else approved = true;
876
- const updated = await this.#store.updateStatus(candidateId, approved ? "approved" : "rejected");
877
- await this.#audit.append({
878
- type: "candidate.reviewed",
879
- target: candidateId,
880
- actor: input.actor,
881
- data: {
882
- approved,
883
- reason: decision.reason
884
- }
885
- });
886
- return updated;
887
- }
888
- /** publish 全链路:approved 前置 → 写出 staging → validateSkills → install → 版本 → 审计 */
889
- async publish(candidateId, input) {
890
- const candidate = await this.#store.get(candidateId);
891
- if (candidate.status !== "approved") throw new WebSkillError("APPROVAL_REQUIRED", `Candidate "${candidate.name}" must be approved before publishing (status: ${candidate.status})`);
892
- const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-candidate-"))).split(path.sep).join("/");
893
- try {
894
- const skillDir = `${stagingRoot}/${candidate.name}`;
895
- for (const file of candidate.files) await this.#fs.writeText(resolveInsideRoot(skillDir, file.path), file.content);
896
- const report = await validateSkills(this.#fs, [stagingRoot]);
897
- if (!report.ok) {
898
- const errors = report.issues.filter((i) => i.severity === "error");
899
- throw new WebSkillError("GOVERNANCE_FAILED", `Candidate "${candidate.name}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
900
- }
901
- const manifest = await this.#skillManager.install({
902
- type: "local",
903
- path: skillDir
904
- });
905
- const archiveOut = `${stagingRoot}/version-archive.zip`;
906
- await exportArchive(this.#fs, `${this.#skillManager.managedRoot}/${candidate.name}`, {
907
- format: "zip",
908
- outPath: archiveOut
909
- });
910
- await this.#versions.add(candidate.name, {
911
- reason: `Publish candidate ${candidateId}`,
912
- manifest,
913
- archive: await this.#fs.readBinary(archiveOut)
914
- });
915
- await this.#store.updateStatus(candidateId, "published");
916
- await this.#audit.append({
917
- type: "skill.published",
918
- target: candidate.name,
919
- actor: input.actor,
920
- data: {
921
- candidateId,
922
- digest: manifest.integrity.digest
923
- }
924
- });
925
- return manifest;
926
- } catch (e) {
927
- if (e instanceof WebSkillError) throw e;
928
- throw new WebSkillError("GOVERNANCE_FAILED", `Failed to publish candidate "${candidateId}": ${messageOf(e)}`, e);
929
- } finally {
930
- try {
931
- await this.#fs.remove(stagingRoot, { recursive: true });
932
- } catch {}
933
- }
934
- }
935
- /**
936
- * 真实回滚(受审批保护:仅经显式 actor 调用并全程审计):
937
- * 版本归档解包 → staging 校验 → 原子安装(复用安装管线 swap)→ 追加新版本 + 审计。
938
- * RepairPlanner 的 rollback 选项(targetVersionId)经本方法执行。
939
- */
940
- async applyRollback(skillName, versionId, input) {
941
- assertSafePathSegment(skillName, "skill name");
942
- assertSafePathSegment(versionId, "version id");
943
- const version = await this.#versions.get(skillName, versionId);
944
- const archive = await this.#versions.readArchive(skillName, versionId);
945
- const stagingRoot = (await mkdtemp(path.join(tmpdir(), "webskill-rollback-"))).split(path.sep).join("/");
946
- try {
947
- const skillDir = `${stagingRoot}/${skillName}`;
948
- for (const [rel, content] of await unzipWithLimits(archive)) {
949
- if (rel.endsWith("/")) continue;
950
- await this.#fs.writeBinary(resolveInsideRoot(skillDir, rel), content);
951
- }
952
- const report = await validateSkills(this.#fs, [stagingRoot]);
953
- if (!report.ok) {
954
- const errors = report.issues.filter((i) => i.severity === "error");
955
- throw new WebSkillError("GOVERNANCE_FAILED", `Rollback archive of "${skillName}" failed validation: ${errors.map((i) => i.message).join("; ")}`, errors);
956
- }
957
- const manifest = await this.#skillManager.install({
958
- type: "local",
959
- path: skillDir
960
- });
961
- if (manifest.integrity.digest !== version.manifest.integrity.digest) throw new WebSkillError("GOVERNANCE_FAILED", `Rollback of "${skillName}" to version "${versionId}" produced a digest mismatch: expected ${version.manifest.integrity.digest}, got ${manifest.integrity.digest}`);
962
- await this.#versions.add(skillName, {
963
- reason: input.reason ?? `Rollback to version ${versionId}`,
964
- manifest,
965
- archive
966
- });
967
- await this.#audit.append({
968
- type: "skill.rolled_back",
969
- target: skillName,
970
- actor: input.actor,
971
- data: {
972
- targetVersionId: versionId,
973
- reason: input.reason
974
- }
975
- });
976
- return manifest;
977
- } catch (e) {
978
- if (e instanceof WebSkillError) throw e;
979
- throw new WebSkillError("GOVERNANCE_FAILED", `Failed to roll back "${skillName}" to version "${versionId}": ${messageOf(e)}`, e);
980
- } finally {
981
- try {
982
- await this.#fs.remove(stagingRoot, { recursive: true });
983
- } catch {}
984
- }
985
- }
986
- };
987
- /**
988
- * 治理评估专用 runtime 装配(不可信技能试用路径):
989
- * 默认注入 ProcessSandboxExecutor(fork + --permission 真实进程隔离;子进程
990
- * env 默认清空防密钥泄露,需透传时经 ProcessSandboxOptions.envWhitelist 显式放行)。
991
- * 可配置 executor 切回 SandboxedScriptExecutor(worker_threads 能力面收敛形态,
992
- * 非安全边界;envWhitelist 同样适用于该执行器)。
993
- */
994
- function createEvaluationRuntime(deps) {
995
- return new WebSkillRuntime({
996
- ...deps,
997
- executor: deps.executor ?? new ProcessSandboxExecutor(deps.fs)
998
- });
999
- }
1000
839
  const EXTRACT_PROMPT = (doc, nameHint) => [
1001
840
  "Extract an executable skill from the following document as STRICT JSON only.",
1002
841
  "Schema: {\"name\": string, \"description\": string, \"risk\": \"low\"|\"medium\"|\"high\",",
@@ -1080,4 +919,4 @@ function createMissHook(deps) {
1080
919
  }
1081
920
 
1082
921
  //#endregion
1083
- export { AlwaysHumanApprovalPolicy, ApprovalWorkflow, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, RepairPlanner, SCORING_WEIGHTS, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createEvaluationRuntime, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };
922
+ export { AlwaysHumanApprovalPolicy, CandidateStore, CompositeApprovalPolicy, DependencyGraph, DocumentSkillExtractor, EvaluationRunner, FailureAnalyzer, FsAuditLog, LlmCandidateGenerator, RepairPlanner, SCORING_WEIGHTS, SimilarityDetector, SkillScorer, SkillStatePolicy, SkillVersionStore, candidateToCatalogEntry, completeText, createMissHook, jaccardSimilarity, normalizeCandidate, normalizeCandidateFiles, normalizeRisk, parseJsonObject, readDocument, sanitizeCandidateName, suggestFromFailedRun, validateCandidate };