@soederpop/luca 0.0.2

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 (358) hide show
  1. package/CLAUDE.md +71 -0
  2. package/README.md +78 -0
  3. package/bun.lock +2928 -0
  4. package/bunfig.toml +3 -0
  5. package/commands/audit-docs.ts +740 -0
  6. package/commands/build-scaffolds.ts +154 -0
  7. package/commands/generate-api-docs.ts +114 -0
  8. package/commands/update-introspection.ts +67 -0
  9. package/docs/CLI.md +335 -0
  10. package/docs/README.md +88 -0
  11. package/docs/TABLE-OF-CONTENTS.md +157 -0
  12. package/docs/apis/clients/elevenlabs.md +84 -0
  13. package/docs/apis/clients/graph.md +56 -0
  14. package/docs/apis/clients/openai.md +69 -0
  15. package/docs/apis/clients/rest.md +41 -0
  16. package/docs/apis/clients/websocket.md +107 -0
  17. package/docs/apis/features/agi/assistant.md +471 -0
  18. package/docs/apis/features/agi/assistants-manager.md +154 -0
  19. package/docs/apis/features/agi/claude-code.md +602 -0
  20. package/docs/apis/features/agi/conversation-history.md +352 -0
  21. package/docs/apis/features/agi/conversation.md +333 -0
  22. package/docs/apis/features/agi/docs-reader.md +121 -0
  23. package/docs/apis/features/agi/openai-codex.md +318 -0
  24. package/docs/apis/features/agi/openapi.md +138 -0
  25. package/docs/apis/features/agi/semantic-search.md +387 -0
  26. package/docs/apis/features/agi/skills-library.md +216 -0
  27. package/docs/apis/features/node/container-link.md +133 -0
  28. package/docs/apis/features/node/content-db.md +313 -0
  29. package/docs/apis/features/node/disk-cache.md +379 -0
  30. package/docs/apis/features/node/dns.md +651 -0
  31. package/docs/apis/features/node/docker.md +705 -0
  32. package/docs/apis/features/node/downloader.md +81 -0
  33. package/docs/apis/features/node/esbuild.md +59 -0
  34. package/docs/apis/features/node/file-manager.md +182 -0
  35. package/docs/apis/features/node/fs.md +581 -0
  36. package/docs/apis/features/node/git.md +330 -0
  37. package/docs/apis/features/node/google-auth.md +174 -0
  38. package/docs/apis/features/node/google-calendar.md +187 -0
  39. package/docs/apis/features/node/google-docs.md +151 -0
  40. package/docs/apis/features/node/google-drive.md +225 -0
  41. package/docs/apis/features/node/google-sheets.md +179 -0
  42. package/docs/apis/features/node/grep.md +290 -0
  43. package/docs/apis/features/node/helpers.md +135 -0
  44. package/docs/apis/features/node/ink.md +334 -0
  45. package/docs/apis/features/node/ipc-socket.md +260 -0
  46. package/docs/apis/features/node/json-tree.md +86 -0
  47. package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
  48. package/docs/apis/features/node/networking.md +281 -0
  49. package/docs/apis/features/node/nlp.md +133 -0
  50. package/docs/apis/features/node/opener.md +97 -0
  51. package/docs/apis/features/node/os.md +118 -0
  52. package/docs/apis/features/node/package-finder.md +402 -0
  53. package/docs/apis/features/node/postgres.md +212 -0
  54. package/docs/apis/features/node/proc.md +430 -0
  55. package/docs/apis/features/node/process-manager.md +210 -0
  56. package/docs/apis/features/node/python.md +278 -0
  57. package/docs/apis/features/node/repl.md +88 -0
  58. package/docs/apis/features/node/runpod.md +673 -0
  59. package/docs/apis/features/node/secure-shell.md +169 -0
  60. package/docs/apis/features/node/semantic-search.md +401 -0
  61. package/docs/apis/features/node/sqlite.md +211 -0
  62. package/docs/apis/features/node/telegram.md +254 -0
  63. package/docs/apis/features/node/tts.md +118 -0
  64. package/docs/apis/features/node/ui.md +703 -0
  65. package/docs/apis/features/node/vault.md +64 -0
  66. package/docs/apis/features/node/vm.md +84 -0
  67. package/docs/apis/features/node/window-manager.md +337 -0
  68. package/docs/apis/features/node/yaml-tree.md +85 -0
  69. package/docs/apis/features/node/yaml.md +176 -0
  70. package/docs/apis/features/web/asset-loader.md +47 -0
  71. package/docs/apis/features/web/container-link.md +133 -0
  72. package/docs/apis/features/web/esbuild.md +59 -0
  73. package/docs/apis/features/web/helpers.md +135 -0
  74. package/docs/apis/features/web/network.md +30 -0
  75. package/docs/apis/features/web/speech.md +55 -0
  76. package/docs/apis/features/web/vault.md +64 -0
  77. package/docs/apis/features/web/vm.md +84 -0
  78. package/docs/apis/features/web/voice.md +67 -0
  79. package/docs/apis/servers/express.md +127 -0
  80. package/docs/apis/servers/mcp.md +213 -0
  81. package/docs/apis/servers/websocket.md +99 -0
  82. package/docs/documentation-audit.md +134 -0
  83. package/docs/examples/content-db.md +77 -0
  84. package/docs/examples/disk-cache.md +83 -0
  85. package/docs/examples/docker.md +101 -0
  86. package/docs/examples/downloader.md +70 -0
  87. package/docs/examples/esbuild.md +80 -0
  88. package/docs/examples/file-manager.md +82 -0
  89. package/docs/examples/fs.md +83 -0
  90. package/docs/examples/git.md +85 -0
  91. package/docs/examples/google-auth.md +88 -0
  92. package/docs/examples/google-calendar.md +94 -0
  93. package/docs/examples/google-docs.md +82 -0
  94. package/docs/examples/google-drive.md +96 -0
  95. package/docs/examples/google-sheets.md +95 -0
  96. package/docs/examples/grep.md +85 -0
  97. package/docs/examples/ink-blocks.md +75 -0
  98. package/docs/examples/ink-renderer.md +41 -0
  99. package/docs/examples/ink.md +103 -0
  100. package/docs/examples/ipc-socket.md +103 -0
  101. package/docs/examples/json-tree.md +91 -0
  102. package/docs/examples/launcher-app-command-listener.md +120 -0
  103. package/docs/examples/networking.md +58 -0
  104. package/docs/examples/nlp.md +91 -0
  105. package/docs/examples/opener.md +78 -0
  106. package/docs/examples/os.md +72 -0
  107. package/docs/examples/package-finder.md +89 -0
  108. package/docs/examples/port-exposer.md +89 -0
  109. package/docs/examples/postgres.md +91 -0
  110. package/docs/examples/proc.md +81 -0
  111. package/docs/examples/process-manager.md +79 -0
  112. package/docs/examples/python.md +91 -0
  113. package/docs/examples/repl.md +93 -0
  114. package/docs/examples/runpod.md +119 -0
  115. package/docs/examples/secure-shell.md +92 -0
  116. package/docs/examples/sqlite.md +86 -0
  117. package/docs/examples/telegram.md +77 -0
  118. package/docs/examples/tts.md +86 -0
  119. package/docs/examples/ui.md +80 -0
  120. package/docs/examples/vault.md +70 -0
  121. package/docs/examples/vm.md +86 -0
  122. package/docs/examples/window-manager.md +125 -0
  123. package/docs/examples/yaml-tree.md +93 -0
  124. package/docs/examples/yaml.md +104 -0
  125. package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
  126. package/docs/ideas/container-use-api.md +9 -0
  127. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  128. package/docs/ideas/feature-stacks.md +22 -0
  129. package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
  130. package/docs/ideas/mcp-design.md +9 -0
  131. package/docs/ideas/web-container-debugging-feature.md +13 -0
  132. package/docs/introspection-audit.md +49 -0
  133. package/docs/introspection.md +154 -0
  134. package/docs/mcp/readme.md +162 -0
  135. package/docs/models.ts +38 -0
  136. package/docs/philosophy.md +85 -0
  137. package/docs/principles.md +7 -0
  138. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  139. package/docs/prompts/mcp-test-easy-command.md +27 -0
  140. package/docs/reports/assistant-bugs.md +38 -0
  141. package/docs/reports/attach-pattern-usage.md +18 -0
  142. package/docs/reports/code-audit-results.md +391 -0
  143. package/docs/reports/introspection-audit-tasks.md +378 -0
  144. package/docs/reports/luca-mcp-improvements.md +128 -0
  145. package/docs/scaffolds/client.md +140 -0
  146. package/docs/scaffolds/command.md +106 -0
  147. package/docs/scaffolds/endpoint.md +176 -0
  148. package/docs/scaffolds/feature.md +148 -0
  149. package/docs/scaffolds/server.md +187 -0
  150. package/docs/tasks/web-container-helper-discovery.md +71 -0
  151. package/docs/todos.md +1 -0
  152. package/docs/tutorials/01-getting-started.md +106 -0
  153. package/docs/tutorials/02-container.md +210 -0
  154. package/docs/tutorials/03-scripts.md +194 -0
  155. package/docs/tutorials/04-features-overview.md +196 -0
  156. package/docs/tutorials/05-state-and-events.md +171 -0
  157. package/docs/tutorials/06-servers.md +157 -0
  158. package/docs/tutorials/07-endpoints.md +198 -0
  159. package/docs/tutorials/08-commands.md +171 -0
  160. package/docs/tutorials/09-clients.md +162 -0
  161. package/docs/tutorials/10-creating-features.md +198 -0
  162. package/docs/tutorials/11-contentbase.md +191 -0
  163. package/docs/tutorials/12-assistants.md +215 -0
  164. package/docs/tutorials/13-introspection.md +147 -0
  165. package/docs/tutorials/14-type-system.md +174 -0
  166. package/docs/tutorials/15-project-patterns.md +222 -0
  167. package/docs/tutorials/16-google-features.md +534 -0
  168. package/docs/tutorials/17-tui-blocks.md +530 -0
  169. package/docs/tutorials/18-semantic-search.md +334 -0
  170. package/index.ts +1 -0
  171. package/luca.console.ts +9 -0
  172. package/main.py +6 -0
  173. package/package.json +154 -0
  174. package/pyproject.toml +7 -0
  175. package/scripts/animations/chrome-glitch.ts +55 -0
  176. package/scripts/animations/index.ts +16 -0
  177. package/scripts/animations/neon-pulse.ts +64 -0
  178. package/scripts/animations/types.ts +6 -0
  179. package/scripts/build-web.ts +28 -0
  180. package/scripts/examples/ask-luca-expert.ts +42 -0
  181. package/scripts/examples/assistant-questions.ts +12 -0
  182. package/scripts/examples/excalidraw-expert.ts +75 -0
  183. package/scripts/examples/expert-chat.ts +0 -0
  184. package/scripts/examples/file-manager.ts +14 -0
  185. package/scripts/examples/ideas.ts +12 -0
  186. package/scripts/examples/interactive-chat.ts +20 -0
  187. package/scripts/examples/openai-tool-calls.ts +113 -0
  188. package/scripts/examples/opening-a-web-browser.ts +5 -0
  189. package/scripts/examples/telegram-bot.ts +79 -0
  190. package/scripts/examples/telegram-ink-ui.ts +302 -0
  191. package/scripts/examples/using-assistant-with-mcp.ts +560 -0
  192. package/scripts/examples/using-claude-code.ts +10 -0
  193. package/scripts/examples/using-contentdb.ts +35 -0
  194. package/scripts/examples/using-conversations.ts +35 -0
  195. package/scripts/examples/using-disk-cache.ts +10 -0
  196. package/scripts/examples/using-docker-shell.ts +75 -0
  197. package/scripts/examples/using-elevenlabs.ts +25 -0
  198. package/scripts/examples/using-google-calendar.ts +57 -0
  199. package/scripts/examples/using-google-docs.ts +74 -0
  200. package/scripts/examples/using-google-drive.ts +74 -0
  201. package/scripts/examples/using-google-sheets.ts +89 -0
  202. package/scripts/examples/using-nlp.ts +55 -0
  203. package/scripts/examples/using-ollama.ts +10 -0
  204. package/scripts/examples/using-openai-codex.ts +23 -0
  205. package/scripts/examples/using-postgres.ts +55 -0
  206. package/scripts/examples/using-runpod.ts +32 -0
  207. package/scripts/examples/using-tts.ts +40 -0
  208. package/scripts/examples/vm-loading-esm-modules.ts +16 -0
  209. package/scripts/scaffold.ts +391 -0
  210. package/scripts/scratch.ts +15 -0
  211. package/scripts/test-command-listener.ts +123 -0
  212. package/scripts/test-window-manager-lifecycle.ts +86 -0
  213. package/scripts/test-window-manager.ts +43 -0
  214. package/scripts/update-introspection-data.ts +58 -0
  215. package/src/agi/README.md +14 -0
  216. package/src/agi/container.server.ts +114 -0
  217. package/src/agi/endpoints/ask.ts +60 -0
  218. package/src/agi/endpoints/conversations/[id].ts +45 -0
  219. package/src/agi/endpoints/conversations.ts +31 -0
  220. package/src/agi/endpoints/experts.ts +37 -0
  221. package/src/agi/features/assistant.ts +767 -0
  222. package/src/agi/features/assistants-manager.ts +260 -0
  223. package/src/agi/features/claude-code.ts +1111 -0
  224. package/src/agi/features/conversation-history.ts +497 -0
  225. package/src/agi/features/conversation.ts +799 -0
  226. package/src/agi/features/openai-codex.ts +631 -0
  227. package/src/agi/features/openapi.ts +438 -0
  228. package/src/agi/features/skills-library.ts +425 -0
  229. package/src/agi/index.ts +6 -0
  230. package/src/agi/lib/token-counter.ts +122 -0
  231. package/src/browser.ts +25 -0
  232. package/src/bus.ts +100 -0
  233. package/src/cli/cli.ts +70 -0
  234. package/src/client.ts +461 -0
  235. package/src/clients/civitai/index.ts +541 -0
  236. package/src/clients/client-template.ts +41 -0
  237. package/src/clients/comfyui/index.ts +597 -0
  238. package/src/clients/elevenlabs/index.ts +291 -0
  239. package/src/clients/openai/index.ts +451 -0
  240. package/src/clients/supabase/index.ts +366 -0
  241. package/src/command.ts +164 -0
  242. package/src/commands/chat.ts +182 -0
  243. package/src/commands/console.ts +192 -0
  244. package/src/commands/describe.ts +433 -0
  245. package/src/commands/eval.ts +116 -0
  246. package/src/commands/help.ts +214 -0
  247. package/src/commands/index.ts +14 -0
  248. package/src/commands/mcp.ts +64 -0
  249. package/src/commands/prompt.ts +807 -0
  250. package/src/commands/run.ts +257 -0
  251. package/src/commands/sandbox-mcp.ts +439 -0
  252. package/src/commands/scaffold.ts +79 -0
  253. package/src/commands/serve.ts +172 -0
  254. package/src/container.ts +781 -0
  255. package/src/endpoint.ts +340 -0
  256. package/src/feature.ts +75 -0
  257. package/src/hash-object.ts +97 -0
  258. package/src/helper.ts +543 -0
  259. package/src/introspection/generated.agi.ts +23388 -0
  260. package/src/introspection/generated.node.ts +18899 -0
  261. package/src/introspection/generated.web.ts +2021 -0
  262. package/src/introspection/index.ts +256 -0
  263. package/src/introspection/scan.ts +912 -0
  264. package/src/node/container.ts +354 -0
  265. package/src/node/feature.ts +13 -0
  266. package/src/node/features/container-link.ts +558 -0
  267. package/src/node/features/content-db.ts +475 -0
  268. package/src/node/features/disk-cache.ts +382 -0
  269. package/src/node/features/dns.ts +655 -0
  270. package/src/node/features/docker.ts +912 -0
  271. package/src/node/features/downloader.ts +92 -0
  272. package/src/node/features/esbuild.ts +68 -0
  273. package/src/node/features/file-manager.ts +357 -0
  274. package/src/node/features/fs.ts +534 -0
  275. package/src/node/features/git.ts +492 -0
  276. package/src/node/features/google-auth.ts +502 -0
  277. package/src/node/features/google-calendar.ts +300 -0
  278. package/src/node/features/google-docs.ts +404 -0
  279. package/src/node/features/google-drive.ts +339 -0
  280. package/src/node/features/google-sheets.ts +279 -0
  281. package/src/node/features/grep.ts +406 -0
  282. package/src/node/features/helpers.ts +374 -0
  283. package/src/node/features/ink.ts +490 -0
  284. package/src/node/features/ipc-socket.ts +459 -0
  285. package/src/node/features/json-tree.ts +188 -0
  286. package/src/node/features/launcher-app-command-listener.ts +388 -0
  287. package/src/node/features/networking.ts +925 -0
  288. package/src/node/features/nlp.ts +211 -0
  289. package/src/node/features/opener.ts +166 -0
  290. package/src/node/features/os.ts +157 -0
  291. package/src/node/features/package-finder.ts +539 -0
  292. package/src/node/features/port-exposer.ts +342 -0
  293. package/src/node/features/postgres.ts +273 -0
  294. package/src/node/features/proc.ts +502 -0
  295. package/src/node/features/process-manager.ts +542 -0
  296. package/src/node/features/python.ts +444 -0
  297. package/src/node/features/repl.ts +194 -0
  298. package/src/node/features/runpod.ts +802 -0
  299. package/src/node/features/secure-shell.ts +248 -0
  300. package/src/node/features/semantic-search.ts +924 -0
  301. package/src/node/features/sqlite.ts +289 -0
  302. package/src/node/features/telegram.ts +342 -0
  303. package/src/node/features/tts.ts +184 -0
  304. package/src/node/features/ui.ts +857 -0
  305. package/src/node/features/vault.ts +164 -0
  306. package/src/node/features/vm.ts +312 -0
  307. package/src/node/features/window-manager.ts +804 -0
  308. package/src/node/features/yaml-tree.ts +149 -0
  309. package/src/node/features/yaml.ts +132 -0
  310. package/src/node.ts +70 -0
  311. package/src/react/index.ts +175 -0
  312. package/src/registry.ts +199 -0
  313. package/src/scaffolds/generated.ts +1613 -0
  314. package/src/scaffolds/template.ts +37 -0
  315. package/src/schemas/base.ts +255 -0
  316. package/src/server.ts +135 -0
  317. package/src/servers/express.ts +209 -0
  318. package/src/servers/mcp.ts +805 -0
  319. package/src/servers/socket.ts +120 -0
  320. package/src/state.ts +101 -0
  321. package/src/web/clients/socket.ts +82 -0
  322. package/src/web/container.ts +74 -0
  323. package/src/web/extension.ts +30 -0
  324. package/src/web/feature.ts +12 -0
  325. package/src/web/features/asset-loader.ts +64 -0
  326. package/src/web/features/container-link.ts +385 -0
  327. package/src/web/features/esbuild.ts +79 -0
  328. package/src/web/features/helpers.ts +267 -0
  329. package/src/web/features/network.ts +61 -0
  330. package/src/web/features/speech.ts +87 -0
  331. package/src/web/features/vault.ts +189 -0
  332. package/src/web/features/vm.ts +78 -0
  333. package/src/web/features/voice-recognition.ts +129 -0
  334. package/src/web/shims/isomorphic-vm.ts +149 -0
  335. package/test/bus.test.ts +134 -0
  336. package/test/clients-servers.test.ts +216 -0
  337. package/test/container-link.test.ts +274 -0
  338. package/test/features.test.ts +160 -0
  339. package/test/integration.test.ts +787 -0
  340. package/test/node-container.test.ts +121 -0
  341. package/test/rate-limit.test.ts +272 -0
  342. package/test/semantic-search.test.ts +550 -0
  343. package/test/state.test.ts +121 -0
  344. package/test-integration/assistant.test.ts +138 -0
  345. package/test-integration/assistants-manager.test.ts +123 -0
  346. package/test-integration/claude-code.test.ts +98 -0
  347. package/test-integration/conversation-history.test.ts +205 -0
  348. package/test-integration/conversation.test.ts +137 -0
  349. package/test-integration/elevenlabs.test.ts +55 -0
  350. package/test-integration/google-services.test.ts +80 -0
  351. package/test-integration/helpers.ts +89 -0
  352. package/test-integration/openai-codex.test.ts +93 -0
  353. package/test-integration/runpod.test.ts +58 -0
  354. package/test-integration/server-endpoints.test.ts +97 -0
  355. package/test-integration/skills-library.test.ts +157 -0
  356. package/test-integration/telegram.test.ts +46 -0
  357. package/tsconfig.json +58 -0
  358. package/uv.lock +8 -0
