@soederpop/luca 0.0.31 → 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 +19 -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 +2499 -63
  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
@@ -1,6 +1,6 @@
1
1
  # Python (features.python)
2
2
 
3
- The Python VM feature provides Python virtual machine capabilities for executing Python code. This feature automatically detects Python environments (uv, conda, venv, system) and provides methods to install dependencies and execute Python scripts. It can manage project-specific Python environments and maintain context between executions.
3
+ The Python VM feature provides Python virtual machine capabilities for executing Python code. This feature automatically detects Python environments (uv, conda, venv, system) and provides methods to install dependencies and execute Python scripts. It can manage project-specific Python environments and maintain context between executions. Supports two modes: - **Stateless** (default): `execute()` and `executeFile()` spawn a fresh process per call - **Persistent session**: `startSession()` spawns a long-lived bridge process that maintains state across `run()` calls, enabling real codebase interaction with imports and session variables
4
4
 
5
5
  ## Usage
6
6
 
@@ -136,6 +136,154 @@ Gets information about the current Python environment.
136
136
 
137
137
 
138
138
 
139
+ ### startSession
140
+
141
+ Starts a persistent Python session by spawning the bridge process. The bridge sets up sys.path for the project directory, then enters a JSON-line REPL loop. State (variables, imports) persists across run() calls until stopSession() or resetSession() is called.
142
+
143
+ **Returns:** `Promise<void>`
144
+
145
+ ```ts
146
+ const python = container.feature('python', { dir: '/path/to/project' })
147
+ await python.enable()
148
+ await python.startSession()
149
+ await python.run('x = 42')
150
+ const result = await python.run('print(x)')
151
+ console.log(result.stdout) // '42\n'
152
+ await python.stopSession()
153
+ ```
154
+
155
+
156
+
157
+ ### stopSession
158
+
159
+ Stops the persistent Python session and cleans up the bridge process.
160
+
161
+ **Returns:** `Promise<void>`
162
+
163
+ ```ts
164
+ await python.stopSession()
165
+ ```
166
+
167
+
168
+
169
+ ### run
170
+
171
+ Executes Python code in the persistent session. Variables and imports survive across calls. This is the session equivalent of execute().
172
+
173
+ **Parameters:**
174
+
175
+ | Name | Type | Required | Description |
176
+ |------|------|----------|-------------|
177
+ | `code` | `string` | ✓ | Python code to execute |
178
+ | `variables` | `Record<string, any>` | | Variables to inject into the namespace before execution |
179
+
180
+ **Returns:** `Promise<RunResult>`
181
+
182
+ ```ts
183
+ await python.startSession()
184
+
185
+ // State persists across calls
186
+ await python.run('x = 42')
187
+ const result = await python.run('print(x * 2)')
188
+ console.log(result.stdout) // '84\n'
189
+
190
+ // Inject variables from JS
191
+ const result2 = await python.run('print(f"Hello {name}!")', { name: 'World' })
192
+ console.log(result2.stdout) // 'Hello World!\n'
193
+ ```
194
+
195
+
196
+
197
+ ### eval
198
+
199
+ Evaluates a Python expression in the persistent session and returns its value.
200
+
201
+ **Parameters:**
202
+
203
+ | Name | Type | Required | Description |
204
+ |------|------|----------|-------------|
205
+ | `expression` | `string` | ✓ | Python expression to evaluate |
206
+
207
+ **Returns:** `Promise<any>`
208
+
209
+ ```ts
210
+ await python.run('x = 42')
211
+ const result = await python.eval('x * 2')
212
+ console.log(result) // 84
213
+ ```
214
+
215
+
216
+
217
+ ### importModule
218
+
219
+ Imports a Python module into the persistent session namespace.
220
+
221
+ **Parameters:**
222
+
223
+ | Name | Type | Required | Description |
224
+ |------|------|----------|-------------|
225
+ | `moduleName` | `string` | ✓ | Dotted module path (e.g. 'myapp.models') |
226
+ | `alias` | `string` | | Optional alias for the import (defaults to the last segment) |
227
+
228
+ **Returns:** `Promise<void>`
229
+
230
+ ```ts
231
+ await python.importModule('json')
232
+ await python.importModule('myapp.models', 'models')
233
+ const result = await python.eval('models.User')
234
+ ```
235
+
236
+
237
+
238
+ ### call
239
+
240
+ Calls a function by dotted path in the persistent session namespace.
241
+
242
+ **Parameters:**
243
+
244
+ | Name | Type | Required | Description |
245
+ |------|------|----------|-------------|
246
+ | `funcPath` | `string` | ✓ | Dotted path to the function (e.g. 'json.dumps' or 'my_func') |
247
+ | `args` | `any[]` | | Positional arguments |
248
+ | `kwargs` | `Record<string, any>` | | Keyword arguments |
249
+
250
+ **Returns:** `Promise<any>`
251
+
252
+ ```ts
253
+ await python.importModule('json')
254
+ const result = await python.call('json.dumps', [{ a: 1 }], { indent: 2 })
255
+ ```
256
+
257
+
258
+
259
+ ### getLocals
260
+
261
+ Returns all non-dunder variables from the persistent session namespace.
262
+
263
+ **Returns:** `Promise<Record<string, any>>`
264
+
265
+ ```ts
266
+ await python.run('x = 42\ny = "hello"')
267
+ const locals = await python.getLocals()
268
+ console.log(locals) // { x: 42, y: 'hello' }
269
+ ```
270
+
271
+
272
+
273
+ ### resetSession
274
+
275
+ Clears all variables and imports from the persistent session namespace. The session remains active — you can continue calling run() after reset.
276
+
277
+ **Returns:** `Promise<void>`
278
+
279
+ ```ts
280
+ await python.run('x = 42')
281
+ await python.resetSession()
282
+ // x is now undefined
283
+ ```
284
+
285
+
286
+
139
287
  ## Getters
