@nodetool-ai/websocket 0.7.0-rc.11

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 (261) hide show
  1. package/dist/agent/agent-runtime.d.ts +47 -0
  2. package/dist/agent/agent-runtime.d.ts.map +1 -0
  3. package/dist/agent/agent-runtime.js +577 -0
  4. package/dist/agent/agent-runtime.js.map +1 -0
  5. package/dist/agent/codex-agent.d.ts +41 -0
  6. package/dist/agent/codex-agent.d.ts.map +1 -0
  7. package/dist/agent/codex-agent.js +334 -0
  8. package/dist/agent/codex-agent.js.map +1 -0
  9. package/dist/agent/index.d.ts +6 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +4 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/llm-agent.d.ts +51 -0
  14. package/dist/agent/llm-agent.d.ts.map +1 -0
  15. package/dist/agent/llm-agent.js +526 -0
  16. package/dist/agent/llm-agent.js.map +1 -0
  17. package/dist/agent/mcp-tool-server.d.ts +33 -0
  18. package/dist/agent/mcp-tool-server.d.ts.map +1 -0
  19. package/dist/agent/mcp-tool-server.js +292 -0
  20. package/dist/agent/mcp-tool-server.js.map +1 -0
  21. package/dist/agent/opencode-agent.d.ts +32 -0
  22. package/dist/agent/opencode-agent.d.ts.map +1 -0
  23. package/dist/agent/opencode-agent.js +332 -0
  24. package/dist/agent/opencode-agent.js.map +1 -0
  25. package/dist/agent/pi-agent.d.ts +42 -0
  26. package/dist/agent/pi-agent.d.ts.map +1 -0
  27. package/dist/agent/pi-agent.js +429 -0
  28. package/dist/agent/pi-agent.js.map +1 -0
  29. package/dist/agent/sdk-provider.d.ts +46 -0
  30. package/dist/agent/sdk-provider.d.ts.map +1 -0
  31. package/dist/agent/sdk-provider.js +50 -0
  32. package/dist/agent/sdk-provider.js.map +1 -0
  33. package/dist/agent/socket-route.d.ts +11 -0
  34. package/dist/agent/socket-route.d.ts.map +1 -0
  35. package/dist/agent/socket-route.js +262 -0
  36. package/dist/agent/socket-route.js.map +1 -0
  37. package/dist/agent/transport.d.ts +39 -0
  38. package/dist/agent/transport.d.ts.map +1 -0
  39. package/dist/agent/transport.js +17 -0
  40. package/dist/agent/transport.js.map +1 -0
  41. package/dist/agent/types.d.ts +11 -0
  42. package/dist/agent/types.d.ts.map +1 -0
  43. package/dist/agent/types.js +10 -0
  44. package/dist/agent/types.js.map +1 -0
  45. package/dist/collection-api.d.ts +14 -0
  46. package/dist/collection-api.d.ts.map +1 -0
  47. package/dist/collection-api.js +75 -0
  48. package/dist/collection-api.js.map +1 -0
  49. package/dist/error-codes.d.ts +3 -0
  50. package/dist/error-codes.d.ts.map +1 -0
  51. package/dist/error-codes.js +2 -0
  52. package/dist/error-codes.js.map +1 -0
  53. package/dist/file-api.d.ts +12 -0
  54. package/dist/file-api.d.ts.map +1 -0
  55. package/dist/file-api.js +76 -0
  56. package/dist/file-api.js.map +1 -0
  57. package/dist/http-api.d.ts +94 -0
  58. package/dist/http-api.d.ts.map +1 -0
  59. package/dist/http-api.js +1472 -0
  60. package/dist/http-api.js.map +1 -0
  61. package/dist/index.d.ts +6 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +6 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/lib/asset-paths.d.ts +10 -0
  66. package/dist/lib/asset-paths.d.ts.map +1 -0
  67. package/dist/lib/asset-paths.js +45 -0
  68. package/dist/lib/asset-paths.js.map +1 -0
  69. package/dist/lib/bridge.d.ts +8 -0
  70. package/dist/lib/bridge.d.ts.map +1 -0
  71. package/dist/lib/bridge.js +70 -0
  72. package/dist/lib/bridge.js.map +1 -0
  73. package/dist/mcp-server.d.ts +32 -0
  74. package/dist/mcp-server.d.ts.map +1 -0
  75. package/dist/mcp-server.js +842 -0
  76. package/dist/mcp-server.js.map +1 -0
  77. package/dist/models-api.d.ts +12 -0
  78. package/dist/models-api.d.ts.map +1 -0
  79. package/dist/models-api.js +730 -0
  80. package/dist/models-api.js.map +1 -0
  81. package/dist/oauth-api.d.ts +28 -0
  82. package/dist/oauth-api.d.ts.map +1 -0
  83. package/dist/oauth-api.js +681 -0
  84. package/dist/oauth-api.js.map +1 -0
  85. package/dist/openai-api.d.ts +63 -0
  86. package/dist/openai-api.d.ts.map +1 -0
  87. package/dist/openai-api.js +359 -0
  88. package/dist/openai-api.js.map +1 -0
  89. package/dist/plugins/websocket.d.ts +14 -0
  90. package/dist/plugins/websocket.d.ts.map +1 -0
  91. package/dist/plugins/websocket.js +431 -0
  92. package/dist/plugins/websocket.js.map +1 -0
  93. package/dist/resolve-media-urls.d.ts +19 -0
  94. package/dist/resolve-media-urls.d.ts.map +1 -0
  95. package/dist/resolve-media-urls.js +109 -0
  96. package/dist/resolve-media-urls.js.map +1 -0
  97. package/dist/routes/assets.d.ts +22 -0
  98. package/dist/routes/assets.d.ts.map +1 -0
  99. package/dist/routes/assets.js +168 -0
  100. package/dist/routes/assets.js.map +1 -0
  101. package/dist/routes/collections.d.ts +16 -0
  102. package/dist/routes/collections.d.ts.map +1 -0
  103. package/dist/routes/collections.js +32 -0
  104. package/dist/routes/collections.js.map +1 -0
  105. package/dist/routes/files.d.ts +12 -0
  106. package/dist/routes/files.d.ts.map +1 -0
  107. package/dist/routes/files.js +11 -0
  108. package/dist/routes/files.js.map +1 -0
  109. package/dist/routes/health.d.ts +4 -0
  110. package/dist/routes/health.d.ts.map +1 -0
  111. package/dist/routes/health.js +41 -0
  112. package/dist/routes/health.js.map +1 -0
  113. package/dist/routes/jobs.d.ts +16 -0
  114. package/dist/routes/jobs.d.ts.map +1 -0
  115. package/dist/routes/jobs.js +25 -0
  116. package/dist/routes/jobs.js.map +1 -0
  117. package/dist/routes/nodes.d.ts +8 -0
  118. package/dist/routes/nodes.d.ts.map +1 -0
  119. package/dist/routes/nodes.js +124 -0
  120. package/dist/routes/nodes.js.map +1 -0
  121. package/dist/routes/oauth.d.ts +8 -0
  122. package/dist/routes/oauth.d.ts.map +1 -0
  123. package/dist/routes/oauth.js +19 -0
  124. package/dist/routes/oauth.js.map +1 -0
  125. package/dist/routes/openai.d.ts +8 -0
  126. package/dist/routes/openai.d.ts.map +1 -0
  127. package/dist/routes/openai.js +20 -0
  128. package/dist/routes/openai.js.map +1 -0
  129. package/dist/routes/storage.d.ts +12 -0
  130. package/dist/routes/storage.d.ts.map +1 -0
  131. package/dist/routes/storage.js +13 -0
  132. package/dist/routes/storage.js.map +1 -0
  133. package/dist/routes/workflows.d.ts +18 -0
  134. package/dist/routes/workflows.d.ts.map +1 -0
  135. package/dist/routes/workflows.js +25 -0
  136. package/dist/routes/workflows.js.map +1 -0
  137. package/dist/routes/workspace.d.ts +15 -0
  138. package/dist/routes/workspace.d.ts.map +1 -0
  139. package/dist/routes/workspace.js +24 -0
  140. package/dist/routes/workspace.js.map +1 -0
  141. package/dist/screenshot-server.d.ts +21 -0
  142. package/dist/screenshot-server.d.ts.map +1 -0
  143. package/dist/screenshot-server.js +222 -0
  144. package/dist/screenshot-server.js.map +1 -0
  145. package/dist/server.d.ts +10 -0
  146. package/dist/server.d.ts.map +1 -0
  147. package/dist/server.js +783 -0
  148. package/dist/server.js.map +1 -0
  149. package/dist/settings-registry.d.ts +29 -0
  150. package/dist/settings-registry.d.ts.map +1 -0
  151. package/dist/settings-registry.js +117 -0
  152. package/dist/settings-registry.js.map +1 -0
  153. package/dist/storage-api.d.ts +5 -0
  154. package/dist/storage-api.d.ts.map +1 -0
  155. package/dist/storage-api.js +221 -0
  156. package/dist/storage-api.js.map +1 -0
  157. package/dist/test-ui-server.d.ts +19 -0
  158. package/dist/test-ui-server.d.ts.map +1 -0
  159. package/dist/test-ui-server.js +1256 -0
  160. package/dist/test-ui-server.js.map +1 -0
  161. package/dist/trpc/context.d.ts +21 -0
  162. package/dist/trpc/context.d.ts.map +1 -0
  163. package/dist/trpc/context.js +10 -0
  164. package/dist/trpc/context.js.map +1 -0
  165. package/dist/trpc/error-formatter.d.ts +18 -0
  166. package/dist/trpc/error-formatter.d.ts.map +1 -0
  167. package/dist/trpc/error-formatter.js +41 -0
  168. package/dist/trpc/error-formatter.js.map +1 -0
  169. package/dist/trpc/index.d.ts +18 -0
  170. package/dist/trpc/index.d.ts.map +1 -0
  171. package/dist/trpc/index.js +13 -0
  172. package/dist/trpc/index.js.map +1 -0
  173. package/dist/trpc/middleware.d.ts +8 -0
  174. package/dist/trpc/middleware.d.ts.map +1 -0
  175. package/dist/trpc/middleware.js +12 -0
  176. package/dist/trpc/middleware.js.map +1 -0
  177. package/dist/trpc/router.d.ts +2543 -0
  178. package/dist/trpc/router.d.ts.map +1 -0
  179. package/dist/trpc/router.js +43 -0
  180. package/dist/trpc/router.js.map +1 -0
  181. package/dist/trpc/routers/assets.d.ts +229 -0
  182. package/dist/trpc/routers/assets.d.ts.map +1 -0
  183. package/dist/trpc/routers/assets.js +263 -0
  184. package/dist/trpc/routers/assets.js.map +1 -0
  185. package/dist/trpc/routers/collections.d.ts +88 -0
  186. package/dist/trpc/routers/collections.d.ts.map +1 -0
  187. package/dist/trpc/routers/collections.js +166 -0
  188. package/dist/trpc/routers/collections.js.map +1 -0
  189. package/dist/trpc/routers/costs.d.ts +129 -0
  190. package/dist/trpc/routers/costs.d.ts.map +1 -0
  191. package/dist/trpc/routers/costs.js +95 -0
  192. package/dist/trpc/routers/costs.js.map +1 -0
  193. package/dist/trpc/routers/files.d.ts +34 -0
  194. package/dist/trpc/routers/files.d.ts.map +1 -0
  195. package/dist/trpc/routers/files.js +92 -0
  196. package/dist/trpc/routers/files.js.map +1 -0
  197. package/dist/trpc/routers/jobs.d.ts +92 -0
  198. package/dist/trpc/routers/jobs.d.ts.map +1 -0
  199. package/dist/trpc/routers/jobs.js +100 -0
  200. package/dist/trpc/routers/jobs.js.map +1 -0
  201. package/dist/trpc/routers/mcp-config.d.ts +69 -0
  202. package/dist/trpc/routers/mcp-config.d.ts.map +1 -0
  203. package/dist/trpc/routers/mcp-config.js +290 -0
  204. package/dist/trpc/routers/mcp-config.js.map +1 -0
  205. package/dist/trpc/routers/messages.d.ts +121 -0
  206. package/dist/trpc/routers/messages.d.ts.map +1 -0
  207. package/dist/trpc/routers/messages.js +113 -0
  208. package/dist/trpc/routers/messages.js.map +1 -0
  209. package/dist/trpc/routers/models.d.ts +748 -0
  210. package/dist/trpc/routers/models.d.ts.map +1 -0
  211. package/dist/trpc/routers/models.js +1107 -0
  212. package/dist/trpc/routers/models.js.map +1 -0
  213. package/dist/trpc/routers/nodes.d.ts +48 -0
  214. package/dist/trpc/routers/nodes.d.ts.map +1 -0
  215. package/dist/trpc/routers/nodes.js +57 -0
  216. package/dist/trpc/routers/nodes.js.map +1 -0
  217. package/dist/trpc/routers/sandboxes.d.ts +64 -0
  218. package/dist/trpc/routers/sandboxes.d.ts.map +1 -0
  219. package/dist/trpc/routers/sandboxes.js +327 -0
  220. package/dist/trpc/routers/sandboxes.js.map +1 -0
  221. package/dist/trpc/routers/settings.d.ts +113 -0
  222. package/dist/trpc/routers/settings.d.ts.map +1 -0
  223. package/dist/trpc/routers/settings.js +193 -0
  224. package/dist/trpc/routers/settings.js.map +1 -0
  225. package/dist/trpc/routers/skills.d.ts +41 -0
  226. package/dist/trpc/routers/skills.d.ts.map +1 -0
  227. package/dist/trpc/routers/skills.js +194 -0
  228. package/dist/trpc/routers/skills.js.map +1 -0
  229. package/dist/trpc/routers/storage.d.ts +44 -0
  230. package/dist/trpc/routers/storage.d.ts.map +1 -0
  231. package/dist/trpc/routers/storage.js +153 -0
  232. package/dist/trpc/routers/storage.js.map +1 -0
  233. package/dist/trpc/routers/threads.d.ts +95 -0
  234. package/dist/trpc/routers/threads.d.ts.map +1 -0
  235. package/dist/trpc/routers/threads.js +150 -0
  236. package/dist/trpc/routers/threads.js.map +1 -0
  237. package/dist/trpc/routers/users.d.ts +79 -0
  238. package/dist/trpc/routers/users.d.ts.map +1 -0
  239. package/dist/trpc/routers/users.js +125 -0
  240. package/dist/trpc/routers/users.js.map +1 -0
  241. package/dist/trpc/routers/workflows.d.ts +681 -0
  242. package/dist/trpc/routers/workflows.d.ts.map +1 -0
  243. package/dist/trpc/routers/workflows.js +725 -0
  244. package/dist/trpc/routers/workflows.js.map +1 -0
  245. package/dist/trpc/routers/workspace.d.ts +127 -0
  246. package/dist/trpc/routers/workspace.d.ts.map +1 -0
  247. package/dist/trpc/routers/workspace.js +203 -0
  248. package/dist/trpc/routers/workspace.js.map +1 -0
  249. package/dist/unified-websocket-runner.d.ts +280 -0
  250. package/dist/unified-websocket-runner.d.ts.map +1 -0
  251. package/dist/unified-websocket-runner.js +3840 -0
  252. package/dist/unified-websocket-runner.js.map +1 -0
  253. package/dist/workspace-api.d.ts +14 -0
  254. package/dist/workspace-api.d.ts.map +1 -0
  255. package/dist/workspace-api.js +104 -0
  256. package/dist/workspace-api.js.map +1 -0
  257. package/dist/ws-adapter.d.ts +25 -0
  258. package/dist/ws-adapter.d.ts.map +1 -0
  259. package/dist/ws-adapter.js +63 -0
  260. package/dist/ws-adapter.js.map +1 -0
  261. package/package.json +92 -0