@@ -0,0 +1,541 @@
1
+ import {
2
+ Client,
3
+ type ClientOptions,
4
+ type ClientsInterface,
5
+ RestClient,
6
+ } from "@soederpop/luca/client";
7
+ import { Container, type ContainerContext } from "@soederpop/luca/container";
8
+ import { isEmpty, maxBy, omitBy } from "lodash-es";
9
+ import { NodeContainer } from "@soederpop/luca/node/container";
10
+ import { z } from 'zod'
11
+ import { ClientStateSchema } from '@soederpop/luca/schemas/base.js'
12
+
13
+ declare module "@soederpop/luca/client" {
14
+ interface AvailableClients {
15
+ civitai: typeof CivitaiClient;
16
+ }
17
+ }
18
+
19
+ export const CivitaiClientStateSchema = ClientStateSchema.extend({
20
+ checkpoints: z.array(z.string()).default([]).describe('List of downloaded checkpoint file IDs'),
21
+ })
22
+
23
+ export type CivitaiClientState = z.infer<typeof CivitaiClientStateSchema>
24
+
25
+ /**
26
+ * Civitai client — search, browse, and download AI models from civitai.com.
27
+ *
28
+ * Wraps the Civitai REST API to search for checkpoints, LoRA models, embeddings,
29
+ * and other model types. Supports downloading models directly to disk with
30
+ * metadata extraction.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const civitai = container.client('civitai')
35
+ * const results = await civitai.search({ query: 'anime', type: 'Checkpoint' })
36
+ * console.log(results.items.map(m => m.name))
37
+ * ```
38
+ */
39
+ export class CivitaiClient<T extends CivitaiClientState> extends RestClient<T> {
40
+ static override stateSchema = CivitaiClientStateSchema;
41
+ // @ts-ignore
42
+ static attach(container: Container & ClientsInterface, options?: any) {
43
+ container.clients.register("civitai", CivitaiClient);
44
+ return container
45
+ }
46
+
47
+ constructor(options: ClientOptions, context: ContainerContext) {
48
+ options = {
49
+ ...options,
50
+ baseURL: "https://civitai.com",
51
+ };
52
+
53
+ super(options, context);
54
+ }
55
+
56
+ types = [
57
+ "Checkpoint",
58
+ "TextualInversion",
59
+ "Hypernetwork",
60
+ "AestheticGradient",
61
+ "LORA",
62
+ "Controlnet",
63
+ "Poses",
64
+ ];
65
+
66
+ /**
67
+ * Search models by tag.
68
+ *
69
+ * @param tag - The tag to search for
70
+ * @param options - Additional search filters
71
+ * @returns Search results with items array
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const results = await civitai.searchByTag('anime')
76
+ * ```
77
+ */
78
+ async searchByTag(
79
+ tag: string,
80
+ { limit = 100, page = 1, query, type, username }: any = {}
81
+ ): Promise<{ items: ModelInfo[] }> {
82
+ return this.search({ tag, limit, page, type, username, query });
83
+ }
84
+
85
+ /**
86
+ * Search models by creator username.
87
+ *
88
+ * @param username - The creator's username
89
+ * @param options - Additional search filters
90
+ * @returns Search results with items array
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const results = await civitai.searchByUsername('stabilityai')
95
+ * ```
96
+ */
97
+ async searchByUsername(
98
+ username: string,
99
+ { limit = 100, page = 1, type, query }: any = {}
100
+ ): Promise<{ items: ModelInfo[] }> {
101
+ return this.search({ username, limit, page, type, query });
102
+ }
103
+
104
+ /**
105
+ * Search for models with full filter support.
106
+ *
107
+ * @param options - Search parameters (query, tag, username, type, sorting, pagination)
108
+ * @returns Search results with items array
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const results = await civitai.search({ query: 'portrait', type: 'LORA', newest: true })
113
+ * ```
114
+ */
115
+ async search({
116
+ limit = 100,
117
+ page = 1,
118
+ query = "",
119
+ newest = false,
120
+ best = true,
121
+ popular = false,
122
+ tag,
123
+ username,
124
+ type,
125
+ }: any = {}): Promise<{ items: ModelInfo[] }> {
126
+ const params = omitBy({ limit, page, query, tag, username }, (v,k) => typeof v === 'undefined');
127
+
128
+ let sort = 'Highest Rated'
129
+
130
+ if (newest) {
131
+ sort = 'Newest'
132
+ } else if (popular) {
133
+ sort = 'Most Downloaded'
134
+ }
135
+
136
+ return this.get("/api/v1/models", {
137
+ ...params,
138
+ sort,
139
+ types: type
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Get full model details by ID.
145
+ *
146
+ * @param modelId - The Civitai model ID
147
+ * @returns Complete model info including versions and files
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const model = await civitai.getModel('12345')
152
+ * console.log(model.name, model.type)
153
+ * ```
154
+ */
155
+ async getModel(modelId: string): Promise<ModelInfo> {
156
+ return this.get(`/api/v1/models/${modelId}`);
157
+ }
158
+
159
+ override get container() {
160
+ return this.context.container as NodeContainer;
161
+ }
162
+
163
+ /**
164
+ * Download a checkpoint model to a local folder.
165
+ *
166
+ * Fetches model info, saves metadata as YAML, and optionally downloads the safetensors file.
167
+ *
168
+ * @param modelId - The Civitai model ID
169
+ * @param destinationFolder - Local folder path to download into
170
+ * @param skipDownloadFile - If true, only save metadata without downloading the file
171
+ * @returns Model info with downloadPath
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const result = await civitai.downloadCheckpoint('12345', '/models/checkpoints')
176
+ * console.log(`Downloaded to: ${result.downloadPath}`)
177
+ * ```
178
+ */
179
+ async downloadCheckpoint(modelId: string, destinationFolder: string, skipDownloadFile = false) {
180
+ const { paths, utils } = this.container;
181
+ const { stringUtils } = utils;
182
+ const downloader = this.container.feature("downloader");
183
+ const yaml = this.container.feature("yaml");
184
+
185
+ const info = await this.getCheckpointInfo(modelId);
186
+ const { downloadUrl, fileName } = info;
187
+
188
+ const destinationPath = paths.resolve(destinationFolder, fileName.replace(/\s/g, "_"))
189
+
190
+ const metaName = info.fileId;
191
+
192
+ await this.container.fs.ensureFolder(paths.resolve("checkpoints"));
193
+ const checkpointMetaPath = paths.resolve("checkpoints", metaName);
194
+ await this.container.fs.writeFileAsync(
195
+ checkpointMetaPath,
196
+ yaml.stringify(info)
197
+ );
198
+
199
+ if (skipDownloadFile) {
200
+ return {
201
+ ...info,
202
+ downloadPath: destinationPath,
203
+ };
204
+ } else {
205
+ const downloadPath = await downloader.download(
206
+ downloadUrl,
207
+ destinationPath
208
+ );
209
+
210
+ return {
211
+ ...info,
212
+ downloadPath,
213
+ };
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Download a textual inversion / embedding model to a local folder.
219
+ *
220
+ * @param modelId - The Civitai model ID
221
+ * @param destinationFolder - Local folder path to download into
222
+ * @param skipDownloadFile - If true, only save metadata without downloading
223
+ * @param modelFileId - Specific version ID to download
224
+ * @returns Model info with downloadPath
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * const result = await civitai.downloadEmbedding('67890', '/models/embeddings')
229
+ * ```
230
+ */
231
+ async downloadEmbedding(modelId: string, destinationFolder: string,skipDownloadFile = false, modelFileId?: string) {
232
+ const { paths, utils } = this.container;
233
+ const downloader = this.container.feature("downloader");
234
+ const yaml = this.container.feature("yaml");
235
+
236
+ const info = await this.getEmbeddingModelInfo(modelId);
237
+ const { downloadUrl, fileName } = info;
238
+
239
+ const destinationPath = paths.resolve(
240
+ destinationFolder,
241
+ fileName
242
+ );
243
+
244
+ const embeddingPath = paths.resolve(
245
+ "embeddings",
246
+ `${utils.stringUtils.camelCase(
247
+ utils.stringUtils.kebabCase(info.name)
248
+ )}.yml`
249
+ );
250
+
251
+ await this.container.fs.writeFileAsync(embeddingPath, yaml.stringify(info));
252
+
253
+ if (!skipDownloadFile) {
254
+ const downloadPath = await downloader.download(
255
+ downloadUrl,
256
+ destinationPath
257
+ );
258
+ return {
259
+ ...info,
260
+ downloadPath,
261
+ };
262
+ }
263
+
264
+ return {
265
+ ...info,
266
+ downloadPath: destinationPath,
267
+ };
268
+ }
269
+
270
+
271
+ /**
272
+ * Download a LoRA model to a local folder.
273
+ *
274
+ * @param modelId - The Civitai model ID
275
+ * @param fileId - The specific file/version ID to download
276
+ * @param destinationFolder - Local folder path to download into
277
+ * @param skipDownloadFile - If true, only return info without downloading
278
+ * @returns Model info with downloadPath and LoRA tag
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const result = await civitai.downloadLoraModel('12345', '67890', '/models/loras')
283
+ * console.log(result.tag) // '<lora:model_name:1>'
284
+ * ```
285
+ */
286
+ async downloadLoraModel(modelId: string, fileId: string | number, destinationFolder: string, skipDownloadFile = false) {
287
+ const { paths } = this.container;
288
+ const downloader = this.container.feature("downloader");
289
+
290
+ const info = await this.getLoraModelInfo(modelId, fileId);
291
+ const { downloadUrl, fileName } = info;
292
+
293
+ const destinationPath = paths.resolve(
294
+ destinationFolder,
295
+ fileName
296
+ );
297
+
298
+ if (!skipDownloadFile) {
299
+ const downloadPath = await downloader.download(
300
+ downloadUrl,
301
+ destinationPath
302
+ );
303
+ return {
304
+ ...info,
305
+ downloadPath,
306
+ };
307
+ }
308
+
309
+ return {
310
+ ...info,
311
+ downloadPath: destinationPath,
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Get metadata for the latest checkpoint version of a model.
317
+ *
318
+ * @param modelId - The Civitai model ID
319
+ * @returns Checkpoint info with download URL, file name, and image URLs
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * const info = await civitai.getCheckpointInfo('12345')
324
+ * console.log(info.fileName, info.downloadUrl)
325
+ * ```
326
+ */
327
+ async getCheckpointInfo(modelId: string) {
328
+ const { utils } = this.container;
329
+ const model = await this.getModel(modelId);
330
+ const { modelVersions = [] } = model;
331
+ const latest = maxBy(modelVersions, "createdAt")!;
332
+ const primaryFile =
333
+ latest?.files?.find((f) => f.primary)! || latest?.files[0]!;
334
+ const fileId = `${utils.stringUtils.camelCase(
335
+ utils.stringUtils.kebabCase(model.name)
336
+ )}.yml`;
337
+
338
+ if (!primaryFile) {
339
+ console.log(latest);
340
+ throw new Error(`Can not find primary file for ${modelId}`);
341
+ }
342
+
343
+ return {
344
+ modelId: model.id,
345
+ name: model.name,
346
+ fileId,
347
+ fileName: primaryFile.name,
348
+ downloadUrl: primaryFile.downloadUrl,
349
+ imageUrls: latest.images.map((i) => i.url),
350
+ imageInfo: latest.images.map((i) => ({
351
+ url: i.url,
352
+ prompt: i.meta?.prompt || "",
353
+ negativePrompt: i.meta?.negativePrompt || "",
354
+ })),
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Get metadata for a LoRA model including trained words and LoRA tag.
360
+ *
361
+ * @param modelId - The Civitai model ID
362
+ * @param downloadFileId - Specific version ID (defaults to latest)
363
+ * @returns LoRA info with download URL, tag, trained words, and images
364
+ *
365
+ * @example
366
+ * ```typescript
367
+ * const info = await civitai.getLoraModelInfo('12345')
368
+ * console.log(info.tag, info.words)
369
+ * ```
370
+ */
371
+ async getLoraModelInfo(modelId: string, downloadFileId?: string | number) {
372
+ const { utils } = this.container;
373
+
374
+ const model = await this.getModel(modelId);
375
+ const { modelVersions = [] } = model;
376
+ const latest = maxBy(modelVersions, "createdAt")!;
377
+ const primaryFile = downloadFileId ? modelVersions.find(m => String(m.id) === String(downloadFileId))! : latest?.files?.find((f) => f.primary)! || modelVersions[0]!;
378
+ let fileId = `${utils.stringUtils.camelCase(
379
+ utils.stringUtils.kebabCase(model.name)
380
+ )}.yml`.replace(/\W/g, "_");
381
+
382
+ if (downloadFileId) {
383
+ fileId = [downloadFileId, fileId].join("_")
384
+ }
385
+
386
+ // @ts-ignore-next-line
387
+ if (!primaryFile?.downloadUrl) {
388
+ throw new Error(`No primary file found for ${modelId} ${model.name}`);
389
+ }
390
+
391
+ return {
392
+ modelId: model.id,
393
+ name: model.name,
394
+ fileId,
395
+ fileName: primaryFile.name,
396
+ modelVersions,
397
+ // @ts-ignore-next-line
398
+ downloadUrl: primaryFile.downloadUrl,
399
+ tag: `<lora:${primaryFile.name.replace(".safetensors", "")}:1>`,
400
+ words: latest.trainedWords,
401
+ imageUrls: latest.images.map((i) => i.url),
402
+ imageInfo: latest.images.map((i) => ({
403
+ url: i.url,
404
+ prompt: i.meta?.prompt || "",
405
+ negativePrompt: i.meta?.negativePrompt || "",
406
+ })),
407
+ };
408
+ }
409
+
410
+ /**
411
+ * Get metadata for an embedding model including trained words.
412
+ *
413
+ * @param modelId - The Civitai model ID
414
+ * @param modelFileId - Specific version ID (defaults to latest)
415
+ * @returns Embedding info with download URL, trained words, and images
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * const info = await civitai.getEmbeddingModelInfo('12345')
420
+ * console.log(info.words)
421
+ * ```
422
+ */
423
+ async getEmbeddingModelInfo(modelId: string, modelFileId?: string) {
424
+ const { utils } = this.container;
425
+
426
+ const model = await this.getModel(modelId);
427
+ const { modelVersions = [] } = model;
428
+ const latest = modelVersions.find(i => i.id.toString() === String(modelFileId)) || maxBy(modelVersions, "createdAt")!;
429
+ const primaryFile = latest?.files?.find((f) => f.primary)!;
430
+ const fileId = `${utils.stringUtils.camelCase(
431
+ utils.stringUtils.kebabCase(model.name)
432
+ )}.yml`;
433
+
434
+ if (!primaryFile) {
435
+ throw new Error(`No primary file found for ${modelId} ${model.name}`);
436
+ }
437
+
438
+ return {
439
+ modelId: model.id,
440
+ name: model.name,
441
+ fileId,
442
+ fileName: primaryFile.name,
443
+ downloadUrl: primaryFile.downloadUrl,
444
+ words: latest.trainedWords,
445
+ imageUrls: latest.images.map((i) => i.url),
446
+ imageInfo: latest.images.map((i) => ({
447
+ url: i.url,
448
+ prompt: i.meta?.prompt || "",
449
+ negativePrompt: i.meta?.negativePrompt || "",
450
+ })),
451
+ };
452
+ }
453
+
454
+ }
455
+
456
+ export type ModelInfo = {
457
+ id: number;
458
+ name: string;
459
+ description: string;
460
+ type: string;
461
+ poi: boolean;
462
+ nsfw: boolean;
463
+ allowNoCredit: boolean;
464
+ allowCommercialUse: string;
465
+ allowDerivatives: boolean;
466
+ allowDifferentLicense: boolean;
467
+ stats: {
468
+ downloadCount: number;
469
+ favoriteCount: number;
470
+ commentCount: number;
471
+ ratingCount: number;
472
+ rating: number;
473
+ };
474
+ creator: {
475
+ username: string;
476
+ image: string;
477
+ };
478
+ tags: {
479
+ name: string;
480
+ }[];
481
+ modelVersions: {
482
+ id: number;
483
+ modelId: number;
484
+ name: string;
485
+ createdAt: string;
486
+ updatedAt: string;
487
+ trainedWords: string[];
488
+ baseModel: string;
489
+ earlyAccessTimeFrame: number;
490
+ description: string;
491
+ stats: {
492
+ downloadCount: number;
493
+ ratingCount: number;
494
+ rating: number;
495
+ };
496
+ files: {
497
+ name: string;
498
+ id: number;
499
+ sizeKB: number;
500
+ type: string;
501
+ metadata: {
502
+ fp: string;
503
+ size: string | number;
504
+ format: string;
505
+ };
506
+ pickleScanResult: string;
507
+ pickleScanMessage: string;
508
+ virusScanResult: string;
509
+ scannedAt: string;
510
+ hashes: {
511
+ AutoV1: string;
512
+ AutoV2: string;
513
+ SHA256: string;
514
+ CRC32: string;
515
+ BLAKE3: string;
516
+ };
517
+ downloadUrl: string;
518
+ primary: boolean;
519
+ }[];
520
+ images: {
521
+ url: string;
522
+ nsfw: boolean | string;
523
+ width: number;
524
+ height: number;
525
+ hash: string;
526
+ meta: {
527
+ Size: string;
528
+ seed: number;
529
+ steps: number;
530
+ prompt: string;
531
+ sampler: string;
532
+ cfgScale: number;
533
+ resources: any[];
534
+ Model_hash: string;
535
+ Variation_seed: string;
536
+ negativePrompt: string;
537
+ Variation_seed_strength: string;
538
+ };
539
+ }[];
540
+ }[];
541
+ };
@@ -0,0 +1,41 @@
1
+ import {
2
+ type ClientOptions,
3
+ type ClientsInterface,
4
+ clients,
5
+ RestClient,
6
+ } from "@soederpop/luca/client";
7
+ import { type ContainerContext } from "@soederpop/luca/container";
8
+ import { z } from 'zod'
9
+ import { ClientStateSchema } from '@soederpop/luca/schemas/base.js'
10
+
11
+ declare module "@soederpop/luca/client" {
12
+ interface AvailableClients {
13
+ myClient: typeof MyClient;
14
+ }
15
+ }
16
+
17
+ export const MyClientStateSchema = ClientStateSchema.extend({
18
+ checkpoints: z.array(z.string()).default([]),
19
+ })
20
+
21
+ export type MyClientState = z.infer<typeof MyClientStateSchema>
22
+
23
+ export class MyClient<T extends MyClientState> extends RestClient<T> {
24
+ static override stateSchema = MyClientStateSchema;
25
+ // @ts-ignore
26
+ static attach(container: Container & ClientsInterface, options?: any) {
27
+ container.clients.register("civitai", MyClient);
28
+ return container
29
+ }
30
+
31
+ constructor(options: ClientOptions, context: ContainerContext) {
32
+ options = {
33
+ ...options,
34
+ baseURL: "https://platform.cloud.coveo.com/rest/search/v2",
35
+ };
36
+
37
+ super(options, context);
38
+ }
39
+ }
40
+
41
+ export default clients.register('civitai', MyClient)