@soederpop/luca 0.0.32 → 0.0.35

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 (92) hide show
  1. package/README.md +241 -36
  2. package/bun.lock +24 -6
  3. package/commands/build-python-bridge.ts +43 -0
  4. package/docs/README.md +1 -1
  5. package/docs/TABLE-OF-CONTENTS.md +0 -1
  6. package/docs/apis/clients/rest.md +7 -7
  7. package/docs/apis/clients/websocket.md +23 -10
  8. package/docs/apis/features/agi/assistant.md +155 -8
  9. package/docs/apis/features/agi/assistants-manager.md +90 -22
  10. package/docs/apis/features/agi/auto-assistant.md +377 -0
  11. package/docs/apis/features/agi/browser-use.md +802 -0
  12. package/docs/apis/features/agi/claude-code.md +6 -1
  13. package/docs/apis/features/agi/conversation-history.md +7 -6
  14. package/docs/apis/features/agi/conversation.md +111 -38
  15. package/docs/apis/features/agi/docs-reader.md +35 -57
  16. package/docs/apis/features/agi/file-tools.md +163 -0
  17. package/docs/apis/features/agi/openapi.md +2 -2
  18. package/docs/apis/features/agi/skills-library.md +227 -0
  19. package/docs/apis/features/node/content-db.md +125 -4
  20. package/docs/apis/features/node/disk-cache.md +11 -11
  21. package/docs/apis/features/node/downloader.md +1 -1
  22. package/docs/apis/features/node/file-manager.md +15 -15
  23. package/docs/apis/features/node/fs.md +78 -21
  24. package/docs/apis/features/node/git.md +50 -10
  25. package/docs/apis/features/node/google-calendar.md +3 -0
  26. package/docs/apis/features/node/google-docs.md +10 -1
  27. package/docs/apis/features/node/google-drive.md +3 -0
  28. package/docs/apis/features/node/google-mail.md +214 -0
  29. package/docs/apis/features/node/google-sheets.md +3 -0
  30. package/docs/apis/features/node/ink.md +10 -10
  31. package/docs/apis/features/node/ipc-socket.md +83 -93
  32. package/docs/apis/features/node/networking.md +5 -5
  33. package/docs/apis/features/node/os.md +7 -7
  34. package/docs/apis/features/node/package-finder.md +14 -14
  35. package/docs/apis/features/node/proc.md +2 -1
  36. package/docs/apis/features/node/process-manager.md +70 -3
  37. package/docs/apis/features/node/python.md +265 -9
  38. package/docs/apis/features/node/redis.md +380 -0
  39. package/docs/apis/features/node/ui.md +13 -13
  40. package/docs/apis/servers/express.md +35 -7
  41. package/docs/apis/servers/mcp.md +3 -3
  42. package/docs/apis/servers/websocket.md +51 -8
  43. package/docs/bootstrap/CLAUDE.md +1 -1
  44. package/docs/bootstrap/SKILL.md +93 -7
  45. package/docs/examples/feature-as-tool-provider.md +143 -0
  46. package/docs/examples/python.md +42 -1
  47. package/docs/introspection.md +15 -5
  48. package/docs/tutorials/00-bootstrap.md +3 -3
  49. package/docs/tutorials/02-container.md +2 -2
  50. package/docs/tutorials/10-creating-features.md +5 -0
  51. package/docs/tutorials/13-introspection.md +12 -2
  52. package/docs/tutorials/19-python-sessions.md +401 -0
  53. package/package.json +8 -5
  54. package/scripts/examples/using-assistant-with-mcp.ts +2 -7
  55. package/scripts/test-linux-binary.sh +80 -0
  56. package/src/agi/container.server.ts +8 -0
  57. package/src/agi/features/assistant.ts +18 -0
  58. package/src/agi/features/autonomous-assistant.ts +435 -0
  59. package/src/agi/features/conversation.ts +58 -6
  60. package/src/agi/features/file-tools.ts +286 -0
  61. package/src/agi/features/luca-coder.ts +643 -0
  62. package/src/bootstrap/generated.ts +705 -107
  63. package/src/cli/build-info.ts +2 -2
  64. package/src/cli/cli.ts +22 -13
  65. package/src/commands/bootstrap.ts +49 -6
  66. package/src/commands/code.ts +369 -0
  67. package/src/commands/describe.ts +7 -2
  68. package/src/commands/index.ts +1 -0
  69. package/src/commands/sandbox-mcp.ts +7 -7
  70. package/src/commands/save-api-docs.ts +1 -1
  71. package/src/container-describer.ts +4 -4
  72. package/src/container.ts +10 -19
  73. package/src/helper.ts +24 -33
  74. package/src/introspection/generated.agi.ts +3026 -849
  75. package/src/introspection/generated.node.ts +1690 -1012
  76. package/src/introspection/generated.web.ts +15 -57
  77. package/src/node/container.ts +5 -5
  78. package/src/node/features/figlet-fonts.ts +597 -0
  79. package/src/node/features/fs.ts +3 -9
  80. package/src/node/features/helpers.ts +20 -0
  81. package/src/node/features/python.ts +429 -16
  82. package/src/node/features/redis.ts +446 -0
  83. package/src/node/features/ui.ts +4 -11
  84. package/src/python/bridge.py +220 -0
  85. package/src/python/generated.ts +227 -0
  86. package/src/scaffolds/generated.ts +1 -1
  87. package/test/python-session.test.ts +105 -0
  88. package/assistants/lucaExpert/CORE.md +0 -37
  89. package/assistants/lucaExpert/hooks.ts +0 -9
  90. package/assistants/lucaExpert/tools.ts +0 -177
  91. package/docs/examples/port-exposer.md +0 -89
  92. package/src/node/features/port-exposer.ts +0 -351
@@ -1,7 +1,7 @@
1
1
  import { setBuildTimeData, setContainerBuildTimeData } from './index.js';
2
2
 
3
3
  // Auto-generated introspection registry data
4
- // Generated at: 2026-03-24T09:09:09.885Z
4
+ // Generated at: 2026-03-27T03:29:26.519Z
5
5
 
