@qing3a/flow-rpa-engine 0.1.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.
Files changed (55) hide show
  1. package/dist/atomic.d.ts +9 -0
  2. package/dist/atomic.js +32 -0
  3. package/dist/atomic.js.map +1 -0
  4. package/dist/bench.d.ts +57 -0
  5. package/dist/bench.js +112 -0
  6. package/dist/bench.js.map +1 -0
  7. package/dist/cdp/client.d.ts +70 -0
  8. package/dist/cdp/client.js +272 -0
  9. package/dist/cdp/client.js.map +1 -0
  10. package/dist/cdp/page.d.ts +93 -0
  11. package/dist/cdp/page.js +411 -0
  12. package/dist/cdp/page.js.map +1 -0
  13. package/dist/executor.d.ts +122 -0
  14. package/dist/executor.js +399 -0
  15. package/dist/executor.js.map +1 -0
  16. package/dist/export.d.ts +128 -0
  17. package/dist/export.js +234 -0
  18. package/dist/export.js.map +1 -0
  19. package/dist/gate.d.ts +56 -0
  20. package/dist/gate.js +144 -0
  21. package/dist/gate.js.map +1 -0
  22. package/dist/hello.d.ts +5 -0
  23. package/dist/hello.js +13 -0
  24. package/dist/hello.js.map +1 -0
  25. package/dist/human.d.ts +162 -0
  26. package/dist/human.js +506 -0
  27. package/dist/human.js.map +1 -0
  28. package/dist/index.d.ts +20 -0
  29. package/dist/index.js +16 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/learning.d.ts +82 -0
  32. package/dist/learning.js +240 -0
  33. package/dist/learning.js.map +1 -0
  34. package/dist/mask.d.ts +32 -0
  35. package/dist/mask.js +70 -0
  36. package/dist/mask.js.map +1 -0
  37. package/dist/platform.d.ts +66 -0
  38. package/dist/platform.js +215 -0
  39. package/dist/platform.js.map +1 -0
  40. package/dist/record.d.ts +48 -0
  41. package/dist/record.js +42 -0
  42. package/dist/record.js.map +1 -0
  43. package/dist/schema.d.ts +130 -0
  44. package/dist/schema.js +93 -0
  45. package/dist/schema.js.map +1 -0
  46. package/dist/simprofile.d.ts +94 -0
  47. package/dist/simprofile.js +247 -0
  48. package/dist/simprofile.js.map +1 -0
  49. package/dist/storage.d.ts +63 -0
  50. package/dist/storage.js +138 -0
  51. package/dist/storage.js.map +1 -0
  52. package/dist/validate.d.ts +37 -0
  53. package/dist/validate.js +108 -0
  54. package/dist/validate.js.map +1 -0
  55. package/package.json +32 -0
