@lokvis/runtime 0.2.2 → 0.4.1

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 (125) hide show
  1. package/dist/asset-store.d.ts +13 -0
  2. package/dist/asset-store.d.ts.map +1 -1
  3. package/dist/asset-store.js +15 -2
  4. package/dist/asset-store.js.map +1 -1
  5. package/dist/batch-processor.d.ts +32 -161
  6. package/dist/batch-processor.d.ts.map +1 -1
  7. package/dist/batch-processor.js +107 -333
  8. package/dist/batch-processor.js.map +1 -1
  9. package/dist/batch-progress.d.ts +94 -0
  10. package/dist/batch-progress.d.ts.map +1 -0
  11. package/dist/batch-progress.js +128 -0
  12. package/dist/batch-progress.js.map +1 -0
  13. package/dist/batch-scheduler.d.ts +70 -0
  14. package/dist/batch-scheduler.d.ts.map +1 -0
  15. package/dist/batch-scheduler.js +147 -0
  16. package/dist/batch-scheduler.js.map +1 -0
  17. package/dist/browser-detect.d.ts +115 -0
  18. package/dist/browser-detect.d.ts.map +1 -0
  19. package/dist/browser-detect.js +160 -0
  20. package/dist/browser-detect.js.map +1 -0
  21. package/dist/concurrency-controller.d.ts +31 -0
  22. package/dist/concurrency-controller.d.ts.map +1 -0
  23. package/dist/concurrency-controller.js +33 -0
  24. package/dist/concurrency-controller.js.map +1 -0
  25. package/dist/errors.d.ts +51 -0
  26. package/dist/errors.d.ts.map +1 -0
  27. package/dist/errors.js +74 -0
  28. package/dist/errors.js.map +1 -0
  29. package/dist/executor.d.ts +9 -0
  30. package/dist/executor.d.ts.map +1 -1
  31. package/dist/executor.js +87 -48
  32. package/dist/executor.js.map +1 -1
  33. package/dist/history.d.ts.map +1 -1
  34. package/dist/history.js +3 -2
  35. package/dist/history.js.map +1 -1
  36. package/dist/idb-asset-store.d.ts.map +1 -1
  37. package/dist/idb-asset-store.js +6 -1
  38. package/dist/idb-asset-store.js.map +1 -1
  39. package/dist/index.d.ts +17 -15
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +20 -15
  42. package/dist/index.js.map +1 -1
  43. package/dist/managers/asset-manager.d.ts +114 -0
  44. package/dist/managers/asset-manager.d.ts.map +1 -0
  45. package/dist/managers/asset-manager.js +238 -0
  46. package/dist/managers/asset-manager.js.map +1 -0
  47. package/dist/managers/history-manager.d.ts +100 -0
  48. package/dist/managers/history-manager.d.ts.map +1 -0
  49. package/dist/managers/history-manager.js +296 -0
  50. package/dist/managers/history-manager.js.map +1 -0
  51. package/dist/managers/mcp-manifest-builder.d.ts +18 -0
  52. package/dist/managers/mcp-manifest-builder.d.ts.map +1 -0
  53. package/dist/managers/mcp-manifest-builder.js +99 -0
  54. package/dist/managers/mcp-manifest-builder.js.map +1 -0
  55. package/dist/managers/quota-manager.d.ts +42 -0
  56. package/dist/managers/quota-manager.d.ts.map +1 -0
  57. package/dist/managers/quota-manager.js +126 -0
  58. package/dist/managers/quota-manager.js.map +1 -0
  59. package/dist/managers/workflow-coordinator.d.ts +62 -0
  60. package/dist/managers/workflow-coordinator.d.ts.map +1 -0
  61. package/dist/managers/workflow-coordinator.js +125 -0
  62. package/dist/managers/workflow-coordinator.js.map +1 -0
  63. package/dist/opfs-asset-store.d.ts.map +1 -1
  64. package/dist/opfs-asset-store.js +12 -2
  65. package/dist/opfs-asset-store.js.map +1 -1
  66. package/dist/plugin-context.d.ts +35 -0
  67. package/dist/plugin-context.d.ts.map +1 -0
  68. package/dist/plugin-context.js +62 -0
  69. package/dist/plugin-context.js.map +1 -0
  70. package/dist/plugin-permissions.d.ts +99 -0
  71. package/dist/plugin-permissions.d.ts.map +1 -0
  72. package/dist/plugin-permissions.js +205 -0
  73. package/dist/plugin-permissions.js.map +1 -0
  74. package/dist/runtime-impl.d.ts +116 -0
  75. package/dist/runtime-impl.d.ts.map +1 -0
  76. package/dist/runtime-impl.js +270 -0
  77. package/dist/runtime-impl.js.map +1 -0
  78. package/dist/runtime.d.ts +10 -233
  79. package/dist/runtime.d.ts.map +1 -1
  80. package/dist/runtime.js +12 -978
  81. package/dist/runtime.js.map +1 -1
  82. package/dist/test-utils/fakes.d.ts +35 -0
  83. package/dist/test-utils/fakes.d.ts.map +1 -0
  84. package/dist/test-utils/fakes.js +60 -0
  85. package/dist/test-utils/fakes.js.map +1 -0
  86. package/dist/types.d.ts +113 -1
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/worker-host.d.ts.map +1 -1
  89. package/dist/worker-host.js +32 -9
  90. package/dist/worker-host.js.map +1 -1
  91. package/dist/worker-protocol.d.ts +30 -0
  92. package/dist/worker-protocol.d.ts.map +1 -1
  93. package/dist/worker-protocol.js +24 -0
  94. package/dist/worker-protocol.js.map +1 -1
  95. package/package.json +2 -2
  96. package/src/asset-store.ts +32 -2
  97. package/src/batch-processor.ts +133 -510
  98. package/src/batch-progress.ts +228 -0
  99. package/src/batch-scheduler.ts +209 -0
  100. package/src/browser-detect.ts +221 -0
  101. package/src/concurrency-controller.ts +52 -0
  102. package/src/errors.ts +82 -0
  103. package/src/executor.ts +106 -55
  104. package/src/history.ts +3 -2
  105. package/src/idb-asset-store.ts +7 -1
  106. package/src/index.ts +50 -15
  107. package/src/managers/asset-manager.ts +259 -0
  108. package/src/managers/history-manager.ts +327 -0
  109. package/src/managers/mcp-manifest-builder.ts +108 -0
  110. package/src/managers/quota-manager.ts +149 -0
  111. package/src/managers/workflow-coordinator.ts +158 -0
  112. package/src/opfs-asset-store.ts +16 -2
  113. package/src/plugin-context.ts +87 -0
  114. package/src/plugin-permissions.ts +228 -0
  115. package/src/runtime-impl.ts +298 -0
  116. package/src/runtime.ts +16 -1116
  117. package/src/test-utils/fakes.ts +72 -0
  118. package/src/types.ts +116 -0
  119. package/src/worker-host.ts +35 -7
  120. package/src/worker-protocol.ts +47 -0
  121. package/dist/workflow-builder.d.ts +0 -124
  122. package/dist/workflow-builder.d.ts.map +0 -1
  123. package/dist/workflow-builder.js +0 -240
  124. package/dist/workflow-builder.js.map +0 -1
  125. package/src/workflow-builder.ts +0 -303
