slackhive 0.1.37 → 0.1.39
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/.dockerignore +14 -0
- package/.env.example +44 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +65 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +27 -0
- package/.github/dependabot.yml +20 -0
- package/.github/workflows/audit.yml +149 -0
- package/.github/workflows/ci.yml +135 -0
- package/CHANGELOG.md +52 -0
- package/CODE_OF_CONDUCT.md +37 -0
- package/CONTRIBUTING.md +204 -0
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/SECURITY.md +47 -0
- package/apps/runner/Dockerfile +33 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +8 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.js +63 -0
- package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +1 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +20 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +178 -0
- package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +1 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +13 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.js +144 -0
- package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +1 -0
- package/apps/runner/dist/__tests__/memory-sync.test.d.ts +11 -0
- package/apps/runner/dist/__tests__/memory-sync.test.js +56 -0
- package/apps/runner/dist/__tests__/memory-sync.test.js.map +1 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +9 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.js +271 -0
- package/apps/runner/dist/__tests__/slack-file-support.test.js.map +1 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +12 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.js +400 -0
- package/apps/runner/dist/__tests__/slack-formatting.test.js.map +1 -0
- package/apps/runner/dist/__tests__/thread-context.test.d.ts +12 -0
- package/apps/runner/dist/__tests__/thread-context.test.js +182 -0
- package/apps/runner/dist/__tests__/thread-context.test.js.map +1 -0
- package/apps/runner/dist/agent-runner.d.ts +118 -0
- package/apps/runner/dist/agent-runner.js +352 -0
- package/apps/runner/dist/agent-runner.js.map +1 -0
- package/apps/runner/dist/claude-handler.d.ts +122 -0
- package/apps/runner/dist/claude-handler.js +402 -0
- package/apps/runner/dist/claude-handler.js.map +1 -0
- package/apps/runner/dist/compile-claude-md.d.ts +59 -0
- package/apps/runner/dist/compile-claude-md.js +291 -0
- package/apps/runner/dist/compile-claude-md.js.map +1 -0
- package/apps/runner/dist/correction-handler.d.ts +46 -0
- package/apps/runner/dist/correction-handler.js +162 -0
- package/apps/runner/dist/correction-handler.js.map +1 -0
- package/apps/runner/dist/correction-manager.d.ts +53 -0
- package/apps/runner/dist/correction-manager.js +241 -0
- package/apps/runner/dist/correction-manager.js.map +1 -0
- package/apps/runner/dist/db.d.ts +193 -0
- package/apps/runner/dist/db.js +492 -0
- package/apps/runner/dist/db.js.map +1 -0
- package/apps/runner/dist/index.d.ts +9 -0
- package/apps/runner/dist/index.js +43 -0
- package/apps/runner/dist/index.js.map +1 -0
- package/apps/runner/dist/job-scheduler.d.ts +57 -0
- package/apps/runner/dist/job-scheduler.js +150 -0
- package/apps/runner/dist/job-scheduler.js.map +1 -0
- package/apps/runner/dist/logger.d.ts +32 -0
- package/apps/runner/dist/logger.js +52 -0
- package/apps/runner/dist/logger.js.map +1 -0
- package/apps/runner/dist/mcp-process-manager.d.ts +38 -0
- package/apps/runner/dist/mcp-process-manager.js +189 -0
- package/apps/runner/dist/mcp-process-manager.js.map +1 -0
- package/apps/runner/dist/memory-mcp.d.ts +14 -0
- package/apps/runner/dist/memory-mcp.js +88 -0
- package/apps/runner/dist/memory-mcp.js.map +1 -0
- package/apps/runner/dist/memory-watcher.d.ts +78 -0
- package/apps/runner/dist/memory-watcher.js +220 -0
- package/apps/runner/dist/memory-watcher.js.map +1 -0
- package/apps/runner/dist/slack-handler.d.ts +120 -0
- package/apps/runner/dist/slack-handler.js +843 -0
- package/apps/runner/dist/slack-handler.js.map +1 -0
- package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/apps/runner/package.json +42 -0
- package/apps/runner/src/__tests__/channel-restrictions.test.ts +75 -0
- package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +160 -0
- package/apps/runner/src/__tests__/compile-claude-md.test.ts +139 -0
- package/apps/runner/src/__tests__/memory-sync.test.ts +59 -0
- package/apps/runner/src/__tests__/slack-file-support.test.ts +376 -0
- package/apps/runner/src/__tests__/slack-formatting.test.ts +495 -0
- package/apps/runner/src/__tests__/thread-context.test.ts +215 -0
- package/apps/runner/src/agent-runner.ts +397 -0
- package/apps/runner/src/claude-handler.ts +475 -0
- package/apps/runner/src/compile-claude-md.ts +283 -0
- package/apps/runner/src/correction-handler.ts +191 -0
- package/apps/runner/src/correction-manager.ts +285 -0
- package/apps/runner/src/db.ts +604 -0
- package/apps/runner/src/index.ts +46 -0
- package/apps/runner/src/job-scheduler.ts +165 -0
- package/apps/runner/src/logger.ts +49 -0
- package/apps/runner/src/mcp-process-manager.ts +195 -0
- package/apps/runner/src/memory-mcp.ts +85 -0
- package/apps/runner/src/memory-watcher.ts +215 -0
- package/apps/runner/src/slack-handler.ts +929 -0
- package/apps/runner/tsconfig.json +17 -0
- package/apps/runner/vitest.config.mts +17 -0
- package/apps/web/.eslintrc.json +3 -0
- package/apps/web/.next/app-build-manifest.json +323 -0
- package/apps/web/.next/app-path-routes-manifest.json +46 -0
- package/apps/web/.next/build-manifest.json +33 -0
- package/apps/web/.next/cache/.previewinfo +1 -0
- package/apps/web/.next/cache/.rscinfo +1 -0
- package/apps/web/.next/cache/webpack/client-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/2.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/3.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/4.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
- package/apps/web/.next/cache/webpack/server-production/0.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/1.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/2.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/index.pack +0 -0
- package/apps/web/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/apps/web/.next/diagnostics/build-diagnostics.json +6 -0
- package/apps/web/.next/diagnostics/framework.json +1 -0
- package/apps/web/.next/package.json +1 -0
- package/apps/web/.next/react-loadable-manifest.json +1 -0
- package/apps/web/.next/server/app/_not-found/page.js +2 -0
- package/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/agents/[slug]/page.js +4 -0
- package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/agents/new/page.js +2 -0
- package/apps/web/.next/server/app/agents/new/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +6 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +3 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/route.js +33 -0
- package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/agents/route.js +91 -0
- package/apps/web/.next/server/app/api/agents/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/login/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/me/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/route.js +1 -0
- package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/route.js +1 -0
- package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/jobs/route.js +1 -0
- package/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/mcps/route.js +1 -0
- package/apps/web/.next/server/app/api/mcps/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/api/settings/route.js +1 -0
- package/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/icon.svg/route.js +1 -0
- package/apps/web/.next/server/app/icon.svg/route.js.nft.json +1 -0
- package/apps/web/.next/server/app/jobs/page.js +2 -0
- package/apps/web/.next/server/app/jobs/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/login/page.js +2 -0
- package/apps/web/.next/server/app/login/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/login/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/page.js +2 -0
- package/apps/web/.next/server/app/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/env-vars/page.js +2 -0
- package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/mcps/page.js +2 -0
- package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app/settings/page.js +2 -0
- package/apps/web/.next/server/app/settings/page.js.nft.json +1 -0
- package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -0
- package/apps/web/.next/server/app-paths-manifest.json +46 -0
- package/apps/web/.next/server/chunks/1157.js +9 -0
- package/apps/web/.next/server/chunks/2287.js +1 -0
- package/apps/web/.next/server/chunks/3444.js +1 -0
- package/apps/web/.next/server/chunks/383.js +6 -0
- package/apps/web/.next/server/chunks/4012.js +58 -0
- package/apps/web/.next/server/chunks/6791.js +1 -0
- package/apps/web/.next/server/chunks/7171.js +1 -0
- package/apps/web/.next/server/chunks/8819.js +22 -0
- package/apps/web/.next/server/edge-runtime-webpack.js +2 -0
- package/apps/web/.next/server/edge-runtime-webpack.js.map +1 -0
- package/apps/web/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/apps/web/.next/server/middleware-build-manifest.js +1 -0
- package/apps/web/.next/server/middleware-manifest.json +32 -0
- package/apps/web/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/apps/web/.next/server/next-font-manifest.js +1 -0
- package/apps/web/.next/server/next-font-manifest.json +1 -0
- package/apps/web/.next/server/pages/_app.js +1 -0
- package/apps/web/.next/server/pages/_app.js.nft.json +1 -0
- package/apps/web/.next/server/pages/_document.js +1 -0
- package/apps/web/.next/server/pages/_document.js.nft.json +1 -0
- package/apps/web/.next/server/pages/_error.js +19 -0
- package/apps/web/.next/server/pages/_error.js.nft.json +1 -0
- package/apps/web/.next/server/pages-manifest.json +5 -0
- package/apps/web/.next/server/server-reference-manifest.js +1 -0
- package/apps/web/.next/server/server-reference-manifest.json +1 -0
- package/apps/web/.next/server/src/middleware.js +14 -0
- package/apps/web/.next/server/src/middleware.js.map +1 -0
- package/apps/web/.next/server/webpack-runtime.js +1 -0
- package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +1 -0
- package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +1 -0
- package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +1 -0
- package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +1 -0
- package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +1 -0
- package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +1 -0
- package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +1 -0
- package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +1 -0
- package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +1 -0
- package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +1 -0
- package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +1 -0
- package/apps/web/.next/static/chunks/framework-811407f832a33072.js +1 -0
- package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +1 -0
- package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +1 -0
- package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +1 -0
- package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +1 -0
- package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +1 -0
- package/apps/web/.next/static/css/15371687405525e2.css +5 -0
- package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +1 -0
- package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +1 -0
- package/apps/web/.next/trace +5 -0
- package/apps/web/.next/types/app/agents/[slug]/page.ts +84 -0
- package/apps/web/.next/types/app/agents/new/page.ts +84 -0
- package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +347 -0
- package/apps/web/.next/types/app/api/agents/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/login/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/logout/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/me/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/auth/users/route.ts +347 -0
- package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +347 -0
- package/apps/web/.next/types/app/api/env-vars/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +347 -0
- package/apps/web/.next/types/app/api/jobs/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/[id]/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +347 -0
- package/apps/web/.next/types/app/api/mcps/route.ts +347 -0
- package/apps/web/.next/types/app/api/settings/route.ts +347 -0
- package/apps/web/.next/types/app/jobs/page.ts +84 -0
- package/apps/web/.next/types/app/login/layout.ts +84 -0
- package/apps/web/.next/types/app/login/page.ts +84 -0
- package/apps/web/.next/types/app/page.ts +84 -0
- package/apps/web/.next/types/app/settings/env-vars/page.ts +84 -0
- package/apps/web/.next/types/app/settings/mcps/page.ts +84 -0
- package/apps/web/.next/types/app/settings/page.ts +84 -0
- package/apps/web/.next/types/cache-life.d.ts +141 -0
- package/apps/web/.next/types/package.json +1 -0
- package/apps/web/.next/types/routes.d.ts +114 -0
- package/apps/web/.next/types/validator.ts +448 -0
- package/apps/web/Dockerfile +37 -0
- package/apps/web/next-env.d.ts +6 -0
- package/apps/web/next.config.js +6 -0
- package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/apps/web/package.json +48 -0
- package/apps/web/postcss.config.js +3 -0
- package/apps/web/public/logo.svg +17 -0
- package/apps/web/src/app/agents/[slug]/page.tsx +2235 -0
- package/apps/web/src/app/agents/new/page.tsx +1161 -0
- package/apps/web/src/app/api/agents/[id]/access/route.ts +76 -0
- package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +111 -0
- package/apps/web/src/app/api/agents/[id]/logs/route.ts +84 -0
- package/apps/web/src/app/api/agents/[id]/manifest/route.ts +32 -0
- package/apps/web/src/app/api/agents/[id]/mcps/route.ts +73 -0
- package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +31 -0
- package/apps/web/src/app/api/agents/[id]/memories/route.ts +56 -0
- package/apps/web/src/app/api/agents/[id]/permissions/route.ts +74 -0
- package/apps/web/src/app/api/agents/[id]/reload/route.ts +33 -0
- package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +85 -0
- package/apps/web/src/app/api/agents/[id]/route.ts +81 -0
- package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +52 -0
- package/apps/web/src/app/api/agents/[id]/skills/route.ts +80 -0
- package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +38 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +61 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +53 -0
- package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +84 -0
- package/apps/web/src/app/api/agents/[id]/start/route.ts +35 -0
- package/apps/web/src/app/api/agents/[id]/stop/route.ts +35 -0
- package/apps/web/src/app/api/agents/route.ts +99 -0
- package/apps/web/src/app/api/auth/login/route.ts +39 -0
- package/apps/web/src/app/api/auth/logout/route.ts +21 -0
- package/apps/web/src/app/api/auth/me/route.ts +24 -0
- package/apps/web/src/app/api/auth/users/[id]/route.ts +48 -0
- package/apps/web/src/app/api/auth/users/route.ts +63 -0
- package/apps/web/src/app/api/env-vars/[key]/route.ts +66 -0
- package/apps/web/src/app/api/env-vars/route.ts +59 -0
- package/apps/web/src/app/api/jobs/[id]/route.ts +51 -0
- package/apps/web/src/app/api/jobs/[id]/runs/route.ts +24 -0
- package/apps/web/src/app/api/jobs/route.ts +42 -0
- package/apps/web/src/app/api/mcps/[id]/route.ts +60 -0
- package/apps/web/src/app/api/mcps/[id]/test/route.ts +195 -0
- package/apps/web/src/app/api/mcps/route.ts +72 -0
- package/apps/web/src/app/api/settings/route.ts +42 -0
- package/apps/web/src/app/globals.css +124 -0
- package/apps/web/src/app/icon.svg +17 -0
- package/apps/web/src/app/jobs/page.tsx +543 -0
- package/apps/web/src/app/layout-shell.tsx +89 -0
- package/apps/web/src/app/layout.tsx +18 -0
- package/apps/web/src/app/login/layout.tsx +9 -0
- package/apps/web/src/app/login/page.tsx +150 -0
- package/apps/web/src/app/page.tsx +573 -0
- package/apps/web/src/app/settings/env-vars/page.tsx +216 -0
- package/apps/web/src/app/settings/mcps/page.tsx +763 -0
- package/apps/web/src/app/settings/page.tsx +528 -0
- package/apps/web/src/app/sidebar.tsx +345 -0
- package/apps/web/src/lib/__tests__/api-guard.test.ts +189 -0
- package/apps/web/src/lib/__tests__/auth.test.ts +262 -0
- package/apps/web/src/lib/__tests__/boss-registry.test.ts +323 -0
- package/apps/web/src/lib/__tests__/compile.test.ts +161 -0
- package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +136 -0
- package/apps/web/src/lib/__tests__/db-env-vars.test.ts +216 -0
- package/apps/web/src/lib/__tests__/db-restrictions.test.ts +117 -0
- package/apps/web/src/lib/__tests__/db.integration.test.ts +271 -0
- package/apps/web/src/lib/__tests__/diff.test.ts +102 -0
- package/apps/web/src/lib/__tests__/mcp-mask.test.ts +274 -0
- package/apps/web/src/lib/__tests__/skill-templates.test.ts +237 -0
- package/apps/web/src/lib/__tests__/slack-manifest.test.ts +105 -0
- package/apps/web/src/lib/api-guard.ts +68 -0
- package/apps/web/src/lib/auth-context.tsx +71 -0
- package/apps/web/src/lib/auth.ts +128 -0
- package/apps/web/src/lib/boss-registry.ts +90 -0
- package/apps/web/src/lib/compile.ts +51 -0
- package/apps/web/src/lib/db.ts +1196 -0
- package/apps/web/src/lib/diff.ts +43 -0
- package/apps/web/src/lib/mcp-mask.ts +91 -0
- package/apps/web/src/lib/portal.tsx +23 -0
- package/apps/web/src/lib/skill-templates.ts +148 -0
- package/apps/web/src/lib/slack-manifest.ts +85 -0
- package/apps/web/src/middleware.ts +68 -0
- package/apps/web/tailwind.config.js +6 -0
- package/apps/web/tsconfig.json +23 -0
- package/apps/web/vitest.config.mts +21 -0
- package/cli/.claude/settings.local.json +6 -0
- package/cli/README.md +281 -0
- package/cli/node_modules/.package-lock.json +427 -0
- package/cli/node_modules/commander/LICENSE +22 -0
- package/cli/node_modules/commander/Readme.md +1157 -0
- package/cli/node_modules/commander/esm.mjs +16 -0
- package/cli/node_modules/commander/index.js +24 -0
- package/cli/node_modules/commander/lib/argument.js +149 -0
- package/cli/node_modules/commander/lib/command.js +2509 -0
- package/cli/node_modules/commander/lib/error.js +39 -0
- package/cli/node_modules/commander/lib/help.js +520 -0
- package/cli/node_modules/commander/lib/option.js +330 -0
- package/cli/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/cli/node_modules/commander/package-support.json +16 -0
- package/cli/node_modules/commander/package.json +84 -0
- package/cli/node_modules/commander/typings/esm.d.mts +3 -0
- package/cli/node_modules/commander/typings/index.d.ts +969 -0
- package/cli/package-lock.json +449 -0
- package/cli/package.json +44 -0
- package/cli/src/commands/init.ts +514 -0
- package/cli/src/commands/manage.ts +115 -0
- package/cli/src/index.ts +63 -0
- package/cli/tsconfig.json +14 -0
- package/docker-compose.yml +122 -0
- package/docs/agents/boss-agents.mdx +108 -0
- package/docs/agents/creating-agents.mdx +132 -0
- package/docs/agents/memory.mdx +113 -0
- package/docs/agents/tools.mdx +103 -0
- package/docs/configuration/env-vars.mdx +166 -0
- package/docs/configuration/mcp-servers.mdx +203 -0
- package/docs/configuration/slack-app.mdx +175 -0
- package/docs/docs.json +79 -0
- package/docs/favicon.svg +17 -0
- package/docs/features/history.mdx +60 -0
- package/docs/features/import-export.mdx +77 -0
- package/docs/features/logs.mdx +131 -0
- package/docs/features/multi-workspace.mdx +90 -0
- package/docs/features/scheduled-jobs.mdx +231 -0
- package/docs/features/users.mdx +92 -0
- package/docs/introduction.mdx +160 -0
- package/docs/logo/dark.svg +17 -0
- package/docs/logo/light.svg +17 -0
- package/docs/logo/wide-dark.svg +12 -0
- package/docs/logo/wide-light.svg +12 -0
- package/docs/quickstart.mdx +270 -0
- package/docs/self-hosting/docker.mdx +151 -0
- package/docs/self-hosting/production.mdx +176 -0
- package/package.json +20 -36
- package/packages/shared/dist/index.d.ts +8 -0
- package/packages/shared/dist/index.d.ts.map +1 -0
- package/packages/shared/dist/index.js +24 -0
- package/packages/shared/dist/index.js.map +1 -0
- package/packages/shared/dist/types.d.ts +584 -0
- package/packages/shared/dist/types.d.ts.map +1 -0
- package/packages/shared/dist/types.js +39 -0
- package/packages/shared/dist/types.js.map +1 -0
- package/packages/shared/package.json +15 -0
- package/packages/shared/src/db/schema.sql +354 -0
- package/packages/shared/src/index.ts +8 -0
- package/packages/shared/src/types.ts +683 -0
- package/packages/shared/tsconfig.json +17 -0
- package/scripts/dev.sh +45 -0
- /package/{dist → cli/dist}/commands/init.d.ts +0 -0
- /package/{dist → cli/dist}/commands/init.js +0 -0
- /package/{dist → cli/dist}/commands/manage.d.ts +0 -0
- /package/{dist → cli/dist}/commands/manage.js +0 -0
- /package/{dist → cli/dist}/index.d.ts +0 -0
- /package/{dist → cli/dist}/index.js +0 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview `slackhive init` — clone, configure, and start SlackHive.
|
|
3
|
+
*
|
|
4
|
+
* @module cli/commands/init
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync, spawn } from 'child_process';
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
9
|
+
import { join, resolve } from 'path';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import ora from 'ora';
|
|
12
|
+
import prompts from 'prompts';
|
|
13
|
+
|
|
14
|
+
const REPO_URL = 'https://github.com/pelago-labs/slackhive.git';
|
|
15
|
+
|
|
16
|
+
interface InitOptions {
|
|
17
|
+
dir: string;
|
|
18
|
+
skipStart?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function detectClaudeBin(): string {
|
|
22
|
+
let claudeBin: string;
|
|
23
|
+
try {
|
|
24
|
+
claudeBin = execSync('which claude', { encoding: 'utf-8' }).trim();
|
|
25
|
+
} catch {
|
|
26
|
+
throw new Error('Claude Code not found. Please install Claude Code first.');
|
|
27
|
+
}
|
|
28
|
+
if (!claudeBin) throw new Error('Claude Code not found. Please install Claude Code first.');
|
|
29
|
+
return claudeBin;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface OAuthCredentials {
|
|
33
|
+
accessToken: string;
|
|
34
|
+
refreshToken: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parses a JSON credential blob and extracts OAuth tokens.
|
|
39
|
+
*/
|
|
40
|
+
function parseOAuthFromJson(json: string): OAuthCredentials | null {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(json);
|
|
43
|
+
const oauth = parsed?.claudeAiOauth;
|
|
44
|
+
if (oauth?.accessToken && oauth?.refreshToken) {
|
|
45
|
+
return { accessToken: oauth.accessToken, refreshToken: oauth.refreshToken };
|
|
46
|
+
}
|
|
47
|
+
} catch { /* invalid json */ }
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Extracts the OAuth credentials from the OS credential store.
|
|
53
|
+
* Tries macOS Keychain, then Linux secret-tool (GNOME Keyring).
|
|
54
|
+
* Returns access + refresh tokens, or null if not found.
|
|
55
|
+
*/
|
|
56
|
+
function extractOAuthCredentials(): OAuthCredentials | null {
|
|
57
|
+
// macOS: read from Keychain
|
|
58
|
+
try {
|
|
59
|
+
const creds = execSync('security find-generic-password -s "Claude Code-credentials" -w', {
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
62
|
+
}).trim();
|
|
63
|
+
const result = parseOAuthFromJson(creds);
|
|
64
|
+
if (result) return result;
|
|
65
|
+
} catch { /* not macOS or not found */ }
|
|
66
|
+
|
|
67
|
+
// Linux: try secret-tool (GNOME Keyring)
|
|
68
|
+
try {
|
|
69
|
+
const creds = execSync('secret-tool lookup service "Claude Code-credentials"', {
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
72
|
+
}).trim();
|
|
73
|
+
const result = parseOAuthFromJson(creds);
|
|
74
|
+
if (result) return result;
|
|
75
|
+
} catch { /* not available */ }
|
|
76
|
+
|
|
77
|
+
// Fallback: read credentials file directly (headless Linux / no keyring)
|
|
78
|
+
try {
|
|
79
|
+
const credPath = join(process.env.HOME || '~', '.claude', '.credentials.json');
|
|
80
|
+
if (existsSync(credPath)) {
|
|
81
|
+
const creds = readFileSync(credPath, 'utf-8').trim();
|
|
82
|
+
const result = parseOAuthFromJson(creds);
|
|
83
|
+
if (result) return result;
|
|
84
|
+
}
|
|
85
|
+
} catch { /* file not readable or invalid */ }
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Runs `slackhive init` — interactive setup wizard.
|
|
92
|
+
*
|
|
93
|
+
* @param {InitOptions} opts - CLI options.
|
|
94
|
+
*/
|
|
95
|
+
export async function init(opts: InitOptions): Promise<void> {
|
|
96
|
+
const dir = resolve(opts.dir);
|
|
97
|
+
|
|
98
|
+
const O = chalk.hex('#D97757').bold;
|
|
99
|
+
const W = chalk.hex('#EBE6E0').bold;
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(' ' + W('│ │'));
|
|
102
|
+
console.log(' ' + W('───┼───┼───'));
|
|
103
|
+
console.log(' ' + O('>') + W(' ──┼──') + O('█') + W('┼──'));
|
|
104
|
+
console.log(' ' + W('│ │'));
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log(chalk.bold(' SlackHive') + chalk.gray(' — AI agent teams on Slack'));
|
|
107
|
+
console.log('');
|
|
108
|
+
|
|
109
|
+
// ── Step 1: Check prerequisites ───────────────────────────────────────────
|
|
110
|
+
console.log(chalk.bold.hex('#D97757')(' [1/4]') + chalk.bold(' Checking prerequisites'));
|
|
111
|
+
console.log('');
|
|
112
|
+
|
|
113
|
+
const checks = [
|
|
114
|
+
{ name: 'Docker daemon', cmd: 'docker info', errMsg: 'Docker is not running. Please start Docker Desktop and try again.' },
|
|
115
|
+
{ name: 'Docker Compose', cmd: 'docker compose version', errMsg: 'Docker Compose not found. Please install Docker Desktop.' },
|
|
116
|
+
{ name: 'Git', cmd: 'git --version', errMsg: 'Git not found. Please install Git first.' },
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
for (const check of checks) {
|
|
120
|
+
const spinner = ora(` Checking ${check.name}...`).start();
|
|
121
|
+
try {
|
|
122
|
+
execSync(check.cmd, { stdio: 'ignore' });
|
|
123
|
+
spinner.succeed(chalk.green(`${check.name} ready`));
|
|
124
|
+
} catch {
|
|
125
|
+
spinner.fail(chalk.red(`${check.name}: ${check.errMsg}`));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
// ── Step 2: Clone ─────────────────────────────────────────────────────────
|
|
132
|
+
console.log(chalk.bold.hex('#D97757')(' [2/4]') + chalk.bold(' Getting SlackHive'));
|
|
133
|
+
console.log('');
|
|
134
|
+
|
|
135
|
+
if (existsSync(dir)) {
|
|
136
|
+
console.log(chalk.yellow(` note: Directory ${opts.dir} already exists — using existing`));
|
|
137
|
+
} else {
|
|
138
|
+
const spinner = ora(' Cloning repository...').start();
|
|
139
|
+
try {
|
|
140
|
+
execSync(`git clone --depth 1 ${REPO_URL} "${dir}"`, { stdio: 'ignore' });
|
|
141
|
+
spinner.succeed('Repository cloned');
|
|
142
|
+
} catch {
|
|
143
|
+
spinner.fail('Failed to clone repository');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log('');
|
|
148
|
+
|
|
149
|
+
// ── Step 3: Configure .env ────────────────────────────────────────────────
|
|
150
|
+
const envPath = join(dir, '.env');
|
|
151
|
+
const freshEnv = !existsSync(envPath);
|
|
152
|
+
|
|
153
|
+
if (freshEnv) {
|
|
154
|
+
console.log(chalk.bold.hex('#D97757')(' [3/4]') + chalk.bold(' Configure environment'));
|
|
155
|
+
console.log('');
|
|
156
|
+
|
|
157
|
+
const authMode = await prompts({
|
|
158
|
+
type: 'select',
|
|
159
|
+
name: 'mode',
|
|
160
|
+
message: 'Claude authentication',
|
|
161
|
+
choices: [
|
|
162
|
+
{ title: 'API Key — pay-per-use via Anthropic API', value: 'apikey' },
|
|
163
|
+
{ title: 'Subscription — run `claude login` first', value: 'subscription' },
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (!authMode.mode) {
|
|
168
|
+
console.log(chalk.red('\n Setup cancelled.'));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const questions: prompts.PromptObject[] = [];
|
|
173
|
+
let oauthCreds: OAuthCredentials | null = null;
|
|
174
|
+
|
|
175
|
+
if (authMode.mode === 'apikey') {
|
|
176
|
+
questions.push({
|
|
177
|
+
type: 'text',
|
|
178
|
+
name: 'anthropicKey',
|
|
179
|
+
message: 'Anthropic API key',
|
|
180
|
+
validate: (v: string) => v.startsWith('sk-') ? true : 'Must start with sk-',
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
// Claude subscription mode — extract OAuth token
|
|
184
|
+
const claudeDir = join(process.env.HOME || '~', '.claude');
|
|
185
|
+
if (!existsSync(claudeDir)) {
|
|
186
|
+
console.log(chalk.yellow('\n warning: ~/.claude not found. Run `claude login` first, then re-run `slackhive init`.'));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
console.log(chalk.green(' ✓') + ' Found ~/.claude credentials');
|
|
190
|
+
|
|
191
|
+
const spinner = ora(' Extracting OAuth credentials...').start();
|
|
192
|
+
oauthCreds = extractOAuthCredentials();
|
|
193
|
+
if (oauthCreds) {
|
|
194
|
+
spinner.succeed('OAuth credentials extracted');
|
|
195
|
+
} else {
|
|
196
|
+
spinner.warn('Could not auto-extract credentials from keychain');
|
|
197
|
+
console.log(chalk.gray(' On Linux/headless servers, paste your OAuth token manually.'));
|
|
198
|
+
console.log(chalk.gray(' Get it from a machine where you ran `claude login`:'));
|
|
199
|
+
console.log(chalk.gray(' security find-generic-password -s "Claude Code-credentials" -w'));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
const tokenResponse = await prompts([
|
|
203
|
+
{ type: 'password', name: 'accessToken', message: 'OAuth access token (sk-ant-oat01-...)', validate: (v: string) => v.startsWith('sk-ant-oat') ? true : 'Must start with sk-ant-oat' },
|
|
204
|
+
{ type: 'password', name: 'refreshToken', message: 'OAuth refresh token (sk-ant-ort01-...)', validate: (v: string) => v.startsWith('sk-ant-ort') ? true : 'Must start with sk-ant-ort' },
|
|
205
|
+
]);
|
|
206
|
+
if (!tokenResponse.accessToken || !tokenResponse.refreshToken) {
|
|
207
|
+
console.log(chalk.red('\n Setup cancelled. Use API Key mode instead on headless servers.'));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
oauthCreds = { accessToken: tokenResponse.accessToken, refreshToken: tokenResponse.refreshToken };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
questions.push(
|
|
215
|
+
{ type: 'text', name: 'adminUsername', message: 'Admin username', initial: 'admin' },
|
|
216
|
+
{ type: 'password', name: 'adminPassword', message: 'Admin password', validate: (v: string) => v.length >= 6 ? true : 'At least 6 characters' },
|
|
217
|
+
{ type: 'text', name: 'postgresPassword', message: 'Postgres password', initial: randomSecret().slice(0, 16) },
|
|
218
|
+
{ type: 'text', name: 'redisPassword', message: 'Redis password', initial: randomSecret().slice(0, 16) },
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const response = await prompts(questions);
|
|
222
|
+
|
|
223
|
+
if (!response.adminPassword) {
|
|
224
|
+
console.log(chalk.red('\n Setup cancelled.'));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let envContent = '# Generated by slackhive init\n\n';
|
|
229
|
+
if (authMode.mode === 'apikey') {
|
|
230
|
+
envContent += `ANTHROPIC_API_KEY=${response.anthropicKey}\n`;
|
|
231
|
+
} else {
|
|
232
|
+
envContent += `# Claude Code subscription — OAuth credentials from keychain\n`;
|
|
233
|
+
envContent += `CLAUDE_CODE_OAUTH_TOKEN=${oauthCreds!.accessToken}\n`;
|
|
234
|
+
envContent += `CLAUDE_CODE_OAUTH_REFRESH_TOKEN=${oauthCreds!.refreshToken}\n`;
|
|
235
|
+
}
|
|
236
|
+
envContent += `\nPOSTGRES_DB=slackhive\n`;
|
|
237
|
+
envContent += `POSTGRES_USER=slackhive\n`;
|
|
238
|
+
envContent += `POSTGRES_PASSWORD=${response.postgresPassword}\n`;
|
|
239
|
+
envContent += `\nREDIS_PASSWORD=${response.redisPassword}\n`;
|
|
240
|
+
envContent += `\nADMIN_USERNAME=${response.adminUsername}\n`;
|
|
241
|
+
envContent += `ADMIN_PASSWORD=${response.adminPassword}\n`;
|
|
242
|
+
envContent += `AUTH_SECRET=${randomSecret()}\n`;
|
|
243
|
+
envContent += `ENV_SECRET_KEY=${randomSecret()}\n`;
|
|
244
|
+
envContent += `\nNODE_ENV=production\n`;
|
|
245
|
+
|
|
246
|
+
writeFileSync(envPath, envContent);
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(chalk.green(' ✓') + ' .env file created');
|
|
249
|
+
console.log('');
|
|
250
|
+
} else {
|
|
251
|
+
console.log(chalk.bold.hex('#D97757')(' [3/4]') + chalk.bold(' Configure environment'));
|
|
252
|
+
console.log('');
|
|
253
|
+
// Check if existing .env is missing required keys
|
|
254
|
+
const envContents = existsSync(envPath) ? require('fs').readFileSync(envPath, 'utf-8') : '';
|
|
255
|
+
const missingKeys: string[] = [];
|
|
256
|
+
if (!envContents.includes('REDIS_PASSWORD=')) missingKeys.push('REDIS_PASSWORD');
|
|
257
|
+
if (!envContents.includes('AUTH_SECRET=')) missingKeys.push('AUTH_SECRET');
|
|
258
|
+
if (!envContents.includes('ENV_SECRET_KEY=')) missingKeys.push('ENV_SECRET_KEY');
|
|
259
|
+
if (missingKeys.length > 0) {
|
|
260
|
+
console.log(chalk.yellow(` warning: .env is missing: ${missingKeys.join(', ')} — patching...`));
|
|
261
|
+
let patch = '';
|
|
262
|
+
if (!envContents.includes('REDIS_PASSWORD=')) patch += `\nREDIS_PASSWORD=${randomSecret().slice(0, 16)}\n`;
|
|
263
|
+
if (!envContents.includes('AUTH_SECRET=')) patch += `AUTH_SECRET=${randomSecret()}\n`;
|
|
264
|
+
if (!envContents.includes('ENV_SECRET_KEY=')) patch += `ENV_SECRET_KEY=${randomSecret()}\n`;
|
|
265
|
+
require('fs').appendFileSync(envPath, patch);
|
|
266
|
+
console.log(chalk.green(' ✓') + ' .env patched');
|
|
267
|
+
} else {
|
|
268
|
+
console.log(chalk.yellow(' note: .env already exists — skipping configuration'));
|
|
269
|
+
}
|
|
270
|
+
console.log('');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── Step 4: Build & start ─────────────────────────────────────────────────
|
|
274
|
+
let webReady = true;
|
|
275
|
+
if (!opts.skipStart) {
|
|
276
|
+
console.log(chalk.bold.hex('#D97757')(' [4/4]') + chalk.bold(' Building & starting services'));
|
|
277
|
+
console.log(chalk.gray(' This takes 3–5 minutes on first run while Docker builds images.'));
|
|
278
|
+
console.log('');
|
|
279
|
+
|
|
280
|
+
// Pre-flight: check Docker has enough disk space (need ~3GB)
|
|
281
|
+
try {
|
|
282
|
+
const dfOut = execSync('docker system df --format "{{.Size}}"', { encoding: 'utf-8' });
|
|
283
|
+
void dfOut; // just checking it runs without error
|
|
284
|
+
} catch {
|
|
285
|
+
console.log(chalk.yellow(' note: Could not check Docker disk usage — continuing anyway'));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Pre-flight: warn if low disk space on host
|
|
289
|
+
try {
|
|
290
|
+
const df = execSync('df -k . | tail -1', { encoding: 'utf-8' }).trim();
|
|
291
|
+
const available = parseInt(df.split(/\s+/)[3]);
|
|
292
|
+
if (!isNaN(available) && available < 3 * 1024 * 1024) {
|
|
293
|
+
console.log(chalk.yellow(` warning: less than 3GB disk space available. Build may fail.`));
|
|
294
|
+
console.log('');
|
|
295
|
+
}
|
|
296
|
+
} catch { /* non-fatal */ }
|
|
297
|
+
|
|
298
|
+
// Remove stale Postgres volume only on fresh init — prevents password mismatch
|
|
299
|
+
// when credentials were just generated. Skip if .env already existed (re-run).
|
|
300
|
+
if (freshEnv) {
|
|
301
|
+
try {
|
|
302
|
+
execSync('docker compose down -v', { cwd: dir, stdio: 'ignore' });
|
|
303
|
+
} catch { /* non-fatal — may not exist yet */ }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const buildOk = await runDockerBuild(dir, opts.dir);
|
|
307
|
+
|
|
308
|
+
if (buildOk) {
|
|
309
|
+
// If containers didn't come up during build, retry once silently
|
|
310
|
+
try {
|
|
311
|
+
execSync('docker compose up -d', { cwd: dir, stdio: 'ignore' });
|
|
312
|
+
} catch { /* non-fatal */ }
|
|
313
|
+
|
|
314
|
+
// Wait for web UI — up to 3 minutes
|
|
315
|
+
const webSpinner = ora(' Waiting for web UI to be ready...').start();
|
|
316
|
+
let ready = false;
|
|
317
|
+
for (let i = 0; i < 60; i++) {
|
|
318
|
+
try {
|
|
319
|
+
execSync('curl -sf http://localhost:3001/login', { stdio: 'ignore' });
|
|
320
|
+
ready = true;
|
|
321
|
+
break;
|
|
322
|
+
} catch {
|
|
323
|
+
await sleep(3000);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (ready) {
|
|
327
|
+
webSpinner.succeed('Web UI is ready');
|
|
328
|
+
} else {
|
|
329
|
+
webReady = false;
|
|
330
|
+
webSpinner.stopAndPersist({ symbol: ' ' });
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
webReady = false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ── Done ──────────────────────────────────────────────────────────────────
|
|
338
|
+
console.log('');
|
|
339
|
+
if (webReady) {
|
|
340
|
+
console.log(' ' + chalk.bgHex('#D97757').black.bold(' SlackHive is ready! '));
|
|
341
|
+
console.log('');
|
|
342
|
+
console.log(` ${chalk.bold('Open:')} ${chalk.cyan('http://localhost:3001')}`);
|
|
343
|
+
} else {
|
|
344
|
+
console.log(' ' + chalk.bold('Setup complete!'));
|
|
345
|
+
console.log('');
|
|
346
|
+
console.log(chalk.gray(' Services are still starting. Once ready:'));
|
|
347
|
+
console.log(` ${chalk.bold('Run:')} ${chalk.cyan('slackhive start')}`);
|
|
348
|
+
console.log(` ${chalk.bold('Open:')} ${chalk.cyan('http://localhost:3001')}`);
|
|
349
|
+
}
|
|
350
|
+
console.log(` ${chalk.bold('Dir:')} ${chalk.gray(dir)}`);
|
|
351
|
+
console.log('');
|
|
352
|
+
console.log(chalk.gray(' Useful commands:'));
|
|
353
|
+
console.log(chalk.gray(' slackhive start — Start services'));
|
|
354
|
+
console.log(chalk.gray(' slackhive stop — Stop services'));
|
|
355
|
+
console.log(chalk.gray(' slackhive status — Show container status'));
|
|
356
|
+
console.log(chalk.gray(' slackhive logs — Tail runner logs'));
|
|
357
|
+
console.log(chalk.gray(' slackhive update — Pull latest & rebuild'));
|
|
358
|
+
console.log('');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Runs `docker compose up -d --build`.
|
|
363
|
+
* Shows a single updating spinner line with the current build step.
|
|
364
|
+
* Docker's raw progress output is suppressed to keep the terminal clean.
|
|
365
|
+
*/
|
|
366
|
+
function runDockerBuild(cwd: string, displayDir: string): Promise<boolean> {
|
|
367
|
+
return new Promise((resolve) => {
|
|
368
|
+
const proc = spawn('docker', ['compose', '--progress', 'plain', 'up', '-d', '--build'], {
|
|
369
|
+
cwd,
|
|
370
|
+
env: { ...process.env },
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const startTime = Date.now();
|
|
374
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
375
|
+
let frameIdx = 0;
|
|
376
|
+
|
|
377
|
+
// Phased progress tracking
|
|
378
|
+
const phases = [
|
|
379
|
+
{ name: 'Installing system packages', weight: 10, pattern: /apk add|fetch.*APKINDEX/i },
|
|
380
|
+
{ name: 'Installing npm dependencies', weight: 30, pattern: /npm ci|npm install|added \d+ packages/i },
|
|
381
|
+
{ name: 'Compiling TypeScript', weight: 10, pattern: /tsc|--skipLibCheck/i },
|
|
382
|
+
{ name: 'Building web app', weight: 30, pattern: /next build|next\.config/i },
|
|
383
|
+
{ name: 'Creating containers', weight: 10, pattern: /exporting to image|naming to|exporting layers/i },
|
|
384
|
+
{ name: 'Starting services', weight: 10, pattern: /Container .*(Starting|Started|Healthy|Created)/i },
|
|
385
|
+
];
|
|
386
|
+
let currentPhase = 0;
|
|
387
|
+
let phaseStartTime = Date.now();
|
|
388
|
+
|
|
389
|
+
function getProgress(): number {
|
|
390
|
+
let pct = 0;
|
|
391
|
+
for (let i = 0; i < currentPhase; i++) pct += phases[i].weight;
|
|
392
|
+
// Add partial progress within current phase
|
|
393
|
+
if (currentPhase < phases.length) {
|
|
394
|
+
const elapsed = (Date.now() - phaseStartTime) / 1000;
|
|
395
|
+
const estimatedDuration = currentPhase === 1 ? 90 : currentPhase === 3 ? 100 : 30;
|
|
396
|
+
const partial = Math.min(0.9, elapsed / estimatedDuration);
|
|
397
|
+
pct += phases[currentPhase].weight * partial;
|
|
398
|
+
}
|
|
399
|
+
return Math.min(99, Math.round(pct));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function renderBar(): string {
|
|
403
|
+
const pct = getProgress();
|
|
404
|
+
const cols = process.stdout.columns || 80;
|
|
405
|
+
const barWidth = Math.min(20, Math.max(10, cols - 55));
|
|
406
|
+
const filled = Math.round((pct / 100) * barWidth);
|
|
407
|
+
const empty = barWidth - filled;
|
|
408
|
+
const bar = chalk.hex('#D97757')('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
409
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
410
|
+
const phaseName = currentPhase < phases.length ? phases[currentPhase].name : 'Finishing';
|
|
411
|
+
const frame = frames[frameIdx++ % frames.length];
|
|
412
|
+
const pctStr = String(pct).padStart(2);
|
|
413
|
+
return ` ${chalk.hex('#D97757')(frame)} ${bar} ${chalk.bold(pctStr + '%')} ${phaseName} ${chalk.gray('(' + elapsed + 's)')}`;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const spinnerInterval = setInterval(() => {
|
|
417
|
+
process.stdout.write(`\r\x1b[K${renderBar()}`);
|
|
418
|
+
}, 80);
|
|
419
|
+
|
|
420
|
+
let buf = '';
|
|
421
|
+
const errorLines: string[] = [];
|
|
422
|
+
|
|
423
|
+
const onData = (chunk: Buffer) => {
|
|
424
|
+
buf += chunk.toString().replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
425
|
+
const lines = buf.split('\n');
|
|
426
|
+
buf = lines.pop() ?? '';
|
|
427
|
+
for (const raw of lines) {
|
|
428
|
+
const line = raw.trim();
|
|
429
|
+
if (!line) continue;
|
|
430
|
+
|
|
431
|
+
// Check if we've entered a new phase
|
|
432
|
+
for (let i = currentPhase + 1; i < phases.length; i++) {
|
|
433
|
+
if (phases[i].pattern.test(line)) {
|
|
434
|
+
// Print completed phases
|
|
435
|
+
const elapsed = Math.floor((Date.now() - phaseStartTime) / 1000);
|
|
436
|
+
process.stdout.write('\r\x1b[K');
|
|
437
|
+
console.log(' ' + chalk.green('✓') + ' ' + phases[currentPhase].name + chalk.gray(` (${elapsed}s)`));
|
|
438
|
+
// Skip intermediate phases
|
|
439
|
+
for (let j = currentPhase + 1; j < i; j++) {
|
|
440
|
+
console.log(' ' + chalk.green('✓') + ' ' + phases[j].name + chalk.gray(' (cached)'));
|
|
441
|
+
}
|
|
442
|
+
currentPhase = i;
|
|
443
|
+
phaseStartTime = Date.now();
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (/error/i.test(line)) errorLines.push(line);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
proc.stdout.on('data', onData);
|
|
453
|
+
proc.stderr.on('data', onData);
|
|
454
|
+
|
|
455
|
+
proc.on('close', (code) => {
|
|
456
|
+
clearInterval(spinnerInterval);
|
|
457
|
+
process.stdout.write('\r\x1b[K');
|
|
458
|
+
|
|
459
|
+
if (code === 0) {
|
|
460
|
+
// Print any remaining phases as done
|
|
461
|
+
const elapsed = Math.floor((Date.now() - phaseStartTime) / 1000);
|
|
462
|
+
if (currentPhase < phases.length) {
|
|
463
|
+
console.log(' ' + chalk.green('✓') + ' ' + phases[currentPhase].name + chalk.gray(` (${elapsed}s)`));
|
|
464
|
+
for (let j = currentPhase + 1; j < phases.length; j++) {
|
|
465
|
+
console.log(' ' + chalk.green('✓') + ' ' + phases[j].name);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
console.log('');
|
|
469
|
+
console.log(' ' + chalk.green('✓') + chalk.bold(' All services started'));
|
|
470
|
+
resolve(true);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.log(' ' + chalk.red('✗') + ' Failed to start services');
|
|
475
|
+
console.log('');
|
|
476
|
+
|
|
477
|
+
const allErrors = errorLines.join('\n').toLowerCase();
|
|
478
|
+
if (allErrors.includes('no space left') || allErrors.includes('disk full')) {
|
|
479
|
+
console.log(chalk.yellow(' Cause: Docker is out of disk space.'));
|
|
480
|
+
console.log(chalk.gray(' Fix: docker system prune -a'));
|
|
481
|
+
} else if (allErrors.includes('port is already allocated') || allErrors.includes('address already in use')) {
|
|
482
|
+
const portMatch = /bind for .+:(\d+)/.exec(allErrors);
|
|
483
|
+
const port = portMatch ? portMatch[1] : 'a required port';
|
|
484
|
+
console.log(chalk.yellow(` Cause: Port ${port} is already in use.`));
|
|
485
|
+
console.log(chalk.gray(` Fix: stop the process on port ${port} and retry`));
|
|
486
|
+
} else if (allErrors.includes('permission denied') || allErrors.includes('unauthorized')) {
|
|
487
|
+
console.log(chalk.yellow(' Cause: Docker permission denied — is Docker Desktop running?'));
|
|
488
|
+
} else if (allErrors.includes('memory') || allErrors.includes('oom')) {
|
|
489
|
+
console.log(chalk.yellow(' Cause: Docker ran out of memory.'));
|
|
490
|
+
console.log(chalk.gray(' Fix: increase Docker Desktop memory to 4GB+ in Settings → Resources'));
|
|
491
|
+
} else if (allErrors.includes('network') || allErrors.includes('timeout') || allErrors.includes('pull') || allErrors.includes('tls') || allErrors.includes('certificate')) {
|
|
492
|
+
console.log(chalk.yellow(' Cause: Network/TLS error — try restarting Docker Desktop.'));
|
|
493
|
+
} else if (errorLines.length > 0) {
|
|
494
|
+
console.log(chalk.gray(' Error details:'));
|
|
495
|
+
errorLines.slice(-5).forEach(l => console.log(chalk.red(' ' + l)));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
console.log('');
|
|
499
|
+
console.log(chalk.gray(` To retry: cd ${displayDir} && docker compose up -d --build`));
|
|
500
|
+
resolve(false);
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function randomSecret(): string {
|
|
506
|
+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
507
|
+
let result = '';
|
|
508
|
+
for (let i = 0; i < 32; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function sleep(ms: number): Promise<void> {
|
|
513
|
+
return new Promise(r => setTimeout(r, ms));
|
|
514
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Management commands — start, stop, status, logs, update.
|
|
3
|
+
*
|
|
4
|
+
* All commands look for docker-compose.yml in the current directory
|
|
5
|
+
* or the `slackhive` subdirectory.
|
|
6
|
+
*
|
|
7
|
+
* @module cli/commands/manage
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync, spawn } from 'child_process';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import ora from 'ora';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Finds the SlackHive project directory.
|
|
18
|
+
* Checks cwd first, then ./slackhive subdirectory.
|
|
19
|
+
*
|
|
20
|
+
* @returns {string} Path to the project directory.
|
|
21
|
+
*/
|
|
22
|
+
function findProjectDir(): string {
|
|
23
|
+
if (existsSync(join(process.cwd(), 'docker-compose.yml'))) {
|
|
24
|
+
return process.cwd();
|
|
25
|
+
}
|
|
26
|
+
const sub = join(process.cwd(), 'slackhive');
|
|
27
|
+
if (existsSync(join(sub, 'docker-compose.yml'))) {
|
|
28
|
+
return sub;
|
|
29
|
+
}
|
|
30
|
+
console.log(chalk.red(' Could not find SlackHive project.'));
|
|
31
|
+
console.log(chalk.gray(' Run this command from the SlackHive directory, or run `slackhive init` first.'));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Start all SlackHive services.
|
|
37
|
+
*/
|
|
38
|
+
export async function start(): Promise<void> {
|
|
39
|
+
const dir = findProjectDir();
|
|
40
|
+
const spinner = ora('Starting SlackHive services...').start();
|
|
41
|
+
try {
|
|
42
|
+
execSync('docker compose up -d', { cwd: dir, stdio: 'ignore' });
|
|
43
|
+
spinner.succeed('All services started');
|
|
44
|
+
console.log(chalk.gray(' Web UI: http://localhost:3001'));
|
|
45
|
+
} catch {
|
|
46
|
+
spinner.fail('Failed to start services');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stop all SlackHive services.
|
|
52
|
+
*/
|
|
53
|
+
export async function stop(): Promise<void> {
|
|
54
|
+
const dir = findProjectDir();
|
|
55
|
+
const spinner = ora('Stopping SlackHive services...').start();
|
|
56
|
+
try {
|
|
57
|
+
execSync('docker compose stop', { cwd: dir, stdio: 'ignore' });
|
|
58
|
+
spinner.succeed('All services stopped');
|
|
59
|
+
} catch {
|
|
60
|
+
spinner.fail('Failed to stop services');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Show running SlackHive containers.
|
|
66
|
+
*/
|
|
67
|
+
export async function status(): Promise<void> {
|
|
68
|
+
const dir = findProjectDir();
|
|
69
|
+
try {
|
|
70
|
+
const output = execSync('docker compose ps', { cwd: dir, encoding: 'utf-8' });
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(chalk.bold(' SlackHive Status'));
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(output);
|
|
75
|
+
} catch {
|
|
76
|
+
console.log(chalk.red(' Failed to get status'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tail runner service logs.
|
|
82
|
+
*/
|
|
83
|
+
export async function logs(opts: { follow?: boolean }): Promise<void> {
|
|
84
|
+
const dir = findProjectDir();
|
|
85
|
+
const args = ['compose', 'logs', 'runner'];
|
|
86
|
+
if (opts.follow !== false) args.push('-f');
|
|
87
|
+
|
|
88
|
+
const proc = spawn('docker', args, { cwd: dir, stdio: 'inherit' });
|
|
89
|
+
proc.on('error', () => console.log(chalk.red(' Failed to tail logs')));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Pull latest changes and rebuild.
|
|
94
|
+
*/
|
|
95
|
+
export async function update(): Promise<void> {
|
|
96
|
+
const dir = findProjectDir();
|
|
97
|
+
|
|
98
|
+
const pullSpinner = ora('Pulling latest changes...').start();
|
|
99
|
+
try {
|
|
100
|
+
execSync('git pull', { cwd: dir, stdio: 'ignore' });
|
|
101
|
+
pullSpinner.succeed('Code updated');
|
|
102
|
+
} catch {
|
|
103
|
+
pullSpinner.fail('Failed to pull — do you have uncommitted changes?');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const buildSpinner = ora('Rebuilding services (this may take a minute)...').start();
|
|
108
|
+
try {
|
|
109
|
+
execSync('docker compose up -d --build', { cwd: dir, stdio: 'ignore', timeout: 600000 });
|
|
110
|
+
buildSpinner.succeed('Services rebuilt and restarted');
|
|
111
|
+
console.log(chalk.gray(' Web UI: http://localhost:3001'));
|
|
112
|
+
} catch {
|
|
113
|
+
buildSpinner.fail('Failed to rebuild');
|
|
114
|
+
}
|
|
115
|
+
}
|
package/cli/src/index.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview SlackHive CLI — install, configure, and manage SlackHive.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* slackhive init — Clone repo, configure .env, start services
|
|
8
|
+
* slackhive start — Start all Docker Compose services
|
|
9
|
+
* slackhive stop — Stop all services
|
|
10
|
+
* slackhive status — Show running containers
|
|
11
|
+
* slackhive logs — Tail runner logs
|
|
12
|
+
* slackhive update — Pull latest changes and rebuild
|
|
13
|
+
*
|
|
14
|
+
* @module cli
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
import { init } from './commands/init';
|
|
19
|
+
import { start, stop, status, logs, update } from './commands/manage';
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
+
const { version } = require('../package.json');
|
|
22
|
+
|
|
23
|
+
const program = new Command();
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.name('slackhive')
|
|
27
|
+
.description('CLI to install and manage SlackHive — AI agent teams on Slack')
|
|
28
|
+
.version(version);
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command('init')
|
|
32
|
+
.description('Clone SlackHive repo, configure environment, and start services')
|
|
33
|
+
.option('-d, --dir <path>', 'Directory to install into', 'slackhive')
|
|
34
|
+
.option('--skip-start', 'Skip starting services after init')
|
|
35
|
+
.action(init);
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.command('start')
|
|
39
|
+
.description('Start all SlackHive services')
|
|
40
|
+
.action(start);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command('stop')
|
|
44
|
+
.description('Stop all SlackHive services')
|
|
45
|
+
.action(stop);
|
|
46
|
+
|
|
47
|
+
program
|
|
48
|
+
.command('status')
|
|
49
|
+
.description('Show running SlackHive containers')
|
|
50
|
+
.action(status);
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.command('logs')
|
|
54
|
+
.description('Tail runner service logs')
|
|
55
|
+
.option('-f, --follow', 'Follow log output', true)
|
|
56
|
+
.action(logs);
|
|
57
|
+
|
|
58
|
+
program
|
|
59
|
+
.command('update')
|
|
60
|
+
.description('Pull latest changes and rebuild')
|
|
61
|
+
.action(update);
|
|
62
|
+
|
|
63
|
+
program.parse(process.argv);
|