@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,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-07T16:53:43.396Z
4
+ // Generated at: 2026-03-12T02:48:58.250Z
5
5
 
6
6
  setBuildTimeData('features.containerLink', {
7
7
  "id": "features.containerLink",
@@ -98,7 +98,7 @@ setBuildTimeData('features.containerLink', {
98
98
 
99
99
  setBuildTimeData('features.esbuild', {
100
100
  "id": "features.esbuild",
101
- "description": "Esbuild helper",
101
+ "description": "Browser-side TypeScript/ESM compilation feature using esbuild-wasm. Loads esbuild's WebAssembly build via the AssetLoader, then provides `compile()` and `transform()` methods that work entirely in the browser. Useful for live playgrounds, in-browser REPLs, and client-side bundling.",
102
102
  "shortcut": "features.esbuild",
103
103
  "className": "Esbuild",
104
104
  "methods": {
@@ -141,12 +141,18 @@ setBuildTimeData('features.esbuild', {
141
141
  "events": {},
142
142
  "state": {},
143
143
  "options": {},
144
- "envVars": []
144
+ "envVars": [],
145
+ "examples": [
146
+ {
147
+ "language": "ts",
148
+ "code": "const esbuild = container.feature('esbuild')\nawait esbuild.start()\nconst result = await esbuild.compile('const x: number = 1')\nconsole.log(result.code)"
149
+ }
150
+ ]
145
151
  });
146
152
 
147
153
  setBuildTimeData('features.voice', {
148
154
  "id": "features.voice",
149
- "description": "VoiceRecognition helper",
155
+ "description": "Speech-to-text recognition using the Web Speech API (SpeechRecognition). Wraps the browser's built-in speech recognition, supporting continuous listening, interim results, and language selection. Recognized text is accumulated in state and emitted as events for real-time transcription UIs.",
150
156
  "shortcut": "features.voice",
151
157
  "className": "VoiceRecognition",
152
158
  "methods": {
@@ -210,12 +216,18 @@ setBuildTimeData('features.voice', {
210
216
  },
211
217
  "state": {},
212
218
  "options": {},
213
- "envVars": []
219
+ "envVars": [],
220
+ "examples": [
221
+ {
222
+ "language": "ts",
223
+ "code": "const voice = container.feature('voice', { continuous: true, autoListen: true })\n\nvoice.on('transcript', ({ text }) => {\n console.log('Heard:', text)\n})\n\n// Or start manually\nvoice.start()"
224
+ }
225
+ ]
214
226
  });
215
227
 
216
228
  setBuildTimeData('features.vm', {
217
229
  "id": "features.vm",
218
- "description": "The VM features providers a virtual machine for executing JavaScript code in a sandboxed environment. The Vm feature automatically injects the container.context object into the global scope, so these things can be referenced in the code and the code can use anything provided by the container.",
230
+ "description": "Sandboxed JavaScript execution environment for the browser. Automatically injects the container's context object into the global scope, so evaluated code can use anything provided by the container. Useful for live code playgrounds, plugin systems, and dynamic script evaluation.",
219
231
  "shortcut": "features.vm",
220
232
  "className": "VM",
221
233
  "methods": {
@@ -269,12 +281,18 @@ setBuildTimeData('features.vm', {
269
281
  "events": {},
270
282
  "state": {},
271
283
  "options": {},
272
- "envVars": []
284
+ "envVars": [],
285
+ "examples": [
286
+ {
287
+ "language": "ts",
288
+ "code": "const vm = container.feature('vm')\nconst result = vm.run('1 + 2 + 3') // 6\nconst greeting = vm.run('container.uuid') // accesses container globals"
289
+ }
290
+ ]
273
291
  });
274
292
 
275
293
  setBuildTimeData('features.assetLoader', {
276
294
  "id": "features.assetLoader",
277
- "description": "The AssetLoader provides an API for injecting scripts and stylesheets into the page. It also provides a convenient way of loading any library from unpkg.com",
295
+ "description": "Injects scripts and stylesheets into the page at runtime. Provides helpers for loading external libraries from unpkg.com, injecting arbitrary script/link tags, and managing load state. Used by other web features (e.g. Esbuild) to pull in dependencies on demand.",
278
296
  "shortcut": "features.assetLoader",
279
297
  "className": "AssetLoader",
280
298
  "methods": {
@@ -327,12 +345,18 @@ setBuildTimeData('features.assetLoader', {
327
345
  "events": {},
328
346
  "state": {},
329
347
  "options": {},
330
- "envVars": []
348
+ "envVars": [],
349
+ "examples": [
350
+ {
351
+ "language": "ts",
352
+ "code": "const loader = container.feature('assetLoader')\nawait loader.loadScript('https://unpkg.com/lodash')\nawait AssetLoader.loadStylesheet('https://unpkg.com/normalize.css')"
353
+ }
354
+ ]
331
355
  });
332
356
 
333
357
  setBuildTimeData('features.vault', {
334
358
  "id": "features.vault",
335
- "description": "WebVault helper",
359
+ "description": "AES-256-GCM encryption and decryption for the browser using the Web Crypto API. Generates or accepts a secret key and provides `encrypt()` / `decrypt()` methods that work entirely client-side. Keys are stored as base64-encoded state so they can persist across sessions when needed.",
336
360
  "shortcut": "features.vault",
337
361
  "className": "WebVault",
338
362
  "methods": {
@@ -378,12 +402,18 @@ setBuildTimeData('features.vault', {
378
402
  "events": {},
379
403
  "state": {},
380
404
  "options": {},
381
- "envVars": []
405
+ "envVars": [],
406
+ "examples": [
407
+ {
408
+ "language": "ts",
409
+ "code": "const vault = container.feature('vault')\nconst encrypted = await vault.encrypt('secret data')\nconst decrypted = await vault.decrypt(encrypted)\nconsole.log(decrypted) // 'secret data'"
410
+ }
411
+ ]
382
412
  });
383
413
 
384
414
  setBuildTimeData('features.network', {
385
415
  "id": "features.network",
386
- "description": "Network helper",
416
+ "description": "Tracks browser online/offline connectivity state. Listens for the browser's `online` and `offline` events and keeps the feature state in sync. Other features can observe the `offline` state value or listen for change events to react to connectivity changes.",
387
417
  "shortcut": "features.network",
388
418
  "className": "Network",
389
419
  "methods": {
@@ -413,12 +443,18 @@ setBuildTimeData('features.network', {
413
443
  "events": {},
414
444
  "state": {},
415
445
  "options": {},
416
- "envVars": []
446
+ "envVars": [],
447
+ "examples": [
448
+ {
449
+ "language": "ts",
450
+ "code": "const network = container.feature('network')\nconsole.log(network.state.get('offline')) // false when online\n\nnetwork.on('stateChanged', ({ offline }) => {\n console.log(offline ? 'Went offline' : 'Back online')\n})"
451
+ }
452
+ ]
417
453
  });
418
454
 
419
455
  setBuildTimeData('features.speech', {
420
456
  "id": "features.speech",
421
- "description": "Speech helper",
457
+ "description": "Text-to-speech synthesis using the Web Speech API (SpeechSynthesis). Wraps the browser's built-in speech synthesis, providing voice selection, queue management, and state tracking. Voices are discovered on init and exposed via state for UI binding.",
422
458
  "shortcut": "features.speech",
423
459
  "className": "Speech",
424
460
  "methods": {
@@ -478,7 +514,13 @@ setBuildTimeData('features.speech', {
478
514
  "events": {},
479
515
  "state": {},
480
516
  "options": {},
481
- "envVars": []
517
+ "envVars": [],
518
+ "examples": [
519
+ {
520
+ "language": "ts",
521
+ "code": "const speech = container.feature('speech')\nspeech.say('Hello from the browser!')\n\n// Choose a specific voice\nconst speech = container.feature('speech', { voice: 'Google UK English Female' })\nspeech.say('Cheerio!')"
522
+ }
523
+ ]
482
524
  });
483
525
 
484
526
  setBuildTimeData('features.helpers', {
@@ -1109,7 +1151,7 @@ export const introspectionData = [
1109
1151
  },
1110
1152
  {
1111
1153
  "id": "features.esbuild",
1112
- "description": "Esbuild helper",
1154
+ "description": "Browser-side TypeScript/ESM compilation feature using esbuild-wasm. Loads esbuild's WebAssembly build via the AssetLoader, then provides `compile()` and `transform()` methods that work entirely in the browser. Useful for live playgrounds, in-browser REPLs, and client-side bundling.",
1113
1155
  "shortcut": "features.esbuild",
1114
1156
  "className": "Esbuild",
1115
1157
  "methods": {
@@ -1152,11 +1194,17 @@ export const introspectionData = [
1152
1194
  "events": {},
1153
1195
  "state": {},
1154
1196
  "options": {},
1155
- "envVars": []
1197
+ "envVars": [],
1198
+ "examples": [
1199
+ {
1200
+ "language": "ts",
1201
+ "code": "const esbuild = container.feature('esbuild')\nawait esbuild.start()\nconst result = await esbuild.compile('const x: number = 1')\nconsole.log(result.code)"
1202
+ }
1203
+ ]
1156
1204
  },
1157
1205
  {
1158
1206
  "id": "features.voice",
1159
- "description": "VoiceRecognition helper",
1207
+ "description": "Speech-to-text recognition using the Web Speech API (SpeechRecognition). Wraps the browser's built-in speech recognition, supporting continuous listening, interim results, and language selection. Recognized text is accumulated in state and emitted as events for real-time transcription UIs.",
1160
1208
  "shortcut": "features.voice",
1161
1209
  "className": "VoiceRecognition",
1162
1210
  "methods": {
@@ -1220,11 +1268,17 @@ export const introspectionData = [
1220
1268
  },
1221
1269
  "state": {},
1222
1270
  "options": {},
1223
- "envVars": []
1271
+ "envVars": [],
1272
+ "examples": [
1273
+ {
1274
+ "language": "ts",
1275
+ "code": "const voice = container.feature('voice', { continuous: true, autoListen: true })\n\nvoice.on('transcript', ({ text }) => {\n console.log('Heard:', text)\n})\n\n// Or start manually\nvoice.start()"
1276
+ }
1277
+ ]
1224
1278
  },
1225
1279
  {
1226
1280
  "id": "features.vm",
1227
- "description": "The VM features providers a virtual machine for executing JavaScript code in a sandboxed environment. The Vm feature automatically injects the container.context object into the global scope, so these things can be referenced in the code and the code can use anything provided by the container.",
1281
+ "description": "Sandboxed JavaScript execution environment for the browser. Automatically injects the container's context object into the global scope, so evaluated code can use anything provided by the container. Useful for live code playgrounds, plugin systems, and dynamic script evaluation.",
1228
1282
  "shortcut": "features.vm",
1229
1283
  "className": "VM",
1230
1284
  "methods": {
@@ -1278,11 +1332,17 @@ export const introspectionData = [
1278
1332
  "events": {},
1279
1333
  "state": {},
1280
1334
  "options": {},
1281
- "envVars": []
1335
+ "envVars": [],
1336
+ "examples": [
1337
+ {
1338
+ "language": "ts",
1339
+ "code": "const vm = container.feature('vm')\nconst result = vm.run('1 + 2 + 3') // 6\nconst greeting = vm.run('container.uuid') // accesses container globals"
1340
+ }
1341
+ ]
1282
1342
  },
1283
1343
  {
1284
1344
  "id": "features.assetLoader",
1285
- "description": "The AssetLoader provides an API for injecting scripts and stylesheets into the page. It also provides a convenient way of loading any library from unpkg.com",
1345
+ "description": "Injects scripts and stylesheets into the page at runtime. Provides helpers for loading external libraries from unpkg.com, injecting arbitrary script/link tags, and managing load state. Used by other web features (e.g. Esbuild) to pull in dependencies on demand.",
1286
1346
  "shortcut": "features.assetLoader",
1287
1347
  "className": "AssetLoader",
1288
1348
  "methods": {
@@ -1335,11 +1395,17 @@ export const introspectionData = [
1335
1395
  "events": {},
1336
1396
  "state": {},
1337
1397
  "options": {},
1338
- "envVars": []
1398
+ "envVars": [],
1399
+ "examples": [
1400
+ {
1401
+ "language": "ts",
1402
+ "code": "const loader = container.feature('assetLoader')\nawait loader.loadScript('https://unpkg.com/lodash')\nawait AssetLoader.loadStylesheet('https://unpkg.com/normalize.css')"
1403
+ }
1404
+ ]
1339
1405
  },
1340
1406
  {
1341
1407
  "id": "features.vault",
1342
- "description": "WebVault helper",
1408
+ "description": "AES-256-GCM encryption and decryption for the browser using the Web Crypto API. Generates or accepts a secret key and provides `encrypt()` / `decrypt()` methods that work entirely client-side. Keys are stored as base64-encoded state so they can persist across sessions when needed.",
1343
1409
  "shortcut": "features.vault",
1344
1410
  "className": "WebVault",
1345
1411
  "methods": {
@@ -1385,11 +1451,17 @@ export const introspectionData = [
1385
1451
  "events": {},
1386
1452
  "state": {},
1387
1453
  "options": {},
1388
- "envVars": []
1454
+ "envVars": [],
1455
+ "examples": [
1456
+ {
1457
+ "language": "ts",
1458
+ "code": "const vault = container.feature('vault')\nconst encrypted = await vault.encrypt('secret data')\nconst decrypted = await vault.decrypt(encrypted)\nconsole.log(decrypted) // 'secret data'"
1459
+ }
1460
+ ]
1389
1461
  },
1390
1462
  {
1391
1463
  "id": "features.network",
1392
- "description": "Network helper",
1464
+ "description": "Tracks browser online/offline connectivity state. Listens for the browser's `online` and `offline` events and keeps the feature state in sync. Other features can observe the `offline` state value or listen for change events to react to connectivity changes.",
1393
1465
  "shortcut": "features.network",
1394
1466
  "className": "Network",
1395
1467
  "methods": {
@@ -1419,11 +1491,17 @@ export const introspectionData = [
1419
1491
  "events": {},
1420
1492
  "state": {},
1421
1493
  "options": {},
1422
- "envVars": []
1494
+ "envVars": [],
1495
+ "examples": [
1496
+ {
1497
+ "language": "ts",
1498
+ "code": "const network = container.feature('network')\nconsole.log(network.state.get('offline')) // false when online\n\nnetwork.on('stateChanged', ({ offline }) => {\n console.log(offline ? 'Went offline' : 'Back online')\n})"
1499
+ }
1500
+ ]
1423
1501
  },
1424
1502
  {
1425
1503
  "id": "features.speech",
1426
- "description": "Speech helper",
1504
+ "description": "Text-to-speech synthesis using the Web Speech API (SpeechSynthesis). Wraps the browser's built-in speech synthesis, providing voice selection, queue management, and state tracking. Voices are discovered on init and exposed via state for UI binding.",
1427
1505
  "shortcut": "features.speech",
1428
1506
  "className": "Speech",
1429
1507
  "methods": {
@@ -1483,7 +1561,13 @@ export const introspectionData = [
1483
1561
  "events": {},
1484
1562
  "state": {},
1485
1563
  "options": {},
1486
- "envVars": []
1564
+ "envVars": [],
1565
+ "examples": [
1566
+ {
1567
+ "language": "ts",
1568
+ "code": "const speech = container.feature('speech')\nspeech.say('Hello from the browser!')\n\n// Choose a specific voice\nconst speech = container.feature('speech', { voice: 'Google UK English Female' })\nspeech.say('Cheerio!')"
1569
+ }
1570
+ ]
1487
1571
  },
1488
1572
  {
1489
1573
  "id": "features.helpers",
@@ -213,7 +213,7 @@ export function interceptRegistration(registry: any, helperConstructor: any) {
213
213
 
214
214
  const introspection: HelperIntrospection = {
215
215
  id: key,
216
- description: helperConstructor.description || existing?.description || '',
216
+ description: (helperConstructor.hasOwnProperty('description') ? helperConstructor.description : null) || existing?.description || helperConstructor.description || '',
217
217
  shortcut: helperConstructor.shortcut,
218
218
  className: helperConstructor.name || existing?.className,
219
219
  // preserve build-time AST data if generated file already loaded
@@ -18,6 +18,7 @@ export const IntrospectionScannerOptionsSchema = FeatureOptionsSchema.extend({
18
18
  src: z.array(z.string()).optional(),
19
19
  outputPath: z.string().optional(),
20
20
  includePrivate: z.boolean().optional(),
21
+ importSource: z.string().optional(),
21
22
  })
22
23
 
23
24
  export type IntrospectionScannerState = z.infer<typeof IntrospectionScannerStateSchema>
@@ -874,11 +875,12 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
874
875
  private createRegistryScript(results: HelperIntrospection[], containerResults: Partial<ContainerIntrospection>[] = []): string {
875
876
  const hasContainers = containerResults.length > 0;
876
877
 
878
+ const importSource = this.options.importSource || './index.js';
877
879
  let imports = `import { setBuildTimeData`;
878
880
  if (hasContainers) {
879
881
  imports += `, setContainerBuildTimeData`;
880
882
  }
881
- imports += ` } from './index.js';\n\n`;
883
+ imports += ` } from '${importSource}';\n\n`;
882
884
 
883
885
  const registrations = results.map(result => {
884
886
  const data = JSON.stringify(result, null, 2);
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { randomBytes } from 'crypto'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
4
- import { Feature, features } from '../feature.js'
4
+ import { Feature } from '../feature.js'
5
5
  import { WebSocketServer } from 'ws'
6
6
 
7
7
  // --- Message Types ---
@@ -146,6 +146,7 @@ export class ContainerLink extends Feature<ContainerLinkState, ContainerLinkOpti
146
146
  static override stateSchema = ContainerLinkStateSchema
147
147
  static override optionsSchema = ContainerLinkOptionsSchema
148
148
  static override eventsSchema = ContainerLinkEventsSchema
149
+ static { Feature.register(this, 'containerLink') }
149
150
 
150
151
  private _wss?: WebSocketServer
151
152
  private _connections = new Map<string, ConnectedContainer>()
@@ -555,4 +556,4 @@ export class ContainerLink extends Feature<ContainerLinkState, ContainerLinkOpti
555
556
  }
556
557
  }
557
558
 
558
- export default features.register('containerLink', ContainerLink)
559
+ export default ContainerLink
@@ -1,4 +1,4 @@
1
- import { Feature, features } from '../feature.js'
1
+ import { Feature } from '../feature.js'
2
2
  import { parse, Collection, extractSections, type ModelDefinition } from 'contentbase'
3
3
  import { z } from 'zod'
4
4
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
@@ -37,6 +37,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
37
37
  static override shortcut = 'features.contentDb' as const
38
38
  static override stateSchema = ContentDbStateSchema
39
39
  static override optionsSchema = ContentDbOptionsSchema
40
+ static { Feature.register(this, 'contentDb') }
40
41
 
41
42
  override get initialState(): ContentDbState {
42
43
  return {
@@ -127,6 +128,13 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
127
128
  return this
128
129
  }
129
130
 
131
+ /** Force-reload the collection from disk, picking up new/changed/deleted documents. */
132
+ async reload(): Promise<ContentDb> {
133
+ await this.collection.load({ refresh: true })
134
+ this.emit('reloaded')
135
+ return this
136
+ }
137
+
130
138
  /**
131
139
  * Read a single document by its path ID, optionally filtering to specific sections.
132
140
  *
@@ -472,4 +480,4 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
472
480
  }
473
481
  }
474
482
 
475
- export default features.register('contentDb', ContentDb)
483
+ export default ContentDb
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import cacache from "cacache";
4
- import { Feature, type FeatureState, features } from "../feature.js";
4
+ import { Feature, type FeatureState } from "../feature.js";
5
5
  import { NodeContainer } from "../container.js";
6
6
  import { partial } from "lodash-es";
7
7
  import type { ContainerContext } from "../../container.js";
@@ -34,8 +34,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
34
34
  static override shortcut = "features.diskCache" as const
35
35
  static override stateSchema = FeatureStateSchema
36
36
  static override optionsSchema = DiskCacheOptionsSchema
37
-
38
- static attach(c: NodeContainer) {}
37
+ static { Feature.register(this, 'diskCache') }
39
38
 
40
39
  constructor(options: DiskCacheOptions, context: ContainerContext) {
41
40
  super(options, context)
@@ -379,4 +378,4 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
379
378
  }
380
379
  }
381
380
 
382
- export default features.register("diskCache", DiskCache);
381
+ export default DiskCache;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod'
2
- import { Feature, features } from '../feature.js'
2
+ import { Feature } from '../feature.js'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
4
 
5
5
  /** Supported DNS record types. */
@@ -121,6 +121,7 @@ export class Dns extends Feature<DnsState, DnsOptions> {
121
121
  static override description = 'DNS lookup utilities wrapping the dig CLI'
122
122
  static override stateSchema = DnsStateSchema
123
123
  static override optionsSchema = DnsOptionsSchema
124
+ static { Feature.register(this, 'dns') }
124
125
 
125
126
  override get initialState(): DnsState {
126
127
  return {
@@ -652,4 +653,4 @@ export class Dns extends Feature<DnsState, DnsOptions> {
652
653
  }
653
654
  }
654
655
 
655
- export default features.register('dns', Dns)
656
+ export default Dns
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import { Feature, features } from '../feature.js'
3
+ import { Feature } from '../feature.js'
4
4
 
5
5
  export const DockerContainerSchema = z.object({
6
6
  /** Container ID */
@@ -87,6 +87,7 @@ export class Docker extends Feature<DockerState, DockerOptions> {
87
87
  static override shortcut = 'features.docker' as const
88
88
  static override stateSchema = DockerStateSchema
89
89
  static override optionsSchema = DockerOptionsSchema
90
+ static { Feature.register(this, 'docker') }
90
91
 
91
92
  override get initialState(): DockerState {
92
93
  return {
@@ -909,4 +910,4 @@ export class Docker extends Feature<DockerState, DockerOptions> {
909
910
  }
910
911
  }
911
912
 
912
- export default features.register('docker', Docker)
913
+ export default Docker
@@ -1,6 +1,5 @@
1
- import { Feature, features } from '../feature.js'
1
+ import { Feature } from '../feature.js'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
- import fetch from 'cross-fetch'
4
3
 
5
4
  /**
6
5
  * A feature that provides file downloading capabilities from URLs.
@@ -25,16 +24,10 @@ import fetch from 'cross-fetch'
25
24
  * @extends Feature
26
25
  */
27
26
  export class Downloader extends Feature {
28
- /**
29
- * The shortcut path for accessing this feature through the container.
30
- *
31
- * @static
32
- * @readonly
33
- * @type {string}
34
- */
35
27
  static override shortcut = 'features.downloader' as const
36
28
  static override stateSchema = FeatureStateSchema
37
29
  static override optionsSchema = FeatureOptionsSchema
30
+ static { Feature.register(this, 'downloader') }
38
31
 
39
32
  /**
40
33
  * Downloads a file from a URL and saves it to the specified local path.
@@ -83,10 +76,4 @@ export class Downloader extends Feature {
83
76
 
84
77
  }
85
78
 
86
- /**
87
- * Registers the Downloader feature with the features registry.
88
- * This makes the feature available for use in containers via `container.use('downloader')`.
89
- *
90
- * @type {typeof Downloader}
91
- */
92
- export default features.register('downloader', Downloader)
79
+ export default Downloader
@@ -1,7 +1,6 @@
1
1
  import * as esbuild from 'esbuild-wasm'
2
- import { Feature, features } from '../feature.js'
2
+ import { Feature } from '../feature.js'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
4
- import { NodeContainer } from '../container.js'
5
4
 
6
5
  /**
7
6
  * A Feature for compiling typescript / esm modules, etc to JavaScript
@@ -19,17 +18,9 @@ export class ESBuild extends Feature {
19
18
  static override shortcut = 'features.esbuild' as const
20
19
  static override stateSchema = FeatureStateSchema
21
20
  static override optionsSchema = FeatureOptionsSchema
21
+ static { Feature.register(this, 'esbuild') }
22
22
 
23
23
  /**
24
- * Attaches the ESBuild feature to a NodeContainer instance.
25
- *
26
- * @param c - The NodeContainer to attach to
27
- * @returns The container for method chaining
28
- */
29
- static attach(c: NodeContainer) {
30
- return c
31
- }
32
-
33
24
  /**
34
25
  * Transform code synchronously
35
26
  * @param code - The code to transform
@@ -65,4 +56,4 @@ export class ESBuild extends Feature {
65
56
  }
66
57
  }
67
58
 
68
- export default features.register('esbuild', ESBuild)
59
+ export default ESBuild
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import { State } from "../../state.js";
4
- import { Feature, features } from "../feature.js";
4
+ import { Feature } from "../feature.js";
5
5
  import { parse, relative } from "path";
6
6
  import { statSync } from "fs";
7
7
  import micromatch from "micromatch";
@@ -61,6 +61,7 @@ export class FileManager<
61
61
  static override shortcut = 'features.fileManager' as const
62
62
  static override stateSchema = FileManagerStateSchema
63
63
  static override optionsSchema = FileManagerOptionsSchema
64
+ static { Feature.register(this, 'fileManager') }
64
65
 
65
66
  files: State<Record<string, File>> = new State<Record<string, File>>({
66
67
  initialState: {},
@@ -354,4 +355,4 @@ export class FileManager<
354
355
  }
355
356
  }
356
357
 
357
- export default features.register("fileManager", FileManager);
358
+ export default FileManager
@@ -1,4 +1,4 @@
1
- import { features, Feature } from "../feature.js";
1
+ import { Feature } from "../feature.js";
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import {
4
4
  mkdirSync,
@@ -37,6 +37,7 @@ export class FS extends Feature {
37
37
  static override shortcut = "features.fs" as const
38
38
  static override stateSchema = FeatureStateSchema
39
39
  static override optionsSchema = FeatureOptionsSchema
40
+ static { Feature.register(this, 'fs') }
40
41
 
41
42
  /**
42
43
  * Asynchronously reads a file and returns its contents as a Buffer.
@@ -285,7 +286,11 @@ export class FS extends Feature {
285
286
  */
286
287
  ensureFolder(path: string) {
287
288
  mkdirSync(this.container.paths.resolve(path), { recursive: true });
288
- return path;
289
+ return this.container.paths.resolve(path);
290
+ }
291
+
292
+ mkdirp(folder: string) {
293
+ return this.ensureFolder(folder)
289
294
  }
290
295
 
291
296
  /**
@@ -400,6 +405,10 @@ export class FS extends Feature {
400
405
  }
401
406
  }
402
407
 
408
+ existsSync(path: string): boolean {
409
+ return this.exists(path)
410
+ }
411
+
403
412
  /**
404
413
  * Asynchronously removes a file.
405
414
  *
@@ -531,4 +540,4 @@ export class FS extends Feature {
531
540
  }
532
541
  }
533
542
 
534
- export default features.register("fs", FS);
543
+ export default FS
@@ -2,7 +2,7 @@ import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import { dirname, resolve, isAbsolute } from 'path';
4
4
  import { State } from '../../state.js';
5
- import { features, Feature } from '../feature.js'
5
+ import { Feature } from '../feature.js'
6
6
 
7
7
  type LsFilesOptions = {
8
8
  cached?: boolean;
@@ -48,6 +48,7 @@ export class Git extends Feature {
48
48
  static override shortcut = 'features.git' as const
49
49
  static override stateSchema = GitStateSchema
50
50
  static override optionsSchema = FeatureOptionsSchema
51
+ static { Feature.register(this, 'git') }
51
52
  override state: State<GitState> = new State()
52
53
 
53
54
  /**
@@ -489,4 +490,4 @@ export class Git extends Feature {
489
490
  }
490
491
  }
491
492
 
492
- export default features.register('git', Git)
493
+ export default Git
@@ -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 { google } from 'googleapis'
5
5
  import type { OAuth2Client } from 'google-auth-library'
6
6
 
@@ -90,6 +90,7 @@ export class GoogleAuth extends Feature<GoogleAuthState, GoogleAuthOptions> {
90
90
  static override stateSchema = GoogleAuthStateSchema
91
91
  static override optionsSchema = GoogleAuthOptionsSchema
92
92
  static override eventsSchema = GoogleAuthEventsSchema
93
+ static { Feature.register(this, 'googleAuth') }
93
94
 
94
95
  private _oauth2Client?: OAuth2Client
95
96
  private _redirectUri?: string
@@ -499,4 +500,4 @@ declare module '../../feature' {
499
500
  }
500
501
  }
501
502
 
502
- export default features.register('googleAuth', GoogleAuth)
503
+ export default GoogleAuth