140
288
 
141
289
  | Property | Type | Description |
@@ -194,6 +342,24 @@ Event emitted by Python
194
342
 
195
343
 
196
344
 
345
+ ### sessionError
346
+
347
+ Event emitted by Python
348
+
349
+
350
+
351
+ ### sessionStarted
352
+
353
+ Event emitted by Python
354
+
355
+
356
+
357
+ ### sessionStopped
358
+
359
+ Event emitted by Python
360
+
361
+
362
+
197
363
  ## State (Zod v4 schema)
198
364
 
199
365
  | Property | Type | Description |
@@ -204,25 +370,27 @@ Event emitted by Python
204
370
  | `environmentType` | `any` | Detected Python environment type (uv, conda, venv, or system) |
205
371
  | `isReady` | `boolean` | Whether the Python environment is ready for execution |
206
372
  | `lastExecutedScript` | `any` | Path to the last executed Python script |
373
+ | `sessionActive` | `boolean` | Whether a persistent Python session is currently active |
374
+ | `sessionId` | `any` | Unique ID of the current persistent session |
207
375
 
208
376
  ## Examples
209
377
 
210
378
  **features.python**
211
379
 
212
380
  ```ts
213
- const python = container.feature('python', {
381
+ const python = container.feature('python', {
214
382
  dir: "/path/to/python/project",
215
- contextScript: "/path/to/setup-context.py"
216
383
  })
217
384
 
218
- // Auto-install dependencies
219
- await python.installDependencies()
220
-
221
- // Execute Python code
385
+ // Stateless execution
222
386
  const result = await python.execute('print("Hello from Python!")')
223
387
 
224
- // Execute with custom variables
225
- const result2 = await python.execute('print(f"Hello {name}!")', { name: 'World' })
388
+ // Persistent session
389
+ await python.startSession()
390
+ await python.run('import myapp.models')
391
+ await python.run('users = myapp.models.User.objects.all()')
392
+ const result = await python.run('print(len(users))')
393
+ await python.stopSession()
226
394
  ```
227
395
 
228
396
 
@@ -276,3 +444,91 @@ const result = await python.executeFile('/path/to/script.py')
276
444
  console.log(result.stdout)
