@soederpop/luca 0.0.2
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.
- package/CLAUDE.md +71 -0
- package/README.md +78 -0
- package/bun.lock +2928 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-scaffolds.ts +154 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/update-introspection.ts +67 -0
- package/docs/CLI.md +335 -0
- package/docs/README.md +88 -0
- package/docs/TABLE-OF-CONTENTS.md +157 -0
- package/docs/apis/clients/elevenlabs.md +84 -0
- package/docs/apis/clients/graph.md +56 -0
- package/docs/apis/clients/openai.md +69 -0
- package/docs/apis/clients/rest.md +41 -0
- package/docs/apis/clients/websocket.md +107 -0
- package/docs/apis/features/agi/assistant.md +471 -0
- package/docs/apis/features/agi/assistants-manager.md +154 -0
- package/docs/apis/features/agi/claude-code.md +602 -0
- package/docs/apis/features/agi/conversation-history.md +352 -0
- package/docs/apis/features/agi/conversation.md +333 -0
- package/docs/apis/features/agi/docs-reader.md +121 -0
- package/docs/apis/features/agi/openai-codex.md +318 -0
- package/docs/apis/features/agi/openapi.md +138 -0
- package/docs/apis/features/agi/semantic-search.md +387 -0
- package/docs/apis/features/agi/skills-library.md +216 -0
- package/docs/apis/features/node/container-link.md +133 -0
- package/docs/apis/features/node/content-db.md +313 -0
- package/docs/apis/features/node/disk-cache.md +379 -0
- package/docs/apis/features/node/dns.md +651 -0
- package/docs/apis/features/node/docker.md +705 -0
- package/docs/apis/features/node/downloader.md +81 -0
- package/docs/apis/features/node/esbuild.md +59 -0
- package/docs/apis/features/node/file-manager.md +182 -0
- package/docs/apis/features/node/fs.md +581 -0
- package/docs/apis/features/node/git.md +330 -0
- package/docs/apis/features/node/google-auth.md +174 -0
- package/docs/apis/features/node/google-calendar.md +187 -0
- package/docs/apis/features/node/google-docs.md +151 -0
- package/docs/apis/features/node/google-drive.md +225 -0
- package/docs/apis/features/node/google-sheets.md +179 -0
- package/docs/apis/features/node/grep.md +290 -0
- package/docs/apis/features/node/helpers.md +135 -0
- package/docs/apis/features/node/ink.md +334 -0
- package/docs/apis/features/node/ipc-socket.md +260 -0
- package/docs/apis/features/node/json-tree.md +86 -0
- package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
- package/docs/apis/features/node/networking.md +281 -0
- package/docs/apis/features/node/nlp.md +133 -0
- package/docs/apis/features/node/opener.md +97 -0
- package/docs/apis/features/node/os.md +118 -0
- package/docs/apis/features/node/package-finder.md +402 -0
- package/docs/apis/features/node/postgres.md +212 -0
- package/docs/apis/features/node/proc.md +430 -0
- package/docs/apis/features/node/process-manager.md +210 -0
- package/docs/apis/features/node/python.md +278 -0
- package/docs/apis/features/node/repl.md +88 -0
- package/docs/apis/features/node/runpod.md +673 -0
- package/docs/apis/features/node/secure-shell.md +169 -0
- package/docs/apis/features/node/semantic-search.md +401 -0
- package/docs/apis/features/node/sqlite.md +211 -0
- package/docs/apis/features/node/telegram.md +254 -0
- package/docs/apis/features/node/tts.md +118 -0
- package/docs/apis/features/node/ui.md +703 -0
- package/docs/apis/features/node/vault.md +64 -0
- package/docs/apis/features/node/vm.md +84 -0
- package/docs/apis/features/node/window-manager.md +337 -0
- package/docs/apis/features/node/yaml-tree.md +85 -0
- package/docs/apis/features/node/yaml.md +176 -0
- package/docs/apis/features/web/asset-loader.md +47 -0
- package/docs/apis/features/web/container-link.md +133 -0
- package/docs/apis/features/web/esbuild.md +59 -0
- package/docs/apis/features/web/helpers.md +135 -0
- package/docs/apis/features/web/network.md +30 -0
- package/docs/apis/features/web/speech.md +55 -0
- package/docs/apis/features/web/vault.md +64 -0
- package/docs/apis/features/web/vm.md +84 -0
- package/docs/apis/features/web/voice.md +67 -0
- package/docs/apis/servers/express.md +127 -0
- package/docs/apis/servers/mcp.md +213 -0
- package/docs/apis/servers/websocket.md +99 -0
- package/docs/documentation-audit.md +134 -0
- package/docs/examples/content-db.md +77 -0
- package/docs/examples/disk-cache.md +83 -0
- package/docs/examples/docker.md +101 -0
- package/docs/examples/downloader.md +70 -0
- package/docs/examples/esbuild.md +80 -0
- package/docs/examples/file-manager.md +82 -0
- package/docs/examples/fs.md +83 -0
- package/docs/examples/git.md +85 -0
- package/docs/examples/google-auth.md +88 -0
- package/docs/examples/google-calendar.md +94 -0
- package/docs/examples/google-docs.md +82 -0
- package/docs/examples/google-drive.md +96 -0
- package/docs/examples/google-sheets.md +95 -0
- package/docs/examples/grep.md +85 -0
- package/docs/examples/ink-blocks.md +75 -0
- package/docs/examples/ink-renderer.md +41 -0
- package/docs/examples/ink.md +103 -0
- package/docs/examples/ipc-socket.md +103 -0
- package/docs/examples/json-tree.md +91 -0
- package/docs/examples/launcher-app-command-listener.md +120 -0
- package/docs/examples/networking.md +58 -0
- package/docs/examples/nlp.md +91 -0
- package/docs/examples/opener.md +78 -0
- package/docs/examples/os.md +72 -0
- package/docs/examples/package-finder.md +89 -0
- package/docs/examples/port-exposer.md +89 -0
- package/docs/examples/postgres.md +91 -0
- package/docs/examples/proc.md +81 -0
- package/docs/examples/process-manager.md +79 -0
- package/docs/examples/python.md +91 -0
- package/docs/examples/repl.md +93 -0
- package/docs/examples/runpod.md +119 -0
- package/docs/examples/secure-shell.md +92 -0
- package/docs/examples/sqlite.md +86 -0
- package/docs/examples/telegram.md +77 -0
- package/docs/examples/tts.md +86 -0
- package/docs/examples/ui.md +80 -0
- package/docs/examples/vault.md +70 -0
- package/docs/examples/vm.md +86 -0
- package/docs/examples/window-manager.md +125 -0
- package/docs/examples/yaml-tree.md +93 -0
- package/docs/examples/yaml.md +104 -0
- package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
- package/docs/ideas/container-use-api.md +9 -0
- package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
- package/docs/ideas/feature-stacks.md +22 -0
- package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
- package/docs/ideas/mcp-design.md +9 -0
- package/docs/ideas/web-container-debugging-feature.md +13 -0
- package/docs/introspection-audit.md +49 -0
- package/docs/introspection.md +154 -0
- package/docs/mcp/readme.md +162 -0
- package/docs/models.ts +38 -0
- package/docs/philosophy.md +85 -0
- package/docs/principles.md +7 -0
- package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
- package/docs/prompts/mcp-test-easy-command.md +27 -0
- package/docs/reports/assistant-bugs.md +38 -0
- package/docs/reports/attach-pattern-usage.md +18 -0
- package/docs/reports/code-audit-results.md +391 -0
- package/docs/reports/introspection-audit-tasks.md +378 -0
- package/docs/reports/luca-mcp-improvements.md +128 -0
- package/docs/scaffolds/client.md +140 -0
- package/docs/scaffolds/command.md +106 -0
- package/docs/scaffolds/endpoint.md +176 -0
- package/docs/scaffolds/feature.md +148 -0
- package/docs/scaffolds/server.md +187 -0
- package/docs/tasks/web-container-helper-discovery.md +71 -0
- package/docs/todos.md +1 -0
- package/docs/tutorials/01-getting-started.md +106 -0
- package/docs/tutorials/02-container.md +210 -0
- package/docs/tutorials/03-scripts.md +194 -0
- package/docs/tutorials/04-features-overview.md +196 -0
- package/docs/tutorials/05-state-and-events.md +171 -0
- package/docs/tutorials/06-servers.md +157 -0
- package/docs/tutorials/07-endpoints.md +198 -0
- package/docs/tutorials/08-commands.md +171 -0
- package/docs/tutorials/09-clients.md +162 -0
- package/docs/tutorials/10-creating-features.md +198 -0
- package/docs/tutorials/11-contentbase.md +191 -0
- package/docs/tutorials/12-assistants.md +215 -0
- package/docs/tutorials/13-introspection.md +147 -0
- package/docs/tutorials/14-type-system.md +174 -0
- package/docs/tutorials/15-project-patterns.md +222 -0
- package/docs/tutorials/16-google-features.md +534 -0
- package/docs/tutorials/17-tui-blocks.md +530 -0
- package/docs/tutorials/18-semantic-search.md +334 -0
- package/index.ts +1 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +154 -0
- package/pyproject.toml +7 -0
- package/scripts/animations/chrome-glitch.ts +55 -0
- package/scripts/animations/index.ts +16 -0
- package/scripts/animations/neon-pulse.ts +64 -0
- package/scripts/animations/types.ts +6 -0
- package/scripts/build-web.ts +28 -0
- package/scripts/examples/ask-luca-expert.ts +42 -0
- package/scripts/examples/assistant-questions.ts +12 -0
- package/scripts/examples/excalidraw-expert.ts +75 -0
- package/scripts/examples/expert-chat.ts +0 -0
- package/scripts/examples/file-manager.ts +14 -0
- package/scripts/examples/ideas.ts +12 -0
- package/scripts/examples/interactive-chat.ts +20 -0
- package/scripts/examples/openai-tool-calls.ts +113 -0
- package/scripts/examples/opening-a-web-browser.ts +5 -0
- package/scripts/examples/telegram-bot.ts +79 -0
- package/scripts/examples/telegram-ink-ui.ts +302 -0
- package/scripts/examples/using-assistant-with-mcp.ts +560 -0
- package/scripts/examples/using-claude-code.ts +10 -0
- package/scripts/examples/using-contentdb.ts +35 -0
- package/scripts/examples/using-conversations.ts +35 -0
- package/scripts/examples/using-disk-cache.ts +10 -0
- package/scripts/examples/using-docker-shell.ts +75 -0
- package/scripts/examples/using-elevenlabs.ts +25 -0
- package/scripts/examples/using-google-calendar.ts +57 -0
- package/scripts/examples/using-google-docs.ts +74 -0
- package/scripts/examples/using-google-drive.ts +74 -0
- package/scripts/examples/using-google-sheets.ts +89 -0
- package/scripts/examples/using-nlp.ts +55 -0
- package/scripts/examples/using-ollama.ts +10 -0
- package/scripts/examples/using-openai-codex.ts +23 -0
- package/scripts/examples/using-postgres.ts +55 -0
- package/scripts/examples/using-runpod.ts +32 -0
- package/scripts/examples/using-tts.ts +40 -0
- package/scripts/examples/vm-loading-esm-modules.ts +16 -0
- package/scripts/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/test-command-listener.ts +123 -0
- package/scripts/test-window-manager-lifecycle.ts +86 -0
- package/scripts/test-window-manager.ts +43 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +114 -0
- package/src/agi/endpoints/ask.ts +60 -0
- package/src/agi/endpoints/conversations/[id].ts +45 -0
- package/src/agi/endpoints/conversations.ts +31 -0
- package/src/agi/endpoints/experts.ts +37 -0
- package/src/agi/features/assistant.ts +767 -0
- package/src/agi/features/assistants-manager.ts +260 -0
- package/src/agi/features/claude-code.ts +1111 -0
- package/src/agi/features/conversation-history.ts +497 -0
- package/src/agi/features/conversation.ts +799 -0
- package/src/agi/features/openai-codex.ts +631 -0
- package/src/agi/features/openapi.ts +438 -0
- package/src/agi/features/skills-library.ts +425 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/token-counter.ts +122 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +100 -0
- package/src/cli/cli.ts +70 -0
- package/src/client.ts +461 -0
- package/src/clients/civitai/index.ts +541 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +597 -0
- package/src/clients/elevenlabs/index.ts +291 -0
- package/src/clients/openai/index.ts +451 -0
- package/src/clients/supabase/index.ts +366 -0
- package/src/command.ts +164 -0
- package/src/commands/chat.ts +182 -0
- package/src/commands/console.ts +192 -0
- package/src/commands/describe.ts +433 -0
- package/src/commands/eval.ts +116 -0
- package/src/commands/help.ts +214 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +807 -0
- package/src/commands/run.ts +257 -0
- package/src/commands/sandbox-mcp.ts +439 -0
- package/src/commands/scaffold.ts +79 -0
- package/src/commands/serve.ts +172 -0
- package/src/container.ts +781 -0
- package/src/endpoint.ts +340 -0
- package/src/feature.ts +75 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +543 -0
- package/src/introspection/generated.agi.ts +23388 -0
- package/src/introspection/generated.node.ts +18899 -0
- package/src/introspection/generated.web.ts +2021 -0
- package/src/introspection/index.ts +256 -0
- package/src/introspection/scan.ts +912 -0
- package/src/node/container.ts +354 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +558 -0
- package/src/node/features/content-db.ts +475 -0
- package/src/node/features/disk-cache.ts +382 -0
- package/src/node/features/dns.ts +655 -0
- package/src/node/features/docker.ts +912 -0
- package/src/node/features/downloader.ts +92 -0
- package/src/node/features/esbuild.ts +68 -0
- package/src/node/features/file-manager.ts +357 -0
- package/src/node/features/fs.ts +534 -0
- package/src/node/features/git.ts +492 -0
- package/src/node/features/google-auth.ts +502 -0
- package/src/node/features/google-calendar.ts +300 -0
- package/src/node/features/google-docs.ts +404 -0
- package/src/node/features/google-drive.ts +339 -0
- package/src/node/features/google-sheets.ts +279 -0
- package/src/node/features/grep.ts +406 -0
- package/src/node/features/helpers.ts +374 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +459 -0
- package/src/node/features/json-tree.ts +188 -0
- package/src/node/features/launcher-app-command-listener.ts +388 -0
- package/src/node/features/networking.ts +925 -0
- package/src/node/features/nlp.ts +211 -0
- package/src/node/features/opener.ts +166 -0
- package/src/node/features/os.ts +157 -0
- package/src/node/features/package-finder.ts +539 -0
- package/src/node/features/port-exposer.ts +342 -0
- package/src/node/features/postgres.ts +273 -0
- package/src/node/features/proc.ts +502 -0
- package/src/node/features/process-manager.ts +542 -0
- package/src/node/features/python.ts +444 -0
- package/src/node/features/repl.ts +194 -0
- package/src/node/features/runpod.ts +802 -0
- package/src/node/features/secure-shell.ts +248 -0
- package/src/node/features/semantic-search.ts +924 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +342 -0
- package/src/node/features/tts.ts +184 -0
- package/src/node/features/ui.ts +857 -0
- package/src/node/features/vault.ts +164 -0
- package/src/node/features/vm.ts +312 -0
- package/src/node/features/window-manager.ts +804 -0
- package/src/node/features/yaml-tree.ts +149 -0
- package/src/node/features/yaml.ts +132 -0
- package/src/node.ts +70 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +199 -0
- package/src/scaffolds/generated.ts +1613 -0
- package/src/scaffolds/template.ts +37 -0
- package/src/schemas/base.ts +255 -0
- package/src/server.ts +135 -0
- package/src/servers/express.ts +209 -0
- package/src/servers/mcp.ts +805 -0
- package/src/servers/socket.ts +120 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +82 -0
- package/src/web/container.ts +74 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +64 -0
- package/src/web/features/container-link.ts +385 -0
- package/src/web/features/esbuild.ts +79 -0
- package/src/web/features/helpers.ts +267 -0
- package/src/web/features/network.ts +61 -0
- package/src/web/features/speech.ts +87 -0
- package/src/web/features/vault.ts +189 -0
- package/src/web/features/vm.ts +78 -0
- package/src/web/features/voice-recognition.ts +129 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/test/bus.test.ts +134 -0
- package/test/clients-servers.test.ts +216 -0
- package/test/container-link.test.ts +274 -0
- package/test/features.test.ts +160 -0
- package/test/integration.test.ts +787 -0
- package/test/node-container.test.ts +121 -0
- package/test/rate-limit.test.ts +272 -0
- package/test/semantic-search.test.ts +550 -0
- package/test/state.test.ts +121 -0
- package/test-integration/assistant.test.ts +138 -0
- package/test-integration/assistants-manager.test.ts +123 -0
- package/test-integration/claude-code.test.ts +98 -0
- package/test-integration/conversation-history.test.ts +205 -0
- package/test-integration/conversation.test.ts +137 -0
- package/test-integration/elevenlabs.test.ts +55 -0
- package/test-integration/google-services.test.ts +80 -0
- package/test-integration/helpers.ts +89 -0
- package/test-integration/openai-codex.test.ts +93 -0
- package/test-integration/runpod.test.ts +58 -0
- package/test-integration/server-endpoints.test.ts +97 -0
- package/test-integration/skills-library.test.ts +157 -0
- package/test-integration/telegram.test.ts +46 -0
- package/tsconfig.json +58 -0
- package/uv.lock +8 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { inspect } from 'util'
|
|
3
|
+
import { commands } from '../command.js'
|
|
4
|
+
import { CommandOptionsSchema } from '../schemas/base.js'
|
|
5
|
+
import type { ContainerContext } from '../container.js'
|
|
6
|
+
|
|
7
|
+
declare module '../command.js' {
|
|
8
|
+
interface AvailableCommands {
|
|
9
|
+
eval: ReturnType<typeof commands.registerHandler>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
14
|
+
json: z.boolean().default(false).describe('Serialize output as JSON'),
|
|
15
|
+
enable: z.string().optional().describe('Enable a feature before evaluating (e.g. --enable diskCache)'),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export default async function evalCommand(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
19
|
+
const container = context.container as any
|
|
20
|
+
|
|
21
|
+
container.addContext('feature', (...args: any) => container.feature(...args))
|
|
22
|
+
|
|
23
|
+
await container.helpers.discoverAll()
|
|
24
|
+
|
|
25
|
+
const args = container.argv._ as string[]
|
|
26
|
+
// args[0] is "eval", the rest is the code snippet
|
|
27
|
+
const code = args.slice(1).join(' ')
|
|
28
|
+
|
|
29
|
+
if (!code.trim()) {
|
|
30
|
+
console.error('Usage: luca eval "<code>" [--json]')
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const vm = container.feature('vm')
|
|
35
|
+
|
|
36
|
+
// HACK
|
|
37
|
+
Array(container.argv.enable).map((id) => {
|
|
38
|
+
container.feature(id, { ...container.argv, enable: true }).enable()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Build context with container and all enabled feature instances
|
|
42
|
+
const ctx: Record<string, any> = { container }
|
|
43
|
+
for (const [name, instance] of Object.entries(container.enabledFeatures ?? {})) {
|
|
44
|
+
ctx[name] = instance
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const result = await vm.run(code, ctx)
|
|
48
|
+
|
|
49
|
+
if (options.json) {
|
|
50
|
+
console.log(JSON.stringify(result, null, 2))
|
|
51
|
+
} else {
|
|
52
|
+
displayResult(result)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const BUILTIN_TYPES = new Set(['Object', 'Array', 'Map', 'Set', 'Date', 'RegExp', 'Promise', 'Error', 'Number', 'String', 'Boolean'])
|
|
57
|
+
|
|
58
|
+
export function displayResult(value: any) {
|
|
59
|
+
if (typeof value !== 'object' || value === null) {
|
|
60
|
+
console.log(value)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hasCustomInspect = typeof value[Symbol.for('nodejs.util.inspect.custom')] === 'function'
|
|
65
|
+
const ctorName = value.constructor?.name
|
|
66
|
+
const isClassInstance = ctorName && !BUILTIN_TYPES.has(ctorName)
|
|
67
|
+
|
|
68
|
+
// Objects with custom inspect or builtins: use standard inspect
|
|
69
|
+
if (hasCustomInspect || !isClassInstance) {
|
|
70
|
+
console.log(inspect(value, { colors: true, depth: 4 }))
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Class instances: show clean data (no _ props, no functions)
|
|
75
|
+
const data: Record<string, any> = {}
|
|
76
|
+
for (const [k, v] of Object.entries(value)) {
|
|
77
|
+
if (k.startsWith('_') || typeof v === 'function') continue
|
|
78
|
+
data[k] = v
|
|
79
|
+
}
|
|
80
|
+
const body = inspect(data, { colors: true, depth: 3 })
|
|
81
|
+
console.log(`${ctorName} ${body}`)
|
|
82
|
+
|
|
83
|
+
// Collect methods and getters from own + prototype chain
|
|
84
|
+
const methods: string[] = []
|
|
85
|
+
const getters: string[] = []
|
|
86
|
+
|
|
87
|
+
for (const [k, v] of Object.entries(value)) {
|
|
88
|
+
if (k.startsWith('_')) continue
|
|
89
|
+
if (typeof v === 'function') methods.push(k)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let proto = Object.getPrototypeOf(value)
|
|
93
|
+
while (proto && proto !== Object.prototype) {
|
|
94
|
+
for (const k of Object.getOwnPropertyNames(proto)) {
|
|
95
|
+
if (k === 'constructor' || k.startsWith('_')) continue
|
|
96
|
+
const desc = Object.getOwnPropertyDescriptor(proto, k)
|
|
97
|
+
if (!desc) continue
|
|
98
|
+
if (desc.get && !getters.includes(k)) getters.push(k)
|
|
99
|
+
else if (typeof desc.value === 'function' && !methods.includes(k)) methods.push(k)
|
|
100
|
+
}
|
|
101
|
+
proto = Object.getPrototypeOf(proto)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (getters.length || methods.length) {
|
|
105
|
+
const parts: string[] = []
|
|
106
|
+
if (getters.length) parts.push(` \x1b[36mgetters:\x1b[0m ${getters.sort().join(', ')}`)
|
|
107
|
+
if (methods.length) parts.push(` \x1b[36mmethods:\x1b[0m ${methods.sort().map(m => m + '()').join(', ')}`)
|
|
108
|
+
console.log(parts.join('\n'))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
commands.registerHandler('eval', {
|
|
113
|
+
description: 'Evaluate a JavaScript/TypeScript expression with the container in scope',
|
|
114
|
+
argsSchema,
|
|
115
|
+
handler: evalCommand,
|
|
116
|
+
})
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { commands } from '../command.js'
|
|
3
|
+
import { CommandOptionsSchema } from '../schemas/base.js'
|
|
4
|
+
import type { ContainerContext } from '../container.js'
|
|
5
|
+
|
|
6
|
+
declare module '../command.js' {
|
|
7
|
+
interface AvailableCommands {
|
|
8
|
+
help: ReturnType<typeof commands.registerHandler>
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const argsSchema = CommandOptionsSchema.extend({})
|
|
13
|
+
|
|
14
|
+
/** Hidden option prefixes — legacy aliases that shouldn't clutter help output. */
|
|
15
|
+
const HIDDEN_PREFIXES = ['only-']
|
|
16
|
+
const HIDDEN_KEYS = new Set(['_', 'name', '_cacheKey'])
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Extract CLI option info from a Zod schema.
|
|
20
|
+
* Walks through Zod v4 wrapper types (default, optional) to find descriptions, types, and defaults.
|
|
21
|
+
*/
|
|
22
|
+
function extractOptions(schema: any): Array<{ flag: string; description: string; type: string; defaultValue?: any }> {
|
|
23
|
+
if (!schema?.shape) return []
|
|
24
|
+
|
|
25
|
+
const options: Array<{ flag: string; description: string; type: string; defaultValue?: any }> = []
|
|
26
|
+
|
|
27
|
+
for (const [key, field] of Object.entries(schema.shape)) {
|
|
28
|
+
if (HIDDEN_KEYS.has(key)) continue
|
|
29
|
+
if (HIDDEN_PREFIXES.some((p) => key.startsWith(p))) continue
|
|
30
|
+
|
|
31
|
+
const f = field as any
|
|
32
|
+
// In Zod v4, description lives on the schema object itself
|
|
33
|
+
const description = f.description || ''
|
|
34
|
+
let type = 'string'
|
|
35
|
+
let defaultValue: any = undefined
|
|
36
|
+
|
|
37
|
+
// Walk through wrapper types to find leaf type and default
|
|
38
|
+
let current = f
|
|
39
|
+
while (current) {
|
|
40
|
+
const defType = current._def?.type || current.type
|
|
41
|
+
if (defType === 'default') {
|
|
42
|
+
defaultValue = current._def?.defaultValue
|
|
43
|
+
if (typeof defaultValue === 'function') defaultValue = defaultValue()
|
|
44
|
+
}
|
|
45
|
+
if (defType === 'boolean') { type = 'boolean'; break }
|
|
46
|
+
if (defType === 'string') { type = 'string'; break }
|
|
47
|
+
if (defType === 'number') { type = 'number'; break }
|
|
48
|
+
if (defType === 'enum') { type = current.options?.join(' | ') || 'enum'; break }
|
|
49
|
+
// Unwrap
|
|
50
|
+
current = current._def?.innerType
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
options.push({ flag: key, description, type, defaultValue })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return options
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format CLI-oriented help text for a single command.
|
|
61
|
+
* Exported so other commands (like describe) can reuse it.
|
|
62
|
+
*/
|
|
63
|
+
export function formatCommandHelp(name: string, Cmd: any, colors: any): string {
|
|
64
|
+
const desc = Cmd.commandDescription || ''
|
|
65
|
+
const schema = Cmd.argsSchema
|
|
66
|
+
const lines: string[] = []
|
|
67
|
+
|
|
68
|
+
lines.push('')
|
|
69
|
+
lines.push(` ${colors.cyan.bold(`luca ${name}`)} ${desc ? `${colors.dim('—')} ${desc}` : ''}`)
|
|
70
|
+
lines.push('')
|
|
71
|
+
|
|
72
|
+
const options = extractOptions(schema)
|
|
73
|
+
|
|
74
|
+
if (options.length === 0) {
|
|
75
|
+
lines.push(` ${colors.white('Usage:')} ${colors.cyan(`luca ${name}`)}`)
|
|
76
|
+
} else {
|
|
77
|
+
const booleans = options.filter((o) => o.type === 'boolean')
|
|
78
|
+
const valued = options.filter((o) => o.type !== 'boolean')
|
|
79
|
+
|
|
80
|
+
lines.push(` ${colors.white('Usage:')} ${colors.cyan(`luca ${name}`)} ${colors.dim('[options]')}`)
|
|
81
|
+
lines.push('')
|
|
82
|
+
|
|
83
|
+
if (valued.length > 0) {
|
|
84
|
+
lines.push(` ${colors.white('Options:')}`)
|
|
85
|
+
lines.push('')
|
|
86
|
+
const maxLen = Math.max(...valued.map((o) => `--${o.flag} <${o.type}>`.length))
|
|
87
|
+
for (const opt of valued) {
|
|
88
|
+
const flag = `--${opt.flag} <${opt.type}>`
|
|
89
|
+
let line = ` ${colors.green(flag.padEnd(maxLen + 2))} ${opt.description}`
|
|
90
|
+
if (opt.defaultValue !== undefined && opt.defaultValue !== false) {
|
|
91
|
+
line += ` ${colors.dim(`(default: ${opt.defaultValue})`)}`
|
|
92
|
+
}
|
|
93
|
+
lines.push(line)
|
|
94
|
+
}
|
|
95
|
+
lines.push('')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (booleans.length > 0) {
|
|
99
|
+
lines.push(` ${colors.white('Flags:')}`)
|
|
100
|
+
lines.push('')
|
|
101
|
+
const maxLen = Math.max(...booleans.map((o) => `--${o.flag}`.length))
|
|
102
|
+
for (const opt of booleans) {
|
|
103
|
+
const flag = `--${opt.flag}`
|
|
104
|
+
let line = ` ${colors.green(flag.padEnd(maxLen + 2))} ${opt.description}`
|
|
105
|
+
if (opt.defaultValue === true) {
|
|
106
|
+
line += ` ${colors.dim('(default: true)')}`
|
|
107
|
+
}
|
|
108
|
+
lines.push(line)
|
|
109
|
+
}
|
|
110
|
+
lines.push('')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return lines.join('\n')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Strip ANSI escape codes for visible width calculation. */
|
|
118
|
+
function stripAnsi(s: string): string {
|
|
119
|
+
return s.replace(/\x1B\[[0-9;]*m/g, '')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Merge two multi-line blocks side by side with a gap. */
|
|
123
|
+
function sideBySide(left: string[], right: string[], gap = 3): string[] {
|
|
124
|
+
const maxLeftWidth = Math.max(...left.map((l) => stripAnsi(l).length))
|
|
125
|
+
const maxLines = Math.max(left.length, right.length)
|
|
126
|
+
const result: string[] = []
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < maxLines; i++) {
|
|
129
|
+
const l = left[i] || ''
|
|
130
|
+
const r = right[i] || ''
|
|
131
|
+
const visLen = stripAnsi(l).length
|
|
132
|
+
const pad = Math.max(0, maxLeftWidth - visLen) + gap
|
|
133
|
+
result.push(l + ' '.repeat(pad) + r)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return result
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const LEGO_ROBOT = [
|
|
140
|
+
' ┌─○○─┐ ',
|
|
141
|
+
' │ ●● │ ',
|
|
142
|
+
' ├○──○┤ ',
|
|
143
|
+
' └─╨╨─┘ ',
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
const BANNER_COLORS: string[] = ['cyan', 'blue', 'magenta']
|
|
147
|
+
|
|
148
|
+
export default async function help(_options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
149
|
+
const container = context.container as any
|
|
150
|
+
const ui = container.feature('ui') as any
|
|
151
|
+
const c = ui.colors
|
|
152
|
+
|
|
153
|
+
const args = container.argv._ as string[]
|
|
154
|
+
const target = args[1] as string
|
|
155
|
+
|
|
156
|
+
if (!target) {
|
|
157
|
+
// Robot (left) + banner (right), same height — direct 1:1 alignment
|
|
158
|
+
const banner = ui.banner('luca', { font: 'Small Slant', colors: BANNER_COLORS })
|
|
159
|
+
const bannerLines = banner.split('\n').filter((l: string) => l.trim())
|
|
160
|
+
const coloredRobot = ui.applyGradient(LEGO_ROBOT.join('\n'), BANNER_COLORS)
|
|
161
|
+
const robotLines = coloredRobot.split('\n') as string[]
|
|
162
|
+
const robotWidth = Math.max(...LEGO_ROBOT.map((l: string) => l.length))
|
|
163
|
+
|
|
164
|
+
const headerLines: string[] = []
|
|
165
|
+
const maxLines = Math.max(robotLines.length, bannerLines.length)
|
|
166
|
+
for (let i = 0; i < maxLines; i++) {
|
|
167
|
+
const rLine = robotLines[i] || ''
|
|
168
|
+
const rPad = robotWidth - stripAnsi(rLine).length
|
|
169
|
+
const bLine = bannerLines[i] || ''
|
|
170
|
+
headerLines.push(rLine + ' '.repeat(rPad + 2) + bLine)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log('\n')
|
|
174
|
+
console.log(headerLines.join('\n'))
|
|
175
|
+
console.log(c.dim(' Lightweight Universal Conversational Architecture'))
|
|
176
|
+
console.log()
|
|
177
|
+
console.log(c.white(' Usage: ') + c.cyan('luca') + c.dim(' <command|file> [options]'))
|
|
178
|
+
console.log()
|
|
179
|
+
console.log(c.white(' Commands:'))
|
|
180
|
+
console.log()
|
|
181
|
+
|
|
182
|
+
// Dynamic padding based on longest command name
|
|
183
|
+
const commandNames = (container.commands.available as string[]).filter((n: string) => n !== 'help')
|
|
184
|
+
const maxNameLen = Math.max(...commandNames.map((n: string) => n.length)) + 2
|
|
185
|
+
|
|
186
|
+
for (const name of commandNames) {
|
|
187
|
+
const Cmd = container.commands.lookup(name) as any
|
|
188
|
+
const desc = Cmd.commandDescription || ''
|
|
189
|
+
console.log(` ${c.cyan(name.padEnd(maxNameLen))} ${c.dim(desc)}`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log()
|
|
193
|
+
console.log(c.dim(' Run ') + c.cyan('luca <file>') + c.dim(' to execute a script or markdown (.ts, .js, .md)'))
|
|
194
|
+
console.log(c.dim(' Run ') + c.cyan('luca help <command>') + c.dim(' for detailed usage of a command'))
|
|
195
|
+
console.log()
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!container.commands.has(target)) {
|
|
200
|
+
console.error(` Unknown command: ${c.red(target)}`)
|
|
201
|
+
console.error()
|
|
202
|
+
console.error(` Run ${c.cyan('luca help')} to see available commands.`)
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const Cmd = container.commands.lookup(target) as any
|
|
207
|
+
console.log(formatCommandHelp(target, Cmd, c))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
commands.registerHandler('help', {
|
|
211
|
+
description: 'Show help for luca commands',
|
|
212
|
+
argsSchema,
|
|
213
|
+
handler: help,
|
|
214
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { Command, commands, CommandsRegistry, type AvailableCommands, type CommandsInterface, type CommandHandler, type CommandState, type CommandOptions } from '../command.js'
|
|
2
|
+
|
|
3
|
+
// Side-effect imports register each command
|
|
4
|
+
import './run.js'
|
|
5
|
+
import './console.js'
|
|
6
|
+
import './serve.js'
|
|
7
|
+
import './chat.js'
|
|
8
|
+
import './prompt.js'
|
|
9
|
+
import './mcp.js'
|
|
10
|
+
import './sandbox-mcp.js'
|
|
11
|
+
import './describe.js'
|
|
12
|
+
import './eval.js'
|
|
13
|
+
import './help.js'
|
|
14
|
+
import './scaffold.js'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { commands } from '../command.js'
|
|
3
|
+
import { CommandOptionsSchema } from '../schemas/base.js'
|
|
4
|
+
import type { ContainerContext } from '../container.js'
|
|
5
|
+
import type { MCPServer } from '../servers/mcp.js'
|
|
6
|
+
|
|
7
|
+
declare module '../command.js' {
|
|
8
|
+
interface AvailableCommands {
|
|
9
|
+
mcp: ReturnType<typeof commands.registerHandler>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
14
|
+
transport: z.enum(['stdio', 'http']).default('stdio').describe('Transport type (stdio or http)'),
|
|
15
|
+
port: z.number().default(3001).describe('Port for HTTP transport'),
|
|
16
|
+
name: z.string().optional().describe('Server name reported to MCP clients'),
|
|
17
|
+
version: z.string().optional().describe('Server version reported to MCP clients'),
|
|
18
|
+
mcpCompat: z.enum(['standard', 'codex']).optional()
|
|
19
|
+
.describe('HTTP compatibility profile. Defaults to standard. Can also be set via MCP_HTTP_COMPAT.'),
|
|
20
|
+
stdioCompat: z.enum(['standard', 'codex', 'auto']).optional()
|
|
21
|
+
.describe('Stdio framing compatibility profile. Defaults to standard. Can also be set via MCP_STDIO_COMPAT.'),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default async function mcp(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
25
|
+
const container = context.container as any
|
|
26
|
+
const envCompat = process.env.MCP_HTTP_COMPAT?.toLowerCase()
|
|
27
|
+
const resolvedCompat = options.mcpCompat || (envCompat === 'codex' ? 'codex' : 'standard')
|
|
28
|
+
const envStdioCompat = process.env.MCP_STDIO_COMPAT?.toLowerCase()
|
|
29
|
+
const resolvedStdioCompat = options.stdioCompat
|
|
30
|
+
|| (envStdioCompat === 'codex' || envStdioCompat === 'auto' ? envStdioCompat : 'standard')
|
|
31
|
+
|
|
32
|
+
const mcpServer = container.server('mcp', {
|
|
33
|
+
transport: options.transport,
|
|
34
|
+
port: options.port,
|
|
35
|
+
serverName: options.name || container.manifest?.name || 'luca-mcp',
|
|
36
|
+
serverVersion: options.version || container.manifest?.version || '1.0.0',
|
|
37
|
+
mcpCompat: options.mcpCompat,
|
|
38
|
+
stdioCompat: options.stdioCompat,
|
|
39
|
+
}) as MCPServer
|
|
40
|
+
|
|
41
|
+
await mcpServer.start({
|
|
42
|
+
transport: options.transport,
|
|
43
|
+
port: options.port,
|
|
44
|
+
mcpCompat: options.mcpCompat,
|
|
45
|
+
stdioCompat: options.stdioCompat,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (options.transport === 'http') {
|
|
49
|
+
const name = options.name || container.manifest?.name || 'MCP Server'
|
|
50
|
+
console.log(`\n${name} listening on http://localhost:${options.port}/mcp`)
|
|
51
|
+
console.log(`Transport: HTTP (Streamable)`)
|
|
52
|
+
console.log(`Compatibility: ${resolvedCompat}`)
|
|
53
|
+
} else {
|
|
54
|
+
// stdio mode — don't print to stdout as it's used for the protocol
|
|
55
|
+
console.error(`MCP server started (stdio transport)`)
|
|
56
|
+
console.error(`Stdio Compatibility: ${resolvedStdioCompat}`)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
commands.registerHandler('mcp', {
|
|
61
|
+
description: 'Start an MCP (Model Context Protocol) server',
|
|
62
|
+
argsSchema,
|
|
63
|
+
handler: mcp,
|
|
64
|
+
})
|