@soederpop/luca 0.0.32 → 0.0.34

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 (86) hide show
  1. package/README.md +241 -36
  2. package/bun.lock +24 -5
  3. package/commands/build-python-bridge.ts +43 -0
  4. package/docs/apis/clients/rest.md +7 -7
  5. package/docs/apis/clients/websocket.md +23 -10
  6. package/docs/apis/features/agi/assistant.md +155 -8
  7. package/docs/apis/features/agi/assistants-manager.md +90 -22
  8. package/docs/apis/features/agi/auto-assistant.md +377 -0
  9. package/docs/apis/features/agi/browser-use.md +802 -0
  10. package/docs/apis/features/agi/claude-code.md +6 -1
  11. package/docs/apis/features/agi/conversation-history.md +7 -6
  12. package/docs/apis/features/agi/conversation.md +111 -38
  13. package/docs/apis/features/agi/docs-reader.md +35 -57
  14. package/docs/apis/features/agi/file-tools.md +163 -0
  15. package/docs/apis/features/agi/openapi.md +2 -2
  16. package/docs/apis/features/agi/skills-library.md +227 -0
  17. package/docs/apis/features/node/content-db.md +125 -4
  18. package/docs/apis/features/node/disk-cache.md +11 -11
  19. package/docs/apis/features/node/downloader.md +1 -1
  20. package/docs/apis/features/node/file-manager.md +15 -15
  21. package/docs/apis/features/node/fs.md +78 -21
  22. package/docs/apis/features/node/git.md +50 -10
  23. package/docs/apis/features/node/google-calendar.md +3 -0
  24. package/docs/apis/features/node/google-docs.md +10 -1
  25. package/docs/apis/features/node/google-drive.md +3 -0
  26. package/docs/apis/features/node/google-mail.md +214 -0
  27. package/docs/apis/features/node/google-sheets.md +3 -0
  28. package/docs/apis/features/node/ink.md +10 -10
  29. package/docs/apis/features/node/ipc-socket.md +83 -93
  30. package/docs/apis/features/node/networking.md +5 -5
  31. package/docs/apis/features/node/os.md +7 -7
  32. package/docs/apis/features/node/package-finder.md +14 -14
  33. package/docs/apis/features/node/proc.md +2 -1
  34. package/docs/apis/features/node/process-manager.md +70 -3
  35. package/docs/apis/features/node/python.md +265 -9
  36. package/docs/apis/features/node/redis.md +380 -0
  37. package/docs/apis/features/node/ui.md +13 -13
  38. package/docs/apis/servers/express.md +35 -7
  39. package/docs/apis/servers/mcp.md +3 -3
  40. package/docs/apis/servers/websocket.md +51 -8
  41. package/docs/bootstrap/CLAUDE.md +1 -1
  42. package/docs/bootstrap/SKILL.md +93 -7
  43. package/docs/examples/feature-as-tool-provider.md +143 -0
  44. package/docs/examples/python.md +42 -1
  45. package/docs/introspection.md +15 -5
  46. package/docs/tutorials/00-bootstrap.md +3 -3
  47. package/docs/tutorials/02-container.md +2 -2
  48. package/docs/tutorials/10-creating-features.md +5 -0
  49. package/docs/tutorials/13-introspection.md +12 -2
  50. package/docs/tutorials/19-python-sessions.md +401 -0
  51. package/package.json +8 -4
  52. package/src/agi/container.server.ts +8 -0
  53. package/src/agi/features/assistant.ts +18 -0
  54. package/src/agi/features/autonomous-assistant.ts +435 -0
  55. package/src/agi/features/conversation.ts +58 -6
  56. package/src/agi/features/file-tools.ts +286 -0
  57. package/src/agi/features/luca-coder.ts +643 -0
  58. package/src/bootstrap/generated.ts +705 -17
  59. package/src/cli/build-info.ts +2 -2
  60. package/src/cli/cli.ts +22 -13
  61. package/src/commands/bootstrap.ts +49 -6
  62. package/src/commands/code.ts +369 -0
  63. package/src/commands/describe.ts +7 -2
  64. package/src/commands/index.ts +1 -0
  65. package/src/commands/sandbox-mcp.ts +7 -7
  66. package/src/commands/save-api-docs.ts +1 -1
  67. package/src/container-describer.ts +4 -4
  68. package/src/container.ts +10 -19
  69. package/src/helper.ts +24 -33
  70. package/src/introspection/generated.agi.ts +3026 -590
  71. package/src/introspection/generated.node.ts +1625 -688
  72. package/src/introspection/generated.web.ts +15 -57
  73. package/src/node/container.ts +5 -0
  74. package/src/node/features/figlet-fonts.ts +597 -0
  75. package/src/node/features/fs.ts +3 -9
  76. package/src/node/features/helpers.ts +20 -0
  77. package/src/node/features/python.ts +429 -16
  78. package/src/node/features/redis.ts +446 -0
  79. package/src/node/features/ui.ts +4 -11
  80. package/src/python/bridge.py +220 -0
  81. package/src/python/generated.ts +227 -0
  82. package/src/scaffolds/generated.ts +1 -1
  83. package/test/python-session.test.ts +105 -0
  84. package/assistants/lucaExpert/CORE.md +0 -37
  85. package/assistants/lucaExpert/hooks.ts +0 -9
  86. package/assistants/lucaExpert/tools.ts +0 -177