@@ -0,0 +1,82 @@
1
+ import type { RunRecord, StepRecord } from './record.js';
2
+ /**
3
+ * L2 学习管线 —— 执行记录 → 结构化信号 → 改进建议 → 人工确认后应用(移植自 Rust `learning.rs`)。
4
+ *
5
+ * 数据流:
6
+ * 1. `learnFromRun`:把一次执行记录(run.json)转成结构化信号,与历史信号一起
7
+ * 喂给 `SuggestionStore`,产出改进建议(标注来源=local 规则分析)。
8
+ * 2. `SuggestionStore`:建议持久化(suggestions/ 目录),供 list_suggestions /
9
+ * apply_suggestion / dismiss_suggestion。
10
+ * 3. 成熟度阈值:同一改进被多次成功复用(样本 ≥ MATURITY_MIN_SAMPLES=5)后标记「成熟」,
11
+ * 此后 apply 可自动执行,不再强制人工确认。
12
+ * 4. 人工确认后应用:未成熟建议 apply 要求 confirm=true(结构改动必须人工确认)。
13
+ */
14
+ /** 成熟度门槛(样本数):同一改进样本 ≥ 门槛后标记「成熟」(与观察样本门槛 <5 同构) */
15
+ export declare const MATURITY_MIN_SAMPLES = 5;
16
+ /** 本地规则分析来源标识(与平台建议区分) */
17
+ export declare const SOURCE_LOCAL = "local";
18
+ /** 平台侧建议来源标识 */
19
+ export declare const SOURCE_PLATFORM = "platform";
20
+ /** 建议类别(结构化字段) */
21
+ export type SuggestionKind = 'extend_verify_timeout' | 'change_selector' | 'add_verify' | 'change_extract_selector' | 'check_input_value' | 'add_pre_wait';
22
+ export declare const SUGGESTION_KINDS: readonly SuggestionKind[];
23
+ /** 一条改进建议(持久化在 suggestions/<flow_id>/ 下,suggestion_id 即文件名) */
24
+ export interface Suggestion {
25
+ suggestionId: string;
26
+ flowId: string;
27
+ kind: SuggestionKind;
28
+ /** 目标步骤 id(空 = 流程级) */
29
+ stepId: string;
30
+ /** 人类可读的建议内容(标注来源与依据) */
31
+ message: string;
32
+ /** 来源:local(本地规则分析)/ platform(平台 agent 回复) */
33
+ source: string;
34
+ /** pending(待人工确认)/ applied(已应用)/ dismissed(已忽略)/ mature(成熟,可自动执行) */
35
+ status: 'pending' | 'applied' | 'dismissed' | 'mature';
36
+ /** 该改进已被成功复用的次数(成熟度样本计数) */
37
+ reuseCount: number;
38
+ /** 创建时间(ISO) */
39
+ createdAt: string;
40
+ /** 应用结果(status=applied 时) */
41
+ appliedNote?: string;
42
+ }
43
+ /** 建议存储:suggestions/<flow_id>/<suggestion_id>.json + 内存索引(去重/成熟度) */
44
+ export declare class SuggestionStore {
45
+ private readonly root;
46
+ /** 索引:suggestion_id → 文件路径 */
47
+ private index;
48
+ /** flow_id → 建议列表(懒加载缓存) */
49
+ private byFlow;
50
+ constructor(root: string);
51
+ private flowDir;
52
+ private suggestionPath;
53
+ /** 读取某流程的全部建议(懒加载缓存;suggestions/<flow_id>/ 下 *.json) */
54
+ list(flowId: string): Suggestion[];
55
+ /** 列出全部流程的建议(供 list_suggestions 无 flow_id 时用) */
56
+ listAll(): Suggestion[];
57
+ private save;
58
+ /**
59
+ * 插入/去重:同 flow+kind+step_id 且未终止(pending/mature)的建议不重复创建,
60
+ * 只累加 reuseCount;否则创建新建议(status=pending)。返回建议 id。
61
+ */
62
+ upsert(flowId: string, kind: SuggestionKind, stepId: string, message: string): string;
63
+ private loadById;
64
+ /**
65
+ * 应用建议(人工确认后;成熟建议自动执行)。返回 (成功与否, 说明)。
66
+ * confirm:未成熟建议必须 confirm=true(人工确认);成熟建议可 confirm=false 自动执行。
67
+ */
68
+ apply(suggestionId: string, confirm: boolean): string;
69
+ /** 忽略建议(人工处置:不再展示) */
70
+ dismiss(suggestionId: string): string;
71
+ }
72
+ /**
73
+ * 从一次执行记录提取结构化信号,调用学习管线生成建议。
74
+ * 返回本次学习产生的建议 id 列表(去重后实际新增/累积的)。
75
+ */
76
+ export declare function learnFromRun(rec: RunRecord, store: SuggestionStore): string[];
77
+ /**
78
+ * 从失败步骤分类出建议类别(v1 规则)。无法分类的失败返回 undefined —— 不产生建议。
79
+ */
80
+ export declare function classifyFailure(step: StepRecord): SuggestionKind | undefined;
81
+ /** 判断是否「成熟」(同改进被复用 ≥ 门槛后自动执行) */
82
+ export declare function isMature(s: Suggestion): boolean;
@@ -0,0 +1,240 @@
1
+ import { mkdirSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { writeJsonAtomic } from './atomic.js';
4
+ import { nowIso } from './storage.js';
5
+ /**
6
+ * L2 学习管线 —— 执行记录 → 结构化信号 → 改进建议 → 人工确认后应用(移植自 Rust `learning.rs`)。
7
+ *
8
+ * 数据流:
9
+ * 1. `learnFromRun`:把一次执行记录(run.json)转成结构化信号,与历史信号一起
10
+ * 喂给 `SuggestionStore`,产出改进建议(标注来源=local 规则分析)。
11
+ * 2. `SuggestionStore`:建议持久化(suggestions/ 目录),供 list_suggestions /
12
+ * apply_suggestion / dismiss_suggestion。
13
+ * 3. 成熟度阈值:同一改进被多次成功复用(样本 ≥ MATURITY_MIN_SAMPLES=5)后标记「成熟」,
14
+ * 此后 apply 可自动执行,不再强制人工确认。
15
+ * 4. 人工确认后应用:未成熟建议 apply 要求 confirm=true(结构改动必须人工确认)。
16
+ */
17
+ /** 成熟度门槛(样本数):同一改进样本 ≥ 门槛后标记「成熟」(与观察样本门槛 <5 同构) */
18
+ export const MATURITY_MIN_SAMPLES = 5;
19
+ /** 本地规则分析来源标识(与平台建议区分) */
20
+ export const SOURCE_LOCAL = 'local';
21
+ /** 平台侧建议来源标识 */
22
+ export const SOURCE_PLATFORM = 'platform';
23
+ export const SUGGESTION_KINDS = [
24
+ 'extend_verify_timeout',
25
+ 'change_selector',
26
+ 'add_verify',
27
+ 'change_extract_selector',
28
+ 'check_input_value',
29
+ 'add_pre_wait',
30
+ ];
31
+ /** 建议存储:suggestions/<flow_id>/<suggestion_id>.json + 内存索引(去重/成熟度) */
32
+ export class SuggestionStore {
33
+ root;
34
+ /** 索引:suggestion_id → 文件路径 */
35
+ index = new Map();
36
+ /** flow_id → 建议列表(懒加载缓存) */
37
+ byFlow = new Map();
38
+ constructor(root) {
39
+ this.root = root;
40
+ mkdirSync(join(root, 'suggestions'), { recursive: true });
41
+ }
42
+ flowDir(flowId) {
43
+ return join(this.root, 'suggestions', flowId);
44
+ }
45
+ suggestionPath(flowId, suggestionId) {
46
+ return join(this.flowDir(flowId), `${suggestionId}.json`);
47
+ }
48
+ /** 读取某流程的全部建议(懒加载缓存;suggestions/<flow_id>/ 下 *.json) */
49
+ list(flowId) {
50
+ const cached = this.byFlow.get(flowId);
51
+ if (cached)
52
+ return [...cached];
53
+ const out = [];
54
+ try {
55
+ for (const name of readdirSync(this.flowDir(flowId))) {
56
+ if (!name.endsWith('.json'))
57
+ continue;
58
+ const path = join(this.flowDir(flowId), name);
59
+ try {
60
+ const s = JSON.parse(readFileSync(path, 'utf8'));
61
+ if (s && typeof s === 'object' && typeof s.suggestionId === 'string') {
62
+ this.index.set(s.suggestionId, path);
63
+ out.push(s);
64
+ }
65
+ }
66
+ catch {
67
+ // 跳过损坏建议文件
68
+ }
69
+ }
70
+ }
71
+ catch {
72
+ // 目录不存在 → 空列表
73
+ }
74
+ out.sort((a, b) => (a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0));
75
+ this.byFlow.set(flowId, [...out]);
76
+ return out;
77
+ }
78
+ /** 列出全部流程的建议(供 list_suggestions 无 flow_id 时用) */
79
+ listAll() {
80
+ const out = [];
81
+ try {
82
+ for (const name of readdirSync(join(this.root, 'suggestions'))) {
83
+ out.push(...this.list(name));
84
+ }
85
+ }
86
+ catch {
87
+ // 无 suggestions 目录 → 空
88
+ }
89
+ return out;
90
+ }
91
+ save(s) {
92
+ mkdirSync(this.flowDir(s.flowId), { recursive: true });
93
+ const path = this.suggestionPath(s.flowId, s.suggestionId);
94
+ writeJsonAtomic(path, s);
95
+ this.index.set(s.suggestionId, path);
96
+ const entries = this.byFlow.get(s.flowId) ?? [];
97
+ const idx = entries.findIndex((e) => e.suggestionId === s.suggestionId);
98
+ if (idx !== -1)
99
+ entries[idx] = { ...s };
100
+ else
101
+ entries.push({ ...s });
102
+ entries.sort((a, b) => (a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0));
103
+ this.byFlow.set(s.flowId, [...entries]);
104
+ }
105
+ /**
106
+ * 插入/去重:同 flow+kind+step_id 且未终止(pending/mature)的建议不重复创建,
107
+ * 只累加 reuseCount;否则创建新建议(status=pending)。返回建议 id。
108
+ */
109
+ upsert(flowId, kind, stepId, message) {
110
+ this.list(flowId);
111
+ const active = this.byFlow.get(flowId)?.filter((s) => s.status === 'pending' || s.status === 'mature') ?? [];
112
+ const existing = active.find((s) => s.kind === kind && s.stepId === stepId);
113
+ if (existing) {
114
+ existing.reuseCount += 1;
115
+ if (existing.reuseCount >= MATURITY_MIN_SAMPLES) {
116
+ existing.status = 'mature';
117
+ }
118
+ this.save(existing);
119
+ return existing.suggestionId;
120
+ }
121
+ const id = `sug_${flowId}_${nowIso().replace(/[-:]/g, '')}`;
122
+ const s = {
123
+ suggestionId: id,
124
+ flowId,
125
+ kind,
126
+ stepId,
127
+ message,
128
+ source: SOURCE_LOCAL,
129
+ status: 'pending',
130
+ reuseCount: 1,
131
+ createdAt: nowIso(),
132
+ appliedNote: undefined,
133
+ };
134
+ this.save(s);
135
+ return id;
136
+ }
137
+ loadById(suggestionId) {
138
+ const path = this.index.get(suggestionId);
139
+ if (!path)
140
+ return undefined;
141
+ try {
142
+ return JSON.parse(readFileSync(path, 'utf8'));
143
+ }
144
+ catch {
145
+ return undefined;
146
+ }
147
+ }
148
+ /**
149
+ * 应用建议(人工确认后;成熟建议自动执行)。返回 (成功与否, 说明)。
150
+ * confirm:未成熟建议必须 confirm=true(人工确认);成熟建议可 confirm=false 自动执行。
151
+ */
152
+ apply(suggestionId, confirm) {
153
+ const s = this.loadById(suggestionId);
154
+ if (!s)
155
+ throw new Error(`建议不存在: ${suggestionId}`);
156
+ if (s.status === 'applied')
157
+ throw new Error('建议已应用过');
158
+ if (s.status === 'dismissed')
159
+ throw new Error('建议已被忽略');
160
+ if (s.status === 'pending' && !confirm) {
161
+ throw new Error('建议待人工确认:需 confirm=true 后应用(结构改动必须人工确认)');
162
+ }
163
+ s.status = 'applied';
164
+ s.appliedNote = `已应用(${confirm ? '人工确认' : '成熟自动执行'},样本 ${s.reuseCount}/${MATURITY_MIN_SAMPLES})`;
165
+ this.save(s);
166
+ return `建议 ${suggestionId} 已应用(${s.appliedNote})`;
167
+ }
168
+ /** 忽略建议(人工处置:不再展示) */
169
+ dismiss(suggestionId) {
170
+ const s = this.loadById(suggestionId);
171
+ if (!s)
172
+ throw new Error(`建议不存在: ${suggestionId}`);
173
+ if (s.status === 'applied')
174
+ throw new Error('建议已应用,不能忽略');
175
+ s.status = 'dismissed';
176
+ this.save(s);
177
+ return `建议 ${suggestionId} 已忽略`;
178
+ }
179
+ }
180
+ /**
181
+ * 从一次执行记录提取结构化信号,调用学习管线生成建议。
182
+ * 返回本次学习产生的建议 id 列表(去重后实际新增/累积的)。
183
+ */
184
+ export function learnFromRun(rec, store) {
185
+ const produced = [];
186
+ // 失败步骤才有学习价值(v1:只从失败里学)
187
+ for (const step of rec.steps.filter((s) => s.status === 'failed')) {
188
+ const kind = classifyFailure(step);
189
+ if (!kind)
190
+ continue;
191
+ const message = suggestionMessage(step.stepId, kind, step.error ?? '');
192
+ const id = store.upsert(rec.flowId, kind, step.stepId, message);
193
+ produced.push(id);
194
+ }
195
+ return produced;
196
+ }
197
+ /** 建议内容按类别生成(与 Rust learning.rs 文案一致) */
198
+ function suggestionMessage(stepId, kind, error) {
199
+ switch (kind) {
200
+ case 'extend_verify_timeout':
201
+ return `步骤 ${stepId} 的 wait_selector 验证超时(${error}),建议延长 timeoutMs 或确认选择器在页面中稳定存在`;
202
+ case 'change_selector':
203
+ return `步骤 ${stepId} 的 wait_selector 验证超时(${error}),建议改用页面改版后更稳定的选择器`;
204
+ case 'add_verify':
205
+ return `步骤 ${stepId} 的 extract 返回空内容,建议补充 verify(如 textContains 或 wait_selector),否则执行器无法判定该步成败`;
206
+ case 'change_extract_selector':
207
+ return `步骤 ${stepId} 的 extract 返回空内容(${error}),建议改选择器或检查页面结构`;
208
+ case 'check_input_value':
209
+ return `步骤 ${stepId} 的 text_contains 验证失败(${error}),建议检查输入值/变量是否缺失(掩码后的输入值)`;
210
+ case 'add_pre_wait':
211
+ return `步骤 ${stepId} 的点击未命中(${error}),建议在该步骤前补 wait_selector 预等待目标出现`;
212
+ }
213
+ }
214
+ /**
215
+ * 从失败步骤分类出建议类别(v1 规则)。无法分类的失败返回 undefined —— 不产生建议。
216
+ */
217
+ export function classifyFailure(step) {
218
+ const kind = step.type;
219
+ if (!kind)
220
+ return undefined;
221
+ const err = step.error ?? '';
222
+ if (kind === 'click' && (err.includes('未命中') || err.includes('点击'))) {
223
+ return 'add_pre_wait';
224
+ }
225
+ if ((kind === 'navigate' || kind === 'click' || kind === 'input' || kind === 'wait') && (err.includes('等待选择器') || err.includes('超时'))) {
226
+ return 'extend_verify_timeout';
227
+ }
228
+ if (kind === 'extract' && (err.length === 0 || err.includes('空'))) {
229
+ return 'change_extract_selector';
230
+ }
231
+ if (kind === 'input' && (err.includes('页面文本不含') || err.includes('变量缺失'))) {
232
+ return 'check_input_value';
233
+ }
234
+ return undefined;
235
+ }
236
+ /** 判断是否「成熟」(同改进被复用 ≥ 门槛后自动执行) */
237
+ export function isMature(s) {
238
+ return s.status === 'mature' || s.reuseCount >= MATURITY_MIN_SAMPLES;
239
+ }
240
+ //# sourceMappingURL=learning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning.js","sourceRoot":"","sources":["../src/learning.ts"],"names":[],"mappings":"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"}
package/dist/mask.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 掩码规则(移植自 Rust `export.rs` 的 `Masker`;掩码前移的落盘时刻入口)。
3
+ *
4
+ * 掩码前移(ARCHITECTURE #6 / FLOW-SCHEMA):敏感字段(密码/cookie/登录态/输入值)
5
+ * 在落盘时刻就掩码,本地执行记录与日志即脱敏,不只在上传时。
6
+ * `maskInputValue` 是 executor 落盘 input 步骤 `inputValueMasked` 的入口。
7
+ *
8
+ * 注:Rust 版 Masker 位于 export.rs;此处独立成 mask.ts 供 executor(M4)先落地最小面,
9
+ * M6 的 export.ts(统计通道 / 观察包)复用同一实现并 re-export。
10
+ */
11
+ /** 统计通道:标识符清洗用类型占位符 */
12
+ export declare const PLACEHOLDER_COMPANY = "<company>";
13
+ export declare const PLACEHOLDER_KEYWORD = "<keyword>";
14
+ export declare const PLACEHOLDER_PERSON = "<person>";
15
+ export declare const PLACEHOLDER_JOB = "<job>";
16
+ /**
17
+ * 掩码输入值(密码/cookie/登录态/输入值):长度 ≤4 全掩,否则保留首尾各 1 字符。
18
+ * 按字符边界切([...v] 迭代 Unicode 码点,避免中文等多字节切割错乱)。
19
+ */
20
+ export declare function maskInputValue(v: string): string;
21
+ /** 掩码 URL 中的敏感子串(如带账号的链接);v1 仅掩码已知敏感片段 */
22
+ export declare function maskUrl(url: string): string;
23
+ /** 掩码文本中的关键词(统计通道标识符清洗的补充:文本内出现已知关键词一律替换) */
24
+ export declare function maskKeywordInText(text: string, keyword: string): string;
25
+ /**
26
+ * 掩码文本中的多个关键词(落盘时刻 error 掩码用;掩码前移)。
27
+ * - 长度降序替换,避免短词作为长词子串时先被替换、长词残留
28
+ * - 只替换关键词原文,不动类别短语(「不含」/「超时」/「未命中」等)——
29
+ * 失败类别判定不受影响
30
+ * - 空串/空列表跳过(无关键词上下文时原样返回)
31
+ */
32
+ export declare function maskKeywords(text: string, keywords: string[]): string;
package/dist/mask.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 掩码规则(移植自 Rust `export.rs` 的 `Masker`;掩码前移的落盘时刻入口)。
3
+ *
4
+ * 掩码前移(ARCHITECTURE #6 / FLOW-SCHEMA):敏感字段(密码/cookie/登录态/输入值)
5
+ * 在落盘时刻就掩码,本地执行记录与日志即脱敏,不只在上传时。
6
+ * `maskInputValue` 是 executor 落盘 input 步骤 `inputValueMasked` 的入口。
7
+ *
8
+ * 注:Rust 版 Masker 位于 export.rs;此处独立成 mask.ts 供 executor(M4)先落地最小面,
9
+ * M6 的 export.ts(统计通道 / 观察包)复用同一实现并 re-export。
10
+ */
11
+ /** 统计通道:标识符清洗用类型占位符 */
12
+ export const PLACEHOLDER_COMPANY = '<company>';
13
+ export const PLACEHOLDER_KEYWORD = '<keyword>';
14
+ export const PLACEHOLDER_PERSON = '<person>';
15
+ export const PLACEHOLDER_JOB = '<job>';
16
+ /**
17
+ * 掩码输入值(密码/cookie/登录态/输入值):长度 ≤4 全掩,否则保留首尾各 1 字符。
18
+ * 按字符边界切([...v] 迭代 Unicode 码点,避免中文等多字节切割错乱)。
19
+ */
20
+ export function maskInputValue(v) {
21
+ const chars = [...v];
22
+ if (chars.length <= 4)
23
+ return '****';
24
+ return `${chars[0]}****${chars[chars.length - 1]}`;
25
+ }
26
+ /** 掩码 URL 中的敏感子串(如带账号的链接);v1 仅掩码已知敏感片段 */
27
+ export function maskUrl(url) {
28
+ let out = url;
29
+ for (const [k, v] of [
30
+ ['password=', '<pwd>'],
31
+ ['passwd=', '<pwd>'],
32
+ ['token=', '<token>'],
33
+ ]) {
34
+ const pos = out.indexOf(k);
35
+ if (pos !== -1) {
36
+ const rest = out.slice(pos + k.length);
37
+ const end = /[&#]/.exec(rest);
38
+ const stop = end ? pos + k.length + end.index : out.length;
39
+ out = out.slice(0, pos + k.length) + v + out.slice(stop);
40
+ }
41
+ }
42
+ return out;
43
+ }
44
+ /** 掩码文本中的关键词(统计通道标识符清洗的补充:文本内出现已知关键词一律替换) */
45
+ export function maskKeywordInText(text, keyword) {
46
+ if (keyword.length === 0)
47
+ return text;
48
+ return text.split(keyword).join(PLACEHOLDER_KEYWORD);
49
+ }
50
+ /**
51
+ * 掩码文本中的多个关键词(落盘时刻 error 掩码用;掩码前移)。
52
+ * - 长度降序替换,避免短词作为长词子串时先被替换、长词残留
53
+ * - 只替换关键词原文,不动类别短语(「不含」/「超时」/「未命中」等)——
54
+ * 失败类别判定不受影响
55
+ * - 空串/空列表跳过(无关键词上下文时原样返回)
56
+ */
57
+ export function maskKeywords(text, keywords) {
58
+ if (keywords.length === 0)
59
+ return text;
60
+ const ks = keywords.filter((k) => k.length > 0);
61
+ if (ks.length === 0)
62
+ return text;
63
+ ks.sort((a, b) => b.length - a.length);
64
+ let out = text;
65
+ for (const k of ks) {
66
+ out = out.split(k).join(PLACEHOLDER_KEYWORD);
67
+ }
68
+ return out;
69
+ }
70
+ //# sourceMappingURL=mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask.js","sourceRoot":"","sources":["../src/mask.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,uBAAuB;AACvB,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAA;AAC9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAA;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAC5C,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA;AAEtC;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IACpB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAA;IACpC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,EAAE,CAAA;AACrD,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,IAAI,GAAG,GAAG,GAAG,CAAA;IACb,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI;QACnB,CAAC,WAAW,EAAE,OAAO,CAAC;QACtB,CAAC,SAAS,EAAE,OAAO,CAAC;QACpB,CAAC,QAAQ,EAAE,SAAS,CAAC;KACb,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC1B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;YACtC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAA;YAC1D,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAe;IAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;AACtD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,QAAkB;IAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACtC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC/C,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAChC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;IACtC,IAAI,GAAG,GAAG,IAAI,CAAA;IACd,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACnB,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * md-forge 平台客户端(移植自 Rust `platform.rs`;MIGRATION-NODE 七 / EXECUTION-SPEC 七)。
3
+ *
4
+ * 协议(md-forge M3 端点,契约已确认):
5
+ * GET /api/packages/<name> → { ok:true, data: {name,type,version,hash,files[]} }
6
+ * GET /api/packages/<name>/files/<f> → 包文件(Bearer 令牌)
7
+ *
8
+ * 包 hash 口径与 md-forge `hashFiles` 一致:对 files 按键排序后逐文件
9
+ * `name\0content` 做 SHA-256 hex(跨实现可校验)。
10
+ *
11
+ * 流程包(type=flow):校验通过后自动替换本地 flows/<name>/(写 flow.json + 其余文件 +
12
+ * 本地 manifest.json);软件包(type=software):不自动替换,提示用户(v1 只做流程包自动部署)。
13
+ * 哈希校验失败 → 不落盘(返回错误)。
14
+ */
15
+ /** 包 manifest(与 md-forge 通用包模型一致) */
16
+ export interface PkgManifest {
17
+ name: string;
18
+ type: string;
19
+ version: string;
20
+ hash: string;
21
+ files: string[];
22
+ signature?: string;
23
+ }
24
+ /** 平台客户端(无状态;每次 pkg_update 独立请求) */
25
+ export declare class PlatformClient {
26
+ readonly baseUrl: string;
27
+ readonly token: string;
28
+ constructor(baseUrl: string, token: string);
29
+ /** 拉取包 manifest(GET /api/packages/<name>,Bearer 认证) */
30
+ fetchManifest(name: string): Promise<PkgManifest>;
31
+ /** 下载包内单文件(GET /api/packages/<name>/files/<f>,Bearer 认证) */
32
+ fetchFile(name: string, file: string): Promise<Uint8Array>;
33
+ /** 下载整个包(按 manifest.files),返回 {文件名: 内容}(按键排序) */
34
+ downloadPackage(manifest: PkgManifest): Promise<Map<string, Uint8Array>>;
35
+ /** 计算包 hash(与 md-forge hashFiles 同口径:files 按键排序,name\0content,SHA-256 hex) */
36
+ static computeHash(files: Map<string, Uint8Array> | Record<string, Uint8Array>): string;
37
+ /**
38
+ * M10 D-O1:上传观察包目录到平台(POST {base}/api/observations,multipart)。
39
+ * bundleDir 为 outbox/<flow_id>_v<version>_<run_id>/;身份前缀 = <flow_id>_v<version>_<run_id>。
40
+ * 返回 { status: 'accepted' | 'duplicate', dir };网络/4xx/5xx 抛错(由调用方记台账 Failed)。
41
+ */
42
+ uploadObservation(bundleDir: string): Promise<{
43
+ status: string;
44
+ dir: string;
45
+ }>;
46
+ }
47
+ /** 哈希校验:下载的包文件重新计算 hash,与 manifest.hash 比对 */
48
+ export declare function verifyHash(manifest: PkgManifest, files: Map<string, Uint8Array>): void;
49
+ /** 本地 flows/ 目录中某 flow 的当前版本(读 flow.json 的 version 字段;不存在返回 undefined) */
50
+ export declare function localFlowVersion(flowsDir: string, name: string): number | undefined;
51
+ /** 流程包更新结果 */
52
+ export interface PkgUpdateResult {
53
+ name: string;
54
+ /** up_to_date / updated / newer / not_a_flow / error */
55
+ status: string;
56
+ localVersion?: number;
57
+ platformVersion?: number;
58
+ hash: string;
59
+ dir?: string;
60
+ message?: string;
61
+ }
62
+ /**
63
+ * 执行 pkg_update:拉 manifest → 比版本 → 有更新下载 → 哈希校验 → 替换 flows/。
64
+ * 校验失败抛错(不落盘)。
65
+ */
66
+ export declare function pkgUpdate(client: PlatformClient, flowsDir: string, name: string): Promise<PkgUpdateResult>;