@soederpop/luca 0.0.6 → 0.0.7

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 (211) hide show
  1. package/CLAUDE.md +10 -1
  2. package/bun.lock +1 -1
  3. package/commands/build-bootstrap.ts +78 -0
  4. package/commands/build-scaffolds.ts +24 -2
  5. package/commands/try-all-challenges.ts +543 -0
  6. package/commands/try-challenge.ts +100 -0
  7. package/docs/README.md +52 -80
  8. package/docs/TABLE-OF-CONTENTS.md +82 -51
  9. package/docs/apis/clients/elevenlabs.md +232 -8
  10. package/docs/apis/clients/graph.md +59 -8
  11. package/docs/apis/clients/openai.md +362 -2
  12. package/docs/apis/clients/rest.md +122 -2
  13. package/docs/apis/clients/websocket.md +71 -17
  14. package/docs/apis/features/agi/assistant.md +9 -3
  15. package/docs/apis/features/agi/assistants-manager.md +2 -2
  16. package/docs/apis/features/agi/claude-code.md +153 -14
  17. package/docs/apis/features/agi/conversation-history.md +15 -3
  18. package/docs/apis/features/agi/conversation.md +133 -20
  19. package/docs/apis/features/agi/openai-codex.md +90 -12
  20. package/docs/apis/features/agi/skills-library.md +23 -5
  21. package/docs/apis/features/node/container-link.md +59 -0
  22. package/docs/apis/features/node/content-db.md +1 -1
  23. package/docs/apis/features/node/disk-cache.md +1 -1
  24. package/docs/apis/features/node/dns.md +1 -0
  25. package/docs/apis/features/node/docker.md +2 -1
  26. package/docs/apis/features/node/esbuild.md +4 -3
  27. package/docs/apis/features/node/file-manager.md +13 -4
  28. package/docs/apis/features/node/fs.md +726 -171
  29. package/docs/apis/features/node/git.md +1 -0
  30. package/docs/apis/features/node/google-auth.md +23 -4
  31. package/docs/apis/features/node/google-calendar.md +14 -2
  32. package/docs/apis/features/node/google-docs.md +15 -2
  33. package/docs/apis/features/node/google-drive.md +21 -3
  34. package/docs/apis/features/node/google-sheets.md +14 -2
  35. package/docs/apis/features/node/grep.md +2 -0
  36. package/docs/apis/features/node/helpers.md +29 -0
  37. package/docs/apis/features/node/ink.md +2 -2
  38. package/docs/apis/features/node/networking.md +39 -4
  39. package/docs/apis/features/node/os.md +28 -0
  40. package/docs/apis/features/node/postgres.md +26 -4
  41. package/docs/apis/features/node/proc.md +37 -28
  42. package/docs/apis/features/node/process-manager.md +33 -5
  43. package/docs/apis/features/node/repl.md +1 -1
  44. package/docs/apis/features/node/runpod.md +1 -0
  45. package/docs/apis/features/node/secure-shell.md +7 -0
  46. package/docs/apis/features/node/semantic-search.md +12 -5
  47. package/docs/apis/features/node/sqlite.md +26 -4
  48. package/docs/apis/features/node/telegram.md +30 -5
  49. package/docs/apis/features/node/tts.md +17 -2
  50. package/docs/apis/features/node/ui.md +1 -1
  51. package/docs/apis/features/node/vault.md +4 -9
  52. package/docs/apis/features/node/vm.md +3 -12
  53. package/docs/apis/features/node/window-manager.md +128 -20
  54. package/docs/apis/features/web/asset-loader.md +13 -1
  55. package/docs/apis/features/web/container-link.md +59 -0
  56. package/docs/apis/features/web/esbuild.md +4 -3
  57. package/docs/apis/features/web/helpers.md +29 -0
  58. package/docs/apis/features/web/network.md +16 -2
  59. package/docs/apis/features/web/speech.md +16 -2
  60. package/docs/apis/features/web/vault.md +4 -9
  61. package/docs/apis/features/web/vm.md +3 -12
  62. package/docs/apis/features/web/voice.md +18 -1
  63. package/docs/apis/servers/express.md +18 -2
  64. package/docs/apis/servers/mcp.md +29 -4
  65. package/docs/apis/servers/websocket.md +34 -6
  66. package/docs/bootstrap/CLAUDE.md +100 -0
  67. package/docs/bootstrap/SKILL.md +222 -0
  68. package/docs/bootstrap/templates/about-command.ts +41 -0
  69. package/docs/bootstrap/templates/docs-models.ts +22 -0
  70. package/docs/bootstrap/templates/docs-readme.md +43 -0
  71. package/docs/bootstrap/templates/example-feature.ts +53 -0
  72. package/docs/bootstrap/templates/health-endpoint.ts +15 -0
  73. package/docs/bootstrap/templates/luca-cli.ts +25 -0
  74. package/docs/challenges/caching-proxy.md +16 -0
  75. package/docs/challenges/content-db-round-trip.md +14 -0
  76. package/docs/challenges/custom-command.md +9 -0
  77. package/docs/challenges/file-watcher-pipeline.md +11 -0
  78. package/docs/challenges/grep-audit-report.md +15 -0
  79. package/docs/challenges/multi-feature-dashboard.md +14 -0
  80. package/docs/challenges/process-orchestrator.md +17 -0
  81. package/docs/challenges/rest-api-server-with-client.md +12 -0
  82. package/docs/challenges/script-runner-with-vm.md +11 -0
  83. package/docs/challenges/simple-rest-api.md +15 -0
  84. package/docs/challenges/websocket-serve-and-client.md +11 -0
  85. package/docs/challenges/yaml-config-system.md +14 -0
  86. package/docs/command-system-overhaul.md +94 -0
  87. package/docs/examples/assistant/CORE.md +18 -0
  88. package/docs/examples/assistant/hooks.ts +3 -0
  89. package/docs/examples/assistant/tools.ts +10 -0
  90. package/docs/examples/window-manager-layouts.md +180 -0
  91. package/docs/in-memory-fs.md +4 -0
  92. package/docs/models.ts +13 -10
  93. package/docs/philosophy.md +4 -3
  94. package/docs/reports/console-hmr-design.md +170 -0
  95. package/docs/reports/helper-semantic-search.md +72 -0
  96. package/docs/scaffolds/client.md +29 -20
  97. package/docs/scaffolds/command.md +64 -50
  98. package/docs/scaffolds/endpoint.md +31 -36
  99. package/docs/scaffolds/feature.md +28 -18
  100. package/docs/scaffolds/selector.md +91 -0
  101. package/docs/scaffolds/server.md +18 -9
  102. package/docs/selectors.md +115 -0
  103. package/docs/sessions/custom-command/attempt-log-2.md +195 -0
  104. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
  105. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
  106. package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
  107. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
  108. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
  109. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
  110. package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
  111. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
  112. package/docs/tutorials/00-bootstrap.md +148 -0
  113. package/docs/tutorials/07-endpoints.md +7 -7
  114. package/docs/tutorials/08-commands.md +153 -72
  115. package/luca.cli.ts +3 -0
  116. package/package.json +6 -5
  117. package/public/index.html +1430 -0
  118. package/scripts/examples/using-ollama.ts +2 -1
  119. package/scripts/update-introspection-data.ts +2 -2
  120. package/src/agi/endpoints/experts.ts +1 -1
  121. package/src/agi/features/assistant.ts +7 -0
  122. package/src/agi/features/assistants-manager.ts +5 -5
  123. package/src/agi/features/claude-code.ts +263 -3
  124. package/src/agi/features/conversation-history.ts +7 -1
  125. package/src/agi/features/conversation.ts +26 -3
  126. package/src/agi/features/openai-codex.ts +26 -2
  127. package/src/agi/features/openapi.ts +6 -1
  128. package/src/agi/features/skills-library.ts +9 -1
  129. package/src/bootstrap/generated.ts +540 -0
  130. package/src/cli/cli.ts +64 -21
  131. package/src/client.ts +23 -357
  132. package/src/clients/civitai/index.ts +1 -1
  133. package/src/clients/client-template.ts +1 -1
  134. package/src/clients/comfyui/index.ts +13 -2
  135. package/src/clients/elevenlabs/index.ts +2 -1
  136. package/src/clients/graph.ts +87 -0
  137. package/src/clients/openai/index.ts +10 -1
  138. package/src/clients/rest.ts +207 -0
  139. package/src/clients/websocket.ts +176 -0
  140. package/src/command.ts +281 -34
  141. package/src/commands/bootstrap.ts +181 -0
  142. package/src/commands/chat.ts +5 -4
  143. package/src/commands/describe.ts +225 -2
  144. package/src/commands/help.ts +35 -9
  145. package/src/commands/index.ts +3 -0
  146. package/src/commands/introspect.ts +92 -2
  147. package/src/commands/prompt.ts +5 -6
  148. package/src/commands/run.ts +33 -10
  149. package/src/commands/save-api-docs.ts +49 -0
  150. package/src/commands/scaffold.ts +169 -23
  151. package/src/commands/select.ts +94 -0
  152. package/src/commands/serve.ts +10 -1
  153. package/src/container.ts +15 -0
  154. package/src/endpoint.ts +19 -0
  155. package/src/graft.ts +181 -0
  156. package/src/introspection/generated.agi.ts +12458 -8968
  157. package/src/introspection/generated.node.ts +10573 -7145
  158. package/src/introspection/generated.web.ts +1 -1
  159. package/src/introspection/index.ts +26 -0
  160. package/src/node/container.ts +6 -7
  161. package/src/node/features/content-db.ts +49 -2
  162. package/src/node/features/disk-cache.ts +16 -9
  163. package/src/node/features/dns.ts +16 -3
  164. package/src/node/features/docker.ts +16 -4
  165. package/src/node/features/esbuild.ts +20 -0
  166. package/src/node/features/file-manager.ts +184 -29
  167. package/src/node/features/fs.ts +704 -248
  168. package/src/node/features/git.ts +21 -8
  169. package/src/node/features/grep.ts +23 -3
  170. package/src/node/features/helpers.ts +372 -43
  171. package/src/node/features/networking.ts +39 -4
  172. package/src/node/features/opener.ts +28 -15
  173. package/src/node/features/os.ts +76 -0
  174. package/src/node/features/port-exposer.ts +11 -1
  175. package/src/node/features/postgres.ts +17 -1
  176. package/src/node/features/proc.ts +4 -1
  177. package/src/node/features/python.ts +63 -14
  178. package/src/node/features/repl.ts +11 -7
  179. package/src/node/features/runpod.ts +16 -3
  180. package/src/node/features/secure-shell.ts +27 -2
  181. package/src/node/features/semantic-search.ts +12 -1
  182. package/src/node/features/ui.ts +5 -69
  183. package/src/node/features/vm.ts +17 -0
  184. package/src/node/features/window-manager.ts +68 -20
  185. package/src/node.ts +5 -0
  186. package/src/scaffolds/generated.ts +492 -290
  187. package/src/scaffolds/template.ts +9 -0
  188. package/src/schemas/base.ts +46 -5
  189. package/src/selector.ts +282 -0
  190. package/src/server.ts +11 -0
  191. package/src/servers/express.ts +27 -12
  192. package/src/servers/socket.ts +45 -11
  193. package/src/web/clients/socket.ts +4 -1
  194. package/src/web/container.ts +2 -1
  195. package/src/web/features/network.ts +7 -1
  196. package/src/web/features/voice-recognition.ts +16 -1
  197. package/test/clients-servers.test.ts +2 -1
  198. package/test/command.test.ts +267 -0
  199. package/test-integration/assistants-manager.test.ts +10 -20
  200. package/tmp/.cache/luca-disk-cache/content-v2/sha512/1b/b5/c75b28794f00f94c4d609a98978e9420e9b7146d204a7fbf5b0b30477292581705d207c0100dabaac27eef540aaaece3374af75104a93219d4ec8bfb44e7 +1 -0
  201. package/tmp/.cache/luca-disk-cache/content-v2/sha512/da/df/1d90ce4e042abeb035a197832c6d6893420a747a056be773eb00e4f745a037d505c8db13dde7d36b36b6b893addbb7df0f5fe9f0c13e665f20056447318b +1 -0
  202. package/tmp/.cache/luca-disk-cache/content-v2/sha512/ed/04/e1d0c2a58c2db29b3921ca2affb3ea4febe831c53b38ebc21019fb799823aba6ed5b4611873d2cd25d422d49955b852a9c326da0d678899bc1c2c2960901 +1 -0
  203. package/tmp/.cache/luca-disk-cache/index-v5/00/13/572aa4c9a94f99eda999695d050cdd0ca7fe2d23a50af03234d4c8ce0791 +2 -0
  204. package/tmp/.cache/luca-disk-cache/index-v5/75/a9/cb61dc0f0589e8ec10a9aca27b834bc73884c479941042d22a2b22324cd3 +2 -0
  205. package/tmp/.cache/luca-disk-cache/index-v5/9f/0f/8b1f915ee64cfff7667dd96acd7a5ac0a96aa91a346e19cefd45909a9c9c +2 -0
  206. package/docs/apis/features/node/launcher-app-command-listener.md +0 -145
  207. package/docs/examples/launcher-app-command-listener.md +0 -120
  208. package/docs/tasks/web-container-helper-discovery.md +0 -71
  209. package/docs/todos.md +0 -1
  210. package/scripts/test-command-listener.ts +0 -123
  211. package/src/node/features/launcher-app-command-listener.ts +0 -389
