@vellumai/assistant 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
package/src/skills/clawhub.ts
CHANGED
|
@@ -23,7 +23,7 @@ function getClawhubProjectRoot(): string {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Validate slug format (alphanumeric, hyphens, dots, underscores; optional namespace with single slash)
|
|
26
|
-
function validateSlug(slug: string): boolean {
|
|
26
|
+
export function validateSlug(slug: string): boolean {
|
|
27
27
|
return /^[a-zA-Z0-9]([a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)?)?$/.test(
|
|
28
28
|
slug,
|
|
29
29
|
);
|
|
@@ -312,18 +312,37 @@ export async function clawhubSearch(
|
|
|
312
312
|
};
|
|
313
313
|
}
|
|
314
314
|
} catch {
|
|
315
|
-
// CLI outputs text
|
|
315
|
+
// CLI outputs text — fall through to line parser below.
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
// Parse text output lines
|
|
318
|
+
// Parse text output lines. The CLI format varies by version:
|
|
319
|
+
// With version: "slug v1.0.0 Display Name (3.459)"
|
|
320
|
+
// Without version: "slug Display Name (3.459)"
|
|
319
321
|
const skills: ClawhubSearchResultItem[] = [];
|
|
320
322
|
for (const line of result.stdout.split("\n")) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
+
// Try format with version first
|
|
324
|
+
const withVersion = line.match(/^(\S+)\s+v(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
|
|
325
|
+
if (withVersion) {
|
|
323
326
|
skills.push({
|
|
324
|
-
slug:
|
|
325
|
-
version:
|
|
326
|
-
name:
|
|
327
|
+
slug: withVersion[1],
|
|
328
|
+
version: withVersion[2],
|
|
329
|
+
name: withVersion[3].trim(),
|
|
330
|
+
description: "",
|
|
331
|
+
author: "",
|
|
332
|
+
stars: 0,
|
|
333
|
+
installs: 0,
|
|
334
|
+
createdAt: 0,
|
|
335
|
+
source: "clawhub",
|
|
336
|
+
});
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// Try format without version
|
|
340
|
+
const withoutVersion = line.match(/^(\S+)\s+(.+?)\s+\([\d.]+\)\s*$/);
|
|
341
|
+
if (withoutVersion) {
|
|
342
|
+
skills.push({
|
|
343
|
+
slug: withoutVersion[1],
|
|
344
|
+
version: "",
|
|
345
|
+
name: withoutVersion[2].trim(),
|
|
327
346
|
description: "",
|
|
328
347
|
author: "",
|
|
329
348
|
stars: 0,
|
|
@@ -420,12 +439,18 @@ export async function clawhubInspect(
|
|
|
420
439
|
}
|
|
421
440
|
try {
|
|
422
441
|
const parsed = JSON.parse(result.stdout);
|
|
423
|
-
// Normalize the raw inspect response to our interface
|
|
442
|
+
// Normalize the raw inspect response to our interface.
|
|
443
|
+
// The CLI nests skill metadata under `parsed.skill` and files
|
|
444
|
+
// under `parsed.version.files`. Fall back to top-level fields
|
|
445
|
+
// for older CLI versions that used a flat structure.
|
|
446
|
+
const ps = parsed.skill ?? parsed;
|
|
447
|
+
const rawStats = ps.stats ?? parsed.stats;
|
|
448
|
+
const rawFiles = parsed.version?.files ?? parsed.files ?? null;
|
|
424
449
|
const data: ClawhubInspectResult = {
|
|
425
450
|
skill: {
|
|
426
|
-
slug:
|
|
427
|
-
displayName:
|
|
428
|
-
summary:
|
|
451
|
+
slug: ps.slug ?? slug,
|
|
452
|
+
displayName: ps.displayName ?? ps.name ?? slug,
|
|
453
|
+
summary: ps.summary ?? ps.description ?? "",
|
|
429
454
|
},
|
|
430
455
|
owner: parsed.owner
|
|
431
456
|
? {
|
|
@@ -434,26 +459,24 @@ export async function clawhubInspect(
|
|
|
434
459
|
image: parsed.owner.image ?? parsed.owner.avatar ?? undefined,
|
|
435
460
|
}
|
|
436
461
|
: null,
|
|
437
|
-
stats:
|
|
462
|
+
stats: rawStats
|
|
438
463
|
? {
|
|
439
|
-
stars:
|
|
440
|
-
installs:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
parsed.stats.downloadsAllTime ?? parsed.stats.downloads ?? 0,
|
|
444
|
-
versions: parsed.stats.versions ?? 0,
|
|
464
|
+
stars: rawStats.stars ?? 0,
|
|
465
|
+
installs: rawStats.installsAllTime ?? rawStats.installs ?? 0,
|
|
466
|
+
downloads: rawStats.downloadsAllTime ?? rawStats.downloads ?? 0,
|
|
467
|
+
versions: rawStats.versions ?? 0,
|
|
445
468
|
}
|
|
446
469
|
: null,
|
|
447
|
-
createdAt: parsed.createdAt ?? null,
|
|
448
|
-
updatedAt: parsed.updatedAt ?? null,
|
|
470
|
+
createdAt: ps.createdAt ?? parsed.createdAt ?? null,
|
|
471
|
+
updatedAt: ps.updatedAt ?? parsed.updatedAt ?? null,
|
|
449
472
|
latestVersion: parsed.latestVersion
|
|
450
473
|
? {
|
|
451
474
|
version: parsed.latestVersion.version ?? "",
|
|
452
475
|
changelog: parsed.latestVersion.changelog ?? undefined,
|
|
453
476
|
}
|
|
454
477
|
: null,
|
|
455
|
-
files: Array.isArray(
|
|
456
|
-
?
|
|
478
|
+
files: Array.isArray(rawFiles)
|
|
479
|
+
? rawFiles.map((f: Record<string, unknown>) => ({
|
|
457
480
|
path: (f.path as string) ?? "",
|
|
458
481
|
size: (f.size as number) ?? 0,
|
|
459
482
|
contentType: (f.contentType as string) ?? undefined,
|
|
@@ -475,6 +498,44 @@ export async function clawhubInspect(
|
|
|
475
498
|
}
|
|
476
499
|
}
|
|
477
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Fetch a single file's content from a published clawhub skill.
|
|
503
|
+
* Calls `npx clawhub inspect <slug> --json --file <filePath>` and returns
|
|
504
|
+
* the file content string, or `null` on failure.
|
|
505
|
+
*/
|
|
506
|
+
export async function clawhubInspectFile(
|
|
507
|
+
slug: string,
|
|
508
|
+
filePath: string,
|
|
509
|
+
): Promise<string | null> {
|
|
510
|
+
if (!validateSlug(slug)) return null;
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
const result = await runClawhub([
|
|
514
|
+
"inspect",
|
|
515
|
+
slug,
|
|
516
|
+
"--json",
|
|
517
|
+
"--file",
|
|
518
|
+
filePath,
|
|
519
|
+
]);
|
|
520
|
+
if (result.exitCode !== 0) return null;
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
const parsed = JSON.parse(result.stdout);
|
|
524
|
+
// The CLI returns the file content in one of these fields
|
|
525
|
+
const content =
|
|
526
|
+
parsed.skillMdContent ??
|
|
527
|
+
parsed.fileContents?.[filePath] ??
|
|
528
|
+
parsed.file?.content ??
|
|
529
|
+
null;
|
|
530
|
+
return typeof content === "string" ? content : null;
|
|
531
|
+
} catch {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
} catch {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
478
539
|
export async function clawhubCheckUpdates(): Promise<ClawhubUpdateCheckItem[]> {
|
|
479
540
|
// This is a placeholder -- clawhub doesn't have a dedicated check-updates command
|
|
480
541
|
// For now return empty; will be implemented when the CLI supports it
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
|
|
2
|
+
import type { SkillFileEntry } from "./catalog-files.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A file provider can resolve file listings and single-file content for
|
|
6
|
+
* skills that are NOT installed locally. Each origin (vellum catalog,
|
|
7
|
+
* skills.sh, clawhub) implements this interface.
|
|
8
|
+
*/
|
|
9
|
+
export interface SkillFileProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Return true if this provider can handle the given skill id.
|
|
12
|
+
* Called synchronously — must not perform I/O.
|
|
13
|
+
*/
|
|
14
|
+
canHandle(skillId: string): boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* List all files in the skill directory. Returns entries with
|
|
18
|
+
* `content: null` (content is fetched on demand via `readFileContent`).
|
|
19
|
+
* Returns `null` if the skill doesn't exist in this provider.
|
|
20
|
+
*/
|
|
21
|
+
listFiles(skillId: string): Promise<SkillFileEntry[] | null>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read a single file's content. `relativePath` has already been
|
|
25
|
+
* sanitized by the caller (sanitizeRelativePath + hasHiddenOrSkippedSegment).
|
|
26
|
+
* Returns `null` if the file doesn't exist.
|
|
27
|
+
*/
|
|
28
|
+
readFileContent(
|
|
29
|
+
skillId: string,
|
|
30
|
+
sanitizedPath: string,
|
|
31
|
+
): Promise<SkillFileEntry | null>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Synthesize a SlimSkillResponse for an uninstalled skill in this
|
|
35
|
+
* provider. Used by getSkill/getSkillFiles when the skill isn't in
|
|
36
|
+
* the local catalog. Returns `null` if the provider can't produce
|
|
37
|
+
* metadata for this skill.
|
|
38
|
+
*/
|
|
39
|
+
toSlimSkill(skillId: string): Promise<SlimSkillResponse | null>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skillssh-files — SkillFileProvider for skills.sh (GitHub-hosted) skills.
|
|
3
|
+
*
|
|
4
|
+
* Lists files and reads individual file content via the GitHub Contents API
|
|
5
|
+
* and Tree API, without downloading or installing the skill locally.
|
|
6
|
+
*
|
|
7
|
+
* Path resolution (conventional `skills/<slug>/` with tree-search fallback)
|
|
8
|
+
* mirrors the logic in `fetchSkillFromGitHub` from `skillssh-registry.ts`,
|
|
9
|
+
* but only collects metadata (no file content on listing) and fetches
|
|
10
|
+
* content on demand for individual files.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { basename } from "node:path";
|
|
14
|
+
|
|
15
|
+
import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
|
|
16
|
+
import {
|
|
17
|
+
isTextMimeType as isTextMime,
|
|
18
|
+
MAX_INLINE_TEXT_SIZE,
|
|
19
|
+
} from "../runtime/routes/workspace-utils.js";
|
|
20
|
+
import { getLogger } from "../util/logger.js";
|
|
21
|
+
import type { SkillFileEntry } from "./catalog-files.js";
|
|
22
|
+
import {
|
|
23
|
+
hasHiddenOrSkippedSegment,
|
|
24
|
+
sanitizeRelativePath,
|
|
25
|
+
SKIP_DIRS,
|
|
26
|
+
} from "./catalog-files.js";
|
|
27
|
+
import type { SkillFileProvider } from "./skill-file-provider.js";
|
|
28
|
+
import type { GitHubContentsEntry } from "./skillssh-registry.js";
|
|
29
|
+
import {
|
|
30
|
+
findSkillDirInTree,
|
|
31
|
+
githubHeaders,
|
|
32
|
+
resolveSkillSource,
|
|
33
|
+
} from "./skillssh-registry.js";
|
|
34
|
+
|
|
35
|
+
const log = getLogger("skillssh-files");
|
|
36
|
+
|
|
37
|
+
// ─── Path resolution cache ──────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
interface CacheEntry {
|
|
40
|
+
dirPath: string;
|
|
41
|
+
expiresAt: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* In-memory cache for resolved skill directory paths. Keyed by
|
|
48
|
+
* `${owner}/${repo}/${skillSlug}` so repeated requests don't re-probe the
|
|
49
|
+
* GitHub Contents/Tree APIs.
|
|
50
|
+
*/
|
|
51
|
+
const dirPathCache = new Map<string, CacheEntry>();
|
|
52
|
+
|
|
53
|
+
function cacheKey(owner: string, repo: string, skillSlug: string): string {
|
|
54
|
+
return `${owner}/${repo}/${skillSlug}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getCachedDirPath(
|
|
58
|
+
owner: string,
|
|
59
|
+
repo: string,
|
|
60
|
+
skillSlug: string,
|
|
61
|
+
): string | null {
|
|
62
|
+
const key = cacheKey(owner, repo, skillSlug);
|
|
63
|
+
const entry = dirPathCache.get(key);
|
|
64
|
+
if (!entry) return null;
|
|
65
|
+
if (Date.now() > entry.expiresAt) {
|
|
66
|
+
dirPathCache.delete(key);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return entry.dirPath;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function setCachedDirPath(
|
|
73
|
+
owner: string,
|
|
74
|
+
repo: string,
|
|
75
|
+
skillSlug: string,
|
|
76
|
+
dirPath: string,
|
|
77
|
+
): void {
|
|
78
|
+
const key = cacheKey(owner, repo, skillSlug);
|
|
79
|
+
dirPathCache.set(key, { dirPath, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Exported for testing only
|
|
83
|
+
export function clearDirPathCache(): void {
|
|
84
|
+
dirPathCache.clear();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Binary classification ──────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Classify a file as binary from its name alone. Mirrors the
|
|
91
|
+
* `classifyByName` pattern in `catalog-files.ts`.
|
|
92
|
+
*/
|
|
93
|
+
function classifyByName(name: string): boolean {
|
|
94
|
+
const mime = Bun.file(name).type;
|
|
95
|
+
return !isTextMime(mime, name);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Skill directory resolution ─────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the directory path for a skill in a GitHub repo. Tries the
|
|
102
|
+
* conventional `skills/<slug>/` path first, falls back to tree search.
|
|
103
|
+
* Returns null if the skill cannot be located.
|
|
104
|
+
*/
|
|
105
|
+
async function resolveSkillDir(
|
|
106
|
+
owner: string,
|
|
107
|
+
repo: string,
|
|
108
|
+
skillSlug: string,
|
|
109
|
+
ref?: string,
|
|
110
|
+
): Promise<string | null> {
|
|
111
|
+
// Check cache first
|
|
112
|
+
const cached = getCachedDirPath(owner, repo, skillSlug);
|
|
113
|
+
if (cached !== null) return cached;
|
|
114
|
+
|
|
115
|
+
const headers = githubHeaders();
|
|
116
|
+
const conventionalPath = `skills/${encodeURIComponent(skillSlug)}`;
|
|
117
|
+
|
|
118
|
+
const probeUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${conventionalPath}${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`;
|
|
119
|
+
|
|
120
|
+
const probeResponse = await fetch(probeUrl, {
|
|
121
|
+
headers,
|
|
122
|
+
signal: AbortSignal.timeout(15_000),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (probeResponse.ok) {
|
|
126
|
+
setCachedDirPath(owner, repo, skillSlug, conventionalPath);
|
|
127
|
+
return conventionalPath;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (probeResponse.status === 404) {
|
|
131
|
+
// Fall back to tree search
|
|
132
|
+
const treeRef = ref ?? "HEAD";
|
|
133
|
+
const foundPath = await findSkillDirInTree(
|
|
134
|
+
owner,
|
|
135
|
+
repo,
|
|
136
|
+
skillSlug,
|
|
137
|
+
treeRef,
|
|
138
|
+
headers,
|
|
139
|
+
);
|
|
140
|
+
if (foundPath) {
|
|
141
|
+
setCachedDirPath(owner, repo, skillSlug, foundPath);
|
|
142
|
+
return foundPath;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Non-404 error — log and return null
|
|
148
|
+
log.warn(
|
|
149
|
+
{ status: probeResponse.status, owner, repo, skillSlug },
|
|
150
|
+
"GitHub Contents API returned non-2xx during skill dir probe",
|
|
151
|
+
);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── Recursive directory listing ────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Recursively list files in a GitHub directory via the Contents API.
|
|
159
|
+
* Collects `SkillFileEntry` objects with `content: null`. Skips hidden
|
|
160
|
+
* files, `node_modules`, `__pycache__`, `.git` (same filtering as
|
|
161
|
+
* `walkSkillDir` in `catalog-files.ts`).
|
|
162
|
+
*/
|
|
163
|
+
async function listGitHubDir(
|
|
164
|
+
owner: string,
|
|
165
|
+
repo: string,
|
|
166
|
+
dirPath: string,
|
|
167
|
+
prefix: string,
|
|
168
|
+
ref: string | undefined,
|
|
169
|
+
headers: Record<string, string>,
|
|
170
|
+
): Promise<SkillFileEntry[]> {
|
|
171
|
+
let apiUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${dirPath}`;
|
|
172
|
+
if (ref) {
|
|
173
|
+
apiUrl += `?ref=${encodeURIComponent(ref)}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const response = await fetch(apiUrl, {
|
|
177
|
+
headers,
|
|
178
|
+
signal: AbortSignal.timeout(15_000),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`GitHub Contents API error: HTTP ${response.status} ${response.statusText}`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const entries = (await response.json()) as GitHubContentsEntry[];
|
|
188
|
+
if (!Array.isArray(entries)) return [];
|
|
189
|
+
|
|
190
|
+
const result: SkillFileEntry[] = [];
|
|
191
|
+
|
|
192
|
+
for (const entry of entries) {
|
|
193
|
+
// Skip hidden files/dirs (same as walkSkillDir)
|
|
194
|
+
if (entry.name.startsWith(".")) continue;
|
|
195
|
+
|
|
196
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
197
|
+
|
|
198
|
+
if (entry.type === "dir") {
|
|
199
|
+
// Skip well-known heavyweight directories
|
|
200
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
201
|
+
|
|
202
|
+
const subEntries = await listGitHubDir(
|
|
203
|
+
owner,
|
|
204
|
+
repo,
|
|
205
|
+
`${dirPath}/${entry.name}`,
|
|
206
|
+
relativePath,
|
|
207
|
+
ref,
|
|
208
|
+
headers,
|
|
209
|
+
);
|
|
210
|
+
result.push(...subEntries);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (entry.type !== "file") continue;
|
|
215
|
+
|
|
216
|
+
result.push({
|
|
217
|
+
path: relativePath,
|
|
218
|
+
name: basename(relativePath),
|
|
219
|
+
size: 0, // GitHub Contents API directory listings don't include size
|
|
220
|
+
mimeType: "",
|
|
221
|
+
isBinary: classifyByName(entry.name),
|
|
222
|
+
content: null,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ─── Provider implementation ────────────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
export function createSkillsShProvider(): SkillFileProvider {
|
|
232
|
+
return {
|
|
233
|
+
canHandle(skillId: string): boolean {
|
|
234
|
+
return skillId.split("/").length >= 3;
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
async listFiles(skillId: string): Promise<SkillFileEntry[] | null> {
|
|
238
|
+
let source;
|
|
239
|
+
try {
|
|
240
|
+
source = resolveSkillSource(skillId);
|
|
241
|
+
} catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const dirPath = await resolveSkillDir(
|
|
247
|
+
source.owner,
|
|
248
|
+
source.repo,
|
|
249
|
+
source.skillSlug,
|
|
250
|
+
source.ref,
|
|
251
|
+
);
|
|
252
|
+
if (!dirPath) return null;
|
|
253
|
+
|
|
254
|
+
const headers = githubHeaders();
|
|
255
|
+
const entries = await listGitHubDir(
|
|
256
|
+
source.owner,
|
|
257
|
+
source.repo,
|
|
258
|
+
dirPath,
|
|
259
|
+
"",
|
|
260
|
+
source.ref,
|
|
261
|
+
headers,
|
|
262
|
+
);
|
|
263
|
+
entries.sort((a, b) => a.path.localeCompare(b.path));
|
|
264
|
+
return entries;
|
|
265
|
+
} catch (err) {
|
|
266
|
+
log.warn({ err, skillId }, "Failed to list files for skills.sh skill");
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
async readFileContent(
|
|
272
|
+
skillId: string,
|
|
273
|
+
sanitizedPath: string,
|
|
274
|
+
): Promise<SkillFileEntry | null> {
|
|
275
|
+
// Re-validate the path even though the caller should have sanitized
|
|
276
|
+
const safe = sanitizeRelativePath(sanitizedPath);
|
|
277
|
+
if (!safe) return null;
|
|
278
|
+
if (hasHiddenOrSkippedSegment(safe)) return null;
|
|
279
|
+
|
|
280
|
+
let source;
|
|
281
|
+
try {
|
|
282
|
+
source = resolveSkillSource(skillId);
|
|
283
|
+
} catch {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const dirPath = await resolveSkillDir(
|
|
289
|
+
source.owner,
|
|
290
|
+
source.repo,
|
|
291
|
+
source.skillSlug,
|
|
292
|
+
source.ref,
|
|
293
|
+
);
|
|
294
|
+
if (!dirPath) return null;
|
|
295
|
+
|
|
296
|
+
const headers = githubHeaders();
|
|
297
|
+
const filePath = `${dirPath}/${safe}`;
|
|
298
|
+
|
|
299
|
+
// Fetch the file entry via GitHub Contents API
|
|
300
|
+
let apiUrl = `https://api.github.com/repos/${encodeURIComponent(source.owner)}/${encodeURIComponent(source.repo)}/contents/${filePath}`;
|
|
301
|
+
if (source.ref) {
|
|
302
|
+
apiUrl += `?ref=${encodeURIComponent(source.ref)}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const response = await fetch(apiUrl, {
|
|
306
|
+
headers,
|
|
307
|
+
signal: AbortSignal.timeout(15_000),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
if (!response.ok) return null;
|
|
311
|
+
|
|
312
|
+
const entry = (await response.json()) as GitHubContentsEntry & {
|
|
313
|
+
size?: number;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Ensure it's a file, not a directory
|
|
317
|
+
if (entry.type !== "file") return null;
|
|
318
|
+
|
|
319
|
+
const name = basename(safe);
|
|
320
|
+
const isBinary = classifyByName(name);
|
|
321
|
+
|
|
322
|
+
// Return null content for binary files
|
|
323
|
+
if (isBinary) {
|
|
324
|
+
return {
|
|
325
|
+
path: safe,
|
|
326
|
+
name,
|
|
327
|
+
size: entry.size ?? 0,
|
|
328
|
+
mimeType: "",
|
|
329
|
+
isBinary: true,
|
|
330
|
+
content: null,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// For text files, check size and fetch content
|
|
335
|
+
const size = entry.size ?? 0;
|
|
336
|
+
if (size > MAX_INLINE_TEXT_SIZE) {
|
|
337
|
+
return {
|
|
338
|
+
path: safe,
|
|
339
|
+
name,
|
|
340
|
+
size,
|
|
341
|
+
mimeType: "",
|
|
342
|
+
isBinary: false,
|
|
343
|
+
content: null,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Fetch the actual file content via download_url
|
|
348
|
+
if (!entry.download_url) return null;
|
|
349
|
+
|
|
350
|
+
const contentResponse = await fetch(entry.download_url, {
|
|
351
|
+
headers,
|
|
352
|
+
signal: AbortSignal.timeout(10_000),
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
if (!contentResponse.ok) return null;
|
|
356
|
+
|
|
357
|
+
const content = await contentResponse.text();
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
path: safe,
|
|
361
|
+
name,
|
|
362
|
+
size: content.length,
|
|
363
|
+
mimeType: "",
|
|
364
|
+
isBinary: false,
|
|
365
|
+
content,
|
|
366
|
+
};
|
|
367
|
+
} catch (err) {
|
|
368
|
+
log.warn(
|
|
369
|
+
{ err, skillId, path: sanitizedPath },
|
|
370
|
+
"Failed to read file content for skills.sh skill",
|
|
371
|
+
);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
async toSlimSkill(skillId: string): Promise<SlimSkillResponse | null> {
|
|
377
|
+
try {
|
|
378
|
+
const source = resolveSkillSource(skillId);
|
|
379
|
+
return {
|
|
380
|
+
id: skillId,
|
|
381
|
+
name: source.skillSlug,
|
|
382
|
+
description: "",
|
|
383
|
+
kind: "catalog",
|
|
384
|
+
status: "available",
|
|
385
|
+
origin: "skillssh",
|
|
386
|
+
slug: skillId,
|
|
387
|
+
sourceRepo: `${source.owner}/${source.repo}`,
|
|
388
|
+
installs: 0,
|
|
389
|
+
};
|
|
390
|
+
} catch {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
@@ -189,14 +189,14 @@ export function resolveSkillSource(source: string): ResolvedSkillSource {
|
|
|
189
189
|
|
|
190
190
|
// ─── GitHub fetch ───────────────────────────────────────────────────────────
|
|
191
191
|
|
|
192
|
-
interface GitHubContentsEntry {
|
|
192
|
+
export interface GitHubContentsEntry {
|
|
193
193
|
name: string;
|
|
194
194
|
type: "file" | "dir";
|
|
195
195
|
download_url: string | null;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/** Build common headers for GitHub API requests (User-Agent + optional auth). */
|
|
199
|
-
function githubHeaders(): Record<string, string> {
|
|
199
|
+
export function githubHeaders(): Record<string, string> {
|
|
200
200
|
const headers: Record<string, string> = {
|
|
201
201
|
Accept: "application/vnd.github.v3+json",
|
|
202
202
|
"User-Agent": "vellum-assistant",
|
|
@@ -208,7 +208,7 @@ function githubHeaders(): Record<string, string> {
|
|
|
208
208
|
return headers;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
interface GitHubTreeEntry {
|
|
211
|
+
export interface GitHubTreeEntry {
|
|
212
212
|
path: string;
|
|
213
213
|
type: "blob" | "tree";
|
|
214
214
|
}
|
|
@@ -217,7 +217,7 @@ interface GitHubTreeEntry {
|
|
|
217
217
|
* Search the repo tree for a directory containing `<slug>/SKILL.md`.
|
|
218
218
|
* Returns the directory path (e.g. "examples/skills-tool/skills/csv") or null.
|
|
219
219
|
*/
|
|
220
|
-
async function findSkillDirInTree(
|
|
220
|
+
export async function findSkillDirInTree(
|
|
221
221
|
owner: string,
|
|
222
222
|
repo: string,
|
|
223
223
|
skillSlug: string,
|