@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,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
|
|
8
|
+
const elevenLabsKey = requireEnv('ELEVENLABS_API_KEY')
|
|
9
|
+
|
|
10
|
+
describeWithRequirements('ElevenLabs Integration', [elevenLabsKey], () => {
|
|
11
|
+
let container: any
|
|
12
|
+
let el: any
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
container = createAGIContainer()
|
|
16
|
+
el = container.client('elevenlabs', {
|
|
17
|
+
apiKey: elevenLabsKey.value,
|
|
18
|
+
})
|
|
19
|
+
await el.connect()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it(
|
|
23
|
+
'listVoices returns available voices',
|
|
24
|
+
async () => {
|
|
25
|
+
const voices = await el.listVoices()
|
|
26
|
+
expect(Array.isArray(voices)).toBe(true)
|
|
27
|
+
expect(voices.length).toBeGreaterThan(0)
|
|
28
|
+
expect(voices[0]).toHaveProperty('voice_id')
|
|
29
|
+
expect(voices[0]).toHaveProperty('name')
|
|
30
|
+
},
|
|
31
|
+
API_TIMEOUT
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
it(
|
|
35
|
+
'listModels returns available models',
|
|
36
|
+
async () => {
|
|
37
|
+
const models = await el.listModels()
|
|
38
|
+
expect(Array.isArray(models)).toBe(true)
|
|
39
|
+
expect(models.length).toBeGreaterThan(0)
|
|
40
|
+
},
|
|
41
|
+
API_TIMEOUT
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
it(
|
|
45
|
+
'synthesize produces audio buffer',
|
|
46
|
+
async () => {
|
|
47
|
+
const audio = await el.synthesize('Hello, integration test.', {
|
|
48
|
+
voiceId: '21m00Tcm4TlvDq8ikWAM', // Rachel
|
|
49
|
+
})
|
|
50
|
+
expect(audio).toBeDefined()
|
|
51
|
+
expect(audio.length).toBeGreaterThan(0)
|
|
52
|
+
},
|
|
53
|
+
API_TIMEOUT
|
|
54
|
+
)
|
|
55
|
+
})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
|
|
8
|
+
const serviceAccountKey = requireEnv('GOOGLE_SERVICE_ACCOUNT_KEY')
|
|
9
|
+
|
|
10
|
+
describeWithRequirements('Google Services Integration', [serviceAccountKey], () => {
|
|
11
|
+
let container: any
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
container = createAGIContainer()
|
|
15
|
+
const auth = container.feature('googleAuth', {
|
|
16
|
+
scopes: [
|
|
17
|
+
'https://www.googleapis.com/auth/spreadsheets.readonly',
|
|
18
|
+
'https://www.googleapis.com/auth/calendar.readonly',
|
|
19
|
+
],
|
|
20
|
+
})
|
|
21
|
+
await auth.authenticateServiceAccount()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('Google Sheets', () => {
|
|
25
|
+
it(
|
|
26
|
+
'getSpreadsheet returns metadata when given a valid spreadsheet ID',
|
|
27
|
+
async () => {
|
|
28
|
+
const sheets = container.feature('googleSheets')
|
|
29
|
+
// This test requires GOOGLE_TEST_SPREADSHEET_ID to be set
|
|
30
|
+
const spreadsheetId = process.env.GOOGLE_TEST_SPREADSHEET_ID
|
|
31
|
+
if (!spreadsheetId) {
|
|
32
|
+
console.log('GOOGLE_TEST_SPREADSHEET_ID not set, skipping sheets data test')
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
const meta = await sheets.getSpreadsheet(spreadsheetId)
|
|
36
|
+
expect(meta).toBeDefined()
|
|
37
|
+
expect(meta.spreadsheetId).toBe(spreadsheetId)
|
|
38
|
+
},
|
|
39
|
+
API_TIMEOUT
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
it(
|
|
43
|
+
'listSheets returns sheet names',
|
|
44
|
+
async () => {
|
|
45
|
+
const sheets = container.feature('googleSheets')
|
|
46
|
+
const spreadsheetId = process.env.GOOGLE_TEST_SPREADSHEET_ID
|
|
47
|
+
if (!spreadsheetId) {
|
|
48
|
+
console.log('GOOGLE_TEST_SPREADSHEET_ID not set, skipping')
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const sheetList = await sheets.listSheets(spreadsheetId)
|
|
52
|
+
expect(Array.isArray(sheetList)).toBe(true)
|
|
53
|
+
expect(sheetList.length).toBeGreaterThan(0)
|
|
54
|
+
},
|
|
55
|
+
API_TIMEOUT
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('Google Calendar', () => {
|
|
60
|
+
it(
|
|
61
|
+
'listCalendars returns at least one calendar',
|
|
62
|
+
async () => {
|
|
63
|
+
const calendar = container.feature('googleCalendar')
|
|
64
|
+
const calendars = await calendar.listCalendars()
|
|
65
|
+
expect(Array.isArray(calendars)).toBe(true)
|
|
66
|
+
},
|
|
67
|
+
API_TIMEOUT
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
it(
|
|
71
|
+
'getToday returns events array',
|
|
72
|
+
async () => {
|
|
73
|
+
const calendar = container.feature('googleCalendar')
|
|
74
|
+
const events = await calendar.getToday()
|
|
75
|
+
expect(Array.isArray(events)).toBe(true)
|
|
76
|
+
},
|
|
77
|
+
API_TIMEOUT
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { AGIContainer } from '../src/agi/container.server'
|
|
2
|
+
import { NodeContainer } from '../src/node/container'
|
|
3
|
+
import { mkdtempSync, rmSync, realpathSync } from 'fs'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
import { tmpdir } from 'os'
|
|
6
|
+
import { execSync } from 'child_process'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Checks that an environment variable is set.
|
|
10
|
+
* Returns its value, or calls describe.skip-style logic.
|
|
11
|
+
* Use at the top of describe blocks — if missing, the value will be empty string
|
|
12
|
+
* and `shouldSkip()` returns the skip reason.
|
|
13
|
+
*/
|
|
14
|
+
export function requireEnv(name: string): { value: string; skip?: string } {
|
|
15
|
+
const value = process.env[name]
|
|
16
|
+
if (!value) {
|
|
17
|
+
return { value: '', skip: `${name} not set, skipping` }
|
|
18
|
+
}
|
|
19
|
+
return { value }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks that a binary is available on PATH.
|
|
24
|
+
* Returns the path or a skip reason.
|
|
25
|
+
*/
|
|
26
|
+
export function requireBinary(name: string): { path: string; skip?: string } {
|
|
27
|
+
try {
|
|
28
|
+
const result = execSync(`which ${name}`, { encoding: 'utf-8' }).trim()
|
|
29
|
+
return { path: result }
|
|
30
|
+
} catch {
|
|
31
|
+
return { path: '', skip: `'${name}' binary not found in PATH, skipping` }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a describe block that skips if any of the provided checks have skip reasons.
|
|
37
|
+
*/
|
|
38
|
+
export function describeWithRequirements(
|
|
39
|
+
title: string,
|
|
40
|
+
checks: Array<{ skip?: string }>,
|
|
41
|
+
fn: () => void
|
|
42
|
+
) {
|
|
43
|
+
const skipReason = checks.find((c) => c.skip)?.skip
|
|
44
|
+
if (skipReason) {
|
|
45
|
+
describe.skip(`${title} (${skipReason})`, fn)
|
|
46
|
+
} else {
|
|
47
|
+
describe(title, fn)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates an AGIContainer for integration tests.
|
|
53
|
+
*/
|
|
54
|
+
export function createAGIContainer(opts?: { cwd?: string }): any {
|
|
55
|
+
const options = opts?.cwd ? { cwd: opts.cwd } : undefined
|
|
56
|
+
return new (AGIContainer as any)(options)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a NodeContainer with an optional temp directory.
|
|
61
|
+
* If useTempDir is true, creates a temp directory and cleans up on dispose.
|
|
62
|
+
*/
|
|
63
|
+
export function createNodeContainer(opts?: {
|
|
64
|
+
cwd?: string
|
|
65
|
+
useTempDir?: boolean
|
|
66
|
+
}): { container: NodeContainer; tempDir?: string; cleanup: () => void } {
|
|
67
|
+
if (opts?.useTempDir) {
|
|
68
|
+
const tempDir = realpathSync(mkdtempSync(join(tmpdir(), 'luca-integ-')))
|
|
69
|
+
const container = new NodeContainer({ cwd: tempDir })
|
|
70
|
+
return {
|
|
71
|
+
container,
|
|
72
|
+
tempDir,
|
|
73
|
+
cleanup: () => rmSync(tempDir, { recursive: true, force: true }),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const container = new NodeContainer({ cwd: opts?.cwd || process.cwd() })
|
|
78
|
+
return { container, cleanup: () => {} }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Default timeout for API-calling tests (30 seconds).
|
|
83
|
+
*/
|
|
84
|
+
export const API_TIMEOUT = 30_000
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Extended timeout for slow operations like CLI subprocesses (60 seconds).
|
|
88
|
+
*/
|
|
89
|
+
export const CLI_TIMEOUT = 60_000
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
requireBinary,
|
|
4
|
+
describeWithRequirements,
|
|
5
|
+
createAGIContainer,
|
|
6
|
+
CLI_TIMEOUT,
|
|
7
|
+
} from './helpers'
|
|
8
|
+
import type { AGIContainer } from '../src/agi/container.server'
|
|
9
|
+
|
|
10
|
+
const openaiKey = requireEnv('OPENAI_API_KEY')
|
|
11
|
+
const codexBin = requireBinary('codex')
|
|
12
|
+
|
|
13
|
+
describeWithRequirements(
|
|
14
|
+
'OpenAI Codex Integration',
|
|
15
|
+
[openaiKey, codexBin],
|
|
16
|
+
() => {
|
|
17
|
+
let container: AGIContainer
|
|
18
|
+
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
container = createAGIContainer()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it(
|
|
24
|
+
'checkAvailability confirms codex is installed',
|
|
25
|
+
async () => {
|
|
26
|
+
const codex = container.feature('openaiCodex')
|
|
27
|
+
const available = await codex.checkAvailability()
|
|
28
|
+
expect(available).toBe(true)
|
|
29
|
+
},
|
|
30
|
+
CLI_TIMEOUT
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
it(
|
|
34
|
+
'run() with a simple prompt in read-only sandbox returns a session',
|
|
35
|
+
async () => {
|
|
36
|
+
const codex = container.feature('openaiCodex', {
|
|
37
|
+
sandbox: 'read-only',
|
|
38
|
+
model: 'o4-mini',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const session = await codex.run(
|
|
42
|
+
'Reply with exactly: CODEX_TEST_OK',
|
|
43
|
+
{
|
|
44
|
+
sandbox: 'read-only',
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
expect(session).toBeDefined()
|
|
49
|
+
expect(session.status).toBe('completed')
|
|
50
|
+
expect(typeof session.result).toBe('string')
|
|
51
|
+
},
|
|
52
|
+
CLI_TIMEOUT
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
it(
|
|
56
|
+
'session events fire during execution',
|
|
57
|
+
async () => {
|
|
58
|
+
const codex = container.feature('openaiCodex', {
|
|
59
|
+
sandbox: 'read-only',
|
|
60
|
+
model: 'o4-mini',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const events: string[] = []
|
|
64
|
+
codex.on('session:start', () => events.push('start'))
|
|
65
|
+
codex.on('session:result', () => events.push('result'))
|
|
66
|
+
|
|
67
|
+
await codex.run('Say hello', { sandbox: 'read-only' })
|
|
68
|
+
|
|
69
|
+
expect(events).toContain('start')
|
|
70
|
+
expect(events).toContain('result')
|
|
71
|
+
},
|
|
72
|
+
CLI_TIMEOUT
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
it(
|
|
76
|
+
'session items are populated from event stream',
|
|
77
|
+
async () => {
|
|
78
|
+
const codex = container.feature('openaiCodex', {
|
|
79
|
+
sandbox: 'read-only',
|
|
80
|
+
model: 'o4-mini',
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const session = await codex.run('What is 2+2? Reply with just the number.', {
|
|
84
|
+
sandbox: 'read-only',
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Items array should exist even if empty
|
|
88
|
+
expect(Array.isArray(session.items)).toBe(true)
|
|
89
|
+
},
|
|
90
|
+
CLI_TIMEOUT
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
|
|
8
|
+
const runpodKey = requireEnv('RUNPOD_API_KEY')
|
|
9
|
+
|
|
10
|
+
describeWithRequirements('RunPod Integration', [runpodKey], () => {
|
|
11
|
+
let container: any
|
|
12
|
+
let runpod: any
|
|
13
|
+
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
container = createAGIContainer()
|
|
16
|
+
runpod = container.feature('runpod', {
|
|
17
|
+
apiKey: runpodKey.value,
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it(
|
|
22
|
+
'lists templates (read-only)',
|
|
23
|
+
async () => {
|
|
24
|
+
const templates = await runpod.listTemplates({ includeRunpod: true })
|
|
25
|
+
expect(Array.isArray(templates)).toBe(true)
|
|
26
|
+
expect(templates.length).toBeGreaterThan(0)
|
|
27
|
+
},
|
|
28
|
+
API_TIMEOUT
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
it(
|
|
32
|
+
'lists secure GPU availability',
|
|
33
|
+
async () => {
|
|
34
|
+
const gpus = await runpod.listSecureGPUs()
|
|
35
|
+
expect(Array.isArray(gpus)).toBe(true)
|
|
36
|
+
expect(gpus.length).toBeGreaterThan(0)
|
|
37
|
+
},
|
|
38
|
+
API_TIMEOUT
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
it(
|
|
42
|
+
'lists network volumes',
|
|
43
|
+
async () => {
|
|
44
|
+
const volumes = await runpod.listVolumes()
|
|
45
|
+
expect(Array.isArray(volumes)).toBe(true)
|
|
46
|
+
},
|
|
47
|
+
API_TIMEOUT
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
it(
|
|
51
|
+
'lists existing pods',
|
|
52
|
+
async () => {
|
|
53
|
+
const pods = await runpod.listPods()
|
|
54
|
+
expect(Array.isArray(pods)).toBe(true)
|
|
55
|
+
},
|
|
56
|
+
API_TIMEOUT
|
|
57
|
+
)
|
|
58
|
+
})
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
|
|
8
|
+
const openaiKey = requireEnv('OPENAI_API_KEY')
|
|
9
|
+
|
|
10
|
+
describeWithRequirements('Server & Endpoints Integration', [openaiKey], () => {
|
|
11
|
+
let container: any
|
|
12
|
+
let server: any
|
|
13
|
+
let port: number
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
container = createAGIContainer()
|
|
17
|
+
|
|
18
|
+
server = container.server('express', {
|
|
19
|
+
port: 0,
|
|
20
|
+
cors: true,
|
|
21
|
+
create: (app: any) => {
|
|
22
|
+
app.get('/health', (_req: any, res: any) => {
|
|
23
|
+
res.json({ status: 'ok' })
|
|
24
|
+
})
|
|
25
|
+
return app
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
await server.start()
|
|
30
|
+
port = server.port
|
|
31
|
+
}, API_TIMEOUT)
|
|
32
|
+
|
|
33
|
+
afterAll(async () => {
|
|
34
|
+
if (server?.isListening) {
|
|
35
|
+
await server.stop()
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('server starts and reports listening', () => {
|
|
40
|
+
expect(server.isListening).toBe(true)
|
|
41
|
+
expect(port).toBeGreaterThan(0)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it(
|
|
45
|
+
'health endpoint returns OK',
|
|
46
|
+
async () => {
|
|
47
|
+
const res = await fetch(`http://localhost:${port}/health`)
|
|
48
|
+
expect(res.status).toBe(200)
|
|
49
|
+
const body = await res.json()
|
|
50
|
+
expect(body.status).toBe('ok')
|
|
51
|
+
},
|
|
52
|
+
API_TIMEOUT
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
it(
|
|
56
|
+
'loads AGI endpoints directory',
|
|
57
|
+
async () => {
|
|
58
|
+
const agiEndpointsDir = container.paths.resolve(
|
|
59
|
+
'src',
|
|
60
|
+
'agi',
|
|
61
|
+
'endpoints'
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (container.fs.exists(agiEndpointsDir)) {
|
|
65
|
+
await server.useEndpoints(agiEndpointsDir)
|
|
66
|
+
// After loading endpoints, the /ask endpoint should be available
|
|
67
|
+
const res = await fetch(`http://localhost:${port}/ask`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
question: 'Reply with exactly: ENDPOINT_TEST_OK',
|
|
72
|
+
model: 'gpt-4o-mini',
|
|
73
|
+
}),
|
|
74
|
+
})
|
|
75
|
+
// The endpoint may stream SSE or return JSON
|
|
76
|
+
expect(res.status).toBeLessThan(500)
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
API_TIMEOUT
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
it(
|
|
83
|
+
'server shuts down cleanly',
|
|
84
|
+
async () => {
|
|
85
|
+
const testServer = container.server('express', {
|
|
86
|
+
port: 0,
|
|
87
|
+
cors: true,
|
|
88
|
+
})
|
|
89
|
+
await testServer.start()
|
|
90
|
+
expect(testServer.isListening).toBe(true)
|
|
91
|
+
|
|
92
|
+
await testServer.stop()
|
|
93
|
+
expect(testServer.isStopped).toBe(true)
|
|
94
|
+
},
|
|
95
|
+
API_TIMEOUT
|
|
96
|
+
)
|
|
97
|
+
})
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createAGIContainer } from './helpers'
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, realpathSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { tmpdir } from 'os'
|
|
5
|
+
|
|
6
|
+
describe('Skills Library Integration', () => {
|
|
7
|
+
let container: any
|
|
8
|
+
let tempDir: string
|
|
9
|
+
let skillsDir: string
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
tempDir = realpathSync(mkdtempSync(join(tmpdir(), 'luca-skills-test-')))
|
|
13
|
+
skillsDir = join(tempDir, '.claude', 'skills')
|
|
14
|
+
|
|
15
|
+
// Create a test skill
|
|
16
|
+
const testSkillDir = join(skillsDir, 'code-review')
|
|
17
|
+
mkdirSync(testSkillDir, { recursive: true })
|
|
18
|
+
writeFileSync(
|
|
19
|
+
join(testSkillDir, 'SKILL.md'),
|
|
20
|
+
`---
|
|
21
|
+
name: code-review
|
|
22
|
+
description: Reviews code for common issues and suggests improvements
|
|
23
|
+
version: 1.0.0
|
|
24
|
+
tags:
|
|
25
|
+
- code
|
|
26
|
+
- review
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Instructions
|
|
30
|
+
|
|
31
|
+
Review the provided code for:
|
|
32
|
+
1. Common bugs and anti-patterns
|
|
33
|
+
2. Performance issues
|
|
34
|
+
3. Security vulnerabilities
|
|
35
|
+
`
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// Create another test skill
|
|
39
|
+
const debugSkillDir = join(skillsDir, 'debug-helper')
|
|
40
|
+
mkdirSync(debugSkillDir, { recursive: true })
|
|
41
|
+
writeFileSync(
|
|
42
|
+
join(debugSkillDir, 'SKILL.md'),
|
|
43
|
+
`---
|
|
44
|
+
name: debug-helper
|
|
45
|
+
description: Helps debug runtime errors and exceptions
|
|
46
|
+
version: 1.0.0
|
|
47
|
+
tags:
|
|
48
|
+
- debug
|
|
49
|
+
- errors
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Instructions
|
|
53
|
+
|
|
54
|
+
Help the user debug their issue by asking clarifying questions.
|
|
55
|
+
`
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
container = createAGIContainer({ cwd: tempDir })
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
afterAll(() => {
|
|
62
|
+
rmSync(tempDir, { recursive: true, force: true })
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('loads skills from project directory', async () => {
|
|
66
|
+
const skills = container.feature('skillsLibrary', {
|
|
67
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
68
|
+
})
|
|
69
|
+
await skills.load()
|
|
70
|
+
|
|
71
|
+
const list = skills.list()
|
|
72
|
+
expect(list.length).toBeGreaterThanOrEqual(2)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('finds a skill by name', async () => {
|
|
76
|
+
const skills = container.feature('skillsLibrary', {
|
|
77
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
78
|
+
})
|
|
79
|
+
await skills.load()
|
|
80
|
+
|
|
81
|
+
const entry = skills.find('code-review')
|
|
82
|
+
expect(entry).toBeDefined()
|
|
83
|
+
expect(entry!.name).toBe('code-review')
|
|
84
|
+
expect(entry!.description).toContain('Reviews code')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('searches skills by keyword', async () => {
|
|
88
|
+
const skills = container.feature('skillsLibrary', {
|
|
89
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
90
|
+
})
|
|
91
|
+
await skills.load()
|
|
92
|
+
|
|
93
|
+
const results = skills.search('debug')
|
|
94
|
+
expect(results.length).toBeGreaterThanOrEqual(1)
|
|
95
|
+
expect(results[0].name).toBe('debug-helper')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('creates a new skill', async () => {
|
|
99
|
+
const skills = container.feature('skillsLibrary', {
|
|
100
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
101
|
+
})
|
|
102
|
+
await skills.load()
|
|
103
|
+
|
|
104
|
+
const entry = await skills.create(
|
|
105
|
+
{
|
|
106
|
+
name: 'test-new-skill',
|
|
107
|
+
description: 'A skill created during testing',
|
|
108
|
+
body: '## Instructions\n\nDo test things.',
|
|
109
|
+
meta: { tags: ['test'], version: '0.1.0' },
|
|
110
|
+
},
|
|
111
|
+
'project'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
expect(entry).toBeDefined()
|
|
115
|
+
expect(entry.name).toBe('test-new-skill')
|
|
116
|
+
|
|
117
|
+
// Verify it's findable
|
|
118
|
+
const found = skills.find('test-new-skill')
|
|
119
|
+
expect(found).toBeDefined()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('removes a skill', async () => {
|
|
123
|
+
const skills = container.feature('skillsLibrary', {
|
|
124
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
125
|
+
})
|
|
126
|
+
await skills.load()
|
|
127
|
+
|
|
128
|
+
// Create one to remove
|
|
129
|
+
await skills.create(
|
|
130
|
+
{
|
|
131
|
+
name: 'to-remove',
|
|
132
|
+
description: 'Will be removed',
|
|
133
|
+
body: '## Instructions\n\nRemove me.',
|
|
134
|
+
meta: {},
|
|
135
|
+
},
|
|
136
|
+
'project'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
const removed = await skills.remove('to-remove')
|
|
140
|
+
expect(removed).toBe(true)
|
|
141
|
+
|
|
142
|
+
const found = skills.find('to-remove')
|
|
143
|
+
expect(found).toBeUndefined()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('generates system prompt block', async () => {
|
|
147
|
+
const skills = container.feature('skillsLibrary', {
|
|
148
|
+
projectSkillsPath: join(tempDir, '.claude', 'skills'),
|
|
149
|
+
})
|
|
150
|
+
await skills.load()
|
|
151
|
+
|
|
152
|
+
const block = skills.toSystemPromptBlock()
|
|
153
|
+
expect(typeof block).toBe('string')
|
|
154
|
+
expect(block).toContain('code-review')
|
|
155
|
+
expect(block).toContain('debug-helper')
|
|
156
|
+
})
|
|
157
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireEnv,
|
|
3
|
+
describeWithRequirements,
|
|
4
|
+
createAGIContainer,
|
|
5
|
+
API_TIMEOUT,
|
|
6
|
+
} from './helpers'
|
|
7
|
+
|
|
8
|
+
const telegramToken = requireEnv('TELEGRAM_BOT_TOKEN')
|
|
9
|
+
|
|
10
|
+
describeWithRequirements('Telegram Integration', [telegramToken], () => {
|
|
11
|
+
let container: any
|
|
12
|
+
let tg: any
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
container = createAGIContainer()
|
|
16
|
+
tg = container.feature('telegram', {
|
|
17
|
+
token: telegramToken.value,
|
|
18
|
+
autoStart: false,
|
|
19
|
+
pollingTimeout: 0,
|
|
20
|
+
})
|
|
21
|
+
await tg.enable()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it(
|
|
25
|
+
'getMe returns bot info',
|
|
26
|
+
async () => {
|
|
27
|
+
const me = await tg.getMe()
|
|
28
|
+
expect(me).toBeDefined()
|
|
29
|
+
expect(me.id).toBeDefined()
|
|
30
|
+
expect(typeof me.first_name).toBe('string')
|
|
31
|
+
expect(me.is_bot).toBe(true)
|
|
32
|
+
},
|
|
33
|
+
API_TIMEOUT
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
it('command registration works', () => {
|
|
37
|
+
tg.command('test_integ', (ctx: any) => ctx.reply('test'))
|
|
38
|
+
// If command registration didn't throw, it's working
|
|
39
|
+
expect(true).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('bot instance is accessible', () => {
|
|
43
|
+
expect(tg.bot).toBeDefined()
|
|
44
|
+
expect(tg.bot.api).toBeDefined()
|
|
45
|
+
})
|
|
46
|
+
})
|