llm-mock-server 1.0.4 → 1.0.6
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/.desloppify/query.json +1162 -62
- package/.desloppify/review_packet_blind.json +18 -18
- package/.desloppify/review_packets/holistic_packet_20260315_185401.json +1407 -0
- package/.desloppify/review_packets/holistic_packet_20260315_185613.json +1407 -0
- package/.desloppify/state-typescript.json +2530 -645
- package/.desloppify/state-typescript.json.bak +2494 -582
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-1.log +384 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-10.log +484 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-2.log +408 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-3.log +416 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-4.log +360 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-5.log +360 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-6.log +364 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-7.log +428 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-8.log +388 -0
- package/.desloppify/subagents/runs/20260315_185401/logs/batch-9.log +500 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-1.md +83 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-10.md +108 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-2.md +89 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-3.md +91 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-4.md +77 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-5.md +77 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-6.md +78 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-7.md +94 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-8.md +84 -0
- package/.desloppify/subagents/runs/20260315_185401/prompts/batch-9.md +112 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-1.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-10.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-2.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-3.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-4.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-5.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-6.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-7.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-8.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/results/batch-9.raw.txt +0 -0
- package/.desloppify/subagents/runs/20260315_185401/run.log +36 -0
- package/.desloppify/subagents/runs/20260315_185401/run_summary.json +156 -0
- package/.desloppify/subagents/runs/20260315_185613/holistic_findings_merged.json +741 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-1.log +579 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-10.log +1537 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-2.log +829 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-3.log +927 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-4.log +429 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-5.log +276 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-6.log +450 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-7.log +730 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-8.log +698 -0
- package/.desloppify/subagents/runs/20260315_185613/logs/batch-9.log +938 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-1.md +83 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-10.md +108 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-2.md +89 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-3.md +91 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-4.md +77 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-5.md +77 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-6.md +78 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-7.md +94 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-8.md +84 -0
- package/.desloppify/subagents/runs/20260315_185613/prompts/batch-9.md +112 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-1.raw.txt +78 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-10.raw.txt +242 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-2.raw.txt +102 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-3.raw.txt +94 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-4.raw.txt +86 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-5.raw.txt +1 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-6.raw.txt +87 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-7.raw.txt +1 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-8.raw.txt +107 -0
- package/.desloppify/subagents/runs/20260315_185613/results/batch-9.raw.txt +67 -0
- package/.desloppify/subagents/runs/20260315_185613/run.log +96 -0
- package/.desloppify/subagents/runs/20260315_185613/run_summary.json +156 -0
- package/.github/workflows/docs.yml +46 -0
- package/.github/workflows/test.yml +3 -0
- package/README.md +8 -4
- package/docs/ARCHITECTURE.md +11 -11
- package/package.json +18 -11
- package/scorecard.png +0 -0
- package/src/{cli.ts → cli/cli.ts} +6 -11
- package/src/{cli-validators.ts → cli/validators.ts} +10 -9
- package/src/formats/anthropic/index.ts +2 -2
- package/src/formats/anthropic/parse.ts +5 -2
- package/src/formats/anthropic/serialize.ts +3 -3
- package/src/formats/openai/{index.ts → chat-completions/index.ts} +3 -3
- package/src/formats/openai/{parse.ts → chat-completions/parse.ts} +5 -2
- package/src/formats/openai/{serialize.ts → chat-completions/serialize.ts} +3 -3
- package/src/formats/{responses → openai/responses}/index.ts +2 -2
- package/src/formats/{responses → openai/responses}/parse.ts +5 -2
- package/src/formats/{responses → openai/responses}/serialize.ts +3 -3
- package/src/formats/request-helpers.ts +6 -1
- package/src/formats/serialize-helpers.ts +9 -4
- package/src/formats/types.ts +2 -6
- package/src/history.ts +6 -2
- package/src/loader.ts +2 -1
- package/src/mock-server.ts +55 -106
- package/src/route-handler.ts +7 -11
- package/src/rule-builder.ts +73 -0
- package/src/rule-engine.ts +3 -10
- package/src/sse-writer.ts +1 -1
- package/src/types/reply.ts +51 -8
- package/src/types/request.ts +21 -6
- package/src/types/rule.ts +65 -7
- package/test/cli-validators.test.ts +13 -5
- package/test/formats/openai.test.ts +40 -28
- package/test/formats/responses.test.ts +2 -2
- package/test/history.test.ts +1 -1
- package/test/loader.test.ts +3 -3
- package/test/logger.test.ts +2 -2
- package/test/mock-server.test.ts +1 -1
- package/test/rule-engine.test.ts +1 -1
- package/tsconfig.json +2 -4
- package/typedoc.json +9 -0
- /package/src/formats/openai/{schema.ts → chat-completions/schema.ts} +0 -0
- /package/src/formats/{responses → openai/responses}/schema.ts +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
2026-03-15T18:56:13+00:00 run-start runner=codex parallel=True max_parallel=1 timeout=1200s heartbeat=15.0s stall_warning=0s stall_kill=120s retries=2 retry_backoff=2.0s upper_bound=200m selected=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
2
|
+
2026-03-15T18:56:13+00:00 run-path /Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613
|
|
3
|
+
2026-03-15T18:56:13+00:00 packet /Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packets/holistic_packet_20260315_185613.json
|
|
4
|
+
2026-03-15T18:56:13+00:00 blind-packet /Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packet_blind.json
|
|
5
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=1 position=1/10
|
|
6
|
+
2026-03-15T18:56:13+00:00 batch-start batch=1 position=1/10
|
|
7
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=2 position=2/10
|
|
8
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=3 position=3/10
|
|
9
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=4 position=4/10
|
|
10
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=5 position=5/10
|
|
11
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=6 position=6/10
|
|
12
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=7 position=7/10
|
|
13
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=8 position=8/10
|
|
14
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=9 position=9/10
|
|
15
|
+
2026-03-15T18:56:13+00:00 batch-queued batch=10 position=10/10
|
|
16
|
+
2026-03-15T18:56:28+00:00 heartbeat active=[1] queued=[2, 3, 4, 5, 6, 7, 8, 9, 10] elapsed={1:15}
|
|
17
|
+
2026-03-15T18:56:43+00:00 heartbeat active=[1] queued=[2, 3, 4, 5, 6, 7, 8, 9, 10] elapsed={1:30}
|
|
18
|
+
2026-03-15T18:56:58+00:00 heartbeat active=[1] queued=[2, 3, 4, 5, 6, 7, 8, 9, 10] elapsed={1:45}
|
|
19
|
+
2026-03-15T18:57:02+00:00 batch-start batch=2 position=2/10
|
|
20
|
+
2026-03-15T18:57:02+00:00 batch-done batch=1 position=1/10 code=0 elapsed=49
|
|
21
|
+
2026-03-15T18:57:17+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:15}
|
|
22
|
+
2026-03-15T18:57:32+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:30}
|
|
23
|
+
2026-03-15T18:57:47+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:45}
|
|
24
|
+
2026-03-15T18:58:02+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:60}
|
|
25
|
+
2026-03-15T18:58:17+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:75}
|
|
26
|
+
2026-03-15T18:58:32+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:90}
|
|
27
|
+
2026-03-15T18:58:47+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:105}
|
|
28
|
+
2026-03-15T18:59:02+00:00 heartbeat active=[2] queued=[3, 4, 5, 6, 7, 8, 9, 10] elapsed={2:120}
|
|
29
|
+
2026-03-15T18:59:03+00:00 batch-done batch=2 position=2/10 code=0 elapsed=121
|
|
30
|
+
2026-03-15T18:59:03+00:00 batch-start batch=3 position=3/10
|
|
31
|
+
2026-03-15T18:59:18+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:15}
|
|
32
|
+
2026-03-15T18:59:33+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:30}
|
|
33
|
+
2026-03-15T18:59:48+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:45}
|
|
34
|
+
2026-03-15T19:00:03+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:60}
|
|
35
|
+
2026-03-15T19:00:18+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:75}
|
|
36
|
+
2026-03-15T19:00:33+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:90}
|
|
37
|
+
2026-03-15T19:00:48+00:00 heartbeat active=[3] queued=[4, 5, 6, 7, 8, 9, 10] elapsed={3:105}
|
|
38
|
+
2026-03-15T19:00:54+00:00 batch-done batch=3 position=3/10 code=0 elapsed=110
|
|
39
|
+
2026-03-15T19:00:54+00:00 batch-start batch=4 position=4/10
|
|
40
|
+
2026-03-15T19:01:09+00:00 heartbeat active=[4] queued=[5, 6, 7, 8, 9, 10] elapsed={4:15}
|
|
41
|
+
2026-03-15T19:01:24+00:00 heartbeat active=[4] queued=[5, 6, 7, 8, 9, 10] elapsed={4:30}
|
|
42
|
+
2026-03-15T19:01:39+00:00 heartbeat active=[4] queued=[5, 6, 7, 8, 9, 10] elapsed={4:45}
|
|
43
|
+
2026-03-15T19:01:48+00:00 batch-done batch=4 position=4/10 code=0 elapsed=54
|
|
44
|
+
2026-03-15T19:01:48+00:00 batch-start batch=5 position=5/10
|
|
45
|
+
2026-03-15T19:02:03+00:00 heartbeat active=[5] queued=[6, 7, 8, 9, 10] elapsed={5:15}
|
|
46
|
+
2026-03-15T19:02:18+00:00 heartbeat active=[5] queued=[6, 7, 8, 9, 10] elapsed={5:30}
|
|
47
|
+
2026-03-15T19:02:28+00:00 batch-start batch=6 position=6/10
|
|
48
|
+
2026-03-15T19:02:28+00:00 batch-done batch=5 position=5/10 code=0 elapsed=40
|
|
49
|
+
2026-03-15T19:02:43+00:00 heartbeat active=[6] queued=[7, 8, 9, 10] elapsed={6:15}
|
|
50
|
+
2026-03-15T19:02:58+00:00 heartbeat active=[6] queued=[7, 8, 9, 10] elapsed={6:30}
|
|
51
|
+
2026-03-15T19:03:13+00:00 heartbeat active=[6] queued=[7, 8, 9, 10] elapsed={6:45}
|
|
52
|
+
2026-03-15T19:03:19+00:00 batch-start batch=7 position=7/10
|
|
53
|
+
2026-03-15T19:03:19+00:00 batch-done batch=6 position=6/10 code=0 elapsed=50
|
|
54
|
+
2026-03-15T19:03:34+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:15}
|
|
55
|
+
2026-03-15T19:03:49+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:30}
|
|
56
|
+
2026-03-15T19:04:04+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:45}
|
|
57
|
+
2026-03-15T19:04:19+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:60}
|
|
58
|
+
2026-03-15T19:04:34+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:75}
|
|
59
|
+
2026-03-15T19:04:49+00:00 heartbeat active=[7] queued=[8, 9, 10] elapsed={7:90}
|
|
60
|
+
2026-03-15T19:05:02+00:00 batch-done batch=7 position=7/10 code=0 elapsed=103
|
|
61
|
+
2026-03-15T19:05:02+00:00 batch-start batch=8 position=8/10
|
|
62
|
+
2026-03-15T19:05:17+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:15}
|
|
63
|
+
2026-03-15T19:05:32+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:30}
|
|
64
|
+
2026-03-15T19:05:47+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:45}
|
|
65
|
+
2026-03-15T19:06:02+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:60}
|
|
66
|
+
2026-03-15T19:06:17+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:75}
|
|
67
|
+
2026-03-15T19:06:32+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:90}
|
|
68
|
+
2026-03-15T19:06:47+00:00 heartbeat active=[8] queued=[9, 10] elapsed={8:105}
|
|
69
|
+
2026-03-15T19:06:51+00:00 batch-start batch=9 position=9/10
|
|
70
|
+
2026-03-15T19:06:51+00:00 batch-done batch=8 position=8/10 code=0 elapsed=108
|
|
71
|
+
2026-03-15T19:07:06+00:00 heartbeat active=[9] queued=[10] elapsed={9:15}
|
|
72
|
+
2026-03-15T19:07:21+00:00 heartbeat active=[9] queued=[10] elapsed={9:30}
|
|
73
|
+
2026-03-15T19:07:36+00:00 heartbeat active=[9] queued=[10] elapsed={9:45}
|
|
74
|
+
2026-03-15T19:07:51+00:00 heartbeat active=[9] queued=[10] elapsed={9:60}
|
|
75
|
+
2026-03-15T19:08:06+00:00 heartbeat active=[9] queued=[10] elapsed={9:75}
|
|
76
|
+
2026-03-15T19:08:21+00:00 heartbeat active=[9] queued=[10] elapsed={9:90}
|
|
77
|
+
2026-03-15T19:08:35+00:00 batch-done batch=9 position=9/10 code=0 elapsed=103
|
|
78
|
+
2026-03-15T19:08:35+00:00 batch-start batch=10 position=10/10
|
|
79
|
+
2026-03-15T19:08:50+00:00 heartbeat active=[10] queued=[] elapsed={10:15}
|
|
80
|
+
2026-03-15T19:09:05+00:00 heartbeat active=[10] queued=[] elapsed={10:30}
|
|
81
|
+
2026-03-15T19:09:20+00:00 heartbeat active=[10] queued=[] elapsed={10:45}
|
|
82
|
+
2026-03-15T19:09:35+00:00 heartbeat active=[10] queued=[] elapsed={10:60}
|
|
83
|
+
2026-03-15T19:09:50+00:00 heartbeat active=[10] queued=[] elapsed={10:75}
|
|
84
|
+
2026-03-15T19:10:05+00:00 heartbeat active=[10] queued=[] elapsed={10:90}
|
|
85
|
+
2026-03-15T19:10:20+00:00 heartbeat active=[10] queued=[] elapsed={10:105}
|
|
86
|
+
2026-03-15T19:10:35+00:00 heartbeat active=[10] queued=[] elapsed={10:120}
|
|
87
|
+
2026-03-15T19:10:50+00:00 heartbeat active=[10] queued=[] elapsed={10:135}
|
|
88
|
+
2026-03-15T19:11:05+00:00 heartbeat active=[10] queued=[] elapsed={10:150}
|
|
89
|
+
2026-03-15T19:11:20+00:00 heartbeat active=[10] queued=[] elapsed={10:165}
|
|
90
|
+
2026-03-15T19:11:35+00:00 heartbeat active=[10] queued=[] elapsed={10:180}
|
|
91
|
+
2026-03-15T19:11:50+00:00 heartbeat active=[10] queued=[] elapsed={10:195}
|
|
92
|
+
2026-03-15T19:12:05+00:00 heartbeat active=[10] queued=[] elapsed={10:210}
|
|
93
|
+
2026-03-15T19:12:20+00:00 heartbeat active=[10] queued=[] elapsed={10:225}
|
|
94
|
+
2026-03-15T19:12:25+00:00 batch-done batch=10 position=10/10 code=0 elapsed=230
|
|
95
|
+
2026-03-15T19:12:25+00:00 run-summary /Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/run_summary.json
|
|
96
|
+
2026-03-15T19:12:25+00:00 run-finished successful=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] failed=[] imported=/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/holistic_findings_merged.json
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"created_at": "2026-03-15T18:56:13+00:00",
|
|
3
|
+
"run_stamp": "20260315_185613",
|
|
4
|
+
"runner": "codex",
|
|
5
|
+
"parallel": true,
|
|
6
|
+
"selected_batches": [
|
|
7
|
+
1,
|
|
8
|
+
2,
|
|
9
|
+
3,
|
|
10
|
+
4,
|
|
11
|
+
5,
|
|
12
|
+
6,
|
|
13
|
+
7,
|
|
14
|
+
8,
|
|
15
|
+
9,
|
|
16
|
+
10
|
|
17
|
+
],
|
|
18
|
+
"successful_batches": [
|
|
19
|
+
1,
|
|
20
|
+
2,
|
|
21
|
+
3,
|
|
22
|
+
4,
|
|
23
|
+
5,
|
|
24
|
+
6,
|
|
25
|
+
7,
|
|
26
|
+
8,
|
|
27
|
+
9,
|
|
28
|
+
10
|
|
29
|
+
],
|
|
30
|
+
"failed_batches": [],
|
|
31
|
+
"allow_partial": false,
|
|
32
|
+
"max_parallel_batches": 1,
|
|
33
|
+
"batch_timeout_seconds": 1200,
|
|
34
|
+
"batch_max_retries": 2,
|
|
35
|
+
"batch_retry_backoff_seconds": 2.0,
|
|
36
|
+
"batch_heartbeat_seconds": 15.0,
|
|
37
|
+
"batch_stall_warning_seconds": 0,
|
|
38
|
+
"batch_stall_kill_seconds": 120,
|
|
39
|
+
"immutable_packet": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packets/holistic_packet_20260315_185613.json",
|
|
40
|
+
"blind_packet": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/review_packet_blind.json",
|
|
41
|
+
"run_dir": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613",
|
|
42
|
+
"logs_dir": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs",
|
|
43
|
+
"run_log": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/run.log",
|
|
44
|
+
"batches": {
|
|
45
|
+
"1": {
|
|
46
|
+
"position": 1,
|
|
47
|
+
"status": "succeeded",
|
|
48
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-1.md",
|
|
49
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-1.raw.txt",
|
|
50
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-1.log",
|
|
51
|
+
"started_at": "2026-03-15T18:56:13+00:00",
|
|
52
|
+
"elapsed_seconds": 49,
|
|
53
|
+
"exit_code": 0,
|
|
54
|
+
"completed_at": "2026-03-15T18:57:02+00:00"
|
|
55
|
+
},
|
|
56
|
+
"2": {
|
|
57
|
+
"position": 2,
|
|
58
|
+
"status": "succeeded",
|
|
59
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-2.md",
|
|
60
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-2.raw.txt",
|
|
61
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-2.log",
|
|
62
|
+
"started_at": "2026-03-15T18:57:02+00:00",
|
|
63
|
+
"elapsed_seconds": 121,
|
|
64
|
+
"exit_code": 0,
|
|
65
|
+
"completed_at": "2026-03-15T18:59:03+00:00"
|
|
66
|
+
},
|
|
67
|
+
"3": {
|
|
68
|
+
"position": 3,
|
|
69
|
+
"status": "succeeded",
|
|
70
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-3.md",
|
|
71
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-3.raw.txt",
|
|
72
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-3.log",
|
|
73
|
+
"started_at": "2026-03-15T18:59:03+00:00",
|
|
74
|
+
"elapsed_seconds": 110,
|
|
75
|
+
"exit_code": 0,
|
|
76
|
+
"completed_at": "2026-03-15T19:00:54+00:00"
|
|
77
|
+
},
|
|
78
|
+
"4": {
|
|
79
|
+
"position": 4,
|
|
80
|
+
"status": "succeeded",
|
|
81
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-4.md",
|
|
82
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-4.raw.txt",
|
|
83
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-4.log",
|
|
84
|
+
"started_at": "2026-03-15T19:00:54+00:00",
|
|
85
|
+
"elapsed_seconds": 54,
|
|
86
|
+
"exit_code": 0,
|
|
87
|
+
"completed_at": "2026-03-15T19:01:48+00:00"
|
|
88
|
+
},
|
|
89
|
+
"5": {
|
|
90
|
+
"position": 5,
|
|
91
|
+
"status": "succeeded",
|
|
92
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-5.md",
|
|
93
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-5.raw.txt",
|
|
94
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-5.log",
|
|
95
|
+
"started_at": "2026-03-15T19:01:48+00:00",
|
|
96
|
+
"elapsed_seconds": 40,
|
|
97
|
+
"exit_code": 0,
|
|
98
|
+
"completed_at": "2026-03-15T19:02:28+00:00"
|
|
99
|
+
},
|
|
100
|
+
"6": {
|
|
101
|
+
"position": 6,
|
|
102
|
+
"status": "succeeded",
|
|
103
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-6.md",
|
|
104
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-6.raw.txt",
|
|
105
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-6.log",
|
|
106
|
+
"started_at": "2026-03-15T19:02:28+00:00",
|
|
107
|
+
"elapsed_seconds": 50,
|
|
108
|
+
"exit_code": 0,
|
|
109
|
+
"completed_at": "2026-03-15T19:03:19+00:00"
|
|
110
|
+
},
|
|
111
|
+
"7": {
|
|
112
|
+
"position": 7,
|
|
113
|
+
"status": "succeeded",
|
|
114
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-7.md",
|
|
115
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-7.raw.txt",
|
|
116
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-7.log",
|
|
117
|
+
"started_at": "2026-03-15T19:03:19+00:00",
|
|
118
|
+
"elapsed_seconds": 103,
|
|
119
|
+
"exit_code": 0,
|
|
120
|
+
"completed_at": "2026-03-15T19:05:02+00:00"
|
|
121
|
+
},
|
|
122
|
+
"8": {
|
|
123
|
+
"position": 8,
|
|
124
|
+
"status": "succeeded",
|
|
125
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-8.md",
|
|
126
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-8.raw.txt",
|
|
127
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-8.log",
|
|
128
|
+
"started_at": "2026-03-15T19:05:02+00:00",
|
|
129
|
+
"elapsed_seconds": 108,
|
|
130
|
+
"exit_code": 0,
|
|
131
|
+
"completed_at": "2026-03-15T19:06:51+00:00"
|
|
132
|
+
},
|
|
133
|
+
"9": {
|
|
134
|
+
"position": 9,
|
|
135
|
+
"status": "succeeded",
|
|
136
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-9.md",
|
|
137
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-9.raw.txt",
|
|
138
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-9.log",
|
|
139
|
+
"started_at": "2026-03-15T19:06:51+00:00",
|
|
140
|
+
"elapsed_seconds": 103,
|
|
141
|
+
"exit_code": 0,
|
|
142
|
+
"completed_at": "2026-03-15T19:08:35+00:00"
|
|
143
|
+
},
|
|
144
|
+
"10": {
|
|
145
|
+
"position": 10,
|
|
146
|
+
"status": "succeeded",
|
|
147
|
+
"prompt_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/prompts/batch-10.md",
|
|
148
|
+
"result_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/results/batch-10.raw.txt",
|
|
149
|
+
"log_path": "/Users/suyash.x.srijan/Documents/Personal_Projects/llm-mock-server/.desloppify/subagents/runs/20260315_185613/logs/batch-10.log",
|
|
150
|
+
"started_at": "2026-03-15T19:08:35+00:00",
|
|
151
|
+
"elapsed_seconds": 230,
|
|
152
|
+
"exit_code": 0,
|
|
153
|
+
"completed_at": "2026-03-15T19:12:25+00:00"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Deploy API docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: pages
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment:
|
|
20
|
+
name: github-pages
|
|
21
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@v6.0.2
|
|
26
|
+
|
|
27
|
+
- name: Setup Node.js
|
|
28
|
+
uses: actions/setup-node@v6.3.0
|
|
29
|
+
with:
|
|
30
|
+
node-version-file: 'package.json'
|
|
31
|
+
cache: 'npm'
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: npm ci
|
|
35
|
+
|
|
36
|
+
- name: Generate docs
|
|
37
|
+
run: npm run docs
|
|
38
|
+
|
|
39
|
+
- name: Upload pages artifact
|
|
40
|
+
uses: actions/upload-pages-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
path: docs/api
|
|
43
|
+
|
|
44
|
+
- name: Deploy to GitHub Pages
|
|
45
|
+
id: deployment
|
|
46
|
+
uses: actions/deploy-pages@v4
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# llm-mock-server
|
|
1
|
+
# llm-mock-server [](https://www.npmjs.com/package/llm-mock-server) 
|
|
2
2
|
|
|
3
|
-
A mock LLM server for testing. It handles OpenAI `/chat/completions`, Anthropic `/messages`, and OpenAI `/responses` API formats, with both streaming (SSE) and non-streaming responses. Point any client at it and get instant, deterministic replies.
|
|
3
|
+
A mock LLM server for testing. It handles OpenAI `/chat/completions`, Anthropic `/messages`, and OpenAI `/responses` API formats, with both streaming (SSE) and non-streaming responses. Point any client at it and get instant, deterministic replies. Used by [xcode-copilot-server](https://github.com/theblixguy/xcode-copilot-server) and [copilot-sdk-proxy](https://github.com/theblixguy/copilot-sdk-proxy) for their integration tests.
|
|
4
4
|
|
|
5
5
|
## Table of contents
|
|
6
6
|
|
|
@@ -25,6 +25,7 @@ A mock LLM server for testing. It handles OpenAI `/chat/completions`, Anthropic
|
|
|
25
25
|
- [CLI](#cli)
|
|
26
26
|
- [Security](#security)
|
|
27
27
|
- [Architecture](#architecture)
|
|
28
|
+
- [API reference](#api-reference)
|
|
28
29
|
- [Licence](#licence)
|
|
29
30
|
|
|
30
31
|
## Quick start
|
|
@@ -390,7 +391,6 @@ llm-mock-server [options]
|
|
|
390
391
|
| `--port` | `-p` | `5555` | Port to listen on |
|
|
391
392
|
| `--host` | `-H` | `127.0.0.1` | Host to bind to |
|
|
392
393
|
| `--rules` | `-r` | | Path to rules file or directory |
|
|
393
|
-
| `--handler` | | | Path to handler file |
|
|
394
394
|
| `--latency` | `-l` | `0` | Ms between SSE chunks |
|
|
395
395
|
| `--chunk-size` | `-c` | `0` | Characters per SSE chunk |
|
|
396
396
|
| `--fallback` | `-f` | | Fallback reply text |
|
|
@@ -410,7 +410,7 @@ This is a testing tool, not a production service. It's designed to run locally o
|
|
|
410
410
|
|
|
411
411
|
### Handler files execute code
|
|
412
412
|
|
|
413
|
-
When you call `server.load()` or pass `--
|
|
413
|
+
When you call `server.load()` or pass `--rules` on the CLI, `.ts`/`.js` files are loaded via dynamic `import()`. They run with the same permissions as the rest of your Node.js process. Only load files you trust.
|
|
414
414
|
|
|
415
415
|
### JSON5 rule files are data only
|
|
416
416
|
|
|
@@ -428,6 +428,10 @@ Request bodies are capped at 1 MB by Fastify's default. Responses are serialised
|
|
|
428
428
|
|
|
429
429
|
See [ARCHITECTURE.md](docs/ARCHITECTURE.md) for how the codebase is structured, the request lifecycle, rule matching, and response serialisation.
|
|
430
430
|
|
|
431
|
+
## API reference
|
|
432
|
+
|
|
433
|
+
Full API docs are available [here](https://theblixguy.github.io/llm-mock-server/).
|
|
434
|
+
|
|
431
435
|
## Licence
|
|
432
436
|
|
|
433
437
|
MIT License
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# llm-mock-server architecture
|
|
2
2
|
|
|
3
|
-
A mock LLM server built on Fastify. Clients send requests in OpenAI, Anthropic, or Responses API format. The server normalises them into a common shape, matches against registered rules, and sends back responses in the right format.
|
|
3
|
+
A mock LLM server built on Fastify. Clients send requests in OpenAI Chat Completions, Anthropic Messages, or OpenAI Responses API format. The server normalises them into a common shape, matches against registered rules, and sends back responses in the right format.
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
flowchart LR
|
|
@@ -8,7 +8,7 @@ flowchart LR
|
|
|
8
8
|
Server["llm-mock-server<br/>(Fastify)"]
|
|
9
9
|
Rules["Rule engine"]
|
|
10
10
|
|
|
11
|
-
Client <-->|"
|
|
11
|
+
Client <-->|"Chat Completions / Messages / Responses"| Server
|
|
12
12
|
Server <--> Rules
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -26,9 +26,9 @@ flowchart LR
|
|
|
26
26
|
|
|
27
27
|
## Startup
|
|
28
28
|
|
|
29
|
-
[`cli.ts`](src/cli.ts) is the CLI entry point. It parses flags with Commander, validates them through [`
|
|
29
|
+
[`cli.ts`](src/cli/cli.ts) is the CLI entry point. It parses flags with Commander, validates them through [`validators.ts`](src/cli/validators.ts), creates a `MockServer`, loads any rule files, and handles SIGINT/SIGTERM. With `--watch`, it sets up `fs.watch()` on the rules path and reloads on changes.
|
|
30
30
|
|
|
31
|
-
[`MockServer`](src/mock-server.ts) is the main class. The constructor creates a Fastify instance and registers a route handler for each format. Consumers interact with it through
|
|
31
|
+
[`MockServer`](src/mock-server.ts) is the main class. The constructor creates a Fastify instance and registers a route handler for each format. Rule authoring (`when()`, `whenTool()`, `whenToolResult()`, `nextError()`) lives in [`RuleBuilder`](src/rule-builder.ts) and is proxied onto `MockServer` via `.bind()`. Consumers interact with it through the rule methods, `fallback()`, `load()`, and the lifecycle methods.
|
|
32
32
|
|
|
33
33
|
[`createMock()`](src/index.ts) is a convenience that creates a server and starts it in one call.
|
|
34
34
|
|
|
@@ -40,9 +40,9 @@ Three formats are included:
|
|
|
40
40
|
|
|
41
41
|
| Format | Route | Directory |
|
|
42
42
|
| ------ | ----- | --------- |
|
|
43
|
-
| OpenAI | `POST /v1/chat/completions` | [`formats/openai/`](src/formats/openai/) |
|
|
44
|
-
| Anthropic | `POST /v1/messages` | [`formats/anthropic/`](src/formats/anthropic/) |
|
|
45
|
-
| OpenAI Responses | `POST /v1/responses` | [`formats/responses/`](src/formats/responses/) |
|
|
43
|
+
| OpenAI Chat Completions | `POST /v1/chat/completions` | [`formats/openai/chat-completions/`](src/formats/openai/chat-completions/) |
|
|
44
|
+
| Anthropic Messages | `POST /v1/messages` | [`formats/anthropic/`](src/formats/anthropic/) |
|
|
45
|
+
| OpenAI Responses | `POST /v1/responses` | [`formats/openai/responses/`](src/formats/openai/responses/) |
|
|
46
46
|
|
|
47
47
|
Each format directory has three files:
|
|
48
48
|
|
|
@@ -50,7 +50,7 @@ Each format directory has three files:
|
|
|
50
50
|
- `serialize.ts` takes a [`ReplyObject`](src/types/reply.ts) and produces SSE chunks or a JSON response
|
|
51
51
|
- `index.ts` wires parse and serialize together into a `Format` object
|
|
52
52
|
|
|
53
|
-
Shared helpers
|
|
53
|
+
Shared helpers are split into request and response files: [`request-helpers.ts`](src/formats/request-helpers.ts) has `buildMockRequest()` and `isStreaming()` (used by parsers), and [`serialize-helpers.ts`](src/formats/serialize-helpers.ts) has `genId()`, `splitText()`, `shouldEmitText()`, and `finishReason()` (used by serialisers).
|
|
54
54
|
|
|
55
55
|
To add a new format (say, Gemini), you would create a new directory with those three files and add it to the `formats` array in `mock-server.ts`. Everything else (rule matching, streaming, logging, history) works automatically.
|
|
56
56
|
|
|
@@ -91,7 +91,7 @@ Types are split across three files in [`src/types/`](src/types/):
|
|
|
91
91
|
- [`reply.ts`](src/types/reply.ts) has `Reply`, `ReplyObject`, `ErrorReply`, `ToolCall`, `Resolver`, and `ReplyOptions`
|
|
92
92
|
- [`rule.ts`](src/types/rule.ts) has `Match`, `MatchObject`, `PendingRule`, `RuleHandle`, `RuleSummary`, `Handler`, and `Rule`
|
|
93
93
|
|
|
94
|
-
[`src/types.ts`](src/types.ts) re-exports everything
|
|
94
|
+
[`src/types.ts`](src/types.ts) re-exports everything as a barrel for the public API. Internal modules import directly from the leaf files.
|
|
95
95
|
|
|
96
96
|
## Streaming
|
|
97
97
|
|
|
@@ -116,10 +116,10 @@ Directories are read and processed in sorted order.
|
|
|
116
116
|
|
|
117
117
|
The threat model is simple: the server runs locally or in CI, loading files written by the developer. There is no multi-tenant isolation or sandboxing.
|
|
118
118
|
|
|
119
|
-
Handler files (`.ts`, `.js`, `.mjs`) are loaded via `import()` and execute with full process permissions. The trust boundary is the file system path passed to `load()` or `--
|
|
119
|
+
Handler files (`.ts`, `.js`, `.mjs`) are loaded via `import()` and execute with full process permissions. The trust boundary is the file system path passed to `load()` or `--rules`. If an attacker can write to that path, they already have code execution on the machine. No path restriction is enforced because legitimate setups often load rules from outside the project directory.
|
|
120
120
|
|
|
121
121
|
JSON5 files go through Zod validation and never execute code. The only dynamic construction is `new RegExp()` for regex patterns in rule files, which could hang on pathological backtracking patterns but poses no injection risk.
|
|
122
122
|
|
|
123
123
|
Fastify caps request bodies at 1 MB by default. The server binds to `127.0.0.1` unless explicitly configured otherwise. Responses are serialised through JSON, so reply text cannot break out of SSE framing.
|
|
124
124
|
|
|
125
|
-
CLI inputs (port, host, latency, log level) are validated through [`
|
|
125
|
+
CLI inputs (port, host, latency, log level) are validated through [`validators.ts`](src/cli/validators.ts) before use.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-mock-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "A standalone mock LLM server for deterministic testing: OpenAI, Anthropic, and Responses API formats",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
},
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
|
+
"imports": {
|
|
19
|
+
"#/*": "./src/*"
|
|
20
|
+
},
|
|
18
21
|
"bin": {
|
|
19
|
-
"llm-mock-server": "./dist/cli.js"
|
|
22
|
+
"llm-mock-server": "./dist/cli/cli.js"
|
|
20
23
|
},
|
|
21
24
|
"scripts": {
|
|
22
25
|
"build": "tsc",
|
|
@@ -26,8 +29,10 @@
|
|
|
26
29
|
"test": "vitest run",
|
|
27
30
|
"test:watch": "vitest",
|
|
28
31
|
"check": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && npm run fmt:check && npm run lint && npm test",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
32
|
+
"docs": "typedoc",
|
|
33
|
+
"docs:check": "typedoc --emit none",
|
|
34
|
+
"dev": "tsx src/cli/cli.ts",
|
|
35
|
+
"start": "node dist/cli/cli.js"
|
|
31
36
|
},
|
|
32
37
|
"keywords": [
|
|
33
38
|
"llm",
|
|
@@ -41,19 +46,21 @@
|
|
|
41
46
|
"license": "MIT",
|
|
42
47
|
"dependencies": {
|
|
43
48
|
"commander": "14.0.3",
|
|
44
|
-
"fastify": "5.8.
|
|
49
|
+
"fastify": "5.8.4",
|
|
45
50
|
"json5": "2.2.3",
|
|
46
|
-
"llm-schemas": "1.0.
|
|
51
|
+
"llm-schemas": "1.0.2",
|
|
47
52
|
"picocolors": "1.1.1",
|
|
48
53
|
"zod": "4.3.6"
|
|
49
54
|
},
|
|
50
55
|
"devDependencies": {
|
|
51
56
|
"@types/node": "25.5.0",
|
|
52
|
-
"@vitest/coverage-v8": "4.1.
|
|
53
|
-
"oxfmt": "0.
|
|
54
|
-
"oxlint": "1.
|
|
57
|
+
"@vitest/coverage-v8": "4.1.2",
|
|
58
|
+
"oxfmt": "0.42.0",
|
|
59
|
+
"oxlint": "1.57.0",
|
|
55
60
|
"tsx": "4.21.0",
|
|
56
|
-
"
|
|
57
|
-
"
|
|
61
|
+
"typedoc": "0.28.18",
|
|
62
|
+
"typedoc-theme-oxide": "0.2.5",
|
|
63
|
+
"typescript": "6.0.2",
|
|
64
|
+
"vitest": "4.1.2"
|
|
58
65
|
}
|
|
59
66
|
}
|
package/scorecard.png
CHANGED
|
Binary file
|
|
@@ -4,18 +4,18 @@ import { watch } from "node:fs";
|
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import pc from "picocolors";
|
|
7
|
-
import { MockServer } from "
|
|
8
|
-
import { Logger } from "
|
|
7
|
+
import { MockServer } from "#/mock-server.js";
|
|
8
|
+
import { Logger } from "#/logger.js";
|
|
9
9
|
import {
|
|
10
10
|
parsePort,
|
|
11
11
|
parseHost,
|
|
12
12
|
parseLogLevel,
|
|
13
13
|
parseChunkSize,
|
|
14
14
|
parseLatency,
|
|
15
|
-
} from "./
|
|
15
|
+
} from "./validators.js";
|
|
16
16
|
|
|
17
17
|
const require = createRequire(import.meta.url);
|
|
18
|
-
const { version } = require("
|
|
18
|
+
const { version } = require("../../package.json") as { version: string };
|
|
19
19
|
|
|
20
20
|
const WATCH_DEBOUNCE_MS = 100;
|
|
21
21
|
|
|
@@ -23,7 +23,6 @@ interface StartOptions {
|
|
|
23
23
|
port: string;
|
|
24
24
|
host: string;
|
|
25
25
|
rules?: string;
|
|
26
|
-
handler?: string;
|
|
27
26
|
latency: string;
|
|
28
27
|
chunkSize: string;
|
|
29
28
|
fallback?: string;
|
|
@@ -54,9 +53,6 @@ async function start(options: StartOptions): Promise<void> {
|
|
|
54
53
|
if (options.rules) {
|
|
55
54
|
await server.load(options.rules);
|
|
56
55
|
}
|
|
57
|
-
if (options.handler) {
|
|
58
|
-
await server.load(options.handler);
|
|
59
|
-
}
|
|
60
56
|
|
|
61
57
|
const quiet = logLevel === "none";
|
|
62
58
|
|
|
@@ -78,7 +74,7 @@ async function start(options: StartOptions): Promise<void> {
|
|
|
78
74
|
);
|
|
79
75
|
}
|
|
80
76
|
console.log(
|
|
81
|
-
` ${pc.dim("Endpoints")} ${
|
|
77
|
+
` ${pc.dim("Endpoints")} ${server.routes.map((r) => pc.green(r)).join(", ")}`,
|
|
82
78
|
);
|
|
83
79
|
console.log();
|
|
84
80
|
}
|
|
@@ -131,8 +127,7 @@ program
|
|
|
131
127
|
.description("Start the mock server")
|
|
132
128
|
.option("-p, --port <number>", "port to listen on", "5555")
|
|
133
129
|
.option("-H, --host <address>", "host to bind to", "127.0.0.1")
|
|
134
|
-
.option("-r, --rules <path>", "path to
|
|
135
|
-
.option("--handler <path>", "path to .ts handler file")
|
|
130
|
+
.option("-r, --rules <path>", "path to rules file or directory")
|
|
136
131
|
.option("-l, --latency <ms>", "latency between SSE chunks (ms)", "0")
|
|
137
132
|
.option("-c, --chunk-size <chars>", "characters per SSE chunk", "0")
|
|
138
133
|
.option("-f, --fallback <text>", "fallback reply text")
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { isIP } from "node:net";
|
|
2
2
|
import { lookup } from "node:dns/promises";
|
|
3
|
-
import { LEVEL_PRIORITY, type LogLevel } from "
|
|
3
|
+
import { LEVEL_PRIORITY, type LogLevel } from "#/logger.js";
|
|
4
4
|
|
|
5
|
-
const VALID_LOG_LEVELS = Object.keys(LEVEL_PRIORITY)
|
|
5
|
+
const VALID_LOG_LEVELS: string[] = Object.keys(LEVEL_PRIORITY);
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function parseStrictInt(value: string): number {
|
|
8
|
+
if (!/^\d+$/.test(value)) return NaN;
|
|
9
|
+
return Number(value);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const MAX_PORT = 65535;
|
|
12
13
|
|
|
13
14
|
export function parsePort(value: string): number {
|
|
14
|
-
const port =
|
|
15
|
+
const port = parseStrictInt(value);
|
|
15
16
|
if (isNaN(port) || port < 1 || port > MAX_PORT) {
|
|
16
17
|
throw new Error(`Invalid port "${value}". Must be 1-${String(MAX_PORT)}.`);
|
|
17
18
|
}
|
|
@@ -19,12 +20,12 @@ export function parsePort(value: string): number {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export function parseLogLevel(value: string): LogLevel {
|
|
22
|
-
if (!
|
|
23
|
+
if (!VALID_LOG_LEVELS.includes(value)) {
|
|
23
24
|
throw new Error(
|
|
24
25
|
`Invalid log level "${value}". Valid: ${VALID_LOG_LEVELS.join(", ")}`,
|
|
25
26
|
);
|
|
26
27
|
}
|
|
27
|
-
return value;
|
|
28
|
+
return value as LogLevel;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export async function parseHost(value: string): Promise<string> {
|
|
@@ -47,7 +48,7 @@ export async function parseHost(value: string): Promise<string> {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export function parseChunkSize(value: string): number {
|
|
50
|
-
const size =
|
|
51
|
+
const size = parseStrictInt(value);
|
|
51
52
|
if (isNaN(size) || size < 0) {
|
|
52
53
|
throw new Error(
|
|
53
54
|
`Invalid chunk size "${value}". Must be a non-negative integer.`,
|
|
@@ -57,7 +58,7 @@ export function parseChunkSize(value: string): number {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export function parseLatency(value: string): number {
|
|
60
|
-
const ms =
|
|
61
|
+
const ms = parseStrictInt(value);
|
|
61
62
|
if (isNaN(ms) || ms < 0) {
|
|
62
63
|
throw new Error(
|
|
63
64
|
`Invalid latency "${value}". Must be a non-negative integer (ms).`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Format } from "
|
|
2
|
-
import { isStreaming } from "
|
|
1
|
+
import type { Format } from "#/formats/types.js";
|
|
2
|
+
import { isStreaming } from "#/formats/request-helpers.js";
|
|
3
3
|
import { parseRequest } from "./parse.js";
|
|
4
4
|
import { serialize, serializeComplete, serializeError } from "./serialize.js";
|
|
5
5
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { MockRequest, Message, ToolDef } from "
|
|
2
|
-
import {
|
|
1
|
+
import type { MockRequest, Message, ToolDef } from "#/types/request.js";
|
|
2
|
+
import {
|
|
3
|
+
buildMockRequest,
|
|
4
|
+
type RequestMeta,
|
|
5
|
+
} from "#/formats/request-helpers.js";
|
|
3
6
|
import { AnthropicRequestSchema, type AnthropicRequest } from "./schema.js";
|
|
4
7
|
|
|
5
8
|
function extractSystem(system: AnthropicRequest["system"]): Message[] {
|