@max-null/dsh-memory 0.14.0 → 0.15.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/engine.d.ts +0 -64
- package/dist/engine.js +4 -51
- package/dist/schema.d.ts +88 -0
- package/dist/schema.js +69 -0
- package/package.json +1 -1
- package/scripts/check-memory-files.mjs +9 -4
package/dist/engine.d.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* is always created `suggested` and becomes effective only through `setStatus`.
|
|
6
6
|
*/
|
|
7
7
|
import { Service } from '@deepseek-ai/cordis';
|
|
8
|
-
import { z } from 'zod';
|
|
9
8
|
import { type PromptFile } from './prompt-files.ts';
|
|
10
9
|
import type { AnchorProbes, MemoryAnchor } from './anchors.ts';
|
|
11
10
|
import type { MemorySource, SourceStat } from './query-log.ts';
|
|
@@ -281,69 +280,6 @@ declare module '@deepseek-ai/cordis' {
|
|
|
281
280
|
'memory/changed'(change: MemoryChange): void;
|
|
282
281
|
}
|
|
283
282
|
}
|
|
284
|
-
/**
|
|
285
|
-
* 存储块的 schema(门三的判据来源,2026-09-25)。
|
|
286
|
-
*
|
|
287
|
-
* **导出是为了让离线扫描器(`scripts/check-memory-files.mjs`)用它而不是自己再写一套**——
|
|
288
|
-
* 两份判据会漂移,而漂移的方向恰好是「扫描器说没事、打开时炸」这种最难查的形态。
|
|
289
|
-
*/
|
|
290
|
-
export declare const blockSchema: z.ZodObject<{
|
|
291
|
-
namespace: z.ZodEnum<{
|
|
292
|
-
global: "global";
|
|
293
|
-
project: "project";
|
|
294
|
-
}>;
|
|
295
|
-
status: z.ZodEnum<{
|
|
296
|
-
suggested: "suggested";
|
|
297
|
-
approved: "approved";
|
|
298
|
-
auto: "auto";
|
|
299
|
-
suggest: "suggest";
|
|
300
|
-
}>;
|
|
301
|
-
injected: z.ZodOptional<z.ZodBoolean>;
|
|
302
|
-
quarantined: z.ZodOptional<z.ZodBoolean>;
|
|
303
|
-
quarantineReason: z.ZodOptional<z.ZodString>;
|
|
304
|
-
hitCount: z.ZodOptional<z.ZodNumber>;
|
|
305
|
-
source: z.ZodOptional<z.ZodEnum<{
|
|
306
|
-
agent: "agent";
|
|
307
|
-
human: "human";
|
|
308
|
-
}>>;
|
|
309
|
-
injectedAuto: z.ZodOptional<z.ZodBoolean>;
|
|
310
|
-
anchor: z.ZodOptional<z.ZodObject<{
|
|
311
|
-
kind: z.ZodEnum<{
|
|
312
|
-
env: "env";
|
|
313
|
-
"tool-list": "tool-list";
|
|
314
|
-
"self-version": "self-version";
|
|
315
|
-
"path-exists": "path-exists";
|
|
316
|
-
}>;
|
|
317
|
-
name: z.ZodOptional<z.ZodString>;
|
|
318
|
-
value: z.ZodString;
|
|
319
|
-
}, z.core.$strip>>;
|
|
320
|
-
stale: z.ZodOptional<z.ZodBoolean>;
|
|
321
|
-
staleReason: z.ZodOptional<z.ZodString>;
|
|
322
|
-
content: z.ZodString;
|
|
323
|
-
keywords: z.ZodArray<z.ZodString>;
|
|
324
|
-
createdAt: z.ZodNumber;
|
|
325
|
-
updatedAt: z.ZodNumber;
|
|
326
|
-
lastUsedAt: z.ZodOptional<z.ZodNumber>;
|
|
327
|
-
vector: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
328
|
-
kind: z.ZodOptional<z.ZodEnum<{
|
|
329
|
-
fact: "fact";
|
|
330
|
-
prompt: "prompt";
|
|
331
|
-
}>>;
|
|
332
|
-
meta: z.ZodOptional<z.ZodObject<{
|
|
333
|
-
seq: z.ZodOptional<z.ZodNumber>;
|
|
334
|
-
name: z.ZodString;
|
|
335
|
-
dimension: z.ZodOptional<z.ZodString>;
|
|
336
|
-
difficulty: z.ZodOptional<z.ZodString>;
|
|
337
|
-
tags: z.ZodArray<z.ZodString>;
|
|
338
|
-
summary: z.ZodString;
|
|
339
|
-
path: z.ZodString;
|
|
340
|
-
mtime: z.ZodNumber;
|
|
341
|
-
source: z.ZodEnum<{
|
|
342
|
-
user: "user";
|
|
343
|
-
agent: "agent";
|
|
344
|
-
}>;
|
|
345
|
-
}, z.core.$strip>>;
|
|
346
|
-
}, z.core.$strip>;
|
|
347
283
|
/**
|
|
348
284
|
* 嵌入能力(0.5.2 混合检索):缺省不配置 = 纯 BM25(行为不变);
|
|
349
285
|
* 配置后 memory_search 以 BM25 与语义结果做 RRF 融合,向量持久化到
|
package/dist/engine.js
CHANGED
|
@@ -10,14 +10,14 @@ import { readdir } from 'node:fs/promises';
|
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
11
|
import { dirname, join } from 'node:path';
|
|
12
12
|
import { Service } from '@deepseek-ai/cordis';
|
|
13
|
-
import { z } from 'zod';
|
|
14
13
|
import { defineDomain, domainTable, DomainFacility } from '@deepseek-ai/dsh-storage-domain';
|
|
15
14
|
import { JsonStorageBackend } from '@deepseek-ai/dsh-storage-json';
|
|
16
15
|
import { bm25FieldScores, cosineSimilarity, rrfFuse } from "./bm25.js";
|
|
17
16
|
import { parsePromptFile, scanPromptDir, writePromptFile } from "./prompt-files.js";
|
|
18
17
|
import { detectSensitive } from "./quarantine.js";
|
|
19
18
|
import { SELF_VERSION } from "./self.js";
|
|
20
|
-
import {
|
|
19
|
+
import { anchorHolds } from "./anchors.js";
|
|
20
|
+
import { blockSchema, describeIssues } from "./schema.js";
|
|
21
21
|
import { DEFAULT_INJECTION_BUDGET, DEFAULT_SUMMARY_CHARS, deriveSummary, renderInjection, } from "./injection.js";
|
|
22
22
|
import { QueryLog, rankGapCandidates, summarizeQueryLog, summarizeSources } from "./query-log.js";
|
|
23
23
|
/** Brand a string as a {@link MemoryId} (compile-time only). */
|
|
@@ -76,56 +76,9 @@ export function isAutoDemoteCandidate(record, threshold) {
|
|
|
76
76
|
return (record.lastUsedAt ?? record.updatedAt) <= threshold;
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
|
-
* 存储块的 schema
|
|
80
|
-
*
|
|
81
|
-
* **导出是为了让离线扫描器(`scripts/check-memory-files.mjs`)用它而不是自己再写一套**——
|
|
82
|
-
* 两份判据会漂移,而漂移的方向恰好是「扫描器说没事、打开时炸」这种最难查的形态。
|
|
79
|
+
* 存储块的 schema 与 zod issue 的单行格式化,都在 {@link ./schema.ts}——单独成模块是为了
|
|
80
|
+
* 让离线扫描器能零 peer 依赖地加载它(见该文件头部)。
|
|
83
81
|
*/
|
|
84
|
-
export const blockSchema = z.object({
|
|
85
|
-
namespace: z.enum(['global', 'project']),
|
|
86
|
-
status: z.enum(['suggested', 'approved', 'auto', 'suggest']),
|
|
87
|
-
injected: z.boolean().optional(),
|
|
88
|
-
quarantined: z.boolean().optional(),
|
|
89
|
-
quarantineReason: z.string().optional(),
|
|
90
|
-
hitCount: z.number().optional(),
|
|
91
|
-
source: z.enum(['agent', 'human']).optional(),
|
|
92
|
-
injectedAuto: z.boolean().optional(),
|
|
93
|
-
anchor: z.object({
|
|
94
|
-
kind: z.enum(ANCHOR_KINDS),
|
|
95
|
-
name: z.string().optional(),
|
|
96
|
-
value: z.string(),
|
|
97
|
-
}).optional(),
|
|
98
|
-
stale: z.boolean().optional(),
|
|
99
|
-
staleReason: z.string().optional(),
|
|
100
|
-
content: z.string(),
|
|
101
|
-
keywords: z.array(z.string()),
|
|
102
|
-
createdAt: z.number(),
|
|
103
|
-
updatedAt: z.number(),
|
|
104
|
-
lastUsedAt: z.number().optional(),
|
|
105
|
-
vector: z.array(z.number()).optional(),
|
|
106
|
-
kind: z.enum(['fact', 'prompt']).optional(),
|
|
107
|
-
meta: z.object({
|
|
108
|
-
seq: z.number().optional(),
|
|
109
|
-
name: z.string(),
|
|
110
|
-
dimension: z.string().optional(),
|
|
111
|
-
difficulty: z.string().optional(),
|
|
112
|
-
tags: z.array(z.string()),
|
|
113
|
-
summary: z.string(),
|
|
114
|
-
path: z.string(),
|
|
115
|
-
mtime: z.number(),
|
|
116
|
-
source: z.enum(['user', 'agent']),
|
|
117
|
-
}).optional(),
|
|
118
|
-
});
|
|
119
|
-
/**
|
|
120
|
-
* 把 zod 的 issue 压成一行(门一,2026-09-25)。
|
|
121
|
-
*
|
|
122
|
-
* 排查成本的大头在「从 domain 打不开倒推」——`dsh-storage-domain` 封装后的错误只给到
|
|
123
|
-
* 「哪个 domain 的哪条记录不合规」(`stored record '<id>' in table 'blocks' does not
|
|
124
|
-
* match its schema`),**说不出是哪个字段、什么值、期望什么**。这行补的就是那段。
|
|
125
|
-
*/
|
|
126
|
-
function describeIssues(error) {
|
|
127
|
-
return error.issues.map(issue => `${issue.path.join('.') || '(root)'}: ${issue.message}`).join('; ');
|
|
128
|
-
}
|
|
129
82
|
/** Shared table shape; the two domains differ only by name and backend route. */
|
|
130
83
|
function memorySpec(name) {
|
|
131
84
|
return defineDomain({
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 存储块的 schema —— **零 peer 依赖地可独立加载**。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成模块:离线扫描器(`scripts/check-memory-files.mjs`)要 import 编译产物来跑
|
|
5
|
+
* 存量校验,而 `engine.js` 在运行时 import 了 `@deepseek-ai/cordis`、
|
|
6
|
+
* `@deepseek-ai/dsh-storage-domain`、`@deepseek-ai/dsh-storage-json`——那三个都是**由宿主
|
|
7
|
+
* 提供的 peer**,装版实体里没有。2026-09-29 实测:在 profile 里直接跑扫描器会报
|
|
8
|
+
* `Cannot find package '@deepseek-ai/cordis' imported from dist/engine.js`,于是
|
|
9
|
+
* 「用插件自己的 schema 扫存量」这条设计**在用户环境里根本跑不起来**。
|
|
10
|
+
*
|
|
11
|
+
* 本模块只依赖 `zod`(真 `dependencies`,装版有)与 `./anchors.ts`(只有 `node:fs` /
|
|
12
|
+
* `node:path`),所以 `dist/schema.js` 能被独立加载——**判据仍然只有一份**,不另写。
|
|
13
|
+
*
|
|
14
|
+
* @see docs/设计/2026-09-25-写入兜底与失败可见化-设计方案.md §四.3
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
/**
|
|
18
|
+
* 存储块的 schema(门一与门三共用的判据来源,2026-09-25)。
|
|
19
|
+
*
|
|
20
|
+
* 门一(写入端)在 `putBlock` 里用它拒绝不合规的块;门三(存量体检)在扫描器里用它逐条
|
|
21
|
+
* 校验已有文件——**同一份定义**,因为两份判据会漂移,而漂移的方向恰好是「扫描器说没事、
|
|
22
|
+
* 打开时炸」这种最难查的形态。
|
|
23
|
+
*/
|
|
24
|
+
export declare const blockSchema: z.ZodObject<{
|
|
25
|
+
namespace: z.ZodEnum<{
|
|
26
|
+
global: "global";
|
|
27
|
+
project: "project";
|
|
28
|
+
}>;
|
|
29
|
+
status: z.ZodEnum<{
|
|
30
|
+
suggested: "suggested";
|
|
31
|
+
approved: "approved";
|
|
32
|
+
auto: "auto";
|
|
33
|
+
suggest: "suggest";
|
|
34
|
+
}>;
|
|
35
|
+
injected: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
+
quarantined: z.ZodOptional<z.ZodBoolean>;
|
|
37
|
+
quarantineReason: z.ZodOptional<z.ZodString>;
|
|
38
|
+
hitCount: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
source: z.ZodOptional<z.ZodEnum<{
|
|
40
|
+
agent: "agent";
|
|
41
|
+
human: "human";
|
|
42
|
+
}>>;
|
|
43
|
+
injectedAuto: z.ZodOptional<z.ZodBoolean>;
|
|
44
|
+
anchor: z.ZodOptional<z.ZodObject<{
|
|
45
|
+
kind: z.ZodEnum<{
|
|
46
|
+
env: "env";
|
|
47
|
+
"tool-list": "tool-list";
|
|
48
|
+
"self-version": "self-version";
|
|
49
|
+
"path-exists": "path-exists";
|
|
50
|
+
}>;
|
|
51
|
+
name: z.ZodOptional<z.ZodString>;
|
|
52
|
+
value: z.ZodString;
|
|
53
|
+
}, z.core.$strip>>;
|
|
54
|
+
stale: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
+
staleReason: z.ZodOptional<z.ZodString>;
|
|
56
|
+
content: z.ZodString;
|
|
57
|
+
keywords: z.ZodArray<z.ZodString>;
|
|
58
|
+
createdAt: z.ZodNumber;
|
|
59
|
+
updatedAt: z.ZodNumber;
|
|
60
|
+
lastUsedAt: z.ZodOptional<z.ZodNumber>;
|
|
61
|
+
vector: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
62
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
63
|
+
fact: "fact";
|
|
64
|
+
prompt: "prompt";
|
|
65
|
+
}>>;
|
|
66
|
+
meta: z.ZodOptional<z.ZodObject<{
|
|
67
|
+
seq: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
name: z.ZodString;
|
|
69
|
+
dimension: z.ZodOptional<z.ZodString>;
|
|
70
|
+
difficulty: z.ZodOptional<z.ZodString>;
|
|
71
|
+
tags: z.ZodArray<z.ZodString>;
|
|
72
|
+
summary: z.ZodString;
|
|
73
|
+
path: z.ZodString;
|
|
74
|
+
mtime: z.ZodNumber;
|
|
75
|
+
source: z.ZodEnum<{
|
|
76
|
+
user: "user";
|
|
77
|
+
agent: "agent";
|
|
78
|
+
}>;
|
|
79
|
+
}, z.core.$strip>>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
/**
|
|
82
|
+
* 把 zod 的 issue 压成一行(门一,2026-09-25)。
|
|
83
|
+
*
|
|
84
|
+
* 排查成本的大头在「从 domain 打不开倒推」——`dsh-storage-domain` 封装后的错误只给到
|
|
85
|
+
* 「哪个 domain 的哪条记录不合规」(`stored record '<id>' in table 'blocks' does not
|
|
86
|
+
* match its schema`),**说不出是哪个字段、什么值、期望什么**。这行补的就是那段。
|
|
87
|
+
*/
|
|
88
|
+
export declare function describeIssues(error: z.ZodError): string;
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 存储块的 schema —— **零 peer 依赖地可独立加载**。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独成模块:离线扫描器(`scripts/check-memory-files.mjs`)要 import 编译产物来跑
|
|
5
|
+
* 存量校验,而 `engine.js` 在运行时 import 了 `@deepseek-ai/cordis`、
|
|
6
|
+
* `@deepseek-ai/dsh-storage-domain`、`@deepseek-ai/dsh-storage-json`——那三个都是**由宿主
|
|
7
|
+
* 提供的 peer**,装版实体里没有。2026-09-29 实测:在 profile 里直接跑扫描器会报
|
|
8
|
+
* `Cannot find package '@deepseek-ai/cordis' imported from dist/engine.js`,于是
|
|
9
|
+
* 「用插件自己的 schema 扫存量」这条设计**在用户环境里根本跑不起来**。
|
|
10
|
+
*
|
|
11
|
+
* 本模块只依赖 `zod`(真 `dependencies`,装版有)与 `./anchors.ts`(只有 `node:fs` /
|
|
12
|
+
* `node:path`),所以 `dist/schema.js` 能被独立加载——**判据仍然只有一份**,不另写。
|
|
13
|
+
*
|
|
14
|
+
* @see docs/设计/2026-09-25-写入兜底与失败可见化-设计方案.md §四.3
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { ANCHOR_KINDS } from "./anchors.js";
|
|
18
|
+
/**
|
|
19
|
+
* 存储块的 schema(门一与门三共用的判据来源,2026-09-25)。
|
|
20
|
+
*
|
|
21
|
+
* 门一(写入端)在 `putBlock` 里用它拒绝不合规的块;门三(存量体检)在扫描器里用它逐条
|
|
22
|
+
* 校验已有文件——**同一份定义**,因为两份判据会漂移,而漂移的方向恰好是「扫描器说没事、
|
|
23
|
+
* 打开时炸」这种最难查的形态。
|
|
24
|
+
*/
|
|
25
|
+
export const blockSchema = z.object({
|
|
26
|
+
namespace: z.enum(['global', 'project']),
|
|
27
|
+
status: z.enum(['suggested', 'approved', 'auto', 'suggest']),
|
|
28
|
+
injected: z.boolean().optional(),
|
|
29
|
+
quarantined: z.boolean().optional(),
|
|
30
|
+
quarantineReason: z.string().optional(),
|
|
31
|
+
hitCount: z.number().optional(),
|
|
32
|
+
source: z.enum(['agent', 'human']).optional(),
|
|
33
|
+
injectedAuto: z.boolean().optional(),
|
|
34
|
+
anchor: z.object({
|
|
35
|
+
kind: z.enum(ANCHOR_KINDS),
|
|
36
|
+
name: z.string().optional(),
|
|
37
|
+
value: z.string(),
|
|
38
|
+
}).optional(),
|
|
39
|
+
stale: z.boolean().optional(),
|
|
40
|
+
staleReason: z.string().optional(),
|
|
41
|
+
content: z.string(),
|
|
42
|
+
keywords: z.array(z.string()),
|
|
43
|
+
createdAt: z.number(),
|
|
44
|
+
updatedAt: z.number(),
|
|
45
|
+
lastUsedAt: z.number().optional(),
|
|
46
|
+
vector: z.array(z.number()).optional(),
|
|
47
|
+
kind: z.enum(['fact', 'prompt']).optional(),
|
|
48
|
+
meta: z.object({
|
|
49
|
+
seq: z.number().optional(),
|
|
50
|
+
name: z.string(),
|
|
51
|
+
dimension: z.string().optional(),
|
|
52
|
+
difficulty: z.string().optional(),
|
|
53
|
+
tags: z.array(z.string()),
|
|
54
|
+
summary: z.string(),
|
|
55
|
+
path: z.string(),
|
|
56
|
+
mtime: z.number(),
|
|
57
|
+
source: z.enum(['user', 'agent']),
|
|
58
|
+
}).optional(),
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* 把 zod 的 issue 压成一行(门一,2026-09-25)。
|
|
62
|
+
*
|
|
63
|
+
* 排查成本的大头在「从 domain 打不开倒推」——`dsh-storage-domain` 封装后的错误只给到
|
|
64
|
+
* 「哪个 domain 的哪条记录不合规」(`stored record '<id>' in table 'blocks' does not
|
|
65
|
+
* match its schema`),**说不出是哪个字段、什么值、期望什么**。这行补的就是那段。
|
|
66
|
+
*/
|
|
67
|
+
export function describeIssues(error) {
|
|
68
|
+
return error.issues.map(issue => `${issue.path.join('.') || '(root)'}: ${issue.message}`).join('; ');
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Cross-session plaintext memory plugin for the DeepSeek Harness — deterministic BM25 recall, writes take effect immediately, signal-driven injection, no embeddings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
* 的记忆静默消失」。2026-09-25 那次 154 条 project 记忆全部不可见就是这么来的:写入放行、
|
|
8
8
|
* 加载才炸、失败还被吞。
|
|
9
9
|
*
|
|
10
|
-
* **判据怎么算**:用插件自己的 `blockSchema`(从 `dist/
|
|
10
|
+
* **判据怎么算**:用插件自己的 `blockSchema`(从 `dist/schema.js` import)逐条 `safeParse`。
|
|
11
11
|
* 同源是刻意的——另写一套判定迟早漂移成「扫描器说没事、打开时炸」这种最难查的形态。
|
|
12
12
|
*
|
|
13
|
+
* 为什么是 `dist/schema.js` 而不是 `dist/engine.js`:后者在运行时 import 了 `cordis` /
|
|
14
|
+
* `dsh-storage-domain` / `dsh-storage-json`,那三个是**宿主提供的 peer**,装版实体里没有
|
|
15
|
+
* ——直接加载会报 `Cannot find package '@deepseek-ai/cordis'`(2026-09-29 实测)。
|
|
16
|
+
* `schema.js` 只依赖 `zod`(真 dependencies)与 `anchors.js`(只有 node 内置),能独立加载。
|
|
17
|
+
*
|
|
13
18
|
* **哪些情况答不了**:
|
|
14
19
|
* - 只校验 `tables.blocks` 里的条目,不校验外层信封结构;
|
|
15
20
|
* - 不处理压缩帧(那是 `unzstd-frames.mjs` 的事);
|
|
@@ -37,13 +42,13 @@ function fail(message) {
|
|
|
37
42
|
|
|
38
43
|
let blockSchema
|
|
39
44
|
try {
|
|
40
|
-
({ blockSchema } = await import('../dist/
|
|
45
|
+
({ blockSchema } = await import('../dist/schema.js'))
|
|
41
46
|
} catch (error) {
|
|
42
47
|
fail(
|
|
43
|
-
`无法加载 dist/
|
|
48
|
+
`无法加载 dist/schema.js(先跑 npm run build):${error instanceof Error ? error.message : String(error)}`,
|
|
44
49
|
)
|
|
45
50
|
}
|
|
46
|
-
if (blockSchema === undefined) fail('dist/
|
|
51
|
+
if (blockSchema === undefined) fail('dist/schema.js 没有导出 blockSchema——构建产物太旧?')
|
|
47
52
|
|
|
48
53
|
/** 递归收集目录下的 *.json(跳过 node_modules)。 */
|
|
49
54
|
function collect(target, out = []) {
|