@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,162 @@
1
+ ---
2
+ title: Using Clients
3
+ tags: [clients, rest, graphql, websocket, http, api, axios]
4
+ ---
5
+
6
+ # Using Clients
7
+
8
+ Clients connect your application to external services. Luca provides built-in clients for REST APIs, GraphQL, and WebSocket connections.
9
+
10
+ ## REST Client
11
+
12
+ The REST client wraps axios with Luca's helper patterns (state, events, introspection):
13
+
14
+ ```typescript
15
+ const api = container.client('rest', {
16
+ baseURL: 'https://api.example.com',
17
+ headers: {
18
+ Authorization: 'Bearer my-token',
19
+ },
20
+ })
21
+
22
+ await api.connect()
23
+
24
+ // Standard HTTP methods
25
+ const users = await api.get('/users')
26
+ const user = await api.get('/users/123')
27
+ const created = await api.post('/users', { name: 'Alice', email: 'alice@example.com' })
28
+ const updated = await api.put('/users/123', { name: 'Alice Updated' })
29
+ await api.delete('/users/123')
30
+ ```
31
+
32
+ ### REST Client Events
33
+
34
+ ```typescript
35
+ api.on('failure', (error) => {
36
+ console.error('Request failed:', error.message)
37
+ })
38
+
39
+ // State changes track connection status
40
+ api.state.observe((type, key, value) => {
41
+ if (key === 'connected') {
42
+ console.log(`Client connected: ${value}`)
43
+ }
44
+ })
45
+ ```
46
+
47
+ ## GraphQL Client
48
+
49
+ For GraphQL APIs, use the REST client's `post()` method to send queries and mutations:
50
+
51
+ ```typescript
52
+ const graph = container.client('rest', {
53
+ baseURL: 'https://api.example.com/graphql',
54
+ headers: { Authorization: 'Bearer my-token' },
55
+ })
56
+
57
+ await graph.connect()
58
+
59
+ // Send a query
60
+ const result = await graph.post('/', {
61
+ query: `
62
+ query GetUser($id: ID!) {
63
+ user(id: $id) {
64
+ name
65
+ email
66
+ posts { title }
67
+ }
68
+ }
69
+ `,
70
+ variables: { id: '123' },
71
+ })
72
+
73
+ // Send a mutation
74
+ const mutationResult = await graph.post('/', {
75
+ query: `
76
+ mutation CreatePost($input: PostInput!) {
77
+ createPost(input: $input) {
78
+ id
79
+ title
80
+ }
81
+ }
82
+ `,
83
+ variables: { input: { title: 'Hello World', body: '...' } },
84
+ })
85
+ ```
86
+
87
+ ## WebSocket Client
88
+
89
+ The WebSocket client wraps a raw `WebSocket` connection:
90
+
91
+ ```typescript
92
+ const ws = container.client('websocket', {
93
+ baseURL: 'wss://realtime.example.com',
94
+ })
95
+
96
+ await ws.connect()
97
+
98
+ // Access the underlying WebSocket via ws.ws
99
+ ws.ws.onmessage = (event) => {
100
+ console.log('Received:', event.data)
101
+ }
102
+
103
+ ws.ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }))
104
+
105
+ // Clean up
106
+ ws.ws.close()
107
+ ```
108
+
109
+ ## Discovering Clients
110
+
111
+ ```typescript
112
+ container.clients.available // ['rest', 'graph', 'websocket']
113
+ container.clients.describe('rest')
114
+ ```
115
+
116
+ ## Using Clients in Endpoints
117
+
118
+ ```typescript
119
+ // endpoints/proxy.ts
120
+ import { z } from 'zod'
121
+ import type { EndpointContext } from '@soederpop/luca'
122
+
123
+ export const path = '/api/external-data'
124
+
125
+ export const getSchema = z.object({
126
+ query: z.string().describe('Search query'),
127
+ })
128
+
129
+ export async function get(params: z.infer<typeof getSchema>, ctx: EndpointContext) {
130
+ const api = ctx.container.client('rest', {
131
+ baseURL: 'https://external-api.com',
132
+ })
133
+
134
+ await api.connect()
135
+ const data = await api.get(`/search?q=${encodeURIComponent(params.query)}`)
136
+
137
+ return { results: data }
138
+ }
139
+ ```
140
+
141
+ ## Using Clients in Features
142
+
143
+ ```typescript
144
+ class WeatherService extends Feature<WeatherState, WeatherOptions> {
145
+ private api: any
146
+
147
+ async initialize() {
148
+ this.api = this.container.client('rest', {
149
+ baseURL: 'https://api.weather.com',
150
+ headers: { 'X-API-Key': this.options.apiKey },
151
+ })
152
+ await this.api.connect()
153
+ }
154
+
155
+ async getForecast(city: string) {
156
+ const data = await this.api.get(`/forecast/${encodeURIComponent(city)}`)
157
+ this.state.set('lastForecast', data)
158
+ this.emit('forecastFetched', data)
159
+ return data
160
+ }
161
+ }
162
+ ```
@@ -0,0 +1,198 @@
1
+ ---
2
+ title: Creating Custom Features
3
+ tags: [features, custom, extend, zod, state, events, module-augmentation, helper]
4
+ ---
5
+
6
+ # Creating Custom Features
7
+
8
+ You can create your own features to encapsulate domain logic, then register them so they're available through `container.feature('yourFeature')` with full type safety.
9
+
10
+ ## Anatomy of a Feature
11
+
12
+ A feature has:
13
+ - **State** -- observable, defined by a Zod schema
14
+ - **Options** -- configuration passed at creation, defined by a Zod schema
15
+ - **Events** -- typed event bus
16
+ - **Methods** -- your domain logic
17
+ - **Access to the container** -- via `this.container`
18
+
19
+ ## Basic Example
20
+
21
+ ```typescript
22
+ import { z } from 'zod'
23
+ import { Feature, features, FeatureStateSchema, FeatureOptionsSchema } from '@soederpop/luca'
24
+
25
+ // Define state schema by extending the base FeatureStateSchema
26
+ export const CounterStateSchema = FeatureStateSchema.extend({
27
+ count: z.number().describe('Current count value'),
28
+ lastUpdated: z.string().optional().describe('ISO timestamp of last update'),
29
+ })
30
+ export type CounterState = z.infer<typeof CounterStateSchema>
31
+
32
+ // Define options schema by extending the base FeatureOptionsSchema
33
+ export const CounterOptionsSchema = FeatureOptionsSchema.extend({
34
+ initialCount: z.number().default(0).describe('Starting count value'),
35
+ step: z.number().default(1).describe('Increment step size'),
36
+ })
37
+ export type CounterOptions = z.infer<typeof CounterOptionsSchema>
38
+
39
+ /**
40
+ * A simple counter feature that demonstrates the feature pattern.
41
+ * Tracks a count value with observable state and events.
42
+ */
43
+ export class Counter extends Feature<CounterState, CounterOptions> {
44
+ static override stateSchema = CounterStateSchema
45
+ static override optionsSchema = CounterOptionsSchema
46
+
47
+ /** Called when the feature is created */
48
+ async initialize() {
49
+ this.state.set('count', this.options.initialCount ?? 0)
50
+ }
51
+
52
+ /** Increment the counter by the configured step */
53
+ increment() {
54
+ const current = this.state.get('count') || 0
55
+ const next = current + (this.options.step ?? 1)
56
+ this.state.set('count', next)
57
+ this.state.set('lastUpdated', new Date().toISOString())
58
+ this.emit('incremented', next)
59
+ return next
60
+ }
61
+
62
+ /** Decrement the counter by the configured step */
63
+ decrement() {
64
+ const current = this.state.get('count') || 0
65
+ const next = current - (this.options.step ?? 1)
66
+ this.state.set('count', next)
67
+ this.state.set('lastUpdated', new Date().toISOString())
68
+ this.emit('decremented', next)
69
+ return next
70
+ }
71
+
72
+ /** Reset the counter to its initial value */
73
+ reset() {
74
+ this.state.set('count', this.options.initialCount ?? 0)
75
+ this.emit('reset')
76
+ }
77
+
78
+ /** Get the current count */
79
+ get value(): number {
80
+ return this.state.get('count') || 0
81
+ }
82
+ }
83
+
84
+ // Register the feature
85
+ features.register('counter', Counter)
86
+
87
+ // Module augmentation for type safety
88
+ declare module '@soederpop/luca' {
89
+ interface AvailableFeatures {
90
+ counter: typeof Counter
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## Using Your Feature
96
+
97
+ ```typescript
98
+ import './features/counter' // Side-effect import to register
99
+
100
+ const counter = container.feature('counter', { initialCount: 10, step: 5 })
101
+
102
+ counter.on('incremented', (value) => {
103
+ console.log(`Count is now ${value}`)
104
+ })
105
+
106
+ counter.increment() // 15
107
+ counter.increment() // 20
108
+ counter.value // 20
109
+ counter.reset() // Back to 10
110
+
111
+ // Observe state changes
112
+ counter.state.observe((type, key, value) => {
113
+ console.log(`${key} ${type}d:`, value)
114
+ })
115
+ ```
116
+
117
+ ## Enabling on the Container
118
+
119
+ If your feature should be a container-level singleton with a shortcut:
120
+
121
+ ```typescript
122
+ export class Counter extends Feature<CounterState, CounterOptions> {
123
+ // This creates the container.counter shortcut when enabled
124
+ static override shortcut = 'features.counter' as const
125
+ // ...
126
+ }
127
+
128
+ // Enable it
129
+ container.feature('counter', { enable: true })
130
+
131
+ // Now accessible as:
132
+ container.counter.increment()
133
+ ```
134
+
135
+ ## Feature with Container Access
136
+
137
+ Features can access other features and the full container:
138
+
139
+ ```typescript
140
+ export class Analytics extends Feature<AnalyticsState, AnalyticsOptions> {
141
+ /** Log an event, writing to disk cache for persistence */
142
+ async logEvent(name: string, data: Record<string, any>) {
143
+ const cache = this.container.feature('diskCache', { path: './.analytics' })
144
+ const timestamp = new Date().toISOString()
145
+
146
+ await cache.set(`event:${timestamp}`, { name, data, timestamp })
147
+
148
+ this.state.set('totalEvents', (this.state.get('totalEvents') || 0) + 1)
149
+ this.emit('eventLogged', { name, data })
150
+ }
151
+
152
+ /** Get recent events from the cache */
153
+ async recentEvents(limit = 10) {
154
+ const fs = this.container.fs
155
+ // ... read from cache directory
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Documenting Your Feature
161
+
162
+ Document your classes, methods, and getters with JSDoc. This is important because Luca's introspection system extracts these docs and makes them available at runtime:
163
+
164
+ ```typescript
165
+ /**
166
+ * Manages user sessions with automatic expiration and renewal.
167
+ * Sessions are persisted to disk and can survive process restarts.
168
+ */
169
+ export class SessionManager extends Feature<SessionState, SessionOptions> {
170
+ /**
171
+ * Create a new session for the given user.
172
+ * Returns a session token that can be used for authentication.
173
+ */
174
+ async createSession(userId: string): Promise<string> {
175
+ // ...
176
+ }
177
+
178
+ /** The number of currently active sessions */
179
+ get activeCount(): number {
180
+ return this.state.get('sessions')?.length || 0
181
+ }
182
+ }
183
+ ```
184
+
185
+ Then anyone (human or AI) can discover your feature:
186
+
187
+ ```typescript
188
+ container.features.describe('sessionManager')
189
+ // Returns the full markdown documentation extracted from your JSDoc
190
+ ```
191
+
192
+ ## Best Practices
193
+
194
+ 1. **Use Zod `.describe()` on schema fields** -- these descriptions appear in introspection and help documentation
195
+ 2. **Emit events for significant actions** -- enables reactive patterns and decoupled observers
196
+ 3. **Use state for observable values** -- don't hide important state in private variables if consumers need to watch it
197
+ 4. **Access the container, not imports** -- prefer `this.container.feature('fs')` over importing fs directly, so the feature works in any container
198
+ 5. **Document everything** -- JSDoc on the class, methods, and getters feeds the introspection system
@@ -0,0 +1,191 @@
1
+ ---
2
+ title: Contentbase - Markdown as a Database
3
+ tags: [contentbase, contentdb, markdown, database, models, query, collections]
4
+ ---
5
+
6
+ # Contentbase - Markdown as a Database
7
+
8
+ Contentbase lets you treat folders of markdown files as queryable database collections. Define models with Zod schemas, extract structured data from frontmatter and content, and query it with a fluent API.
9
+
10
+ ## Setup
11
+
12
+ ```typescript
13
+ import container from '@soederpop/luca'
14
+
15
+ const db = container.feature('contentDb', { rootPath: './content' })
16
+ const { defineModel, section, hasMany, belongsTo } = db.library
17
+ ```
18
+
19
+ ## Directory Structure
20
+
21
+ ```
22
+ content/
23
+ ├── posts/
24
+ │ ├── hello-world.md
25
+ │ ├── getting-started.md
26
+ │ └── advanced-tips.md
27
+ ├── authors/
28
+ │ ├── alice.md
29
+ │ └── bob.md
30
+ └── tags/
31
+ ├── javascript.md
32
+ └── typescript.md
33
+ ```
34
+
35
+ ## Defining Models
36
+
37
+ Models map to subdirectories and define the shape of your content:
38
+
39
+ ```typescript
40
+ import { z } from 'zod'
41
+
42
+ const Post = defineModel('Post', {
43
+ // Maps to content/posts/
44
+ prefix: 'posts',
45
+
46
+ // Frontmatter schema
47
+ meta: z.object({
48
+ title: z.string(),
49
+ date: z.string(),
50
+ status: z.enum(['draft', 'published', 'archived']),
51
+ author: z.string().optional(),
52
+ tags: z.array(z.string()).default([]),
53
+ }),
54
+
55
+ // Extract structured data from the markdown body
56
+ sections: {
57
+ summary: section('Summary', {
58
+ extract: (query) => query.select('paragraph')?.toString() || '',
59
+ schema: z.string(),
60
+ }),
61
+ codeExamples: section('Code Examples', {
62
+ extract: (query) => query.selectAll('code').map((n: any) => n.toString()),
63
+ schema: z.array(z.string()),
64
+ }),
65
+ },
66
+
67
+ // Relationships
68
+ relationships: {
69
+ author: belongsTo(() => Author, { key: 'meta.author' }),
70
+ tags: hasMany(() => Tag, { heading: 'Tags' }),
71
+ },
72
+ })
73
+
74
+ const Author = defineModel('Author', {
75
+ prefix: 'authors',
76
+ meta: z.object({
77
+ name: z.string(),
78
+ email: z.string().email(),
79
+ role: z.enum(['writer', 'editor', 'admin']),
80
+ }),
81
+ relationships: {
82
+ posts: hasMany(() => Post, { foreignKey: 'meta.author' }),
83
+ },
84
+ })
85
+ ```
86
+
87
+ ## Registering and Loading
88
+
89
+ ```typescript
90
+ db.register(Post)
91
+ db.register(Author)
92
+ await db.load() // Parses all markdown files and builds the queryable index
93
+ ```
94
+
95
+ ## Querying
96
+
97
+ Contentbase provides a fluent query API:
98
+
99
+ ```typescript
100
+ // Fetch all posts
101
+ const allPosts = await db.query(Post).fetchAll()
102
+
103
+ // Filter by frontmatter fields
104
+ const published = await db.query(Post)
105
+ .where('meta.status', 'published')
106
+ .fetchAll()
107
+
108
+ // Multiple filters
109
+ const recentPosts = await db.query(Post)
110
+ .where('meta.status', 'published')
111
+ .where('meta.tags', 'includes', 'javascript')
112
+ .fetchAll()
113
+
114
+ // Get a single document by slug (filename without .md)
115
+ const post = await db.query(Post).find('hello-world')
116
+ ```
117
+
118
+ ## Markdown File Format
119
+
120
+ Each markdown file has YAML frontmatter and a body:
121
+
122
+ ```markdown
123
+ ---
124
+ title: Hello World
125
+ date: 2024-01-15
126
+ status: published
127
+ author: alice
128
+ tags: [javascript, tutorial]
129
+ ---
130
+
131
+ # Hello World
132
+
133
+ This is the post content.
134
+
135
+ ## Summary
136
+
137
+ A brief introduction to our blog.
138
+
139
+ ## Code Examples
140
+
141
+ \`\`\`javascript
142
+ console.log('Hello!')
143
+ \`\`\`
144
+ ```
145
+
146
+ ## Use Cases
147
+
148
+ - **Documentation sites** -- query and render docs with frontmatter metadata
149
+ - **Blog engines** -- posts with authors, tags, categories
150
+ - **Knowledge bases** -- structured content with relationships
151
+ - **Project management** -- epics, stories, tasks as markdown with status tracking
152
+ - **Configuration** -- human-readable config files that are also queryable
153
+
154
+ ## Full Example: Blog Engine
155
+
156
+ ```typescript
157
+ import container from '@soederpop/luca'
158
+ import { z } from 'zod'
159
+
160
+ const db = container.feature('contentDb', { rootPath: './blog' })
161
+ const { defineModel, section, hasMany } = db.library
162
+
163
+ const Post = defineModel('Post', {
164
+ prefix: 'posts',
165
+ meta: z.object({
166
+ title: z.string(),
167
+ date: z.string(),
168
+ status: z.enum(['draft', 'published']),
169
+ tags: z.array(z.string()).default([]),
170
+ }),
171
+ sections: {
172
+ excerpt: section('Excerpt', {
173
+ extract: (q) => q.select('paragraph')?.toString() || '',
174
+ schema: z.string(),
175
+ }),
176
+ },
177
+ })
178
+
179
+ db.register(Post)
180
+ await db.load()
181
+
182
+ // Get published posts for the homepage
183
+ const posts = await db.query(Post)
184
+ .where('meta.status', 'published')
185
+ .fetchAll()
186
+
187
+ for (const post of posts) {
188
+ console.log(`${post.meta.title} (${post.meta.date})`)
189
+ console.log(` ${post.sections.excerpt}`)
190
+ }
191
+ ```