@@ -0,0 +1,730 @@
1
+ import { access, readdir } from "node:fs/promises";
2
+ import { constants as fsConstants } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { basename, join } from "node:path";
5
+ import { getProvider, isProviderConfigured, listRegisteredProviderIds, providerCapabilities, RECOMMENDED_MODELS } from "@nodetool-ai/runtime";
6
+ import { readCachedHfModels, searchCachedHfModels, getModelsByHfType, deleteCachedHfModel, getHuggingfaceFileInfos } from "@nodetool-ai/huggingface";
7
+ const LLAMA_CPP_MODEL_TYPES = new Set([
8
+ "llama_cpp_model",
9
+ "llama_cpp",
10
+ "hf.gguf"
11
+ ]);
12
+ function jsonResponse(data, init) {
13
+ return new Response(JSON.stringify(data), {
14
+ status: init?.status ?? 200,
15
+ headers: {
16
+ "content-type": "application/json",
17
+ ...(init?.headers ?? {})
18
+ }
19
+ });
20
+ }
21
+ function errorResponse(status, detail) {
22
+ return jsonResponse({ detail }, { status });
23
+ }
24
+ function isProduction() {
25
+ const value = (process.env.NODETOOL_ENV ??
26
+ process.env.NODE_ENV ??
27
+ "").toLowerCase();
28
+ return value === "production";
29
+ }
30
+ function dedupeModels(models) {
31
+ const seen = new Set();
32
+ const deduped = [];
33
+ for (const model of models) {
34
+ const key = `${model.repo_id ?? ""}::${model.path ?? ""}`;
35
+ if (!seen.has(key)) {
36
+ seen.add(key);
37
+ deduped.push(model);
38
+ }
39
+ }
40
+ return deduped;
41
+ }
42
+ function normalizePatterns(patterns) {
43
+ if (patterns == null)
44
+ return null;
45
+ if (typeof patterns === "string")
46
+ return patterns ? [patterns] : null;
47
+ const cleaned = patterns.filter((p) => Boolean(p));
48
+ return cleaned.length > 0 ? cleaned : null;
49
+ }
50
+ function wildcardToRegExp(pattern) {
51
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
52
+ const regex = `^${escaped.replaceAll("*", ".*").replaceAll("?", ".")}$`;
53
+ return new RegExp(regex);
54
+ }
55
+ function matchesPattern(value, pattern) {
56
+ return wildcardToRegExp(pattern).test(value);
57
+ }
58
+ function isIgnored(path, ignorePatterns) {
59
+ if (!ignorePatterns || ignorePatterns.length === 0)
60
+ return false;
61
+ return ignorePatterns.some((pattern) => matchesPattern(path, pattern));
62
+ }
63
+ function isDownloadedFromFiles(files, allowPatterns, ignorePatterns) {
64
+ if (files.length === 0)
65
+ return false;
66
+ if (allowPatterns && allowPatterns.length > 0) {
67
+ return allowPatterns.every((pattern) => files.some((path) => matchesPattern(path, pattern) && !isIgnored(path, ignorePatterns)));
68
+ }
69
+ return files.some((path) => !isIgnored(path, ignorePatterns));
70
+ }
71
+ function getHfCacheRoot() {
72
+ const cacheEnv = process.env.HUGGINGFACE_HUB_CACHE ?? process.env.HF_HOME;
73
+ return cacheEnv
74
+ ? join(cacheEnv, "hub")
75
+ : join(homedir(), ".cache", "huggingface", "hub");
76
+ }
77
+ function repoToCacheDir(repoId) {
78
+ return `models--${repoId.replaceAll("/", "--")}`;
79
+ }
80
+ async function pathExists(path) {
81
+ try {
82
+ await access(path, fsConstants.F_OK);
83
+ return true;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ async function listSnapshotDirs(repoId) {
90
+ const snapshotsRoot = join(getHfCacheRoot(), repoToCacheDir(repoId), "snapshots");
91
+ if (!(await pathExists(snapshotsRoot)))
92
+ return [];
93
+ const entries = await readdir(snapshotsRoot, { withFileTypes: true });
94
+ return entries
95
+ .filter((entry) => entry.isDirectory())
96
+ .map((entry) => join(snapshotsRoot, entry.name));
97
+ }
98
+ async function repoFileInCache(repoId, relativePath) {
99
+ const snapshotDirs = await listSnapshotDirs(repoId);
100
+ for (const snapshotDir of snapshotDirs) {
101
+ if (await pathExists(join(snapshotDir, relativePath))) {
102
+ return true;
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ async function listRepoCachedFiles(repoId) {
108
+ const snapshotDirs = await listSnapshotDirs(repoId);
109
+ const collected = new Set();
110
+ async function walk(root, current) {
111
+ const entries = await readdir(current, { withFileTypes: true });
112
+ for (const entry of entries) {
113
+ const full = join(current, entry.name);
114
+ if (entry.isDirectory()) {
115
+ await walk(root, full);
116
+ continue;
117
+ }
118
+ if (entry.isFile() || entry.isSymbolicLink()) {
119
+ const rel = full.slice(root.length + 1).replaceAll("\\", "/");
120
+ collected.add(rel);
121
+ }
122
+ }
123
+ }
124
+ for (const snapshotDir of snapshotDirs) {
125
+ await walk(snapshotDir, snapshotDir);
126
+ }
127
+ return [...collected];
128
+ }
129
+ async function hasCachedFiles(repoId) {
130
+ return (await listRepoCachedFiles(repoId)).length > 0;
131
+ }
132
+ function toUnifiedLanguageModel(model) {
133
+ return {
134
+ id: model.id,
135
+ type: "language_model",
136
+ name: model.name,
137
+ repo_id: null,
138
+ path: model.id,
139
+ downloaded: model.provider === "ollama" || model.provider === "llama_cpp",
140
+ tags: [model.provider]
141
+ };
142
+ }
143
+ function toUnifiedModel(model, type) {
144
+ return {
145
+ id: model.id,
146
+ type,
147
+ name: model.name,
148
+ repo_id: null,
149
+ path: model.id,
150
+ downloaded: model.provider === "ollama" || model.provider === "llama_cpp",
151
+ tags: [model.provider]
152
+ };
153
+ }
154
+ function toOllamaModel(model) {
155
+ return {
156
+ type: "llama_model",
157
+ name: model.name,
158
+ repo_id: model.id,
159
+ modified_at: "",
160
+ size: 0,
161
+ digest: "",
162
+ details: {}
163
+ };
164
+ }
165
+ // ---------------------------------------------------------------------------
166
+ // Provider access — delegates to the runtime's single provider registry.
167
+ // No duplicate registry here; the runtime's registerProvider() calls in
168
+ // providers/index.ts are the single source of truth.
169
+ // ---------------------------------------------------------------------------
170
+ import { PythonProvider, registerProvider } from "@nodetool-ai/runtime";
171
+ /**
172
+ * Register Python-only providers (HuggingFace Local, MLX) discovered
173
+ * via the Python stdio bridge. Call after the bridge has connected.
174
+ */
175
+ export async function registerPythonProviders(bridge) {
176
+ const providers = await bridge.listProviders();
177
+ const registered = [];
178
+ for (const info of providers) {
179
+ if (listRegisteredProviderIds().includes(info.id))
180
+ continue;
181
+ const secrets = {};
182
+ registerProvider(info.id, PythonProvider, {
183
+ _bridge: bridge,
184
+ _id: info.id,
185
+ ...secrets
186
+ });
187
+ registered.push(info.id);
188
+ }
189
+ return registered;
190
+ }
191
+ /** Returns only providers whose required credentials are present (env or DB) for the given user. */
192
+ async function getAvailableProviderIds(userId = "1") {
193
+ const ids = listRegisteredProviderIds();
194
+ const checks = await Promise.all(ids.map(async (id) => ({
195
+ id,
196
+ available: await isProviderConfigured(id, userId)
197
+ })));
198
+ return checks.filter((c) => c.available).map((c) => c.id);
199
+ }
200
+ async function instantiateProvider(provider, userId = "1") {
201
+ if (!(await isProviderConfigured(provider, userId)))
202
+ return null;
203
+ try {
204
+ return await getProvider(provider, userId);
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ }
210
+ async function getProvidersInfo(userId = "1") {
211
+ const infos = [];
212
+ for (const provider of await getAvailableProviderIds(userId)) {
213
+ const instance = await instantiateProvider(provider, userId);
214
+ if (!instance)
215
+ continue;
216
+ infos.push({ provider, capabilities: providerCapabilities(instance) });
217
+ }
218
+ return infos;
219
+ }
220
+ async function withProvider(provider, run, userId = "1") {
221
+ const instance = await instantiateProvider(provider, userId);
222
+ if (!instance) {
223
+ throw new Error(`Provider ${provider} not available`);
224
+ }
225
+ return run(instance);
226
+ }
227
+ async function getLanguageModelsByProvider(provider, userId = "1") {
228
+ try {
229
+ return await withProvider(provider, (instance) => instance.getAvailableLanguageModels(), userId);
230
+ }
231
+ catch {
232
+ return [];
233
+ }
234
+ }
235
+ async function getImageModelsByProvider(provider, userId = "1") {
236
+ try {
237
+ return await withProvider(provider, (instance) => instance.getAvailableImageModels(), userId);
238
+ }
239
+ catch {
240
+ return [];
241
+ }
242
+ }
243
+ async function getTtsModelsByProvider(provider, userId = "1") {
244
+ try {
245
+ return await withProvider(provider, (instance) => instance.getAvailableTTSModels(), userId);
246
+ }
247
+ catch {
248
+ return [];
249
+ }
250
+ }
251
+ async function getAsrModelsByProvider(provider, userId = "1") {
252
+ try {
253
+ return await withProvider(provider, (instance) => instance.getAvailableASRModels(), userId);
254
+ }
255
+ catch {
256
+ return [];
257
+ }
258
+ }
259
+ async function getVideoModelsByProvider(provider, userId = "1") {
260
+ try {
261
+ return await withProvider(provider, (instance) => instance.getAvailableVideoModels(), userId);
262
+ }
263
+ catch {
264
+ return [];
265
+ }
266
+ }
267
+ async function getEmbeddingModelsByProvider(provider, userId = "1") {
268
+ try {
269
+ return await withProvider(provider, (instance) => instance.getAvailableEmbeddingModels(), userId);
270
+ }
271
+ catch {
272
+ return [];
273
+ }
274
+ }
275
+ async function isServerReachable(url) {
276
+ const controller = new AbortController();
277
+ const timer = setTimeout(() => controller.abort(), 2000);
278
+ try {
279
+ const response = await fetch(url, { signal: controller.signal });
280
+ return response.ok;
281
+ }
282
+ catch {
283
+ return false;
284
+ }
285
+ finally {
286
+ clearTimeout(timer);
287
+ }
288
+ }
289
+ async function serverAllowsModel(model, servers) {
290
+ // Local server providers need reachability check
291
+ if (model.provider === "ollama")
292
+ return servers.ollama ?? false;
293
+ if (model.provider === "llama_cpp")
294
+ return servers.llama_cpp ?? false;
295
+ if (model.provider === "lmstudio")
296
+ return servers.lmstudio ?? false;
297
+ if (model.provider === "vllm")
298
+ return servers.vllm ?? false;
299
+ // API-key providers: available if key is set (env or secrets DB)
300
+ if (model.provider)
301
+ return await isProviderConfigured(model.provider);
302
+ return true;
303
+ }
304
+ async function getServerAvailability() {
305
+ // Skip localhost probes in production — local servers won't be available
306
+ if (isProduction())
307
+ return { ollama: false, llama_cpp: false };
308
+ const ollamaUrl = (process.env.OLLAMA_API_URL ?? "http://127.0.0.1:11434").replace(/\/+$/, "");
309
+ const llamaUrl = process.env.LLAMA_CPP_URL?.replace(/\/+$/, "") ?? "";
310
+ const [ollama, llama] = await Promise.all([
311
+ isServerReachable(`${ollamaUrl}/api/tags`),
312
+ llamaUrl
313
+ ? isServerReachable(`${llamaUrl}/v1/models`)
314
+ : Promise.resolve(false)
315
+ ]);
316
+ return { ollama, llama_cpp: llama };
317
+ }
318
+ async function recommendedModels(checkServers) {
319
+ const models = [...RECOMMENDED_MODELS];
320
+ if (!checkServers)
321
+ return models;
322
+ const servers = await getServerAvailability();
323
+ const filtered = [];
324
+ for (const model of models) {
325
+ if (await serverAllowsModel(model, servers)) {
326
+ filtered.push(model);
327
+ }
328
+ }
329
+ return filtered;
330
+ }
331
+ function selectRecommended(modality, task) {
332
+ return RECOMMENDED_MODELS.filter((model) => model.modality === modality && (!task || model.task === task));
333
+ }
334
+ async function getAllModels(userId = "1") {
335
+ const all = [];
336
+ // Always include recommended models as a baseline
337
+ all.push(...RECOMMENDED_MODELS);
338
+ // Include language models from all available providers
339
+ const availableIds = await getAvailableProviderIds(userId);
340
+ for (const providerId of availableIds) {
341
+ try {
342
+ const models = await getLanguageModelsByProvider(providerId, userId);
343
+ all.push(...models.map(toUnifiedLanguageModel));
344
+ }
345
+ catch {
346
+ // Provider unavailable — skip
347
+ }
348
+ }
349
+ // Include HuggingFace cached/recommended models
350
+ if (!isProduction()) {
351
+ try {
352
+ const hfModels = await readCachedHfModels();
353
+ all.push(...hfModels);
354
+ }
355
+ catch {
356
+ // HF models unavailable — continue without them
357
+ }
358
+ }
359
+ return dedupeModels(all);
360
+ }
361
+ async function parseJsonBody(request) {
362
+ const contentType = request.headers.get("content-type") ?? "";
363
+ if (!contentType.toLowerCase().includes("application/json")) {
364
+ return null;
365
+ }
366
+ try {
367
+ return (await request.json());
368
+ }
369
+ catch {
370
+ return null;
371
+ }
372
+ }
373
+ function pathFromModelsPrefix(pathname) {
374
+ const base = "/api/models";
375
+ if (pathname === base)
376
+ return "";
377
+ if (pathname.startsWith(`${base}/`))
378
+ return pathname.slice(base.length);
379
+ return pathname;
380
+ }
381
+ function parseProvider(path, prefix) {
382
+ if (!path.startsWith(prefix))
383
+ return null;
384
+ const value = decodeURIComponent(path.slice(prefix.length));
385
+ if (!value)
386
+ return null;
387
+ return value;
388
+ }
389
+ async function checkHfCache(body) {
390
+ const allowPatterns = normalizePatterns(body.allow_pattern);
391
+ const ignorePatterns = normalizePatterns(body.ignore_pattern);
392
+ const files = await listRepoCachedFiles(body.repo_id);
393
+ const missing = [];
394
+ if (allowPatterns) {
395
+ for (const pattern of allowPatterns) {
396
+ const matched = files.some((path) => matchesPattern(path, pattern) && !isIgnored(path, ignorePatterns));
397
+ if (!matched) {
398
+ missing.push(pattern);
399
+ }
400
+ }
401
+ }
402
+ return {
403
+ repo_id: body.repo_id,
404
+ all_present: missing.length === 0,
405
+ total_files: files.length,
406
+ missing
407
+ };
408
+ }
409
+ async function isLlamaCppModelCached(repoId, filePath) {
410
+ if (await repoFileInCache(repoId, filePath)) {
411
+ return true;
412
+ }
413
+ const cacheRoot = process.env.LLAMA_CPP_CACHE_DIR ??
414
+ join(homedir(), "Library", "Caches", "llama.cpp", "hf");
415
+ const repoDir = join(cacheRoot, repoToCacheDir(repoId), "snapshots");
416
+ if (!(await pathExists(repoDir)))
417
+ return false;
418
+ const snapshots = await readdir(repoDir, { withFileTypes: true });
419
+ for (const snapshot of snapshots) {
420
+ if (!snapshot.isDirectory())
421
+ continue;
422
+ if (await pathExists(join(repoDir, snapshot.name, filePath))) {
423
+ return true;
424
+ }
425
+ if (await pathExists(join(repoDir, snapshot.name, basename(filePath)))) {
426
+ return true;
427
+ }
428
+ }
429
+ return false;
430
+ }
431
+ async function fastCacheStatus(items) {
432
+ return Promise.all(items.map(async (item) => {
433
+ const allowPatterns = normalizePatterns(item.allow_patterns);
434
+ const ignorePatterns = normalizePatterns(item.ignore_patterns);
435
+ if (item.model_type && LLAMA_CPP_MODEL_TYPES.has(item.model_type)) {
436
+ if (!item.path)
437
+ return { key: item.key, downloaded: false };
438
+ return {
439
+ key: item.key,
440
+ downloaded: await isLlamaCppModelCached(item.repo_id, item.path)
441
+ };
442
+ }
443
+ if (item.path) {
444
+ return {
445
+ key: item.key,
446
+ downloaded: await repoFileInCache(item.repo_id, item.path)
447
+ };
448
+ }
449
+ const files = await listRepoCachedFiles(item.repo_id);
450
+ return {
451
+ key: item.key,
452
+ downloaded: isDownloadedFromFiles(files, allowPatterns, ignorePatterns)
453
+ };
454
+ }));
455
+ }
456
+ export async function handleModelsApiRequest(request) {
457
+ const url = new URL(request.url);
458
+ const path = pathFromModelsPrefix(url.pathname);
459
+ const userId = request.headers.get("x-user-id") ?? "1";
460
+ if (path === "/providers") {
461
+ if (request.method !== "GET")
462
+ return errorResponse(405, "Method not allowed");
463
+ return jsonResponse(await getProvidersInfo(userId));
464
+ }
465
+ if (path === "/recommended") {
466
+ if (request.method !== "GET")
467
+ return errorResponse(405, "Method not allowed");
468
+ const checkServers = url.searchParams.get("check_servers") === "true";
469
+ return jsonResponse(await recommendedModels(checkServers));
470
+ }
471
+ if (path === "/recommended/image") {
472
+ if (request.method !== "GET")
473
+ return errorResponse(405, "Method not allowed");
474
+ return jsonResponse(selectRecommended("image"));
475
+ }
476
+ if (path === "/recommended/image/text-to-image") {
477
+ if (request.method !== "GET")
478
+ return errorResponse(405, "Method not allowed");
479
+ return jsonResponse(selectRecommended("image", "text_to_image"));
480
+ }
481
+ if (path === "/recommended/image/image-to-image") {
482
+ if (request.method !== "GET")
483
+ return errorResponse(405, "Method not allowed");
484
+ return jsonResponse(selectRecommended("image", "image_to_image"));
485
+ }
486
+ if (path === "/recommended/language") {
487
+ if (request.method !== "GET")
488
+ return errorResponse(405, "Method not allowed");
489
+ return jsonResponse(selectRecommended("language"));
490
+ }
491
+ if (path === "/recommended/language/text-generation") {
492
+ if (request.method !== "GET")
493
+ return errorResponse(405, "Method not allowed");
494
+ return jsonResponse(selectRecommended("language", "text_generation"));
495
+ }
496
+ if (path === "/recommended/language/embedding") {
497
+ if (request.method !== "GET")
498
+ return errorResponse(405, "Method not allowed");
499
+ return jsonResponse(selectRecommended("language", "embedding"));
500
+ }
501
+ if (path === "/recommended/asr") {
502
+ if (request.method !== "GET")
503
+ return errorResponse(405, "Method not allowed");
504
+ return jsonResponse(selectRecommended("asr"));
505
+ }
506
+ if (path === "/recommended/tts") {
507
+ if (request.method !== "GET")
508
+ return errorResponse(405, "Method not allowed");
509
+ return jsonResponse(selectRecommended("tts"));
510
+ }
511
+ if (path === "/recommended/video/text-to-video") {
512
+ if (request.method !== "GET")
513
+ return errorResponse(405, "Method not allowed");
514
+ return jsonResponse(selectRecommended("video", "text_to_video"));
515
+ }
516
+ if (path === "/recommended/video/image-to-video") {
517
+ if (request.method !== "GET")
518
+ return errorResponse(405, "Method not allowed");
519
+ return jsonResponse(selectRecommended("video", "image_to_video"));
520
+ }
521
+ if (path === "/all") {
522
+ if (request.method !== "GET")
523
+ return errorResponse(405, "Method not allowed");
524
+ return jsonResponse(await getAllModels(userId));
525
+ }
526
+ if (path === "/huggingface") {
527
+ if (request.method === "GET") {
528
+ if (isProduction())
529
+ return jsonResponse([]);
530
+ try {
531
+ const models = await readCachedHfModels();
532
+ return jsonResponse(models);
533
+ }
534
+ catch {
535
+ return jsonResponse([]);
536
+ }
537
+ }
538
+ if (request.method === "DELETE") {
539
+ const repoId = url.searchParams.get("repo_id");
540
+ if (!repoId)
541
+ return errorResponse(400, "Missing repo_id parameter");
542
+ if (isProduction())
543
+ return jsonResponse(false);
544
+ try {
545
+ const deleted = await deleteCachedHfModel(repoId);
546
+ return jsonResponse(deleted);
547
+ }
548
+ catch {
549
+ return jsonResponse(false);
550
+ }
551
+ }
552
+ return errorResponse(405, "Method not allowed");
553
+ }
554
+ if (path === "/huggingface/search") {
555
+ if (request.method !== "GET")
556
+ return errorResponse(405, "Method not allowed");
557
+ if (isProduction())
558
+ return jsonResponse([]);
559
+ const rawQuery = url.searchParams.get("query") ?? undefined;
560
+ const type = url.searchParams.get("type") ?? undefined;
561
+ // Wrap bare queries with wildcards so "whisper" matches "openai/whisper-small"
562
+ const query = rawQuery && !rawQuery.includes("*") ? `*${rawQuery}*` : rawQuery;
563
+ try {
564
+ const models = await searchCachedHfModels(query ? [query] : undefined, type ? [type] : undefined);
565
+ return jsonResponse(models);
566
+ }
567
+ catch {
568
+ return jsonResponse([]);
569
+ }
570
+ }
571
+ if (path.startsWith("/huggingface/type/")) {
572
+ if (request.method !== "GET")
573
+ return errorResponse(405, "Method not allowed");
574
+ const modelType = decodeURIComponent(path.slice("/huggingface/type/".length));
575
+ if (!modelType)
576
+ return jsonResponse([]);
577
+ try {
578
+ const models = await getModelsByHfType(modelType);
579
+ return jsonResponse(models);
580
+ }
581
+ catch {
582
+ return jsonResponse([]);
583
+ }
584
+ }
585
+ if (path === "/ollama") {
586
+ if (request.method === "DELETE") {
587
+ if (isProduction())
588
+ return jsonResponse(false);
589
+ return jsonResponse(false);
590
+ }
591
+ if (request.method !== "GET")
592
+ return errorResponse(405, "Method not allowed");
593
+ const models = await getLanguageModelsByProvider("ollama", userId);
594
+ return jsonResponse(models.map((model) => toOllamaModel(model)));
595
+ }
596
+ const llmProvider = parseProvider(path, "/llm/");
597
+ if (llmProvider) {
598
+ if (request.method !== "GET")
599
+ return errorResponse(405, "Method not allowed");
600
+ return jsonResponse(await getLanguageModelsByProvider(llmProvider, userId));
601
+ }
602
+ const imageProvider = parseProvider(path, "/image/");
603
+ if (imageProvider) {
604
+ if (request.method !== "GET")
605
+ return errorResponse(405, "Method not allowed");
606
+ return jsonResponse(await getImageModelsByProvider(imageProvider, userId));
607
+ }
608
+ if (path === "/tts") {
609
+ if (request.method !== "GET")
610
+ return errorResponse(405, "Method not allowed");
611
+ const availableIds = await getAvailableProviderIds(userId);
612
+ const providers = await Promise.all(availableIds.map((provider) => getTtsModelsByProvider(provider, userId)));
613
+ return jsonResponse(providers.flat());
614
+ }
615
+ const ttsProvider = parseProvider(path, "/tts/");
616
+ if (ttsProvider) {
617
+ if (request.method !== "GET")
618
+ return errorResponse(405, "Method not allowed");
619
+ return jsonResponse(await getTtsModelsByProvider(ttsProvider, userId));
620
+ }
621
+ const asrProvider = parseProvider(path, "/asr/");
622
+ if (asrProvider) {
623
+ if (request.method !== "GET")
624
+ return errorResponse(405, "Method not allowed");
625
+ return jsonResponse(await getAsrModelsByProvider(asrProvider, userId));
626
+ }
627
+ const videoProvider = parseProvider(path, "/video/");
628
+ if (videoProvider) {
629
+ if (request.method !== "GET")
630
+ return errorResponse(405, "Method not allowed");
631
+ return jsonResponse(await getVideoModelsByProvider(videoProvider, userId));
632
+ }
633
+ const embeddingProvider = parseProvider(path, "/embedding/");
634
+ if (embeddingProvider) {
635
+ if (request.method !== "GET")
636
+ return errorResponse(405, "Method not allowed");
637
+ return jsonResponse(await getEmbeddingModelsByProvider(embeddingProvider, userId));
638
+ }
639
+ if (path === "/ollama_model_info") {
640
+ if (request.method !== "GET")
641
+ return errorResponse(405, "Method not allowed");
642
+ return jsonResponse(null);
643
+ }
644
+ if (path === "/huggingface/try_cache_files") {
645
+ if (request.method !== "POST")
646
+ return errorResponse(405, "Method not allowed");
647
+ const body = await parseJsonBody(request);
648
+ if (!body)
649
+ return errorResponse(400, "Invalid JSON body");
650
+ const checked = await Promise.all(body.map(async (entry) => {
651
+ const repoId = entry.repo_id ?? "";
652
+ const repoPath = entry.path ?? "";
653
+ return {
654
+ repo_id: repoId,
655
+ path: repoPath,
656
+ downloaded: repoId.length > 0 && repoPath.length > 0
657
+ ? await repoFileInCache(repoId, repoPath)
658
+ : false
659
+ };
660
+ }));
661
+ return jsonResponse(checked);
662
+ }
663
+ if (path === "/huggingface/try_cache_repos") {
664
+ if (request.method !== "POST")
665
+ return errorResponse(405, "Method not allowed");
666
+ const body = await parseJsonBody(request);
667
+ if (!body)
668
+ return errorResponse(400, "Invalid JSON body");
669
+ const checked = await Promise.all(body.map(async (repoId) => ({
670
+ repo_id: repoId,
671
+ downloaded: await hasCachedFiles(repoId)
672
+ })));
673
+ return jsonResponse(checked);
674
+ }
675
+ if (path === "/huggingface/check_cache") {
676
+ if (request.method !== "POST")
677
+ return errorResponse(405, "Method not allowed");
678
+ const body = await parseJsonBody(request);
679
+ if (!body ||
680
+ typeof body.repo_id !== "string" ||
681
+ body.repo_id.length === 0) {
682
+ return errorResponse(400, "Invalid JSON body");
683
+ }
684
+ return jsonResponse(await checkHfCache(body));
685
+ }
686
+ if (path === "/huggingface/cache_status") {
687
+ if (request.method !== "POST")
688
+ return errorResponse(405, "Method not allowed");
689
+ const body = await parseJsonBody(request);
690
+ if (!body)
691
+ return errorResponse(400, "Invalid JSON body");
692
+ return jsonResponse(await fastCacheStatus(body));
693
+ }
694
+ if (path === "/pull_ollama_model") {
695
+ if (request.method !== "POST")
696
+ return errorResponse(405, "Method not allowed");
697
+ if (isProduction())
698
+ return errorResponse(503, {
699
+ status: "unavailable",
700
+ message: "Not available in production"
701
+ });
702
+ // Streaming Ollama model pulls require Server-Sent Events with progress deltas.
703
+ // The TS standalone server does not implement this streaming protocol yet.
704
+ // Clients should use the Python backend or manage Ollama pulls directly via the Ollama HTTP API.
705
+ return errorResponse(501, "Streaming Ollama model pulls are not available in the TS standalone server. Use the Ollama API directly or the Python backend.");
706
+ }
707
+ if (path === "/huggingface/file_info") {
708
+ if (request.method !== "POST")
709
+ return errorResponse(405, "Method not allowed");
710
+ if (isProduction())
711
+ return jsonResponse([]);
712
+ const body = await parseJsonBody(request);
713
+ if (!body)
714
+ return errorResponse(400, "Invalid JSON body");
715
+ try {
716
+ const { getSecret } = await import("@nodetool-ai/models");
717
+ const token = (await getSecret("HF_TOKEN", "1")) ?? undefined;
718
+ const infos = await getHuggingfaceFileInfos(body, token);
719
+ return jsonResponse(infos);
720
+ }
721
+ catch {
722
+ return jsonResponse([]);
723
+ }
724
+ }
725
+ return null;
726
+ }
727
+ export function toUnifiedModelsFromLanguage(models) {
728
+ return models.map((model) => toUnifiedModel(model, "language_model"));
729
+ }
730
+ //# sourceMappingURL=models-api.js.map