@actalk/inkos-core 0.3.6 → 0.4.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/agents/architect.d.ts.map +1 -1
- package/dist/agents/architect.js +17 -3
- package/dist/agents/architect.js.map +1 -1
- package/dist/agents/base.d.ts +10 -0
- package/dist/agents/base.d.ts.map +1 -1
- package/dist/agents/base.js +12 -0
- package/dist/agents/base.js.map +1 -1
- package/dist/agents/continuity.d.ts +3 -1
- package/dist/agents/continuity.d.ts.map +1 -1
- package/dist/agents/continuity.js +78 -13
- package/dist/agents/continuity.js.map +1 -1
- package/dist/agents/post-write-validator.d.ts +16 -0
- package/dist/agents/post-write-validator.d.ts.map +1 -0
- package/dist/agents/post-write-validator.js +204 -0
- package/dist/agents/post-write-validator.js.map +1 -0
- package/dist/agents/reviser.d.ts +1 -1
- package/dist/agents/reviser.d.ts.map +1 -1
- package/dist/agents/reviser.js +16 -3
- package/dist/agents/reviser.js.map +1 -1
- package/dist/agents/sensitive-words.d.ts +22 -0
- package/dist/agents/sensitive-words.d.ts.map +1 -0
- package/dist/agents/sensitive-words.js +91 -0
- package/dist/agents/sensitive-words.js.map +1 -0
- package/dist/agents/writer-prompts.d.ts +1 -1
- package/dist/agents/writer-prompts.d.ts.map +1 -1
- package/dist/agents/writer-prompts.js +122 -11
- package/dist/agents/writer-prompts.js.map +1 -1
- package/dist/agents/writer.d.ts +15 -0
- package/dist/agents/writer.d.ts.map +1 -1
- package/dist/agents/writer.js +150 -5
- package/dist/agents/writer.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/provider.d.ts +1 -0
- package/dist/llm/provider.d.ts.map +1 -1
- package/dist/llm/provider.js +9 -4
- package/dist/llm/provider.js.map +1 -1
- package/dist/models/book.d.ts +4 -4
- package/dist/models/book.d.ts.map +1 -1
- package/dist/models/book.js +0 -3
- package/dist/models/book.js.map +1 -1
- package/dist/models/project.d.ts +23 -7
- package/dist/models/project.d.ts.map +1 -1
- package/dist/models/project.js +13 -6
- package/dist/models/project.js.map +1 -1
- package/dist/notify/webhook.d.ts +1 -1
- package/dist/notify/webhook.d.ts.map +1 -1
- package/dist/notify/webhook.js.map +1 -1
- package/dist/pipeline/agent.d.ts.map +1 -1
- package/dist/pipeline/agent.js +76 -8
- package/dist/pipeline/agent.js.map +1 -1
- package/dist/pipeline/runner.d.ts +14 -1
- package/dist/pipeline/runner.d.ts.map +1 -1
- package/dist/pipeline/runner.js +267 -30
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/pipeline/scheduler.d.ts +20 -2
- package/dist/pipeline/scheduler.d.ts.map +1 -1
- package/dist/pipeline/scheduler.js +162 -32
- package/dist/pipeline/scheduler.js.map +1 -1
- package/dist/utils/web-search.d.ts +13 -0
- package/dist/utils/web-search.d.ts.map +1 -0
- package/dist/utils/web-search.js +37 -0
- package/dist/utils/web-search.js.map +1 -0
- package/genres/horror.md +1 -1
- package/genres/other.md +1 -1
- package/genres/urban.md +1 -1
- package/genres/xianxia.md +1 -1
- package/genres/xuanhuan.md +1 -1
- package/package.json +2 -1
- package/dist/__tests__/ai-tells.test.d.ts +0 -2
- package/dist/__tests__/ai-tells.test.d.ts.map +0 -1
- package/dist/__tests__/ai-tells.test.js +0 -78
- package/dist/__tests__/ai-tells.test.js.map +0 -1
- package/dist/__tests__/detection-insights.test.d.ts +0 -2
- package/dist/__tests__/detection-insights.test.d.ts.map +0 -1
- package/dist/__tests__/detection-insights.test.js +0 -50
- package/dist/__tests__/detection-insights.test.js.map +0 -1
- package/dist/__tests__/detector.test.d.ts +0 -2
- package/dist/__tests__/detector.test.d.ts.map +0 -1
- package/dist/__tests__/detector.test.js +0 -71
- package/dist/__tests__/detector.test.js.map +0 -1
- package/dist/__tests__/models.test.d.ts +0 -2
- package/dist/__tests__/models.test.d.ts.map +0 -1
- package/dist/__tests__/models.test.js +0 -328
- package/dist/__tests__/models.test.js.map +0 -1
- package/dist/__tests__/state-manager.test.d.ts +0 -2
- package/dist/__tests__/state-manager.test.d.ts.map +0 -1
- package/dist/__tests__/state-manager.test.js +0 -264
- package/dist/__tests__/state-manager.test.js.map +0 -1
- package/dist/__tests__/style-analyzer.test.d.ts +0 -2
- package/dist/__tests__/style-analyzer.test.d.ts.map +0 -1
- package/dist/__tests__/style-analyzer.test.js +0 -53
- package/dist/__tests__/style-analyzer.test.js.map +0 -1
- package/dist/__tests__/webhook.test.d.ts +0 -2
- package/dist/__tests__/webhook.test.d.ts.map +0 -1
- package/dist/__tests__/webhook.test.js +0 -62
- package/dist/__tests__/webhook.test.js.map +0 -1
- package/dist/__tests__/writer-parser.test.d.ts +0 -2
- package/dist/__tests__/writer-parser.test.d.ts.map +0 -1
- package/dist/__tests__/writer-parser.test.js +0 -195
- package/dist/__tests__/writer-parser.test.js.map +0 -1
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { mkdtemp, rm, writeFile, readFile, mkdir, stat } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { StateManager } from "../state/manager.js";
|
|
6
|
-
describe("StateManager", () => {
|
|
7
|
-
let tempDir;
|
|
8
|
-
let manager;
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
tempDir = await mkdtemp(join(tmpdir(), "inkos-test-"));
|
|
11
|
-
manager = new StateManager(tempDir);
|
|
12
|
-
});
|
|
13
|
-
afterEach(async () => {
|
|
14
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
15
|
-
});
|
|
16
|
-
// -------------------------------------------------------------------------
|
|
17
|
-
// BookConfig persistence
|
|
18
|
-
// -------------------------------------------------------------------------
|
|
19
|
-
describe("saveBookConfig / loadBookConfig", () => {
|
|
20
|
-
const bookConfig = {
|
|
21
|
-
id: "test-book",
|
|
22
|
-
title: "Test Novel",
|
|
23
|
-
platform: "tomato",
|
|
24
|
-
genre: "xuanhuan",
|
|
25
|
-
status: "active",
|
|
26
|
-
targetChapters: 200,
|
|
27
|
-
chapterWordCount: 3000,
|
|
28
|
-
createdAt: "2026-01-01T00:00:00Z",
|
|
29
|
-
updatedAt: "2026-01-01T00:00:00Z",
|
|
30
|
-
};
|
|
31
|
-
it("round-trips a BookConfig through save and load", async () => {
|
|
32
|
-
await manager.saveBookConfig("test-book", bookConfig);
|
|
33
|
-
const loaded = await manager.loadBookConfig("test-book");
|
|
34
|
-
expect(loaded).toEqual(bookConfig);
|
|
35
|
-
});
|
|
36
|
-
it("creates the book directory on save", async () => {
|
|
37
|
-
await manager.saveBookConfig("new-book", {
|
|
38
|
-
...bookConfig,
|
|
39
|
-
id: "new-book",
|
|
40
|
-
});
|
|
41
|
-
const dirStat = await stat(manager.bookDir("new-book"));
|
|
42
|
-
expect(dirStat.isDirectory()).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
it("throws when loading a non-existent book", async () => {
|
|
45
|
-
await expect(manager.loadBookConfig("nope")).rejects.toThrow();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
// -------------------------------------------------------------------------
|
|
49
|
-
// ChapterIndex persistence
|
|
50
|
-
// -------------------------------------------------------------------------
|
|
51
|
-
describe("saveChapterIndex / loadChapterIndex", () => {
|
|
52
|
-
const chapters = [
|
|
53
|
-
{
|
|
54
|
-
number: 1,
|
|
55
|
-
title: "Ch1",
|
|
56
|
-
status: "drafted",
|
|
57
|
-
wordCount: 3000,
|
|
58
|
-
createdAt: "2026-01-01T00:00:00Z",
|
|
59
|
-
updatedAt: "2026-01-01T00:00:00Z",
|
|
60
|
-
auditIssues: [],
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
number: 2,
|
|
64
|
-
title: "Ch2",
|
|
65
|
-
status: "drafting",
|
|
66
|
-
wordCount: 0,
|
|
67
|
-
createdAt: "2026-01-02T00:00:00Z",
|
|
68
|
-
updatedAt: "2026-01-02T00:00:00Z",
|
|
69
|
-
auditIssues: ["pacing issue"],
|
|
70
|
-
},
|
|
71
|
-
];
|
|
72
|
-
it("round-trips chapter index through save and load", async () => {
|
|
73
|
-
await manager.saveChapterIndex("book-a", chapters);
|
|
74
|
-
const loaded = await manager.loadChapterIndex("book-a");
|
|
75
|
-
expect(loaded).toEqual(chapters);
|
|
76
|
-
});
|
|
77
|
-
it("returns empty array when no index exists", async () => {
|
|
78
|
-
const loaded = await manager.loadChapterIndex("nonexistent");
|
|
79
|
-
expect(loaded).toEqual([]);
|
|
80
|
-
});
|
|
81
|
-
it("creates the chapters directory on save", async () => {
|
|
82
|
-
await manager.saveChapterIndex("book-b", []);
|
|
83
|
-
const dirStat = await stat(join(manager.bookDir("book-b"), "chapters"));
|
|
84
|
-
expect(dirStat.isDirectory()).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
// -------------------------------------------------------------------------
|
|
88
|
-
// getNextChapterNumber
|
|
89
|
-
// -------------------------------------------------------------------------
|
|
90
|
-
describe("getNextChapterNumber", () => {
|
|
91
|
-
it("returns 1 for an empty book (no chapters)", async () => {
|
|
92
|
-
const next = await manager.getNextChapterNumber("empty-book");
|
|
93
|
-
expect(next).toBe(1);
|
|
94
|
-
});
|
|
95
|
-
it("returns max+1 when chapters exist", async () => {
|
|
96
|
-
const chapters = [
|
|
97
|
-
{
|
|
98
|
-
number: 1,
|
|
99
|
-
title: "Ch1",
|
|
100
|
-
status: "published",
|
|
101
|
-
wordCount: 3000,
|
|
102
|
-
createdAt: "2026-01-01T00:00:00Z",
|
|
103
|
-
updatedAt: "2026-01-01T00:00:00Z",
|
|
104
|
-
auditIssues: [],
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
number: 5,
|
|
108
|
-
title: "Ch5",
|
|
109
|
-
status: "drafted",
|
|
110
|
-
wordCount: 2800,
|
|
111
|
-
createdAt: "2026-01-05T00:00:00Z",
|
|
112
|
-
updatedAt: "2026-01-05T00:00:00Z",
|
|
113
|
-
auditIssues: [],
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
number: 3,
|
|
117
|
-
title: "Ch3",
|
|
118
|
-
status: "approved",
|
|
119
|
-
wordCount: 3100,
|
|
120
|
-
createdAt: "2026-01-03T00:00:00Z",
|
|
121
|
-
updatedAt: "2026-01-03T00:00:00Z",
|
|
122
|
-
auditIssues: [],
|
|
123
|
-
},
|
|
124
|
-
];
|
|
125
|
-
await manager.saveChapterIndex("book-x", chapters);
|
|
126
|
-
const next = await manager.getNextChapterNumber("book-x");
|
|
127
|
-
expect(next).toBe(6);
|
|
128
|
-
});
|
|
129
|
-
it("returns 2 when only chapter 1 exists", async () => {
|
|
130
|
-
const chapters = [
|
|
131
|
-
{
|
|
132
|
-
number: 1,
|
|
133
|
-
title: "Ch1",
|
|
134
|
-
status: "drafted",
|
|
135
|
-
wordCount: 3000,
|
|
136
|
-
createdAt: "2026-01-01T00:00:00Z",
|
|
137
|
-
updatedAt: "2026-01-01T00:00:00Z",
|
|
138
|
-
auditIssues: [],
|
|
139
|
-
},
|
|
140
|
-
];
|
|
141
|
-
await manager.saveChapterIndex("book-y", chapters);
|
|
142
|
-
const next = await manager.getNextChapterNumber("book-y");
|
|
143
|
-
expect(next).toBe(2);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
// -------------------------------------------------------------------------
|
|
147
|
-
// listBooks
|
|
148
|
-
// -------------------------------------------------------------------------
|
|
149
|
-
describe("listBooks", () => {
|
|
150
|
-
it("returns empty array when no books directory exists", async () => {
|
|
151
|
-
const books = await manager.listBooks();
|
|
152
|
-
expect(books).toEqual([]);
|
|
153
|
-
});
|
|
154
|
-
it("returns book IDs for directories with book.json", async () => {
|
|
155
|
-
const bookConfig = {
|
|
156
|
-
id: "alpha",
|
|
157
|
-
title: "Alpha",
|
|
158
|
-
platform: "tomato",
|
|
159
|
-
genre: "urban",
|
|
160
|
-
status: "active",
|
|
161
|
-
targetChapters: 100,
|
|
162
|
-
chapterWordCount: 3000,
|
|
163
|
-
createdAt: "2026-01-01T00:00:00Z",
|
|
164
|
-
updatedAt: "2026-01-01T00:00:00Z",
|
|
165
|
-
};
|
|
166
|
-
await manager.saveBookConfig("alpha", bookConfig);
|
|
167
|
-
await manager.saveBookConfig("beta", { ...bookConfig, id: "beta", title: "Beta" });
|
|
168
|
-
// Create a decoy directory without book.json
|
|
169
|
-
await mkdir(join(manager.booksDir, "not-a-book"), { recursive: true });
|
|
170
|
-
const books = await manager.listBooks();
|
|
171
|
-
expect(books).toContain("alpha");
|
|
172
|
-
expect(books).toContain("beta");
|
|
173
|
-
expect(books).not.toContain("not-a-book");
|
|
174
|
-
expect(books).toHaveLength(2);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
// -------------------------------------------------------------------------
|
|
178
|
-
// snapshotState / restoreState
|
|
179
|
-
// -------------------------------------------------------------------------
|
|
180
|
-
describe("snapshotState / restoreState", () => {
|
|
181
|
-
const bookId = "snap-book";
|
|
182
|
-
beforeEach(async () => {
|
|
183
|
-
const storyDir = join(manager.bookDir(bookId), "story");
|
|
184
|
-
await mkdir(storyDir, { recursive: true });
|
|
185
|
-
await writeFile(join(storyDir, "current_state.md"), "# State at ch1", "utf-8");
|
|
186
|
-
await writeFile(join(storyDir, "particle_ledger.md"), "# Ledger at ch1", "utf-8");
|
|
187
|
-
await writeFile(join(storyDir, "pending_hooks.md"), "# Hooks at ch1", "utf-8");
|
|
188
|
-
});
|
|
189
|
-
it("snapshots current state files to a numbered directory", async () => {
|
|
190
|
-
await manager.snapshotState(bookId, 1);
|
|
191
|
-
const snapshotDir = join(manager.bookDir(bookId), "story", "snapshots", "1");
|
|
192
|
-
const state = await readFile(join(snapshotDir, "current_state.md"), "utf-8");
|
|
193
|
-
expect(state).toBe("# State at ch1");
|
|
194
|
-
const ledger = await readFile(join(snapshotDir, "particle_ledger.md"), "utf-8");
|
|
195
|
-
expect(ledger).toBe("# Ledger at ch1");
|
|
196
|
-
const hooks = await readFile(join(snapshotDir, "pending_hooks.md"), "utf-8");
|
|
197
|
-
expect(hooks).toBe("# Hooks at ch1");
|
|
198
|
-
});
|
|
199
|
-
it("restores state from a previous snapshot", async () => {
|
|
200
|
-
await manager.snapshotState(bookId, 1);
|
|
201
|
-
// Modify the current state files
|
|
202
|
-
const storyDir = join(manager.bookDir(bookId), "story");
|
|
203
|
-
await writeFile(join(storyDir, "current_state.md"), "# State at ch2 (modified)", "utf-8");
|
|
204
|
-
await writeFile(join(storyDir, "particle_ledger.md"), "# Ledger at ch2 (modified)", "utf-8");
|
|
205
|
-
await writeFile(join(storyDir, "pending_hooks.md"), "# Hooks at ch2 (modified)", "utf-8");
|
|
206
|
-
const restored = await manager.restoreState(bookId, 1);
|
|
207
|
-
expect(restored).toBe(true);
|
|
208
|
-
// Verify restored content
|
|
209
|
-
const state = await readFile(join(storyDir, "current_state.md"), "utf-8");
|
|
210
|
-
expect(state).toBe("# State at ch1");
|
|
211
|
-
const ledger = await readFile(join(storyDir, "particle_ledger.md"), "utf-8");
|
|
212
|
-
expect(ledger).toBe("# Ledger at ch1");
|
|
213
|
-
});
|
|
214
|
-
it("returns false when restoring from non-existent snapshot", async () => {
|
|
215
|
-
const restored = await manager.restoreState(bookId, 999);
|
|
216
|
-
expect(restored).toBe(false);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
// -------------------------------------------------------------------------
|
|
220
|
-
// acquireBookLock
|
|
221
|
-
// -------------------------------------------------------------------------
|
|
222
|
-
describe("acquireBookLock", () => {
|
|
223
|
-
it("acquires a lock and returns a release function", async () => {
|
|
224
|
-
// Ensure book directory exists
|
|
225
|
-
await mkdir(manager.bookDir("lock-book"), { recursive: true });
|
|
226
|
-
const release = await manager.acquireBookLock("lock-book");
|
|
227
|
-
expect(typeof release).toBe("function");
|
|
228
|
-
// Lock file should exist
|
|
229
|
-
const lockPath = join(manager.bookDir("lock-book"), ".write.lock");
|
|
230
|
-
const lockStat = await stat(lockPath);
|
|
231
|
-
expect(lockStat.isFile()).toBe(true);
|
|
232
|
-
// Release the lock
|
|
233
|
-
await release();
|
|
234
|
-
// Lock file should be gone
|
|
235
|
-
await expect(stat(lockPath)).rejects.toThrow();
|
|
236
|
-
});
|
|
237
|
-
it("throws when lock is already held", async () => {
|
|
238
|
-
await mkdir(manager.bookDir("lock-book-2"), { recursive: true });
|
|
239
|
-
const release = await manager.acquireBookLock("lock-book-2");
|
|
240
|
-
await expect(manager.acquireBookLock("lock-book-2")).rejects.toThrow(/is locked/);
|
|
241
|
-
await release();
|
|
242
|
-
});
|
|
243
|
-
it("allows re-acquiring lock after release", async () => {
|
|
244
|
-
await mkdir(manager.bookDir("lock-book-3"), { recursive: true });
|
|
245
|
-
const release1 = await manager.acquireBookLock("lock-book-3");
|
|
246
|
-
await release1();
|
|
247
|
-
const release2 = await manager.acquireBookLock("lock-book-3");
|
|
248
|
-
expect(typeof release2).toBe("function");
|
|
249
|
-
await release2();
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
// -------------------------------------------------------------------------
|
|
253
|
-
// Path helpers
|
|
254
|
-
// -------------------------------------------------------------------------
|
|
255
|
-
describe("path helpers", () => {
|
|
256
|
-
it("booksDir points to <projectRoot>/books", () => {
|
|
257
|
-
expect(manager.booksDir).toBe(join(tempDir, "books"));
|
|
258
|
-
});
|
|
259
|
-
it("bookDir returns <booksDir>/<bookId>", () => {
|
|
260
|
-
expect(manager.bookDir("my-book")).toBe(join(tempDir, "books", "my-book"));
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
//# sourceMappingURL=state-manager.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state-manager.test.js","sourceRoot":"","sources":["../../src/__tests__/state-manager.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"style-analyzer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/style-analyzer.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { analyzeStyle } from "../agents/style-analyzer.js";
|
|
3
|
-
describe("analyzeStyle", () => {
|
|
4
|
-
const sampleText = [
|
|
5
|
-
"陈风一脚踩碎了脚下的石板。碎石飞溅,打在旁边的墙壁上发出清脆的声响。他低头看了一眼,嘴角微微上扬。",
|
|
6
|
-
"",
|
|
7
|
-
"\"谁?\"他低喝一声,手已经按上了腰间的刀柄。指尖触到冰凉的金属,心跳稍微稳了一些。",
|
|
8
|
-
"",
|
|
9
|
-
"黑暗中,一双眼睛正盯着他。那目光冰冷得像冬夜的寒风,带着审视和一丝不易察觉的警惕。来者不善。但陈风并不怕。他经历过比这更恶劣的处境。比这更危险的对手。他攥紧了刀柄,朝着那双眼睛走了过去。脚步声在空旷的巷子里回荡。",
|
|
10
|
-
].join("\n");
|
|
11
|
-
it("calculates sentence length statistics", () => {
|
|
12
|
-
const profile = analyzeStyle(sampleText);
|
|
13
|
-
expect(profile.avgSentenceLength).toBeGreaterThan(0);
|
|
14
|
-
expect(profile.sentenceLengthStdDev).toBeGreaterThan(0);
|
|
15
|
-
});
|
|
16
|
-
it("calculates paragraph length statistics", () => {
|
|
17
|
-
const profile = analyzeStyle(sampleText);
|
|
18
|
-
expect(profile.avgParagraphLength).toBeGreaterThan(0);
|
|
19
|
-
expect(profile.paragraphLengthRange.min).toBeGreaterThan(0);
|
|
20
|
-
expect(profile.paragraphLengthRange.max).toBeGreaterThanOrEqual(profile.paragraphLengthRange.min);
|
|
21
|
-
});
|
|
22
|
-
it("calculates vocabulary diversity", () => {
|
|
23
|
-
const profile = analyzeStyle(sampleText);
|
|
24
|
-
expect(profile.vocabularyDiversity).toBeGreaterThan(0);
|
|
25
|
-
expect(profile.vocabularyDiversity).toBeLessThanOrEqual(1);
|
|
26
|
-
});
|
|
27
|
-
it("includes source name when provided", () => {
|
|
28
|
-
const profile = analyzeStyle(sampleText, "测试来源");
|
|
29
|
-
expect(profile.sourceName).toBe("测试来源");
|
|
30
|
-
});
|
|
31
|
-
it("includes analyzed timestamp", () => {
|
|
32
|
-
const profile = analyzeStyle(sampleText);
|
|
33
|
-
expect(profile.analyzedAt).toBeDefined();
|
|
34
|
-
});
|
|
35
|
-
it("handles empty text", () => {
|
|
36
|
-
const profile = analyzeStyle("");
|
|
37
|
-
expect(profile.avgSentenceLength).toBe(0);
|
|
38
|
-
expect(profile.avgParagraphLength).toBe(0);
|
|
39
|
-
expect(profile.vocabularyDiversity).toBe(0);
|
|
40
|
-
});
|
|
41
|
-
it("detects top patterns from repeated sentence openings", () => {
|
|
42
|
-
const repetitiveText = [
|
|
43
|
-
"他看着远方。他看着山峰。他看着大海。他看着天空。",
|
|
44
|
-
"",
|
|
45
|
-
"风在吹。雨在下。",
|
|
46
|
-
].join("\n");
|
|
47
|
-
const profile = analyzeStyle(repetitiveText);
|
|
48
|
-
// "他看" should be detected as a top pattern
|
|
49
|
-
const hasHeKan = profile.topPatterns.some((p) => p.includes("他看"));
|
|
50
|
-
expect(hasHeKan).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
//# sourceMappingURL=style-analyzer.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"style-analyzer.test.js","sourceRoot":"","sources":["../../src/__tests__/style-analyzer.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,UAAU,GAAG;QACjB,mDAAmD;QACnD,EAAE;QACF,6CAA6C;QAC7C,EAAE;QACF,4GAA4G;KAC7G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5D,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,sBAAsB,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,cAAc,GAAG;YACrB,0BAA0B;YAC1B,EAAE;YACF,UAAU;SACX,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,OAAO,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;QAC7C,2CAA2C;QAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACnE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/webhook.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { sendWebhook } from "../notify/webhook.js";
|
|
3
|
-
import { createHmac } from "node:crypto";
|
|
4
|
-
const mockFetch = vi.fn();
|
|
5
|
-
vi.stubGlobal("fetch", mockFetch);
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
mockFetch.mockReset();
|
|
8
|
-
});
|
|
9
|
-
const basePayload = {
|
|
10
|
-
event: "chapter-complete",
|
|
11
|
-
bookId: "test-book",
|
|
12
|
-
chapterNumber: 5,
|
|
13
|
-
timestamp: "2026-03-14T00:00:00.000Z",
|
|
14
|
-
data: { title: "测试章节", wordCount: 3000 },
|
|
15
|
-
};
|
|
16
|
-
describe("sendWebhook", () => {
|
|
17
|
-
it("sends POST request with JSON payload", async () => {
|
|
18
|
-
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
19
|
-
await sendWebhook({ url: "https://example.com/hook" }, basePayload);
|
|
20
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
21
|
-
const [url, opts] = mockFetch.mock.calls[0];
|
|
22
|
-
expect(url).toBe("https://example.com/hook");
|
|
23
|
-
expect(opts.method).toBe("POST");
|
|
24
|
-
expect(opts.headers["Content-Type"]).toBe("application/json");
|
|
25
|
-
const body = JSON.parse(opts.body);
|
|
26
|
-
expect(body.event).toBe("chapter-complete");
|
|
27
|
-
expect(body.bookId).toBe("test-book");
|
|
28
|
-
});
|
|
29
|
-
it("includes HMAC signature when secret is configured", async () => {
|
|
30
|
-
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
31
|
-
const secret = "my-secret-key";
|
|
32
|
-
await sendWebhook({ url: "https://example.com/hook", secret }, basePayload);
|
|
33
|
-
const [, opts] = mockFetch.mock.calls[0];
|
|
34
|
-
const expectedSig = createHmac("sha256", secret)
|
|
35
|
-
.update(opts.body)
|
|
36
|
-
.digest("hex");
|
|
37
|
-
expect(opts.headers["X-InkOS-Signature"]).toBe(`sha256=${expectedSig}`);
|
|
38
|
-
});
|
|
39
|
-
it("skips event when not in subscribed events list", async () => {
|
|
40
|
-
await sendWebhook({ url: "https://example.com/hook", events: ["audit-passed"] }, basePayload);
|
|
41
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
42
|
-
});
|
|
43
|
-
it("sends event when it matches subscribed events list", async () => {
|
|
44
|
-
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
45
|
-
await sendWebhook({ url: "https://example.com/hook", events: ["chapter-complete"] }, basePayload);
|
|
46
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
47
|
-
});
|
|
48
|
-
it("sends all events when events list is empty", async () => {
|
|
49
|
-
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
50
|
-
await sendWebhook({ url: "https://example.com/hook", events: [] }, basePayload);
|
|
51
|
-
expect(mockFetch).toHaveBeenCalledOnce();
|
|
52
|
-
});
|
|
53
|
-
it("throws on non-ok response", async () => {
|
|
54
|
-
mockFetch.mockResolvedValueOnce({
|
|
55
|
-
ok: false,
|
|
56
|
-
status: 500,
|
|
57
|
-
text: async () => "Internal Server Error",
|
|
58
|
-
});
|
|
59
|
-
await expect(sendWebhook({ url: "https://example.com/hook" }, basePayload)).rejects.toThrow("Webhook POST to https://example.com/hook failed: 500");
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
//# sourceMappingURL=webhook.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.test.js","sourceRoot":"","sources":["../../src/__tests__/webhook.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/writer-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { WriterAgent } from "../agents/writer.js";
|
|
3
|
-
const defaultGenreProfile = {
|
|
4
|
-
name: "测试",
|
|
5
|
-
id: "test",
|
|
6
|
-
chapterTypes: [],
|
|
7
|
-
fatigueWords: [],
|
|
8
|
-
numericalSystem: true,
|
|
9
|
-
powerScaling: false,
|
|
10
|
-
eraResearch: false,
|
|
11
|
-
pacingRule: "",
|
|
12
|
-
satisfactionTypes: [],
|
|
13
|
-
auditDimensions: [],
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* WriterAgent.parseOutput is private, so we access it via prototype to test
|
|
17
|
-
* the extraction logic directly without needing to mock the full LLM pipeline.
|
|
18
|
-
*/
|
|
19
|
-
function callParseOutput(chapterNumber, content, genreProfile = defaultGenreProfile) {
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
-
const proto = WriterAgent.prototype;
|
|
22
|
-
return proto.parseOutput.call(null, chapterNumber, content, genreProfile);
|
|
23
|
-
}
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Full tagged output
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
describe("WriterAgent parseOutput", () => {
|
|
28
|
-
const fullOutput = [
|
|
29
|
-
"=== PRE_WRITE_CHECK ===",
|
|
30
|
-
"| 检查项 | 本章记录 | 备注 |",
|
|
31
|
-
"|--------|----------|------|",
|
|
32
|
-
"| 上下文范围 | 第1章 | |",
|
|
33
|
-
"",
|
|
34
|
-
"=== CHAPTER_TITLE ===",
|
|
35
|
-
"吞天之始",
|
|
36
|
-
"",
|
|
37
|
-
"=== CHAPTER_CONTENT ===",
|
|
38
|
-
"陈风站在悬崖边,俯视着脚下的万丈深渊。",
|
|
39
|
-
"一股强烈的吸力从深渊中传来,仿佛有什么东西在召唤他。",
|
|
40
|
-
"",
|
|
41
|
-
"=== POST_SETTLEMENT ===",
|
|
42
|
-
"| 结算项 | 本章记录 | 备注 |",
|
|
43
|
-
"|--------|----------|------|",
|
|
44
|
-
"| 资源账本 | 期初0 / 增量+100 / 期末100 | |",
|
|
45
|
-
"",
|
|
46
|
-
"=== UPDATED_STATE ===",
|
|
47
|
-
"# 状态卡",
|
|
48
|
-
"| 字段 | 值 |",
|
|
49
|
-
"|------|-----|",
|
|
50
|
-
"| 章节 | 1 |",
|
|
51
|
-
"",
|
|
52
|
-
"=== UPDATED_LEDGER ===",
|
|
53
|
-
"# 资源账本",
|
|
54
|
-
"| 章节 | 期初 | 来源 | 增量 | 期末 |",
|
|
55
|
-
"|------|------|------|------|------|",
|
|
56
|
-
"| 1 | 0 | 深渊果实 | +100 | 100 |",
|
|
57
|
-
"",
|
|
58
|
-
"=== UPDATED_HOOKS ===",
|
|
59
|
-
"# 伏笔池",
|
|
60
|
-
"| ID | 伏笔 | 状态 |",
|
|
61
|
-
"|-----|------|------|",
|
|
62
|
-
"| H001 | 深渊之物 | open |",
|
|
63
|
-
].join("\n");
|
|
64
|
-
it("extracts all sections from a complete tagged output", () => {
|
|
65
|
-
const result = callParseOutput(1, fullOutput);
|
|
66
|
-
expect(result.chapterNumber).toBe(1);
|
|
67
|
-
expect(result.title).toBe("吞天之始");
|
|
68
|
-
expect(result.content).toContain("陈风站在悬崖边");
|
|
69
|
-
expect(result.content).toContain("召唤他");
|
|
70
|
-
expect(result.preWriteCheck).toContain("检查项");
|
|
71
|
-
expect(result.postSettlement).toContain("资源账本");
|
|
72
|
-
expect(result.updatedState).toContain("状态卡");
|
|
73
|
-
expect(result.updatedLedger).toContain("深渊果实");
|
|
74
|
-
expect(result.updatedHooks).toContain("H001");
|
|
75
|
-
});
|
|
76
|
-
it("calculates wordCount as the length of chapter content", () => {
|
|
77
|
-
const result = callParseOutput(1, fullOutput);
|
|
78
|
-
const expectedContent = "陈风站在悬崖边,俯视着脚下的万丈深渊。\n一股强烈的吸力从深渊中传来,仿佛有什么东西在召唤他。";
|
|
79
|
-
expect(result.wordCount).toBe(expectedContent.length);
|
|
80
|
-
});
|
|
81
|
-
// -------------------------------------------------------------------------
|
|
82
|
-
// Missing sections
|
|
83
|
-
// -------------------------------------------------------------------------
|
|
84
|
-
it("returns default title when CHAPTER_TITLE is missing", () => {
|
|
85
|
-
const output = [
|
|
86
|
-
"=== CHAPTER_CONTENT ===",
|
|
87
|
-
"Some content here.",
|
|
88
|
-
].join("\n");
|
|
89
|
-
const result = callParseOutput(42, output);
|
|
90
|
-
expect(result.title).toBe("第42章");
|
|
91
|
-
});
|
|
92
|
-
it("returns empty content when CHAPTER_CONTENT is missing", () => {
|
|
93
|
-
const output = [
|
|
94
|
-
"=== CHAPTER_TITLE ===",
|
|
95
|
-
"A Title",
|
|
96
|
-
].join("\n");
|
|
97
|
-
const result = callParseOutput(1, output);
|
|
98
|
-
expect(result.content).toBe("");
|
|
99
|
-
expect(result.wordCount).toBe(0);
|
|
100
|
-
});
|
|
101
|
-
it("returns fallback strings for missing state sections", () => {
|
|
102
|
-
const output = [
|
|
103
|
-
"=== CHAPTER_TITLE ===",
|
|
104
|
-
"Title",
|
|
105
|
-
"",
|
|
106
|
-
"=== CHAPTER_CONTENT ===",
|
|
107
|
-
"Content.",
|
|
108
|
-
].join("\n");
|
|
109
|
-
const result = callParseOutput(1, output);
|
|
110
|
-
expect(result.updatedState).toBe("(状态卡未更新)");
|
|
111
|
-
expect(result.updatedLedger).toBe("(账本未更新)");
|
|
112
|
-
expect(result.updatedHooks).toBe("(伏笔池未更新)");
|
|
113
|
-
});
|
|
114
|
-
it("returns empty string for missing PRE_WRITE_CHECK", () => {
|
|
115
|
-
const output = [
|
|
116
|
-
"=== CHAPTER_TITLE ===",
|
|
117
|
-
"Title",
|
|
118
|
-
"",
|
|
119
|
-
"=== CHAPTER_CONTENT ===",
|
|
120
|
-
"Content.",
|
|
121
|
-
].join("\n");
|
|
122
|
-
const result = callParseOutput(1, output);
|
|
123
|
-
expect(result.preWriteCheck).toBe("");
|
|
124
|
-
});
|
|
125
|
-
it("returns empty string for missing POST_SETTLEMENT", () => {
|
|
126
|
-
const output = [
|
|
127
|
-
"=== CHAPTER_TITLE ===",
|
|
128
|
-
"Title",
|
|
129
|
-
"",
|
|
130
|
-
"=== CHAPTER_CONTENT ===",
|
|
131
|
-
"Content.",
|
|
132
|
-
].join("\n");
|
|
133
|
-
const result = callParseOutput(1, output);
|
|
134
|
-
expect(result.postSettlement).toBe("");
|
|
135
|
-
});
|
|
136
|
-
// -------------------------------------------------------------------------
|
|
137
|
-
// Edge cases
|
|
138
|
-
// -------------------------------------------------------------------------
|
|
139
|
-
it("handles completely empty input", () => {
|
|
140
|
-
const result = callParseOutput(1, "");
|
|
141
|
-
expect(result.chapterNumber).toBe(1);
|
|
142
|
-
expect(result.title).toBe("第1章");
|
|
143
|
-
expect(result.content).toBe("");
|
|
144
|
-
expect(result.wordCount).toBe(0);
|
|
145
|
-
expect(result.updatedState).toBe("(状态卡未更新)");
|
|
146
|
-
expect(result.updatedLedger).toBe("(账本未更新)");
|
|
147
|
-
expect(result.updatedHooks).toBe("(伏笔池未更新)");
|
|
148
|
-
});
|
|
149
|
-
it("handles content with no tags at all", () => {
|
|
150
|
-
const result = callParseOutput(5, "Just some random text without tags");
|
|
151
|
-
expect(result.title).toBe("第5章");
|
|
152
|
-
expect(result.content).toBe("");
|
|
153
|
-
expect(result.wordCount).toBe(0);
|
|
154
|
-
});
|
|
155
|
-
it("preserves multiline content within a section", () => {
|
|
156
|
-
const output = [
|
|
157
|
-
"=== CHAPTER_CONTENT ===",
|
|
158
|
-
"第一段:这里是开头。",
|
|
159
|
-
"",
|
|
160
|
-
"第二段:这里是中间。",
|
|
161
|
-
"",
|
|
162
|
-
"第三段:这里是结尾。",
|
|
163
|
-
"",
|
|
164
|
-
"=== POST_SETTLEMENT ===",
|
|
165
|
-
"No settlement.",
|
|
166
|
-
].join("\n");
|
|
167
|
-
const result = callParseOutput(1, output);
|
|
168
|
-
expect(result.content).toContain("第一段");
|
|
169
|
-
expect(result.content).toContain("第二段");
|
|
170
|
-
expect(result.content).toContain("第三段");
|
|
171
|
-
});
|
|
172
|
-
it("trims whitespace from extracted section values", () => {
|
|
173
|
-
const output = [
|
|
174
|
-
"=== CHAPTER_TITLE ===",
|
|
175
|
-
" 吞天之始 ",
|
|
176
|
-
"",
|
|
177
|
-
"=== CHAPTER_CONTENT ===",
|
|
178
|
-
" 内容 ",
|
|
179
|
-
].join("\n");
|
|
180
|
-
const result = callParseOutput(1, output);
|
|
181
|
-
expect(result.title).toBe("吞天之始");
|
|
182
|
-
expect(result.content).toBe("内容");
|
|
183
|
-
});
|
|
184
|
-
it("correctly counts Chinese characters in wordCount", () => {
|
|
185
|
-
const chineseContent = "这是一段测试文本,包含二十个中文字符加上标点符号。";
|
|
186
|
-
const output = [
|
|
187
|
-
"=== CHAPTER_CONTENT ===",
|
|
188
|
-
chineseContent,
|
|
189
|
-
].join("\n");
|
|
190
|
-
const result = callParseOutput(1, output);
|
|
191
|
-
// wordCount is content.length which counts each character (including punctuation)
|
|
192
|
-
expect(result.wordCount).toBe(chineseContent.length);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
//# sourceMappingURL=writer-parser.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"writer-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/writer-parser.test.ts"],"names":[],"mappings":"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"}
|