277
445
  ```
278
446
 
447
+
448
+
449
+ **startSession**
450
+
451
+ ```ts
452
+ const python = container.feature('python', { dir: '/path/to/project' })
453
+ await python.enable()
454
+ await python.startSession()
455
+ await python.run('x = 42')
456
+ const result = await python.run('print(x)')
457
+ console.log(result.stdout) // '42\n'
458
+ await python.stopSession()
459
+ ```
460
+
461
+
462
+
463
+ **stopSession**
464
+
465
+ ```ts
466
+ await python.stopSession()
467
+ ```
468
+
469
+
470
+
471
+ **run**
472
+
473
+ ```ts
474
+ await python.startSession()
475
+
476
+ // State persists across calls
477
+ await python.run('x = 42')
478
+ const result = await python.run('print(x * 2)')
479
+ console.log(result.stdout) // '84\n'
480
+
481
+ // Inject variables from JS
482
+ const result2 = await python.run('print(f"Hello {name}!")', { name: 'World' })
483
+ console.log(result2.stdout) // 'Hello World!\n'
484
+ ```
485
+
486
+
487
+
488
+ **eval**
489
+
490
+ ```ts
491
+ await python.run('x = 42')
492
+ const result = await python.eval('x * 2')
493
+ console.log(result) // 84
494
+ ```
495
+
496
+
497
+
498
+ **importModule**
499
+
500
+ ```ts
501
+ await python.importModule('json')
502
+ await python.importModule('myapp.models', 'models')
503
+ const result = await python.eval('models.User')
504
+ ```
505
+
506
+
507
+
508
+ **call**
509
+
510
+ ```ts
511
+ await python.importModule('json')
512
+ const result = await python.call('json.dumps', [{ a: 1 }], { indent: 2 })
513
+ ```
514
+
515
+
516
+
517
+ **getLocals**
518
+
519
+ ```ts
520
+ await python.run('x = 42\ny = "hello"')
521
+ const locals = await python.getLocals()
522
+ console.log(locals) // { x: 42, y: 'hello' }
523
+ ```
524
+
525
+
526
+
527
+ **resetSession**
528
+
529
+ ```ts
530
+ await python.run('x = 42')
531
+ await python.resetSession()
532
+ // x is now undefined
533
+ ```
534
+
@@ -0,0 +1,380 @@
1
+ # RedisFeature (features.redis)
2
+
3
+ Redis feature for shared state and pub/sub communication between container instances. Wraps ioredis with a focused API for the primitives that matter most: key/value state, pub/sub messaging, and cross-instance coordination. Uses a dedicated subscriber connection for pub/sub (ioredis requirement), created lazily on first subscribe call.
4
+
5
+ ## Usage
6
+
7
+ ```ts
8
+ container.feature('redis', {
9
+ // Redis connection URL, e.g. redis://localhost:6379. Defaults to redis://localhost:6379
10
+ url,
11
+ // Key prefix applied to all get/set/del operations for namespace isolation
12
+ prefix,
13
+ // If true, connection is deferred until first command
14
+ lazyConnect,
15
+ })
16
+ ```
17
+
18
+ ## Options (Zod v4 schema)
19
+
20
+ | Property | Type | Description |
21
+ |----------|------|-------------|
22
+ | `url` | `string` | Redis connection URL, e.g. redis://localhost:6379. Defaults to redis://localhost:6379 |
23
+ | `prefix` | `string` | Key prefix applied to all get/set/del operations for namespace isolation |
24
+ | `lazyConnect` | `boolean` | If true, connection is deferred until first command |
25
+
26
+ ## Methods
27
+
28
+ ### set
29
+
30
+ Set a key to a string value with optional TTL.
31
+
32
+ **Parameters:**
33
+
34
+ | Name | Type | Required | Description |
35
+ |------|------|----------|-------------|
36
+ | `key` | `string` | ✓ | The key name |
37
+ | `value` | `string` | ✓ | The string value to store |
38
+ | `ttl` | `number` | | Optional time-to-live in seconds |
39
+
40
+ **Returns:** `Promise<void>`
41
+
42
+
43
+
44
+ ### get
45
+
46
+ Get a key's value. Returns null if the key doesn't exist.
47
+
48
+ **Parameters:**
49
+
50
+ | Name | Type | Required | Description |
51
+ |------|------|----------|-------------|
52
+ | `key` | `string` | ✓ | The key name |
53
+
54
+ **Returns:** `Promise<string | null>`
55
+
56
+
57
+
58
+ ### del
59
+
60
+ Delete one or more keys.
61
+
62
+ **Parameters:**
63
+
64
+ | Name | Type | Required | Description |
65
+ |------|------|----------|-------------|
66
+ | `keys` | `string[]` | ✓ | One or more key names to delete |
67
+
68
+ **Returns:** `Promise<number>`
69
+
70
+
71
+
72
+ ### exists
73
+
74
+ Check if a key exists.
75
+
76
+ **Parameters:**
77
+
78
+ | Name | Type | Required | Description |
79
+ |------|------|----------|-------------|
80
+ | `key` | `string` | ✓ | The key name |
81
+
82
+ **Returns:** `Promise<boolean>`
83
+
84
+
85
+
86
+ ### expire
87
+
88
+ Set a key's TTL in seconds.
89
+
90
+ **Parameters:**
91
+
92
+ | Name | Type | Required | Description |
93
+ |------|------|----------|-------------|
94
+ | `key` | `string` | ✓ | The key name |
95
+ | `seconds` | `number` | ✓ | TTL in seconds |
96
+
97
+ **Returns:** `Promise<boolean>`
98
+
99
+
100
+
101
+ ### keys
102
+
103
+ Find keys matching a glob pattern (respects prefix).
104
+
105
+ **Parameters:**
106
+
107
+ | Name | Type | Required | Description |
108
+ |------|------|----------|-------------|
109
+ | `pattern` | `string` | | Glob pattern, e.g. "worker:*" |
110
+
111
+ **Returns:** `Promise<string[]>`
112
+
113
+
114
+
115
+ ### setJSON
116
+
117
+ Store a value as JSON.
118
+
119
+ **Parameters:**
120
+
121
+ | Name | Type | Required | Description |
122
+ |------|------|----------|-------------|
123
+ | `key` | `string` | ✓ | The key name |
124
+ | `value` | `unknown` | ✓ | Any JSON-serializable value |
125
+ | `ttl` | `number` | | Optional TTL in seconds |
126
+
127
+ **Returns:** `Promise<void>`
128
+
129
+
130
+
131
+ ### getJSON
132
+
133
+ Retrieve and parse a JSON value.
134
+
135
+ **Parameters:**
136
+
137
+ | Name | Type | Required | Description |
138
+ |------|------|----------|-------------|
139
+ | `key` | `string` | ✓ | The key name |
140
+
141
+ **Returns:** `Promise<T | null>`
142
+
143
+
144
+
145
+ ### hset
146
+
147
+ Set fields on a hash.
148
+
149
+ **Parameters:**
150
+
151
+ | Name | Type | Required | Description |
152
+ |------|------|----------|-------------|
153
+ | `key` | `string` | ✓ | The hash key |
154
+ | `fields` | `Record<string, string>` | ✓ | Object of field/value pairs |
155
+
156
+ **Returns:** `Promise<void>`
157
+
158
+
159
+
160
+ ### hgetall
161
+
162
+ Get all fields from a hash.
163
+
164
+ **Parameters:**
165
+
166
+ | Name | Type | Required | Description |
167
+ |------|------|----------|-------------|
168
+ | `key` | `string` | ✓ | The hash key |
169
+
170
+ **Returns:** `Promise<Record<string, string>>`
171
+
172
+
173
+
174
+ ### hget
175
+
176
+ Get a single field from a hash.
177
+
178
+ **Parameters:**
179
+
180
+ | Name | Type | Required | Description |
181
+ |------|------|----------|-------------|
182
+ | `key` | `string` | ✓ | The hash key |
183
+ | `field` | `string` | ✓ | The field name |
184
+
185
+ **Returns:** `Promise<string | null>`
186
+
187
+
188
+
189
+ ### subscribe
190
+
191
+ Subscribe to one or more channels. Optionally pass a handler that fires only for these channels. The feature also emits a `message` event for all messages.
192
+
193
+ **Parameters:**
194
+
195
+ | Name | Type | Required | Description |
196
+ |------|------|----------|-------------|
197
+ | `channels` | `string | string[]` | ✓ | Channel name(s) to subscribe to |
198
+ | `handler` | `MessageHandler` | | Optional per-channel message handler |
199
+
200
+ **Returns:** `Promise<void>`
201
+
202
+ ```ts
203
+ await redis.subscribe('tasks', (channel, msg) => {
204
+ console.log(`Got ${msg} on ${channel}`)
205
+ })
206
+ ```
207
+
208
+
209
+
210
+ ### unsubscribe
211
+
212
+ Unsubscribe from one or more channels.
213
+
214
+ **Parameters:**
215
+
216
+ | Name | Type | Required | Description |
217
+ |------|------|----------|-------------|
218
+ | `channels` | `string[]` | ✓ | Channel name(s) to unsubscribe from |
219
+
220
+ **Returns:** `Promise<void>`
221
+
222
+
223
+
224
+ ### publish
225
+
226
+ Publish a message to a channel.
227
+
228
+ **Parameters:**
229
+
230
+ | Name | Type | Required | Description |
231
+ |------|------|----------|-------------|
232
+ | `channel` | `string` | ✓ | The channel to publish to |
233
+ | `message` | `string` | ✓ | The message string (use JSON.stringify for objects) |
234
+
235
+ **Returns:** `Promise<number>`
236
+
237
+
238
+
239
+ ### ensureLocalDocker
240
+
241
+ Spin up a local Redis instance via Docker. Checks if a container with the given name already exists and starts it if stopped, or creates a new one from redis:alpine. Requires the docker feature to be available on the container.
242
+
243
+ **Parameters:**
244
+
245
+ | Name | Type | Required | Description |
246
+ |------|------|----------|-------------|
247
+ | `options` | `{ name?: string; port?: number; image?: string }` | | Container name and host port |
248
+
249
+ **Returns:** `Promise<string>`
250
+
251
+ ```ts
252
+ const redis = container.feature('redis', { url: 'redis://localhost:6379', lazyConnect: true })
253
+ await redis.ensureLocalDocker()
254
+ ```
255
+
256
+
257
+
258
+ ### close
259
+
260
+ Close all redis connections (main client + subscriber).
261
+
262
+ **Returns:** `Promise<this>`
263
+
264
+
265
+
266
+ ## Getters
267
+
268
+ | Property | Type | Description |
269
+ |----------|------|-------------|
270
+ | `client` | `Redis` | The underlying ioredis client for advanced operations. |
271
+ | `subscriber` | `Redis | null` | The dedicated subscriber connection, if pub/sub is active. |
272
+
273
+ ## Events (Zod v4 schema)
274
+
275
+ ### message
276
+
277
+ When a message is received on a subscribed channel
278
+
279
+ **Event Arguments:**
280
+
281
+ | Name | Type | Description |
282
+ |------|------|-------------|
283
+ | `arg0` | `string` | The channel name |
284
+ | `arg1` | `string` | The message payload |
285
+
286
+
287
+
288
+ ### error
289
+
290
+ When a redis operation fails
291
+
292
+ **Event Arguments:**
293
+
294
+ | Name | Type | Description |
295
+ |------|------|-------------|
296
+ | `arg0` | `string` | The error message |
297
+
298
+
299
+
300
+ ### subscribed
301
+
302
+ When successfully subscribed to a channel
303
+
304
+ **Event Arguments:**
305
+
306
+ | Name | Type | Description |
307
+ |------|------|-------------|
308
+ | `arg0` | `string` | The channel name |
309
+
310
+
311
+
312
+ ### unsubscribed
313
+
314
+ When unsubscribed from a channel
315
+
316
+ **Event Arguments:**
317
+
318
+ | Name | Type | Description |
319
+ |------|------|-------------|
320
+ | `arg0` | `string` | The channel name |
321
+
322
+
323
+
324
+ ### closed
325
+
326
+ When the redis connection is closed
327
+
328
+
329
+
330
+ ## State (Zod v4 schema)
331
+
332
+ | Property | Type | Description |
333
+ |----------|------|-------------|
334
+ | `enabled` | `boolean` | Whether this feature is currently enabled |
335
+ | `connected` | `boolean` | Whether the redis connection is currently open |
336
+ | `url` | `string` | Connection URL used for this redis feature instance |
337
+ | `subscriberConnected` | `boolean` | Whether the dedicated subscriber connection is open |
338
+ | `subscribedChannels` | `array` | List of channels currently subscribed to |
339
+ | `lastError` | `string` | Most recent redis error message, if any |
340
+
341
+ ## Examples
342
+
343
+ **features.redis**
344
+
345
+ ```ts
346
+ const redis = container.feature('redis', { url: 'redis://localhost:6379' })
347
+
348
+ // Shared state
349
+ await redis.set('worker:status', 'active')
350
+ const status = await redis.get('worker:status')
351
+
352
+ // Pub/sub between instances
353
+ redis.on('message', (channel, msg) => console.log(`${channel}: ${msg}`))
354
+ await redis.subscribe('tasks')
355
+ await redis.publish('tasks', JSON.stringify({ type: 'ping' }))
356
+
357
+ // JSON helpers
358
+ await redis.setJSON('config', { workers: 4, debug: true })
359
+ const config = await redis.getJSON<{ workers: number }>('config')
360
+ ```
361
+
362
+
363
+
364
+ **subscribe**
365
+
366
+ ```ts
367
+ await redis.subscribe('tasks', (channel, msg) => {
368
+ console.log(`Got ${msg} on ${channel}`)
369
+ })
370
+ ```
371
+
372
+
373
+
374
+ **ensureLocalDocker**
375
+
376
+ ```ts
377
+ const redis = container.feature('redis', { url: 'redis://localhost:6379', lazyConnect: true })
378
+ await redis.ensureLocalDocker()
379
+ ```
380
+