@soederpop/luca 0.0.3 → 0.0.4

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 (113) hide show
  1. package/AGENTS.md +98 -0
  2. package/CLAUDE.md +27 -0
  3. package/SPEC.md +304 -0
  4. package/bun.lock +110 -265
  5. package/docs/CLI.md +1 -1
  6. package/docs/apis/features/node/content-db.md +16 -0
  7. package/docs/apis/features/node/fs.md +24 -0
  8. package/docs/apis/features/node/ipc-socket.md +0 -1
  9. package/docs/apis/features/node/package-finder.md +1 -11
  10. package/docs/apis/features/node/proc.md +0 -41
  11. package/docs/apis/features/node/ui.md +0 -2
  12. package/package.json +12 -8
  13. package/src/agi/container.server.ts +16 -3
  14. package/src/agi/features/assistant.ts +3 -7
  15. package/src/agi/features/assistants-manager.ts +3 -7
  16. package/src/agi/features/claude-code.ts +3 -7
  17. package/src/agi/features/conversation-history.ts +3 -7
  18. package/src/agi/features/conversation.ts +4 -8
  19. package/src/agi/features/openai-codex.ts +3 -7
  20. package/src/agi/features/openapi.ts +4 -2
  21. package/src/agi/features/skills-library.ts +4 -8
  22. package/src/cli/cli.ts +22 -0
  23. package/src/client.ts +69 -26
  24. package/src/clients/civitai/index.ts +3 -7
  25. package/src/clients/comfyui/index.ts +5 -9
  26. package/src/clients/elevenlabs/index.ts +39 -19
  27. package/src/clients/openai/index.ts +3 -7
  28. package/src/clients/supabase/index.ts +4 -13
  29. package/src/commands/console.ts +0 -3
  30. package/src/commands/eval.ts +1 -1
  31. package/src/commands/index.ts +1 -0
  32. package/src/commands/introspect.ts +128 -0
  33. package/src/commands/prompt.ts +1 -4
  34. package/src/commands/run.ts +6 -13
  35. package/src/commands/sandbox-mcp.ts +1 -13
  36. package/src/feature.ts +45 -2
  37. package/src/introspection/generated.agi.ts +175 -101
  38. package/src/introspection/generated.node.ts +175 -101
  39. package/src/introspection/generated.web.ts +113 -29
  40. package/src/introspection/index.ts +1 -1
  41. package/src/introspection/scan.ts +3 -1
  42. package/src/node/features/container-link.ts +3 -2
  43. package/src/node/features/content-db.ts +10 -2
  44. package/src/node/features/disk-cache.ts +3 -4
  45. package/src/node/features/dns.ts +3 -2
  46. package/src/node/features/docker.ts +3 -2
  47. package/src/node/features/downloader.ts +3 -16
  48. package/src/node/features/esbuild.ts +3 -12
  49. package/src/node/features/file-manager.ts +3 -2
  50. package/src/node/features/fs.ts +12 -3
  51. package/src/node/features/git.ts +3 -2
  52. package/src/node/features/google-auth.ts +3 -2
  53. package/src/node/features/google-calendar.ts +3 -2
  54. package/src/node/features/google-docs.ts +3 -2
  55. package/src/node/features/google-drive.ts +3 -2
  56. package/src/node/features/google-sheets.ts +3 -2
  57. package/src/node/features/grep.ts +3 -2
  58. package/src/node/features/helpers.ts +13 -2
  59. package/src/node/features/ink.ts +3 -3
  60. package/src/node/features/ipc-socket.ts +3 -3
  61. package/src/node/features/json-tree.ts +3 -21
  62. package/src/node/features/launcher-app-command-listener.ts +3 -2
  63. package/src/node/features/networking.ts +3 -2
  64. package/src/node/features/nlp.ts +3 -2
  65. package/src/node/features/opener.ts +8 -7
  66. package/src/node/features/os.ts +3 -2
  67. package/src/node/features/package-finder.ts +3 -2
  68. package/src/node/features/port-exposer.ts +3 -4
  69. package/src/node/features/postgres.ts +3 -3
  70. package/src/node/features/proc.ts +37 -64
  71. package/src/node/features/process-manager.ts +3 -2
  72. package/src/node/features/python.ts +3 -3
  73. package/src/node/features/repl.ts +3 -2
  74. package/src/node/features/runpod.ts +3 -3
  75. package/src/node/features/secure-shell.ts +3 -2
  76. package/src/node/features/semantic-search.ts +4 -6
  77. package/src/node/features/sqlite.ts +3 -3
  78. package/src/node/features/telegram.ts +3 -2
  79. package/src/node/features/tts.ts +3 -2
  80. package/src/node/features/ui.ts +3 -3
  81. package/src/node/features/vault.ts +3 -14
  82. package/src/node/features/vm.ts +41 -3
  83. package/src/node/features/window-manager.ts +165 -22
  84. package/src/node/features/yaml-tree.ts +3 -4
  85. package/src/node/features/yaml.ts +3 -2
  86. package/src/registry.ts +1 -1
  87. package/src/scaffolds/generated.ts +1 -1
  88. package/src/server.ts +43 -0
  89. package/src/servers/express.ts +24 -8
  90. package/src/servers/mcp.ts +2 -6
  91. package/src/servers/socket.ts +22 -7
  92. package/src/web/clients/socket.ts +3 -5
  93. package/src/web/features/asset-loader.ts +20 -12
  94. package/src/web/features/container-link.ts +3 -6
  95. package/src/web/features/esbuild.ts +21 -7
  96. package/src/web/features/helpers.ts +4 -2
  97. package/src/web/features/network.ts +24 -7
  98. package/src/web/features/speech.ts +24 -7
  99. package/src/web/features/vault.ts +21 -3
  100. package/src/web/features/vm.ts +20 -13
  101. package/src/web/features/voice-recognition.ts +26 -9
  102. package/commands/update-introspection.ts +0 -67
  103. package/docs/ideas/class-registration-refactor-possibilities.md +0 -197
  104. package/docs/ideas/container-use-api.md +0 -9
  105. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  106. package/docs/ideas/feature-stacks.md +0 -22
  107. package/docs/ideas/luca-cli-self-sufficiency-demo.md +0 -23
  108. package/docs/ideas/mcp-design.md +0 -9
  109. package/docs/ideas/web-container-debugging-feature.md +0 -13
  110. package/scripts/animations/chrome-glitch.ts +0 -55
  111. package/scripts/animations/index.ts +0 -16
  112. package/scripts/animations/neon-pulse.ts +0 -64
  113. package/scripts/animations/types.ts +0 -6