@@ -0,0 +1,100 @@
1
+ /**
2
+ * History Manager(W1.3 从 runtime.ts 抽取)
3
+ *
4
+ * 持有每个工作流的 HistoryStack + initialInputs / currentOutputs 映射,
5
+ * 提供 history / undo / redo / jumpTo,监听 node:finished 自动 append,
6
+ * LRU 上限清理,持久化快照到 historyStore(IDB)支持跨会话恢复。
7
+ * deps 注入 eventBus / assetStore / historyStore?,不持有 Runtime 实例避免
8
+ * 循环依赖。prepareForRun / recordRunResult 供 WorkflowCoordinator 调用。
9
+ */
10
+ import type { AssetId, EventBus, HistoryEntry, LokvisEvent } from '@lokvis/schema';
11
+ import { HistoryStack } from '../history.js';
12
+ import type { HistoryStore } from '../history-store.js';
13
+ import type { QuotaAwareAssetStore } from './quota-manager.js';
14
+ export interface HistoryManagerDeps {
15
+ eventBus: EventBus;
16
+ assetStore: QuotaAwareAssetStore;
17
+ /** undefined 时退化为仅内存历史(刷新后丢失) */
18
+ historyStore?: HistoryStore;
19
+ }
20
+ /** 历史栈管理封装,由 RuntimeImpl 持有并委托 */
21
+ export declare class HistoryManager {
22
+ private readonly deps;
23
+ private historyStacks;
24
+ private initialInputsMap;
25
+ private currentOutputsMap;
26
+ /** 加载快照期间守卫:restore() 触发 onChanged → persistHistory 时跳过冗余写回 */
27
+ private isLoadingHistory;
28
+ /**
29
+ * 加载期间被跳过的 persistHistory 加入此 Set,loadPersistedHistory 结束后
30
+ * 逐个补 persist,避免加载期间变更被永久丢弃(W7.2 review 修复)。
31
+ */
32
+ private dirtyDuringLoad;
33
+ constructor(deps: HistoryManagerDeps);
34
+ history(workflowId: string): Promise<HistoryEntry[]>;
35
+ getHistoryState(workflowId: string): Promise<{
36
+ entries: HistoryEntry[];
37
+ cursor: number;
38
+ }>;
39
+ /** 工作流当前输出 AssetId(undo/redo 后的"当前"状态) */
40
+ getCurrentOutputs(workflowId: string): AssetId[];
41
+ undo(workflowId: string): Promise<void>;
42
+ redo(workflowId: string): Promise<void>;
43
+ jumpTo(workflowId: string, index: number): Promise<void>;
44
+ /**
45
+ * run() 前的历史栈准备:
46
+ * - appendHistory=true 且已有栈:保留栈与 currentOutputs,仅确保初始输入已记录
47
+ * - 否则(默认重置):丢弃旧历史,重新初始化
48
+ * 然后执行 LRU 上限清理。
49
+ */
50
+ prepareForRun(workflowId: string, inputIds: AssetId[], appendHistory: boolean): void;
51
+ /** run() 成功完成后,记录最终输出为当前(outputs 非空时)。 */
52
+ recordRunResult(workflowId: string, outputs: AssetId[]): void;
53
+ /** node:finished 事件处理:append 到对应工作流栈,inputs 取上一步 outputs。 */
54
+ recordFromNodeEvent(event: Extract<LokvisEvent, {
55
+ type: 'node:finished';
56
+ }>): void;
57
+ /**
58
+ * LRU 上限清理:超 MAX_CONCURRENT_WORKFLOW_STACKS 时按 FIFO 删最旧 stack
59
+ * (reset 触发 onEvict → assetStore.remove 回收资产)。
60
+ * appendHistory 模式下当前工作流栈必须跳过,否则 FIFO 首位时被误删。
61
+ */
62
+ enforceStacksLimit(currentWorkflowId: string): void;
63
+ /** 获取或创建工作流对应的 HistoryStack */
64
+ getOrCreateStack(workflowId: string): HistoryStack;
65
+ /**
66
+ * 清理指定工作流的历史栈(reset 触发 onEvict 回收资产),不取消运行中执行。
67
+ *
68
+ * TD-2.1 长期方案:返回 Promise 并在末尾 `await persistHistory`,使调用方
69
+ * (disposeWorkflow / disposeAll)能在持久化完成后才返回,消除测试轮询。
70
+ * stack.reset() 触发 onChanged → fire-and-forget persistHistory(删除空记录),
71
+ * 此处显式 await 保证 IDB 删除已落地;两次 delete 幂等,无副作用。
72
+ */
73
+ disposeHistory(workflowId: string): Promise<void>;
74
+ /**
75
+ * 清理所有工作流的历史栈(W21.6 runtime.dispose 用)。
76
+ *
77
+ * 复制 keys 后逐个 await disposeHistory,触发 onEvict 回收 outputs 资产。
78
+ * TD-2.1:改为 async 顺序 await,确保 runtime.dispose() 返回前所有持久化
79
+ * 删除已落地(避免 dispose 后 IDB 仍有残留记录的竞态)。
80
+ */
81
+ disposeAll(): Promise<void>;
82
+ /**
83
+ * 把工作流当前历史快照写入 historyStore(W7.2)。
84
+ *
85
+ * 时序:currentOutputs 从 stack snapshot 派生(cursor === -1 用 initialInputs,
86
+ * 否则用 entries[cursor].outputs),不读 currentOutputsMap —— onChanged 在
87
+ * undo/redo/jumpTo/run 内部同步触发时 map 尚未更新会读到旧值。
88
+ * 竞态:多个 fire-and-forget save 的 IDB readwrite 事务由 IndexedDB 引擎按发起
89
+ * 顺序串行化,无需应用层加链。
90
+ * entries 为空 → delete;加载期间跳过写回,记入 dirtyDuringLoad 后续补 persist。
91
+ */
92
+ persistHistory(workflowId: string): Promise<void>;
93
+ /**
94
+ * 从 historyStore 预加载所有持久化的历史快照(由 createRuntime 工厂调用)。
95
+ * 加载期间置 isLoadingHistory 守卫;被跳过的 persist 记入 dirtyDuringLoad,
96
+ * 加载结束后补 persist,避免加载期间其他来源变更被永久丢弃。
97
+ */
98
+ loadPersistedHistory(): Promise<void>;
99
+ }
100
+ //# sourceMappingURL=history-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-manager.d.ts","sourceRoot":"","sources":["../../src/managers/history-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAA2B,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAO/D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,oBAAoB,CAAC;IACjC,iCAAiC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,kCAAkC;AAClC,qBAAa,cAAc;IAYb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,iBAAiB,CAAgC;IACzD,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAS;IACjC;;;OAGG;IACH,OAAO,CAAC,eAAe,CAAqB;gBAEf,IAAI,EAAE,kBAAkB;IAQ/C,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIpD,eAAe,CACnB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAOvD,0CAA0C;IAC1C,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE;IAK1C,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9D;;;;;OAKG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,OAAO,GACrB,IAAI;IAgBP,0CAA0C;IAC1C,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI;IAM7D,6DAA6D;IAC7D,mBAAmB,CACjB,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,eAAe,CAAA;KAAE,CAAC,GACrD,IAAI;IAsBP;;;;OAIG;IACH,kBAAkB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAoBnD,+BAA+B;IAC/B,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY;IAgClD;;;;;;;OAOG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC;;;;;;;;;OASG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCvD;;;;OAIG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;CAsB5C"}
@@ -0,0 +1,296 @@
1
+ /**
2
+ * History Manager(W1.3 从 runtime.ts 抽取)
3
+ *
4
+ * 持有每个工作流的 HistoryStack + initialInputs / currentOutputs 映射,
5
+ * 提供 history / undo / redo / jumpTo,监听 node:finished 自动 append,
6
+ * LRU 上限清理,持久化快照到 historyStore(IDB)支持跨会话恢复。
7
+ * deps 注入 eventBus / assetStore / historyStore?,不持有 Runtime 实例避免
8
+ * 循环依赖。prepareForRun / recordRunResult 供 WorkflowCoordinator 调用。
9
+ */
10
+ import { generateId } from '../asset-store.js';
11
+ import { HistoryStack } from '../history.js';
12
+ const DEFAULT_MAX_HISTORY = 10;
13
+ /** 同时持有的工作流历史栈上限(W2.8);超限按 FIFO 清理最旧 stack。 */
14
+ const MAX_CONCURRENT_WORKFLOW_STACKS = 32;
15
+ /** 历史栈管理封装,由 RuntimeImpl 持有并委托 */
16
+ export class HistoryManager {
17
+ deps;
18
+ historyStacks = new Map();
19
+ initialInputsMap = new Map();
20
+ currentOutputsMap = new Map();
21
+ /** 加载快照期间守卫:restore() 触发 onChanged → persistHistory 时跳过冗余写回 */
22
+ isLoadingHistory = false;
23
+ /**
24
+ * 加载期间被跳过的 persistHistory 加入此 Set,loadPersistedHistory 结束后
25
+ * 逐个补 persist,避免加载期间变更被永久丢弃(W7.2 review 修复)。
26
+ */
27
+ dirtyDuringLoad = new Set();
28
+ constructor(deps) {
29
+ this.deps = deps;
30
+ this.deps.eventBus.on('node:finished', (event) => {
31
+ this.recordFromNodeEvent(event);
32
+ });
33
+ }
34
+ async history(workflowId) {
35
+ return this.historyStacks.get(workflowId)?.list() ?? [];
36
+ }
37
+ async getHistoryState(workflowId) {
38
+ const stack = this.historyStacks.get(workflowId);
39
+ if (!stack)
40
+ return { entries: [], cursor: -1 };
41
+ const snap = stack.snapshot();
42
+ return { entries: snap.entries, cursor: snap.cursor };
43
+ }
44
+ /** 工作流当前输出 AssetId(undo/redo 后的"当前"状态) */
45
+ getCurrentOutputs(workflowId) {
46
+ return this.currentOutputsMap.get(workflowId) ?? [];
47
+ }
48
+ // history:changed 事件由 stack.onChanged 统一发射,避免双发
49
+ async undo(workflowId) {
50
+ const stack = this.getOrCreateStack(workflowId);
51
+ const result = stack.undo();
52
+ if (result === undefined)
53
+ return;
54
+ // null 表示回到初始状态 → 用 initialInputs;entry → 用其 outputs
55
+ const newCurrent = result === null
56
+ ? (this.initialInputsMap.get(workflowId) ?? [])
57
+ : result.outputs;
58
+ this.currentOutputsMap.set(workflowId, newCurrent);
59
+ }
60
+ async redo(workflowId) {
61
+ const stack = this.getOrCreateStack(workflowId);
62
+ const entry = stack.redo();
63
+ if (entry === undefined)
64
+ return;
65
+ this.currentOutputsMap.set(workflowId, entry.outputs);
66
+ }
67
+ async jumpTo(workflowId, index) {
68
+ const stack = this.getOrCreateStack(workflowId);
69
+ const result = stack.jumpTo(index);
70
+ if (result === undefined)
71
+ return;
72
+ const newCurrent = result === null
73
+ ? (this.initialInputsMap.get(workflowId) ?? [])
74
+ : result.outputs;
75
+ this.currentOutputsMap.set(workflowId, newCurrent);
76
+ }
77
+ /**
78
+ * run() 前的历史栈准备:
79
+ * - appendHistory=true 且已有栈:保留栈与 currentOutputs,仅确保初始输入已记录
80
+ * - 否则(默认重置):丢弃旧历史,重新初始化
81
+ * 然后执行 LRU 上限清理。
82
+ */
83
+ prepareForRun(workflowId, inputIds, appendHistory) {
84
+ if (appendHistory && this.historyStacks.has(workflowId)) {
85
+ if (!this.initialInputsMap.has(workflowId)) {
86
+ this.initialInputsMap.set(workflowId, inputIds);
87
+ }
88
+ }
89
+ else {
90
+ const existingStack = this.historyStacks.get(workflowId);
91
+ if (existingStack) {
92
+ existingStack.reset();
93
+ }
94
+ this.initialInputsMap.set(workflowId, inputIds);
95
+ this.currentOutputsMap.set(workflowId, inputIds);
96
+ }
97
+ this.enforceStacksLimit(workflowId);
98
+ }
99
+ /** run() 成功完成后,记录最终输出为当前(outputs 非空时)。 */
100
+ recordRunResult(workflowId, outputs) {
101
+ if (outputs.length > 0) {
102
+ this.currentOutputsMap.set(workflowId, outputs);
103
+ }
104
+ }
105
+ /** node:finished 事件处理:append 到对应工作流栈,inputs 取上一步 outputs。 */
106
+ recordFromNodeEvent(event) {
107
+ if (!event.outputs || event.outputs.length === 0)
108
+ return;
109
+ const stack = this.getOrCreateStack(event.workflowId);
110
+ const outputs = event.outputs.map((a) => a.id);
111
+ const inputs = this.currentOutputsMap.get(event.workflowId) ?? [];
112
+ const entry = {
113
+ id: generateId(),
114
+ workflowId: event.workflowId,
115
+ nodeId: event.nodeId,
116
+ capability: event.capability,
117
+ params: event.params,
118
+ inputs,
119
+ outputs,
120
+ timestamp: Date.now(),
121
+ };
122
+ stack.append(entry);
123
+ this.currentOutputsMap.set(event.workflowId, outputs);
124
+ }
125
+ /**
126
+ * LRU 上限清理:超 MAX_CONCURRENT_WORKFLOW_STACKS 时按 FIFO 删最旧 stack
127
+ * (reset 触发 onEvict → assetStore.remove 回收资产)。
128
+ * appendHistory 模式下当前工作流栈必须跳过,否则 FIFO 首位时被误删。
129
+ */
130
+ enforceStacksLimit(currentWorkflowId) {
131
+ while (this.historyStacks.size >= MAX_CONCURRENT_WORKFLOW_STACKS) {
132
+ let oldestId = this.historyStacks.keys().next().value;
133
+ if (oldestId === undefined)
134
+ break;
135
+ if (oldestId === currentWorkflowId) {
136
+ const iter = this.historyStacks.keys();
137
+ iter.next();
138
+ oldestId = iter.next().value;
139
+ if (oldestId === undefined)
140
+ break;
141
+ }
142
+ const stack = this.historyStacks.get(oldestId);
143
+ if (stack) {
144
+ stack.reset();
145
+ }
146
+ this.historyStacks.delete(oldestId);
147
+ this.initialInputsMap.delete(oldestId);
148
+ this.currentOutputsMap.delete(oldestId);
149
+ }
150
+ }
151
+ /** 获取或创建工作流对应的 HistoryStack */
152
+ getOrCreateStack(workflowId) {
153
+ let stack = this.historyStacks.get(workflowId);
154
+ if (!stack) {
155
+ const stackConfig = {
156
+ maxEntries: DEFAULT_MAX_HISTORY,
157
+ onEvict: (entry) => {
158
+ // 淘汰条目时清理其 outputs 资产(避免 OPFS 泄漏)
159
+ // TD-3.2: opfs/idb asset-store 的 remove 已对 NotFoundError 静默忽略
160
+ // (幂等删除),因此此处 catch 到的都是真实错误(权限/IO/quota 等),
161
+ // 用 error 级别记录,便于 Sentry 上报与孤儿资产排查
162
+ for (const assetId of entry.outputs) {
163
+ this.deps.assetStore.remove(assetId).catch((err) => {
164
+ console.error(`[lokvis] onEvict: remove(${assetId}) failed:`, err);
165
+ });
166
+ }
167
+ },
168
+ onChanged: (wfId, entries, currentIndex) => {
169
+ this.deps.eventBus.emit({
170
+ type: 'history:changed',
171
+ workflowId: wfId,
172
+ entries,
173
+ currentIndex,
174
+ });
175
+ void this.persistHistory(wfId);
176
+ },
177
+ };
178
+ stack = new HistoryStack(workflowId, stackConfig);
179
+ this.historyStacks.set(workflowId, stack);
180
+ }
181
+ return stack;
182
+ }
183
+ /**
184
+ * 清理指定工作流的历史栈(reset 触发 onEvict 回收资产),不取消运行中执行。
185
+ *
186
+ * TD-2.1 长期方案:返回 Promise 并在末尾 `await persistHistory`,使调用方
187
+ * (disposeWorkflow / disposeAll)能在持久化完成后才返回,消除测试轮询。
188
+ * stack.reset() 触发 onChanged → fire-and-forget persistHistory(删除空记录),
189
+ * 此处显式 await 保证 IDB 删除已落地;两次 delete 幂等,无副作用。
190
+ */
191
+ async disposeHistory(workflowId) {
192
+ const stack = this.historyStacks.get(workflowId);
193
+ if (stack) {
194
+ stack.reset();
195
+ this.historyStacks.delete(workflowId);
196
+ }
197
+ this.initialInputsMap.delete(workflowId);
198
+ this.currentOutputsMap.delete(workflowId);
199
+ await this.persistHistory(workflowId);
200
+ }
201
+ /**
202
+ * 清理所有工作流的历史栈(W21.6 runtime.dispose 用)。
203
+ *
204
+ * 复制 keys 后逐个 await disposeHistory,触发 onEvict 回收 outputs 资产。
205
+ * TD-2.1:改为 async 顺序 await,确保 runtime.dispose() 返回前所有持久化
206
+ * 删除已落地(避免 dispose 后 IDB 仍有残留记录的竞态)。
207
+ */
208
+ async disposeAll() {
209
+ // 复制一份:disposeHistory 内 stack.reset() → onEvict → assetStore.remove
210
+ // 可能间接触发其他 listener 改动 historyStacks(理论上不会,但防御性写法)
211
+ for (const workflowId of [...this.historyStacks.keys()]) {
212
+ await this.disposeHistory(workflowId);
213
+ }
214
+ this.dirtyDuringLoad.clear();
215
+ }
216
+ /**
217
+ * 把工作流当前历史快照写入 historyStore(W7.2)。
218
+ *
219
+ * 时序:currentOutputs 从 stack snapshot 派生(cursor === -1 用 initialInputs,
220
+ * 否则用 entries[cursor].outputs),不读 currentOutputsMap —— onChanged 在
221
+ * undo/redo/jumpTo/run 内部同步触发时 map 尚未更新会读到旧值。
222
+ * 竞态:多个 fire-and-forget save 的 IDB readwrite 事务由 IndexedDB 引擎按发起
223
+ * 顺序串行化,无需应用层加链。
224
+ * entries 为空 → delete;加载期间跳过写回,记入 dirtyDuringLoad 后续补 persist。
225
+ */
226
+ async persistHistory(workflowId) {
227
+ if (!this.deps.historyStore)
228
+ return;
229
+ if (this.isLoadingHistory) {
230
+ this.dirtyDuringLoad.add(workflowId);
231
+ return;
232
+ }
233
+ // fire-and-forget 调用方用 `void this.persistHistory(...)`,故内部必须 try/catch,
234
+ // 否则 IDB 故障会变成 unhandled promise rejection。
235
+ try {
236
+ const stack = this.historyStacks.get(workflowId);
237
+ // 栈已从内存移除(disposeHistory / enforceStacksLimit 后异步到达)→ 删除持久化记录
238
+ if (!stack) {
239
+ await this.deps.historyStore.delete(workflowId);
240
+ return;
241
+ }
242
+ const { entries, cursor } = stack.snapshot();
243
+ if (entries.length === 0) {
244
+ await this.deps.historyStore.delete(workflowId);
245
+ return;
246
+ }
247
+ const initialInputs = this.initialInputsMap.get(workflowId) ?? [];
248
+ const currentOutputs = cursor === -1
249
+ ? initialInputs
250
+ : (entries[cursor]?.outputs ?? initialInputs);
251
+ await this.deps.historyStore.save({
252
+ workflowId,
253
+ entries,
254
+ cursor,
255
+ initialInputs,
256
+ currentOutputs,
257
+ updatedAt: Date.now(),
258
+ });
259
+ }
260
+ catch (err) {
261
+ console.warn(`[lokvis] persistHistory failed for workflow ${workflowId}:`, err);
262
+ }
263
+ }
264
+ /**
265
+ * 从 historyStore 预加载所有持久化的历史快照(由 createRuntime 工厂调用)。
266
+ * 加载期间置 isLoadingHistory 守卫;被跳过的 persist 记入 dirtyDuringLoad,
267
+ * 加载结束后补 persist,避免加载期间其他来源变更被永久丢弃。
268
+ */
269
+ async loadPersistedHistory() {
270
+ if (!this.deps.historyStore)
271
+ return;
272
+ let records = [];
273
+ this.isLoadingHistory = true;
274
+ try {
275
+ records = await this.deps.historyStore.loadAll();
276
+ for (const record of records) {
277
+ if (record.entries.length === 0)
278
+ continue; // 跳过空记录(双重防御)
279
+ const stack = this.getOrCreateStack(record.workflowId);
280
+ stack.restore({ entries: record.entries, cursor: record.cursor });
281
+ this.initialInputsMap.set(record.workflowId, record.initialInputs);
282
+ this.currentOutputsMap.set(record.workflowId, record.currentOutputs);
283
+ }
284
+ }
285
+ finally {
286
+ this.isLoadingHistory = false;
287
+ // 补 persist:逐个 await 保证顺序;复制一份避免补 persist 过程中
288
+ // 新触发 onChanged → dirtyDuringLoad 死循环
289
+ const pending = [...this.dirtyDuringLoad];
290
+ this.dirtyDuringLoad.clear();
291
+ for (const wfId of pending)
292
+ await this.persistHistory(wfId);
293
+ }
294
+ }
295
+ }
296
+ //# sourceMappingURL=history-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history-manager.js","sourceRoot":"","sources":["../../src/managers/history-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAA2B,MAAM,eAAe,CAAC;AAItE,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,+CAA+C;AAC/C,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAS1C,kCAAkC;AAClC,MAAM,OAAO,cAAc;IAYI;IAXrB,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;IAChD,gBAAgB,GAAG,IAAI,GAAG,EAAqB,CAAC;IAChD,iBAAiB,GAAG,IAAI,GAAG,EAAqB,CAAC;IACzD,+DAA+D;IACvD,gBAAgB,GAAG,KAAK,CAAC;IACjC;;;OAGG;IACK,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAE5C,YAA6B,IAAwB;QAAxB,SAAI,GAAJ,IAAI,CAAoB;QACnD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/C,IAAI,CAAC,mBAAmB,CACtB,KAAwD,CACzD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,UAAkB;QAC9B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,UAAkB;QAElB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,0CAA0C;IAC1C,iBAAiB,CAAC,UAAkB;QAClC,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IACtD,CAAC;IAED,gDAAgD;IAChD,KAAK,CAAC,IAAI,CAAC,UAAkB;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,qDAAqD;QACrD,MAAM,UAAU,GAAG,MAAM,KAAK,IAAI;YAChC,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC/C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACnB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAkB;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QAChC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,KAAa;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,MAAM,UAAU,GAAG,MAAM,KAAK,IAAI;YAChC,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC/C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACnB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,aAAa,CACX,UAAkB,EAClB,QAAmB,EACnB,aAAsB;QAEtB,IAAI,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACzD,IAAI,aAAa,EAAE,CAAC;gBAClB,aAAa,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;YACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,0CAA0C;IAC1C,eAAe,CAAC,UAAkB,EAAE,OAAkB;QACpD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,mBAAmB,CACjB,KAAsD;QAEtD,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEzD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAElE,MAAM,KAAK,GAAiB;YAC1B,EAAE,EAAE,UAAU,EAAE;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM;YACN,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,iBAAyB;QAC1C,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,IAAI,8BAA8B,EAAE,CAAC;YACjE,IAAI,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACtD,IAAI,QAAQ,KAAK,SAAS;gBAAE,MAAM;YAClC,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;gBACvC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBAC7B,IAAI,QAAQ,KAAK,SAAS;oBAAE,MAAM;YACpC,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACvC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,gBAAgB,CAAC,UAAkB;QACjC,IAAI,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,WAAW,GAAgC;gBAC/C,UAAU,EAAE,mBAAmB;gBAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACjB,kCAAkC;oBAClC,8DAA8D;oBAC9D,6CAA6C;oBAC7C,mCAAmC;oBACnC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;wBACpC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;4BACjD,OAAO,CAAC,KAAK,CAAC,4BAA4B,OAAO,WAAW,EAAE,GAAG,CAAC,CAAC;wBACrE,CAAC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBACD,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE;oBACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;wBACtB,IAAI,EAAE,iBAAiB;wBACvB,UAAU,EAAE,IAAI;wBAChB,OAAO;wBACP,YAAY;qBACb,CAAC,CAAC;oBACH,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;gBACjC,CAAC;aACF,CAAC;YACF,KAAK,GAAG,IAAI,YAAY,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YAClD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,UAAU;QACd,oEAAoE;QACpE,mDAAmD;QACnD,KAAK,MAAM,UAAU,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB;QACrC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QACpC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QACD,wEAAwE;QACxE,4CAA4C;QAC5C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACjD,8DAA8D;YAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YACD,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAClE,MAAM,cAAc,GAAG,MAAM,KAAK,CAAC,CAAC;gBAClC,CAAC,CAAC,aAAa;gBACf,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,IAAI,aAAa,CAAC,CAAC;YAChD,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;gBAChC,UAAU;gBACV,OAAO;gBACP,MAAM;gBACN,aAAa;gBACb,cAAc;gBACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,+CAA+C,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,oBAAoB;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QACpC,IAAI,OAAO,GAAiD,EAAE,CAAC;QAC/D,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YACjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS,CAAC,cAAc;gBACzD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACvD,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAClE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;gBACnE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;YAC9B,8CAA8C;YAC9C,sCAAsC;YACtC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;YAC1C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,OAAO;gBAAE,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * MCP Manifest Builder(W1.5 从 runtime.ts 抽取)
3
+ *
4
+ * 把已注册 Capability 列表转换为 MCP server manifest(纯函数,无 `this`,
5
+ * 不依赖 CapabilityRegistry)。Runtime.toMcpManifest() 委托本模块。
6
+ *
7
+ * 暴露规则(见 docs/AI生态冲击调整方案.md §6):
8
+ * - private:任何模式都不暴露
9
+ * - batch-only:仅在 options.batchMode=true 时暴露(避免单文件误用)
10
+ * - 其余(默认 public):总是暴露
11
+ * tool 名取 capability.mcpToolName 或 `lokvis_${name.replace(/\./g, '_')}`;
12
+ * resource 固定为 capabilities 与 workflows 两个清单。
13
+ */
14
+ import type { Capability, McpManifest } from '@lokvis/schema';
15
+ import type { ToMcpManifestOptions } from '../types.js';
16
+ /** 生成 MCP server manifest(不启动 server,仅描述可被 MCP 暴露的能力)。 */
17
+ export declare function toMcpManifest(capabilities: Capability[], options: ToMcpManifestOptions | undefined, version: string): McpManifest;
18
+ //# sourceMappingURL=mcp-manifest-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-manifest-builder.d.ts","sourceRoot":"","sources":["../../src/managers/mcp-manifest-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAMxD,0DAA0D;AAC1D,wBAAgB,aAAa,CAC3B,YAAY,EAAE,UAAU,EAAE,EAC1B,OAAO,EAAE,oBAAoB,YAAK,EAClC,OAAO,EAAE,MAAM,GACd,WAAW,CAwBb"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * MCP Manifest Builder(W1.5 从 runtime.ts 抽取)
3
+ *
4
+ * 把已注册 Capability 列表转换为 MCP server manifest(纯函数,无 `this`,
5
+ * 不依赖 CapabilityRegistry)。Runtime.toMcpManifest() 委托本模块。
6
+ *
7
+ * 暴露规则(见 docs/AI生态冲击调整方案.md §6):
8
+ * - private:任何模式都不暴露
9
+ * - batch-only:仅在 options.batchMode=true 时暴露(避免单文件误用)
10
+ * - 其余(默认 public):总是暴露
11
+ * tool 名取 capability.mcpToolName 或 `lokvis_${name.replace(/\./g, '_')}`;
12
+ * resource 固定为 capabilities 与 workflows 两个清单。
13
+ */
14
+ const SERVER_NAME = 'lokvis';
15
+ const CAPABILITIES_URI = 'lokvis://capabilities';
16
+ const WORKFLOWS_URI = 'lokvis://workflows';
17
+ /** 生成 MCP server manifest(不启动 server,仅描述可被 MCP 暴露的能力)。 */
18
+ export function toMcpManifest(capabilities, options = {}, version) {
19
+ const { batchMode = false } = options;
20
+ const tools = [];
21
+ for (const cap of capabilities) {
22
+ if (cap.mcpExposure === 'private')
23
+ continue;
24
+ if (cap.mcpExposure === 'batch-only' && !batchMode)
25
+ continue;
26
+ const toolName = cap.mcpToolName ?? `lokvis_${cap.name.replace(/\./g, '_')}`;
27
+ tools.push({
28
+ name: toolName,
29
+ description: cap.description,
30
+ inputSchema: capabilityParamsToJsonSchema(cap.params),
31
+ capabilities: [cap.name],
32
+ });
33
+ }
34
+ return {
35
+ serverName: SERVER_NAME, version, tools,
36
+ resources: [
37
+ { uri: CAPABILITIES_URI, name: 'Capabilities',
38
+ description: 'List all available Lokvis capabilities',
39
+ mimeType: 'application/json' },
40
+ { uri: WORKFLOWS_URI, name: 'Workflows',
41
+ description: 'List saved workflows', mimeType: 'application/json' },
42
+ ],
43
+ };
44
+ }
45
+ /** Capability.params(CapabilityParam[])→ JSON Schema 对象(手写,非 zod-to-json-schema)。 */
46
+ function capabilityParamsToJsonSchema(params) {
47
+ const properties = {};
48
+ const required = [];
49
+ for (const p of params) {
50
+ properties[p.name] = capabilityParamToJsonSchemaProperty(p);
51
+ if (p.required)
52
+ required.push(p.name);
53
+ }
54
+ return { type: 'object', properties,
55
+ ...(required.length > 0 ? { required } : {}) };
56
+ }
57
+ /**
58
+ * CapabilityParamType → 合法 JSON Schema 类型片段。
59
+ * color/file/enum 非 JSON Schema 标准类型,color→string+format,
60
+ * file/enum→string(enum 值由外层 enum 字段追加);其余同名 JSON Schema 类型。
61
+ */
62
+ function capabilityParamTypeToJsonType(type) {
63
+ switch (type) {
64
+ case 'color': return { type: 'string', format: 'color' };
65
+ case 'file':
66
+ case 'enum': return { type: 'string' };
67
+ case 'number':
68
+ case 'string':
69
+ case 'boolean':
70
+ case 'object':
71
+ case 'array': return { type };
72
+ default: return { type: 'string' };
73
+ }
74
+ }
75
+ /** 单个 CapabilityParam → JSON Schema property */
76
+ function capabilityParamToJsonSchemaProperty(p) {
77
+ const { type: jsonType, format } = capabilityParamTypeToJsonType(p.type);
78
+ const prop = { type: jsonType };
79
+ if (format)
80
+ prop.format = format;
81
+ if (p.description)
82
+ prop.description = p.description;
83
+ if (p.default !== undefined)
84
+ prop.default = p.default;
85
+ // min/max 仅对 number 合法(string 应为 minLength/maxLength)
86
+ if (p.type === 'number') {
87
+ if (typeof p.min === 'number')
88
+ prop.minimum = p.min;
89
+ if (typeof p.max === 'number')
90
+ prop.maximum = p.max;
91
+ }
92
+ if (p.type === 'enum' && p.values)
93
+ prop.enum = p.values;
94
+ if (p.type === 'array' && p.items) {
95
+ prop.items = capabilityParamTypeToJsonType(p.items);
96
+ }
97
+ return prop;
98
+ }
99
+ //# sourceMappingURL=mcp-manifest-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-manifest-builder.js","sourceRoot":"","sources":["../../src/managers/mcp-manifest-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Quota Manager
3
+ *
4
+ * 从 runtime.ts 抽取的存储配额管理逻辑(W2.9)。
5
+ *
6
+ * 职责:用配额校验包裹 AssetStore,import/create 超限时抛 QuotaExceededError,
7
+ * 并维护运行中的已用字节数供 runtime.getStorageUsage O(1) 读取。
8
+ *
9
+ * 并发安全:import/create/remove 通过 promise 链串行化,避免 check 与 update
10
+ * 之间的 TOCTOU 窗口导致两个并发操作都基于旧 usage 通过校验或回退。
11
+ */
12
+ import type { AssetSource } from '@lokvis/schema';
13
+ import type { AssetStore } from '../asset-store.js';
14
+ /** 存储配额超限时抛出(W2.9) */
15
+ export declare class QuotaExceededError extends Error {
16
+ readonly usage: number;
17
+ readonly delta: number;
18
+ readonly quota: number;
19
+ constructor(usage: number, delta: number, quota: number);
20
+ }
21
+ /**
22
+ * 从 AssetSource 估算导入字节数用于配额预检。
23
+ * url/opfs 源大小未知,返回 0 跳过预检 —— 真实大小在 import 完成后
24
+ * 通过 `usage += asset.metadata.size` 补记到账面,后续操作仍受配额约束。
25
+ */
26
+ export declare function estimateSourceSize(source: AssetSource): number;
27
+ /**
28
+ * 配额感知的 AssetStore:在 AssetStore 接口之上扩展 _getQuotaUsage,
29
+ * 供 runtime.getStorageUsage O(1) 读取内部 usage(下划线表"内部 API")。
30
+ */
31
+ export type QuotaAwareAssetStore = AssetStore & {
32
+ _getQuotaUsage: () => number;
33
+ };
34
+ /**
35
+ * 用配额校验包裹 AssetStore:import/create 超限抛 QuotaExceededError(W2.9)。
36
+ * 内部维护运行中的已用字节数,remove 时回退。
37
+ *
38
+ * 并发安全:import/create/remove 通过 promise 链串行化,避免 check 与 update
39
+ * 之间的 TOCTOU 窗口导致两个并发操作都基于旧 usage 通过校验或回退。
40
+ */
41
+ export declare function wrapAssetStoreWithQuota(inner: AssetStore, quota: number): QuotaAwareAssetStore;
42
+ //# sourceMappingURL=quota-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-manager.d.ts","sourceRoot":"","sources":["../../src/managers/quota-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,sBAAsB;AACtB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CASxD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAI9D;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG;IAC9C,cAAc,EAAE,MAAM,MAAM,CAAC;CAC9B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,GACZ,oBAAoB,CAwFtB"}