@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
package/src/helper.ts ADDED
@@ -0,0 +1,543 @@
1
+ import { Bus, type EventMap } from "./bus.js";
2
+ import { type SetStateValue, State } from "./state.js";
3
+ import type { ContainerContext } from './container.js'
4
+ import uuid from 'node-uuid'
5
+ import { get } from 'lodash-es'
6
+ import { introspect, type HelperIntrospection, type IntrospectionSection, type ExampleIntrospection } from "./introspection/index.js";
7
+ import { z } from 'zod'
8
+ import { HelperStateSchema, HelperOptionsSchema, HelperEventsSchema } from './schemas/base.js'
9
+
10
+ export type HelperState = z.infer<typeof HelperStateSchema>
11
+ export type HelperOptions = z.infer<typeof HelperOptionsSchema>
12
+
13
+ /**
14
+ * Helpers are used to represent types of modules.
15
+ *
16
+ * You don't create instances of helpers directly, the container creates instances through
17
+ * factory functions that use the subclasses of Helper as a template. The container
18
+ * provides dependency injection and injects a context object into the Helper constructor.
19
+ *
20
+ * A Helper is something that can be introspected at runtime to learn about the interface.
21
+ *
22
+ * A helper has state.
23
+ *
24
+ * A helper is an event bus.
25
+ *
26
+ * A helper is connected to the container and can access the container's state, events, shared context, or
27
+ * other helpers and features in the container's registry.
28
+ */
29
+ export abstract class Helper<T extends HelperState = HelperState, K extends HelperOptions = any, E extends EventMap = EventMap> {
30
+ static shortcut: string = "unspecified"
31
+
32
+ static description: string = "No description provided"
33
+ static envVars: string[] = []
34
+
35
+ static stateSchema: z.ZodType = HelperStateSchema
36
+ static optionsSchema: z.ZodType = HelperOptionsSchema
37
+ static eventsSchema: z.ZodType = HelperEventsSchema
38
+
39
+ protected readonly _context: ContainerContext
40
+ protected readonly _events = new Bus<E>()
41
+ protected readonly _options: K
42
+
43
+ readonly state: State<T>
44
+
45
+ readonly uuid = uuid.v4()
46
+
47
+ get initialState() : T {
48
+ return {} as T
49
+ }
50
+
51
+ /** Alias for introspect */
52
+ static inspect(section?: IntrospectionSection) : HelperIntrospection | undefined {
53
+ return this.introspect(section)
54
+ }
55
+
56
+ static introspect(section?: IntrospectionSection) : HelperIntrospection | undefined {
57
+ const data = introspect((this as any).shortcut || '')
58
+ if (!data || !section) return data
59
+ return filterIntrospection(data, section)
60
+ }
61
+
62
+ /** Alias for introspectAsText */
63
+ static inspectAsText(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) : string {
64
+ return this.introspectAsText(sectionOrDepth, startHeadingDepth)
65
+ }
66
+
67
+ static introspectAsText(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) : string {
68
+ const { section, depth } = resolveIntrospectAsTextArgs(sectionOrDepth, startHeadingDepth)
69
+ const introspection = this.introspect()
70
+ if (!introspection) return ''
71
+ return presentIntrospectionJSONAsMarkdown(introspection, depth, section)
72
+ }
73
+
74
+
75
+ /**
76
+ * All Helpers can be introspect()ed and, assuming the introspection data has been loaded into the registry,
77
+ * will report information about the Helper that can only get extracted by reading the code, e.g. the type interfaces
78
+ * for the helper's options, state, and the events it emits, as well as the documentation from the helpers code for
79
+ * each of the methods and properties.
80
+ *
81
+ * Pass a section name to get only that section: `'methods'`, `'getters'`, `'events'`, `'state'`, `'options'`, `'envVars'`
82
+ */
83
+ introspect(section?: IntrospectionSection) : HelperIntrospection | undefined {
84
+ const base = (this.constructor as any).introspect()
85
+ if (!base || !section) return base
86
+ return filterIntrospection(base, section)
87
+ }
88
+
89
+ /** Alias for introspect */
90
+ inspect(section?: IntrospectionSection) : HelperIntrospection | undefined {
91
+ return this.introspect()
92
+ }
93
+
94
+ /**
95
+ * Returns the introspection data formatted as a markdown string.
96
+ *
97
+ * The first argument can be a section name (`'methods'`, `'getters'`, etc.) to render only
98
+ * that section, or a number for the starting heading depth (backward compatible).
99
+ */
100
+ introspectAsText(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) : string {
101
+ const { section, depth } = resolveIntrospectAsTextArgs(sectionOrDepth, startHeadingDepth)
102
+ const introspection = this.introspect()
103
+ if (!introspection) return ''
104
+ return presentIntrospectionJSONAsMarkdown(introspection, depth, section)
105
+ }
106
+
107
+ /** Alias for introspectAsText */
108
+ inspectAsText(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) : string {
109
+ return this.introspectAsText(sectionOrDepth, startHeadingDepth)
110
+ }
111
+
112
+ constructor(options: K, context: ContainerContext) {
113
+ const optionSchema = (this.constructor as any).optionsSchema
114
+ if (optionSchema && typeof optionSchema.safeParse === 'function') {
115
+ const parsed = optionSchema.safeParse(options || {})
116
+ if (parsed.success) {
117
+ this._options = parsed.data as K
118
+ } else {
119
+ const details = parsed.error.issues.map((issue: any) => `${issue.path?.join('.') || 'options'}: ${issue.message}`).join('; ')
120
+ throw new Error(`Invalid options for ${(this.constructor as any).shortcut || this.constructor.name}: ${details || parsed.error.message}`)
121
+ }
122
+ } else {
123
+ this._options = options
124
+ }
125
+ this._context = context;
126
+ this.state = new State<T>({ initialState: this.initialState });
127
+
128
+ this.hide('_context', '_state', '_options', '_events', 'uuid')
129
+
130
+ this.state.observe(() => {
131
+ (this as any).emit('stateChange', this.state.current)
132
+ })
133
+
134
+ this.afterInitialize()
135
+
136
+ this.container.emit('helperInitialized', this)
137
+ }
138
+
139
+ /**
140
+ * Every helper has a cache key which is computed at the time it is created through the container.
141
+ *
142
+ * This ensures only a single instance of the helper exists for the requested options.
143
+ */
144
+ get cacheKey() {
145
+ return this._options._cacheKey
146
+ }
147
+
148
+ /**
149
+ * This method will get called in the constructor and can be used instead of overriding the constructor
150
+ * in your helper subclases.
151
+ */
152
+ afterInitialize() {
153
+ // override this method to do something after the helper is initialized
154
+ }
155
+
156
+ setState(newState: SetStateValue<T>) {
157
+ this.state.setState(newState)
158
+ return this
159
+ }
160
+
161
+ /**
162
+ * Convenience method for putting properties on the helper that aren't enumerable,
163
+ * which is a convenience for the REPL mainly.
164
+ */
165
+ hide(...propNames: string[]) {
166
+ propNames.map((propName) => {
167
+ Object.defineProperty(this, propName, { enumerable: false })
168
+ })
169
+
170
+ return this
171
+ }
172
+
173
+ /**
174
+ * python / lodash style get method, which will get a value from the container using dot notation
175
+ * and will return a default value if the value is not found.
176
+ */
177
+ tryGet<K extends (string | string[]), T extends object = any>(key: K, defaultValue?: T) {
178
+ return get(this, key, defaultValue)
179
+ }
180
+
181
+ /**
182
+ * The options passed to the helper when it was created.
183
+ */
184
+ get options() {
185
+ return this._options;
186
+ }
187
+
188
+ /**
189
+ * The context object that was passed to the helper when it was created, this is decided by the container
190
+ * and not something you would manipulate.
191
+ */
192
+ get context() {
193
+ return this._context;
194
+ }
195
+
196
+ /**
197
+ * The container that the helper is connected to.
198
+ */
199
+ get container() {
200
+ return this.context.container;
201
+ }
202
+
203
+ emit<Ev extends string & keyof E>(event: Ev, ...args: E[Ev]) {
204
+ this._events.emit(event, ...args)
205
+ return this
206
+ }
207
+
208
+ on<Ev extends string & keyof E>(event: Ev, listener: (...args: E[Ev]) => void) {
209
+ this._events.on(event, listener)
210
+ return this
211
+ }
212
+
213
+ off<Ev extends string & keyof E>(event: Ev, listener?: (...args: E[Ev]) => void) {
214
+ this._events.off(event, listener)
215
+ return this
216
+ }
217
+
218
+ once<Ev extends string & keyof E>(event: Ev, listener: (...args: E[Ev]) => void) {
219
+ this._events.once(event, listener)
220
+ return this
221
+ }
222
+
223
+ async waitFor<Ev extends string & keyof E>(event: Ev) {
224
+ const resp = await this._events.waitFor(event)
225
+ return resp
226
+ }
227
+ }
228
+
229
+ const INTROSPECTION_SECTIONS: IntrospectionSection[] = ['methods', 'getters', 'events', 'state', 'options', 'envVars', 'examples', 'usage']
230
+
231
+ function filterIntrospection(data: HelperIntrospection, section: IntrospectionSection): HelperIntrospection {
232
+ const filtered: HelperIntrospection = {
233
+ id: data.id,
234
+ description: data.description,
235
+ shortcut: data.shortcut,
236
+ methods: {},
237
+ getters: {},
238
+ events: {},
239
+ state: {},
240
+ options: {},
241
+ envVars: [],
242
+ }
243
+
244
+ if (section === 'examples') {
245
+ // For examples section, include class-level examples and full methods/getters (they carry inline examples)
246
+ filtered.examples = data.examples
247
+ filtered.methods = data.methods
248
+ filtered.getters = data.getters
249
+ } else if (section === 'usage') {
250
+ // Usage is derived from options + shortcut, so pass those through
251
+ filtered.options = data.options
252
+ } else {
253
+ filtered[section] = data[section] as any
254
+ }
255
+
256
+ return filtered
257
+ }
258
+
259
+ function resolveIntrospectAsTextArgs(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) {
260
+ let section: IntrospectionSection | undefined
261
+ let depth = 1
262
+
263
+ if (typeof sectionOrDepth === 'string') {
264
+ section = sectionOrDepth
265
+ depth = startHeadingDepth ?? 1
266
+ } else if (typeof sectionOrDepth === 'number') {
267
+ depth = sectionOrDepth
268
+ }
269
+
270
+ return { section, depth }
271
+ }
272
+
273
+ function renderMethodsSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
274
+ const sections: string[] = []
275
+ if (!introspection.methods || Object.keys(introspection.methods).length === 0) return sections
276
+
277
+ sections.push(`${heading(2)} Methods`)
278
+
279
+ for (const [methodName, methodInfo] of Object.entries(introspection.methods)) {
280
+ sections.push(`${heading(3)} ${methodName}`)
281
+
282
+ if (methodInfo.description) {
283
+ sections.push(methodInfo.description)
284
+ }
285
+
286
+ if (methodInfo.parameters && Object.keys(methodInfo.parameters).length > 0) {
287
+ const tableRows = [
288
+ `**Parameters:**`,
289
+ '',
290
+ `| Name | Type | Required | Description |`,
291
+ `|------|------|----------|-------------|`,
292
+ ]
293
+
294
+ for (const [paramName, paramInfo] of Object.entries(methodInfo.parameters)) {
295
+ const isRequired = methodInfo.required?.includes(paramName) ? '✓' : ''
296
+ const type = paramInfo.type || 'any'
297
+ const description = paramInfo.description || ''
298
+ tableRows.push(`| \`${paramName}\` | \`${type}\` | ${isRequired} | ${description} |`)
299
+
300
+ // Render expanded type properties if available
301
+ if (paramInfo.properties && Object.keys(paramInfo.properties).length > 0) {
302
+ tableRows.push('')
303
+ tableRows.push(`\`${type}\` properties:`)
304
+ tableRows.push('')
305
+ tableRows.push(`| Property | Type | Description |`)
306
+ tableRows.push(`|----------|------|-------------|`)
307
+
308
+ for (const [propName, propInfo] of Object.entries(paramInfo.properties)) {
309
+ tableRows.push(`| \`${propName}\` | \`${propInfo.type || 'any'}\` | ${propInfo.description || ''} |`)
310
+ }
311
+ }
312
+ }
313
+ sections.push(tableRows.join('\n'))
314
+ }
315
+
316
+ if (methodInfo.returns) {
317
+ sections.push(`**Returns:** \`${methodInfo.returns}\``)
318
+ }
319
+
320
+ if (methodInfo.examples && methodInfo.examples.length > 0) {
321
+ for (const example of methodInfo.examples) {
322
+ sections.push(`\`\`\`${normalizeLang(example.language)}\n${example.code}\n\`\`\``)
323
+ }
324
+ }
325
+
326
+ sections.push('')
327
+ }
328
+
329
+ return sections
330
+ }
331
+
332
+ function renderGettersSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
333
+ const sections: string[] = []
334
+ if (!introspection.getters || Object.keys(introspection.getters).length === 0) return sections
335
+
336
+ const tableRows = [
337
+ `${heading(2)} Getters`,
338
+ '',
339
+ `| Property | Type | Description |`,
340
+ `|----------|------|-------------|`,
341
+ ]
342
+
343
+ for (const [getterName, getterInfo] of Object.entries(introspection.getters)) {
344
+ const type = getterInfo.returns || 'any'
345
+ const description = getterInfo.description || ''
346
+ tableRows.push(`| \`${getterName}\` | \`${type}\` | ${description} |`)
347
+ }
348
+ sections.push(tableRows.join('\n'))
349
+
350
+ return sections
351
+ }
352
+
353
+ function renderEventsSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
354
+ const sections: string[] = []
355
+ if (!introspection.events || Object.keys(introspection.events).length === 0) return sections
356
+
357
+ sections.push(`${heading(2)} Events (Zod v4 schema)`)
358
+
359
+ for (const [eventName, eventInfo] of Object.entries(introspection.events)) {
360
+ sections.push(`${heading(3)} ${eventName}`)
361
+
362
+ if (eventInfo.description) {
363
+ sections.push(eventInfo.description)
364
+ }
365
+
366
+ if (eventInfo.arguments && Object.keys(eventInfo.arguments).length > 0) {
367
+ const tableRows = [
368
+ `**Event Arguments:**`,
369
+ '',
370
+ `| Name | Type | Description |`,
371
+ `|------|------|-------------|`,
372
+ ]
373
+
374
+ for (const [argName, argInfo] of Object.entries(eventInfo.arguments)) {
375
+ tableRows.push(`| \`${argName}\` | \`${argInfo.type || 'any'}\` | ${argInfo.description || ''} |`)
376
+ }
377
+ sections.push(tableRows.join('\n'))
378
+ }
379
+
380
+ sections.push('')
381
+ }
382
+
383
+ return sections
384
+ }
385
+
386
+ function renderStateSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
387
+ const sections: string[] = []
388
+ if (!introspection.state || Object.keys(introspection.state).length === 0) return sections
389
+
390
+ const tableRows = [
391
+ `${heading(2)} State (Zod v4 schema)`,
392
+ '',
393
+ `| Property | Type | Description |`,
394
+ `|----------|------|-------------|`,
395
+ ]
396
+
397
+ for (const [stateName, stateInfo] of Object.entries(introspection.state)) {
398
+ tableRows.push(`| \`${stateName}\` | \`${stateInfo.type || 'any'}\` | ${stateInfo.description || ''} |`)
399
+ }
400
+ sections.push(tableRows.join('\n'))
401
+
402
+ return sections
403
+ }
404
+
405
+ function renderOptionsSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
406
+ const sections: string[] = []
407
+ if (!introspection.options || Object.keys(introspection.options).length === 0) return sections
408
+
409
+ const tableRows = [
410
+ `${heading(2)} Options (Zod v4 schema)`,
411
+ '',
412
+ `| Property | Type | Description |`,
413
+ `|----------|------|-------------|`,
414
+ ]
415
+
416
+ for (const [optName, optInfo] of Object.entries(introspection.options)) {
417
+ tableRows.push(`| \`${optName}\` | \`${optInfo.type || 'any'}\` | ${optInfo.description || ''} |`)
418
+ }
419
+ sections.push(tableRows.join('\n'))
420
+
421
+ return sections
422
+ }
423
+
424
+ function renderUsageSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
425
+ const sections: string[] = []
426
+
427
+ // Derive the factory method from the shortcut, e.g. "features.diskCache" -> container.feature('diskCache', { ... })
428
+ const shortcut = introspection.shortcut || introspection.id || ''
429
+ const parts = shortcut.split('.')
430
+ if (parts.length < 2) return sections
431
+
432
+ const scope = parts[0]! // e.g. "features"
433
+ const name = parts.slice(1).join('.') // e.g. "diskCache"
434
+ // Singular form: features -> feature, clients -> client, servers -> server, commands -> command, endpoints -> endpoint
435
+ const factoryMethod = scope.endsWith('s') ? scope.slice(0, -1) : scope
436
+
437
+ const options = introspection.options || {}
438
+ const optionEntries = Object.entries(options)
439
+
440
+ sections.push(`${heading(2)} Usage`)
441
+
442
+ if (optionEntries.length === 0) {
443
+ sections.push(`\`\`\`ts\ncontainer.${factoryMethod}('${name}')\n\`\`\``)
444
+ } else {
445
+ const optionLines = optionEntries.map(([optName, optInfo]) => {
446
+ const desc = optInfo.description ? `// ${optInfo.description}` : ''
447
+ return ` ${desc}\n ${optName},`
448
+ }).join('\n')
449
+
450
+ sections.push(`\`\`\`ts\ncontainer.${factoryMethod}('${name}', {\n${optionLines}\n})\n\`\`\``)
451
+ }
452
+
453
+ return sections
454
+ }
455
+
456
+ function renderEnvVarsSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
457
+ const sections: string[] = []
458
+ if (!introspection.envVars || introspection.envVars.length === 0) return sections
459
+
460
+ sections.push(`${heading(2)} Environment Variables`)
461
+ sections.push(introspection.envVars.map((envVar) => `- \`${envVar}\``).join('\n'))
462
+
463
+ return sections
464
+ }
465
+
466
+ function renderExamplesSection(introspection: HelperIntrospection, heading: (level: number) => string): string[] {
467
+ const sections: string[] = []
468
+
469
+ // Collect all examples: class-level, method-level, getter-level
470
+ const allExamples: { source: string; examples: ExampleIntrospection[] }[] = []
471
+
472
+ if (introspection.examples && introspection.examples.length > 0) {
473
+ allExamples.push({ source: introspection.id, examples: introspection.examples })
474
+ }
475
+
476
+ for (const [name, method] of Object.entries(introspection.methods || {})) {
477
+ if (method.examples && method.examples.length > 0) {
478
+ allExamples.push({ source: name, examples: method.examples })
479
+ }
480
+ }
481
+
482
+ for (const [name, getter] of Object.entries(introspection.getters || {})) {
483
+ if (getter.examples && getter.examples.length > 0) {
484
+ allExamples.push({ source: name, examples: getter.examples })
485
+ }
486
+ }
487
+
488
+ if (allExamples.length === 0) return sections
489
+
490
+ sections.push(`${heading(2)} Examples`)
491
+
492
+ for (const { source, examples } of allExamples) {
493
+ sections.push(`**${source}**`)
494
+ for (const example of examples) {
495
+ sections.push(`\`\`\`${normalizeLang(example.language)}\n${example.code}\n\`\`\``)
496
+ }
497
+ sections.push('')
498
+ }
499
+
500
+ return sections
501
+ }
502
+
503
+ /** Normalize verbose language names to short tags for code fences */
504
+ function normalizeLang(lang: string): string {
505
+ if (lang === 'typescript') return 'ts'
506
+ if (lang === 'javascript') return 'js'
507
+ return lang
508
+ }
509
+
510
+ export { presentIntrospectionJSONAsMarkdown as presentIntrospectionAsMarkdown }
511
+
512
+ function presentIntrospectionJSONAsMarkdown(introspection: HelperIntrospection, startHeadingDepth: number = 1, section?: IntrospectionSection) {
513
+ const sections: string[] = []
514
+ const heading = (level: number) => '#'.repeat(Math.max(1, startHeadingDepth + level - 1))
515
+
516
+ if (!section) {
517
+ const title = introspection.className
518
+ ? `${heading(1)} ${introspection.className} (${introspection.id})`
519
+ : `${heading(1)} ${introspection.id}`
520
+ sections.push(`${title}\n\n${introspection.description}`)
521
+ }
522
+
523
+ const renderers: Record<IntrospectionSection, () => string[]> = {
524
+ usage: () => renderUsageSection(introspection, heading),
525
+ options: () => renderOptionsSection(introspection, heading),
526
+ methods: () => renderMethodsSection(introspection, heading),
527
+ getters: () => renderGettersSection(introspection, heading),
528
+ events: () => renderEventsSection(introspection, heading),
529
+ state: () => renderStateSection(introspection, heading),
530
+ envVars: () => renderEnvVarsSection(introspection, heading),
531
+ examples: () => renderExamplesSection(introspection, heading),
532
+ }
533
+
534
+ if (section) {
535
+ sections.push(...renderers[section]())
536
+ } else {
537
+ for (const renderer of Object.values(renderers)) {
538
+ sections.push(...renderer())
539
+ }
540
+ }
541
+
542
+ return sections.join('\n\n')
543
+ }