package/README.md CHANGED
@@ -1,48 +1,171 @@
1
- # Luca: Lightweight Universal Conversational Architecture
1
+ # Luca
2
2
 
3
- `luca` is a CLI you can download that comes with 40+ self-documenting feature, client, and server modules and types that you can use to build full featured, secure applications, without any complication, or without any `npm install` step.
3
+ **Lightweight Universal Conversational Architecture**
4
4
 
5
- Luca has the concept of a `container` object that is, metaphorically, very similar to a Docker container.
5
+ A single binary CLI that ships 40+ self-documenting features, clients, and servers. No `npm install`, no setup — download it and start building.
6
6
 
7
- Like Docker containers, you can build layers on top of the base luca `NodeContainer` or `WebContainer` and `use()` your own `Feature`, `Client`, `Command`, `Endpoint` and `Server` patterns. Bundle that up into your own single file executable, or browser bundle, and provide a standard foundation for all of your custom applications to build on top of.
7
+ Luca gives you a `container` object — think of it like a Docker container for your application runtime. It's a per-process singleton, event bus, state machine, and dependency injector all in one. Layer your own features, clients, servers, commands, and endpoints on top of the base `NodeContainer` (server) or `WebContainer` (browser), then bundle it into your own single-file executable or browser build.
8
8
 
9
- This library provides a `NodeContainer` for server side applications, `WebContainer` for the browser, and an `AGIContainer` that demonstrates another layer on top of the standard node server / script stack.
9
+ The `AGIContainer` extends the node stack with features, clients, and servers for building AI assistants — wrappers around major coding models, tool orchestration, and UIs to visualize them working.
10
10
 
