waypoi 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/.github/instructions/ui.instructions.md +42 -0
  2. package/.github/workflows/ci.yml +35 -0
  3. package/.github/workflows/publish.yml +71 -0
  4. package/.github/workflows/release.yml +48 -0
  5. package/.playwright-mcp/console-2026-04-04T01-41-10-746Z.log +2 -0
  6. package/.playwright-mcp/console-2026-04-04T01-41-28-799Z.log +3 -0
  7. package/.playwright-mcp/console-2026-04-05T02-26-51-909Z.log +76 -0
  8. package/.playwright-mcp/page-2026-04-04T01-41-10-816Z.yml +1 -0
  9. package/.playwright-mcp/page-2026-04-04T01-41-29-141Z.yml +77 -0
  10. package/.playwright-mcp/page-2026-04-04T01-41-42-633Z.yml +190 -0
  11. package/.playwright-mcp/page-2026-04-04T01-42-03-929Z.yml +262 -0
  12. package/.playwright-mcp/page-2026-04-04T02-12-54-813Z.yml +6 -0
  13. package/.playwright-mcp/page-2026-04-04T02-14-58-600Z.yml +190 -0
  14. package/.playwright-mcp/page-2026-04-04T02-15-03-923Z.yml +190 -0
  15. package/.playwright-mcp/page-2026-04-04T02-15-07-426Z.yml +190 -0
  16. package/.playwright-mcp/page-2026-04-04T02-15-25-729Z.yml +262 -0
  17. package/.playwright-mcp/page-2026-04-04T02-16-22-984Z.yml +262 -0
  18. package/.playwright-mcp/page-2026-04-04T02-17-00-599Z.yml +190 -0
  19. package/.playwright-mcp/page-2026-04-04T02-17-50-874Z.yml +190 -0
  20. package/.playwright-mcp/page-2026-04-05T02-26-55-570Z.yml +6 -0
  21. package/AGENTS.md +48 -0
  22. package/CHANGELOG.md +131 -0
  23. package/README.md +552 -0
  24. package/assets/agent-mode.png +0 -0
  25. package/assets/categorize.png +0 -0
  26. package/assets/dashboard.png +0 -0
  27. package/assets/endpoint-proxy.png +0 -0
  28. package/assets/icon.png +0 -0
  29. package/assets/mcp-generate-image.png +0 -0
  30. package/assets/mcp-understand-image.png +0 -0
  31. package/assets/peek-token-flow.png +0 -0
  32. package/assets/playground.png +0 -0
  33. package/assets/sankey.png +0 -0
  34. package/cli/index.ts +2805 -0
  35. package/cli/legacyRewrite.ts +108 -0
  36. package/cli/modelRef.ts +24 -0
  37. package/dist/cli/index.js +2536 -0
  38. package/dist/cli/legacyRewrite.js +92 -0
  39. package/dist/cli/modelRef.js +20 -0
  40. package/dist/src/benchmark/artifacts.js +131 -0
  41. package/dist/src/benchmark/capabilityClassifier.js +81 -0
  42. package/dist/src/benchmark/capabilityStore.js +144 -0
  43. package/dist/src/benchmark/config.js +238 -0
  44. package/dist/src/benchmark/gates.js +118 -0
  45. package/dist/src/benchmark/jobs.js +252 -0
  46. package/dist/src/benchmark/runner.js +1847 -0
  47. package/dist/src/benchmark/schema.js +353 -0
  48. package/dist/src/benchmark/suites.js +314 -0
  49. package/dist/src/benchmark/tinyQaDataset.js +422 -0
  50. package/dist/src/benchmark/types.js +25 -0
  51. package/dist/src/config.js +47 -0
  52. package/dist/src/index.js +178 -0
  53. package/dist/src/mcp/client.js +215 -0
  54. package/dist/src/mcp/discovery.js +226 -0
  55. package/dist/src/mcp/policy.js +65 -0
  56. package/dist/src/mcp/registry.js +129 -0
  57. package/dist/src/mcp/service.js +460 -0
  58. package/dist/src/middleware/auth.js +179 -0
  59. package/dist/src/middleware/requestCapture.js +192 -0
  60. package/dist/src/middleware/requestStats.js +118 -0
  61. package/dist/src/pools/builder.js +132 -0
  62. package/dist/src/pools/repository.js +69 -0
  63. package/dist/src/pools/scheduler.js +360 -0
  64. package/dist/src/pools/types.js +2 -0
  65. package/dist/src/protocols/adapters/dashscope.js +267 -0
  66. package/dist/src/protocols/adapters/inferenceV2.js +346 -0
  67. package/dist/src/protocols/adapters/openai.js +27 -0
  68. package/dist/src/protocols/registry.js +99 -0
  69. package/dist/src/protocols/types.js +2 -0
  70. package/dist/src/providers/health.js +153 -0
  71. package/dist/src/providers/importer.js +289 -0
  72. package/dist/src/providers/modelRegistry.js +313 -0
  73. package/dist/src/providers/repository.js +361 -0
  74. package/dist/src/providers/types.js +2 -0
  75. package/dist/src/routes/admin.js +531 -0
  76. package/dist/src/routes/audio.js +295 -0
  77. package/dist/src/routes/chat.js +240 -0
  78. package/dist/src/routes/embeddings.js +157 -0
  79. package/dist/src/routes/images.js +288 -0
  80. package/dist/src/routes/mcp.js +256 -0
  81. package/dist/src/routes/mcpService.js +100 -0
  82. package/dist/src/routes/models.js +48 -0
  83. package/dist/src/routes/responses.js +711 -0
  84. package/dist/src/routes/sessions.js +450 -0
  85. package/dist/src/routes/stats.js +270 -0
  86. package/dist/src/routes/ui.js +97 -0
  87. package/dist/src/routes/videos.js +107 -0
  88. package/dist/src/routing/router.js +338 -0
  89. package/dist/src/services/imageGeneration.js +280 -0
  90. package/dist/src/services/imageUnderstanding.js +352 -0
  91. package/dist/src/services/videoGeneration.js +79 -0
  92. package/dist/src/storage/captureRepository.js +1591 -0
  93. package/dist/src/storage/files.js +157 -0
  94. package/dist/src/storage/imageCache.js +346 -0
  95. package/dist/src/storage/repositories.js +388 -0
  96. package/dist/src/storage/sessionRepository.js +370 -0
  97. package/dist/src/storage/statsRepository.js +204 -0
  98. package/dist/src/transport/httpClient.js +126 -0
  99. package/dist/src/types.js +2 -0
  100. package/dist/src/utils/messageMedia.js +285 -0
  101. package/dist/src/utils/modelCapabilities.js +108 -0
  102. package/dist/src/utils/modelDiscovery.js +170 -0
  103. package/dist/src/version.js +5 -0
  104. package/dist/src/workers/captureRetention.js +25 -0
  105. package/dist/src/workers/configWatcher.js +91 -0
  106. package/dist/src/workers/healthChecker.js +21 -0
  107. package/dist/src/workers/statsRotation.js +41 -0
  108. package/docs/LLM/output_schema.md +312 -0
  109. package/docs/benchmark.md +208 -0
  110. package/docs/mcp-guidelines.md +125 -0
  111. package/docs/mcp-service.md +178 -0
  112. package/docs/opencode.md +86 -0
  113. package/docs/providers.md +79 -0
  114. package/examples/benchmark.config.yaml +28 -0
  115. package/examples/providers/alibaba-dashscope.yaml +88 -0
  116. package/examples/providers/alibaba-llm.yaml +64 -0
  117. package/examples/providers/alibaba-registry.yaml +7 -0
  118. package/examples/providers/inference-v2-ray.yaml +29 -0
  119. package/examples/scenarios/assets/omni-call-sample.wav +0 -0
  120. package/examples/scenarios/custom.jsonl +5 -0
  121. package/examples/scenarios/custom.yaml +40 -0
  122. package/model-form-v2.png +0 -0
  123. package/package.json +66 -0
  124. package/provider-form-v2.png +0 -0
  125. package/provider-form.png +0 -0
  126. package/scripts/manual-test.sh +11 -0
  127. package/scripts/version-from-git.js +23 -0
  128. package/src/benchmark/artifacts.ts +149 -0
  129. package/src/benchmark/capabilityClassifier.ts +99 -0
  130. package/src/benchmark/capabilityStore.ts +174 -0
  131. package/src/benchmark/config.ts +337 -0
  132. package/src/benchmark/gates.ts +164 -0
  133. package/src/benchmark/jobs.ts +312 -0
  134. package/src/benchmark/runner.ts +2519 -0
  135. package/src/benchmark/schema.ts +443 -0
  136. package/src/benchmark/suites.ts +323 -0
  137. package/src/benchmark/tinyQaDataset.ts +428 -0
  138. package/src/benchmark/types.ts +442 -0
  139. package/src/config.ts +44 -0
  140. package/src/index.ts +195 -0
  141. package/src/mcp/client.ts +305 -0
  142. package/src/mcp/discovery.ts +266 -0
  143. package/src/mcp/policy.ts +105 -0
  144. package/src/mcp/registry.ts +164 -0
  145. package/src/mcp/service.ts +611 -0
  146. package/src/middleware/auth.ts +251 -0
  147. package/src/middleware/requestCapture.ts +245 -0
  148. package/src/middleware/requestStats.ts +163 -0
  149. package/src/pools/builder.ts +159 -0
  150. package/src/pools/repository.ts +71 -0
  151. package/src/pools/scheduler.ts +425 -0
  152. package/src/pools/types.ts +117 -0
  153. package/src/protocols/adapters/dashscope.ts +335 -0
  154. package/src/protocols/adapters/inferenceV2.ts +428 -0
  155. package/src/protocols/adapters/openai.ts +32 -0
  156. package/src/protocols/registry.ts +117 -0
  157. package/src/protocols/types.ts +81 -0
  158. package/src/providers/health.ts +207 -0
  159. package/src/providers/importer.ts +402 -0
  160. package/src/providers/modelRegistry.ts +415 -0
  161. package/src/providers/repository.ts +439 -0
  162. package/src/providers/types.ts +113 -0
  163. package/src/routes/admin.ts +666 -0
  164. package/src/routes/audio.ts +372 -0
  165. package/src/routes/chat.ts +301 -0
  166. package/src/routes/embeddings.ts +197 -0
  167. package/src/routes/images.ts +356 -0
  168. package/src/routes/mcp.ts +320 -0
  169. package/src/routes/mcpService.ts +114 -0
  170. package/src/routes/models.ts +50 -0
  171. package/src/routes/responses.ts +872 -0
  172. package/src/routes/sessions.ts +558 -0
  173. package/src/routes/stats.ts +312 -0
  174. package/src/routes/ui.ts +96 -0
  175. package/src/routes/videos.ts +132 -0
  176. package/src/routing/router.ts +501 -0
  177. package/src/services/imageGeneration.ts +396 -0
  178. package/src/services/imageUnderstanding.ts +449 -0
  179. package/src/services/videoGeneration.ts +127 -0
  180. package/src/storage/captureRepository.ts +1835 -0
  181. package/src/storage/files.ts +178 -0
  182. package/src/storage/imageCache.ts +405 -0
  183. package/src/storage/repositories.ts +494 -0
  184. package/src/storage/sessionRepository.ts +419 -0
  185. package/src/storage/statsRepository.ts +238 -0
  186. package/src/transport/httpClient.ts +145 -0
  187. package/src/types.ts +322 -0
  188. package/src/utils/messageMedia.ts +293 -0
  189. package/src/utils/modelCapabilities.ts +161 -0
  190. package/src/utils/modelDiscovery.ts +203 -0
  191. package/src/workers/captureRetention.ts +25 -0
  192. package/src/workers/configWatcher.ts +115 -0
  193. package/src/workers/healthChecker.ts +22 -0
  194. package/src/workers/statsRotation.ts +49 -0
  195. package/tests/benchmarkAdminRoutes.test.ts +82 -0
  196. package/tests/benchmarkBasics.test.ts +116 -0
  197. package/tests/captureAdminRoutes.test.ts +420 -0
  198. package/tests/captureRepository.test.ts +797 -0
  199. package/tests/cliLegacyRewrite.test.ts +45 -0
  200. package/tests/imageGeneration.service.test.ts +107 -0
  201. package/tests/imageUnderstanding.service.test.ts +123 -0
  202. package/tests/mcpPolicy.test.ts +105 -0
  203. package/tests/mcpService.test.ts +1245 -0
  204. package/tests/modelRef.test.ts +23 -0
  205. package/tests/modelsRoutes.test.ts +154 -0
  206. package/tests/sessionMediaCache.test.ts +167 -0
  207. package/tests/statsRoutes.test.ts +323 -0
  208. package/tsconfig.json +15 -0
  209. package/ui/index.html +16 -0
  210. package/ui/package-lock.json +8521 -0
  211. package/ui/package.json +52 -0
  212. package/ui/postcss.config.js +6 -0
  213. package/ui/public/assets/apple-touch-icon.png +0 -0
  214. package/ui/public/assets/favicon-16.png +0 -0
  215. package/ui/public/assets/favicon-32.png +0 -0
  216. package/ui/public/assets/icon-192.png +0 -0
  217. package/ui/public/assets/icon-512.png +0 -0
  218. package/ui/src/App.tsx +27 -0
  219. package/ui/src/api/client.ts +1503 -0
  220. package/ui/src/components/EndpointUsageGuide.tsx +361 -0
  221. package/ui/src/components/Layout.tsx +124 -0
  222. package/ui/src/components/MessageContent.tsx +365 -0
  223. package/ui/src/components/ToolCallMessage.tsx +179 -0
  224. package/ui/src/components/ToolPicker.tsx +442 -0
  225. package/ui/src/components/messageContentParser.test.ts +41 -0
  226. package/ui/src/components/messageContentParser.ts +73 -0
  227. package/ui/src/components/thinkingPreview.test.ts +27 -0
  228. package/ui/src/components/thinkingPreview.ts +15 -0
  229. package/ui/src/components/toMermaidSankey.test.ts +78 -0
  230. package/ui/src/components/toMermaidSankey.ts +56 -0
  231. package/ui/src/components/ui/button.tsx +58 -0
  232. package/ui/src/components/ui/input.tsx +21 -0
  233. package/ui/src/components/ui/textarea.tsx +21 -0
  234. package/ui/src/lib/utils.ts +6 -0
  235. package/ui/src/main.tsx +9 -0
  236. package/ui/src/pages/AgentPlayground.tsx +2010 -0
  237. package/ui/src/pages/Benchmark.tsx +988 -0
  238. package/ui/src/pages/Dashboard.tsx +581 -0
  239. package/ui/src/pages/Peek.tsx +962 -0
  240. package/ui/src/pages/Settings.tsx +2013 -0
  241. package/ui/src/pages/agentPlaygroundPayload.test.ts +109 -0
  242. package/ui/src/pages/agentPlaygroundPayload.ts +97 -0
  243. package/ui/src/pages/agentThinkingContent.test.ts +50 -0
  244. package/ui/src/pages/agentThinkingContent.ts +57 -0
  245. package/ui/src/pages/dashboardTokenUsage.test.ts +66 -0
  246. package/ui/src/pages/dashboardTokenUsage.ts +36 -0
  247. package/ui/src/pages/imageUpload.test.ts +39 -0
  248. package/ui/src/pages/imageUpload.ts +71 -0
  249. package/ui/src/pages/peekFilters.test.ts +29 -0
  250. package/ui/src/pages/peekFilters.ts +13 -0
  251. package/ui/src/pages/peekMedia.test.ts +58 -0
  252. package/ui/src/pages/peekMedia.ts +148 -0
  253. package/ui/src/pages/sessionAutoTitle.test.ts +128 -0
  254. package/ui/src/pages/sessionAutoTitle.ts +106 -0
  255. package/ui/src/stores/settings.ts +58 -0
  256. package/ui/src/styles/globals.css +223 -0
  257. package/ui/src/vite-env.d.ts +8 -0
  258. package/ui/tailwind.config.js +106 -0
  259. package/ui/tsconfig.json +32 -0
  260. package/ui/vite.config.ts +37 -0
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.scanMessageModalities = scanMessageModalities;
40
+ exports.normalizeMessagesForUpstream = normalizeMessagesForUpstream;
41
+ const path_1 = __importDefault(require("path"));
42
+ const imageCache_1 = require("../storage/imageCache");
43
+ function scanMessageModalities(messages) {
44
+ const result = { hasImage: false, hasAudio: false };
45
+ if (!Array.isArray(messages)) {
46
+ return result;
47
+ }
48
+ for (const message of messages) {
49
+ if (!message || typeof message !== "object") {
50
+ continue;
51
+ }
52
+ const content = message.content;
53
+ if (!Array.isArray(content)) {
54
+ continue;
55
+ }
56
+ for (const part of content) {
57
+ if (!part || typeof part !== "object") {
58
+ continue;
59
+ }
60
+ const type = part.type;
61
+ if (type === "image_url" || type === "input_image" || type === "image") {
62
+ result.hasImage = true;
63
+ }
64
+ if (type === "input_audio" || type === "audio") {
65
+ result.hasAudio = true;
66
+ }
67
+ }
68
+ }
69
+ return result;
70
+ }
71
+ async function normalizeMessagesForUpstream(paths, messages) {
72
+ if (!Array.isArray(messages)) {
73
+ return messages;
74
+ }
75
+ const normalized = [];
76
+ for (const message of messages) {
77
+ if (!message || typeof message !== "object") {
78
+ normalized.push(message);
79
+ continue;
80
+ }
81
+ const nextMessage = { ...message };
82
+ const content = nextMessage.content;
83
+ if (!Array.isArray(content)) {
84
+ normalized.push(nextMessage);
85
+ continue;
86
+ }
87
+ const nextContent = [];
88
+ for (const rawPart of content) {
89
+ if (!rawPart || typeof rawPart !== "object") {
90
+ nextContent.push(rawPart);
91
+ continue;
92
+ }
93
+ const part = { ...rawPart };
94
+ const type = part.type;
95
+ if (type === "video") {
96
+ throw invalidRequestError("Video content is not supported in v1 omni mode.");
97
+ }
98
+ if (type === "image" && typeof part.image === "string") {
99
+ nextContent.push({ type: "image_url", image_url: { url: part.image } });
100
+ continue;
101
+ }
102
+ if (type === "image_url") {
103
+ nextContent.push(await normalizeImageUrlPart(paths, part));
104
+ continue;
105
+ }
106
+ if (type === "audio" && typeof part.audio === "string") {
107
+ nextContent.push(await normalizeAudioValue(paths, part.audio));
108
+ continue;
109
+ }
110
+ if (type === "input_audio") {
111
+ nextContent.push(await normalizeInputAudioPart(paths, part));
112
+ continue;
113
+ }
114
+ nextContent.push(part);
115
+ }
116
+ nextMessage.content = nextContent;
117
+ normalized.push(nextMessage);
118
+ }
119
+ return normalized;
120
+ }
121
+ async function normalizeImageUrlPart(paths, part) {
122
+ const imageUrlObject = (part.image_url ?? {});
123
+ const value = imageUrlObject.url;
124
+ if (typeof value !== "string" || value.length === 0) {
125
+ return part;
126
+ }
127
+ if (value.startsWith("data:")) {
128
+ return part;
129
+ }
130
+ const hash = extractLocalHash(value);
131
+ if (!hash) {
132
+ return part;
133
+ }
134
+ const mediaPath = await (0, imageCache_1.getMediaPath)(paths, hash);
135
+ const mediaEntry = await (0, imageCache_1.getMediaEntry)(paths, hash);
136
+ if (!mediaPath || !mediaEntry) {
137
+ throw invalidRequestError("Referenced image not found in cache.");
138
+ }
139
+ const file = await Promise.resolve().then(() => __importStar(require("fs/promises")));
140
+ const buffer = await file.readFile(mediaPath);
141
+ const dataUrl = `data:${mediaEntry.mimeType};base64,${buffer.toString("base64")}`;
142
+ return {
143
+ ...part,
144
+ type: "image_url",
145
+ image_url: {
146
+ ...imageUrlObject,
147
+ url: dataUrl,
148
+ },
149
+ };
150
+ }
151
+ async function normalizeInputAudioPart(paths, part) {
152
+ const inputAudio = (part.input_audio ?? {});
153
+ const data = inputAudio.data;
154
+ const url = inputAudio.url;
155
+ if (typeof data === "string" && data.length > 0) {
156
+ return {
157
+ ...part,
158
+ type: "input_audio",
159
+ input_audio: {
160
+ ...inputAudio,
161
+ },
162
+ };
163
+ }
164
+ if (typeof url === "string" && url.length > 0) {
165
+ const resolved = await resolveLocalMediaUrl(paths, url);
166
+ return {
167
+ ...part,
168
+ type: "input_audio",
169
+ input_audio: resolved,
170
+ };
171
+ }
172
+ throw invalidRequestError("input_audio requires either data or a local media url.");
173
+ }
174
+ async function normalizeAudioValue(paths, value) {
175
+ if (value.startsWith("data:")) {
176
+ const parsed = parseDataUrl(value);
177
+ return {
178
+ type: "input_audio",
179
+ input_audio: {
180
+ data: parsed.base64,
181
+ format: parsed.format,
182
+ },
183
+ };
184
+ }
185
+ if (looksLikeBase64(value)) {
186
+ return {
187
+ type: "input_audio",
188
+ input_audio: {
189
+ data: value,
190
+ },
191
+ };
192
+ }
193
+ const resolved = await resolveLocalMediaUrl(paths, value);
194
+ return {
195
+ type: "input_audio",
196
+ input_audio: resolved,
197
+ };
198
+ }
199
+ async function resolveLocalMediaUrl(paths, url) {
200
+ const hash = extractLocalHash(url);
201
+ if (!hash) {
202
+ throw invalidRequestError("Only local /admin/media or /admin/images URLs are allowed for input_audio.");
203
+ }
204
+ const mediaPath = await (0, imageCache_1.getMediaPath)(paths, hash);
205
+ const mediaEntry = await (0, imageCache_1.getMediaEntry)(paths, hash);
206
+ if (!mediaPath || !mediaEntry) {
207
+ throw invalidRequestError("Referenced media not found in cache.");
208
+ }
209
+ const file = await Promise.resolve().then(() => __importStar(require("fs/promises")));
210
+ const buffer = await file.readFile(mediaPath);
211
+ return {
212
+ data: buffer.toString("base64"),
213
+ format: audioFormatFromMime(mediaEntry.mimeType, mediaPath),
214
+ };
215
+ }
216
+ function extractLocalHash(url) {
217
+ const normalized = normalizeLocalUrl(url);
218
+ if (!normalized) {
219
+ return null;
220
+ }
221
+ const mediaMatch = normalized.match(/^\/admin\/(media|images)\/([a-f0-9]{16})$/i);
222
+ if (!mediaMatch) {
223
+ return null;
224
+ }
225
+ return mediaMatch[2];
226
+ }
227
+ function normalizeLocalUrl(url) {
228
+ if (url.startsWith("/")) {
229
+ return url;
230
+ }
231
+ try {
232
+ const parsed = new URL(url);
233
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
234
+ return null;
235
+ }
236
+ if (!["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)) {
237
+ return null;
238
+ }
239
+ return parsed.pathname;
240
+ }
241
+ catch {
242
+ return null;
243
+ }
244
+ }
245
+ function parseDataUrl(value) {
246
+ const match = value.match(/^data:([^;]+);base64,(.+)$/i);
247
+ if (!match) {
248
+ throw invalidRequestError("Invalid data URL for audio input.");
249
+ }
250
+ return {
251
+ base64: match[2].replace(/\s+/g, ""),
252
+ format: audioFormatFromMime(match[1]),
253
+ };
254
+ }
255
+ function audioFormatFromMime(mimeType, filePath) {
256
+ const lower = mimeType.toLowerCase();
257
+ if (lower.includes("wav"))
258
+ return "wav";
259
+ if (lower.includes("mpeg") || lower.includes("mp3"))
260
+ return "mp3";
261
+ if (lower.includes("ogg"))
262
+ return "ogg";
263
+ if (lower.includes("webm"))
264
+ return "webm";
265
+ if (lower.includes("mp4") || lower.includes("m4a"))
266
+ return "m4a";
267
+ if (filePath) {
268
+ const ext = path_1.default.extname(filePath).slice(1).toLowerCase();
269
+ if (ext)
270
+ return ext;
271
+ }
272
+ return undefined;
273
+ }
274
+ function looksLikeBase64(value) {
275
+ if (value.length < 32) {
276
+ return false;
277
+ }
278
+ return /^[A-Za-z0-9+/=\s]+$/.test(value);
279
+ }
280
+ function invalidRequestError(message) {
281
+ const error = new Error(message);
282
+ error.type = "invalid_request";
283
+ error.retryable = false;
284
+ return error;
285
+ }
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveCapabilities = resolveCapabilities;
4
+ exports.inferCapabilities = inferCapabilities;
5
+ exports.supportsRequirements = supportsRequirements;
6
+ function resolveCapabilities(mapping, endpointType, upstreamCaps) {
7
+ if (mapping.capabilities) {
8
+ return normalizeCapabilities(mapping.capabilities, "configured");
9
+ }
10
+ if (upstreamCaps) {
11
+ return normalizeCapabilities(upstreamCaps, "inferred");
12
+ }
13
+ const inferred = inferCapabilities(mapping.publicName, endpointType);
14
+ warnInference(mapping.publicName, endpointType, inferred);
15
+ return normalizeCapabilities(inferred, "inferred");
16
+ }
17
+ function inferCapabilities(modelName, endpointType) {
18
+ const name = modelName.toLowerCase();
19
+ if (endpointType === "embedding") {
20
+ return { input: ["text"], output: ["embedding"] };
21
+ }
22
+ if (endpointType === "diffusion") {
23
+ return { input: ["text"], output: ["image"] };
24
+ }
25
+ if (endpointType === "audio") {
26
+ if (isTtsModelName(name)) {
27
+ return { input: ["text"], output: ["audio"] };
28
+ }
29
+ return { input: ["audio"], output: ["text"] };
30
+ }
31
+ if (endpointType === "video") {
32
+ if (isImageToVideoModelName(name)) {
33
+ return { input: ["text", "image"], output: ["video"] };
34
+ }
35
+ return { input: ["text"], output: ["video"] };
36
+ }
37
+ if (isVisionModelName(name)) {
38
+ return { input: ["text", "image"], output: ["text"], supportsTools: true, supportsStreaming: true };
39
+ }
40
+ return { input: ["text"], output: ["text"], supportsTools: true, supportsStreaming: true };
41
+ }
42
+ function supportsRequirements(capabilities, requirements) {
43
+ if (!requirements) {
44
+ return true;
45
+ }
46
+ if (requirements.requiredInput && requirements.requiredInput.length > 0) {
47
+ for (const modality of requirements.requiredInput) {
48
+ if (!capabilities.input.includes(modality)) {
49
+ return false;
50
+ }
51
+ }
52
+ }
53
+ if (requirements.requiredOutput && requirements.requiredOutput.length > 0) {
54
+ for (const modality of requirements.requiredOutput) {
55
+ if (!capabilities.output.includes(modality)) {
56
+ return false;
57
+ }
58
+ }
59
+ }
60
+ return true;
61
+ }
62
+ function normalizeCapabilities(capabilities, source) {
63
+ return {
64
+ input: normalizeModalities(capabilities.input),
65
+ output: normalizeModalities(capabilities.output),
66
+ supportsTools: capabilities.supportsTools,
67
+ supportsStreaming: capabilities.supportsStreaming,
68
+ source,
69
+ };
70
+ }
71
+ function normalizeModalities(modalities) {
72
+ const allowed = ["text", "image", "audio", "embedding", "video"];
73
+ const unique = new Set();
74
+ for (const modality of modalities) {
75
+ if (allowed.includes(modality)) {
76
+ unique.add(modality);
77
+ }
78
+ }
79
+ return allowed.filter((modality) => unique.has(modality));
80
+ }
81
+ function isTtsModelName(name) {
82
+ return (name.includes("tts") ||
83
+ name.includes("speech") ||
84
+ name.includes("voice") ||
85
+ name.includes("audio-gen"));
86
+ }
87
+ function isVisionModelName(name) {
88
+ return (name.includes("vision") ||
89
+ name.includes("vl") ||
90
+ name.includes("omni") ||
91
+ name.includes("multimodal"));
92
+ }
93
+ function isImageToVideoModelName(name) {
94
+ return (name.includes("i2v") ||
95
+ name.includes("image-to-video") ||
96
+ name.includes("img2vid") ||
97
+ name.includes("kf2v"));
98
+ }
99
+ const capabilityInferenceWarnings = new Set();
100
+ function warnInference(modelName, endpointType, capabilities) {
101
+ const key = `${endpointType}:${modelName}`;
102
+ if (capabilityInferenceWarnings.has(key)) {
103
+ return;
104
+ }
105
+ capabilityInferenceWarnings.add(key);
106
+ console.warn(`[waypoi] Inferred capabilities for model '${modelName}' on ${endpointType}: ` +
107
+ `${capabilities.input.join("+")}->${capabilities.output.join("+")}`);
108
+ }
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveModelMappings = resolveModelMappings;
4
+ exports.discoverUpstreamModels = discoverUpstreamModels;
5
+ const undici_1 = require("undici");
6
+ async function resolveModelMappings(endpoint, mappings) {
7
+ let models = null;
8
+ try {
9
+ models = await discoverUpstreamModels(endpoint);
10
+ }
11
+ catch {
12
+ models = null;
13
+ }
14
+ if (!models || models.length === 0) {
15
+ return mappings;
16
+ }
17
+ // If exactly one model, use it as the upstream for all mappings
18
+ // that don't already have an explicit upstream different from the public name
19
+ if (models.length === 1) {
20
+ const sole = models[0];
21
+ console.log(`[model-discovery] Single model found: ${sole.id}`);
22
+ return mappings.map((mapping) => {
23
+ // If user specified explicit upstream (public=upstream format), keep it
24
+ // Otherwise, use the discovered model as upstream
25
+ if (mapping.publicName !== mapping.upstreamModel) {
26
+ // User specified explicit mapping, keep it
27
+ return mapping;
28
+ }
29
+ // Public and upstream are the same (user just gave public name)
30
+ // Replace upstream with discovered model
31
+ console.log(`[model-discovery] Mapping ${mapping.publicName} -> ${sole.id}`);
32
+ return {
33
+ ...mapping,
34
+ upstreamModel: sole.id,
35
+ capabilities: mapping.capabilities ?? sole.capabilities,
36
+ };
37
+ });
38
+ }
39
+ // Multiple models - check if any mapping's upstream matches available models
40
+ console.log(`[model-discovery] ${models.length} models found: ${models
41
+ .slice(0, 5)
42
+ .map((model) => model.id)
43
+ .join(", ")}${models.length > 5 ? "..." : ""}`);
44
+ const byId = new Map(models.map((model) => [model.id, model]));
45
+ return mappings.map((mapping) => {
46
+ if (mapping.capabilities) {
47
+ return mapping;
48
+ }
49
+ const matched = byId.get(mapping.upstreamModel) ?? byId.get(mapping.publicName);
50
+ if (!matched?.capabilities) {
51
+ return mapping;
52
+ }
53
+ return {
54
+ ...mapping,
55
+ capabilities: matched.capabilities,
56
+ };
57
+ });
58
+ }
59
+ async function discoverUpstreamModels(endpoint) {
60
+ const dispatcher = endpoint.insecureTls
61
+ ? new undici_1.Agent({ connect: { rejectUnauthorized: false } })
62
+ : undefined;
63
+ const { url, headers } = buildDiscoveryRequest(endpoint);
64
+ const response = await (0, undici_1.request)(url, {
65
+ method: "GET",
66
+ headersTimeout: 3000,
67
+ bodyTimeout: 3000,
68
+ dispatcher,
69
+ headers,
70
+ });
71
+ const body = (await readJson(response.body));
72
+ response.body.resume();
73
+ if (response.statusCode < 200 || response.statusCode >= 300) {
74
+ throw new Error(`model discovery failed with status ${response.statusCode}`);
75
+ }
76
+ const list = Array.isArray(body?.data) ? body.data : [];
77
+ const models = [];
78
+ for (const item of list) {
79
+ if (!item.id) {
80
+ continue;
81
+ }
82
+ const modelInfo = { id: item.id };
83
+ const capabilities = extractCapabilities(item);
84
+ if (capabilities) {
85
+ modelInfo.capabilities = capabilities;
86
+ }
87
+ models.push(modelInfo);
88
+ }
89
+ return models;
90
+ }
91
+ function buildDiscoveryRequest(endpoint) {
92
+ const authType = endpoint.auth?.type ?? "bearer";
93
+ const headers = {};
94
+ const url = new URL(buildModelListUrl(endpoint.baseUrl));
95
+ const apiKey = endpoint.apiKey?.trim();
96
+ if (apiKey && authType === "query") {
97
+ const keyParam = endpoint.auth?.keyParam?.trim() || "api_key";
98
+ url.searchParams.set(keyParam, apiKey);
99
+ }
100
+ else if (apiKey && authType === "header") {
101
+ const headerName = endpoint.auth?.headerName?.trim() || endpoint.auth?.keyParam?.trim() || "x-api-key";
102
+ const prefix = endpoint.auth?.keyPrefix?.trim();
103
+ headers[headerName] = prefix ? `${prefix} ${apiKey}` : apiKey;
104
+ }
105
+ else if (apiKey && authType !== "none") {
106
+ headers.authorization = `Bearer ${apiKey}`;
107
+ }
108
+ return { url: url.toString(), headers };
109
+ }
110
+ function buildModelListUrl(baseUrl) {
111
+ const parsed = new URL(baseUrl);
112
+ const pathname = parsed.pathname.replace(/\/+$/, "");
113
+ if (!pathname) {
114
+ parsed.pathname = "/v1/models";
115
+ }
116
+ else if (pathname.endsWith("/v1")) {
117
+ parsed.pathname = `${pathname}/models`;
118
+ }
119
+ else {
120
+ parsed.pathname = `${pathname}/v1/models`;
121
+ }
122
+ return parsed.toString();
123
+ }
124
+ function extractCapabilities(item) {
125
+ const fromCapabilities = item.capabilities;
126
+ if (fromCapabilities?.input && fromCapabilities?.output) {
127
+ const input = normalizeModalities(fromCapabilities.input);
128
+ const output = normalizeModalities(fromCapabilities.output);
129
+ if (input.length > 0 && output.length > 0) {
130
+ return {
131
+ input,
132
+ output,
133
+ supportsTools: fromCapabilities.supportsTools,
134
+ supportsStreaming: fromCapabilities.supportsStreaming,
135
+ source: "inferred",
136
+ };
137
+ }
138
+ }
139
+ const input = normalizeModalities(item.input_modalities ?? []);
140
+ const output = normalizeModalities(item.output_modalities ?? []);
141
+ if (input.length > 0 && output.length > 0) {
142
+ return { input, output, source: "inferred" };
143
+ }
144
+ return undefined;
145
+ }
146
+ function normalizeModalities(values) {
147
+ const normalized = new Set();
148
+ for (const value of values) {
149
+ const lower = value.toLowerCase();
150
+ if (lower === "text" || lower === "image" || lower === "audio" || lower === "embedding") {
151
+ normalized.add(lower);
152
+ }
153
+ }
154
+ return Array.from(normalized);
155
+ }
156
+ async function readJson(stream) {
157
+ const chunks = [];
158
+ for await (const chunk of stream) {
159
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
160
+ }
161
+ if (chunks.length === 0) {
162
+ return null;
163
+ }
164
+ try {
165
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ // Auto-generated from git tags. Do not edit manually.
5
+ exports.VERSION = "0.7.2-alpha.1";
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startCaptureRetentionWorker = startCaptureRetentionWorker;
4
+ exports.stopCaptureRetentionWorker = stopCaptureRetentionWorker;
5
+ const captureRepository_1 = require("../storage/captureRepository");
6
+ let retentionTimer = null;
7
+ function startCaptureRetentionWorker(paths) {
8
+ const run = async () => {
9
+ try {
10
+ await (0, captureRepository_1.runCaptureRetention)(paths);
11
+ }
12
+ catch {
13
+ // ignore background errors
14
+ }
15
+ };
16
+ retentionTimer = setInterval(run, 10 * 60 * 1000);
17
+ retentionTimer.unref();
18
+ void run();
19
+ }
20
+ function stopCaptureRetentionWorker() {
21
+ if (retentionTimer) {
22
+ clearInterval(retentionTimer);
23
+ retentionTimer = null;
24
+ }
25
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startConfigWatcher = startConfigWatcher;
4
+ exports.stopConfigWatcher = stopConfigWatcher;
5
+ exports.getConfigWatcher = getConfigWatcher;
6
+ const fs_1 = require("fs");
7
+ const events_1 = require("events");
8
+ const DEBOUNCE_MS = 500;
9
+ class ConfigWatcherImpl extends events_1.EventEmitter {
10
+ watcher = null;
11
+ debounceTimer = null;
12
+ isWatching = false;
13
+ configPath;
14
+ constructor(configPath) {
15
+ super();
16
+ this.configPath = configPath;
17
+ }
18
+ start() {
19
+ if (this.isWatching)
20
+ return;
21
+ try {
22
+ this.watcher = (0, fs_1.watch)(this.configPath, (eventType) => {
23
+ // Handle both 'change' and 'rename' events
24
+ // Some editors do atomic saves (write temp → rename)
25
+ if (eventType === "change" || eventType === "rename") {
26
+ this.debouncedEmit();
27
+ }
28
+ });
29
+ this.watcher.on("error", (error) => {
30
+ this.emit("error", error);
31
+ });
32
+ this.isWatching = true;
33
+ console.log("[config-watcher] Watching for config changes");
34
+ }
35
+ catch (error) {
36
+ // File might not exist yet, that's okay
37
+ console.log("[config-watcher] Config file not found, will retry on next access");
38
+ }
39
+ }
40
+ debouncedEmit() {
41
+ if (this.debounceTimer) {
42
+ clearTimeout(this.debounceTimer);
43
+ }
44
+ this.debounceTimer = setTimeout(() => {
45
+ console.log("[config-watcher] Config file changed, triggering reload");
46
+ this.emit("config:updated");
47
+ this.debounceTimer = null;
48
+ }, DEBOUNCE_MS);
49
+ }
50
+ stop() {
51
+ if (this.debounceTimer) {
52
+ clearTimeout(this.debounceTimer);
53
+ this.debounceTimer = null;
54
+ }
55
+ if (this.watcher) {
56
+ this.watcher.close();
57
+ this.watcher = null;
58
+ }
59
+ this.isWatching = false;
60
+ console.log("[config-watcher] Stopped watching");
61
+ }
62
+ }
63
+ let globalWatcher = null;
64
+ /**
65
+ * Start watching the config file for changes.
66
+ * Returns an EventEmitter that emits "config:updated" when the file changes.
67
+ */
68
+ function startConfigWatcher(paths) {
69
+ if (globalWatcher) {
70
+ return globalWatcher;
71
+ }
72
+ const watcher = new ConfigWatcherImpl(paths.configPath);
73
+ watcher.start();
74
+ globalWatcher = watcher;
75
+ return watcher;
76
+ }
77
+ /**
78
+ * Stop the config watcher.
79
+ */
80
+ function stopConfigWatcher() {
81
+ if (globalWatcher) {
82
+ globalWatcher.stop();
83
+ globalWatcher = null;
84
+ }
85
+ }
86
+ /**
87
+ * Get the current config watcher instance (if running).
88
+ */
89
+ function getConfigWatcher() {
90
+ return globalWatcher;
91
+ }