@@ -48,75 +48,11 @@ type ColoredPrintFunction = PrintFunction & {
48
48
 
49
49
  /**
50
50
  * UI Feature - Interactive Terminal User Interface Builder
51
- *
52
- * This feature provides comprehensive tools for creating beautiful, interactive terminal experiences.
53
- * It combines several popular libraries (chalk, figlet, inquirer) into a unified interface for
54
- * building professional CLI applications with colors, ASCII art, and interactive prompts.
55
- *
56
- * **Core Capabilities:**
57
- * - Rich color management using chalk library
58
- * - ASCII art generation with multiple fonts
59
- * - Interactive prompts and wizards
60
- * - Automatic color assignment for consistent theming
61
- * - Text padding and formatting utilities
62
- * - Gradient text effects (horizontal and vertical)
63
- * - Banner creation with styled ASCII art
64
- *
65
- * **Color System:**
66
- * - Full chalk API access for complex styling
67
- * - Automatic color assignment with palette cycling
68
- * - Consistent color mapping for named entities
69
- * - Support for hex colors and gradients
70
- *
71
- * **ASCII Art Features:**
72
- * - Multiple font options via figlet
73
- * - Automatic font discovery and caching
74
- * - Banner creation with color gradients
75
- * - Text styling and effects
76
- *
77
- * **Interactive Elements:**
78
- * - Wizard creation with inquirer integration
79
- * - External editor integration
80
- * - User input validation and processing
81
- *
82
- * **Usage Examples:**
83
- *
84
- * **Basic Colors:**
85
- * ```typescript
86
- * const ui = container.feature('ui');
87
- *
88
- * // Direct color usage
89
- * ui.print.red('Error message');
90
- * ui.print.green('Success!');
91
- *
92
- * // Complex styling
93
- * console.log(ui.colors.blue.bold.underline('Important text'));
94
- * ```
95
- *
96
- * **ASCII Art Banners:**
97
- * ```typescript
98
- * const banner = ui.banner('MyApp', {
99
- * font: 'Big',
100
- * colors: ['red', 'white', 'blue']
101
- * });
102
- * console.log(banner);
103
- * ```
104
- *
105
- * **Interactive Wizards:**
106
- * ```typescript
107
- * const answers = await ui.wizard([
108
- * { type: 'input', name: 'name', message: 'Your name?' },
109
- * { type: 'confirm', name: 'continue', message: 'Continue?' }
110
- * ]);
111
- * ```
112
- *
113
- * **Automatic Color Assignment:**
114
- * ```typescript
115
- * const userColor = ui.assignColor('john');
116
- * const adminColor = ui.assignColor('admin');
117
- * console.log(userColor('John\'s message'));
118
- * console.log(adminColor('Admin notice'));
119
- * ```
51
+ *
52
+ * Unified interface for building professional CLI experiences using chalk (colors/styles),
53
+ * figlet (ASCII art), and inquirer (interactive prompts). Provides rich color management,
54
+ * automatic color assignment, text gradients, banner generation, padding utilities,
55
+ * markdown rendering, and interactive wizards.
120
56
  */
121
57
  export class UI<T extends UIState = UIState> extends Feature<T> {
122
58
  static { Feature.register(this, 'ui') }
@@ -155,6 +155,15 @@ export class VM<
155
155
  createContext(ctx: any = {}) {
156
156
  if (this.isContext(ctx)) return ctx
157
157
  return vm.createContext({
158
+ console,
159
+ setTimeout,
160
+ setInterval,
161
+ clearTimeout,
162
+ clearInterval,
163
+ process,
164
+ Buffer,
165
+ URL,
166
+ URLSearchParams,
158
167
  ...this.container.context,
159
168
  ...ctx
160
169
  })
@@ -340,6 +349,14 @@ export class VM<
340
349
  exports: {},
341
350
  module: { exports: {} },
342
351
  console,
352
+ setTimeout,
353
+ setInterval,
354
+ clearTimeout,
355
+ clearInterval,
356
+ process,
357
+ Buffer,
358
+ URL,
359
+ URLSearchParams,
343
360
  ...ctx,
344
361
  })
345
362
 
@@ -72,15 +72,20 @@ export const WindowManagerEventsSchema = FeatureEventsSchema.extend({
72
72
 
73
73
  // --- Types ---
74
74
 
75
+ /** A dimension value — either absolute points or a percentage string like `"50%"`. */
76
+ export type DimensionValue = number | `${number}%`
77
+
75
78
  /**
76
79
  * Options for spawning a new native browser window.
80
+ * Dimensions and positions accept absolute points or percentage strings (e.g. `"50%"`)
81
+ * resolved against the primary display.
77
82
  */
78
83
  export interface SpawnOptions {
79
84
  url?: string
80
- width?: number
81
- height?: number
82
- x?: number
83
- y?: number
85
+ width?: DimensionValue
86
+ height?: DimensionValue
87
+ x?: DimensionValue
88
+ y?: DimensionValue
84
89
  alwaysOnTop?: boolean
85
90
  window?: {
86
91
  decorations?: 'normal' | 'hiddenTitleBar' | 'none'
@@ -94,6 +99,8 @@ export interface SpawnOptions {
94
99
 
95
100
  /**
96
101
  * Options for spawning a native terminal window.
102
+ * Dimensions and positions accept absolute points or percentage strings (e.g. `"50%"`)
103
+ * resolved against the primary display.
97
104
  */
98
105
  export interface SpawnTTYOptions {
99
106
  /** Executable name or path (required). */
@@ -111,13 +118,13 @@ export interface SpawnTTYOptions {
111
118
  /** Window title. */
112
119
  title?: string
113
120
  /** Window width in points. */
114
- width?: number
121
+ width?: DimensionValue
115
122
  /** Window height in points. */
116
- height?: number
123
+ height?: DimensionValue
117
124
  /** Window x position. */
118
- x?: number
125
+ x?: DimensionValue
119
126
  /** Window y position. */
120
- y?: number
127
+ y?: DimensionValue
121
128
  /** Chrome options (decorations, alwaysOnTop, etc.) */
122
129
  window?: SpawnOptions['window']
123
130
  }
@@ -366,10 +373,7 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
366
373
  }
367
374
 
368
375
  private getBridgeListener(): any | undefined {
369
- const listener = (this.container as any).launcherAppCommandListener
370
- if (!listener || listener === this) return undefined
371
- if (typeof listener.send !== 'function') return undefined
372
- return listener
376
+ return undefined
373
377
  }
374
378
 
375
379
  /** Default state: not listening, no client connected, zero windows tracked. */
@@ -440,13 +444,15 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
440
444
  this.handleClientConnect(socket)
441
445
  })
442
446
 
447
+ // Set immediately to prevent parallel calls from creating duplicate servers
448
+ this._server = server
449
+
443
450
  server.on('error', (err) => {
444
451
  this.setState({ lastError: err.message })
445
452
  })
446
453
 
447
454
  const finalPath = socketPath
448
455
  server.listen(finalPath, () => {
449
- this._server = server
450
456
  this.setState({ listening: true, socketPath: finalPath })
451
457
  this.emit('listening')
452
458
  })
@@ -502,7 +508,8 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
502
508
  * @returns A WindowHandle for the spawned window (with `.result` containing the ack data)
503
509
  */
504
510
  async spawn(opts: SpawnOptions = {}): Promise<WindowHandle> {
505
- const { window: windowChrome, ...flat } = opts
511
+ const resolved = this.resolveDimensions(opts)
512
+ const { window: windowChrome, ...flat } = resolved
506
513
 
507
514
  let ackResult: WindowAckResult
508
515
  if (windowChrome) {
@@ -533,7 +540,8 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
533
540
  * @returns A WindowHandle for the spawned terminal (with `.result` containing the ack data)
534
541
  */
535
542
  async spawnTTY(opts: SpawnTTYOptions): Promise<WindowHandle> {
536
- const { window: windowChrome, ...flat } = opts
543
+ const resolved = this.resolveDimensions(opts)
544
+ const { window: windowChrome, ...flat } = resolved
537
545
 
538
546
  let ackResult: WindowAckResult
539
547
  if (windowChrome) {
@@ -668,16 +676,17 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
668
676
  * ```
669
677
  */
670
678
  async spawnLayout(config: LayoutEntry[]): Promise<WindowHandle[]> {
671
- const promises = config.map(entry => {
679
+ const handles: WindowHandle[] = []
680
+ for (const entry of config) {
672
681
  if (entry.type === 'tty' || ('command' in entry && !entry.type)) {
673
682
  const { type, ...opts } = entry as { type?: string } & SpawnTTYOptions
674
- return this.spawnTTY(opts)
683
+ handles.push(await this.spawnTTY(opts))
675
684
  } else {
676
685
  const { type, ...opts } = entry as { type?: string } & SpawnOptions
677
- return this.spawn(opts)
686
+ handles.push(await this.spawn(opts))
678
687
  }
679
- })
680
- return Promise.all(promises)
688
+ }
689
+ return handles
681
690
  }
682
691
 
683
692
  /**
@@ -718,6 +727,45 @@ export class WindowManager extends Feature<WindowManagerState, WindowManagerOpti
718
727
 
719
728
  // --- Private internals ---
720
729
 
730
+ private _displayCache: { width: number; height: number } | null = null
731
+
732
+ /**
733
+ * Get the primary display resolution, cached for the lifetime of the feature.
734
+ */
735
+ private getPrimaryDisplay(): { width: number; height: number } {
736
+ if (this._displayCache) return this._displayCache
737
+ const osFeature = this.container.feature('os')
738
+ const displays = osFeature.getDisplayInfo()
739
+ const primary = displays.find(d => d.main) ?? displays[0]
740
+ if (!primary) throw new Error('No displays found')
741
+ this._displayCache = { width: primary.resolution.width, height: primary.resolution.height }
742
+ return this._displayCache
743
+ }
744
+
745
+ /**
746
+ * Resolve percentage-based dimension values to absolute points using the primary display.
747
+ * Passes through absolute numbers unchanged. Only fetches display info if percentages are present.
748
+ */
749
+ private resolveDimensions<T extends Record<string, any>>(opts: T): T {
750
+ const keys = ['width', 'height', 'x', 'y'] as const
751
+ const hasPercentage = keys.some(k => typeof opts[k] === 'string' && (opts[k] as string).endsWith('%'))
752
+ if (!hasPercentage) return opts
753
+
754
+ const display = this.getPrimaryDisplay()
755
+ const resolved = { ...opts }
756
+
757
+ for (const key of keys) {
758
+ const val = opts[key]
759
+ if (typeof val === 'string' && val.endsWith('%')) {
760
+ const pct = parseFloat(val) / 100
761
+ const ref = (key === 'width' || key === 'x') ? display.width : display.height
762
+ ;(resolved as any)[key] = Math.round(pct * ref)
763
+ }
764
+ }
765
+
766
+ return resolved
767
+ }
768
+
721
769
  /**
722
770
  * Handle a new client connection from the native app.
723
771
  * Sets up NDJSON buffering and event forwarding.
package/src/node.ts CHANGED
@@ -42,8 +42,12 @@ export { Registry } from './registry'
42
42
 
43
43
  // Helper subclasses & registries
44
44
  export { Client, ClientsRegistry } from './client'
45
+ export { RestClient } from './clients/rest'
46
+ export { GraphClient } from './clients/graph'
47
+ export { WebSocketClient } from './clients/websocket'
45
48
  export { Command, CommandsRegistry, commands } from './command'
46
49
  export { Endpoint, EndpointsRegistry, endpoints } from './endpoint'
50
+ export { Selector, SelectorsRegistry, selectors } from './selector'
47
51
  export { Server, ServersRegistry } from './server'
48
52
  export { FeaturesRegistry } from './feature'
49
53
 
@@ -56,6 +60,7 @@ export type { ContainerContext, ContainerArgv, Plugin, Extension } from './conta
56
60
  export type { AvailableClients } from './client'
57
61
  export type { AvailableCommands, CommandHandler } from './command'
58
62
  export type { AvailableEndpoints, EndpointContext } from './endpoint'
63
+ export type { AvailableSelectors, SelectorsInterface, SelectorRunResult, SimpleSelector } from './selector'
59
64
  export type { AvailableFeatures, FeatureOptions, FeatureState } from './feature'
60
65
  export type { NodeContainer, NodeFeatures } from './node/container'
61
66
  export type { AvailableServers, StartOptions, ServersInterface } from './server'