codeksei 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.
- package/LICENSE +661 -0
- package/README.en.md +215 -0
- package/README.md +259 -0
- package/bin/codeksei.js +10 -0
- package/bin/cyberboss.js +11 -0
- package/package.json +86 -0
- package/scripts/install-background-tasks.ps1 +135 -0
- package/scripts/open_shared_wechat_thread.sh +94 -0
- package/scripts/open_wechat_thread.sh +117 -0
- package/scripts/shared-common.js +791 -0
- package/scripts/shared-open.js +46 -0
- package/scripts/shared-start.js +41 -0
- package/scripts/shared-status.js +74 -0
- package/scripts/shared-supervisor.js +141 -0
- package/scripts/shared-task-runner.ps1 +87 -0
- package/scripts/shared-watchdog.js +290 -0
- package/scripts/show_shared_status.sh +53 -0
- package/scripts/start_shared_app_server.sh +65 -0
- package/scripts/start_shared_wechat.sh +108 -0
- package/scripts/timeline-screenshot.sh +15 -0
- package/scripts/uninstall-background-tasks.ps1 +23 -0
- package/src/adapters/channel/weixin/account-store.js +135 -0
- package/src/adapters/channel/weixin/api-v2.js +258 -0
- package/src/adapters/channel/weixin/api.js +180 -0
- package/src/adapters/channel/weixin/context-token-store.js +84 -0
- package/src/adapters/channel/weixin/index.js +605 -0
- package/src/adapters/channel/weixin/legacy.js +567 -0
- package/src/adapters/channel/weixin/login-common.js +63 -0
- package/src/adapters/channel/weixin/login-legacy.js +124 -0
- package/src/adapters/channel/weixin/login-v2.js +186 -0
- package/src/adapters/channel/weixin/media-mime.js +22 -0
- package/src/adapters/channel/weixin/media-receive.js +370 -0
- package/src/adapters/channel/weixin/media-send.js +331 -0
- package/src/adapters/channel/weixin/message-utils-v2.js +282 -0
- package/src/adapters/channel/weixin/message-utils.js +199 -0
- package/src/adapters/channel/weixin/protocol.js +77 -0
- package/src/adapters/channel/weixin/redact.js +41 -0
- package/src/adapters/channel/weixin/reminder-queue-store.js +101 -0
- package/src/adapters/channel/weixin/sync-buffer-store.js +35 -0
- package/src/adapters/runtime/codex/events.js +252 -0
- package/src/adapters/runtime/codex/index.js +502 -0
- package/src/adapters/runtime/codex/message-utils.js +141 -0
- package/src/adapters/runtime/codex/model-catalog.js +106 -0
- package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -0
- package/src/adapters/runtime/codex/rpc-client.js +443 -0
- package/src/adapters/runtime/codex/session-store.js +376 -0
- package/src/app/channel-send-file-cli.js +57 -0
- package/src/app/diary-write-cli.js +620 -0
- package/src/app/note-auto-cli.js +201 -0
- package/src/app/note-sync-cli.js +130 -0
- package/src/app/project-radar-cli.js +165 -0
- package/src/app/reminder-write-cli.js +210 -0
- package/src/app/review-cli.js +134 -0
- package/src/app/system-checkin-poller.js +100 -0
- package/src/app/system-send-cli.js +129 -0
- package/src/app/timeline-event-cli.js +273 -0
- package/src/app/timeline-screenshot-cli.js +109 -0
- package/src/core/app.js +1810 -0
- package/src/core/branding.js +167 -0
- package/src/core/command-registry.js +609 -0
- package/src/core/config.js +84 -0
- package/src/core/default-targets.js +163 -0
- package/src/core/durable-note-schema.js +325 -0
- package/src/core/instructions-template.js +31 -0
- package/src/core/note-sync.js +433 -0
- package/src/core/project-radar.js +402 -0
- package/src/core/review-semantic.js +524 -0
- package/src/core/review.js +1081 -0
- package/src/core/shared-bridge-heartbeat.js +140 -0
- package/src/core/stream-delivery.js +990 -0
- package/src/core/system-message-dispatcher.js +68 -0
- package/src/core/system-message-queue-store.js +128 -0
- package/src/core/thread-state-store.js +135 -0
- package/src/core/timeline-screenshot-queue-store.js +134 -0
- package/src/core/workspace-alias.js +163 -0
- package/src/core/workspace-bootstrap.js +338 -0
- package/src/index.js +270 -0
- package/src/integrations/timeline/index.js +191 -0
- package/templates/weixin-instructions.md +53 -0
- package/templates/weixin-operations.md +69 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const {
|
|
4
|
+
PRIMARY_NOTE_SYNC_MARKER_PREFIX,
|
|
5
|
+
LEGACY_NOTE_SYNC_MARKER_PREFIX,
|
|
6
|
+
} = require("./branding");
|
|
7
|
+
|
|
8
|
+
const { listTrackedProjects } = require("./project-radar");
|
|
9
|
+
|
|
10
|
+
const SLOT_MARKER_PREFIX = PRIMARY_NOTE_SYNC_MARKER_PREFIX;
|
|
11
|
+
const SLOT_MARKER_PREFIXES = [PRIMARY_NOTE_SYNC_MARKER_PREFIX, LEGACY_NOTE_SYNC_MARKER_PREFIX];
|
|
12
|
+
|
|
13
|
+
function resolveNoteSyncTarget(config = {}, options = {}) {
|
|
14
|
+
const normalizedProject = normalizeText(options.project);
|
|
15
|
+
const normalizedPath = normalizeText(options.path);
|
|
16
|
+
if (normalizedProject && normalizedPath) {
|
|
17
|
+
throw new Error("--project 和 --path 只能二选一");
|
|
18
|
+
}
|
|
19
|
+
if (!normalizedProject && !normalizedPath) {
|
|
20
|
+
throw new Error("缺少目标 note,传 --project <slug> 或 --path <path>");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (normalizedProject) {
|
|
24
|
+
const trackedProjects = listTrackedProjects(config);
|
|
25
|
+
const matched = trackedProjects.find((project) => matchesProjectSelector(project, normalizedProject));
|
|
26
|
+
if (!matched) {
|
|
27
|
+
const available = trackedProjects.map((project) => project.slug).join(", ");
|
|
28
|
+
throw new Error(`找不到代码项目: ${normalizedProject};当前可用 slug: ${available}`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
kind: "project",
|
|
32
|
+
label: matched.slug,
|
|
33
|
+
filePath: matched.notePath,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
kind: "path",
|
|
39
|
+
label: normalizedPath,
|
|
40
|
+
filePath: resolveNotePath(config.workspaceRoot, normalizedPath),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveNotePath(workspaceRoot, targetPath) {
|
|
45
|
+
if (path.isAbsolute(targetPath)) {
|
|
46
|
+
return normalizeDisplayPath(path.resolve(targetPath));
|
|
47
|
+
}
|
|
48
|
+
const baseRoot = normalizeText(workspaceRoot) || process.cwd();
|
|
49
|
+
return normalizeDisplayPath(path.resolve(baseRoot, targetPath));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function syncNoteFile(options = {}) {
|
|
53
|
+
const filePath = normalizeText(options.filePath);
|
|
54
|
+
if (!filePath) {
|
|
55
|
+
throw new Error("note filePath 不能为空");
|
|
56
|
+
}
|
|
57
|
+
if (!fs.existsSync(filePath)) {
|
|
58
|
+
throw new Error(`note 文件不存在: ${filePath}`);
|
|
59
|
+
}
|
|
60
|
+
const stat = fs.statSync(filePath);
|
|
61
|
+
if (!stat.isFile()) {
|
|
62
|
+
throw new Error(`只能同步文件,不能同步目录: ${filePath}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const current = fs.readFileSync(filePath, "utf8");
|
|
66
|
+
const result = syncNoteContent(current, options);
|
|
67
|
+
if (result.changed) {
|
|
68
|
+
fs.writeFileSync(filePath, result.content, "utf8");
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
filePath: normalizeDisplayPath(filePath),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function syncNoteContent(content, options = {}) {
|
|
77
|
+
const section = normalizeText(options.section);
|
|
78
|
+
if (!section) {
|
|
79
|
+
throw new Error("缺少 --section");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const style = normalizeStyle(options.style);
|
|
83
|
+
const text = style === "bullet"
|
|
84
|
+
? normalizeBulletText(options.text)
|
|
85
|
+
: normalizeParagraphText(options.text);
|
|
86
|
+
if (!text) {
|
|
87
|
+
throw new Error("note 内容不能为空,传 --text 或通过 stdin 输入");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let working = normalizeFileEnding(content);
|
|
91
|
+
if (!working) {
|
|
92
|
+
working = "";
|
|
93
|
+
}
|
|
94
|
+
if (working && !working.endsWith("\n")) {
|
|
95
|
+
working += "\n";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let range = findSectionRange(working, section);
|
|
99
|
+
let createdSection = false;
|
|
100
|
+
if (!range) {
|
|
101
|
+
working = appendSection(working, section);
|
|
102
|
+
range = findSectionRange(working, section);
|
|
103
|
+
createdSection = true;
|
|
104
|
+
}
|
|
105
|
+
if (!range) {
|
|
106
|
+
throw new Error(`无法定位 section: ${section}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const currentBody = working.slice(range.contentStart, range.end);
|
|
110
|
+
const nextBody = normalizeText(options.slot)
|
|
111
|
+
? upsertManagedSlot(currentBody, range.level, {
|
|
112
|
+
slot: options.slot,
|
|
113
|
+
style,
|
|
114
|
+
text,
|
|
115
|
+
})
|
|
116
|
+
: upsertSectionEntry(currentBody, range.level, {
|
|
117
|
+
style,
|
|
118
|
+
text,
|
|
119
|
+
maxItems: normalizeMaxItems(options.maxItems),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const next = replaceSectionBody(working, range, nextBody);
|
|
123
|
+
const finalContent = ensureTrailingNewline(next);
|
|
124
|
+
return {
|
|
125
|
+
changed: normalizeFileEnding(finalContent) !== ensureTrailingNewline(content),
|
|
126
|
+
createdSection,
|
|
127
|
+
content: finalContent,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function upsertManagedSlot(sectionBody, parentLevel, options = {}) {
|
|
132
|
+
const slot = normalizeText(options.slot);
|
|
133
|
+
if (!slot) {
|
|
134
|
+
throw new Error("slot 不能为空");
|
|
135
|
+
}
|
|
136
|
+
const block = buildManagedBlock(slot, options.text, options.style);
|
|
137
|
+
const existingPattern = buildManagedSlotPattern(slot);
|
|
138
|
+
|
|
139
|
+
const normalizedBody = normalizeFileEnding(sectionBody);
|
|
140
|
+
if (existingPattern.test(normalizedBody)) {
|
|
141
|
+
return normalizedBody.replace(existingPattern, block).trimEnd();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { main, suffix } = splitSectionBody(normalizedBody, parentLevel);
|
|
145
|
+
return joinSectionParts(main, suffix, block);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildManagedBlock(slot, text, style) {
|
|
149
|
+
const markerStart = `<!-- ${SLOT_MARKER_PREFIX}:${slot}:start -->`;
|
|
150
|
+
const markerEnd = `<!-- ${SLOT_MARKER_PREFIX}:${slot}:end -->`;
|
|
151
|
+
const body = renderEntryText(text, style);
|
|
152
|
+
return [markerStart, body, markerEnd].join("\n");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildManagedSlotPattern(slot) {
|
|
156
|
+
const prefixPattern = SLOT_MARKER_PREFIXES.map(escapeRegExp).join("|");
|
|
157
|
+
const normalizedSlot = escapeRegExp(slot);
|
|
158
|
+
return new RegExp(
|
|
159
|
+
`<!--\\s*(?:${prefixPattern}):${normalizedSlot}:start\\s*-->[\\s\\S]*?<!--\\s*(?:${prefixPattern}):${normalizedSlot}:end\\s*-->`,
|
|
160
|
+
"u"
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function upsertSectionEntry(sectionBody, parentLevel, options = {}) {
|
|
165
|
+
const { main, suffix } = splitSectionBody(sectionBody, parentLevel);
|
|
166
|
+
const nextMain = options.style === "paragraph"
|
|
167
|
+
? upsertParagraph(main, options.text)
|
|
168
|
+
: upsertBullet(main, options.text, options.maxItems);
|
|
169
|
+
return joinSectionParts(nextMain, suffix);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function splitSectionBody(sectionBody, parentLevel) {
|
|
173
|
+
const normalizedBody = normalizeFileEnding(sectionBody);
|
|
174
|
+
const headingPattern = /^(#{1,6})\s+/gmu;
|
|
175
|
+
let match = headingPattern.exec(normalizedBody);
|
|
176
|
+
while (match) {
|
|
177
|
+
const level = match[1].length;
|
|
178
|
+
if (level > parentLevel) {
|
|
179
|
+
return {
|
|
180
|
+
main: normalizedBody.slice(0, match.index).trimEnd(),
|
|
181
|
+
suffix: normalizedBody.slice(match.index).trim(),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
match = headingPattern.exec(normalizedBody);
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
main: normalizedBody.trimEnd(),
|
|
188
|
+
suffix: "",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function joinSectionParts(main, suffix, injectedBlock = "") {
|
|
193
|
+
const normalizedMain = normalizeFileEnding(main).trimEnd();
|
|
194
|
+
const normalizedSuffix = normalizeFileEnding(suffix).trim();
|
|
195
|
+
const normalizedBlock = normalizeFileEnding(injectedBlock).trim();
|
|
196
|
+
const parts = [];
|
|
197
|
+
|
|
198
|
+
if (normalizedMain) {
|
|
199
|
+
parts.push(normalizedMain);
|
|
200
|
+
}
|
|
201
|
+
if (normalizedBlock) {
|
|
202
|
+
parts.push(normalizedBlock);
|
|
203
|
+
}
|
|
204
|
+
if (normalizedSuffix) {
|
|
205
|
+
parts.push(normalizedSuffix);
|
|
206
|
+
}
|
|
207
|
+
return parts.join("\n\n").trimEnd();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function upsertBullet(sectionMain, text, maxItems = 0) {
|
|
211
|
+
const normalizedText = normalizeBulletText(text);
|
|
212
|
+
if (!normalizedText) {
|
|
213
|
+
return normalizeFileEnding(sectionMain).trimEnd();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const lines = normalizeFileEnding(sectionMain).split("\n");
|
|
217
|
+
const bulletIndexes = [];
|
|
218
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
219
|
+
const match = /^-\s+(.*)$/u.exec(String(lines[index] || "").trim());
|
|
220
|
+
if (!match) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (normalizeBulletText(match[1]) === normalizedText) {
|
|
224
|
+
return normalizeFileEnding(sectionMain).trimEnd();
|
|
225
|
+
}
|
|
226
|
+
bulletIndexes.push(index);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const entryLine = `- ${normalizedText}`;
|
|
230
|
+
if (bulletIndexes.length) {
|
|
231
|
+
lines.splice(bulletIndexes[0], 0, entryLine);
|
|
232
|
+
} else {
|
|
233
|
+
while (lines.length && !String(lines[lines.length - 1] || "").trim()) {
|
|
234
|
+
lines.pop();
|
|
235
|
+
}
|
|
236
|
+
if (lines.length) {
|
|
237
|
+
lines.push("");
|
|
238
|
+
}
|
|
239
|
+
lines.push(entryLine);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (maxItems > 0) {
|
|
243
|
+
let seen = 0;
|
|
244
|
+
const trimmedLines = [];
|
|
245
|
+
for (const line of lines) {
|
|
246
|
+
const isBullet = /^-\s+/.test(String(line || "").trim());
|
|
247
|
+
if (!isBullet) {
|
|
248
|
+
trimmedLines.push(line);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
seen += 1;
|
|
252
|
+
if (seen <= maxItems) {
|
|
253
|
+
trimmedLines.push(line);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return trimBlock(trimmedLines.join("\n"));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return trimBlock(lines.join("\n"));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function upsertParagraph(sectionMain, text) {
|
|
263
|
+
const normalizedText = normalizeParagraphText(text);
|
|
264
|
+
if (!normalizedText) {
|
|
265
|
+
return normalizeFileEnding(sectionMain).trimEnd();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const blocks = normalizeFileEnding(sectionMain)
|
|
269
|
+
.split(/\n{2,}/u)
|
|
270
|
+
.map((block) => block.trim())
|
|
271
|
+
.filter(Boolean);
|
|
272
|
+
const comparableText = normalizeComparableText(normalizedText);
|
|
273
|
+
if (blocks.some((block) => normalizeComparableText(block) === comparableText)) {
|
|
274
|
+
return normalizeFileEnding(sectionMain).trimEnd();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const normalizedMain = normalizeFileEnding(sectionMain).trimEnd();
|
|
278
|
+
if (!normalizedMain) {
|
|
279
|
+
return normalizedText;
|
|
280
|
+
}
|
|
281
|
+
return `${normalizedMain}\n\n${normalizedText}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function renderEntryText(text, style) {
|
|
285
|
+
if (style === "paragraph") {
|
|
286
|
+
return normalizeParagraphText(text);
|
|
287
|
+
}
|
|
288
|
+
return `- ${normalizeBulletText(text)}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function replaceSectionBody(content, range, newBody) {
|
|
292
|
+
const before = content.slice(0, range.contentStart).replace(/\s*$/u, "");
|
|
293
|
+
const after = content.slice(range.end).replace(/^\s*/u, "");
|
|
294
|
+
const parts = [before];
|
|
295
|
+
const normalizedBody = normalizeFileEnding(newBody).trimEnd();
|
|
296
|
+
if (normalizedBody) {
|
|
297
|
+
parts.push(normalizedBody);
|
|
298
|
+
}
|
|
299
|
+
if (after) {
|
|
300
|
+
parts.push(after);
|
|
301
|
+
}
|
|
302
|
+
return parts.filter(Boolean).join("\n\n");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function findSectionRange(content, sectionTitle) {
|
|
306
|
+
const headings = parseHeadings(content);
|
|
307
|
+
const normalizedTitle = normalizeHeadingText(sectionTitle);
|
|
308
|
+
const currentHeading = headings.find((heading) => normalizeHeadingText(heading.title) === normalizedTitle);
|
|
309
|
+
if (!currentHeading) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
const nextHeading = headings.find((heading) =>
|
|
313
|
+
heading.index > currentHeading.index && heading.level <= currentHeading.level
|
|
314
|
+
);
|
|
315
|
+
return {
|
|
316
|
+
level: currentHeading.level,
|
|
317
|
+
headingStart: currentHeading.index,
|
|
318
|
+
contentStart: currentHeading.lineEnd,
|
|
319
|
+
end: nextHeading ? nextHeading.index : content.length,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function parseHeadings(content) {
|
|
324
|
+
const normalized = normalizeFileEnding(content);
|
|
325
|
+
const pattern = /^(#{1,6})\s+(.+?)\s*$/gmu;
|
|
326
|
+
const headings = [];
|
|
327
|
+
let match = pattern.exec(normalized);
|
|
328
|
+
while (match) {
|
|
329
|
+
let lineEnd = pattern.lastIndex;
|
|
330
|
+
if (normalized[lineEnd] === "\n") {
|
|
331
|
+
lineEnd += 1;
|
|
332
|
+
}
|
|
333
|
+
headings.push({
|
|
334
|
+
level: match[1].length,
|
|
335
|
+
title: match[2],
|
|
336
|
+
index: match.index,
|
|
337
|
+
lineEnd,
|
|
338
|
+
});
|
|
339
|
+
match = pattern.exec(normalized);
|
|
340
|
+
}
|
|
341
|
+
return headings;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function appendSection(content, sectionTitle) {
|
|
345
|
+
const normalized = normalizeFileEnding(content).replace(/\s*$/u, "");
|
|
346
|
+
const heading = `## ${sectionTitle}`;
|
|
347
|
+
if (!normalized) {
|
|
348
|
+
return `${heading}\n`;
|
|
349
|
+
}
|
|
350
|
+
return `${normalized}\n\n${heading}\n`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function matchesProjectSelector(project, selector) {
|
|
354
|
+
const normalizedSelector = normalizeText(selector).toLowerCase();
|
|
355
|
+
return project.slug.toLowerCase() === normalizedSelector
|
|
356
|
+
|| normalizeText(project.title).toLowerCase() === normalizedSelector
|
|
357
|
+
|| (Array.isArray(project.aliases)
|
|
358
|
+
&& project.aliases.some((alias) => normalizeText(alias).toLowerCase() === normalizedSelector));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function normalizeStyle(value) {
|
|
362
|
+
const normalized = normalizeText(value).toLowerCase();
|
|
363
|
+
if (!normalized || normalized === "bullet") {
|
|
364
|
+
return "bullet";
|
|
365
|
+
}
|
|
366
|
+
if (normalized === "paragraph") {
|
|
367
|
+
return "paragraph";
|
|
368
|
+
}
|
|
369
|
+
throw new Error(`不支持的 note style: ${value}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function normalizeMaxItems(value) {
|
|
373
|
+
const raw = String(value || "").trim();
|
|
374
|
+
if (!raw) {
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
const parsed = Number.parseInt(raw, 10);
|
|
378
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
379
|
+
throw new Error(`--max-items 必须是正整数: ${value}`);
|
|
380
|
+
}
|
|
381
|
+
return parsed;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function normalizeBulletText(value) {
|
|
385
|
+
return normalizeParagraphText(value).replace(/\s*\n+\s*/gu, " ").replace(/\s{2,}/gu, " ").trim();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function normalizeParagraphText(value) {
|
|
389
|
+
return normalizeFileEnding(value).trim();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function normalizeHeadingText(value) {
|
|
393
|
+
return normalizeText(value).replace(/\s+/gu, " ").toLowerCase();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function normalizeComparableText(value) {
|
|
397
|
+
return normalizeParagraphText(value).replace(/\s+/gu, " ").toLowerCase();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function normalizeDisplayPath(targetPath) {
|
|
401
|
+
return normalizeText(targetPath).replace(/\\/g, "/");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function normalizeFileEnding(value) {
|
|
405
|
+
return String(value || "").replace(/\r\n/g, "\n");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function normalizeText(value) {
|
|
409
|
+
return typeof value === "string" ? value.trim() : "";
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function ensureTrailingNewline(value) {
|
|
413
|
+
const normalized = normalizeFileEnding(value);
|
|
414
|
+
return normalized.endsWith("\n") ? normalized : `${normalized}\n`;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function trimBlock(value) {
|
|
418
|
+
return normalizeFileEnding(value).replace(/^\s*\n/gu, "").replace(/\n\s*$/gu, "").trimEnd();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function escapeRegExp(value) {
|
|
422
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
module.exports = {
|
|
426
|
+
appendSection,
|
|
427
|
+
findSectionRange,
|
|
428
|
+
normalizeBulletText,
|
|
429
|
+
normalizeStyle,
|
|
430
|
+
resolveNoteSyncTarget,
|
|
431
|
+
syncNoteContent,
|
|
432
|
+
syncNoteFile,
|
|
433
|
+
};
|