@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,79 @@
|
|
|
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 { scaffolds } from '../scaffolds/generated.js'
|
|
6
|
+
import { generateScaffold, toCamelCase } from '../scaffolds/template.js'
|
|
7
|
+
|
|
8
|
+
declare module '../command.js' {
|
|
9
|
+
interface AvailableCommands {
|
|
10
|
+
scaffold: ReturnType<typeof commands.registerHandler>
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const validTypes = Object.keys(scaffolds)
|
|
15
|
+
|
|
16
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
17
|
+
description: z.string().optional().describe('Brief description of the helper'),
|
|
18
|
+
output: z.string().optional().describe('Output file path (defaults to stdout)'),
|
|
19
|
+
tutorial: z.boolean().default(false).describe('Show the full tutorial instead of generating code'),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export default async function scaffoldCommand(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
23
|
+
const container = context.container as any
|
|
24
|
+
const args = container.argv._ as string[]
|
|
25
|
+
|
|
26
|
+
// args: ["scaffold", type?, name?]
|
|
27
|
+
const type = args[1]
|
|
28
|
+
const name = args[2]
|
|
29
|
+
|
|
30
|
+
if (!type || !validTypes.includes(type)) {
|
|
31
|
+
console.log(`Usage: luca scaffold <type> <name> [--description "..."] [--output path] [--tutorial]`)
|
|
32
|
+
console.log(`\nTypes: ${validTypes.join(', ')}`)
|
|
33
|
+
console.log(`\nExamples:`)
|
|
34
|
+
console.log(` luca scaffold feature diskCache --description "File-backed key-value cache"`)
|
|
35
|
+
console.log(` luca scaffold command deploy --output commands/deploy.ts`)
|
|
36
|
+
console.log(` luca scaffold endpoint healthCheck`)
|
|
37
|
+
console.log(` luca scaffold feature --tutorial`)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Tutorial mode — show the full scaffold doc
|
|
42
|
+
if (options.tutorial) {
|
|
43
|
+
const scaffold = scaffolds[type]
|
|
44
|
+
if (scaffold?.tutorial) {
|
|
45
|
+
const ui = container.feature('ui')
|
|
46
|
+
console.log(ui.markdown(scaffold.tutorial))
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`No tutorial available for type: ${type}`)
|
|
49
|
+
}
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!name) {
|
|
54
|
+
console.log(`Usage: luca scaffold ${type} <name> [--description "..."] [--output path]`)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const code = generateScaffold(type, name, options.description)
|
|
59
|
+
|
|
60
|
+
if (!code) {
|
|
61
|
+
console.log(`No scaffold template available for type: ${type}`)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Write to file or stdout
|
|
66
|
+
if (options.output) {
|
|
67
|
+
const fs = container.feature('fs')
|
|
68
|
+
await fs.writeFileAsync(options.output, code)
|
|
69
|
+
console.log(`Wrote ${type} scaffold to ${options.output}`)
|
|
70
|
+
} else {
|
|
71
|
+
console.log(code)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
commands.registerHandler('scaffold', {
|
|
76
|
+
description: 'Generate boilerplate for a new luca feature, client, server, command, or endpoint',
|
|
77
|
+
argsSchema,
|
|
78
|
+
handler: scaffoldCommand,
|
|
79
|
+
})
|
|
@@ -0,0 +1,172 @@
|
|
|
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 { ExpressServer } from '../servers/express.js'
|
|
6
|
+
|
|
7
|
+
declare module '../command.js' {
|
|
8
|
+
interface AvailableCommands {
|
|
9
|
+
serve: ReturnType<typeof commands.registerHandler>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
14
|
+
port: z.number().default(3000).describe('Port to listen on'),
|
|
15
|
+
endpointsDir: z.string().optional().describe('Directory to load endpoints from'),
|
|
16
|
+
staticDir: z.string().optional().describe('Directory to serve static files from'),
|
|
17
|
+
setup: z.string().optional().describe('Path to a TS module whose default export receives the server instance'),
|
|
18
|
+
cors: z.boolean().default(true).describe('Enable CORS'),
|
|
19
|
+
force: z.boolean().default(false).describe('Kill any process currently using the target port'),
|
|
20
|
+
anyPort: z.boolean().default(false).describe('Find an available port starting above 3000'),
|
|
21
|
+
open: z.boolean().default(true).describe('Open the server URL in Google Chrome'),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default async function serve(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
25
|
+
const container = context.container as any
|
|
26
|
+
const { fs, paths, manifest, networking, proc } = container
|
|
27
|
+
|
|
28
|
+
let port = options.port
|
|
29
|
+
const staticDir = options.staticDir ? paths.resolve(options.staticDir) : paths.resolve('public')
|
|
30
|
+
|
|
31
|
+
if (options.anyPort) {
|
|
32
|
+
port = await networking.findOpenPort(3001)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const isPortAvailable = await networking.isPortOpen(port)
|
|
36
|
+
if (!isPortAvailable) {
|
|
37
|
+
if (!options.force) {
|
|
38
|
+
console.error(`Port ${port} is already in use.`)
|
|
39
|
+
console.error(`Use --force to kill the process on this port, or --any-port to find another port.`)
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pids = proc.findPidsByPort(port)
|
|
44
|
+
if (!pids.length) {
|
|
45
|
+
console.error(`Port ${port} is in use, but no PID could be discovered for termination.`)
|
|
46
|
+
process.exit(1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const pid of pids) {
|
|
50
|
+
proc.kill(pid)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let portFreed = false
|
|
54
|
+
for (let i = 0; i < 10; i++) {
|
|
55
|
+
if (await networking.isPortOpen(port)) {
|
|
56
|
+
portFreed = true
|
|
57
|
+
break
|
|
58
|
+
}
|
|
59
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!portFreed) {
|
|
63
|
+
console.error(`Failed to free port ${port} after terminating process(es): ${pids.join(', ')}`)
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Discover the endpoints directory from the project root.
|
|
69
|
+
// Checks explicit flag, then common conventions, then skips if absent.
|
|
70
|
+
const endpointsDir = resolveEndpointsDir(options, fs, paths)
|
|
71
|
+
|
|
72
|
+
// Resolve static directory: explicit flag > public/ > cwd (if index.html exists)
|
|
73
|
+
let resolvedStaticDir: string | undefined
|
|
74
|
+
if (fs.exists(staticDir)) {
|
|
75
|
+
resolvedStaticDir = staticDir
|
|
76
|
+
} else if (fs.exists(paths.resolve('index.html'))) {
|
|
77
|
+
resolvedStaticDir = paths.resolve('.')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If there's nothing to serve at all, bail out.
|
|
81
|
+
if (!endpointsDir && !resolvedStaticDir) {
|
|
82
|
+
console.error(`Can't figure out what to serve in this folder.`)
|
|
83
|
+
console.error(`Expected one of: endpoints/, src/endpoints/, public/, or an index.html in the current directory.`)
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const expressServer = container.server('express', {
|
|
88
|
+
port,
|
|
89
|
+
cors: options.cors,
|
|
90
|
+
static: resolvedStaticDir,
|
|
91
|
+
historyFallback: resolvedStaticDir && !options.staticDir,
|
|
92
|
+
}) as ExpressServer
|
|
93
|
+
|
|
94
|
+
if (endpointsDir) {
|
|
95
|
+
await expressServer.useEndpoints(endpointsDir)
|
|
96
|
+
|
|
97
|
+
expressServer.serveOpenAPISpec({
|
|
98
|
+
title: manifest.name || 'API',
|
|
99
|
+
version: manifest.version || '0.0.0',
|
|
100
|
+
description: manifest.description || '',
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (options.setup) {
|
|
105
|
+
const setupPath = paths.resolve(options.setup)
|
|
106
|
+
if (!fs.exists(setupPath)) {
|
|
107
|
+
console.error(`Setup module not found: ${setupPath}`)
|
|
108
|
+
process.exit(1)
|
|
109
|
+
}
|
|
110
|
+
const vmFeature = container.feature('vm')
|
|
111
|
+
const mod = vmFeature.loadModule(setupPath, { server: expressServer })
|
|
112
|
+
const setupFn = mod.default || mod
|
|
113
|
+
if (typeof setupFn === 'function') {
|
|
114
|
+
await setupFn(expressServer)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await expressServer.start({ port })
|
|
119
|
+
|
|
120
|
+
const name = manifest.name || 'Server'
|
|
121
|
+
console.log(`\n${name} listening on http://localhost:${port}`)
|
|
122
|
+
|
|
123
|
+
if (endpointsDir) {
|
|
124
|
+
console.log(`OpenAPI spec at http://localhost:${port}/openapi.json`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (options.open) {
|
|
128
|
+
try {
|
|
129
|
+
const opener = container.feature('opener')
|
|
130
|
+
await opener.open(`http://localhost:${port}`)
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.warn(`Could not open browser automatically: ${(error as Error).message}`)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (resolvedStaticDir) {
|
|
137
|
+
console.log(`\nStatic files from ${resolvedStaticDir}`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (endpointsDir) {
|
|
141
|
+
if (expressServer._mountedEndpoints.length) {
|
|
142
|
+
console.log(`\nEndpoints:`)
|
|
143
|
+
for (const ep of expressServer._mountedEndpoints) {
|
|
144
|
+
console.log(` ${ep.methods.map((m: string) => m.toUpperCase()).join(', ').padEnd(20)} ${ep.path}`)
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
console.log(`\nNo endpoints found in ${endpointsDir}`)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function resolveEndpointsDir(options: any, fs: any, paths: any): string | null {
|
|
155
|
+
if (options.endpointsDir) {
|
|
156
|
+
const dir = paths.resolve(options.endpointsDir)
|
|
157
|
+
return fs.exists(dir) ? dir : null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const candidate of ['endpoints', 'src/endpoints']) {
|
|
161
|
+
const dir = paths.resolve(candidate)
|
|
162
|
+
if (fs.exists(dir)) return dir
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
commands.registerHandler('serve', {
|
|
169
|
+
description: 'Start the API server with file-based endpoints',
|
|
170
|
+
argsSchema,
|
|
171
|
+
handler: serve,
|
|
172
|
+
})
|