constella 0.1.0
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/.next/BUILD_ID +1 -0
- package/.next/app-path-routes-manifest.json +53 -0
- package/.next/build-manifest.json +20 -0
- package/.next/diagnostics/build-diagnostics.json +6 -0
- package/.next/diagnostics/framework.json +1 -0
- package/.next/export-marker.json +6 -0
- package/.next/images-manifest.json +68 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +36 -0
- package/.next/react-loadable-manifest.json +14 -0
- package/.next/required-server-files.js +343 -0
- package/.next/required-server-files.json +343 -0
- package/.next/routes-manifest.json +362 -0
- package/.next/server/app/(app)/activity/page.js +2 -0
- package/.next/server/app/(app)/activity/page.js.nft.json +1 -0
- package/.next/server/app/(app)/activity/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/agents/[handle]/page.js +18 -0
- package/.next/server/app/(app)/agents/[handle]/page.js.nft.json +1 -0
- package/.next/server/app/(app)/agents/[handle]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/code/page.js +2 -0
- package/.next/server/app/(app)/code/page.js.nft.json +1 -0
- package/.next/server/app/(app)/code/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/config/page.js +2 -0
- package/.next/server/app/(app)/config/page.js.nft.json +1 -0
- package/.next/server/app/(app)/config/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/costs/page.js +2 -0
- package/.next/server/app/(app)/costs/page.js.nft.json +1 -0
- package/.next/server/app/(app)/costs/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/cron/page.js +2 -0
- package/.next/server/app/(app)/cron/page.js.nft.json +1 -0
- package/.next/server/app/(app)/cron/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/dashboard/page.js +2 -0
- package/.next/server/app/(app)/dashboard/page.js.nft.json +1 -0
- package/.next/server/app/(app)/dashboard/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/docs/[id]/page.js +2 -0
- package/.next/server/app/(app)/docs/[id]/page.js.nft.json +1 -0
- package/.next/server/app/(app)/docs/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/docs/page.js +2 -0
- package/.next/server/app/(app)/docs/page.js.nft.json +1 -0
- package/.next/server/app/(app)/docs/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/github/page.js +2 -0
- package/.next/server/app/(app)/github/page.js.nft.json +1 -0
- package/.next/server/app/(app)/github/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/goals/page.js +2 -0
- package/.next/server/app/(app)/goals/page.js.nft.json +1 -0
- package/.next/server/app/(app)/goals/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/inbox/page.js +2 -0
- package/.next/server/app/(app)/inbox/page.js.nft.json +1 -0
- package/.next/server/app/(app)/inbox/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/knowledge/page.js +3 -0
- package/.next/server/app/(app)/knowledge/page.js.nft.json +1 -0
- package/.next/server/app/(app)/knowledge/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/models/page.js +2 -0
- package/.next/server/app/(app)/models/page.js.nft.json +1 -0
- package/.next/server/app/(app)/models/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/notifications/page.js +2 -0
- package/.next/server/app/(app)/notifications/page.js.nft.json +1 -0
- package/.next/server/app/(app)/notifications/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/org/page.js +2 -0
- package/.next/server/app/(app)/org/page.js.nft.json +1 -0
- package/.next/server/app/(app)/org/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/organizations/page.js +2 -0
- package/.next/server/app/(app)/organizations/page.js.nft.json +1 -0
- package/.next/server/app/(app)/organizations/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/page.js +3 -0
- package/.next/server/app/(app)/page.js.nft.json +1 -0
- package/.next/server/app/(app)/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/planner/page.js +2 -0
- package/.next/server/app/(app)/planner/page.js.nft.json +1 -0
- package/.next/server/app/(app)/planner/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/plugins/page.js +2 -0
- package/.next/server/app/(app)/plugins/page.js.nft.json +1 -0
- package/.next/server/app/(app)/plugins/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/pm/page.js +2 -0
- package/.next/server/app/(app)/pm/page.js.nft.json +1 -0
- package/.next/server/app/(app)/pm/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/prepare-deploy/page.js +19 -0
- package/.next/server/app/(app)/prepare-deploy/page.js.nft.json +1 -0
- package/.next/server/app/(app)/prepare-deploy/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/profile/page.js +2 -0
- package/.next/server/app/(app)/profile/page.js.nft.json +1 -0
- package/.next/server/app/(app)/profile/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/pulse/page.js +2 -0
- package/.next/server/app/(app)/pulse/page.js.nft.json +1 -0
- package/.next/server/app/(app)/pulse/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/reports/[id]/page.js +3 -0
- package/.next/server/app/(app)/reports/[id]/page.js.nft.json +1 -0
- package/.next/server/app/(app)/reports/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/reports/page.js +5 -0
- package/.next/server/app/(app)/reports/page.js.nft.json +1 -0
- package/.next/server/app/(app)/reports/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/routines/page.js +2 -0
- package/.next/server/app/(app)/routines/page.js.nft.json +1 -0
- package/.next/server/app/(app)/routines/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/search/page.js +2 -0
- package/.next/server/app/(app)/search/page.js.nft.json +1 -0
- package/.next/server/app/(app)/search/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/security/page.js +2 -0
- package/.next/server/app/(app)/security/page.js.nft.json +1 -0
- package/.next/server/app/(app)/security/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/skills/page.js +18 -0
- package/.next/server/app/(app)/skills/page.js.nft.json +1 -0
- package/.next/server/app/(app)/skills/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/tasks/page.js +2 -0
- package/.next/server/app/(app)/tasks/page.js.nft.json +1 -0
- package/.next/server/app/(app)/tasks/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/test-dev/page.js +2 -0
- package/.next/server/app/(app)/test-dev/page.js.nft.json +1 -0
- package/.next/server/app/(app)/test-dev/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(app)/update/page.js +2 -0
- package/.next/server/app/(app)/update/page.js.nft.json +1 -0
- package/.next/server/app/(app)/update/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(auth)/login/page.js +2 -0
- package/.next/server/app/(auth)/login/page.js.nft.json +1 -0
- package/.next/server/app/(auth)/login/page_client-reference-manifest.js +1 -0
- package/.next/server/app/(auth)/onboarding/page.js +18 -0
- package/.next/server/app/(auth)/onboarding/page.js.nft.json +1 -0
- package/.next/server/app/(auth)/onboarding/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_global-error/page.js +32 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +16 -0
- package/.next/server/app/_global-error.rsc +15 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -0
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +6 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page.js +2 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/[...all]/route.js +1 -0
- package/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/[...all]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/cron/tick/route.js +52 -0
- package/.next/server/app/api/cron/tick/route.js.nft.json +1 -0
- package/.next/server/app/api/cron/tick/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/dev-login/route.js +1 -0
- package/.next/server/app/api/dev-login/route.js.nft.json +1 -0
- package/.next/server/app/api/dev-login/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/locks/acquire/route.js +1 -0
- package/.next/server/app/api/locks/acquire/route.js.nft.json +1 -0
- package/.next/server/app/api/locks/acquire/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models/progress/route.js +1 -0
- package/.next/server/app/api/models/progress/route.js.nft.json +1 -0
- package/.next/server/app/api/models/progress/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/passkey/authenticate/options/route.js +1 -0
- package/.next/server/app/api/passkey/authenticate/options/route.js.nft.json +1 -0
- package/.next/server/app/api/passkey/authenticate/options/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/passkey/authenticate/verify/route.js +1 -0
- package/.next/server/app/api/passkey/authenticate/verify/route.js.nft.json +1 -0
- package/.next/server/app/api/passkey/authenticate/verify/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/passkey/register/options/route.js +1 -0
- package/.next/server/app/api/passkey/register/options/route.js.nft.json +1 -0
- package/.next/server/app/api/passkey/register/options/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/passkey/register/verify/route.js +1 -0
- package/.next/server/app/api/passkey/register/verify/route.js.nft.json +1 -0
- package/.next/server/app/api/passkey/register/verify/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/stream/route.js +4 -0
- package/.next/server/app/api/stream/route.js.nft.json +1 -0
- package/.next/server/app/api/stream/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sync/file/route.js +2 -0
- package/.next/server/app/api/sync/file/route.js.nft.json +1 -0
- package/.next/server/app/api/sync/file/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/telegram/poll/route.js +15 -0
- package/.next/server/app/api/telegram/poll/route.js.nft.json +1 -0
- package/.next/server/app/api/telegram/poll/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/upload/route.js +1 -0
- package/.next/server/app/api/upload/route.js.nft.json +1 -0
- package/.next/server/app/api/upload/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/v1/[[...path]]/route.js +1 -0
- package/.next/server/app/api/v1/[[...path]]/route.js.nft.json +1 -0
- package/.next/server/app/api/v1/[[...path]]/route_client-reference-manifest.js +1 -0
- package/.next/server/app-paths-manifest.json +53 -0
- package/.next/server/chunks/1003.js +1 -0
- package/.next/server/chunks/127.js +26 -0
- package/.next/server/chunks/1388.js +1 -0
- package/.next/server/chunks/1408.js +21 -0
- package/.next/server/chunks/1572.js +1 -0
- package/.next/server/chunks/1591.js +24 -0
- package/.next/server/chunks/1619.js +188 -0
- package/.next/server/chunks/162.js +1 -0
- package/.next/server/chunks/1881.js +1 -0
- package/.next/server/chunks/1968.js +1 -0
- package/.next/server/chunks/2297.js +348 -0
- package/.next/server/chunks/2341.js +1 -0
- package/.next/server/chunks/2517.js +1 -0
- package/.next/server/chunks/2549.js +1 -0
- package/.next/server/chunks/259.js +14 -0
- package/.next/server/chunks/2599.js +1 -0
- package/.next/server/chunks/260.js +1 -0
- package/.next/server/chunks/2867.js +147 -0
- package/.next/server/chunks/3018.js +1 -0
- package/.next/server/chunks/3050.js +18 -0
- package/.next/server/chunks/3085.js +12 -0
- package/.next/server/chunks/3131.js +1 -0
- package/.next/server/chunks/3242.js +1 -0
- package/.next/server/chunks/3266.js +15 -0
- package/.next/server/chunks/3524.js +1 -0
- package/.next/server/chunks/3527.js +479 -0
- package/.next/server/chunks/3533.js +869 -0
- package/.next/server/chunks/3550.js +1 -0
- package/.next/server/chunks/3609.js +2 -0
- package/.next/server/chunks/3667.js +462 -0
- package/.next/server/chunks/3760.js +4 -0
- package/.next/server/chunks/4679.js +1 -0
- package/.next/server/chunks/4804.js +1 -0
- package/.next/server/chunks/4832.js +2 -0
- package/.next/server/chunks/4853.js +1 -0
- package/.next/server/chunks/4979.js +67 -0
- package/.next/server/chunks/5060.js +1 -0
- package/.next/server/chunks/5278.js +1 -0
- package/.next/server/chunks/5614.js +1 -0
- package/.next/server/chunks/5818.js +1 -0
- package/.next/server/chunks/6479.js +1 -0
- package/.next/server/chunks/6658.js +1 -0
- package/.next/server/chunks/6706.js +1 -0
- package/.next/server/chunks/6719.js +1 -0
- package/.next/server/chunks/678.js +1 -0
- package/.next/server/chunks/683.js +1 -0
- package/.next/server/chunks/6862.js +1 -0
- package/.next/server/chunks/6882.js +1 -0
- package/.next/server/chunks/7037.js +1 -0
- package/.next/server/chunks/7107.js +741 -0
- package/.next/server/chunks/73.js +17 -0
- package/.next/server/chunks/7327.js +1 -0
- package/.next/server/chunks/7514.js +1 -0
- package/.next/server/chunks/7622.js +1 -0
- package/.next/server/chunks/7778.js +1 -0
- package/.next/server/chunks/7912.js +1 -0
- package/.next/server/chunks/7949.js +1 -0
- package/.next/server/chunks/7971.js +1 -0
- package/.next/server/chunks/7989.js +1 -0
- package/.next/server/chunks/842.js +22 -0
- package/.next/server/chunks/8762.js +15 -0
- package/.next/server/chunks/8823.js +77 -0
- package/.next/server/chunks/9146.js +4 -0
- package/.next/server/chunks/9676.js +1 -0
- package/.next/server/chunks/9783.js +22 -0
- package/.next/server/chunks/9969.js +3 -0
- package/.next/server/functions-config-manifest.json +18 -0
- package/.next/server/instrumentation.js +1 -0
- package/.next/server/instrumentation.js.nft.json +1 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/middleware.js +18 -0
- package/.next/server/middleware.js.nft.json +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages-manifest.json +3 -0
- package/.next/server/prefetch-hints.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/chunks/1858-339516f78a4b00da.js +1 -0
- package/.next/static/chunks/2320-fc8b39380e69d465.js +2 -0
- package/.next/static/chunks/23550918-ff694f70f4b0648c.js +1 -0
- package/.next/static/chunks/3219-ebb3c23be38c838d.js +1 -0
- package/.next/static/chunks/4263-adecb5b466380b6e.js +1 -0
- package/.next/static/chunks/5479-0cceab68cd0ca9c7.js +1 -0
- package/.next/static/chunks/5701-665b927b06158b76.js +1 -0
- package/.next/static/chunks/5920.6451a68b63918988.js +1 -0
- package/.next/static/chunks/6575-5c9139720bb0f5bf.js +4 -0
- package/.next/static/chunks/6834-4759af1ce7d95fb6.js +32 -0
- package/.next/static/chunks/7509.721cd47a931c5518.js +1 -0
- package/.next/static/chunks/8264-1ca011989ee2b231.js +1 -0
- package/.next/static/chunks/9219-4a39a98b5502d9d1.js +1 -0
- package/.next/static/chunks/9690-53d5222618cbeddb.js +1 -0
- package/.next/static/chunks/app/(app)/activity/page-3973534281ecea81.js +1 -0
- package/.next/static/chunks/app/(app)/agents/[handle]/page-83662a175c098282.js +1 -0
- package/.next/static/chunks/app/(app)/code/page-33979545192cd137.js +1 -0
- package/.next/static/chunks/app/(app)/config/page-9933aed1ca8a85c1.js +1 -0
- package/.next/static/chunks/app/(app)/costs/page-131c4dc580efcc19.js +1 -0
- package/.next/static/chunks/app/(app)/cron/page-53ea1aff998a87ca.js +1 -0
- package/.next/static/chunks/app/(app)/dashboard/page-deed83aaa9d0d447.js +1 -0
- package/.next/static/chunks/app/(app)/docs/[id]/page-38c993d73c0eab4f.js +1 -0
- package/.next/static/chunks/app/(app)/docs/page-bf463b55d0554e86.js +1 -0
- package/.next/static/chunks/app/(app)/error-988cd28480809861.js +1 -0
- package/.next/static/chunks/app/(app)/github/page-62678b4e82dfecb6.js +1 -0
- package/.next/static/chunks/app/(app)/goals/page-4adb426fe1c96106.js +1 -0
- package/.next/static/chunks/app/(app)/inbox/page-e347dc55ab467310.js +1 -0
- package/.next/static/chunks/app/(app)/knowledge/page-65393a045b4349be.js +1 -0
- package/.next/static/chunks/app/(app)/layout-7f65675705b011d8.js +1 -0
- package/.next/static/chunks/app/(app)/models/page-e01f1dd7e49a2951.js +1 -0
- package/.next/static/chunks/app/(app)/notifications/page-56548ac87aef00da.js +1 -0
- package/.next/static/chunks/app/(app)/org/page-699e6a6dc0db7d81.js +1 -0
- package/.next/static/chunks/app/(app)/organizations/page-36051a380a7e8eb7.js +1 -0
- package/.next/static/chunks/app/(app)/page-7d1011a566f81520.js +1 -0
- package/.next/static/chunks/app/(app)/planner/page-dab7ced94083373a.js +1 -0
- package/.next/static/chunks/app/(app)/plugins/page-5b5a1f53389be42e.js +1 -0
- package/.next/static/chunks/app/(app)/pm/page-0de5c08c0b227bb0.js +1 -0
- package/.next/static/chunks/app/(app)/prepare-deploy/page-e426038552df8d41.js +1 -0
- package/.next/static/chunks/app/(app)/profile/page-608dfcaf8aae0a69.js +1 -0
- package/.next/static/chunks/app/(app)/pulse/page-309ccaca91de1faa.js +1 -0
- package/.next/static/chunks/app/(app)/reports/[id]/page-53ea1aff998a87ca.js +1 -0
- package/.next/static/chunks/app/(app)/reports/page-68cdc6dcfa472d86.js +1 -0
- package/.next/static/chunks/app/(app)/routines/page-bcc55550b197a9fa.js +1 -0
- package/.next/static/chunks/app/(app)/search/page-5c5f67558d0dbf0d.js +1 -0
- package/.next/static/chunks/app/(app)/security/page-a7d41e36aa366b45.js +1 -0
- package/.next/static/chunks/app/(app)/skills/page-c5b21e89593b8336.js +1 -0
- package/.next/static/chunks/app/(app)/tasks/page-08ae079e3e54d2ce.js +1 -0
- package/.next/static/chunks/app/(app)/test-dev/page-633f82dfd9c3ce23.js +1 -0
- package/.next/static/chunks/app/(app)/update/page-4be019054351bfac.js +1 -0
- package/.next/static/chunks/app/(auth)/login/page-6e85d3377062acae.js +1 -0
- package/.next/static/chunks/app/(auth)/onboarding/page-ebb10c175abf3b85.js +1 -0
- package/.next/static/chunks/app/_global-error/page-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/_not-found/page-dc38b02aebeab535.js +1 -0
- package/.next/static/chunks/app/api/auth/[...all]/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/cron/tick/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/dev-login/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/locks/acquire/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/models/progress/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/passkey/authenticate/options/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/passkey/authenticate/verify/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/passkey/register/options/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/passkey/register/verify/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/stream/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/sync/file/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/telegram/poll/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/upload/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/api/v1/[[...path]]/route-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/app/error-09899a13c38b6e89.js +1 -0
- package/.next/static/chunks/app/global-error-b8050d4d886f448c.js +1 -0
- package/.next/static/chunks/app/layout-ab9deed1e7e2e9df.js +1 -0
- package/.next/static/chunks/framework-4b2c6b6043dd203f.js +1 -0
- package/.next/static/chunks/main-722e16032e7764d1.js +5 -0
- package/.next/static/chunks/main-app-761880af2b6f1962.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-23fe50a6bf589c97.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-222e3894b78c67db.js +1 -0
- package/.next/static/css/0a9b5805594444e3.css +1 -0
- package/.next/static/yztMvBwyrWWkSqP6jfXoa/_buildManifest.js +1 -0
- package/.next/static/yztMvBwyrWWkSqP6jfXoa/_ssgManifest.js +1 -0
- package/.next/trace-build +1 -0
- package/.next/types/app/(app)/activity/page.ts +87 -0
- package/.next/types/app/(app)/agents/[handle]/page.ts +87 -0
- package/.next/types/app/(app)/code/page.ts +87 -0
- package/.next/types/app/(app)/config/page.ts +87 -0
- package/.next/types/app/(app)/costs/page.ts +87 -0
- package/.next/types/app/(app)/cron/page.ts +87 -0
- package/.next/types/app/(app)/dashboard/page.ts +87 -0
- package/.next/types/app/(app)/docs/[id]/page.ts +87 -0
- package/.next/types/app/(app)/docs/page.ts +87 -0
- package/.next/types/app/(app)/github/page.ts +87 -0
- package/.next/types/app/(app)/goals/page.ts +87 -0
- package/.next/types/app/(app)/inbox/page.ts +87 -0
- package/.next/types/app/(app)/knowledge/page.ts +87 -0
- package/.next/types/app/(app)/models/page.ts +87 -0
- package/.next/types/app/(app)/notifications/page.ts +87 -0
- package/.next/types/app/(app)/org/page.ts +87 -0
- package/.next/types/app/(app)/organizations/page.ts +87 -0
- package/.next/types/app/(app)/page.ts +87 -0
- package/.next/types/app/(app)/planner/page.ts +87 -0
- package/.next/types/app/(app)/plugins/page.ts +87 -0
- package/.next/types/app/(app)/pm/page.ts +87 -0
- package/.next/types/app/(app)/prepare-deploy/page.ts +87 -0
- package/.next/types/app/(app)/profile/page.ts +87 -0
- package/.next/types/app/(app)/pulse/page.ts +87 -0
- package/.next/types/app/(app)/reports/[id]/page.ts +87 -0
- package/.next/types/app/(app)/reports/page.ts +87 -0
- package/.next/types/app/(app)/routines/page.ts +87 -0
- package/.next/types/app/(app)/search/page.ts +87 -0
- package/.next/types/app/(app)/security/page.ts +87 -0
- package/.next/types/app/(app)/skills/page.ts +87 -0
- package/.next/types/app/(app)/tasks/page.ts +87 -0
- package/.next/types/app/(app)/test-dev/page.ts +87 -0
- package/.next/types/app/(app)/update/page.ts +87 -0
- package/.next/types/app/(auth)/login/page.ts +87 -0
- package/.next/types/app/(auth)/onboarding/page.ts +87 -0
- package/.next/types/app/api/auth/[...all]/route.ts +351 -0
- package/.next/types/app/api/cron/tick/route.ts +351 -0
- package/.next/types/app/api/dev-login/route.ts +351 -0
- package/.next/types/app/api/locks/acquire/route.ts +351 -0
- package/.next/types/app/api/models/progress/route.ts +351 -0
- package/.next/types/app/api/passkey/authenticate/options/route.ts +351 -0
- package/.next/types/app/api/passkey/authenticate/verify/route.ts +351 -0
- package/.next/types/app/api/passkey/register/options/route.ts +351 -0
- package/.next/types/app/api/passkey/register/verify/route.ts +351 -0
- package/.next/types/app/api/stream/route.ts +351 -0
- package/.next/types/app/api/sync/file/route.ts +351 -0
- package/.next/types/app/api/telegram/poll/route.ts +351 -0
- package/.next/types/app/api/upload/route.ts +351 -0
- package/.next/types/app/api/v1/[[...path]]/route.ts +351 -0
- package/.next/types/cache-life.d.ts +145 -0
- package/.next/types/link.d.ts +210 -0
- package/.next/types/package.json +1 -0
- package/.next/types/routes.d.ts +120 -0
- package/.next/types/validator.ts +511 -0
- package/CHANGELOG.md +312 -0
- package/LICENSE +21 -0
- package/README.md +382 -0
- package/README.pt-BR.md +391 -0
- package/bin/constella.mjs +329 -0
- package/bin/guard-hook.mjs +44 -0
- package/bin/lock-hook.mjs +49 -0
- package/bin/worker.mjs +142 -0
- package/docs/assets/arch-orbit.svg +56 -0
- package/docs/assets/blackhole.svg +37 -0
- package/docs/assets/divider-orbit.svg +23 -0
- package/docs/assets/hero-constella.svg +72 -0
- package/docs/en/AGENTS.md +279 -0
- package/docs/en/AI_ARCHITECTURE.md +373 -0
- package/docs/en/ARCHITECTURE.md +334 -0
- package/docs/en/AUTH_MODE.md +247 -0
- package/docs/en/CHAT_COMMANDS.md +305 -0
- package/docs/en/CONFIGURATION.md +340 -0
- package/docs/en/DEPLOY.md +331 -0
- package/docs/en/DM.md +297 -0
- package/docs/en/FAQ.md +258 -0
- package/docs/en/GITHUB.md +341 -0
- package/docs/en/GOALS_SPECS_ISSUES.md +303 -0
- package/docs/en/INBOX.md +340 -0
- package/docs/en/INSTALLATION.md +329 -0
- package/docs/en/KB_AGENT.md +305 -0
- package/docs/en/KB_RAG.md +356 -0
- package/docs/en/MCP.md +313 -0
- package/docs/en/MEMORY_RAG.md +289 -0
- package/docs/en/MODELS.md +341 -0
- package/docs/en/ONBOARDING.md +327 -0
- package/docs/en/PLUGINS.md +290 -0
- package/docs/en/PORTABLE_MODE.md +387 -0
- package/docs/en/PO_AGENT.md +379 -0
- package/docs/en/PREPARE_DEPLOY.md +308 -0
- package/docs/en/PROJECT_STACKS.md +258 -0
- package/docs/en/PUBLIC_API.md +315 -0
- package/docs/en/PUBLISHING.md +343 -0
- package/docs/en/README.md +95 -0
- package/docs/en/SECURITY.md +280 -0
- package/docs/en/SKILLS.md +349 -0
- package/docs/en/START_MODE.md +340 -0
- package/docs/en/SYNCED_BLOCKS.md +320 -0
- package/docs/en/TEAM_ROOM.md +285 -0
- package/docs/en/TELEGRAM.md +294 -0
- package/docs/en/TEST_DEV.md +321 -0
- package/docs/en/TROUBLESHOOTING.md +294 -0
- package/docs/en/UPDATE.md +301 -0
- package/docs/en/VPS_MODE.md +334 -0
- package/docs/en/WORKFLOW.md +321 -0
- package/docs/pt/AGENTS.md +279 -0
- package/docs/pt/AI_ARCHITECTURE.md +373 -0
- package/docs/pt/ARCHITECTURE.md +334 -0
- package/docs/pt/AUTH_MODE.md +247 -0
- package/docs/pt/CHAT_COMMANDS.md +307 -0
- package/docs/pt/CONFIGURATION.md +340 -0
- package/docs/pt/DEPLOY.md +331 -0
- package/docs/pt/DM.md +297 -0
- package/docs/pt/FAQ.md +258 -0
- package/docs/pt/GITHUB.md +341 -0
- package/docs/pt/GOALS_SPECS_ISSUES.md +303 -0
- package/docs/pt/INBOX.md +340 -0
- package/docs/pt/INSTALLATION.md +329 -0
- package/docs/pt/KB_AGENT.md +305 -0
- package/docs/pt/KB_RAG.md +356 -0
- package/docs/pt/MCP.md +313 -0
- package/docs/pt/MEMORY_RAG.md +289 -0
- package/docs/pt/MODELS.md +341 -0
- package/docs/pt/ONBOARDING.md +327 -0
- package/docs/pt/PLUGINS.md +290 -0
- package/docs/pt/PORTABLE_MODE.md +387 -0
- package/docs/pt/PO_AGENT.md +379 -0
- package/docs/pt/PREPARE_DEPLOY.md +308 -0
- package/docs/pt/PROJECT_STACKS.md +258 -0
- package/docs/pt/PUBLIC_API.md +315 -0
- package/docs/pt/PUBLISHING.md +343 -0
- package/docs/pt/README.md +95 -0
- package/docs/pt/SECURITY.md +280 -0
- package/docs/pt/SKILLS.md +349 -0
- package/docs/pt/START_MODE.md +340 -0
- package/docs/pt/SYNCED_BLOCKS.md +320 -0
- package/docs/pt/TEAM_ROOM.md +285 -0
- package/docs/pt/TELEGRAM.md +294 -0
- package/docs/pt/TEST_DEV.md +321 -0
- package/docs/pt/TROUBLESHOOTING.md +294 -0
- package/docs/pt/UPDATE.md +301 -0
- package/docs/pt/VPS_MODE.md +334 -0
- package/docs/pt/WORKFLOW.md +321 -0
- package/drizzle/0000_regular_nightshade.sql +644 -0
- package/drizzle/0001_mixed_zombie.sql +106 -0
- package/drizzle/meta/0000_snapshot.json +4650 -0
- package/drizzle/meta/0001_snapshot.json +5418 -0
- package/drizzle/meta/_journal.json +20 -0
- package/drizzle.config.mjs +16 -0
- package/next.config.mjs +18 -0
- package/package.json +130 -0
- package/scripts/clean-repo.mjs +20 -0
- package/scripts/dev-all.mjs +46 -0
- package/scripts/i18n-parity.mjs +57 -0
- package/scripts/mcp-server.mjs +100 -0
- package/scripts/postbuild.mjs +11 -0
- package/scripts/publish-public.mjs +116 -0
- package/scripts/start-all.mjs +45 -0
- package/scripts/trim-next.mjs +23 -0
- package/scripts/vps-install.sh +39 -0
- package/skills/CONTRIBUTING.md +122 -0
- package/skills/COVERAGE.md +129 -0
- package/skills/INDEX.json +3443 -0
- package/skills/README.md +57 -0
- package/skills/design/animation-motion/SKILL.md +60 -0
- package/skills/design/color-and-typography/SKILL.md +60 -0
- package/skills/design/css-techniques/SKILL.md +58 -0
- package/skills/design/design-systems/SKILL.md +60 -0
- package/skills/design/gradients/SKILL.md +59 -0
- package/skills/design/graphic-design-basics/SKILL.md +55 -0
- package/skills/design/microinteractions/SKILL.md +58 -0
- package/skills/design/responsive-layout/SKILL.md +59 -0
- package/skills/design/ui-ux-principles/SKILL.md +58 -0
- package/skills/engineering/architecture/api-design-rest-graphql/SKILL.md +67 -0
- package/skills/engineering/architecture/caching-strategies/SKILL.md +59 -0
- package/skills/engineering/architecture/data-modeling/SKILL.md +64 -0
- package/skills/engineering/architecture/message-queues-async/SKILL.md +58 -0
- package/skills/engineering/architecture/scalability-reliability/SKILL.md +62 -0
- package/skills/engineering/architecture/software-architecture-patterns/SKILL.md +56 -0
- package/skills/engineering/architecture/system-design-fundamentals/SKILL.md +56 -0
- package/skills/engineering/backend/auth-and-authorization/SKILL.md +62 -0
- package/skills/engineering/backend/backend-fundamentals/SKILL.md +65 -0
- package/skills/engineering/backend/observability-logging/SKILL.md +60 -0
- package/skills/engineering/frontend/accessibility-wcag/SKILL.md +57 -0
- package/skills/engineering/frontend/frontend-architecture/SKILL.md +65 -0
- package/skills/engineering/frontend/rendering-strategies-ssr-csr/SKILL.md +60 -0
- package/skills/engineering/frontend/state-management/SKILL.md +69 -0
- package/skills/engineering/performance/backend-performance/SKILL.md +69 -0
- package/skills/engineering/performance/database-query-optimization/SKILL.md +64 -0
- package/skills/engineering/performance/profiling-and-benchmarking/SKILL.md +60 -0
- package/skills/engineering/performance/web-performance-core-vitals/SKILL.md +72 -0
- package/skills/engineering/practices/clean-code/SKILL.md +61 -0
- package/skills/engineering/practices/code-optimization/SKILL.md +60 -0
- package/skills/engineering/practices/code-review-practices/SKILL.md +58 -0
- package/skills/engineering/practices/git-workflow/SKILL.md +62 -0
- package/skills/engineering/practices/refactoring/SKILL.md +58 -0
- package/skills/engineering/security/appsec-fundamentals/SKILL.md +70 -0
- package/skills/engineering/security/dependency-supply-chain/SKILL.md +77 -0
- package/skills/engineering/security/owasp-asvs/SKILL.md +54 -0
- package/skills/engineering/security/owasp-top-10/SKILL.md +63 -0
- package/skills/engineering/security/secrets-management/SKILL.md +58 -0
- package/skills/engineering/security/secure-auth-sessions/SKILL.md +56 -0
- package/skills/engineering/testing/tdd-and-coverage/SKILL.md +62 -0
- package/skills/engineering/testing/testing-strategy-pyramid/SKILL.md +56 -0
- package/skills/engineering/testing/unit-integration-e2e/SKILL.md +75 -0
- package/skills/languages/c/SKILL.md +74 -0
- package/skills/languages/clojure/SKILL.md +73 -0
- package/skills/languages/cpp/SKILL.md +75 -0
- package/skills/languages/csharp/SKILL.md +75 -0
- package/skills/languages/dart/SKILL.md +82 -0
- package/skills/languages/elixir/SKILL.md +74 -0
- package/skills/languages/erlang/SKILL.md +76 -0
- package/skills/languages/go/SKILL.md +83 -0
- package/skills/languages/haskell/SKILL.md +70 -0
- package/skills/languages/java/SKILL.md +71 -0
- package/skills/languages/javascript/SKILL.md +62 -0
- package/skills/languages/kotlin/SKILL.md +68 -0
- package/skills/languages/lua/SKILL.md +79 -0
- package/skills/languages/objectivec/SKILL.md +83 -0
- package/skills/languages/php/SKILL.md +74 -0
- package/skills/languages/python/SKILL.md +68 -0
- package/skills/languages/r/SKILL.md +70 -0
- package/skills/languages/ruby/SKILL.md +67 -0
- package/skills/languages/rust/SKILL.md +72 -0
- package/skills/languages/scala/SKILL.md +73 -0
- package/skills/languages/swift/SKILL.md +73 -0
- package/skills/languages/typescript/SKILL.md +69 -0
- package/skills/meta/authoring-agent-skills/SKILL.md +73 -0
- package/skills/meta/progressive-disclosure/SKILL.md +65 -0
- package/skills/meta/skill-frontmatter-spec/SKILL.md +65 -0
- package/skills/process/adr-technical-decisions/SKILL.md +59 -0
- package/skills/process/app-planning/SKILL.md +63 -0
- package/skills/process/architecture-before-code/SKILL.md +52 -0
- package/skills/process/breaking-work-into-sprints/SKILL.md +53 -0
- package/skills/process/idea-to-product/SKILL.md +50 -0
- package/skills/process/mocks-and-screen-flows/SKILL.md +52 -0
- package/skills/process/prioritization-moscow-rice/SKILL.md +64 -0
- package/skills/process/problem-framing/SKILL.md +51 -0
- package/skills/process/product-discovery/SKILL.md +53 -0
- package/skills/process/readme-generation/SKILL.md +90 -0
- package/skills/process/requirements-to-specs/SKILL.md +53 -0
- package/skills/process/research-official-docs/SKILL.md +58 -0
- package/skills/process/review-code-perf-security/SKILL.md +65 -0
- package/skills/process/security-by-design/SKILL.md +68 -0
- package/skills/process/specs-to-issues/SKILL.md +53 -0
- package/skills/process/testing-before-done/SKILL.md +61 -0
- package/skills/process/validating-ux-navigation/SKILL.md +63 -0
- package/skills/references/ai-attachments-ui/SKILL.md +66 -0
- package/skills/references/ai-in-browser-webllm/SKILL.md +74 -0
- package/skills/references/ai-tool-ui-patterns/SKILL.md +63 -0
- package/skills/references/component-patterns-gallery/SKILL.md +62 -0
- package/skills/references/gradient-resources/SKILL.md +66 -0
- package/skills/references/react-component-libraries/SKILL.md +61 -0
- package/skills/references/saas-landing-patterns/SKILL.md +67 -0
- package/skills/references/shadcn-tailwind-theming/SKILL.md +74 -0
- package/skills/references/vercel-ai-sdk-elements/SKILL.md +66 -0
- package/skills/references/web-animation-codrops/SKILL.md +68 -0
- package/skills/stacks/aiml/jupyter/SKILL.md +68 -0
- package/skills/stacks/aiml/keras/SKILL.md +77 -0
- package/skills/stacks/aiml/numpy/SKILL.md +69 -0
- package/skills/stacks/aiml/pandas/SKILL.md +72 -0
- package/skills/stacks/aiml/pytorch/SKILL.md +77 -0
- package/skills/stacks/aiml/scikit-learn/SKILL.md +74 -0
- package/skills/stacks/aiml/tensorflow/SKILL.md +79 -0
- package/skills/stacks/auth/auth0/SKILL.md +63 -0
- package/skills/stacks/auth/authjs/SKILL.md +69 -0
- package/skills/stacks/auth/clerk/SKILL.md +72 -0
- package/skills/stacks/auth/keycloak/SKILL.md +63 -0
- package/skills/stacks/auth/lucia/SKILL.md +56 -0
- package/skills/stacks/auth/passport/SKILL.md +70 -0
- package/skills/stacks/auth/supabase-auth/SKILL.md +66 -0
- package/skills/stacks/baas/amplify/SKILL.md +71 -0
- package/skills/stacks/baas/appwrite/SKILL.md +79 -0
- package/skills/stacks/baas/firebase/SKILL.md +73 -0
- package/skills/stacks/baas/heroku/SKILL.md +71 -0
- package/skills/stacks/backend/actix/SKILL.md +77 -0
- package/skills/stacks/backend/adonisjs/SKILL.md +65 -0
- package/skills/stacks/backend/aspnet-core/SKILL.md +75 -0
- package/skills/stacks/backend/codeigniter/SKILL.md +76 -0
- package/skills/stacks/backend/django/SKILL.md +62 -0
- package/skills/stacks/backend/express/SKILL.md +65 -0
- package/skills/stacks/backend/fastapi/SKILL.md +64 -0
- package/skills/stacks/backend/fastify/SKILL.md +64 -0
- package/skills/stacks/backend/fiber/SKILL.md +68 -0
- package/skills/stacks/backend/flask/SKILL.md +71 -0
- package/skills/stacks/backend/gin/SKILL.md +68 -0
- package/skills/stacks/backend/graphql/SKILL.md +70 -0
- package/skills/stacks/backend/hono/SKILL.md +64 -0
- package/skills/stacks/backend/koa/SKILL.md +63 -0
- package/skills/stacks/backend/laravel/SKILL.md +73 -0
- package/skills/stacks/backend/nestjs/SKILL.md +70 -0
- package/skills/stacks/backend/nginx/SKILL.md +77 -0
- package/skills/stacks/backend/phoenix/SKILL.md +68 -0
- package/skills/stacks/backend/rails/SKILL.md +67 -0
- package/skills/stacks/backend/spring/SKILL.md +70 -0
- package/skills/stacks/backend/spring-boot/SKILL.md +70 -0
- package/skills/stacks/backend/symfony/SKILL.md +77 -0
- package/skills/stacks/container/containerd/SKILL.md +75 -0
- package/skills/stacks/container/docker/SKILL.md +90 -0
- package/skills/stacks/container/podman/SKILL.md +93 -0
- package/skills/stacks/database/cassandra/SKILL.md +74 -0
- package/skills/stacks/database/cockroachdb/SKILL.md +69 -0
- package/skills/stacks/database/dynamodb/SKILL.md +62 -0
- package/skills/stacks/database/mariadb/SKILL.md +71 -0
- package/skills/stacks/database/mongodb/SKILL.md +71 -0
- package/skills/stacks/database/mysql/SKILL.md +72 -0
- package/skills/stacks/database/neon/SKILL.md +68 -0
- package/skills/stacks/database/planetscale/SKILL.md +70 -0
- package/skills/stacks/database/postgresql/SKILL.md +81 -0
- package/skills/stacks/database/redis/SKILL.md +78 -0
- package/skills/stacks/database/sqlite/SKILL.md +70 -0
- package/skills/stacks/database/supabase/SKILL.md +79 -0
- package/skills/stacks/dataviz/chart-js/SKILL.md +72 -0
- package/skills/stacks/dataviz/d3/SKILL.md +77 -0
- package/skills/stacks/dataviz/grafana/SKILL.md +69 -0
- package/skills/stacks/dataviz/plotly/SKILL.md +71 -0
- package/skills/stacks/frontend/alpine/SKILL.md +75 -0
- package/skills/stacks/frontend/angular/SKILL.md +75 -0
- package/skills/stacks/frontend/backbone/SKILL.md +82 -0
- package/skills/stacks/frontend/ember/SKILL.md +85 -0
- package/skills/stacks/frontend/htmx/SKILL.md +73 -0
- package/skills/stacks/frontend/lit/SKILL.md +76 -0
- package/skills/stacks/frontend/preact/SKILL.md +74 -0
- package/skills/stacks/frontend/qwik/SKILL.md +65 -0
- package/skills/stacks/frontend/react/SKILL.md +77 -0
- package/skills/stacks/frontend/solidjs/SKILL.md +75 -0
- package/skills/stacks/frontend/svelte/SKILL.md +70 -0
- package/skills/stacks/frontend/vue/SKILL.md +69 -0
- package/skills/stacks/infra/ansible/SKILL.md +76 -0
- package/skills/stacks/infra/aws/SKILL.md +66 -0
- package/skills/stacks/infra/azure/SKILL.md +72 -0
- package/skills/stacks/infra/circleci/SKILL.md +78 -0
- package/skills/stacks/infra/cloudflare/SKILL.md +65 -0
- package/skills/stacks/infra/fly-io/SKILL.md +63 -0
- package/skills/stacks/infra/gcp/SKILL.md +66 -0
- package/skills/stacks/infra/jenkins/SKILL.md +73 -0
- package/skills/stacks/infra/kubernetes/SKILL.md +64 -0
- package/skills/stacks/infra/netlify/SKILL.md +60 -0
- package/skills/stacks/infra/railway/SKILL.md +63 -0
- package/skills/stacks/infra/tailscale/SKILL.md +65 -0
- package/skills/stacks/infra/terraform/SKILL.md +75 -0
- package/skills/stacks/infra/vagrant/SKILL.md +70 -0
- package/skills/stacks/infra/vercel/SKILL.md +60 -0
- package/skills/stacks/meta/astro/SKILL.md +64 -0
- package/skills/stacks/meta/docusaurus/SKILL.md +71 -0
- package/skills/stacks/meta/eleventy/SKILL.md +69 -0
- package/skills/stacks/meta/gatsby/SKILL.md +63 -0
- package/skills/stacks/meta/hugo/SKILL.md +73 -0
- package/skills/stacks/meta/jekyll/SKILL.md +70 -0
- package/skills/stacks/meta/nextjs/SKILL.md +62 -0
- package/skills/stacks/meta/nuxt/SKILL.md +66 -0
- package/skills/stacks/meta/remix/SKILL.md +67 -0
- package/skills/stacks/meta/sveltekit/SKILL.md +70 -0
- package/skills/stacks/meta/vite/SKILL.md +63 -0
- package/skills/stacks/mobile/android/SKILL.md +77 -0
- package/skills/stacks/mobile/flutter/SKILL.md +77 -0
- package/skills/stacks/mobile/ionic/SKILL.md +72 -0
- package/skills/stacks/mobile/nativescript/SKILL.md +71 -0
- package/skills/stacks/mobile/react-native/SKILL.md +75 -0
- package/skills/stacks/mobile/xamarin/SKILL.md +73 -0
- package/skills/stacks/orm/diesel/SKILL.md +72 -0
- package/skills/stacks/orm/django-orm/SKILL.md +58 -0
- package/skills/stacks/orm/drizzle/SKILL.md +67 -0
- package/skills/stacks/orm/gorm/SKILL.md +73 -0
- package/skills/stacks/orm/knex/SKILL.md +64 -0
- package/skills/stacks/orm/mongoose/SKILL.md +64 -0
- package/skills/stacks/orm/prisma/SKILL.md +64 -0
- package/skills/stacks/orm/sequelize/SKILL.md +65 -0
- package/skills/stacks/orm/sqlalchemy/SKILL.md +71 -0
- package/skills/stacks/orm/typeorm/SKILL.md +70 -0
- package/skills/stacks/queue/bullmq/SKILL.md +69 -0
- package/skills/stacks/queue/celery/SKILL.md +68 -0
- package/skills/stacks/queue/kafka/SKILL.md +66 -0
- package/skills/stacks/queue/nats/SKILL.md +66 -0
- package/skills/stacks/queue/rabbitmq/SKILL.md +64 -0
- package/skills/stacks/queue/redis/SKILL.md +66 -0
- package/skills/stacks/runtime/beam/SKILL.md +72 -0
- package/skills/stacks/runtime/bun/SKILL.md +80 -0
- package/skills/stacks/runtime/deno/SKILL.md +74 -0
- package/skills/stacks/runtime/dotnet/SKILL.md +64 -0
- package/skills/stacks/runtime/jvm/SKILL.md +66 -0
- package/skills/stacks/runtime/node/SKILL.md +70 -0
- package/skills/stacks/runtime/pypy/SKILL.md +69 -0
- package/skills/stacks/runtime/python3/SKILL.md +70 -0
- package/skills/stacks/styling/bootstrap/SKILL.md +74 -0
- package/skills/stacks/styling/bulma/SKILL.md +80 -0
- package/skills/stacks/styling/chakra-ui/SKILL.md +61 -0
- package/skills/stacks/styling/css-modules/SKILL.md +54 -0
- package/skills/stacks/styling/mui/SKILL.md +60 -0
- package/skills/stacks/styling/sass/SKILL.md +63 -0
- package/skills/stacks/styling/shadcn-ui/SKILL.md +58 -0
- package/skills/stacks/styling/styled-components/SKILL.md +62 -0
- package/skills/stacks/styling/tailwind/SKILL.md +59 -0
- package/skills/stacks/styling/unocss/SKILL.md +64 -0
- package/skills/stacks/styling/vanilla-extract/SKILL.md +64 -0
- package/skills/stacks/styling/vuetify/SKILL.md +89 -0
- package/skills/stacks/testing/cypress/SKILL.md +68 -0
- package/skills/stacks/testing/jasmine/SKILL.md +67 -0
- package/skills/stacks/testing/jest/SKILL.md +67 -0
- package/skills/stacks/testing/mocha/SKILL.md +71 -0
- package/skills/stacks/testing/playwright/SKILL.md +68 -0
- package/skills/stacks/testing/puppeteer/SKILL.md +70 -0
- package/skills/stacks/testing/selenium/SKILL.md +70 -0
- package/skills/stacks/testing/vitest/SKILL.md +68 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Constella launcher — installs/boots the local-first runtime, the way `claude` and `codex` do:
|
|
4
|
+
* everything lives under the user-home runtime root (override with CONSTELLA_HOME).
|
|
5
|
+
*
|
|
6
|
+
* npx constella --onboarding # force the first-time setup wizard
|
|
7
|
+
* npx constella --start # local mode (auto-login, 127.0.0.1)
|
|
8
|
+
* npx constella --auth # real email + password (127.0.0.1)
|
|
9
|
+
* npx constella --vps # server over Tailscale, in Docker (0.0.0.0)
|
|
10
|
+
* npx constella --portable [--path <drive>] # run from a USB drive (0.0.0.0)
|
|
11
|
+
* npx constella update [--check] # detect/apply a new version
|
|
12
|
+
*
|
|
13
|
+
* The launch flag picks the run mode (the UI never picks it in a public build). Network modes
|
|
14
|
+
* (auth/vps/portable) generate + persist a real BETTER_AUTH_SECRET so boot never uses a public key.
|
|
15
|
+
*/
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
import { join, dirname } from "node:path";
|
|
18
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync, chmodSync, readdirSync } from "node:fs";
|
|
19
|
+
import { spawnSync, spawn, execFileSync } from "node:child_process";
|
|
20
|
+
import { createInterface } from "node:readline";
|
|
21
|
+
import { randomBytes } from "node:crypto";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
|
|
24
|
+
// Find a dependency's install dir by walking the node_modules chain up from the constella package —
|
|
25
|
+
// so it works whether the dep is nested under constella OR hoisted to the install root (the flat
|
|
26
|
+
// `npm i` layout) OR symlinked (the dev pnpm tree). `require.resolve` is NOT used: a package's
|
|
27
|
+
// `exports` map can hide deep subpaths (drizzle-kit hides ./bin.cjs), which broke a clean `npm i`.
|
|
28
|
+
function pkgDir(pkg) {
|
|
29
|
+
let dir = PKG_ROOT;
|
|
30
|
+
for (;;) {
|
|
31
|
+
const cand = join(dir, "node_modules", pkg);
|
|
32
|
+
if (existsSync(join(cand, "package.json"))) return cand;
|
|
33
|
+
const parent = dirname(dir);
|
|
34
|
+
if (parent === dir) return null; // hit the filesystem root
|
|
35
|
+
dir = parent;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Resolve a dependency's CLI entry to an absolute JS path so we can run it with `node` directly —
|
|
39
|
+
// no `shell: true`, no bare-name PATH lookup (a hijacked `npx`/`next`/`drizzle-kit` can't shadow it).
|
|
40
|
+
// Reads the REAL `bin` field from the package's package.json (robust to `exports` + hoisting).
|
|
41
|
+
function resolveBin(pkg, binName) {
|
|
42
|
+
const d = pkgDir(pkg);
|
|
43
|
+
if (!d) return null;
|
|
44
|
+
try {
|
|
45
|
+
const b = JSON.parse(readFileSync(join(d, "package.json"), "utf8")).bin;
|
|
46
|
+
const rel = typeof b === "string" ? b : (b?.[binName] ?? b?.[pkg]);
|
|
47
|
+
return rel ? join(d, rel) : null;
|
|
48
|
+
} catch { return null; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Free bytes on the volume holding `p` (dependency-free; df / Get-PSDrive). 0 if the probe fails. */
|
|
52
|
+
function freeBytes(p) {
|
|
53
|
+
try {
|
|
54
|
+
if (process.platform === "win32") {
|
|
55
|
+
const d = (p[0] || "C").toUpperCase();
|
|
56
|
+
const drive = /^[A-Z]$/.test(d) ? d : "C";
|
|
57
|
+
const out = execFileSync("powershell", ["-NoProfile", "-Command", `(Get-PSDrive ${drive}).Free`], { timeout: 6000 }).toString().trim();
|
|
58
|
+
return Number(out.replace(/[^\d]/g, "")) || 0;
|
|
59
|
+
}
|
|
60
|
+
const out = execFileSync("df", ["-k", p], { timeout: 5000 }).toString().trim();
|
|
61
|
+
const parts = (out.split("\n").pop() || "").split(/\s+/);
|
|
62
|
+
return (Number(parts[3]) || 0) * 1024;
|
|
63
|
+
} catch { return 0; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Enumerate mounted REMOVABLE (USB) drives → [{ mount, label, freeBytes }]. Dependency-free, per-OS. */
|
|
67
|
+
function detectRemovableDrives() {
|
|
68
|
+
try {
|
|
69
|
+
if (process.platform === "win32") {
|
|
70
|
+
const ps = `Get-CimInstance Win32_Volume -Filter "DriveType=2" | Where-Object { $_.DriveLetter } | ForEach-Object { "$($_.DriveLetter)|$($_.Label)|$($_.FreeSpace)" }`;
|
|
71
|
+
const out = execFileSync("powershell", ["-NoProfile", "-Command", ps], { timeout: 8000 }).toString().trim();
|
|
72
|
+
return out.split(/\r?\n/).filter(Boolean).map((l) => {
|
|
73
|
+
const [letter, label, free] = l.split("|");
|
|
74
|
+
const mount = letter.endsWith(":") ? letter + "\\" : letter + ":\\";
|
|
75
|
+
return { mount, label: label || "USB", freeBytes: Number(free) || 0 };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (process.platform === "darwin") {
|
|
79
|
+
return readdirSync("/Volumes").map((n) => join("/Volumes", n)).filter((v) => {
|
|
80
|
+
try { const info = execFileSync("diskutil", ["info", v], { timeout: 5000 }).toString(); return /(Removable Media:\s*(Removable|Yes))|(Protocol:\s*USB)/i.test(info) && !/Internal:\s*Yes/i.test(info); }
|
|
81
|
+
catch { return false; }
|
|
82
|
+
}).map((v) => ({ mount: v, label: v.split("/").pop() || "USB", freeBytes: freeBytes(v) }));
|
|
83
|
+
}
|
|
84
|
+
// linux: lsblk RM=1 (removable) + a mountpoint
|
|
85
|
+
const out = execFileSync("lsblk", ["-rpno", "NAME,RM,MOUNTPOINT,LABEL"], { timeout: 6000 }).toString().trim();
|
|
86
|
+
return out.split(/\r?\n/).map((l) => l.split(/\s+/)).filter((p) => p[1] === "1" && p[2]).map((p) => ({ mount: p[2], label: p[3] || "USB", freeBytes: freeBytes(p[2]) }));
|
|
87
|
+
} catch { return []; }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Portable + no --path: detect USB drives and let the user pick one → returns the chosen .constella root. */
|
|
91
|
+
async function pickUsbHome() {
|
|
92
|
+
const drives = detectRemovableDrives();
|
|
93
|
+
if (!drives.length) { console.error("✖ Portable mode: no removable USB drive detected. Insert a pen-drive (or pass --path <drive>)."); process.exit(1); }
|
|
94
|
+
let chosen;
|
|
95
|
+
if (drives.length === 1) {
|
|
96
|
+
chosen = drives[0];
|
|
97
|
+
console.log(`• Using the only USB drive: ${chosen.label} (${chosen.mount}, ${(chosen.freeBytes / 1e9).toFixed(1)} GB free)`);
|
|
98
|
+
} else {
|
|
99
|
+
console.log("Detected USB drives:");
|
|
100
|
+
drives.forEach((d, i) => console.log(` [${i + 1}] ${d.label} ${d.mount} · ${(d.freeBytes / 1e9).toFixed(1)} GB free`));
|
|
101
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
102
|
+
const ans = await new Promise((res) => rl.question(`Choose a drive [1-${drives.length}]: `, res));
|
|
103
|
+
rl.close();
|
|
104
|
+
chosen = drives[parseInt(ans, 10) - 1] ?? drives[0];
|
|
105
|
+
}
|
|
106
|
+
return join(chosen.mount, ".constella");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const PKG = "constella";
|
|
110
|
+
const args = process.argv.slice(2);
|
|
111
|
+
const has = (f) => args.includes(f);
|
|
112
|
+
const flag = (f) => { const i = args.indexOf(f); return i >= 0 ? args[i + 1] : undefined; };
|
|
113
|
+
const cmd = args.find((a) => !a.startsWith("-")) ?? "start";
|
|
114
|
+
|
|
115
|
+
// ---- resolve run mode (explicit flag wins → --bind back-compat → start) ----
|
|
116
|
+
const modeFlags = ["start", "auth", "vps", "portable"].filter((m) => has(`--${m}`));
|
|
117
|
+
if (modeFlags.length > 1) console.warn(`• multiple mode flags given; using --${modeFlags[0]}`);
|
|
118
|
+
const bind = flag("--bind");
|
|
119
|
+
const runMode = modeFlags[0]
|
|
120
|
+
?? (bind === "tailnet" ? "vps" : bind === "portable" ? "portable" : bind === "local" ? "start" : undefined)
|
|
121
|
+
?? "start";
|
|
122
|
+
|
|
123
|
+
// ---- `constella update` — detect + apply a new version (runs outside the server) ----
|
|
124
|
+
function localVersion() {
|
|
125
|
+
try { return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version ?? "0.0.0"; }
|
|
126
|
+
catch { return "0.0.0"; }
|
|
127
|
+
}
|
|
128
|
+
async function latestVersion() {
|
|
129
|
+
try {
|
|
130
|
+
const ac = new AbortController(); const t = setTimeout(() => ac.abort(), 4000);
|
|
131
|
+
const r = await fetch(`https://registry.npmjs.org/${PKG}/latest`, { signal: ac.signal });
|
|
132
|
+
clearTimeout(t);
|
|
133
|
+
return r.ok ? ((await r.json()).version ?? null) : null;
|
|
134
|
+
} catch { return null; }
|
|
135
|
+
}
|
|
136
|
+
if (cmd === "update") {
|
|
137
|
+
const current = localVersion();
|
|
138
|
+
const latest = await latestVersion();
|
|
139
|
+
console.log(`Constella ${current}${latest ? ` · latest ${latest}` : " · (npm registry unavailable)"}`);
|
|
140
|
+
if (has("--check")) process.exit(0);
|
|
141
|
+
if (!latest) { console.log("Couldn't reach the npm registry — try again later."); process.exit(0); }
|
|
142
|
+
if (latest === current) { console.log("✓ Already up to date."); process.exit(0); }
|
|
143
|
+
const fromSource = existsSync(join(process.cwd(), ".git")) && existsSync(join(process.cwd(), "src"));
|
|
144
|
+
if (fromSource) { console.log("Running from source — update with: git pull && pnpm install && pnpm build"); process.exit(0); }
|
|
145
|
+
console.log(`Updating ${current} → ${latest} …`);
|
|
146
|
+
// A global install genuinely needs the user's `npm` from PATH, but drop the shell: pass the
|
|
147
|
+
// platform binary name directly (npm.cmd on Windows) so there's no shell interpretation.
|
|
148
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
149
|
+
const r = spawnSync(npmCmd, ["install", "-g", `${PKG}@latest`], { stdio: "inherit" });
|
|
150
|
+
console.log(r.status === 0 ? "✓ Updated. Restart Constella to load the new version." : "✖ Update failed — retry with elevated permissions.");
|
|
151
|
+
process.exit(r.status === 0 ? 0 : 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ---- runtime root: explicit (CONSTELLA_HOME / --path) wins; portable with no path → pick a USB drive ----
|
|
155
|
+
const explicitHome = process.env.CONSTELLA_HOME ?? flag("--path");
|
|
156
|
+
let HOME = explicitHome ?? join(homedir(), ".constella");
|
|
157
|
+
if (runMode === "portable" && !explicitHome) HOME = await pickUsbHome();
|
|
158
|
+
mkdirSync(join(HOME, "organizations"), { recursive: true });
|
|
159
|
+
process.env.CONSTELLA_HOME = HOME;
|
|
160
|
+
|
|
161
|
+
// The installed package's OWN root — where the compiled `.next`, the `drizzle/` migrations and the
|
|
162
|
+
// configs live. When Constella runs as an npm package these are NOT in the user's CWD, so `next` and
|
|
163
|
+
// `drizzle-kit` must run from here (CWD = the launch dir, which has none of them).
|
|
164
|
+
const PKG_ROOT = fileURLToPath(new URL("../", import.meta.url));
|
|
165
|
+
// Absolute JS entries for the tools we boot — run with `node`, never via a shell or a bare PATH name.
|
|
166
|
+
const NEXT_BIN = resolveBin("next", "next");
|
|
167
|
+
const DRIZZLE_BIN = resolveBin("drizzle-kit", "drizzle-kit");
|
|
168
|
+
// Pin the DB to an ABSOLUTE path under the runtime root so `drizzle-kit migrate` (run from PKG_ROOT)
|
|
169
|
+
// and the app (which else defaults it to <launch dir>/.constella) open the SAME database file.
|
|
170
|
+
if (!process.env.DATABASE_URL) process.env.DATABASE_URL = "file:" + join(HOME, "constella.db");
|
|
171
|
+
// The bundled `skills/` library + other package assets live under the PACKAGE root, NOT the launch
|
|
172
|
+
// dir. Export it so the server can find them when installed (the loader falls back to launchDir for
|
|
173
|
+
// the dev tree). Without this, an installed run found zero library skills.
|
|
174
|
+
process.env.CONSTELLA_PKG_ROOT = PKG_ROOT;
|
|
175
|
+
|
|
176
|
+
const host = flag("--host") ?? (runMode === "vps" || runMode === "portable" ? "0.0.0.0" : "127.0.0.1");
|
|
177
|
+
const port = flag("--port") ?? process.env.PORT ?? "3000";
|
|
178
|
+
process.env.CONSTELLA_RUN_MODE = runMode;
|
|
179
|
+
process.env.CONSTELLA_PUBLIC = "1"; // a CLI launch IS the public runtime → the UI mode picker is hidden
|
|
180
|
+
process.env.CONSTELLA_VERSION = localVersion(); // reliable installed version for the in-app Update check
|
|
181
|
+
if (has("--onboarding") || cmd === "onboard" || cmd === "onboarding") process.env.CONSTELLA_FORCE_ONBOARDING = "1";
|
|
182
|
+
|
|
183
|
+
// ---- secrets persisted under the runtime root (network modes need a real signing key) ----
|
|
184
|
+
const ENV_FILE = join(HOME, ".env");
|
|
185
|
+
function readEnvFile() {
|
|
186
|
+
const out = {};
|
|
187
|
+
if (!existsSync(ENV_FILE)) return out;
|
|
188
|
+
for (const line of readFileSync(ENV_FILE, "utf8").split(/\r?\n/)) {
|
|
189
|
+
const m = /^([A-Z0-9_]+)=(.*)$/.exec(line.trim());
|
|
190
|
+
if (m) out[m[1]] = m[2];
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
function ensureSecret(vars, name, gen) {
|
|
195
|
+
if (process.env[name]) { vars[name] = process.env[name]; return false; } // env wins
|
|
196
|
+
if (vars[name] && !/^(changeme|placeholder)?$/i.test(vars[name])) { process.env[name] = vars[name]; return false; } // reuse persisted
|
|
197
|
+
vars[name] = gen(); process.env[name] = vars[name]; return true; // generate once
|
|
198
|
+
}
|
|
199
|
+
// EVERY mode persists a real BETTER_AUTH_SECRET, vault key and worker secret under the runtime root.
|
|
200
|
+
// `next start` runs under NODE_ENV=production, where better-auth THROWS on its default secret — so even
|
|
201
|
+
// local `start` mode needs a real one (the vault needs CONSTELLA_VAULT_KEY to encrypt provider keys,
|
|
202
|
+
// and the worker fails CLOSED without CONSTELLA_WORKER_SECRET). Persisted (not ephemeral) so login
|
|
203
|
+
// sessions + the encrypted vault survive a restart. The server + worker inherit this process.env.
|
|
204
|
+
{
|
|
205
|
+
const vars = readEnvFile();
|
|
206
|
+
let changed = false;
|
|
207
|
+
changed = ensureSecret(vars, "BETTER_AUTH_SECRET", () => randomBytes(32).toString("base64url")) || changed;
|
|
208
|
+
changed = ensureSecret(vars, "CONSTELLA_VAULT_KEY", () => randomBytes(32).toString("base64")) || changed;
|
|
209
|
+
changed = ensureSecret(vars, "CONSTELLA_WORKER_SECRET", () => randomBytes(24).toString("base64url")) || changed;
|
|
210
|
+
if (changed) {
|
|
211
|
+
writeFileSync(ENV_FILE, Object.entries(vars).map(([k, v]) => `${k}=${v}`).join("\n") + "\n", { mode: 0o600 });
|
|
212
|
+
try { chmodSync(ENV_FILE, 0o600); } catch { /* best-effort on Windows */ }
|
|
213
|
+
}
|
|
214
|
+
console.log(`• Secrets ready (stored in ${ENV_FILE}, never printed).`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Portable: validate the drive BEFORE installing/booting (minimum 32 GB free; no upper recommendation).
|
|
218
|
+
if (runMode === "portable") {
|
|
219
|
+
const free = freeBytes(HOME);
|
|
220
|
+
const gb = Math.round((free / 1e9) * 10) / 10;
|
|
221
|
+
if (free && free < 32e9) { console.error(`✖ Portable needs at least 32 GB free — only ${gb} GB on ${HOME}. Use a bigger drive.`); process.exit(1); }
|
|
222
|
+
else if (free) console.log(`• ${gb} GB free on the drive — good (32 GB minimum; more headroom only helps if you carry local models).`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log(`Constella runtime root : ${HOME}`);
|
|
226
|
+
console.log(`Mode : ${runMode} · ${host}:${port}`);
|
|
227
|
+
|
|
228
|
+
// ---- DB schema: apply the shipped migrations to the (fresh) DB. The compiled distribution ships
|
|
229
|
+
// generated SQL migrations in drizzle/, so a brand-new end-user database is built WITHOUT any source
|
|
230
|
+
// schema file. `migrate` is idempotent — already-applied migrations are skipped. ----
|
|
231
|
+
const dbExisted = existsSync(join(HOME, "constella.db"));
|
|
232
|
+
if (!DRIZZLE_BIN) {
|
|
233
|
+
console.error("✖ drizzle-kit not found in the install — cannot apply the database schema. Reinstall the package.");
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const schema = spawnSync(process.execPath, [DRIZZLE_BIN, "migrate", "--config", join(PKG_ROOT, "drizzle.config.mjs")], { stdio: "inherit", cwd: PKG_ROOT });
|
|
237
|
+
if (schema.status !== 0) {
|
|
238
|
+
// A fresh DB that fails to migrate has NO tables → the app would 500 on every request. Fail loud
|
|
239
|
+
// instead of "continuing". An already-built DB tolerates a no-op/failed re-run.
|
|
240
|
+
if (!dbExisted) { console.error("✖ Database schema migration failed on a fresh database — aborting (the app needs its tables)."); process.exit(1); }
|
|
241
|
+
console.warn("• schema migrate skipped/failed on an existing DB — continuing");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---- build-on-first-run (fallback only) ----
|
|
245
|
+
// The published package ships a prebuilt .next under PKG_ROOT, so this is skipped. It only triggers
|
|
246
|
+
// when running from a source tree without a build, where the toolchain is present.
|
|
247
|
+
let built = existsSync(join(PKG_ROOT, ".next", "BUILD_ID"));
|
|
248
|
+
if (!built) {
|
|
249
|
+
console.log("• No build found — building (first run, one-time)…");
|
|
250
|
+
spawnSync(process.execPath, [NEXT_BIN, "build"], { stdio: "inherit", cwd: PKG_ROOT });
|
|
251
|
+
built = existsSync(join(PKG_ROOT, ".next", "BUILD_ID"));
|
|
252
|
+
if (!built) {
|
|
253
|
+
// Every `bin/constella.mjs` launch is a public/production run (CONSTELLA_PUBLIC=1). Never
|
|
254
|
+
// silently downgrade to the unhardened `next dev` server — fail closed unless a developer
|
|
255
|
+
// explicitly opts in with CONSTELLA_DEV=1 (a source tree without a build).
|
|
256
|
+
if (process.env.CONSTELLA_DEV !== "1") {
|
|
257
|
+
console.error("✖ No production build and the build failed. Install a built package or run `pnpm build`. Refusing to start a dev server in a public/network mode.");
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
console.warn("• build failed — falling back to the dev server (CONSTELLA_DEV=1).");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---- boot: the web server + the 24/7 worker, each SUPERVISED so a crash doesn't take the whole
|
|
265
|
+
// system down. A "24/7" runtime must survive a transient web/worker crash (e.g. an agent run exhausts
|
|
266
|
+
// memory and the OS kills next start) — the supervisor auto-restarts the dead child instead of exiting.
|
|
267
|
+
// A rolling-window cap stops a genuine crash-loop from spinning forever. (Mirrors scripts/start-all.mjs
|
|
268
|
+
// for the dual-process shape, plus restart.) ----
|
|
269
|
+
if (!NEXT_BIN) { console.error("✖ next not found in the install — cannot start the web server. Reinstall the package."); process.exit(1); }
|
|
270
|
+
|
|
271
|
+
let shuttingDown = false;
|
|
272
|
+
let webChild = null;
|
|
273
|
+
let workerChild = null;
|
|
274
|
+
function shutdown(code) {
|
|
275
|
+
if (shuttingDown) return;
|
|
276
|
+
shuttingDown = true;
|
|
277
|
+
for (const c of [webChild, workerChild]) { try { c?.kill(); } catch { /* already gone */ } }
|
|
278
|
+
process.exit(code);
|
|
279
|
+
}
|
|
280
|
+
process.on("SIGINT", () => shutdown(0));
|
|
281
|
+
process.on("SIGTERM", () => shutdown(0));
|
|
282
|
+
|
|
283
|
+
// Crash-loop guard: at most MAX_RESTARTS within WINDOW_MS per child, else give up (real, repeated
|
|
284
|
+
// crash → don't mask it forever). Opt-in heap bump for the web via CONSTELLA_WEB_HEAP_MB.
|
|
285
|
+
const MAX_RESTARTS = 5, WINDOW_MS = 60_000;
|
|
286
|
+
const HEAP_MB = Number(process.env.CONSTELLA_WEB_HEAP_MB) || 0; // 0 = Node default (a JS-heap OOM prints FATAL; a silent kill is usually OS-level)
|
|
287
|
+
|
|
288
|
+
/** Spawn a supervised child; on an unexpected exit, auto-restart (bounded) instead of shutting down. */
|
|
289
|
+
function supervise(name, makeChild, onExitInfo) {
|
|
290
|
+
const restarts = [];
|
|
291
|
+
const launch = () => {
|
|
292
|
+
const child = makeChild();
|
|
293
|
+
child.on("error", (e) => console.error(`[${name}] failed to start:`, e.message));
|
|
294
|
+
child.on("exit", (code) => {
|
|
295
|
+
if (shuttingDown) return;
|
|
296
|
+
const now = Date.now();
|
|
297
|
+
while (restarts.length && now - restarts[0] > WINDOW_MS) restarts.shift();
|
|
298
|
+
if (restarts.length >= MAX_RESTARTS) {
|
|
299
|
+
console.error(`✖ [${name}] exited (${code}) and crashed ${restarts.length}x within ${WINDOW_MS / 1000}s — giving up.${onExitInfo ? " " + onExitInfo : ""}`);
|
|
300
|
+
return shutdown(code ?? 1);
|
|
301
|
+
}
|
|
302
|
+
restarts.push(now);
|
|
303
|
+
console.warn(`• [${name}] exited (${code}) — auto-restarting in 2s (${restarts.length}/${MAX_RESTARTS} within ${WINDOW_MS / 1000}s).`);
|
|
304
|
+
setTimeout(() => { if (!shuttingDown) launch(); }, 2000);
|
|
305
|
+
});
|
|
306
|
+
return child;
|
|
307
|
+
};
|
|
308
|
+
return launch();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const start = built ? [NEXT_BIN, "start", "-H", host, "-p", port] : [NEXT_BIN, "dev", "-H", host, "-p", port];
|
|
312
|
+
const nodeArgs = HEAP_MB > 0 ? [`--max-old-space-size=${HEAP_MB}`] : [];
|
|
313
|
+
console.log(`• Starting: next ${start.slice(1).join(" ")} (from ${PKG_ROOT}${HEAP_MB ? `, heap ${HEAP_MB}MB` : ""}) + worker`);
|
|
314
|
+
webChild = supervise("web",
|
|
315
|
+
() => (webChild = spawn(process.execPath, [...nodeArgs, ...start], { stdio: "inherit", cwd: PKG_ROOT })),
|
|
316
|
+
"Likely OS-level OOM (agent runs exhausting RAM) or a native crash — cap concurrent agents, or raise CONSTELLA_WEB_HEAP_MB if it's a JS-heap OOM.");
|
|
317
|
+
|
|
318
|
+
// The worker connects back to the server over localhost (127.0.0.1, even when the server binds
|
|
319
|
+
// 0.0.0.0 for vps/portable) and self-retries until it answers. It survives a web restart on its own
|
|
320
|
+
// (its tick just fails until the web is back), so a web crash restarts ONLY the web — the worker stays.
|
|
321
|
+
const workerPath = fileURLToPath(new URL("./worker.mjs", import.meta.url));
|
|
322
|
+
setTimeout(() => {
|
|
323
|
+
if (shuttingDown) return;
|
|
324
|
+
workerChild = supervise("worker",
|
|
325
|
+
() => (workerChild = spawn(process.execPath, [workerPath], {
|
|
326
|
+
stdio: "inherit",
|
|
327
|
+
env: { ...process.env, CONSTELLA_BASE_URL: process.env.CONSTELLA_BASE_URL ?? `http://127.0.0.1:${port}` },
|
|
328
|
+
})));
|
|
329
|
+
}, 1500);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code PreToolUse hook — destructive-command guard.
|
|
4
|
+
*
|
|
5
|
+
* Injected (when cmdGuard is on — default) into the agent's clean CLAUDE_CONFIG_DIR by
|
|
6
|
+
* src/server/adapters/cli.ts. Before any Bash run it checks the command against a small deny-list of
|
|
7
|
+
* clearly-catastrophic operations and BLOCKS them (exit 2 + a reason the model reads) so an agent can't
|
|
8
|
+
* wipe the machine, force-push to a remote, or brick a disk. It is intentionally NARROW — only
|
|
9
|
+
* unambiguously destructive patterns — and fails OPEN on anything else, so it never hard-stalls a legit
|
|
10
|
+
* run. Self-contained (node built-ins only); makes no network calls.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, appendFileSync, mkdirSync } from "node:fs";
|
|
13
|
+
|
|
14
|
+
let ev = {};
|
|
15
|
+
try { ev = JSON.parse(readFileSync(0, "utf8")); } catch { process.exit(0); }
|
|
16
|
+
if (ev.tool_name !== "Bash") process.exit(0);
|
|
17
|
+
const cmd = String((ev.tool_input && ev.tool_input.command) || "");
|
|
18
|
+
if (!cmd.trim()) process.exit(0);
|
|
19
|
+
|
|
20
|
+
// [pattern, human reason]. Conservative: only catastrophic, low-false-positive shapes.
|
|
21
|
+
const DENY = [
|
|
22
|
+
[/\brm\s+-[a-z]*r[a-z]*f[a-z]*\s+(?:-[a-z]+\s+)*(?:\/|~|\$HOME|\/\*|\.\.?)(?:\s|\/|$)/i, "recursive force-delete of a root / home / cwd path"],
|
|
23
|
+
[/\brm\s+-[a-z]*f[a-z]*r[a-z]*\s+(?:-[a-z]+\s+)*(?:\/|~|\$HOME|\/\*|\.\.?)(?:\s|\/|$)/i, "recursive force-delete of a root / home / cwd path"],
|
|
24
|
+
[/\bgit\s+push\b[^\n]*\s(?:--force\b|-f\b|--force-with-lease\b)/i, "force-push to a git remote"],
|
|
25
|
+
[/\bgit\s+reset\s+--hard\b[^\n]*\borigin\//i, "hard reset onto a remote ref"],
|
|
26
|
+
[/:\s*\(\s*\)\s*\{\s*:\s*\|\s*:\s*&?\s*\}\s*;\s*:/, "fork bomb"],
|
|
27
|
+
[/\bmkfs(?:\.\w+)?\b/i, "filesystem format (mkfs)"],
|
|
28
|
+
[/\bdd\b[^\n]*\bof=\/dev\//i, "raw write to a device (dd of=/dev/…)"],
|
|
29
|
+
[/>\s*\/dev\/(?:sd|nvme|hd|disk|mapper)\w*/i, "redirect over a raw disk device"],
|
|
30
|
+
[/\bchmod\s+-[a-z]*R[a-z]*\s+0?00\b/i, "recursive chmod 000"],
|
|
31
|
+
[/\b(?:shutdown|reboot|halt|poweroff)\b/i, "power / shutdown command"],
|
|
32
|
+
[/\b(?:curl|wget)\b[^\n|]*\|\s*(?:sudo\s+)?(?:sh|bash|zsh)\b/i, "pipe a downloaded script straight into a shell"],
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
for (const [re, why] of DENY) {
|
|
36
|
+
if (re.test(cmd)) {
|
|
37
|
+
// Bridge the denial to the server Inbox: append it to a workspace log the runner reads + clears
|
|
38
|
+
// after the task. RAG indexes only .md, so this .jsonl never pollutes retrieval. Best-effort.
|
|
39
|
+
try { mkdirSync(".claude", { recursive: true }); appendFileSync(".claude/guard-denials.jsonl", JSON.stringify({ at: new Date().toISOString(), why, cmd: cmd.slice(0, 300) }) + "\n"); } catch { /* ignore */ }
|
|
40
|
+
process.stderr.write(`🛑 Blocked by Constella's safety guard (${why}). Do NOT run destructive system commands. Stay inside the project workspace; if you genuinely need this, stop and ask the operator instead of forcing it.\n`);
|
|
41
|
+
process.exit(2); // block; Claude Code feeds stderr back to the model
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
process.exit(0);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code PreToolUse hook — per-file lock for safe PARALLEL agents.
|
|
4
|
+
*
|
|
5
|
+
* Injected (only when CONSTELLA_AGENT_LOCK_HOOK=1) into the agent's clean CLAUDE_CONFIG_DIR by
|
|
6
|
+
* src/server/adapters/cli.ts. Before any Write/Edit/MultiEdit/NotebookEdit it asks the Constella
|
|
7
|
+
* web server (loopback, worker-secret) to lock that file for this task/agent. If another agent holds
|
|
8
|
+
* the file, it BLOCKS the tool (exit 2 + a reason the model reads) so the agent edits something else.
|
|
9
|
+
*
|
|
10
|
+
* Fails OPEN on anything unexpected (no context, network glitch, non-edit tool) — a hook problem must
|
|
11
|
+
* never hard-stall a run.
|
|
12
|
+
*/
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
|
|
15
|
+
let ev = {};
|
|
16
|
+
try { ev = JSON.parse(readFileSync(0, "utf8")); } catch { process.exit(0); }
|
|
17
|
+
|
|
18
|
+
const EDIT_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
19
|
+
if (!EDIT_TOOLS.has(ev.tool_name)) process.exit(0);
|
|
20
|
+
|
|
21
|
+
const input = ev.tool_input || {};
|
|
22
|
+
const file = input.file_path || input.notebook_path || "";
|
|
23
|
+
if (!file) process.exit(0);
|
|
24
|
+
|
|
25
|
+
const base = process.env.CONSTELLA_BASE_URL;
|
|
26
|
+
const secret = process.env.CONSTELLA_WORKER_SECRET;
|
|
27
|
+
const orgId = process.env.CONSTELLA_ORG_ID;
|
|
28
|
+
if (!base || !secret || !orgId) process.exit(0); // not enough context → allow
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const res = await fetch(base.replace(/\/$/, "") + "/api/locks/acquire", {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "content-type": "application/json", "x-worker-secret": secret },
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
orgId, path: file,
|
|
36
|
+
taskId: process.env.CONSTELLA_TASK_ID || "",
|
|
37
|
+
agentId: process.env.CONSTELLA_AGENT_ID || "",
|
|
38
|
+
agentHandle: process.env.CONSTELLA_AGENT_HANDLE || "",
|
|
39
|
+
}),
|
|
40
|
+
signal: AbortSignal.timeout(4000),
|
|
41
|
+
});
|
|
42
|
+
if (res.status === 423) {
|
|
43
|
+
const j = await res.json().catch(() => ({}));
|
|
44
|
+
const who = j && j.heldBy && j.heldBy.handle ? "@" + j.heldBy.handle : "another agent";
|
|
45
|
+
process.stderr.write(`🔒 ${file} is being edited by ${who} right now — do NOT edit it. Work on a DIFFERENT file, or complete the other parts of your task and return to this file later.\n`);
|
|
46
|
+
process.exit(2); // block; Claude Code feeds stderr back to the model
|
|
47
|
+
}
|
|
48
|
+
} catch { /* network/timeout → fail open (allow the edit) */ }
|
|
49
|
+
process.exit(0);
|
package/bin/worker.mjs
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Headless 24/7 runner + workspace file-watcher. Runs independent of any browser, alongside the web
|
|
2
|
+
// server. Self-contained: imports only node built-ins + chokidar and talks to the running server over
|
|
3
|
+
// HTTP — so it ships in the compiled distribution with no `src/` source. Started by:
|
|
4
|
+
// - bin/constella.mjs (the published CLI launch), and
|
|
5
|
+
// - scripts/start-all.mjs (dev `pnpm start`).
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join, relative, sep, isAbsolute, resolve } from "node:path";
|
|
8
|
+
import { setMaxListeners } from "node:events";
|
|
9
|
+
import chokidar from "chokidar";
|
|
10
|
+
|
|
11
|
+
// Raise Node's default 10-listener ceiling: undici registers a per-request "terminated" listener on
|
|
12
|
+
// its shared internal `[Fetch]` controller, so the tick + telegram + sync fetches trip a benign
|
|
13
|
+
// MaxListenersExceededWarning under load. Bounded (not 0) so a real leak still surfaces.
|
|
14
|
+
setMaxListeners(64);
|
|
15
|
+
|
|
16
|
+
const BASE = process.env.CONSTELLA_BASE_URL ?? "http://localhost:3000";
|
|
17
|
+
const SECRET = process.env.CONSTELLA_WORKER_SECRET ?? "";
|
|
18
|
+
const INTERVAL = Number(process.env.CONSTELLA_WORKER_INTERVAL_MS ?? 60_000);
|
|
19
|
+
// Anchor a relative CONSTELLA_HOME to the launch dir (INIT_CWD), exactly like the app's
|
|
20
|
+
// resolveRuntimePath — otherwise the worker watches a different tree than the app reads/writes.
|
|
21
|
+
const _rawHome = process.env.CONSTELLA_HOME;
|
|
22
|
+
const HOME = _rawHome
|
|
23
|
+
? (isAbsolute(_rawHome) ? _rawHome : resolve(process.env.INIT_CWD || process.cwd(), _rawHome))
|
|
24
|
+
: join(homedir(), ".constella");
|
|
25
|
+
const ORGS = join(HOME, "organizations");
|
|
26
|
+
|
|
27
|
+
// SSRF / secret-exfil guard: the worker attaches the privileged x-worker-secret to every call, so it
|
|
28
|
+
// must only ever talk to the local server. Whoever controls the env (systemd unit, Docker env, shell)
|
|
29
|
+
// could otherwise point CONSTELLA_BASE_URL at an attacker host and harvest the secret. The launcher
|
|
30
|
+
// always sets BASE=http://127.0.0.1:<port> (loopback even in vps/portable), so loopback-only breaks
|
|
31
|
+
// nothing legitimate; a genuine remote worker must opt in explicitly.
|
|
32
|
+
const ALLOW_REMOTE = process.env.CONSTELLA_ALLOW_REMOTE_WORKER_BASE_URL === "1";
|
|
33
|
+
let baseHost = "";
|
|
34
|
+
try { baseHost = new URL(BASE).hostname; }
|
|
35
|
+
catch { console.error("✖ Invalid CONSTELLA_BASE_URL:", BASE); process.exit(1); }
|
|
36
|
+
const isLoopback = ["localhost", "127.0.0.1", "::1", "[::1]"].includes(baseHost);
|
|
37
|
+
if (!isLoopback && !ALLOW_REMOTE) {
|
|
38
|
+
console.error(`✖ Refusing to send the worker secret to a non-loopback host (${baseHost}). Set CONSTELLA_ALLOW_REMOTE_WORKER_BASE_URL=1 to override.`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
if (!isLoopback && ALLOW_REMOTE && new URL(BASE).protocol !== "https:") {
|
|
42
|
+
console.warn(`• CONSTELLA_BASE_URL is a remote http:// host (${baseHost}) — the worker secret will travel in cleartext. Use https://.`);
|
|
43
|
+
}
|
|
44
|
+
const headers = (SECRET && (isLoopback || ALLOW_REMOTE)) ? { "x-worker-secret": SECRET } : {};
|
|
45
|
+
|
|
46
|
+
/* ---- 24/7 tick ---- */
|
|
47
|
+
async function tick() {
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(BASE + "/api/cron/tick", { method: "POST", headers });
|
|
50
|
+
const body = await res.json().catch(() => ({}));
|
|
51
|
+
console.log(new Date().toISOString(), "tick", res.status, JSON.stringify(body).slice(0, 200));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.error(new Date().toISOString(), "tick failed:", e?.message ?? e);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* ---- file-watcher: disk -> DB index ---- */
|
|
58
|
+
// Only these prefixes are indexed (skills/agents/docs/PO/reports as Markdown).
|
|
59
|
+
const INDEXED = /^(\.claude\/skills\/.+\.md|\.claude\/agents\/[^/]+\/(Agent|skills)\.md|DOCS\/.+\.md|PO\/.+\.md|Reports\/.+\.md)$/;
|
|
60
|
+
|
|
61
|
+
function parse(abs) {
|
|
62
|
+
const r = relative(ORGS, abs); // <orgId>/workspace/<rel...>
|
|
63
|
+
if (r.startsWith("..")) return null;
|
|
64
|
+
const parts = r.split(sep);
|
|
65
|
+
if (parts.length < 3 || parts[1] !== "workspace") return null;
|
|
66
|
+
const orgId = parts[0];
|
|
67
|
+
const rel = parts.slice(2).join("/");
|
|
68
|
+
if (!INDEXED.test(rel)) return null;
|
|
69
|
+
return { orgId, rel };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const pending = new Map();
|
|
73
|
+
async function syncFile(orgId, rel, event) {
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(BASE + "/api/sync/file", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
78
|
+
body: JSON.stringify({ orgId, rel, event }),
|
|
79
|
+
});
|
|
80
|
+
console.log(new Date().toISOString(), "sync", event, rel, res.status);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.error(new Date().toISOString(), "sync failed:", e?.message ?? e);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function schedule(abs, event) {
|
|
86
|
+
const hit = parse(abs);
|
|
87
|
+
if (!hit) return;
|
|
88
|
+
const key = hit.orgId + "::" + hit.rel;
|
|
89
|
+
clearTimeout(pending.get(key));
|
|
90
|
+
pending.set(key, setTimeout(() => { pending.delete(key); syncFile(hit.orgId, hit.rel, event); }, 400));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function startWatcher() {
|
|
94
|
+
const watcher = chokidar.watch(ORGS, {
|
|
95
|
+
ignoreInitial: true,
|
|
96
|
+
ignored: /(^|[/\\])(\.git|node_modules)([/\\]|$)/,
|
|
97
|
+
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 },
|
|
98
|
+
});
|
|
99
|
+
watcher
|
|
100
|
+
.on("add", (p) => schedule(p, "add"))
|
|
101
|
+
.on("change", (p) => schedule(p, "change"))
|
|
102
|
+
.on("unlink", (p) => schedule(p, "unlink"))
|
|
103
|
+
.on("ready", () => console.log("watching", ORGS))
|
|
104
|
+
.on("error", (e) => console.error("watcher error:", e?.message ?? e));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* ---- Telegram bot poll (long-poll loop; getUpdates waits ~25s server-side) ---- */
|
|
108
|
+
async function telegramLoop() {
|
|
109
|
+
for (;;) {
|
|
110
|
+
try {
|
|
111
|
+
const res = await fetch(BASE + "/api/telegram/poll", { method: "POST", headers });
|
|
112
|
+
if (res.status === 401) { await new Promise((r) => setTimeout(r, 30_000)); continue; } // not configured/secret
|
|
113
|
+
const body = await res.json().catch(() => ({}));
|
|
114
|
+
if (body?.updates) console.log(new Date().toISOString(), "telegram", body.updates, "update(s)");
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.error(new Date().toISOString(), "telegram poll failed:", e?.message ?? e);
|
|
117
|
+
await new Promise((r) => setTimeout(r, 5_000));
|
|
118
|
+
}
|
|
119
|
+
await new Promise((r) => setTimeout(r, 1_000)); // brief gap between long-polls
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ---- wait for the web server before the first tick ---- */
|
|
124
|
+
// The launcher starts us a beat after `next start`, but Next can still be warming up — firing the
|
|
125
|
+
// first tick into a not-yet-listening server printed an alarming "fetch failed". Probe quietly until
|
|
126
|
+
// the server answers (any HTTP status = up), THEN start ticking. Bounded so we never hang forever.
|
|
127
|
+
async function waitForServer() {
|
|
128
|
+
for (let i = 0; i < 90; i++) {
|
|
129
|
+
try { await fetch(BASE, { method: "GET" }); return true; } // any response means it's listening
|
|
130
|
+
catch { await new Promise((r) => setTimeout(r, 1000)); }
|
|
131
|
+
}
|
|
132
|
+
return false; // give up after ~90s — tick()'s own error handling takes over
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`Constella worker → tick ${BASE}/api/cron/tick every ${INTERVAL}ms; telegram poll; watching ${ORGS}`);
|
|
136
|
+
startWatcher();
|
|
137
|
+
(async () => {
|
|
138
|
+
await waitForServer();
|
|
139
|
+
tick();
|
|
140
|
+
setInterval(tick, INTERVAL);
|
|
141
|
+
telegramLoop();
|
|
142
|
+
})();
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 620" width="640" height="620" role="img" aria-label="Constella control plane and its orbiting agent constellation">
|
|
2
|
+
<defs>
|
|
3
|
+
<radialGradient id="sky" cx="50%" cy="42%" r="75%">
|
|
4
|
+
<stop offset="0%" stop-color="#140e33"/>
|
|
5
|
+
<stop offset="100%" stop-color="#050416"/>
|
|
6
|
+
</radialGradient>
|
|
7
|
+
<linearGradient id="hub" x1="0" y1="0" x2="1" y2="1">
|
|
8
|
+
<stop offset="0" stop-color="#a78bfa"/>
|
|
9
|
+
<stop offset="0.5" stop-color="#22d3ee"/>
|
|
10
|
+
<stop offset="1" stop-color="#e879f9"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<filter id="gl" x="-60%" y="-60%" width="220%" height="220%">
|
|
13
|
+
<feGaussianBlur stdDeviation="3.5" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
|
|
14
|
+
</filter>
|
|
15
|
+
</defs>
|
|
16
|
+
<rect width="640" height="620" fill="url(#sky)"/>
|
|
17
|
+
|
|
18
|
+
<!-- orbit rings -->
|
|
19
|
+
<g fill="none" stroke="#3b3168" stroke-width="1.2" opacity="0.8">
|
|
20
|
+
<circle cx="320" cy="320" r="120" stroke-dasharray="4 8"><animateTransform attributeName="transform" type="rotate" from="0 320 320" to="360 320 320" dur="40s" repeatCount="indefinite"/></circle>
|
|
21
|
+
<circle cx="320" cy="320" r="245" stroke-dasharray="3 10"><animateTransform attributeName="transform" type="rotate" from="360 320 320" to="0 320 320" dur="70s" repeatCount="indefinite"/></circle>
|
|
22
|
+
</g>
|
|
23
|
+
|
|
24
|
+
<!-- spokes -->
|
|
25
|
+
<g stroke="#22d3ee" stroke-width="0.7" opacity="0.18">
|
|
26
|
+
<line x1="320" y1="320" x2="320" y2="200"/><line x1="320" y1="320" x2="216" y2="380"/><line x1="320" y1="320" x2="424" y2="380"/>
|
|
27
|
+
<line x1="320" y1="320" x2="320" y2="75"/><line x1="320" y1="320" x2="129" y2="167"/><line x1="320" y1="320" x2="81" y2="375"/>
|
|
28
|
+
<line x1="320" y1="320" x2="214" y2="541"/><line x1="320" y1="320" x2="426" y2="541"/><line x1="320" y1="320" x2="559" y2="375"/><line x1="320" y1="320" x2="511" y2="167"/>
|
|
29
|
+
</g>
|
|
30
|
+
|
|
31
|
+
<!-- central control plane -->
|
|
32
|
+
<g filter="url(#gl)">
|
|
33
|
+
<polygon points="320,288 352,320 320,352 288,320" fill="url(#hub)"/>
|
|
34
|
+
</g>
|
|
35
|
+
<text x="320" y="378" text-anchor="middle" font-family="'Segoe UI',Arial,sans-serif" font-size="14" font-weight="700" fill="#e2e8f0">CONSTELLA</text>
|
|
36
|
+
<text x="320" y="394" text-anchor="middle" font-family="'Segoe UI',Arial,sans-serif" font-size="9" fill="#64748b" letter-spacing="2">CONTROL PLANE</text>
|
|
37
|
+
|
|
38
|
+
<!-- agent nodes -->
|
|
39
|
+
<g font-family="'Segoe UI',Arial,sans-serif" font-size="11" fill="#cbd5e1" text-anchor="middle">
|
|
40
|
+
<g><circle cx="320" cy="200" r="6.5" fill="#e879f9" filter="url(#gl)"/><text x="320" y="184">Ada · CEO</text></g>
|
|
41
|
+
<g><circle cx="216" cy="380" r="6" fill="#e879f9"/><text x="196" y="398">Linus · CTO</text></g>
|
|
42
|
+
<g><circle cx="424" cy="380" r="6" fill="#22d3ee"/><text x="448" y="398">Donald · PO</text></g>
|
|
43
|
+
<g><circle cx="320" cy="75" r="6" fill="#22d3ee"/><text x="320" y="60">Margaret · Backend</text></g>
|
|
44
|
+
<g><circle cx="129" cy="167" r="6" fill="#22d3ee"/><text x="129" y="152">Grace · Frontend</text></g>
|
|
45
|
+
<g><circle cx="81" cy="375" r="6" fill="#22d3ee"/><text x="78" y="359">Edsger · QA</text></g>
|
|
46
|
+
<g><circle cx="214" cy="541" r="6" fill="#22d3ee"/><text x="214" y="562">Werner · DevOps</text></g>
|
|
47
|
+
<g><circle cx="426" cy="541" r="6" fill="#a78bfa"/><text x="426" y="562">Barbara · Docs</text></g>
|
|
48
|
+
<g><circle cx="559" cy="375" r="6.5" fill="#e879f9" filter="url(#gl)"/><text x="559" y="359">Whitfield · CyberSec</text></g>
|
|
49
|
+
<g><circle cx="511" cy="167" r="6" fill="#a78bfa"/><text x="511" y="152">Vannevar · Knowledge</text></g>
|
|
50
|
+
</g>
|
|
51
|
+
|
|
52
|
+
<!-- travelling photon on the inner orbit -->
|
|
53
|
+
<circle r="3.5" fill="#ffffff" filter="url(#gl)">
|
|
54
|
+
<animateMotion dur="9s" repeatCount="indefinite" path="M 320 200 A 120 120 0 1 1 320 440 A 120 120 0 1 1 320 200"/>
|
|
55
|
+
</circle>
|
|
56
|
+
</svg>
|