11
- The `AGIContainer` provides features, clients, and servers suitable for building a whole manner of AI Assistants, and comes with its own Assistants to write framework code for you or architect and design applications. It provides wrappers around all of the major coding assistants, so you can build cool UI applications to visualize them working.
11
+ ## Installation
12
+
13
+ ### Download the binary
14
+
15
+ Grab the latest release for your platform from [GitHub Releases](https://github.com/soederpop/luca/releases/latest):
16
+
17
+ | Platform | Binary |
18
+ |----------|--------|
19
+ | macOS (Apple Silicon) | `luca-darwin-arm64` |
20
+ | macOS (Intel) | `luca-darwin-x64` |
21
+ | Linux x64 | `luca-linux-x64` |
22
+ | Linux ARM64 | `luca-linux-arm64` |
23
+ | Windows x64 | `luca-windows-x64.exe` |
24
+
25
+ **Quick install (macOS/Linux):**
26
+
27
+ ```sh
28
+ # Download (replace PLATFORM with your arch, e.g. darwin-arm64)
29
+ curl -L -o luca https://github.com/soederpop/luca/releases/latest/download/luca-PLATFORM
30
+ chmod +x luca
31
+ sudo mv luca /usr/local/bin/
32
+ ```
33
+
34
+ ### macOS Gatekeeper warning
35
+
36
+ The binary is not yet code-signed (code signing is in progress). On first run macOS will block it with *"luca can't be opened because Apple cannot check it for malicious software."*
37
+
38
+ To allow it:
39
+
40
+ 1. **System Settings** > **Privacy & Security** > scroll down to the Security section
41
+ 2. You'll see a message about `luca` being blocked — click **Allow Anyway**
42
+ 3. Run `luca` again and click **Open** in the confirmation dialog
43
+
44
+ Or from the terminal:
45
+
46
+ ```sh
47
+ xattr -d com.apple.quarantine /usr/local/bin/luca
48
+ ```
49
+
50
+ After that it runs without interruption.
51
+
52
+ ### Verify
53
+
54
+ ```sh
55
+ luca --version
56
+ # luca v0.0.34 (main@325a0ee) built 2026-03-25T06:10:28Z
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ### Bootstrap a new project
62
+
63
+ ```sh
64
+ luca bootstrap my-app
65
+ cd my-app
66
+ ```
67
+
68
+ Or just run `luca bootstrap` and it'll ask you for a project name. This scaffolds a project with `commands/`, `endpoints/`, `features/`, `docs/`, and AI assistant configuration — everything wired up and ready to extend.
69
+
70
+ ### Explore
71
+
72
+ ```sh
73
+ luca # list all commands
74
+ luca describe features # index of 40+ features
75
+ luca describe fs # full docs for any feature
76
+ luca describe fs.readFile # drill into a specific method
77
+ luca eval "container.features.available" # run code with the container in scope
78
+ luca console # full REPL
79
+ ```
80
+
81
+ ### Run scripts and markdown
82
+
83
+ `luca run` executes TypeScript, JavaScript, and markdown files. Markdown files have their code blocks executed in order, with `container` already in scope — no imports needed:
84
+
85
+ ````md
86
+ # my-script.md
87
+
88
+ Grab some data and print it:
89
+
90
+ ```ts
91
+ const fs = container.feature('fs')
92
+ const files = await fs.readdir('.')
93
+ console.log(`Found ${files.length} files`)
94
+ ```
95
+
96
+ Then do something with it:
97
+
98
+ ```ts
99
+ const yaml = container.feature('yaml')
100
+ console.log(yaml.stringify({ files }))
101
+ ```
102
+ ````
103
+
104
+ ```sh
105
+ luca run my-script.md
106
+ ```
107
+
108
+ Each block shares state with the previous ones, so variables defined in one block are available in the next. Use `--safe` to require approval before each block, or `--console` to drop into a REPL afterward with all the accumulated context. **Note:** if your block uses top-level awaits, we can't preserve context. You can use `container.addContext({ yourVariable })` and it will be available as a global variable in future blocks.
109
+
110
+ ### Serve
111
+
112
+ ```sh
113
+ luca serve # serves endpoints/ folder as HTTP routes
114
+ ```
12
115
 
13
- ## Fully typed at Author-time / Self-Documenting at Runtime
116
+ See [`docs/CLI.md`](./docs/CLI.md) for the full CLI reference.
14
117
 
15
- Besides being fully typed, The `container`'s JavaScript API is self documenting at runtime. All of the constructor options, the shape of observable state, events emitted, environment variables used, method descriptions, are documented and fully typed. This helps while debugging, working in the chrome console or a CLI REPL, allows for insane metaprogramming and is a great way to learn about the framework and its components.
118
+ ## Importing the container into your own scripts / modules
16
119
 
17
120
  ```ts
18
121
  import container from '@soederpop/luca'
122
+ ```
123
+
124
+ That's it — you get one object with everything on it. No factory function, no setup. It's a singleton.
125
+
126
+ We do export the framework classes (`WebContainer`, `Feature`, `Client`, `Server`, etc.) if you want to extend them, but for using the system you only ever need the default export.
127
+
128
+ ### In the browser via esm.sh
19
129
 
20
- container.features.available // ['fs','git','proc','vault',...]
21
- container.clients.available // ['rest','websocket']
22
- container.servers.available // ['express','websocket','ipc','mcp']
130
+ **Static import:**
23
131
 
24
- container.features.describe() // markdown or json summary of all features
25
- container.feature.describe('fileManager') // describe an individual feature
132
+ ```js
133
+ import container from 'https://esm.sh/@soederpop/luca/web'
26
134
 
27
- container.introspect() // a json structure that describes the container, its registries, their members
28
- container.introspectAsText() // a markdown description of the same
135
+ container.features.available // ['fetch', 'state', 'ui', ...]
29
136
  ```
30
137
 
31
- You can pass args to these things to only get a small slice of information, e.g. just usage examples, or a list of events it emits, or documentation for a single method.
138
+ **Dynamic import:**
32
139
 
33
- And the individual components also respond to the same
140
+ ```js
141
+ const { default: container } = await import('https://esm.sh/@soederpop/luca/web')
34
142
 
143
+ container.features.available // same singleton
35
144
  ```
36
- const fileManager = container.feature('fileManager')
37
145
 
38
- fileManager.introspect() // json
39
- fileManager.introspectAsText() // markdown
40
- fileManager.introspectAsText("usage", "examples") // just summarize it for me my boy
146
+ With dynamic import you have to pick it off `default` yourself — there's no top-level default binding like the static form gives you. Either way, it's the same singleton container, and `window.luca` is set automatically so you can poke at it from the console.
147
+
148
+ ## How It Works
149
+
150
+ ### Self-documenting at runtime
151
+
152
+ Every helper (feature, client, server) carries its own introspection metadata — constructor options, observable state shape, events emitted, environment variables used, method signatures. This powers `luca describe`, works in the REPL, and enables metaprogramming.
153
+
154
+ ```ts
155
+ import container from '@soederpop/luca'
156
+
157
+ container.features.available // ['fs','git','proc','vault',...]
158
+ container.clients.available // ['rest','websocket']
159
+ container.servers.available // ['express','websocket','ipc','mcp']
160
+
161
+ container.features.describe() // markdown summary of all features
162
+ container.feature('fs').introspect() // json
163
+ container.feature('fs').introspectAsText() // markdown
41
164
  ```
42
165
 
43
- ## Self Documentation on steroids actually
166
+ ### Content-aware documentation
44
167
 
45
- The node container has a `container.docs` feature that uses [Contentbase](https://contentbase.soederpop.com) to be able to understand and interact with the local project markdown documentation
168
+ The node container includes `container.docs` powered by [Contentbase](https://github.com/soederpop/contentbase) — query your project's markdown documentation like a database:
46
169
 
47
170
  ```ts
48
171
  await container.docs.load()
@@ -50,29 +173,111 @@ const { Tutorial } = container.docs.models
50
173
  const tutorials = await container.docs.query(Tutorial).fetchAll()
51
174
  ```
52
175
 
53
- ## A Perfect Companion for AI Coding Assistants and Students alike
176
+ ### Project extensions
177
+
178
+ Drop files into convention-based folders and they're auto-discovered:
54
179
 
55
- The `luca` CLI [Full Docs Here](./docs/CLI.md) has a few interesting commands.
180
+ - `commands/` — custom CLI commands, run via `luca <name>`
181
+ - `endpoints/` — file-based HTTP routes, served via `luca serve`
182
+ - `features/` — custom container features
56
183
 
57
- - The `luca eval` command lets you run snippets of code to see what they produce. The `container` is already defined for you
58
- - The `luca describe` command lets you view docs, or just parts of docs, of any group of features, clients, etc, as a single markdown doc
59
- - The `luca console` command will bring you into a full blown REPL
60
- - The `luca chat` command will put you in touch with a tutor
61
- - The `luca sandbox-mcp` provides a REPL for your coding assistant and a documentation browser
184
+ Generate boilerplate with `luca scaffold`:
62
185
 
63
- ### Codex MCP note
186
+ ```sh
187
+ luca scaffold command myTask --description "Automate something"
188
+ luca scaffold feature myCache --description "Custom caching layer"
189
+ luca scaffold endpoint users --description "User management API"
190
+ ```
64
191
 
65
- If OpenAI Codex CLI reports `MCP client ... timed out after 10 seconds` for Luca or Contentbase stdio servers, use a Node stdio bridge to launch Bun. This avoids an intermittent Codex<->Bun stdio startup issue where the server appears to start but handshake still times out.
192
+ ### Building an assistant
66
193
 
67
- See [`docs/CLI.md`](./docs/CLI.md) for the exact bridge script and `~/.codex/config.toml` examples.
194
+ Features can inject their own tools into an assistant via `assistant.use()`. Here's an assistant that can browse the web:
68
195
 
69
- ## Installation
196
+ ```ts
197
+ import container from '@soederpop/luca'
198
+
199
+ const browser = container.feature('browserUse', { headed: true })
200
+ const assistant = container.feature('assistant', {
201
+ systemPrompt: 'You are a web research assistant. Use your browser tools to find information.',
202
+ model: 'gpt-4.1-mini',
203
+ })
204
+
205
+ // browserUse injects its tools — open, click, type, screenshot, extract, etc.
206
+ assistant.use(browser)
207
+ await assistant.start()
208
+
209
+ await assistant.ask('Go to hacker news and tell me what the top 3 stories are about')
210
+ ```
70
211
 
71
- To install the CLI
212
+ ### AI coding assistant integration
213
+
214
+ The CLI works great alongside Claude Code, Codex, and other coding assistants:
215
+
216
+ - `luca describe` gives assistants full API docs for any helper
217
+ - `luca eval` lets them test container expressions before committing code
218
+ - `luca sandbox-mcp` provides a REPL and doc browser as an MCP server
219
+
220
+ ## Development
221
+
222
+ ### Prerequisites
223
+
224
+ - [Bun](https://bun.sh) (runtime and test runner)
225
+
226
+ ### Setup
227
+
228
+ ```sh
229
+ git clone https://github.com/soederpop/luca.git
230
+ cd luca
231
+ bun install
232
+ ```
233
+
234
+ ### Running in dev
235
+
236
+ ```sh
237
+ # Run the CLI from source (equivalent to the luca binary)
238
+ bun run src/cli/cli.ts
239
+
240
+ # Examples
241
+ bun run src/cli/cli.ts describe features
242
+ bun run src/cli/cli.ts eval "container.features.available"
243
+ ```
244
+
245
+ ### Testing
246
+
247
+ ```sh
248
+ # Unit tests
249
+ bun test
250
+
251
+ # Integration tests (may require API keys / env vars)
252
+ bun run test:integration
253
+ ```
254
+
255
+ ### Building the binary
72
256
 
73
257
  ```sh
74
- echo 'i will eventually have a url to download the CLI and stuff'
258
+ bun run compile
75
259
  ```
76
260
 
261
+ This runs the full pipeline: introspection generation, scaffold templates, bootstrap code, python bridge, build stamp, then compiles to `dist/luca` via Bun's native compiler.
262
+
263
+ ### Project structure
264
+
265
+ ```
266
+ src/
267
+ cli/ CLI entry point and commands
268
+ node/ NodeContainer and server-side features
269
+ web/ WebContainer and browser features
270
+ agi/ AGIContainer — AI assistant layer
271
+ schemas/ Shared Zod schemas
272
+ react/ React bindings
273
+ test/ Unit tests
274
+ test-integration/ Integration tests
275
+ docs/
276
+ apis/ Generated API docs
277
+ examples/ Runnable examples (luca run docs/examples/grep)
278
+ tutorials/ Longer-form guides
279
+ ```
77
280
 
281
+ ## License
78
282
 
283
+ MIT
package/bun.lock CHANGED
@@ -18,7 +18,7 @@
18
18
  "chokidar": "^3.5.3",
19
19
  "cli-markdown": "^3.5.0",
20
20
  "compromise": "^14.14.5",
21
- "contentbase": "^0.0.5",
21
+ "contentbase": "^0.1.7",
22
22
  "cors": "^2.8.5",
23
23
  "detect-port": "^1.5.1",
24
24
  "dotenv": "^17.2.4",
@@ -33,6 +33,7 @@
33
33
  "inflect": "^0.5.0",
34
34
  "ink": "^6.7.0",
35
35
  "inquirer": "^9.1.5",
36
+ "ioredis": "^5.10.1",
36
37
  "isomorphic-vm": "^0.0.1",
37
38
  "isomorphic-ws": "^5.0.0",
38
39
  "js-tiktoken": "^1.0.21",
@@ -192,6 +193,8 @@
192
193
 
193
194
  "@inquirer/figures": ["@inquirer/figures@1.0.12", "", {}, "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ=="],
194
195
 
196
+ "@ioredis/commands": ["@ioredis/commands@1.5.1", "", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
197
+
195
198
  "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
196
199
 
197
200
  "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
@@ -334,7 +337,7 @@
334
337
 
335
338
  "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="],
336
339
 
337
- "@soederpop/luca": ["@soederpop/luca@0.0.2", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", "@ngrok/ngrok": "^1.5.1", "@openai/codex": "^0.99.0", "@resvg/resvg-js": "^2.6.2", "@supabase/supabase-js": "^2.95.3", "@types/marked": "^6.0.0", "@types/marked-terminal": "^6.1.1", "axios": "^1.3.5", "cacache": "^17.0.7", "chalk": "^5.2.0", "child-process-promise": "^2.2.1", "chokidar": "^3.5.3", "cli-markdown": "^3.5.0", "compromise": "^14.14.5", "contentbase": "", "cors": "^2.8.5", "cross-fetch": "^4.1.0", "detect-port": "^1.5.1", "dotenv": "^17.2.4", "endent": "^2.1.0", "esbuild-wasm": "0.17.18", "excalidraw-to-svg": "^3.1.0", "express": "^4.18.2", "figlet": "^1.6.0", "glob": "^11.0.2", "googleapis": "^171.4.0", "grammy": "^1.40.0", "inflect": "^0.5.0", "ink": "^6.7.0", "inquirer": "^9.1.5", "isomorphic-vm": "^0.0.1", "isomorphic-ws": "^5.0.0", "js-tiktoken": "^1.0.21", "js-yaml": "^4.1.0", "lodash-es": "^4.17.21", "marked": "^15.0.12", "marked-terminal": "^7.3.0", "mdast-util-to-markdown": "^1.5.0", "mdast-util-to-string": "^3.2.0", "micromatch": "^4.0.5", "minimist": "^1.2.8", "node-uuid": "^1.4.8", "object-hash": "^3.0.0", "openai": "^5.1.1", "opener": "^1.5.2", "react": "^19.2.4", "react-devtools-core": "^7.0.1", "react-dom": "^19.2.4", "react-reconciler": "^0.33.0", "remark-gfm": "^3.0.1", "rimraf": "^5.0.0", "typescript": "^5.9.3", "unist-util-find-after": "^4.0.1", "unist-util-find-all-after": "^4.0.1", "unist-util-find-all-before": "^4.0.1", "unist-util-find-before": "^3.0.1", "unist-util-select": "^4.0.3", "unist-util-visit": "^4.1.2", "wink-eng-lite-web-model": "^1.8.1", "wink-nlp": "^2.4.0", "ws": "^8.13.0", "zod": "^4.0.0" }, "optionalDependencies": { "node-llama-cpp": "^3.17.1" }, "bin": { "luca": "src/cli/cli.ts" } }, "sha512-oxHCwZYjnGFWvr9TdkYcbBy6txwFORibumQeXF1UWBQsVTJECot5GlQ+dyVlmMLvEcde7N0JOryJZVWeWQRkQg=="],
340
+ "@soederpop/luca": ["@soederpop/luca@0.0.32", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", "@ngrok/ngrok": "^1.5.1", "@openai/codex": "^0.99.0", "@resvg/resvg-js": "^2.6.2", "@supabase/supabase-js": "^2.95.3", "@types/marked": "^6.0.0", "@types/marked-terminal": "^6.1.1", "axios": "^1.3.5", "cacache": "^17.0.7", "chalk": "^5.2.0", "child-process-promise": "^2.2.1", "chokidar": "^3.5.3", "cli-markdown": "^3.5.0", "compromise": "^14.14.5", "contentbase": "^0.1.7", "cors": "^2.8.5", "detect-port": "^1.5.1", "dotenv": "^17.2.4", "endent": "^2.1.0", "esbuild-wasm": "0.17.18", "excalidraw-to-svg": "^3.1.0", "express": "^4.18.2", "figlet": "^1.6.0", "glob": "^11.0.2", "googleapis": "^171.4.0", "grammy": "^1.40.0", "inflect": "^0.5.0", "ink": "^6.7.0", "inquirer": "^9.1.5", "isomorphic-vm": "^0.0.1", "isomorphic-ws": "^5.0.0", "js-tiktoken": "^1.0.21", "js-yaml": "^4.1.0", "lodash-es": "^4.17.21", "marked": "^15.0.12", "marked-terminal": "^7.3.0", "mdast-util-to-markdown": "^1.5.0", "mdast-util-to-string": "^3.2.0", "micromatch": "^4.0.5", "minimist": "^1.2.8", "node-uuid": "^1.4.8", "object-hash": "^3.0.0", "openai": "^5.1.1", "opener": "^1.5.2", "react": "^19.2.4", "react-devtools-core": "^7.0.1", "react-dom": "^19.2.4", "react-reconciler": "^0.33.0", "remark-gfm": "^3.0.1", "rimraf": "^5.0.0", "typescript": "^5.9.3", "unist-util-find-after": "^4.0.1", "unist-util-find-all-after": "^4.0.1", "unist-util-find-all-before": "^4.0.1", "unist-util-find-before": "^3.0.1", "unist-util-select": "^4.0.3", "unist-util-visit": "^4.1.2", "wink-eng-lite-web-model": "^1.8.1", "wink-nlp": "^2.4.0", "ws": "^8.13.0", "zod": "^4.0.0" }, "optionalDependencies": { "node-llama-cpp": "^3.17.1" }, "bin": { "luca": "src/cli/cli.ts" } }, "sha512-JlIz18N0/J/dDfaFluJdwIvY1P6LkuV/gDBbrj3XGKAN1YaumMLThTcoQARE8CgEyfKVfM8bM9T9nNmygs1lCQ=="],
338
341
 
339
342
  "@supabase/auth-js": ["@supabase/auth-js@2.95.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-vD2YoS8E2iKIX0F7EwXTmqhUpaNsmbU6X2R0/NdFcs02oEfnHyNP/3M716f3wVJ2E5XHGiTFXki6lRckhJ0Thg=="],
340
343
 
@@ -638,6 +641,8 @@
638
641
 
639
642
  "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
640
643
 
644
+ "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
645
+
641
646
  "cmake-js": ["cmake-js@8.0.0", "", { "dependencies": { "debug": "^4.4.3", "fs-extra": "^11.3.3", "node-api-headers": "^1.8.0", "rc": "1.2.8", "semver": "^7.7.3", "tar": "^7.5.6", "url-join": "^4.0.1", "which": "^6.0.0", "yargs": "^17.7.2" }, "bin": { "cmake-js": "bin/cmake-js" } }, "sha512-YbUP88RDwCvoQkZhRtGURYm9RIpWdtvZuhT87fKNoLjk8kIFIFeARpKfuZQGdwfH99GZpUmqSfcDrK62X7lTgg=="],
642
647
 
643
648
  "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
@@ -674,7 +679,7 @@
674
679
 
675
680
  "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
676
681
 
677
- "contentbase": ["contentbase@0.0.5", "", { "dependencies": { "@soederpop/luca": "^0.0.2", "gray-matter": "^4.0.3", "js-yaml": "^4.1.0", "mdast-util-mdxjs-esm": "^2.0.1", "mdast-util-to-markdown": "^2.1.2", "mdast-util-to-string": "^4.0.0", "picomatch": "^4.0.3", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-util-find-after": "^5.0.0", "unist-util-find-all-after": "^5.0.0", "unist-util-find-all-before": "^5.0.0", "unist-util-find-before": "^4.0.0", "unist-util-select": "^5.1.0", "unist-util-visit": "^5.0.0", "zod": "^4.3.6" }, "bin": { "cnotes": "src/cli/index.ts", "contentbase": "src/cli/index.ts" } }, "sha512-TmF9K30CgHnljfJMbd7q3eTMR6fwZltp/WJpLWch9pA79hFMT5tg+r1piEOHS5UIFOYQKmEB79Rm6gugDgCOSw=="],
682
+ "contentbase": ["contentbase@0.1.8", "", { "dependencies": { "@soederpop/luca": ">=0.0.16", "gray-matter": "^4.0.3", "js-yaml": "^4.1.0", "mdast-util-mdxjs-esm": "^2.0.1", "mdast-util-to-markdown": "^2.1.2", "mdast-util-to-string": "^4.0.0", "picomatch": "^4.0.3", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-stringify": "^11.0.0", "unified": "^11.0.5", "unist-util-find-after": "^5.0.0", "unist-util-find-all-after": "^5.0.0", "unist-util-find-all-before": "^5.0.0", "unist-util-find-before": "^4.0.0", "unist-util-select": "^5.1.0", "unist-util-visit": "^5.0.0", "zod": "^4.3.6" }, "bin": { "cnotes": "src/cli/index.ts", "contentbase": "src/cli/index.ts" } }, "sha512-FcivPrOkmowDea2FobSCHPmNdudQz90ZF5xwp2aDF5ajRXu1lujvzOXEvmwAZ9AcJC37BQHVNR0Ifa1guZaODw=="],
678
683
 
679
684
  "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
680
685
 
@@ -684,8 +689,6 @@
684
689
 
685
690
  "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
686
691
 
687
- "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="],
688
-
689
692
  "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
690
693
 
691
694
  "css-declaration-sorter": ["css-declaration-sorter@7.2.0", "", { "peerDependencies": { "postcss": "^8.0.9" } }, "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow=="],
@@ -738,6 +741,8 @@
738
741
 
739
742
  "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
740
743
 
744
+ "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
745
+
741
746
  "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
742
747
 
743
748
  "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
@@ -1032,6 +1037,8 @@
1032
1037
 
1033
1038
  "inquirer": ["inquirer@9.3.7", "", { "dependencies": { "@inquirer/figures": "^1.0.3", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "external-editor": "^3.1.0", "mute-stream": "1.0.0", "ora": "^5.4.1", "run-async": "^3.0.0", "rxjs": "^7.8.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" } }, "sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w=="],
1034
1039
 
1040
+ "ioredis": ["ioredis@5.10.1", "", { "dependencies": { "@ioredis/commands": "1.5.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA=="],
1041
+
1035
1042
  "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
1036
1043
 
1037
1044
  "ipull": ["ipull@3.9.5", "", { "dependencies": { "@tinyhttp/content-disposition": "^2.2.0", "async-retry": "^1.3.3", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-spinners": "^2.9.2", "commander": "^10.0.0", "eventemitter3": "^5.0.1", "filenamify": "^6.0.0", "fs-extra": "^11.1.1", "is-unicode-supported": "^2.0.0", "lifecycle-utils": "^2.0.1", "lodash.debounce": "^4.0.8", "lowdb": "^7.0.1", "pretty-bytes": "^6.1.0", "pretty-ms": "^8.0.0", "sleep-promise": "^9.1.0", "slice-ansi": "^7.1.0", "stdout-update": "^4.0.1", "strip-ansi": "^7.1.0" }, "optionalDependencies": { "@reflink/reflink": "^0.1.16" }, "bin": { "ipull": "dist/cli/cli.js" } }, "sha512-5w/yZB5lXmTfsvNawmvkCjYo4SJNuKQz/av8TC1UiOyfOHyaM+DReqbpU2XpWYfmY+NIUbRRH8PUAWsxaS+IfA=="],
@@ -1122,6 +1129,10 @@
1122
1129
 
1123
1130
  "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
1124
1131
 
1132
+ "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
1133
+
1134
+ "lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
1135
+
1125
1136
  "lodash.memoize": ["lodash.memoize@4.1.2", "", {}, "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="],
1126
1137
 
1127
1138
  "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
@@ -1536,6 +1547,10 @@
1536
1547
 
1537
1548
  "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
1538
1549
 
1550
+ "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
1551
+
1552
+ "redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
1553
+
1539
1554
  "rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
1540
1555
 
1541
1556
  "remark-gfm": ["remark-gfm@3.0.1", "", { "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-gfm": "^2.0.0", "micromark-extension-gfm": "^2.0.0", "unified": "^10.0.0" } }, "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig=="],
@@ -1634,6 +1649,8 @@
1634
1649
 
1635
1650
  "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
1636
1651
 
1652
+ "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
1653
+
1637
1654
  "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
1638
1655
 
1639
1656
  "stdin-discarder": ["stdin-discarder@0.3.1", "", {}, "sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA=="],
@@ -2030,6 +2047,8 @@
2030
2047
 
2031
2048
  "inquirer/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
2032
2049
 
2050
+ "ioredis/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
2051
+
2033
2052
  "ipull/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
2034
2053
 
2035
2054
  "ipull/lifecycle-utils": ["lifecycle-utils@2.1.0", "", {}, "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA=="],
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod'
2
+ import type { ContainerContext } from '@soederpop/luca'
3
+ import { CommandOptionsSchema } from '@soederpop/luca/schemas'
4
+
5
+ export const argsSchema = CommandOptionsSchema.extend({})
6
+
7
+ async function buildPythonBridge(options: z.infer<typeof argsSchema>, context: ContainerContext) {
8
+ const container = context.container as any
9
+ const fs = container.feature('fs')
10
+
11
+ const sourcePath = 'src/python/bridge.py'
12
+ const outputPath = 'src/python/generated.ts'
13
+
14
+ if (!fs.exists(sourcePath)) {
15
+ console.error(` āŒ ${sourcePath} not found`)
16
+ process.exit(1)
17
+ }
18
+
19
+ const content = fs.readFile(sourcePath)
20
+ console.log(` šŸ“„ bridge.py: ${content.length} chars`)
21
+
22
+ const escapeForTemplate = (s: string) =>
23
+ s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
24
+
25
+ const output = `// Auto-generated Python bridge script
26
+ // Generated at: ${new Date().toISOString()}
27
+ // Source: src/python/bridge.py
28
+ //
29
+ // Do not edit manually. Run: luca build-python-bridge
30
+
31
+ export const bridgeScript = \`${escapeForTemplate(content)}\`
32
+ `
33
+
34
+ fs.ensureFolder('src/python')
35
+ await fs.writeFileAsync(outputPath, output)
36
+ console.log(`\n✨ Generated ${outputPath}`)
37
+ }
38
+
39
+ export default {
40
+ description: 'Bundle the Python bridge script into src/python/generated.ts',
41
+ argsSchema,
42
+ handler: buildPythonBridge,
43
+ }
@@ -24,13 +24,13 @@ container.client('rest', {
24
24
 
25
25
  ### beforeRequest
26
26
 
27
- **Returns:** `void`
27
+ **Returns:** `Promise<void>`
28
28
 
29
29
 
30
30
 
31
31
  ### patch
32
32
 
33
- Send a PATCH request.
33
+ Send a PATCH request. Returns the parsed response body directly (not an axios Response wrapper). On HTTP errors, returns the error as JSON instead of throwing.
34
34
 
35
35
  **Parameters:**
36
36
 
@@ -46,7 +46,7 @@ Send a PATCH request.
46
46
 
47
47
  ### put
48
48
 
49
- Send a PUT request.
49
+ Send a PUT request. Returns the parsed response body directly (not an axios Response wrapper). On HTTP errors, returns the error as JSON instead of throwing.
50
50
 
51
51
  **Parameters:**
52
52
 
@@ -62,7 +62,7 @@ Send a PUT request.
62
62
 
63
63
  ### post
64
64
 
65
- Send a POST request.
65
+ Send a POST request. Returns the parsed response body directly (not an axios Response wrapper). On HTTP errors, returns the error as JSON instead of throwing.
66
66
 
67
67
  **Parameters:**
68
68
 
@@ -78,7 +78,7 @@ Send a POST request.
78
78
 
79
79
  ### delete
80
80
 
81
- Send a DELETE request.
81
+ Send a DELETE request. Returns the parsed response body directly (not an axios Response wrapper). On HTTP errors, returns the error as JSON instead of throwing.
82
82
 
83
83
  **Parameters:**
84
84
 
@@ -94,7 +94,7 @@ Send a DELETE request.
94
94
 
95
95
  ### get
96
96
 
97
- Send a GET request.
97
+ Send a GET request. Returns the parsed response body directly (not an axios Response wrapper). On HTTP errors, returns the error as JSON instead of throwing.
98
98
 
99
99
  **Parameters:**
100
100
 
@@ -118,7 +118,7 @@ Handle an axios error by emitting 'failure' and returning the error as JSON.
118
118
  |------|------|----------|-------------|
119
119
  | `error` | `AxiosError` | āœ“ | Parameter error |
120
120
 
121
- **Returns:** `void`
121
+ **Returns:** `Promise<object>`
122
122
 
123
123
 
124
124
 
@@ -1,6 +1,6 @@
1
1
  # WebSocketClient (clients.websocket)
2
2
 
3
- WebSocket client that bridges raw WebSocket events to Luca's Helper event bus, providing a clean interface for sending/receiving messages, tracking connection state, and optional auto-reconnection with exponential backoff. Events emitted: - `open` — connection established - `message` — message received (JSON-parsed when possible) - `close` — connection closed (with code and reason) - `error` — connection error - `reconnecting` — attempting reconnection (with attempt number)
3
+ WebSocketClient helper
4
4
 
5
5
  ## Usage
6
6
 
@@ -53,6 +53,26 @@ Send data over the WebSocket connection. Automatically JSON-serializes the paylo
53
53
 
54
54
 
55
55
 
56
+ ### ask
57
+
58
+ Send a request and wait for a correlated response. The message is sent with a unique `requestId`; the remote side is expected to reply with a message containing `replyTo` set to that same ID.
59
+
60
+ **Parameters:**
61
+
62
+ | Name | Type | Required | Description |
63
+ |------|------|----------|-------------|
64
+ | `type` | `string` | āœ“ | A string identifying the request type |
65
+ | `data` | `any` | | Optional payload to include with the request |
66
+ | `timeout` | `any` | | How long to wait for a response (default 10 000 ms) |
67
+
68
+ **Returns:** `Promise<R>`
69
+
70
+ ```ts
71
+ const result = await ws.ask('getUser', { id: 42 })
72
+ ```
73
+
74
+
75
+
56
76
  ### disconnect
57
77
 
58
78
  Gracefully close the WebSocket connection. Suppresses auto-reconnect and updates connection state to disconnected.
@@ -146,16 +166,9 @@ Emitted when a request fails
146
166
 
147
167
  ## Examples
148
168
 
149
- **clients.websocket**
169
+ **ask**
150
170
 
151
171
  ```ts
152
- const ws = container.client('websocket', {
153
- baseURL: 'ws://localhost:8080',
154
- reconnect: true,
155
- maxReconnectAttempts: 5
156
- })
157
- ws.on('message', (data) => console.log('Received:', data))
158
- await ws.connect()
159
- await ws.send({ type: 'hello' })
172
+ const result = await ws.ask('getUser', { id: 42 })
160
173
  ```
161
174