@@ -1,5 +1,5 @@
1
1
  // Auto-generated scaffold and MCP readme content
2
- // Generated at: 2026-03-07T16:53:46.702Z
2
+ // Generated at: 2026-03-10T18:58:23.358Z
3
3
  // Source: docs/scaffolds/*.md and docs/mcp/readme.md
4
4
  //
5
5
  // Do not edit manually. Run: luca build-scaffolds
package/src/server.ts CHANGED
@@ -29,6 +29,9 @@ export class Server<T extends ServerState = ServerState, K extends ServerOptions
29
29
  static override optionsSchema = ServerOptionsSchema
30
30
  static override eventsSchema = ServerEventsSchema
31
31
 
32
+ /** Self-register a Server subclass from a static initialization block. */
33
+ static register: (SubClass: typeof Server, id?: string) => typeof Server
34
+
32
35
  override get initialState() : T {
33
36
  return ({
34
37
  port: this.options.port || 3000,
@@ -133,3 +136,43 @@ export class ServersRegistry extends Registry<Server<any>> {
133
136
  export const servers = new ServersRegistry()
134
137
 
135
138
  export const helperCache = new Map()
139
+
140
+ /**
141
+ * Self-register a Server subclass from a static initialization block.
142
+ * IMPORTANT: Place the static block AFTER all static override declarations
143
+ * so schemas, envVars, and other metadata are set before interceptRegistration fires.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * export class ExpressServer extends Server {
148
+ * static override stateSchema = ServerStateSchema
149
+ * static override optionsSchema = ExpressServerOptionsSchema
150
+ * static { Server.register(this, 'express') } // must come last
151
+ * }
152
+ * ```
153
+ */
154
+ Server.register = function registerServer(
155
+ SubClass: typeof Server,
156
+ id?: string,
157
+ ) {
158
+ const registryId = id ?? SubClass.name[0]!.toLowerCase() + SubClass.name.slice(1)
159
+
160
+ // Auto-set shortcut if not explicitly overridden on this class
161
+ if (!Object.getOwnPropertyDescriptor(SubClass, 'shortcut')?.value ||
162
+ (SubClass as any).shortcut === 'unspecified') {
163
+ ;(SubClass as any).shortcut = `servers.${registryId}` as const
164
+ }
165
+
166
+ // Register in the servers registry (interceptRegistration sees all statics above)
167
+ servers.register(registryId, SubClass as any)
168
+
169
+ // Generate default attach() if not explicitly overridden on this class
170
+ if (!Object.getOwnPropertyDescriptor(SubClass, 'attach')) {
171
+ ;(SubClass as any).attach = (container: any) => {
172
+ servers.register(registryId, SubClass as any)
173
+ return container
174
+ }
175
+ }
176
+
177
+ return SubClass
178
+ }
@@ -1,10 +1,9 @@
1
- import type { NodeContainer } from '../node/container.js'
2
1
  import express from 'express'
3
2
  import type { Express } from 'express'
4
3
  import cors from 'cors'
5
4
  import { z } from 'zod'
6
5
  import { ServerStateSchema, ServerOptionsSchema } from '../schemas/base.js'
7
- import { servers, type StartOptions, Server, type ServersInterface, type ServerState } from '../server.js'
6
+ import { type StartOptions, Server, type ServerState } from '../server.js'
8
7
  import { Endpoint, type EndpointModule } from '../endpoint.js'
9
8
 
10
9
  declare module '../server' {
@@ -24,14 +23,33 @@ export type ExpressServerOptions = z.infer<typeof ExpressServerOptionsSchema>
24
23
 
25
24
  const defaultCreate = (app: Express, server: Server) => app
26
25
 
26
+ /**
27
+ * Express.js HTTP server with automatic endpoint mounting, CORS, and SPA history fallback.
28
+ *
29
+ * Wraps an Express application with convention-based endpoint discovery. Endpoints
30
+ * defined as modules are automatically mounted as routes. Supports static file serving,
31
+ * CORS configuration, and single-page app history fallback out of the box.
32
+ *
33
+ * @extends Server
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const server = container.server('express', { cors: true, static: './public' })
38
+ * await server.start({ port: 3000 })
39
+ *
40
+ * // Mount endpoints programmatically
41
+ * server.mount(myEndpoint)
42
+ *
43
+ * // Access the underlying Express app
44
+ * server.app.get('/health', (req, res) => res.json({ ok: true }))
45
+ * ```
46
+ */
27
47
  export class ExpressServer<T extends ServerState = ServerState, K extends ExpressServerOptions = ExpressServerOptions> extends Server<T,K> {
28
48
  static override shortcut = 'servers.express' as const
29
49
  static override stateSchema = ServerStateSchema
30
50
  static override optionsSchema = ExpressServerOptionsSchema
31
-
32
- static override attach(container: NodeContainer & ServersInterface) {
33
- return container
34
- }
51
+
52
+ static { Server.register(this, 'express') }
35
53
 
36
54
  _app?: Express
37
55
  _listener?: any
@@ -204,6 +222,4 @@ export class ExpressServer<T extends ServerState = ServerState, K extends Expres
204
222
  }
205
223
  }
206
224
 
207
- servers.register('express', ExpressServer)
208
-
209
225
  export default ExpressServer
@@ -1,7 +1,7 @@
1
1
  import type { NodeContainer } from '../node/container.js'
2
2
  import { z } from 'zod'
3
3
  import { MCPServerStateSchema, MCPServerOptionsSchema, MCPServerEventsSchema } from '../schemas/base.js'
4
- import { servers, Server, type ServersInterface, type ServerState } from '../server.js'
4
+ import { Server } from '../server.js'
5
5
  import { Server as MCPProtocolServer } from '@modelcontextprotocol/sdk/server/index.js'
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
7
7
  import {
@@ -297,9 +297,7 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
297
297
  static override optionsSchema = MCPServerOptionsSchema
298
298
  static override eventsSchema = MCPServerEventsSchema
299
299
 
300
- static override attach(container: NodeContainer & ServersInterface) {
301
- return container
302
- }
300
+ static { Server.register(this, 'mcp') }
303
301
 
304
302
  _mcpServer?: MCPProtocolServer
305
303
  _tools: Map<string, RegisteredTool> = new Map()
@@ -800,6 +798,4 @@ export class MCPServer extends Server<MCPServerState, MCPServerOptions> {
800
798
  }
801
799
  }
802
800
 
803
- servers.register('mcp', MCPServer)
804
-
805
801
  export default MCPServer
@@ -1,7 +1,6 @@
1
- import type { NodeContainer } from '../node/container.js'
2
1
  import { z } from 'zod'
3
2
  import { ServerStateSchema, ServerOptionsSchema } from '../schemas/base.js'
4
- import { type StartOptions, servers, Server, type ServersInterface, type ServerState } from '../server.js';
3
+ import { type StartOptions, Server, type ServerState } from '../server.js';
5
4
  import { WebSocketServer as BaseServer } from 'ws'
6
5
 
7
6
  declare module '../server' {
@@ -15,14 +14,32 @@ export const SocketServerOptionsSchema = ServerOptionsSchema.extend({
15
14
  })
16
15
  export type SocketServerOptions = z.infer<typeof SocketServerOptionsSchema>
17
16
 
17
+ /**
18
+ * WebSocket server built on the `ws` library with optional JSON message framing.
19
+ *
20
+ * Manages WebSocket connections, tracks connected clients, and bridges
21
+ * messages to Luca's event bus. When `json` mode is enabled, messages
22
+ * are automatically parsed and stringified.
23
+ *
24
+ * @extends Server
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const ws = container.server('websocket', { json: true })
29
+ * await ws.start({ port: 8080 })
30
+ *
31
+ * ws.on('message', (client, data) => {
32
+ * console.log('Received:', data)
33
+ * ws.broadcast({ echo: data })
34
+ * })
35
+ * ```
36
+ */
18
37
  export class WebsocketServer<T extends ServerState = ServerState, K extends SocketServerOptions = SocketServerOptions> extends Server<T,K> {
19
38
  static override shortcut = 'servers.websocket' as const
20
39
  static override stateSchema = ServerStateSchema
21
40
  static override optionsSchema = SocketServerOptionsSchema
22
41
 
23
- static override attach(container: NodeContainer & ServersInterface) {
24
- return container
25
- }
42
+ static { Server.register(this, 'websocket') }
26
43
 
27
44
  _wss?: BaseServer
28
45
 
@@ -115,6 +132,4 @@ export class WebsocketServer<T extends ServerState = ServerState, K extends Sock
115
132
  }
116
133
  }
117
134
 
118
- servers.register('websocket', WebsocketServer)
119
-
120
135
  export default WebsocketServer
@@ -1,5 +1,5 @@
1
1
  import Websocket from 'isomorphic-ws'
2
- import { WebSocketClient, clients, type WebSocketClientState, type WebSocketClientOptions } from '../../client'
2
+ import { Client, WebSocketClient, type WebSocketClientState, type WebSocketClientOptions } from '../../client'
3
3
 
4
4
  /**
5
5
  * Web-specific WebSocket client implementation using isomorphic-ws.
@@ -12,9 +12,7 @@ export class SocketClient<T extends WebSocketClientState = WebSocketClientState,
12
12
 
13
13
  static override shortcut = 'clients.websocket' as const
14
14
 
15
- static override attach(...args: any[]) {
16
- clients.register('websocket', SocketClient)
17
- }
15
+ static { Client.register(this, 'websocket') }
18
16
 
19
17
  /**
20
18
  * Send data over the WebSocket with an ID envelope.
@@ -79,4 +77,4 @@ export class SocketClient<T extends WebSocketClientState = WebSocketClientState,
79
77
  }
80
78
  }
81
79
 
82
- export default clients.register('websocket', SocketClient)
80
+ export default SocketClient
@@ -1,21 +1,29 @@
1
1
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
2
- import { Feature, features } from "../feature.js";
3
- import { Container } from "../container.js";
2
+ import { Feature } from "../feature.js";
4
3
 
5
- /**
6
- * The AssetLoader provides an API for injecting scripts and stylesheets into the page.
7
- *
8
- * It also provides a convenient way of loading any library from unpkg.com
9
- */
4
+ /**
5
+ * Injects scripts and stylesheets into the page at runtime.
6
+ *
7
+ * Provides helpers for loading external libraries from unpkg.com,
8
+ * injecting arbitrary script/link tags, and managing load state.
9
+ * Used by other web features (e.g. Esbuild) to pull in dependencies on demand.
10
+ *
11
+ * @extends Feature
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const loader = container.feature('assetLoader')
16
+ * await loader.loadScript('https://unpkg.com/lodash')
17
+ * await AssetLoader.loadStylesheet('https://unpkg.com/normalize.css')
18
+ * ```
19
+ */
10
20
  export class AssetLoader extends Feature {
11
- static attach(container: Container & { assetLoader?: AssetLoader }) {
12
- container.features.register("assetLoader", AssetLoader);
13
- }
14
-
15
21
  static override stateSchema = FeatureStateSchema
16
22
  static override optionsSchema = FeatureOptionsSchema
17
23
  static override shortcut = "features.assetLoader" as const
18
24
 
25
+ static { Feature.register(this, 'assetLoader') }
26
+
19
27
  static loadStylesheet(href: string) {
20
28
  return new Promise((resolve, reject) => {
21
29
  const link = document.createElement("link");
@@ -61,4 +69,4 @@ export class AssetLoader extends Feature {
61
69
  }
62
70
  }
63
71
 
64
- export default features.register("assetLoader", AssetLoader);
72
+ export default AssetLoader;
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { features, Feature } from '../feature.js'
4
- import type { Container } from '../../container.js'
3
+ import { Feature } from '../feature.js'
5
4
 
6
5
  // --- Message Types ---
7
6
 
@@ -124,9 +123,7 @@ export class ContainerLink extends Feature<ContainerLinkState, ContainerLinkOpti
124
123
  static override optionsSchema = ContainerLinkOptionsSchema
125
124
  static override eventsSchema = ContainerLinkEventsSchema
126
125
 
127
- static attach(container: Container & { containerLink?: ContainerLink }) {
128
- container.features.register('containerLink', ContainerLink)
129
- }
126
+ static { Feature.register(this as any, 'containerLink') }
130
127
 
131
128
  private _ws?: WebSocket
132
129
  private _reconnectTimer?: ReturnType<typeof setTimeout>
@@ -382,4 +379,4 @@ export class ContainerLink extends Feature<ContainerLinkState, ContainerLinkOpti
382
379
  }
383
380
  }
384
381
 
385
- export default features.register('containerLink', ContainerLink)
382
+ export default ContainerLink
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, type FeatureState, features } from "../feature.js";
4
- import { Container } from "../container.js";
3
+ import { Feature, type FeatureState } from "../feature.js";
5
4
  import type * as esbuild from 'esbuild-wasm';
6
5
 
7
6
  export const EsbuildWebOptionsSchema = FeatureOptionsSchema.extend({
@@ -11,14 +10,29 @@ export const EsbuildWebOptionsSchema = FeatureOptionsSchema.extend({
11
10
 
12
11
  export type EsbuildWebOptions = z.infer<typeof EsbuildWebOptionsSchema>
13
12
 
13
+ /**
14
+ * Browser-side TypeScript/ESM compilation feature using esbuild-wasm.
15
+ *
16
+ * Loads esbuild's WebAssembly build via the AssetLoader, then provides
17
+ * `compile()` and `transform()` methods that work entirely in the browser.
18
+ * Useful for live playgrounds, in-browser REPLs, and client-side bundling.
19
+ *
20
+ * @extends Feature
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const esbuild = container.feature('esbuild')
25
+ * await esbuild.start()
26
+ * const result = await esbuild.compile('const x: number = 1')
27
+ * console.log(result.code)
28
+ * ```
29
+ */
14
30
  export class Esbuild extends Feature<FeatureState, EsbuildWebOptions> {
15
- static attach(container: Container & { assetLoader?: Esbuild}) {
16
- container.features.register("esbuild", Esbuild);
17
- }
18
-
19
31
  static override stateSchema = FeatureStateSchema
20
32
  static override optionsSchema = EsbuildWebOptionsSchema
21
33
  static override shortcut = "features.esbuild" as const
34
+
35
+ static { Feature.register(this as any, 'esbuild') }
22
36
 
23
37
  /** Returns the assetLoader feature for loading external libraries from unpkg. */
24
38
  get assetLoader() {
@@ -74,6 +88,6 @@ export class Esbuild extends Feature<FeatureState, EsbuildWebOptions> {
74
88
 
75
89
  }
76
90
 
77
- export default features.register("esbuild", Esbuild);
91
+ export default Esbuild;
78
92
 
79
93
  const compileCache = new Map()
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
  import { Client } from '../../client.js'
5
5
  import type { Registry } from '../../registry.js'
6
6
  import type { AssetLoader } from './asset-loader.js'
@@ -79,6 +79,8 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
79
79
  static override optionsSchema = HelpersOptionsSchema
80
80
  static override eventsSchema = HelpersEventsSchema
81
81
 
82
+ static { Feature.register(this as any, 'helpers') }
83
+
82
84
  private _manifest: Manifest | null = null
83
85
 
84
86
  private get registryMap(): Record<RegistryType, { registry: Registry<any> }> {
@@ -264,4 +266,4 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
264
266
  }
265
267
  }
266
268
 
267
- export default features.register('helpers', Helpers)
269
+ export default Helpers
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { features, Feature } from "../feature.js";
4
- import type { Container, ContainerContext } from "../container.js";
3
+ import { Feature } from "../feature.js";
4
+ import type { ContainerContext } from "../container.js";
5
5
 
6
6
  export const NetworkStateSchema = FeatureStateSchema.extend({
7
7
  offline: z.boolean().describe('Whether the browser is currently offline'),
@@ -12,6 +12,25 @@ export const NetworkOptionsSchema = FeatureOptionsSchema.extend({})
12
12
  export type NetworkState = z.infer<typeof NetworkStateSchema>
13
13
  export type NetworkOptions = z.infer<typeof NetworkOptionsSchema>
14
14
 
15
+ /**
16
+ * Tracks browser online/offline connectivity state.
17
+ *
18
+ * Listens for the browser's `online` and `offline` events and keeps the
19
+ * feature state in sync. Other features can observe the `offline` state
20
+ * value or listen for change events to react to connectivity changes.
21
+ *
22
+ * @extends Feature
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const network = container.feature('network')
27
+ * console.log(network.state.get('offline')) // false when online
28
+ *
29
+ * network.on('stateChanged', ({ offline }) => {
30
+ * console.log(offline ? 'Went offline' : 'Back online')
31
+ * })
32
+ * ```
33
+ */
15
34
  export class Network<
16
35
  T extends NetworkState = NetworkState,
17
36
  K extends NetworkOptions = NetworkOptions
@@ -19,10 +38,8 @@ export class Network<
19
38
  static override stateSchema = NetworkStateSchema
20
39
  static override optionsSchema = NetworkOptionsSchema
21
40
  static override shortcut = "features.network" as const
22
-
23
- static attach(container: Container & { network?: Network }) {
24
- container.features.register("network", Network);
25
- }
41
+
42
+ static { Feature.register(this as any, 'network') }
26
43
 
27
44
  constructor(options: K, context: ContainerContext) {
28
45
  super(options, context);
@@ -58,4 +75,4 @@ export class Network<
58
75
  }
59
76
  }
60
77
 
61
- export default features.register('network', Network)
78
+ export default Network
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from "../feature.js";
4
- import { Container, type ContainerContext } from "../container.js";
3
+ import { Feature } from "../feature.js";
4
+ import { type ContainerContext } from "../container.js";
5
5
 
6
6
  export const SpeechOptionsSchema = FeatureOptionsSchema.extend({
7
7
  voice: z.string().optional().describe('The voice to use for the speech'),
@@ -23,18 +23,35 @@ type Voice = {
23
23
  default: boolean;
24
24
  };
25
25
 
26
+ /**
27
+ * Text-to-speech synthesis using the Web Speech API (SpeechSynthesis).
28
+ *
29
+ * Wraps the browser's built-in speech synthesis, providing voice selection,
30
+ * queue management, and state tracking. Voices are discovered on init and
31
+ * exposed via state for UI binding.
32
+ *
33
+ * @extends Feature
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const speech = container.feature('speech')
38
+ * speech.say('Hello from the browser!')
39
+ *
40
+ * // Choose a specific voice
41
+ * const speech = container.feature('speech', { voice: 'Google UK English Female' })
42
+ * speech.say('Cheerio!')
43
+ * ```
44
+ */
26
45
  export class Speech<
27
46
  T extends SpeechState = SpeechState,
28
47
  K extends SpeechOptions = SpeechOptions
29
48
  > extends Feature<T, K> {
30
49
 
31
- static attach(container: Container & { speech?: Speech }) {
32
- container.features.register("speech", Speech);
33
- }
34
-
35
50
  static override stateSchema = SpeechStateSchema
36
51
  static override optionsSchema = SpeechOptionsSchema
37
52
  static override shortcut = "features.speech" as const
53
+
54
+ static { Feature.register(this as any, 'speech') }
38
55
 
39
56
  constructor(options: K, context: ContainerContext) {
40
57
  super(options,context)
@@ -84,4 +101,4 @@ export class Speech<
84
101
  }
85
102
  }
86
103
 
87
- export default features.register("speech", Speech);
104
+ export default Speech;
@@ -1,8 +1,7 @@
1
1
  // @ts-nocheck
2
2
  import { z } from 'zod'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
- import { Feature, features } from '../feature.js'
5
- import { WebContainer} from '../container.js'
4
+ import { Feature } from '../feature.js'
6
5
 
7
6
  export const WebVaultStateSchema = FeatureStateSchema.extend({
8
7
  secret: z.string().optional().describe('Base64-encoded AES-GCM encryption secret key'),
@@ -15,10 +14,29 @@ export const WebVaultOptionsSchema = FeatureOptionsSchema.extend({
15
14
  export type WebVaultState = z.infer<typeof WebVaultStateSchema>
16
15
  export type WebVaultOptions = z.infer<typeof WebVaultOptionsSchema>
17
16
 
17
+ /**
18
+ * AES-256-GCM encryption and decryption for the browser using the Web Crypto API.
19
+ *
20
+ * Generates or accepts a secret key and provides `encrypt()` / `decrypt()` methods
21
+ * that work entirely client-side. Keys are stored as base64-encoded state so they
22
+ * can persist across sessions when needed.
23
+ *
24
+ * @extends Feature
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const vault = container.feature('vault')
29
+ * const encrypted = await vault.encrypt('secret data')
30
+ * const decrypted = await vault.decrypt(encrypted)
31
+ * console.log(decrypted) // 'secret data'
32
+ * ```
33
+ */
18
34
  export class WebVault extends Feature<WebVaultState, WebVaultOptions> {
19
35
  static override stateSchema = WebVaultStateSchema
20
36
  static override optionsSchema = WebVaultOptionsSchema
21
37
  static override shortcut = "features.vault" as const
38
+
39
+ static { Feature.register(this, 'vault') }
22
40
 
23
41
  async secret({ refresh = false, set = true } = {}) : Promise<ArrayBuffer> {
24
42
  if (!this.state.get('secret') && this.options.secret) {
@@ -67,7 +85,7 @@ export class WebVault extends Feature<WebVaultState, WebVaultOptions> {
67
85
  }
68
86
  }
69
87
 
70
- export default features.register('vault', WebVault)
88
+ export default WebVault
71
89
 
72
90
  async function generateSecretKey(): Promise<ArrayBuffer> {
73
91
  const key = await crypto.subtle.generateKey(
@@ -3,8 +3,7 @@
3
3
  import { z } from 'zod'
4
4
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
5
5
  import vm from '../shims/isomorphic-vm'
6
- import { Feature, features } from "../feature.js";
7
- import { Container } from '../../container.js';
6
+ import { Feature } from "../feature.js";
8
7
 
9
8
  export const VMStateSchema = FeatureStateSchema.extend({})
10
9
 
@@ -15,25 +14,33 @@ export const VMOptionsSchema = FeatureOptionsSchema.extend({
15
14
  export type VMState = z.infer<typeof VMStateSchema>
16
15
  export type VMOptions = z.infer<typeof VMOptionsSchema>
17
16
 
18
- /**
19
- * The VM features providers a virtual machine for executing JavaScript code in a sandboxed environment.
20
- *
21
- * The Vm feature automatically injects the container.context object into the global scope, so these things
22
- * can be referenced in the code and the code can use anything provided by the container.
23
- */
17
+ /**
18
+ * Sandboxed JavaScript execution environment for the browser.
19
+ *
20
+ * Automatically injects the container's context object into the global scope,
21
+ * so evaluated code can use anything provided by the container. Useful for
22
+ * live code playgrounds, plugin systems, and dynamic script evaluation.
23
+ *
24
+ * @extends Feature
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const vm = container.feature('vm')
29
+ * const result = vm.run('1 + 2 + 3') // 6
30
+ * const greeting = vm.run('container.uuid') // accesses container globals
31
+ * ```
32
+ */
24
33
  export class VM<
25
34
  T extends VMState = VMState,
26
35
  K extends VMOptions = VMOptions
27
36
  > extends Feature<T, K> {
28
37
 
29
- static attach(container: Container) {
30
- container.features.register('vm', VM)
31
- }
32
-
33
38
  static override stateSchema = VMStateSchema
34
39
  static override optionsSchema = VMOptionsSchema
35
40
  static override shortcut = "features.vm" as const
36
41
 
42
+ static { Feature.register(this, 'vm') }
43
+
37
44
  createScript(code: string) {
38
45
  return new vm.Script(code)
39
46
  }
@@ -75,4 +82,4 @@ export class VM<
75
82
  }
76
83
  }
77
84
 
78
- export default features.register("vm", VM);
85
+ export default VM;
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from "../../feature.js";
4
- import { type WebFeatures, type Container, type ContainerContext } from '../container.js'
3
+ import { Feature } from "../../feature.js";
4
+ import { type ContainerContext } from '../container.js'
5
5
 
6
6
  export const VoiceRecognitionOptionsSchema = FeatureOptionsSchema.extend({
7
7
  language: z.string().optional().describe('BCP 47 language code for recognition (e.g. en-US)'),
@@ -17,20 +17,37 @@ export const VoiceRecognitionStateSchema = FeatureStateSchema.extend({
17
17
  export type VoiceRecognitionOptions = z.infer<typeof VoiceRecognitionOptionsSchema>
18
18
  export type VoiceRecognitionState = z.infer<typeof VoiceRecognitionStateSchema>
19
19
 
20
+ /**
21
+ * Speech-to-text recognition using the Web Speech API (SpeechRecognition).
22
+ *
23
+ * Wraps the browser's built-in speech recognition, supporting continuous
24
+ * listening, interim results, and language selection. Recognized text is
25
+ * accumulated in state and emitted as events for real-time transcription UIs.
26
+ *
27
+ * @extends Feature
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const voice = container.feature('voice', { continuous: true, autoListen: true })
32
+ *
33
+ * voice.on('transcript', ({ text }) => {
34
+ * console.log('Heard:', text)
35
+ * })
36
+ *
37
+ * // Or start manually
38
+ * voice.start()
39
+ * ```
40
+ */
20
41
  export class VoiceRecognition<T extends VoiceRecognitionState = VoiceRecognitionState, K extends VoiceRecognitionOptions = VoiceRecognitionOptions> extends Feature<T, K> {
21
42
  // @ts-ignore-next-line
22
43
  private recognition: SpeechRecognition | null = null;
23
44
 
24
- static attach(container: Container<WebFeatures> & { voice?: VoiceRecognition }, options?: VoiceRecognitionOptions) {
25
- container.features.register('voice', VoiceRecognition)
26
- container.feature('voice', { enable: true })
27
- return container
28
- }
29
-
30
45
  static override stateSchema = VoiceRecognitionStateSchema
31
46
  static override optionsSchema = VoiceRecognitionOptionsSchema
32
47
  static override shortcut = "features.voice" as const
33
48
 
49
+ static { Feature.register(this as any, 'voice') }
50
+
34
51
  constructor(options: K, context: ContainerContext) {
35
52
  super(options, context);
36
53
 
@@ -126,4 +143,4 @@ export class VoiceRecognition<T extends VoiceRecognitionState = VoiceRecognition
126
143
  }
127
144
  }
128
145
 
129
- export default features.register("voice", VoiceRecognition);
146
+ export default VoiceRecognition;