6
6
  setBuildTimeData('features.googleDocs', {
7
7
  "id": "features.googleDocs",
@@ -5927,6 +5927,312 @@ setBuildTimeData('features.googleCalendar', {
5927
5927
  }
5928
5928
  });
5929
5929
 
5930
+ setBuildTimeData('features.redis', {
5931
+ "id": "features.redis",
5932
+ "description": "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.",
5933
+ "shortcut": "features.redis",
5934
+ "className": "RedisFeature",
5935
+ "methods": {
5936
+ "set": {
5937
+ "description": "Set a key to a string value with optional TTL.",
5938
+ "parameters": {
5939
+ "key": {
5940
+ "type": "string",
5941
+ "description": "The key name"
5942
+ },
5943
+ "value": {
5944
+ "type": "string",
5945
+ "description": "The string value to store"
5946
+ },
5947
+ "ttl": {
5948
+ "type": "number",
5949
+ "description": "Optional time-to-live in seconds"
5950
+ }
5951
+ },
5952
+ "required": [
5953
+ "key",
5954
+ "value"
5955
+ ],
5956
+ "returns": "Promise<void>"
5957
+ },
5958
+ "get": {
5959
+ "description": "Get a key's value. Returns null if the key doesn't exist.",
5960
+ "parameters": {
5961
+ "key": {
5962
+ "type": "string",
5963
+ "description": "The key name"
5964
+ }
5965
+ },
5966
+ "required": [
5967
+ "key"
5968
+ ],
5969
+ "returns": "Promise<string | null>"
5970
+ },
5971
+ "del": {
5972
+ "description": "Delete one or more keys.",
5973
+ "parameters": {
5974
+ "keys": {
5975
+ "type": "string[]",
5976
+ "description": "One or more key names to delete"
5977
+ }
5978
+ },
5979
+ "required": [
5980
+ "keys"
5981
+ ],
5982
+ "returns": "Promise<number>"
5983
+ },
5984
+ "exists": {
5985
+ "description": "Check if a key exists.",
5986
+ "parameters": {
5987
+ "key": {
5988
+ "type": "string",
5989
+ "description": "The key name"
5990
+ }
5991
+ },
5992
+ "required": [
5993
+ "key"
5994
+ ],
5995
+ "returns": "Promise<boolean>"
5996
+ },
5997
+ "expire": {
5998
+ "description": "Set a key's TTL in seconds.",
5999
+ "parameters": {
6000
+ "key": {
6001
+ "type": "string",
6002
+ "description": "The key name"
6003
+ },
6004
+ "seconds": {
6005
+ "type": "number",
6006
+ "description": "TTL in seconds"
6007
+ }
6008
+ },
6009
+ "required": [
6010
+ "key",
6011
+ "seconds"
6012
+ ],
6013
+ "returns": "Promise<boolean>"
6014
+ },
6015
+ "keys": {
6016
+ "description": "Find keys matching a glob pattern (respects prefix).",
6017
+ "parameters": {
6018
+ "pattern": {
6019
+ "type": "string",
6020
+ "description": "Glob pattern, e.g. \"worker:*\""
6021
+ }
6022
+ },
6023
+ "required": [],
6024
+ "returns": "Promise<string[]>"
6025
+ },
6026
+ "setJSON": {
6027
+ "description": "Store a value as JSON.",
6028
+ "parameters": {
6029
+ "key": {
6030
+ "type": "string",
6031
+ "description": "The key name"
6032
+ },
6033
+ "value": {
6034
+ "type": "unknown",
6035
+ "description": "Any JSON-serializable value"
6036
+ },
6037
+ "ttl": {
6038
+ "type": "number",
6039
+ "description": "Optional TTL in seconds"
6040
+ }
6041
+ },
6042
+ "required": [
6043
+ "key",
6044
+ "value"
6045
+ ],
6046
+ "returns": "Promise<void>"
6047
+ },
6048
+ "getJSON": {
6049
+ "description": "Retrieve and parse a JSON value.",
6050
+ "parameters": {
6051
+ "key": {
6052
+ "type": "string",
6053
+ "description": "The key name"
6054
+ }
6055
+ },
6056
+ "required": [
6057
+ "key"
6058
+ ],
6059
+ "returns": "Promise<T | null>"
6060
+ },
6061
+ "hset": {
6062
+ "description": "Set fields on a hash.",
6063
+ "parameters": {
6064
+ "key": {
6065
+ "type": "string",
6066
+ "description": "The hash key"
6067
+ },
6068
+ "fields": {
6069
+ "type": "Record<string, string>",
6070
+ "description": "Object of field/value pairs"
6071
+ }
6072
+ },
6073
+ "required": [
6074
+ "key",
6075
+ "fields"
6076
+ ],
6077
+ "returns": "Promise<void>"
6078
+ },
6079
+ "hgetall": {
6080
+ "description": "Get all fields from a hash.",
6081
+ "parameters": {
6082
+ "key": {
6083
+ "type": "string",
6084
+ "description": "The hash key"
6085
+ }
6086
+ },
6087
+ "required": [
6088
+ "key"
6089
+ ],
6090
+ "returns": "Promise<Record<string, string>>"
6091
+ },
6092
+ "hget": {
6093
+ "description": "Get a single field from a hash.",
6094
+ "parameters": {
6095
+ "key": {
6096
+ "type": "string",
6097
+ "description": "The hash key"
6098
+ },
6099
+ "field": {
6100
+ "type": "string",
6101
+ "description": "The field name"
6102
+ }
6103
+ },
6104
+ "required": [
6105
+ "key",
6106
+ "field"
6107
+ ],
6108
+ "returns": "Promise<string | null>"
6109
+ },
6110
+ "subscribe": {
6111
+ "description": "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.",
6112
+ "parameters": {
6113
+ "channels": {
6114
+ "type": "string | string[]",
6115
+ "description": "Channel name(s) to subscribe to"
6116
+ },
6117
+ "handler": {
6118
+ "type": "MessageHandler",
6119
+ "description": "Optional per-channel message handler"
6120
+ }
6121
+ },
6122
+ "required": [
6123
+ "channels"
6124
+ ],
6125
+ "returns": "Promise<void>",
6126
+ "examples": [
6127
+ {
6128
+ "language": "ts",
6129
+ "code": "await redis.subscribe('tasks', (channel, msg) => {\n console.log(`Got ${msg} on ${channel}`)\n})"
6130
+ }
6131
+ ]
6132
+ },
6133
+ "unsubscribe": {
6134
+ "description": "Unsubscribe from one or more channels.",
6135
+ "parameters": {
6136
+ "channels": {
6137
+ "type": "string[]",
6138
+ "description": "Channel name(s) to unsubscribe from"
6139
+ }
6140
+ },
6141
+ "required": [
6142
+ "channels"
6143
+ ],
6144
+ "returns": "Promise<void>"
6145
+ },
6146
+ "publish": {
6147
+ "description": "Publish a message to a channel.",
6148
+ "parameters": {
6149
+ "channel": {
6150
+ "type": "string",
6151
+ "description": "The channel to publish to"
6152
+ },
6153
+ "message": {
6154
+ "type": "string",
6155
+ "description": "The message string (use JSON.stringify for objects)"
6156
+ }
6157
+ },
6158
+ "required": [
6159
+ "channel",
6160
+ "message"
6161
+ ],
6162
+ "returns": "Promise<number>"
6163
+ },
6164
+ "ensureLocalDocker": {
6165
+ "description": "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.",
6166
+ "parameters": {
6167
+ "options": {
6168
+ "type": "{ name?: string; port?: number; image?: string }",
6169
+ "description": "Container name and host port"
6170
+ }
6171
+ },
6172
+ "required": [],
6173
+ "returns": "Promise<string>",
6174
+ "examples": [
6175
+ {
6176
+ "language": "ts",
6177
+ "code": "const redis = container.feature('redis', { url: 'redis://localhost:6379', lazyConnect: true })\nawait redis.ensureLocalDocker()"
6178
+ }
6179
+ ]
6180
+ },
6181
+ "close": {
6182
+ "description": "Close all redis connections (main client + subscriber).",
6183
+ "parameters": {},
6184
+ "required": [],
6185
+ "returns": "Promise<this>"
6186
+ }
6187
+ },
6188
+ "getters": {
6189
+ "client": {
6190
+ "description": "The underlying ioredis client for advanced operations.",
6191
+ "returns": "Redis"
6192
+ },
6193
+ "subscriber": {
6194
+ "description": "The dedicated subscriber connection, if pub/sub is active.",
6195
+ "returns": "Redis | null"
6196
+ }
6197
+ },
6198
+ "events": {
6199
+ "message": {
6200
+ "name": "message",
6201
+ "description": "Event emitted by RedisFeature",
6202
+ "arguments": {}
6203
+ },
6204
+ "error": {
6205
+ "name": "error",
6206
+ "description": "Event emitted by RedisFeature",
6207
+ "arguments": {}
6208
+ },
6209
+ "subscribed": {
6210
+ "name": "subscribed",
6211
+ "description": "Event emitted by RedisFeature",
6212
+ "arguments": {}
6213
+ },
6214
+ "unsubscribed": {
6215
+ "name": "unsubscribed",
6216
+ "description": "Event emitted by RedisFeature",
6217
+ "arguments": {}
6218
+ },
6219
+ "closed": {
6220
+ "name": "closed",
6221
+ "description": "Event emitted by RedisFeature",
6222
+ "arguments": {}
6223
+ }
6224
+ },
6225
+ "state": {},
6226
+ "options": {},
6227
+ "envVars": [],
6228
+ "examples": [
6229
+ {
6230
+ "language": "ts",
6231
+ "code": "const redis = container.feature('redis', { url: 'redis://localhost:6379' })\n\n// Shared state\nawait redis.set('worker:status', 'active')\nconst status = await redis.get('worker:status')\n\n// Pub/sub between instances\nredis.on('message', (channel, msg) => console.log(`${channel}: ${msg}`))\nawait redis.subscribe('tasks')\nawait redis.publish('tasks', JSON.stringify({ type: 'ping' }))\n\n// JSON helpers\nawait redis.setJSON('config', { workers: 4, debug: true })\nconst config = await redis.getJSON<{ workers: number }>('config')"
6232
+ }
6233
+ ]
6234
+ });
6235
+
5930
6236
  setBuildTimeData('features.fs', {
5931
6237
  "id": "features.fs",
5932
6238
  "description": "The FS feature provides methods for interacting with the file system, relative to the container's cwd.",
@@ -7566,7 +7872,7 @@ setBuildTimeData('features.postgres', {
7566
7872
 
7567
7873
  setBuildTimeData('features.python', {
7568
7874
  "id": "features.python",
7569
- "description": "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.",
7875
+ "description": "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",
7570
7876
  "shortcut": "features.python",
7571
7877
  "className": "Python",
7572
7878
  "methods": {
@@ -7666,37 +7972,177 @@ setBuildTimeData('features.python', {
7666
7972
  "parameters": {},
7667
7973
  "required": [],
7668
7974
  "returns": "Promise<{ version: string; path: string; packages: string[] }>"
7669
- }
7670
- },
7671
- "getters": {
7672
- "projectDir": {
7673
- "description": "Returns the root directory of the Python project.",
7674
- "returns": "any"
7675
- },
7676
- "pythonPath": {
7677
- "description": "Returns the path to the Python executable for this environment.",
7678
- "returns": "any"
7679
7975
  },
7680
- "environmentType": {
7681
- "description": "Returns the detected environment type: 'uv', 'conda', 'venv', or 'system'.",
7682
- "returns": "any"
7683
- }
7684
- },
7685
- "events": {
7686
- "ready": {
7687
- "name": "ready",
7688
- "description": "Event emitted by Python",
7689
- "arguments": {}
7976
+ "startSession": {
7977
+ "description": "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.",
7978
+ "parameters": {},
7979
+ "required": [],
7980
+ "returns": "Promise<void>",
7981
+ "examples": [
7982
+ {
7983
+ "language": "ts",
7984
+ "code": "const python = container.feature('python', { dir: '/path/to/project' })\nawait python.enable()\nawait python.startSession()\nawait python.run('x = 42')\nconst result = await python.run('print(x)')\nconsole.log(result.stdout) // '42\\n'\nawait python.stopSession()"
7985
+ }
7986
+ ]
7690
7987
  },
7691
- "environmentDetected": {
7692
- "name": "environmentDetected",
7693
- "description": "Event emitted by Python",
7694
- "arguments": {}
7988
+ "stopSession": {
7989
+ "description": "Stops the persistent Python session and cleans up the bridge process.",
7990
+ "parameters": {},
7991
+ "required": [],
7992
+ "returns": "Promise<void>",
7993
+ "examples": [
7994
+ {
7995
+ "language": "ts",
7996
+ "code": "await python.stopSession()"
7997
+ }
7998
+ ]
7695
7999
  },
7696
- "installingDependencies": {
7697
- "name": "installingDependencies",
7698
- "description": "Event emitted by Python",
7699
- "arguments": {}
8000
+ "run": {
8001
+ "description": "Executes Python code in the persistent session. Variables and imports survive across calls. This is the session equivalent of execute().",
8002
+ "parameters": {
8003
+ "code": {
8004
+ "type": "string",
8005
+ "description": "Python code to execute"
8006
+ },
8007
+ "variables": {
8008
+ "type": "Record<string, any>",
8009
+ "description": "Variables to inject into the namespace before execution"
8010
+ }
8011
+ },
8012
+ "required": [
8013
+ "code"
8014
+ ],
8015
+ "returns": "Promise<RunResult>",
8016
+ "examples": [
8017
+ {
8018
+ "language": "ts",
8019
+ "code": "await python.startSession()\n\n// State persists across calls\nawait python.run('x = 42')\nconst result = await python.run('print(x * 2)')\nconsole.log(result.stdout) // '84\\n'\n\n// Inject variables from JS\nconst result2 = await python.run('print(f\"Hello {name}!\")', { name: 'World' })\nconsole.log(result2.stdout) // 'Hello World!\\n'"
8020
+ }
8021
+ ]
8022
+ },
8023
+ "eval": {
8024
+ "description": "Evaluates a Python expression in the persistent session and returns its value.",
8025
+ "parameters": {
8026
+ "expression": {
8027
+ "type": "string",
8028
+ "description": "Python expression to evaluate"
8029
+ }
8030
+ },
8031
+ "required": [
8032
+ "expression"
8033
+ ],
8034
+ "returns": "Promise<any>",
8035
+ "examples": [
8036
+ {
8037
+ "language": "ts",
8038
+ "code": "await python.run('x = 42')\nconst result = await python.eval('x * 2')\nconsole.log(result) // 84"
8039
+ }
8040
+ ]
8041
+ },
8042
+ "importModule": {
8043
+ "description": "Imports a Python module into the persistent session namespace.",
8044
+ "parameters": {
8045
+ "moduleName": {
8046
+ "type": "string",
8047
+ "description": "Dotted module path (e.g. 'myapp.models')"
8048
+ },
8049
+ "alias": {
8050
+ "type": "string",
8051
+ "description": "Optional alias for the import (defaults to the last segment)"
8052
+ }
8053
+ },
8054
+ "required": [
8055
+ "moduleName"
8056
+ ],
8057
+ "returns": "Promise<void>",
8058
+ "examples": [
8059
+ {
8060
+ "language": "ts",
8061
+ "code": "await python.importModule('json')\nawait python.importModule('myapp.models', 'models')\nconst result = await python.eval('models.User')"
8062
+ }
8063
+ ]
8064
+ },
8065
+ "call": {
8066
+ "description": "Calls a function by dotted path in the persistent session namespace.",
8067
+ "parameters": {
8068
+ "funcPath": {
8069
+ "type": "string",
8070
+ "description": "Dotted path to the function (e.g. 'json.dumps' or 'my_func')"
8071
+ },
8072
+ "args": {
8073
+ "type": "any[]",
8074
+ "description": "Positional arguments"
8075
+ },
8076
+ "kwargs": {
8077
+ "type": "Record<string, any>",
8078
+ "description": "Keyword arguments"
8079
+ }
8080
+ },
8081
+ "required": [
8082
+ "funcPath"
8083
+ ],
8084
+ "returns": "Promise<any>",
8085
+ "examples": [
8086
+ {
8087
+ "language": "ts",
8088
+ "code": "await python.importModule('json')\nconst result = await python.call('json.dumps', [{ a: 1 }], { indent: 2 })"
8089
+ }
8090
+ ]
8091
+ },
8092
+ "getLocals": {
8093
+ "description": "Returns all non-dunder variables from the persistent session namespace.",
8094
+ "parameters": {},
8095
+ "required": [],
8096
+ "returns": "Promise<Record<string, any>>",
8097
+ "examples": [
8098
+ {
8099
+ "language": "ts",
8100
+ "code": "await python.run('x = 42\\ny = \"hello\"')\nconst locals = await python.getLocals()\nconsole.log(locals) // { x: 42, y: 'hello' }"
8101
+ }
8102
+ ]
8103
+ },
8104
+ "resetSession": {
8105
+ "description": "Clears all variables and imports from the persistent session namespace. The session remains active — you can continue calling run() after reset.",
8106
+ "parameters": {},
8107
+ "required": [],
8108
+ "returns": "Promise<void>",
8109
+ "examples": [
8110
+ {
8111
+ "language": "ts",
8112
+ "code": "await python.run('x = 42')\nawait python.resetSession()\n// x is now undefined"
8113
+ }
8114
+ ]
8115
+ }
8116
+ },
8117
+ "getters": {
8118
+ "projectDir": {
8119
+ "description": "Returns the root directory of the Python project.",
8120
+ "returns": "any"
8121
+ },
8122
+ "pythonPath": {
8123
+ "description": "Returns the path to the Python executable for this environment.",
8124
+ "returns": "any"
8125
+ },
8126
+ "environmentType": {
8127
+ "description": "Returns the detected environment type: 'uv', 'conda', 'venv', or 'system'.",
8128
+ "returns": "any"
8129
+ }
8130
+ },
8131
+ "events": {
8132
+ "ready": {
8133
+ "name": "ready",
8134
+ "description": "Event emitted by Python",
8135
+ "arguments": {}
8136
+ },
8137
+ "environmentDetected": {
8138
+ "name": "environmentDetected",
8139
+ "description": "Event emitted by Python",
8140
+ "arguments": {}
8141
+ },
8142
+ "installingDependencies": {
8143
+ "name": "installingDependencies",
8144
+ "description": "Event emitted by Python",
8145
+ "arguments": {}
7700
8146
  },
7701
8147
  "dependenciesInstalled": {
7702
8148
  "name": "dependenciesInstalled",
@@ -7722,6 +8168,21 @@ setBuildTimeData('features.python', {
7722
8168
  "name": "fileExecuted",
7723
8169
  "description": "Event emitted by Python",
7724
8170
  "arguments": {}
8171
+ },
8172
+ "sessionError": {
8173
+ "name": "sessionError",
8174
+ "description": "Event emitted by Python",
8175
+ "arguments": {}
8176
+ },
8177
+ "sessionStarted": {
8178
+ "name": "sessionStarted",
8179
+ "description": "Event emitted by Python",
8180
+ "arguments": {}
8181
+ },
8182
+ "sessionStopped": {
8183
+ "name": "sessionStopped",
8184
+ "description": "Event emitted by Python",
8185
+ "arguments": {}
7725
8186
  }
7726
8187
  },
7727
8188
  "state": {},
@@ -7730,9 +8191,38 @@ setBuildTimeData('features.python', {
7730
8191
  "examples": [
7731
8192
  {
7732
8193
  "language": "ts",
7733
- "code": "const python = container.feature('python', { \n dir: \"/path/to/python/project\",\n contextScript: \"/path/to/setup-context.py\"\n})\n\n// Auto-install dependencies\nawait python.installDependencies()\n\n// Execute Python code\nconst result = await python.execute('print(\"Hello from Python!\")')\n\n// Execute with custom variables\nconst result2 = await python.execute('print(f\"Hello {name}!\")', { name: 'World' })"
8194
+ "code": "const python = container.feature('python', {\n dir: \"/path/to/python/project\",\n})\n\n// Stateless execution\nconst result = await python.execute('print(\"Hello from Python!\")')\n\n// Persistent session\nawait python.startSession()\nawait python.run('import myapp.models')\nawait python.run('users = myapp.models.User.objects.all()')\nconst result = await python.run('print(len(users))')\nawait python.stopSession()"
7734
8195
  }
7735
- ]
8196
+ ],
8197
+ "types": {
8198
+ "RunResult": {
8199
+ "description": "Result from a persistent session run() call.",
8200
+ "properties": {
8201
+ "ok": {
8202
+ "type": "boolean",
8203
+ "description": ""
8204
+ },
8205
+ "result": {
8206
+ "type": "any",
8207
+ "description": ""
8208
+ },
8209
+ "stdout": {
8210
+ "type": "string",
8211
+ "description": ""
8212
+ },
8213
+ "error": {
8214
+ "type": "string",
8215
+ "description": "",
8216
+ "optional": true
8217
+ },
8218
+ "traceback": {
8219
+ "type": "string",
8220
+ "description": "",
8221
+ "optional": true
8222
+ }
8223
+ }
8224
+ }
8225
+ }
7736
8226
  });
7737
8227
 
7738
8228
  setBuildTimeData('features.jsonTree', {
@@ -8371,136 +8861,6 @@ setBuildTimeData('features.processManager', {
8371
8861
  }
8372
8862
  });
8373
8863
 
8374
- setBuildTimeData('portExposer', {
8375
- "id": "portExposer",
8376
- "description": "Port Exposer Feature Exposes local HTTP services via ngrok with SSL-enabled public URLs. Perfect for development, testing, and sharing local services securely. Features: - SSL-enabled public URLs for local services - Custom subdomains and domains (with paid plans) - Authentication options (basic auth, OAuth) - Regional endpoint selection - Connection state management",
8377
- "shortcut": "portExposer",
8378
- "className": "PortExposer",
8379
- "methods": {
8380
- "expose": {
8381
- "description": "Expose the local port via ngrok. Creates an ngrok tunnel to the specified local port and returns the SSL-enabled public URL. Emits `exposed` on success or `error` on failure.",
8382
- "parameters": {
8383
- "port": {
8384
- "type": "number",
8385
- "description": "Optional port override; falls back to `options.port`"
8386
- }
8387
- },
8388
- "required": [],
8389
- "returns": "Promise<string>",
8390
- "examples": [
8391
- {
8392
- "language": "ts",
8393
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nconst url = await exposer.expose()\nconsole.log(`Public URL: ${url}`)\n\n// Override port at call time\nconst url2 = await exposer.expose(8080)"
8394
- }
8395
- ]
8396
- },
8397
- "close": {
8398
- "description": "Stop exposing the port and close the ngrok tunnel. Tears down the ngrok listener, resets connection state, and emits `closed`. Safe to call when no tunnel is active (no-op).",
8399
- "parameters": {},
8400
- "required": [],
8401
- "returns": "Promise<void>",
8402
- "examples": [
8403
- {
8404
- "language": "ts",
8405
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\n// ... later\nawait exposer.close()\nconsole.log(exposer.isConnected()) // false"
8406
- }
8407
- ]
8408
- },
8409
- "getPublicUrl": {
8410
- "description": "Get the current public URL if connected. Returns the live URL from the ngrok listener, or `undefined` if no tunnel is active.",
8411
- "parameters": {},
8412
- "required": [],
8413
- "returns": "string | undefined",
8414
- "examples": [
8415
- {
8416
- "language": "ts",
8417
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nconsole.log(exposer.getPublicUrl()) // 'https://abc123.ngrok.io'"
8418
- }
8419
- ]
8420
- },
8421
- "isConnected": {
8422
- "description": "Check if the ngrok tunnel is currently connected.",
8423
- "parameters": {},
8424
- "required": [],
8425
- "returns": "boolean",
8426
- "examples": [
8427
- {
8428
- "language": "ts",
8429
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nconsole.log(exposer.isConnected()) // false\nawait exposer.expose()\nconsole.log(exposer.isConnected()) // true"
8430
- }
8431
- ]
8432
- },
8433
- "getConnectionInfo": {
8434
- "description": "Get a snapshot of the current connection information. Returns an object with the tunnel's connected status, public URL, local port, connection timestamp, and session metadata.",
8435
- "parameters": {},
8436
- "required": [],
8437
- "returns": "void",
8438
- "examples": [
8439
- {
8440
- "language": "ts",
8441
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nconst info = exposer.getConnectionInfo()\nconsole.log(info.publicUrl, info.localPort, info.connectedAt)"
8442
- }
8443
- ]
8444
- },
8445
- "reconnect": {
8446
- "description": "Close the existing tunnel and re-expose with optionally updated options. Calls `close()` first, merges any new options, then calls `expose()`.",
8447
- "parameters": {
8448
- "newOptions": {
8449
- "type": "Partial<PortExposerOptions>",
8450
- "description": "Optional partial options to merge before reconnecting"
8451
- }
8452
- },
8453
- "required": [],
8454
- "returns": "Promise<string>",
8455
- "examples": [
8456
- {
8457
- "language": "ts",
8458
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\n// Switch to a different port\nconst newUrl = await exposer.reconnect({ port: 8080 })"
8459
- }
8460
- ]
8461
- },
8462
- "disable": {
8463
- "description": "Disable the feature, ensuring the ngrok tunnel is closed first. Overrides the base `disable()` to guarantee that the tunnel is torn down before the feature is marked as disabled.",
8464
- "parameters": {},
8465
- "required": [],
8466
- "returns": "Promise<this>",
8467
- "examples": [
8468
- {
8469
- "language": "ts",
8470
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nawait exposer.disable()"
8471
- }
8472
- ]
8473
- }
8474
- },
8475
- "getters": {},
8476
- "events": {
8477
- "exposed": {
8478
- "name": "exposed",
8479
- "description": "Event emitted by PortExposer",
8480
- "arguments": {}
8481
- },
8482
- "error": {
8483
- "name": "error",
8484
- "description": "Event emitted by PortExposer",
8485
- "arguments": {}
8486
- },
8487
- "closed": {
8488
- "name": "closed",
8489
- "description": "Event emitted by PortExposer",
8490
- "arguments": {}
8491
- }
8492
- },
8493
- "state": {},
8494
- "options": {},
8495
- "envVars": [],
8496
- "examples": [
8497
- {
8498
- "language": "ts",
8499
- "code": "// Basic usage\nconst exposer = container.feature('portExposer', { port: 3000 })\nconst url = await exposer.expose()\nconsole.log(`Service available at: ${url}`)\n\n// With custom subdomain\nconst exposer = container.feature('portExposer', {\n port: 8080,\n subdomain: 'my-app',\n authToken: 'your-ngrok-token'\n})"
8500
- }
8501
- ]
8502
- });
8503
-
8504
8864
  setBuildTimeData('features.googleSheets', {
8505
8865
  "id": "features.googleSheets",
8506
8866
  "description": "Google Sheets feature for reading spreadsheet data as JSON, CSV, or raw arrays. Depends on the googleAuth feature for authentication. Creates a Sheets v4 API client lazily and provides convenient methods for reading tabular data.",
@@ -11696,275 +12056,11 @@ setBuildTimeData('clients.openai', {
11696
12056
  ]
11697
12057
  });
11698
12058
 
11699
- setBuildTimeData('clients.elevenlabs', {
11700
- "id": "clients.elevenlabs",
11701
- "description": "ElevenLabs client text-to-speech synthesis via the ElevenLabs REST API. Provides methods for listing voices, listing models, and generating speech audio. Audio is returned as a Buffer; use `say()` for a convenience method that writes to disk.",
11702
- "shortcut": "clients.elevenlabs",
11703
- "className": "ElevenLabsClient",
11704
- "methods": {
11705
- "beforeRequest": {
11706
- "description": "Inject the xi-api-key header before each request.",
11707
- "parameters": {},
11708
- "required": [],
11709
- "returns": "void"
11710
- },
11711
- "connect": {
11712
- "description": "Validate the API key by listing available models.",
11713
- "parameters": {},
11714
- "required": [],
11715
- "returns": "Promise<this>",
11716
- "examples": [
11717
- {
11718
- "language": "ts",
11719
- "code": "await el.connect()"
11720
- }
11721
- ]
11722
- },
11723
- "listVoices": {
11724
- "description": "List available voices with optional search and filtering.",
11725
- "parameters": {
11726
- "options": {
11727
- "type": "{\n search?: string\n category?: string\n voice_type?: string\n page_size?: number\n next_page_token?: string\n }",
11728
- "description": "Query parameters for filtering voices"
11729
- }
11730
- },
11731
- "required": [],
11732
- "returns": "Promise<any>",
11733
- "examples": [
11734
- {
11735
- "language": "ts",
11736
- "code": "const voices = await el.listVoices()\nconst premade = await el.listVoices({ category: 'premade' })"
11737
- }
11738
- ]
11739
- },
11740
- "getVoice": {
11741
- "description": "Get details for a single voice.",
11742
- "parameters": {
11743
- "voiceId": {
11744
- "type": "string",
11745
- "description": "The voice ID to look up"
11746
- }
11747
- },
11748
- "required": [
11749
- "voiceId"
11750
- ],
11751
- "returns": "Promise<any>",
11752
- "examples": [
11753
- {
11754
- "language": "ts",
11755
- "code": "const voice = await el.getVoice('21m00Tcm4TlvDq8ikWAM')\nconsole.log(voice.name, voice.settings)"
11756
- }
11757
- ]
11758
- },
11759
- "listModels": {
11760
- "description": "List available TTS models.",
11761
- "parameters": {},
11762
- "required": [],
11763
- "returns": "Promise<any[]>",
11764
- "examples": [
11765
- {
11766
- "language": "ts",
11767
- "code": "const models = await el.listModels()\nconsole.log(models.map(m => m.model_id))"
11768
- }
11769
- ]
11770
- },
11771
- "synthesize": {
11772
- "description": "Synthesize speech from text, returning audio as a Buffer.",
11773
- "parameters": {
11774
- "text": {
11775
- "type": "string",
11776
- "description": "The text to convert to speech"
11777
- },
11778
- "options": {
11779
- "type": "SynthesizeOptions",
11780
- "description": "Voice, model, format, and voice settings overrides",
11781
- "properties": {
11782
- "voiceId": {
11783
- "type": "string",
11784
- "description": ""
11785
- },
11786
- "modelId": {
11787
- "type": "string",
11788
- "description": ""
11789
- },
11790
- "outputFormat": {
11791
- "type": "string",
11792
- "description": ""
11793
- },
11794
- "voiceSettings": {
11795
- "type": "ElevenLabsVoiceSettings",
11796
- "description": ""
11797
- },
11798
- "disableCache": {
11799
- "type": "boolean",
11800
- "description": ""
11801
- }
11802
- }
11803
- }
11804
- },
11805
- "required": [
11806
- "text"
11807
- ],
11808
- "returns": "Promise<Buffer>",
11809
- "examples": [
11810
- {
11811
- "language": "ts",
11812
- "code": "const audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data\n\nconst custom = await el.synthesize('Hello', {\n voiceId: '21m00Tcm4TlvDq8ikWAM',\n voiceSettings: { stability: 0.5, similarityBoost: 0.8 }\n})"
11813
- }
11814
- ]
11815
- },
11816
- "say": {
11817
- "description": "Synthesize speech and write the audio to a file.",
11818
- "parameters": {
11819
- "text": {
11820
- "type": "string",
11821
- "description": "The text to convert to speech"
11822
- },
11823
- "outputPath": {
11824
- "type": "string",
11825
- "description": "File path to write the audio to"
11826
- },
11827
- "options": {
11828
- "type": "SynthesizeOptions",
11829
- "description": "Voice, model, format, and voice settings overrides",
11830
- "properties": {
11831
- "voiceId": {
11832
- "type": "string",
11833
- "description": ""
11834
- },
11835
- "modelId": {
11836
- "type": "string",
11837
- "description": ""
11838
- },
11839
- "outputFormat": {
11840
- "type": "string",
11841
- "description": ""
11842
- },
11843
- "voiceSettings": {
11844
- "type": "ElevenLabsVoiceSettings",
11845
- "description": ""
11846
- },
11847
- "disableCache": {
11848
- "type": "boolean",
11849
- "description": ""
11850
- }
11851
- }
11852
- }
11853
- },
11854
- "required": [
11855
- "text",
11856
- "outputPath"
11857
- ],
11858
- "returns": "Promise<string>",
11859
- "examples": [
11860
- {
11861
- "language": "ts",
11862
- "code": "const path = await el.say('Hello world', './hello.mp3')\nconsole.log(`Audio saved to ${path}`)"
11863
- }
11864
- ]
11865
- }
11866
- },
11867
- "getters": {
11868
- "apiKey": {
11869
- "description": "The resolved API key from options or environment.",
11870
- "returns": "string"
11871
- }
11872
- },
11873
- "events": {
11874
- "failure": {
11875
- "name": "failure",
11876
- "description": "Event emitted by ElevenLabsClient",
11877
- "arguments": {}
11878
- },
11879
- "voices": {
11880
- "name": "voices",
11881
- "description": "Event emitted by ElevenLabsClient",
11882
- "arguments": {}
11883
- },
11884
- "speech": {
11885
- "name": "speech",
11886
- "description": "Event emitted by ElevenLabsClient",
11887
- "arguments": {}
11888
- }
11889
- },
11890
- "state": {},
11891
- "options": {},
11892
- "envVars": [],
11893
- "examples": [
11894
- {
11895
- "language": "ts",
11896
- "code": "const el = container.client('elevenlabs')\nawait el.connect()\nconst voices = await el.listVoices()\nconst audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data"
11897
- }
11898
- ],
11899
- "types": {
11900
- "SynthesizeOptions": {
11901
- "description": "",
11902
- "properties": {
11903
- "voiceId": {
11904
- "type": "string",
11905
- "description": "",
11906
- "optional": true
11907
- },
11908
- "modelId": {
11909
- "type": "string",
11910
- "description": "",
11911
- "optional": true
11912
- },
11913
- "outputFormat": {
11914
- "type": "string",
11915
- "description": "",
11916
- "optional": true
11917
- },
11918
- "voiceSettings": {
11919
- "type": "ElevenLabsVoiceSettings",
11920
- "description": "",
11921
- "optional": true
11922
- },
11923
- "disableCache": {
11924
- "type": "boolean",
11925
- "description": "",
11926
- "optional": true
11927
- }
11928
- }
11929
- },
11930
- "ElevenLabsVoiceSettings": {
11931
- "description": "",
11932
- "properties": {
11933
- "stability": {
11934
- "type": "number",
11935
- "description": "",
11936
- "optional": true
11937
- },
11938
- "similarityBoost": {
11939
- "type": "number",
11940
- "description": "",
11941
- "optional": true
11942
- },
11943
- "style": {
11944
- "type": "number",
11945
- "description": "",
11946
- "optional": true
11947
- },
11948
- "speed": {
11949
- "type": "number",
11950
- "description": "",
11951
- "optional": true
11952
- },
11953
- "useSpeakerBoost": {
11954
- "type": "boolean",
11955
- "description": "",
11956
- "optional": true
11957
- }
11958
- }
11959
- }
11960
- }
11961
- });
11962
-
11963
- setBuildTimeData('clients.supabase', {
11964
- "id": "clients.supabase",
11965
- "description": "Supabase client for the Luca container system. Wraps the official `@supabase/supabase-js` SDK and exposes it through Luca's typed state, events, and introspection system. The SDK is isomorphic so this single implementation works in both Node and browser containers. Use `client.sdk` for full SDK access, or use the convenience wrappers for common operations (auth, database queries, storage, edge functions, realtime).",
11966
- "shortcut": "clients.supabase",
11967
- "className": "SupabaseClient",
12059
+ setBuildTimeData('clients.supabase', {
12060
+ "id": "clients.supabase",
12061
+ "description": "Supabase client for the Luca container system. Wraps the official `@supabase/supabase-js` SDK and exposes it through Luca's typed state, events, and introspection system. The SDK is isomorphic so this single implementation works in both Node and browser containers. Use `client.sdk` for full SDK access, or use the convenience wrappers for common operations (auth, database queries, storage, edge functions, realtime).",
12062
+ "shortcut": "clients.supabase",
12063
+ "className": "SupabaseClient",
11968
12064
  "methods": {
11969
12065
  "from": {
11970
12066
  "description": "Start a query on a Postgres table or view.",
@@ -12420,31 +12516,295 @@ setBuildTimeData('clients.comfyui', {
12420
12516
  "description": "Polling interval in ms (default 1000)",
12421
12517
  "optional": true
12422
12518
  },
12423
- "inputMap": {
12424
- "type": "InputMapping",
12425
- "description": "Named input mapping: semantic name -> { nodeId, field }",
12519
+ "inputMap": {
12520
+ "type": "InputMapping",
12521
+ "description": "Named input mapping: semantic name -> { nodeId, field }",
12522
+ "optional": true
12523
+ },
12524
+ "outputDir": {
12525
+ "type": "string",
12526
+ "description": "If provided, output images are downloaded to this directory",
12527
+ "optional": true
12528
+ }
12529
+ }
12530
+ },
12531
+ "WorkflowResult": {
12532
+ "description": "",
12533
+ "properties": {
12534
+ "promptId": {
12535
+ "type": "string",
12536
+ "description": ""
12537
+ },
12538
+ "outputs": {
12539
+ "type": "Record<string, any>",
12540
+ "description": ""
12541
+ },
12542
+ "images": {
12543
+ "type": "Array<{ filename: string; subfolder: string; type: string; localPath?: string }>",
12544
+ "description": "",
12545
+ "optional": true
12546
+ }
12547
+ }
12548
+ }
12549
+ }
12550
+ });
12551
+
12552
+ setBuildTimeData('clients.elevenlabs', {
12553
+ "id": "clients.elevenlabs",
12554
+ "description": "ElevenLabs client — text-to-speech synthesis via the ElevenLabs REST API. Provides methods for listing voices, listing models, and generating speech audio. Audio is returned as a Buffer; use `say()` for a convenience method that writes to disk.",
12555
+ "shortcut": "clients.elevenlabs",
12556
+ "className": "ElevenLabsClient",
12557
+ "methods": {
12558
+ "beforeRequest": {
12559
+ "description": "Inject the xi-api-key header before each request.",
12560
+ "parameters": {},
12561
+ "required": [],
12562
+ "returns": "void"
12563
+ },
12564
+ "connect": {
12565
+ "description": "Validate the API key by listing available models.",
12566
+ "parameters": {},
12567
+ "required": [],
12568
+ "returns": "Promise<this>",
12569
+ "examples": [
12570
+ {
12571
+ "language": "ts",
12572
+ "code": "await el.connect()"
12573
+ }
12574
+ ]
12575
+ },
12576
+ "listVoices": {
12577
+ "description": "List available voices with optional search and filtering.",
12578
+ "parameters": {
12579
+ "options": {
12580
+ "type": "{\n search?: string\n category?: string\n voice_type?: string\n page_size?: number\n next_page_token?: string\n }",
12581
+ "description": "Query parameters for filtering voices"
12582
+ }
12583
+ },
12584
+ "required": [],
12585
+ "returns": "Promise<any>",
12586
+ "examples": [
12587
+ {
12588
+ "language": "ts",
12589
+ "code": "const voices = await el.listVoices()\nconst premade = await el.listVoices({ category: 'premade' })"
12590
+ }
12591
+ ]
12592
+ },
12593
+ "getVoice": {
12594
+ "description": "Get details for a single voice.",
12595
+ "parameters": {
12596
+ "voiceId": {
12597
+ "type": "string",
12598
+ "description": "The voice ID to look up"
12599
+ }
12600
+ },
12601
+ "required": [
12602
+ "voiceId"
12603
+ ],
12604
+ "returns": "Promise<any>",
12605
+ "examples": [
12606
+ {
12607
+ "language": "ts",
12608
+ "code": "const voice = await el.getVoice('21m00Tcm4TlvDq8ikWAM')\nconsole.log(voice.name, voice.settings)"
12609
+ }
12610
+ ]
12611
+ },
12612
+ "listModels": {
12613
+ "description": "List available TTS models.",
12614
+ "parameters": {},
12615
+ "required": [],
12616
+ "returns": "Promise<any[]>",
12617
+ "examples": [
12618
+ {
12619
+ "language": "ts",
12620
+ "code": "const models = await el.listModels()\nconsole.log(models.map(m => m.model_id))"
12621
+ }
12622
+ ]
12623
+ },
12624
+ "synthesize": {
12625
+ "description": "Synthesize speech from text, returning audio as a Buffer.",
12626
+ "parameters": {
12627
+ "text": {
12628
+ "type": "string",
12629
+ "description": "The text to convert to speech"
12630
+ },
12631
+ "options": {
12632
+ "type": "SynthesizeOptions",
12633
+ "description": "Voice, model, format, and voice settings overrides",
12634
+ "properties": {
12635
+ "voiceId": {
12636
+ "type": "string",
12637
+ "description": ""
12638
+ },
12639
+ "modelId": {
12640
+ "type": "string",
12641
+ "description": ""
12642
+ },
12643
+ "outputFormat": {
12644
+ "type": "string",
12645
+ "description": ""
12646
+ },
12647
+ "voiceSettings": {
12648
+ "type": "ElevenLabsVoiceSettings",
12649
+ "description": ""
12650
+ },
12651
+ "disableCache": {
12652
+ "type": "boolean",
12653
+ "description": ""
12654
+ }
12655
+ }
12656
+ }
12657
+ },
12658
+ "required": [
12659
+ "text"
12660
+ ],
12661
+ "returns": "Promise<Buffer>",
12662
+ "examples": [
12663
+ {
12664
+ "language": "ts",
12665
+ "code": "const audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data\n\nconst custom = await el.synthesize('Hello', {\n voiceId: '21m00Tcm4TlvDq8ikWAM',\n voiceSettings: { stability: 0.5, similarityBoost: 0.8 }\n})"
12666
+ }
12667
+ ]
12668
+ },
12669
+ "say": {
12670
+ "description": "Synthesize speech and write the audio to a file.",
12671
+ "parameters": {
12672
+ "text": {
12673
+ "type": "string",
12674
+ "description": "The text to convert to speech"
12675
+ },
12676
+ "outputPath": {
12677
+ "type": "string",
12678
+ "description": "File path to write the audio to"
12679
+ },
12680
+ "options": {
12681
+ "type": "SynthesizeOptions",
12682
+ "description": "Voice, model, format, and voice settings overrides",
12683
+ "properties": {
12684
+ "voiceId": {
12685
+ "type": "string",
12686
+ "description": ""
12687
+ },
12688
+ "modelId": {
12689
+ "type": "string",
12690
+ "description": ""
12691
+ },
12692
+ "outputFormat": {
12693
+ "type": "string",
12694
+ "description": ""
12695
+ },
12696
+ "voiceSettings": {
12697
+ "type": "ElevenLabsVoiceSettings",
12698
+ "description": ""
12699
+ },
12700
+ "disableCache": {
12701
+ "type": "boolean",
12702
+ "description": ""
12703
+ }
12704
+ }
12705
+ }
12706
+ },
12707
+ "required": [
12708
+ "text",
12709
+ "outputPath"
12710
+ ],
12711
+ "returns": "Promise<string>",
12712
+ "examples": [
12713
+ {
12714
+ "language": "ts",
12715
+ "code": "const path = await el.say('Hello world', './hello.mp3')\nconsole.log(`Audio saved to ${path}`)"
12716
+ }
12717
+ ]
12718
+ }
12719
+ },
12720
+ "getters": {
12721
+ "apiKey": {
12722
+ "description": "The resolved API key from options or environment.",
12723
+ "returns": "string"
12724
+ }
12725
+ },
12726
+ "events": {
12727
+ "failure": {
12728
+ "name": "failure",
12729
+ "description": "Event emitted by ElevenLabsClient",
12730
+ "arguments": {}
12731
+ },
12732
+ "voices": {
12733
+ "name": "voices",
12734
+ "description": "Event emitted by ElevenLabsClient",
12735
+ "arguments": {}
12736
+ },
12737
+ "speech": {
12738
+ "name": "speech",
12739
+ "description": "Event emitted by ElevenLabsClient",
12740
+ "arguments": {}
12741
+ }
12742
+ },
12743
+ "state": {},
12744
+ "options": {},
12745
+ "envVars": [],
12746
+ "examples": [
12747
+ {
12748
+ "language": "ts",
12749
+ "code": "const el = container.client('elevenlabs')\nawait el.connect()\nconst voices = await el.listVoices()\nconst audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data"
12750
+ }
12751
+ ],
12752
+ "types": {
12753
+ "SynthesizeOptions": {
12754
+ "description": "",
12755
+ "properties": {
12756
+ "voiceId": {
12757
+ "type": "string",
12758
+ "description": "",
12759
+ "optional": true
12760
+ },
12761
+ "modelId": {
12762
+ "type": "string",
12763
+ "description": "",
12764
+ "optional": true
12765
+ },
12766
+ "outputFormat": {
12767
+ "type": "string",
12768
+ "description": "",
12769
+ "optional": true
12770
+ },
12771
+ "voiceSettings": {
12772
+ "type": "ElevenLabsVoiceSettings",
12773
+ "description": "",
12426
12774
  "optional": true
12427
12775
  },
12428
- "outputDir": {
12429
- "type": "string",
12430
- "description": "If provided, output images are downloaded to this directory",
12776
+ "disableCache": {
12777
+ "type": "boolean",
12778
+ "description": "",
12431
12779
  "optional": true
12432
12780
  }
12433
12781
  }
12434
12782
  },
12435
- "WorkflowResult": {
12783
+ "ElevenLabsVoiceSettings": {
12436
12784
  "description": "",
12437
12785
  "properties": {
12438
- "promptId": {
12439
- "type": "string",
12440
- "description": ""
12786
+ "stability": {
12787
+ "type": "number",
12788
+ "description": "",
12789
+ "optional": true
12441
12790
  },
12442
- "outputs": {
12443
- "type": "Record<string, any>",
12444
- "description": ""
12791
+ "similarityBoost": {
12792
+ "type": "number",
12793
+ "description": "",
12794
+ "optional": true
12445
12795
  },
12446
- "images": {
12447
- "type": "Array<{ filename: string; subfolder: string; type: string; localPath?: string }>",
12796
+ "style": {
12797
+ "type": "number",
12798
+ "description": "",
12799
+ "optional": true
12800
+ },
12801
+ "speed": {
12802
+ "type": "number",
12803
+ "description": "",
12804
+ "optional": true
12805
+ },
12806
+ "useSpeakerBoost": {
12807
+ "type": "boolean",
12448
12808
  "description": "",
12449
12809
  "optional": true
12450
12810
  }
@@ -13185,7 +13545,7 @@ setContainerBuildTimeData('Container', {
13185
13545
  }
13186
13546
  ]
13187
13547
  },
13188
- "inspect": {
13548
+ "introspect": {
13189
13549
  "description": "Returns a full introspection object for this container, merging build-time AST data (JSDoc descriptions, methods, getters) with runtime data (registries, factories, state, environment).",
13190
13550
  "parameters": {},
13191
13551
  "required": [],
@@ -13193,11 +13553,11 @@ setContainerBuildTimeData('Container', {
13193
13553
  "examples": [
13194
13554
  {
13195
13555
  "language": "ts",
13196
- "code": "const info = container.inspect()\nconsole.log(info.methods) // all public methods with descriptions\nconsole.log(info.getters) // all getters with return types\nconsole.log(info.registries) // features, clients, servers, etc."
13556
+ "code": "const info = container.introspect()\nconsole.log(info.methods) // all public methods with descriptions\nconsole.log(info.getters) // all getters with return types\nconsole.log(info.registries) // features, clients, servers, etc."
13197
13557
  }
13198
13558
  ]
13199
13559
  },
13200
- "inspectAsText": {
13560
+ "introspectAsText": {
13201
13561
  "description": "Returns a human-readable markdown representation of this container's introspection data. Useful in REPLs, AI agent contexts, or documentation generation. Pass a section name to render only that section (e.g. 'methods', 'getters', 'events', 'state').",
13202
13562
  "parameters": {
13203
13563
  "sectionOrDepth": {
@@ -13214,32 +13574,17 @@ setContainerBuildTimeData('Container', {
13214
13574
  "examples": [
13215
13575
  {
13216
13576
  "language": "ts",
13217
- "code": "console.log(container.inspectAsText()) // full description\nconsole.log(container.inspectAsText('methods')) // just methods"
13577
+ "code": "console.log(container.introspectAsText()) // full description\nconsole.log(container.introspectAsText('methods')) // just methods"
13218
13578
  }
13219
13579
  ]
13220
13580
  },
13221
- "introspectAsText": {
13222
- "description": "Alias for inspectAsText.",
13223
- "parameters": {
13224
- "sectionOrDepth": {
13225
- "type": "IntrospectionSection | number",
13226
- "description": "Parameter sectionOrDepth"
13227
- },
13228
- "startHeadingDepth": {
13229
- "type": "number",
13230
- "description": "Parameter startHeadingDepth"
13231
- }
13232
- },
13233
- "required": [],
13234
- "returns": "string"
13235
- },
13236
13581
  "introspectAsJSON": {
13237
- "description": "Alias for inspect, returns JSON introspection data.",
13582
+ "description": "Returns JSON introspection data.",
13238
13583
  "parameters": {},
13239
13584
  "required": [],
13240
13585
  "returns": "ContainerIntrospection"
13241
13586
  },
13242
- "inspectAsType": {
13587
+ "introspectAsType": {
13243
13588
  "description": "Returns the container's introspection data formatted as a TypeScript interface declaration. Includes the container's own methods, getters, factories, and registered helper types.",
13244
13589
  "parameters": {},
13245
13590
  "required": [],
@@ -13247,16 +13592,10 @@ setContainerBuildTimeData('Container', {
13247
13592
  "examples": [
13248
13593
  {
13249
13594
  "language": "ts",
13250
- "code": "console.log(container.inspectAsType())\n// interface NodeContainer {\n// feature<T>(id: string, options?: object): T;\n// readonly uuid: string;\n// ...\n// }"
13595
+ "code": "console.log(container.introspectAsType())\n// interface NodeContainer {\n// feature<T>(id: string, options?: object): T;\n// readonly uuid: string;\n// ...\n// }"
13251
13596
  }
13252
13597
  ]
13253
13598
  },
13254
- "introspectAsType": {
13255
- "description": "",
13256
- "parameters": {},
13257
- "required": [],
13258
- "returns": "string"
13259
- },
13260
13599
  "sleep": {
13261
13600
  "description": "Sleep for the specified number of milliseconds. Useful for scripting and sequencing.",
13262
13601
  "parameters": {
@@ -19303,7 +19642,312 @@ export const introspectionData = [
19303
19642
  }
19304
19643
  }
19305
19644
  }
19306
- }
19645
+ }
19646
+ },
19647
+ {
19648
+ "id": "features.redis",
19649
+ "description": "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.",
19650
+ "shortcut": "features.redis",
19651
+ "className": "RedisFeature",
19652
+ "methods": {
19653
+ "set": {
19654
+ "description": "Set a key to a string value with optional TTL.",
19655
+ "parameters": {
19656
+ "key": {
19657
+ "type": "string",
19658
+ "description": "The key name"
19659
+ },
19660
+ "value": {
19661
+ "type": "string",
19662
+ "description": "The string value to store"
19663
+ },
19664
+ "ttl": {
19665
+ "type": "number",
19666
+ "description": "Optional time-to-live in seconds"
19667
+ }
19668
+ },
19669
+ "required": [
19670
+ "key",
19671
+ "value"
19672
+ ],
19673
+ "returns": "Promise<void>"
19674
+ },
19675
+ "get": {
19676
+ "description": "Get a key's value. Returns null if the key doesn't exist.",
19677
+ "parameters": {
19678
+ "key": {
19679
+ "type": "string",
19680
+ "description": "The key name"
19681
+ }
19682
+ },
19683
+ "required": [
19684
+ "key"
19685
+ ],
19686
+ "returns": "Promise<string | null>"
19687
+ },
19688
+ "del": {
19689
+ "description": "Delete one or more keys.",
19690
+ "parameters": {
19691
+ "keys": {
19692
+ "type": "string[]",
19693
+ "description": "One or more key names to delete"
19694
+ }
19695
+ },
19696
+ "required": [
19697
+ "keys"
19698
+ ],
19699
+ "returns": "Promise<number>"
19700
+ },
19701
+ "exists": {
19702
+ "description": "Check if a key exists.",
19703
+ "parameters": {
19704
+ "key": {
19705
+ "type": "string",
19706
+ "description": "The key name"
19707
+ }
19708
+ },
19709
+ "required": [
19710
+ "key"
19711
+ ],
19712
+ "returns": "Promise<boolean>"
19713
+ },
19714
+ "expire": {
19715
+ "description": "Set a key's TTL in seconds.",
19716
+ "parameters": {
19717
+ "key": {
19718
+ "type": "string",
19719
+ "description": "The key name"
19720
+ },
19721
+ "seconds": {
19722
+ "type": "number",
19723
+ "description": "TTL in seconds"
19724
+ }
19725
+ },
19726
+ "required": [
19727
+ "key",
19728
+ "seconds"
19729
+ ],
19730
+ "returns": "Promise<boolean>"
19731
+ },
19732
+ "keys": {
19733
+ "description": "Find keys matching a glob pattern (respects prefix).",
19734
+ "parameters": {
19735
+ "pattern": {
19736
+ "type": "string",
19737
+ "description": "Glob pattern, e.g. \"worker:*\""
19738
+ }
19739
+ },
19740
+ "required": [],
19741
+ "returns": "Promise<string[]>"
19742
+ },
19743
+ "setJSON": {
19744
+ "description": "Store a value as JSON.",
19745
+ "parameters": {
19746
+ "key": {
19747
+ "type": "string",
19748
+ "description": "The key name"
19749
+ },
19750
+ "value": {
19751
+ "type": "unknown",
19752
+ "description": "Any JSON-serializable value"
19753
+ },
19754
+ "ttl": {
19755
+ "type": "number",
19756
+ "description": "Optional TTL in seconds"
19757
+ }
19758
+ },
19759
+ "required": [
19760
+ "key",
19761
+ "value"
19762
+ ],
19763
+ "returns": "Promise<void>"
19764
+ },
19765
+ "getJSON": {
19766
+ "description": "Retrieve and parse a JSON value.",
19767
+ "parameters": {
19768
+ "key": {
19769
+ "type": "string",
19770
+ "description": "The key name"
19771
+ }
19772
+ },
19773
+ "required": [
19774
+ "key"
19775
+ ],
19776
+ "returns": "Promise<T | null>"
19777
+ },
19778
+ "hset": {
19779
+ "description": "Set fields on a hash.",
19780
+ "parameters": {
19781
+ "key": {
19782
+ "type": "string",
19783
+ "description": "The hash key"
19784
+ },
19785
+ "fields": {
19786
+ "type": "Record<string, string>",
19787
+ "description": "Object of field/value pairs"
19788
+ }
19789
+ },
19790
+ "required": [
19791
+ "key",
19792
+ "fields"
19793
+ ],
19794
+ "returns": "Promise<void>"
19795
+ },
19796
+ "hgetall": {
19797
+ "description": "Get all fields from a hash.",
19798
+ "parameters": {
19799
+ "key": {
19800
+ "type": "string",
19801
+ "description": "The hash key"
19802
+ }
19803
+ },
19804
+ "required": [
19805
+ "key"
19806
+ ],
19807
+ "returns": "Promise<Record<string, string>>"
19808
+ },
19809
+ "hget": {
19810
+ "description": "Get a single field from a hash.",
19811
+ "parameters": {
19812
+ "key": {
19813
+ "type": "string",
19814
+ "description": "The hash key"
19815
+ },
19816
+ "field": {
19817
+ "type": "string",
19818
+ "description": "The field name"
19819
+ }
19820
+ },
19821
+ "required": [
19822
+ "key",
19823
+ "field"
19824
+ ],
19825
+ "returns": "Promise<string | null>"
19826
+ },
19827
+ "subscribe": {
19828
+ "description": "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.",
19829
+ "parameters": {
19830
+ "channels": {
19831
+ "type": "string | string[]",
19832
+ "description": "Channel name(s) to subscribe to"
19833
+ },
19834
+ "handler": {
19835
+ "type": "MessageHandler",
19836
+ "description": "Optional per-channel message handler"
19837
+ }
19838
+ },
19839
+ "required": [
19840
+ "channels"
19841
+ ],
19842
+ "returns": "Promise<void>",
19843
+ "examples": [
19844
+ {
19845
+ "language": "ts",
19846
+ "code": "await redis.subscribe('tasks', (channel, msg) => {\n console.log(`Got ${msg} on ${channel}`)\n})"
19847
+ }
19848
+ ]
19849
+ },
19850
+ "unsubscribe": {
19851
+ "description": "Unsubscribe from one or more channels.",
19852
+ "parameters": {
19853
+ "channels": {
19854
+ "type": "string[]",
19855
+ "description": "Channel name(s) to unsubscribe from"
19856
+ }
19857
+ },
19858
+ "required": [
19859
+ "channels"
19860
+ ],
19861
+ "returns": "Promise<void>"
19862
+ },
19863
+ "publish": {
19864
+ "description": "Publish a message to a channel.",
19865
+ "parameters": {
19866
+ "channel": {
19867
+ "type": "string",
19868
+ "description": "The channel to publish to"
19869
+ },
19870
+ "message": {
19871
+ "type": "string",
19872
+ "description": "The message string (use JSON.stringify for objects)"
19873
+ }
19874
+ },
19875
+ "required": [
19876
+ "channel",
19877
+ "message"
19878
+ ],
19879
+ "returns": "Promise<number>"
19880
+ },
19881
+ "ensureLocalDocker": {
19882
+ "description": "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.",
19883
+ "parameters": {
19884
+ "options": {
19885
+ "type": "{ name?: string; port?: number; image?: string }",
19886
+ "description": "Container name and host port"
19887
+ }
19888
+ },
19889
+ "required": [],
19890
+ "returns": "Promise<string>",
19891
+ "examples": [
19892
+ {
19893
+ "language": "ts",
19894
+ "code": "const redis = container.feature('redis', { url: 'redis://localhost:6379', lazyConnect: true })\nawait redis.ensureLocalDocker()"
19895
+ }
19896
+ ]
19897
+ },
19898
+ "close": {
19899
+ "description": "Close all redis connections (main client + subscriber).",
19900
+ "parameters": {},
19901
+ "required": [],
19902
+ "returns": "Promise<this>"
19903
+ }
19904
+ },
19905
+ "getters": {
19906
+ "client": {
19907
+ "description": "The underlying ioredis client for advanced operations.",
19908
+ "returns": "Redis"
19909
+ },
19910
+ "subscriber": {
19911
+ "description": "The dedicated subscriber connection, if pub/sub is active.",
19912
+ "returns": "Redis | null"
19913
+ }
19914
+ },
19915
+ "events": {
19916
+ "message": {
19917
+ "name": "message",
19918
+ "description": "Event emitted by RedisFeature",
19919
+ "arguments": {}
19920
+ },
19921
+ "error": {
19922
+ "name": "error",
19923
+ "description": "Event emitted by RedisFeature",
19924
+ "arguments": {}
19925
+ },
19926
+ "subscribed": {
19927
+ "name": "subscribed",
19928
+ "description": "Event emitted by RedisFeature",
19929
+ "arguments": {}
19930
+ },
19931
+ "unsubscribed": {
19932
+ "name": "unsubscribed",
19933
+ "description": "Event emitted by RedisFeature",
19934
+ "arguments": {}
19935
+ },
19936
+ "closed": {
19937
+ "name": "closed",
19938
+ "description": "Event emitted by RedisFeature",
19939
+ "arguments": {}
19940
+ }
19941
+ },
19942
+ "state": {},
19943
+ "options": {},
19944
+ "envVars": [],
19945
+ "examples": [
19946
+ {
19947
+ "language": "ts",
19948
+ "code": "const redis = container.feature('redis', { url: 'redis://localhost:6379' })\n\n// Shared state\nawait redis.set('worker:status', 'active')\nconst status = await redis.get('worker:status')\n\n// Pub/sub between instances\nredis.on('message', (channel, msg) => console.log(`${channel}: ${msg}`))\nawait redis.subscribe('tasks')\nawait redis.publish('tasks', JSON.stringify({ type: 'ping' }))\n\n// JSON helpers\nawait redis.setJSON('config', { workers: 4, debug: true })\nconst config = await redis.getJSON<{ workers: number }>('config')"
19949
+ }
19950
+ ]
19307
19951
  },
19308
19952
  {
19309
19953
  "id": "features.fs",
@@ -20940,7 +21584,7 @@ export const introspectionData = [
20940
21584
  },
20941
21585
  {
20942
21586
  "id": "features.python",
20943
- "description": "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.",
21587
+ "description": "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",
20944
21588
  "shortcut": "features.python",
20945
21589
  "className": "Python",
20946
21590
  "methods": {
@@ -21040,6 +21684,146 @@ export const introspectionData = [
21040
21684
  "parameters": {},
21041
21685
  "required": [],
21042
21686
  "returns": "Promise<{ version: string; path: string; packages: string[] }>"
21687
+ },
21688
+ "startSession": {
21689
+ "description": "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.",
21690
+ "parameters": {},
21691
+ "required": [],
21692
+ "returns": "Promise<void>",
21693
+ "examples": [
21694
+ {
21695
+ "language": "ts",
21696
+ "code": "const python = container.feature('python', { dir: '/path/to/project' })\nawait python.enable()\nawait python.startSession()\nawait python.run('x = 42')\nconst result = await python.run('print(x)')\nconsole.log(result.stdout) // '42\\n'\nawait python.stopSession()"
21697
+ }
21698
+ ]
21699
+ },
21700
+ "stopSession": {
21701
+ "description": "Stops the persistent Python session and cleans up the bridge process.",
21702
+ "parameters": {},
21703
+ "required": [],
21704
+ "returns": "Promise<void>",
21705
+ "examples": [
21706
+ {
21707
+ "language": "ts",
21708
+ "code": "await python.stopSession()"
21709
+ }
21710
+ ]
21711
+ },
21712
+ "run": {
21713
+ "description": "Executes Python code in the persistent session. Variables and imports survive across calls. This is the session equivalent of execute().",
21714
+ "parameters": {
21715
+ "code": {
21716
+ "type": "string",
21717
+ "description": "Python code to execute"
21718
+ },
21719
+ "variables": {
21720
+ "type": "Record<string, any>",
21721
+ "description": "Variables to inject into the namespace before execution"
21722
+ }
21723
+ },
21724
+ "required": [
21725
+ "code"
21726
+ ],
21727
+ "returns": "Promise<RunResult>",
21728
+ "examples": [
21729
+ {
21730
+ "language": "ts",
21731
+ "code": "await python.startSession()\n\n// State persists across calls\nawait python.run('x = 42')\nconst result = await python.run('print(x * 2)')\nconsole.log(result.stdout) // '84\\n'\n\n// Inject variables from JS\nconst result2 = await python.run('print(f\"Hello {name}!\")', { name: 'World' })\nconsole.log(result2.stdout) // 'Hello World!\\n'"
21732
+ }
21733
+ ]
21734
+ },
21735
+ "eval": {
21736
+ "description": "Evaluates a Python expression in the persistent session and returns its value.",
21737
+ "parameters": {
21738
+ "expression": {
21739
+ "type": "string",
21740
+ "description": "Python expression to evaluate"
21741
+ }
21742
+ },
21743
+ "required": [
21744
+ "expression"
21745
+ ],
21746
+ "returns": "Promise<any>",
21747
+ "examples": [
21748
+ {
21749
+ "language": "ts",
21750
+ "code": "await python.run('x = 42')\nconst result = await python.eval('x * 2')\nconsole.log(result) // 84"
21751
+ }
21752
+ ]
21753
+ },
21754
+ "importModule": {
21755
+ "description": "Imports a Python module into the persistent session namespace.",
21756
+ "parameters": {
21757
+ "moduleName": {
21758
+ "type": "string",
21759
+ "description": "Dotted module path (e.g. 'myapp.models')"
21760
+ },
21761
+ "alias": {
21762
+ "type": "string",
21763
+ "description": "Optional alias for the import (defaults to the last segment)"
21764
+ }
21765
+ },
21766
+ "required": [
21767
+ "moduleName"
21768
+ ],
21769
+ "returns": "Promise<void>",
21770
+ "examples": [
21771
+ {
21772
+ "language": "ts",
21773
+ "code": "await python.importModule('json')\nawait python.importModule('myapp.models', 'models')\nconst result = await python.eval('models.User')"
21774
+ }
21775
+ ]
21776
+ },
21777
+ "call": {
21778
+ "description": "Calls a function by dotted path in the persistent session namespace.",
21779
+ "parameters": {
21780
+ "funcPath": {
21781
+ "type": "string",
21782
+ "description": "Dotted path to the function (e.g. 'json.dumps' or 'my_func')"
21783
+ },
21784
+ "args": {
21785
+ "type": "any[]",
21786
+ "description": "Positional arguments"
21787
+ },
21788
+ "kwargs": {
21789
+ "type": "Record<string, any>",
21790
+ "description": "Keyword arguments"
21791
+ }
21792
+ },
21793
+ "required": [
21794
+ "funcPath"
21795
+ ],
21796
+ "returns": "Promise<any>",
21797
+ "examples": [
21798
+ {
21799
+ "language": "ts",
21800
+ "code": "await python.importModule('json')\nconst result = await python.call('json.dumps', [{ a: 1 }], { indent: 2 })"
21801
+ }
21802
+ ]
21803
+ },
21804
+ "getLocals": {
21805
+ "description": "Returns all non-dunder variables from the persistent session namespace.",
21806
+ "parameters": {},
21807
+ "required": [],
21808
+ "returns": "Promise<Record<string, any>>",
21809
+ "examples": [
21810
+ {
21811
+ "language": "ts",
21812
+ "code": "await python.run('x = 42\\ny = \"hello\"')\nconst locals = await python.getLocals()\nconsole.log(locals) // { x: 42, y: 'hello' }"
21813
+ }
21814
+ ]
21815
+ },
21816
+ "resetSession": {
21817
+ "description": "Clears all variables and imports from the persistent session namespace. The session remains active — you can continue calling run() after reset.",
21818
+ "parameters": {},
21819
+ "required": [],
21820
+ "returns": "Promise<void>",
21821
+ "examples": [
21822
+ {
21823
+ "language": "ts",
21824
+ "code": "await python.run('x = 42')\nawait python.resetSession()\n// x is now undefined"
21825
+ }
21826
+ ]
21043
21827
  }
21044
21828
  },
21045
21829
  "getters": {
@@ -21096,6 +21880,21 @@ export const introspectionData = [
21096
21880
  "name": "fileExecuted",
21097
21881
  "description": "Event emitted by Python",
21098
21882
  "arguments": {}
21883
+ },
21884
+ "sessionError": {
21885
+ "name": "sessionError",
21886
+ "description": "Event emitted by Python",
21887
+ "arguments": {}
21888
+ },
21889
+ "sessionStarted": {
21890
+ "name": "sessionStarted",
21891
+ "description": "Event emitted by Python",
21892
+ "arguments": {}
21893
+ },
21894
+ "sessionStopped": {
21895
+ "name": "sessionStopped",
21896
+ "description": "Event emitted by Python",
21897
+ "arguments": {}
21099
21898
  }
21100
21899
  },
21101
21900
  "state": {},
@@ -21104,9 +21903,38 @@ export const introspectionData = [
21104
21903
  "examples": [
21105
21904
  {
21106
21905
  "language": "ts",
21107
- "code": "const python = container.feature('python', { \n dir: \"/path/to/python/project\",\n contextScript: \"/path/to/setup-context.py\"\n})\n\n// Auto-install dependencies\nawait python.installDependencies()\n\n// Execute Python code\nconst result = await python.execute('print(\"Hello from Python!\")')\n\n// Execute with custom variables\nconst result2 = await python.execute('print(f\"Hello {name}!\")', { name: 'World' })"
21906
+ "code": "const python = container.feature('python', {\n dir: \"/path/to/python/project\",\n})\n\n// Stateless execution\nconst result = await python.execute('print(\"Hello from Python!\")')\n\n// Persistent session\nawait python.startSession()\nawait python.run('import myapp.models')\nawait python.run('users = myapp.models.User.objects.all()')\nconst result = await python.run('print(len(users))')\nawait python.stopSession()"
21108
21907
  }
21109
- ]
21908
+ ],
21909
+ "types": {
21910
+ "RunResult": {
21911
+ "description": "Result from a persistent session run() call.",
21912
+ "properties": {
21913
+ "ok": {
21914
+ "type": "boolean",
21915
+ "description": ""
21916
+ },
21917
+ "result": {
21918
+ "type": "any",
21919
+ "description": ""
21920
+ },
21921
+ "stdout": {
21922
+ "type": "string",
21923
+ "description": ""
21924
+ },
21925
+ "error": {
21926
+ "type": "string",
21927
+ "description": "",
21928
+ "optional": true
21929
+ },
21930
+ "traceback": {
21931
+ "type": "string",
21932
+ "description": "",
21933
+ "optional": true
21934
+ }
21935
+ }
21936
+ }
21937
+ }
21110
21938
  },
21111
21939
  {
21112
21940
  "id": "features.jsonTree",
@@ -21732,143 +22560,14 @@ export const introspectionData = [
21732
22560
  "description": "stdout mode: 'pipe' to capture output, 'inherit', or 'ignore' (default: 'pipe')",
21733
22561
  "optional": true
21734
22562
  },
21735
- "stderr": {
21736
- "type": "'pipe' | 'inherit' | 'ignore' | null",
21737
- "description": "stderr mode: 'pipe' to capture errors, 'inherit', or 'ignore' (default: 'pipe')",
21738
- "optional": true
21739
- }
21740
- }
21741
- }
21742
- }
21743
- },
21744
- {
21745
- "id": "portExposer",
21746
- "description": "Port Exposer Feature Exposes local HTTP services via ngrok with SSL-enabled public URLs. Perfect for development, testing, and sharing local services securely. Features: - SSL-enabled public URLs for local services - Custom subdomains and domains (with paid plans) - Authentication options (basic auth, OAuth) - Regional endpoint selection - Connection state management",
21747
- "shortcut": "portExposer",
21748
- "className": "PortExposer",
21749
- "methods": {
21750
- "expose": {
21751
- "description": "Expose the local port via ngrok. Creates an ngrok tunnel to the specified local port and returns the SSL-enabled public URL. Emits `exposed` on success or `error` on failure.",
21752
- "parameters": {
21753
- "port": {
21754
- "type": "number",
21755
- "description": "Optional port override; falls back to `options.port`"
21756
- }
21757
- },
21758
- "required": [],
21759
- "returns": "Promise<string>",
21760
- "examples": [
21761
- {
21762
- "language": "ts",
21763
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nconst url = await exposer.expose()\nconsole.log(`Public URL: ${url}`)\n\n// Override port at call time\nconst url2 = await exposer.expose(8080)"
21764
- }
21765
- ]
21766
- },
21767
- "close": {
21768
- "description": "Stop exposing the port and close the ngrok tunnel. Tears down the ngrok listener, resets connection state, and emits `closed`. Safe to call when no tunnel is active (no-op).",
21769
- "parameters": {},
21770
- "required": [],
21771
- "returns": "Promise<void>",
21772
- "examples": [
21773
- {
21774
- "language": "ts",
21775
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\n// ... later\nawait exposer.close()\nconsole.log(exposer.isConnected()) // false"
21776
- }
21777
- ]
21778
- },
21779
- "getPublicUrl": {
21780
- "description": "Get the current public URL if connected. Returns the live URL from the ngrok listener, or `undefined` if no tunnel is active.",
21781
- "parameters": {},
21782
- "required": [],
21783
- "returns": "string | undefined",
21784
- "examples": [
21785
- {
21786
- "language": "ts",
21787
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nconsole.log(exposer.getPublicUrl()) // 'https://abc123.ngrok.io'"
21788
- }
21789
- ]
21790
- },
21791
- "isConnected": {
21792
- "description": "Check if the ngrok tunnel is currently connected.",
21793
- "parameters": {},
21794
- "required": [],
21795
- "returns": "boolean",
21796
- "examples": [
21797
- {
21798
- "language": "ts",
21799
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nconsole.log(exposer.isConnected()) // false\nawait exposer.expose()\nconsole.log(exposer.isConnected()) // true"
21800
- }
21801
- ]
21802
- },
21803
- "getConnectionInfo": {
21804
- "description": "Get a snapshot of the current connection information. Returns an object with the tunnel's connected status, public URL, local port, connection timestamp, and session metadata.",
21805
- "parameters": {},
21806
- "required": [],
21807
- "returns": "void",
21808
- "examples": [
21809
- {
21810
- "language": "ts",
21811
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nconst info = exposer.getConnectionInfo()\nconsole.log(info.publicUrl, info.localPort, info.connectedAt)"
21812
- }
21813
- ]
21814
- },
21815
- "reconnect": {
21816
- "description": "Close the existing tunnel and re-expose with optionally updated options. Calls `close()` first, merges any new options, then calls `expose()`.",
21817
- "parameters": {
21818
- "newOptions": {
21819
- "type": "Partial<PortExposerOptions>",
21820
- "description": "Optional partial options to merge before reconnecting"
21821
- }
21822
- },
21823
- "required": [],
21824
- "returns": "Promise<string>",
21825
- "examples": [
21826
- {
21827
- "language": "ts",
21828
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\n// Switch to a different port\nconst newUrl = await exposer.reconnect({ port: 8080 })"
21829
- }
21830
- ]
21831
- },
21832
- "disable": {
21833
- "description": "Disable the feature, ensuring the ngrok tunnel is closed first. Overrides the base `disable()` to guarantee that the tunnel is torn down before the feature is marked as disabled.",
21834
- "parameters": {},
21835
- "required": [],
21836
- "returns": "Promise<this>",
21837
- "examples": [
21838
- {
21839
- "language": "ts",
21840
- "code": "const exposer = container.feature('portExposer', { port: 3000 })\nawait exposer.expose()\nawait exposer.disable()"
21841
- }
21842
- ]
21843
- }
21844
- },
21845
- "getters": {},
21846
- "events": {
21847
- "exposed": {
21848
- "name": "exposed",
21849
- "description": "Event emitted by PortExposer",
21850
- "arguments": {}
21851
- },
21852
- "error": {
21853
- "name": "error",
21854
- "description": "Event emitted by PortExposer",
21855
- "arguments": {}
21856
- },
21857
- "closed": {
21858
- "name": "closed",
21859
- "description": "Event emitted by PortExposer",
21860
- "arguments": {}
21861
- }
21862
- },
21863
- "state": {},
21864
- "options": {},
21865
- "envVars": [],
21866
- "examples": [
21867
- {
21868
- "language": "ts",
21869
- "code": "// Basic usage\nconst exposer = container.feature('portExposer', { port: 3000 })\nconst url = await exposer.expose()\nconsole.log(`Service available at: ${url}`)\n\n// With custom subdomain\nconst exposer = container.feature('portExposer', {\n port: 8080,\n subdomain: 'my-app',\n authToken: 'your-ngrok-token'\n})"
22563
+ "stderr": {
22564
+ "type": "'pipe' | 'inherit' | 'ignore' | null",
22565
+ "description": "stderr mode: 'pipe' to capture errors, 'inherit', or 'ignore' (default: 'pipe')",
22566
+ "optional": true
22567
+ }
22568
+ }
21870
22569
  }
21871
- ]
22570
+ }
21872
22571
  },
21873
22572
  {
21874
22573
  "id": "features.googleSheets",
@@ -25036,286 +25735,23 @@ export const introspectionData = [
25036
25735
  "image": {
25037
25736
  "name": "image",
25038
25737
  "description": "Event emitted by OpenAIClient",
25039
- "arguments": {}
25040
- },
25041
- "models": {
25042
- "name": "models",
25043
- "description": "Event emitted by OpenAIClient",
25044
- "arguments": {}
25045
- }
25046
- },
25047
- "state": {},
25048
- "options": {},
25049
- "envVars": [],
25050
- "examples": [
25051
- {
25052
- "language": "ts",
25053
- "code": "const openai = container.client('openai', { defaultModel: 'gpt-4o' })\nconst answer = await openai.ask('What is the meaning of life?')\nconsole.log(answer)"
25054
- }
25055
- ]
25056
- },
25057
- {
25058
- "id": "clients.elevenlabs",
25059
- "description": "ElevenLabs client — text-to-speech synthesis via the ElevenLabs REST API. Provides methods for listing voices, listing models, and generating speech audio. Audio is returned as a Buffer; use `say()` for a convenience method that writes to disk.",
25060
- "shortcut": "clients.elevenlabs",
25061
- "className": "ElevenLabsClient",
25062
- "methods": {
25063
- "beforeRequest": {
25064
- "description": "Inject the xi-api-key header before each request.",
25065
- "parameters": {},
25066
- "required": [],
25067
- "returns": "void"
25068
- },
25069
- "connect": {
25070
- "description": "Validate the API key by listing available models.",
25071
- "parameters": {},
25072
- "required": [],
25073
- "returns": "Promise<this>",
25074
- "examples": [
25075
- {
25076
- "language": "ts",
25077
- "code": "await el.connect()"
25078
- }
25079
- ]
25080
- },
25081
- "listVoices": {
25082
- "description": "List available voices with optional search and filtering.",
25083
- "parameters": {
25084
- "options": {
25085
- "type": "{\n search?: string\n category?: string\n voice_type?: string\n page_size?: number\n next_page_token?: string\n }",
25086
- "description": "Query parameters for filtering voices"
25087
- }
25088
- },
25089
- "required": [],
25090
- "returns": "Promise<any>",
25091
- "examples": [
25092
- {
25093
- "language": "ts",
25094
- "code": "const voices = await el.listVoices()\nconst premade = await el.listVoices({ category: 'premade' })"
25095
- }
25096
- ]
25097
- },
25098
- "getVoice": {
25099
- "description": "Get details for a single voice.",
25100
- "parameters": {
25101
- "voiceId": {
25102
- "type": "string",
25103
- "description": "The voice ID to look up"
25104
- }
25105
- },
25106
- "required": [
25107
- "voiceId"
25108
- ],
25109
- "returns": "Promise<any>",
25110
- "examples": [
25111
- {
25112
- "language": "ts",
25113
- "code": "const voice = await el.getVoice('21m00Tcm4TlvDq8ikWAM')\nconsole.log(voice.name, voice.settings)"
25114
- }
25115
- ]
25116
- },
25117
- "listModels": {
25118
- "description": "List available TTS models.",
25119
- "parameters": {},
25120
- "required": [],
25121
- "returns": "Promise<any[]>",
25122
- "examples": [
25123
- {
25124
- "language": "ts",
25125
- "code": "const models = await el.listModels()\nconsole.log(models.map(m => m.model_id))"
25126
- }
25127
- ]
25128
- },
25129
- "synthesize": {
25130
- "description": "Synthesize speech from text, returning audio as a Buffer.",
25131
- "parameters": {
25132
- "text": {
25133
- "type": "string",
25134
- "description": "The text to convert to speech"
25135
- },
25136
- "options": {
25137
- "type": "SynthesizeOptions",
25138
- "description": "Voice, model, format, and voice settings overrides",
25139
- "properties": {
25140
- "voiceId": {
25141
- "type": "string",
25142
- "description": ""
25143
- },
25144
- "modelId": {
25145
- "type": "string",
25146
- "description": ""
25147
- },
25148
- "outputFormat": {
25149
- "type": "string",
25150
- "description": ""
25151
- },
25152
- "voiceSettings": {
25153
- "type": "ElevenLabsVoiceSettings",
25154
- "description": ""
25155
- },
25156
- "disableCache": {
25157
- "type": "boolean",
25158
- "description": ""
25159
- }
25160
- }
25161
- }
25162
- },
25163
- "required": [
25164
- "text"
25165
- ],
25166
- "returns": "Promise<Buffer>",
25167
- "examples": [
25168
- {
25169
- "language": "ts",
25170
- "code": "const audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data\n\nconst custom = await el.synthesize('Hello', {\n voiceId: '21m00Tcm4TlvDq8ikWAM',\n voiceSettings: { stability: 0.5, similarityBoost: 0.8 }\n})"
25171
- }
25172
- ]
25173
- },
25174
- "say": {
25175
- "description": "Synthesize speech and write the audio to a file.",
25176
- "parameters": {
25177
- "text": {
25178
- "type": "string",
25179
- "description": "The text to convert to speech"
25180
- },
25181
- "outputPath": {
25182
- "type": "string",
25183
- "description": "File path to write the audio to"
25184
- },
25185
- "options": {
25186
- "type": "SynthesizeOptions",
25187
- "description": "Voice, model, format, and voice settings overrides",
25188
- "properties": {
25189
- "voiceId": {
25190
- "type": "string",
25191
- "description": ""
25192
- },
25193
- "modelId": {
25194
- "type": "string",
25195
- "description": ""
25196
- },
25197
- "outputFormat": {
25198
- "type": "string",
25199
- "description": ""
25200
- },
25201
- "voiceSettings": {
25202
- "type": "ElevenLabsVoiceSettings",
25203
- "description": ""
25204
- },
25205
- "disableCache": {
25206
- "type": "boolean",
25207
- "description": ""
25208
- }
25209
- }
25210
- }
25211
- },
25212
- "required": [
25213
- "text",
25214
- "outputPath"
25215
- ],
25216
- "returns": "Promise<string>",
25217
- "examples": [
25218
- {
25219
- "language": "ts",
25220
- "code": "const path = await el.say('Hello world', './hello.mp3')\nconsole.log(`Audio saved to ${path}`)"
25221
- }
25222
- ]
25223
- }
25224
- },
25225
- "getters": {
25226
- "apiKey": {
25227
- "description": "The resolved API key from options or environment.",
25228
- "returns": "string"
25229
- }
25230
- },
25231
- "events": {
25232
- "failure": {
25233
- "name": "failure",
25234
- "description": "Event emitted by ElevenLabsClient",
25235
- "arguments": {}
25236
- },
25237
- "voices": {
25238
- "name": "voices",
25239
- "description": "Event emitted by ElevenLabsClient",
25240
- "arguments": {}
25241
- },
25242
- "speech": {
25243
- "name": "speech",
25244
- "description": "Event emitted by ElevenLabsClient",
25245
- "arguments": {}
25246
- }
25247
- },
25248
- "state": {},
25249
- "options": {},
25250
- "envVars": [],
25251
- "examples": [
25252
- {
25253
- "language": "ts",
25254
- "code": "const el = container.client('elevenlabs')\nawait el.connect()\nconst voices = await el.listVoices()\nconst audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data"
25255
- }
25256
- ],
25257
- "types": {
25258
- "SynthesizeOptions": {
25259
- "description": "",
25260
- "properties": {
25261
- "voiceId": {
25262
- "type": "string",
25263
- "description": "",
25264
- "optional": true
25265
- },
25266
- "modelId": {
25267
- "type": "string",
25268
- "description": "",
25269
- "optional": true
25270
- },
25271
- "outputFormat": {
25272
- "type": "string",
25273
- "description": "",
25274
- "optional": true
25275
- },
25276
- "voiceSettings": {
25277
- "type": "ElevenLabsVoiceSettings",
25278
- "description": "",
25279
- "optional": true
25280
- },
25281
- "disableCache": {
25282
- "type": "boolean",
25283
- "description": "",
25284
- "optional": true
25285
- }
25286
- }
25738
+ "arguments": {}
25287
25739
  },
25288
- "ElevenLabsVoiceSettings": {
25289
- "description": "",
25290
- "properties": {
25291
- "stability": {
25292
- "type": "number",
25293
- "description": "",
25294
- "optional": true
25295
- },
25296
- "similarityBoost": {
25297
- "type": "number",
25298
- "description": "",
25299
- "optional": true
25300
- },
25301
- "style": {
25302
- "type": "number",
25303
- "description": "",
25304
- "optional": true
25305
- },
25306
- "speed": {
25307
- "type": "number",
25308
- "description": "",
25309
- "optional": true
25310
- },
25311
- "useSpeakerBoost": {
25312
- "type": "boolean",
25313
- "description": "",
25314
- "optional": true
25315
- }
25316
- }
25740
+ "models": {
25741
+ "name": "models",
25742
+ "description": "Event emitted by OpenAIClient",
25743
+ "arguments": {}
25317
25744
  }
25318
- }
25745
+ },
25746
+ "state": {},
25747
+ "options": {},
25748
+ "envVars": [],
25749
+ "examples": [
25750
+ {
25751
+ "language": "ts",
25752
+ "code": "const openai = container.client('openai', { defaultModel: 'gpt-4o' })\nconst answer = await openai.ask('What is the meaning of life?')\nconsole.log(answer)"
25753
+ }
25754
+ ]
25319
25755
  },
25320
25756
  {
25321
25757
  "id": "clients.supabase",
@@ -25647,109 +26083,355 @@ export const introspectionData = [
25647
26083
  "required": [],
25648
26084
  "returns": "Promise<void>"
25649
26085
  },
25650
- "disconnectWs": {
25651
- "description": "Close the WebSocket connection.",
26086
+ "disconnectWs": {
26087
+ "description": "Close the WebSocket connection.",
26088
+ "parameters": {},
26089
+ "required": [],
26090
+ "returns": "void"
26091
+ },
26092
+ "toApiFormat": {
26093
+ "description": "Convert a UI-format workflow to the API format that /prompt expects. Requires a running ComfyUI instance to fetch `object_info` so we can map positional `widgets_values` to their named input fields. If the workflow is already in API format, it's returned as-is.",
26094
+ "parameters": {
26095
+ "workflow": {
26096
+ "type": "Record<string, any>",
26097
+ "description": "Parameter workflow"
26098
+ }
26099
+ },
26100
+ "required": [
26101
+ "workflow"
26102
+ ],
26103
+ "returns": "Promise<Record<string, any>>"
26104
+ },
26105
+ "runWorkflow": {
26106
+ "description": "Run a ComfyUI workflow with optional runtime input overrides. Inputs can be provided in two forms: **Direct node mapping** (when no `inputMap` in options): ``` { '3': { seed: 42 }, '6': { text: 'a cat' } } ``` **Named inputs** (when `inputMap` is provided in options): ``` inputs: { positive_prompt: 'a cat', seed: 42 } options.inputMap: { positive_prompt: { nodeId: '6', field: 'text' }, seed: { nodeId: '3', field: 'seed' } } ```",
26107
+ "parameters": {
26108
+ "workflow": {
26109
+ "type": "Record<string, any>",
26110
+ "description": "Parameter workflow"
26111
+ },
26112
+ "inputs": {
26113
+ "type": "Record<string, any>",
26114
+ "description": "Parameter inputs"
26115
+ },
26116
+ "options": {
26117
+ "type": "WorkflowRunOptions",
26118
+ "description": "Parameter options",
26119
+ "properties": {
26120
+ "poll": {
26121
+ "type": "boolean",
26122
+ "description": "Use polling instead of WebSocket for tracking execution"
26123
+ },
26124
+ "pollInterval": {
26125
+ "type": "number",
26126
+ "description": "Polling interval in ms (default 1000)"
26127
+ },
26128
+ "inputMap": {
26129
+ "type": "InputMapping",
26130
+ "description": "Named input mapping: semantic name -> { nodeId, field }"
26131
+ },
26132
+ "outputDir": {
26133
+ "type": "string",
26134
+ "description": "If provided, output images are downloaded to this directory"
26135
+ }
26136
+ }
26137
+ }
26138
+ },
26139
+ "required": [
26140
+ "workflow"
26141
+ ],
26142
+ "returns": "Promise<WorkflowResult>"
26143
+ }
26144
+ },
26145
+ "getters": {
26146
+ "clientId": {
26147
+ "description": "The unique client ID used for WebSocket session tracking.",
26148
+ "returns": "string"
26149
+ },
26150
+ "wsURL": {
26151
+ "description": "The WebSocket URL derived from baseURL or overridden via options.",
26152
+ "returns": "string"
26153
+ }
26154
+ },
26155
+ "events": {
26156
+ "execution_start": {
26157
+ "name": "execution_start",
26158
+ "description": "Event emitted by ComfyUIClient",
26159
+ "arguments": {}
26160
+ },
26161
+ "execution_complete": {
26162
+ "name": "execution_complete",
26163
+ "description": "Event emitted by ComfyUIClient",
26164
+ "arguments": {}
26165
+ },
26166
+ "executing": {
26167
+ "name": "executing",
26168
+ "description": "Event emitted by ComfyUIClient",
26169
+ "arguments": {}
26170
+ },
26171
+ "progress": {
26172
+ "name": "progress",
26173
+ "description": "Event emitted by ComfyUIClient",
26174
+ "arguments": {}
26175
+ },
26176
+ "executed": {
26177
+ "name": "executed",
26178
+ "description": "Event emitted by ComfyUIClient",
26179
+ "arguments": {}
26180
+ },
26181
+ "execution_cached": {
26182
+ "name": "execution_cached",
26183
+ "description": "Event emitted by ComfyUIClient",
26184
+ "arguments": {}
26185
+ },
26186
+ "execution_error": {
26187
+ "name": "execution_error",
26188
+ "description": "Event emitted by ComfyUIClient",
26189
+ "arguments": {}
26190
+ }
26191
+ },
26192
+ "state": {},
26193
+ "options": {},
26194
+ "envVars": [],
26195
+ "examples": [
26196
+ {
26197
+ "language": "ts",
26198
+ "code": "const comfy = container.client('comfyui', { baseURL: 'http://localhost:8188' })\nconst result = await comfy.runWorkflow(workflow, {\n '6': { text: 'a beautiful sunset' }\n})\nconsole.log(result.images)"
26199
+ }
26200
+ ],
26201
+ "types": {
26202
+ "WorkflowRunOptions": {
26203
+ "description": "",
26204
+ "properties": {
26205
+ "poll": {
26206
+ "type": "boolean",
26207
+ "description": "Use polling instead of WebSocket for tracking execution",
26208
+ "optional": true
26209
+ },
26210
+ "pollInterval": {
26211
+ "type": "number",
26212
+ "description": "Polling interval in ms (default 1000)",
26213
+ "optional": true
26214
+ },
26215
+ "inputMap": {
26216
+ "type": "InputMapping",
26217
+ "description": "Named input mapping: semantic name -> { nodeId, field }",
26218
+ "optional": true
26219
+ },
26220
+ "outputDir": {
26221
+ "type": "string",
26222
+ "description": "If provided, output images are downloaded to this directory",
26223
+ "optional": true
26224
+ }
26225
+ }
26226
+ },
26227
+ "WorkflowResult": {
26228
+ "description": "",
26229
+ "properties": {
26230
+ "promptId": {
26231
+ "type": "string",
26232
+ "description": ""
26233
+ },
26234
+ "outputs": {
26235
+ "type": "Record<string, any>",
26236
+ "description": ""
26237
+ },
26238
+ "images": {
26239
+ "type": "Array<{ filename: string; subfolder: string; type: string; localPath?: string }>",
26240
+ "description": "",
26241
+ "optional": true
26242
+ }
26243
+ }
26244
+ }
26245
+ }
26246
+ },
26247
+ {
26248
+ "id": "clients.elevenlabs",
26249
+ "description": "ElevenLabs client — text-to-speech synthesis via the ElevenLabs REST API. Provides methods for listing voices, listing models, and generating speech audio. Audio is returned as a Buffer; use `say()` for a convenience method that writes to disk.",
26250
+ "shortcut": "clients.elevenlabs",
26251
+ "className": "ElevenLabsClient",
26252
+ "methods": {
26253
+ "beforeRequest": {
26254
+ "description": "Inject the xi-api-key header before each request.",
26255
+ "parameters": {},
26256
+ "required": [],
26257
+ "returns": "void"
26258
+ },
26259
+ "connect": {
26260
+ "description": "Validate the API key by listing available models.",
26261
+ "parameters": {},
26262
+ "required": [],
26263
+ "returns": "Promise<this>",
26264
+ "examples": [
26265
+ {
26266
+ "language": "ts",
26267
+ "code": "await el.connect()"
26268
+ }
26269
+ ]
26270
+ },
26271
+ "listVoices": {
26272
+ "description": "List available voices with optional search and filtering.",
26273
+ "parameters": {
26274
+ "options": {
26275
+ "type": "{\n search?: string\n category?: string\n voice_type?: string\n page_size?: number\n next_page_token?: string\n }",
26276
+ "description": "Query parameters for filtering voices"
26277
+ }
26278
+ },
26279
+ "required": [],
26280
+ "returns": "Promise<any>",
26281
+ "examples": [
26282
+ {
26283
+ "language": "ts",
26284
+ "code": "const voices = await el.listVoices()\nconst premade = await el.listVoices({ category: 'premade' })"
26285
+ }
26286
+ ]
26287
+ },
26288
+ "getVoice": {
26289
+ "description": "Get details for a single voice.",
26290
+ "parameters": {
26291
+ "voiceId": {
26292
+ "type": "string",
26293
+ "description": "The voice ID to look up"
26294
+ }
26295
+ },
26296
+ "required": [
26297
+ "voiceId"
26298
+ ],
26299
+ "returns": "Promise<any>",
26300
+ "examples": [
26301
+ {
26302
+ "language": "ts",
26303
+ "code": "const voice = await el.getVoice('21m00Tcm4TlvDq8ikWAM')\nconsole.log(voice.name, voice.settings)"
26304
+ }
26305
+ ]
26306
+ },
26307
+ "listModels": {
26308
+ "description": "List available TTS models.",
25652
26309
  "parameters": {},
25653
26310
  "required": [],
25654
- "returns": "void"
26311
+ "returns": "Promise<any[]>",
26312
+ "examples": [
26313
+ {
26314
+ "language": "ts",
26315
+ "code": "const models = await el.listModels()\nconsole.log(models.map(m => m.model_id))"
26316
+ }
26317
+ ]
25655
26318
  },
25656
- "toApiFormat": {
25657
- "description": "Convert a UI-format workflow to the API format that /prompt expects. Requires a running ComfyUI instance to fetch `object_info` so we can map positional `widgets_values` to their named input fields. If the workflow is already in API format, it's returned as-is.",
26319
+ "synthesize": {
26320
+ "description": "Synthesize speech from text, returning audio as a Buffer.",
25658
26321
  "parameters": {
25659
- "workflow": {
25660
- "type": "Record<string, any>",
25661
- "description": "Parameter workflow"
26322
+ "text": {
26323
+ "type": "string",
26324
+ "description": "The text to convert to speech"
26325
+ },
26326
+ "options": {
26327
+ "type": "SynthesizeOptions",
26328
+ "description": "Voice, model, format, and voice settings overrides",
26329
+ "properties": {
26330
+ "voiceId": {
26331
+ "type": "string",
26332
+ "description": ""
26333
+ },
26334
+ "modelId": {
26335
+ "type": "string",
26336
+ "description": ""
26337
+ },
26338
+ "outputFormat": {
26339
+ "type": "string",
26340
+ "description": ""
26341
+ },
26342
+ "voiceSettings": {
26343
+ "type": "ElevenLabsVoiceSettings",
26344
+ "description": ""
26345
+ },
26346
+ "disableCache": {
26347
+ "type": "boolean",
26348
+ "description": ""
26349
+ }
26350
+ }
25662
26351
  }
25663
26352
  },
25664
26353
  "required": [
25665
- "workflow"
26354
+ "text"
25666
26355
  ],
25667
- "returns": "Promise<Record<string, any>>"
26356
+ "returns": "Promise<Buffer>",
26357
+ "examples": [
26358
+ {
26359
+ "language": "ts",
26360
+ "code": "const audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data\n\nconst custom = await el.synthesize('Hello', {\n voiceId: '21m00Tcm4TlvDq8ikWAM',\n voiceSettings: { stability: 0.5, similarityBoost: 0.8 }\n})"
26361
+ }
26362
+ ]
25668
26363
  },
25669
- "runWorkflow": {
25670
- "description": "Run a ComfyUI workflow with optional runtime input overrides. Inputs can be provided in two forms: **Direct node mapping** (when no `inputMap` in options): ``` { '3': { seed: 42 }, '6': { text: 'a cat' } } ``` **Named inputs** (when `inputMap` is provided in options): ``` inputs: { positive_prompt: 'a cat', seed: 42 } options.inputMap: { positive_prompt: { nodeId: '6', field: 'text' }, seed: { nodeId: '3', field: 'seed' } } ```",
26364
+ "say": {
26365
+ "description": "Synthesize speech and write the audio to a file.",
25671
26366
  "parameters": {
25672
- "workflow": {
25673
- "type": "Record<string, any>",
25674
- "description": "Parameter workflow"
26367
+ "text": {
26368
+ "type": "string",
26369
+ "description": "The text to convert to speech"
25675
26370
  },
25676
- "inputs": {
25677
- "type": "Record<string, any>",
25678
- "description": "Parameter inputs"
26371
+ "outputPath": {
26372
+ "type": "string",
26373
+ "description": "File path to write the audio to"
25679
26374
  },
25680
26375
  "options": {
25681
- "type": "WorkflowRunOptions",
25682
- "description": "Parameter options",
26376
+ "type": "SynthesizeOptions",
26377
+ "description": "Voice, model, format, and voice settings overrides",
25683
26378
  "properties": {
25684
- "poll": {
25685
- "type": "boolean",
25686
- "description": "Use polling instead of WebSocket for tracking execution"
25687
- },
25688
- "pollInterval": {
25689
- "type": "number",
25690
- "description": "Polling interval in ms (default 1000)"
26379
+ "voiceId": {
26380
+ "type": "string",
26381
+ "description": ""
25691
26382
  },
25692
- "inputMap": {
25693
- "type": "InputMapping",
25694
- "description": "Named input mapping: semantic name -> { nodeId, field }"
26383
+ "modelId": {
26384
+ "type": "string",
26385
+ "description": ""
25695
26386
  },
25696
- "outputDir": {
26387
+ "outputFormat": {
25697
26388
  "type": "string",
25698
- "description": "If provided, output images are downloaded to this directory"
26389
+ "description": ""
26390
+ },
26391
+ "voiceSettings": {
26392
+ "type": "ElevenLabsVoiceSettings",
26393
+ "description": ""
26394
+ },
26395
+ "disableCache": {
26396
+ "type": "boolean",
26397
+ "description": ""
25699
26398
  }
25700
26399
  }
25701
26400
  }
25702
26401
  },
25703
26402
  "required": [
25704
- "workflow"
26403
+ "text",
26404
+ "outputPath"
25705
26405
  ],
25706
- "returns": "Promise<WorkflowResult>"
26406
+ "returns": "Promise<string>",
26407
+ "examples": [
26408
+ {
26409
+ "language": "ts",
26410
+ "code": "const path = await el.say('Hello world', './hello.mp3')\nconsole.log(`Audio saved to ${path}`)"
26411
+ }
26412
+ ]
25707
26413
  }
25708
26414
  },
25709
26415
  "getters": {
25710
- "clientId": {
25711
- "description": "The unique client ID used for WebSocket session tracking.",
25712
- "returns": "string"
25713
- },
25714
- "wsURL": {
25715
- "description": "The WebSocket URL derived from baseURL or overridden via options.",
26416
+ "apiKey": {
26417
+ "description": "The resolved API key from options or environment.",
25716
26418
  "returns": "string"
25717
26419
  }
25718
26420
  },
25719
26421
  "events": {
25720
- "execution_start": {
25721
- "name": "execution_start",
25722
- "description": "Event emitted by ComfyUIClient",
25723
- "arguments": {}
25724
- },
25725
- "execution_complete": {
25726
- "name": "execution_complete",
25727
- "description": "Event emitted by ComfyUIClient",
25728
- "arguments": {}
25729
- },
25730
- "executing": {
25731
- "name": "executing",
25732
- "description": "Event emitted by ComfyUIClient",
25733
- "arguments": {}
25734
- },
25735
- "progress": {
25736
- "name": "progress",
25737
- "description": "Event emitted by ComfyUIClient",
25738
- "arguments": {}
25739
- },
25740
- "executed": {
25741
- "name": "executed",
25742
- "description": "Event emitted by ComfyUIClient",
26422
+ "failure": {
26423
+ "name": "failure",
26424
+ "description": "Event emitted by ElevenLabsClient",
25743
26425
  "arguments": {}
25744
26426
  },
25745
- "execution_cached": {
25746
- "name": "execution_cached",
25747
- "description": "Event emitted by ComfyUIClient",
26427
+ "voices": {
26428
+ "name": "voices",
26429
+ "description": "Event emitted by ElevenLabsClient",
25748
26430
  "arguments": {}
25749
26431
  },
25750
- "execution_error": {
25751
- "name": "execution_error",
25752
- "description": "Event emitted by ComfyUIClient",
26432
+ "speech": {
26433
+ "name": "speech",
26434
+ "description": "Event emitted by ElevenLabsClient",
25753
26435
  "arguments": {}
25754
26436
  }
25755
26437
  },
@@ -25759,48 +26441,65 @@ export const introspectionData = [
25759
26441
  "examples": [
25760
26442
  {
25761
26443
  "language": "ts",
25762
- "code": "const comfy = container.client('comfyui', { baseURL: 'http://localhost:8188' })\nconst result = await comfy.runWorkflow(workflow, {\n '6': { text: 'a beautiful sunset' }\n})\nconsole.log(result.images)"
26444
+ "code": "const el = container.client('elevenlabs')\nawait el.connect()\nconst voices = await el.listVoices()\nconst audio = await el.synthesize('Hello world')\n// audio is a Buffer of mp3 data"
25763
26445
  }
25764
26446
  ],
25765
26447
  "types": {
25766
- "WorkflowRunOptions": {
26448
+ "SynthesizeOptions": {
25767
26449
  "description": "",
25768
26450
  "properties": {
25769
- "poll": {
25770
- "type": "boolean",
25771
- "description": "Use polling instead of WebSocket for tracking execution",
26451
+ "voiceId": {
26452
+ "type": "string",
26453
+ "description": "",
25772
26454
  "optional": true
25773
26455
  },
25774
- "pollInterval": {
25775
- "type": "number",
25776
- "description": "Polling interval in ms (default 1000)",
26456
+ "modelId": {
26457
+ "type": "string",
26458
+ "description": "",
25777
26459
  "optional": true
25778
26460
  },
25779
- "inputMap": {
25780
- "type": "InputMapping",
25781
- "description": "Named input mapping: semantic name -> { nodeId, field }",
26461
+ "outputFormat": {
26462
+ "type": "string",
26463
+ "description": "",
25782
26464
  "optional": true
25783
26465
  },
25784
- "outputDir": {
25785
- "type": "string",
25786
- "description": "If provided, output images are downloaded to this directory",
26466
+ "voiceSettings": {
26467
+ "type": "ElevenLabsVoiceSettings",
26468
+ "description": "",
26469
+ "optional": true
26470
+ },
26471
+ "disableCache": {
26472
+ "type": "boolean",
26473
+ "description": "",
25787
26474
  "optional": true
25788
26475
  }
25789
26476
  }
25790
26477
  },
25791
- "WorkflowResult": {
26478
+ "ElevenLabsVoiceSettings": {
25792
26479
  "description": "",
25793
26480
  "properties": {
25794
- "promptId": {
25795
- "type": "string",
25796
- "description": ""
26481
+ "stability": {
26482
+ "type": "number",
26483
+ "description": "",
26484
+ "optional": true
25797
26485
  },
25798
- "outputs": {
25799
- "type": "Record<string, any>",
25800
- "description": ""
26486
+ "similarityBoost": {
26487
+ "type": "number",
26488
+ "description": "",
26489
+ "optional": true
25801
26490
  },
25802
- "images": {
25803
- "type": "Array<{ filename: string; subfolder: string; type: string; localPath?: string }>",
26491
+ "style": {
26492
+ "type": "number",
26493
+ "description": "",
26494
+ "optional": true
26495
+ },
26496
+ "speed": {
26497
+ "type": "number",
26498
+ "description": "",
26499
+ "optional": true
26500
+ },
26501
+ "useSpeakerBoost": {
26502
+ "type": "boolean",
25804
26503
  "description": "",
25805
26504
  "optional": true
25806
26505
  }
@@ -26539,7 +27238,7 @@ export const containerIntrospectionData = [
26539
27238
  }
26540
27239
  ]
26541
27240
  },
26542
- "inspect": {
27241
+ "introspect": {
26543
27242
  "description": "Returns a full introspection object for this container, merging build-time AST data (JSDoc descriptions, methods, getters) with runtime data (registries, factories, state, environment).",
26544
27243
  "parameters": {},
26545
27244
  "required": [],
@@ -26547,11 +27246,11 @@ export const containerIntrospectionData = [
26547
27246
  "examples": [
26548
27247
  {
26549
27248
  "language": "ts",
26550
- "code": "const info = container.inspect()\nconsole.log(info.methods) // all public methods with descriptions\nconsole.log(info.getters) // all getters with return types\nconsole.log(info.registries) // features, clients, servers, etc."
27249
+ "code": "const info = container.introspect()\nconsole.log(info.methods) // all public methods with descriptions\nconsole.log(info.getters) // all getters with return types\nconsole.log(info.registries) // features, clients, servers, etc."
26551
27250
  }
26552
27251
  ]
26553
27252
  },
26554
- "inspectAsText": {
27253
+ "introspectAsText": {
26555
27254
  "description": "Returns a human-readable markdown representation of this container's introspection data. Useful in REPLs, AI agent contexts, or documentation generation. Pass a section name to render only that section (e.g. 'methods', 'getters', 'events', 'state').",
26556
27255
  "parameters": {
26557
27256
  "sectionOrDepth": {
@@ -26568,32 +27267,17 @@ export const containerIntrospectionData = [
26568
27267
  "examples": [
26569
27268
  {
26570
27269
  "language": "ts",
26571
- "code": "console.log(container.inspectAsText()) // full description\nconsole.log(container.inspectAsText('methods')) // just methods"
27270
+ "code": "console.log(container.introspectAsText()) // full description\nconsole.log(container.introspectAsText('methods')) // just methods"
26572
27271
  }
26573
27272
  ]
26574
27273
  },
26575
- "introspectAsText": {
26576
- "description": "Alias for inspectAsText.",
26577
- "parameters": {
26578
- "sectionOrDepth": {
26579
- "type": "IntrospectionSection | number",
26580
- "description": "Parameter sectionOrDepth"
26581
- },
26582
- "startHeadingDepth": {
26583
- "type": "number",
26584
- "description": "Parameter startHeadingDepth"
26585
- }
26586
- },
26587
- "required": [],
26588
- "returns": "string"
26589
- },
26590
27274
  "introspectAsJSON": {
26591
- "description": "Alias for inspect, returns JSON introspection data.",
27275
+ "description": "Returns JSON introspection data.",
26592
27276
  "parameters": {},
26593
27277
  "required": [],
26594
27278
  "returns": "ContainerIntrospection"
26595
27279
  },
26596
- "inspectAsType": {
27280
+ "introspectAsType": {
26597
27281
  "description": "Returns the container's introspection data formatted as a TypeScript interface declaration. Includes the container's own methods, getters, factories, and registered helper types.",
26598
27282
  "parameters": {},
26599
27283
  "required": [],
@@ -26601,16 +27285,10 @@ export const containerIntrospectionData = [
26601
27285
  "examples": [
26602
27286
  {
26603
27287
  "language": "ts",
26604
- "code": "console.log(container.inspectAsType())\n// interface NodeContainer {\n// feature<T>(id: string, options?: object): T;\n// readonly uuid: string;\n// ...\n// }"
27288
+ "code": "console.log(container.introspectAsType())\n// interface NodeContainer {\n// feature<T>(id: string, options?: object): T;\n// readonly uuid: string;\n// ...\n// }"
26605
27289
  }
26606
27290
  ]
26607
27291
  },
26608
- "introspectAsType": {
26609
- "description": "",
26610
- "parameters": {},
26611
- "required": [],
26612
- "returns": "string"
26613
- },
26614
27292
  "sleep": {
26615
27293
  "description": "Sleep for the specified number of milliseconds. Useful for scripting and sequencing.",
26616
27294
  "parameters": {