@jlongo78/agent-spaces 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +8 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/routes-manifest.json +48 -0
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.html +1 -1
- package/.next/standalone/.next/server/app/admin/users.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.html +1 -1
- package/.next/standalone/.next/server/app/analytics.rsc +17 -16
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin/analytics/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/analytics/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin/users/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/users/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js +3 -3
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/me/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/me/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/setup/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/setup/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/verify/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/totp/verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js +2 -2
- package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/export/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/settings/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/timeline/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/usage/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-callback/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-callback/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-request/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-request/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-request/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/connect-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/discovered/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/discovered/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/handshake/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/health/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/health/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/identity/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/identity/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/keys/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/keys/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/keys/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/keys/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/check/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/check/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/nodes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/proxy/[nodeId]/[...path]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/proxy/[nodeId]/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/terminal/token/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/terminal/token/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/route.js +2 -2
- package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js +2 -2
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/search/route.js +2 -2
- package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tags/route.js +2 -2
- package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/updates/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/updates/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/updates/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/updates/route.js +7 -0
- package/.next/standalone/.next/server/app/api/updates/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/updates/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/updates/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js +2 -2
- package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/route.js +2 -2
- package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/cortex.html +1 -1
- package/.next/standalone/.next/server/app/cortex.rsc +17 -16
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login.html +1 -1
- package/.next/standalone/.next/server/app/login.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects.html +1 -1
- package/.next/standalone/.next/server/app/m/projects.rsc +4 -4
- package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions.html +1 -1
- package/.next/standalone/.next/server/app/m/sessions.rsc +4 -4
- package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/settings.html +1 -1
- package/.next/standalone/.next/server/app/m/settings.rsc +4 -4
- package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/terminal.html +1 -1
- package/.next/standalone/.next/server/app/m/terminal.rsc +4 -4
- package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.html +1 -1
- package/.next/standalone/.next/server/app/m.rsc +4 -4
- package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.html +1 -1
- package/.next/standalone/.next/server/app/network.rsc +17 -16
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.html +1 -1
- package/.next/standalone/.next/server/app/projects.rsc +17 -16
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.html +1 -1
- package/.next/standalone/.next/server/app/sessions.rsc +17 -16
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +17 -16
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.html +1 -1
- package/.next/standalone/.next/server/app/terminal.rsc +17 -16
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.html +1 -1
- package/.next/standalone/.next/server/app/workspaces.rsc +17 -16
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app-paths-manifest.json +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0041efe4._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d5f802e._.js → [root-of-the-server]__00bf0ace._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0fac388d._.js → [root-of-the-server]__08a68343._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0955695e._.js → [root-of-the-server]__0add852f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a7a798c._.js → [root-of-the-server]__0c113ed0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__776b2fd5._.js → [root-of-the-server]__0e1a27e0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__805ab6d4._.js → [root-of-the-server]__0e71d908._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__fcfb3d9b._.js → [root-of-the-server]__0e9142f3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0b622fbb._.js → [root-of-the-server]__10e47926._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6b94cc47._.js → [root-of-the-server]__1194f2c1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__65015776._.js → [root-of-the-server]__1665dc78._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a1c90b00._.js → [root-of-the-server]__175cbabf._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6c6cfe82._.js → [root-of-the-server]__19c2d094._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__efefd88d._.js → [root-of-the-server]__1adae357._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d359752._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d2bbb75a._.js → [root-of-the-server]__1e8fabeb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__c13f151f._.js → [root-of-the-server]__1f8deca0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__3331633d._.js → [root-of-the-server]__253fdda1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d0439252._.js → [root-of-the-server]__28e6434f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__3ba575ca._.js → [root-of-the-server]__2a386564._.js} +13 -13
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__7901e2ab._.js → [root-of-the-server]__2acbd703._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__466c7e18._.js → [root-of-the-server]__2acefabb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__8cddfa2d._.js → [root-of-the-server]__2c20fb38._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4837b8c0._.js → [root-of-the-server]__309132cd._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6d211342._.js → [root-of-the-server]__3786d8ae._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__f3e94a15._.js → [root-of-the-server]__3ae92407._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__7799fe3c._.js → [root-of-the-server]__3beda9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__25d1fc87._.js → [root-of-the-server]__3e3f25a1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0c59850c._.js → [root-of-the-server]__4619e9bd._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__3beca461._.js → [root-of-the-server]__4a051043._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bc97ddc7._.js → [root-of-the-server]__50208a5f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__7b4dd49b._.js → [root-of-the-server]__508002e4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__57b959fd._.js → [root-of-the-server]__5086c373._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ce1f6530._.js → [root-of-the-server]__5913e097._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a4823fef._.js → [root-of-the-server]__5b5f68d2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__b9761ef0._.js → [root-of-the-server]__5c1f2459._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__aeb7413c._.js → [root-of-the-server]__5ec8c977._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__df6bc9e9._.js → [root-of-the-server]__5f8c694a._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__63cebc6c._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__e85d0854._.js → [root-of-the-server]__64d30d4d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ce0bdfd8._.js → [root-of-the-server]__6c54fc2e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bc0ee8ae._.js → [root-of-the-server]__6dc1fb7e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91eb478b._.js → [root-of-the-server]__6e568102._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__c476045d._.js → [root-of-the-server]__6faa04c0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__3aa57139._.js → [root-of-the-server]__727d05f1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__74a34dc3._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__8ca5cdb4._.js → [root-of-the-server]__75d12b32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__05885ab1._.js → [root-of-the-server]__7e7250a4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__9952246f._.js → [root-of-the-server]__8309e0a4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__5e1e3386._.js → [root-of-the-server]__86cc0e2b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1a211143._.js → [root-of-the-server]__8915603e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__db7825cc._.js → [root-of-the-server]__89c2565a._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__b000379d._.js → [root-of-the-server]__8d178ad9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__3ae85e7a._.js → [root-of-the-server]__93ee06f3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6a2926b6._.js → [root-of-the-server]__9e4c154a._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__31f028ec._.js → [root-of-the-server]__a1fbc199._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__aa7644c6._.js → [root-of-the-server]__a9d2e1d3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__b60a5497._.js → [root-of-the-server]__ae53d343._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__f5f82d4b._.js → [root-of-the-server]__b3a04cef._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b4270b77._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__dd9f454c._.js → [root-of-the-server]__b6b6ce60._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b9545dd9._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__41ae8be6._.js → [root-of-the-server]__c200e21a._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__190b05d8._.js → [root-of-the-server]__c3c74ca4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c88b63f7._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6c674ff1._.js → [root-of-the-server]__cbf4ceb0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__8d28317b._.js → [root-of-the-server]__cefdba2f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0dd51116._.js → [root-of-the-server]__cf3c60c2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__dae7832b._.js → [root-of-the-server]__cf9e82bb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__de96c005._.js → [root-of-the-server]__d15515e3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2367f5a6._.js → [root-of-the-server]__d2897392._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__7b4e92a1._.js → [root-of-the-server]__d3b2d856._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__81ece563._.js → [root-of-the-server]__d73273ca._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a5b5007a._.js → [root-of-the-server]__d8417eb6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__73409f3d._.js → [root-of-the-server]__db4726bc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__17e04588._.js → [root-of-the-server]__dc2a55de._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__67c4abf3._.js → [root-of-the-server]__dc6e2e5f._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d225634._.js → [root-of-the-server]__e0d4690b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e678dd53._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a3b0b968._.js → [root-of-the-server]__e9223f55._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91522ce2._.js → [root-of-the-server]__ea630076._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0644670b._.js → [root-of-the-server]__eb8acb65._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bbdbb0e1._.js → [root-of-the-server]__eee4c5e8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a21471b._.js → [root-of-the-server]__f26ca49d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6c15c973._.js → [root-of-the-server]__f33e1101._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__5bbc73ff._.js → [root-of-the-server]__f3a4c668._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2bfbbec2._.js → [root-of-the-server]__f515f865._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fceb5d60._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d9c653fc._.js → [root-of-the-server]__fed41403._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__c09d4b86._.js → [root-of-the-server]__ff2e98c2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_assess_route_actions_3e8ef2a6.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_publish_route_actions_49238be3.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_refine_route_actions_05675688.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_review_route_actions_5cbb5f6b.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_seed_route_actions_21084bdb.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_marketplace_browse_route_actions_7ed0768c.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_marketplace_preview_route_actions_38d4cc17.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_updates_route_actions_91efaab5.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__23f374a2._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__efef0a06._.js → [root-of-the-server]__ca5beb09._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_b4db3983._.js → _01842037._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_7dd2ac82._.js → _036cbaa2._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_10c0d382._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_163d0838._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_9303a965._.js → _43455582._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_7e63066a._.js → _67887e33._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_950142a4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_02bae6e5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +2 -2
- package/.next/standalone/.next/server/edge/chunks/_d73df637._.js +1 -1
- package/.next/standalone/.next/server/middleware-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/static/chunks/{a7b2795949a6c63e.js → 224aab5107987011.js} +2 -2
- package/.next/standalone/.next/static/chunks/415827af44b8c374.js +1 -0
- package/.next/standalone/.next/static/chunks/8a7f58872c123a53.css +3 -0
- package/.next/standalone/.next/static/chunks/b0484608571d975a.js +1 -0
- package/.next/standalone/.next/static/chunks/{e35e863c09511a51.js → b42a38df3e418ff0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0e61e67b7b8fc3f3.js → c418112e102673ce.js} +1 -1
- package/.next/standalone/.next/static/chunks/d6474f65a17c483e.js +1 -0
- package/.next/standalone/.next/static/chunks/e2f0a2330dedff4b.js +85 -0
- package/.next/standalone/.next/static/chunks/f0c8b3f8cc1939ec.js +1 -0
- package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -0
- package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/src/app/(desktop)/cortex/page.tsx +7 -4
- package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -0
- package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -0
- package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +3 -3
- package/.next/standalone/src/app/api/cortex/import/route.ts +51 -19
- package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -0
- package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -0
- package/.next/standalone/src/app/api/cortex/status/route.ts +40 -0
- package/.next/standalone/src/components/cortex/constants.ts +29 -0
- package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +80 -4
- package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -0
- package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -0
- package/.next/standalone/src/components/cortex/knowledge-card.tsx +1 -19
- package/.next/standalone/src/components/cortex/knowledge-tab.tsx +103 -1
- package/.next/standalone/src/components/cortex/lobe-settings.tsx +16 -0
- package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -0
- package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -0
- package/.next/standalone/src/lib/cortex/types.ts +1 -0
- package/bin/spaces-service.js +21 -9
- package/bin/spaces.js +85 -12
- package/bin/terminal-server.js +13 -2
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d85c4a8b._.js +0 -98
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ecce08b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_cd50a174._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_e7a9e2b0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_133a7c8a._.js +0 -3
- package/.next/standalone/.next/static/chunks/441b03060c26dfd8.css +0 -3
- package/.next/standalone/.next/static/chunks/58d78765e5140dcb.js +0 -1
- package/.next/standalone/.next/static/chunks/78dd908e70bf8c4b.js +0 -85
- package/.next/standalone/.next/static/chunks/9196173f0d081f2c.js +0 -1
- package/.next/standalone/.next/static/chunks/9bd8a119aaeafc9e.js +0 -1
- package/.next/standalone/src/components/cortex/context-tab.tsx +0 -171
- /package/.next/standalone/.next/static/{L3G_bc6BtAETwbN4_gS1c → Ku7_sP1T1UuqtRhUTiJZw}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{L3G_bc6BtAETwbN4_gS1c → Ku7_sP1T1UuqtRhUTiJZw}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{L3G_bc6BtAETwbN4_gS1c → Ku7_sP1T1UuqtRhUTiJZw}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,1846 @@
|
|
|
1
|
+
# Cortex UI Integration — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Integrate @spaces/cortex curation, marketplace, quality dashboard, and domain context features into the Spaces UI with 6-tab Cortex page layout.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Extend existing Cortex page (5 tabs → 6 tabs), add API routes that proxy to addon MCP tool handlers, build new UI components following established patterns (client components, `api()` helper, Tailwind dark theme, lucide-react icons).
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Next.js 15, React 19, TypeScript, Tailwind CSS, lucide-react
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### Task 1: Extract Shared Constants
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Create: `src/components/cortex/constants.ts`
|
|
19
|
+
- Modify: `src/components/cortex/knowledge-card.tsx:6-16`
|
|
20
|
+
- Modify: `src/components/cortex/context-tab.tsx:18-28`
|
|
21
|
+
|
|
22
|
+
- [ ] **Step 1: Create the shared constants file**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// src/components/cortex/constants.ts
|
|
26
|
+
export const TYPE_COLORS: Record<string, string> = {
|
|
27
|
+
decision: 'bg-blue-500/20 text-blue-400',
|
|
28
|
+
preference: 'bg-pink-500/20 text-pink-400',
|
|
29
|
+
pattern: 'bg-green-500/20 text-green-400',
|
|
30
|
+
error_fix: 'bg-amber-500/20 text-amber-400',
|
|
31
|
+
context: 'bg-gray-500/20 text-gray-400',
|
|
32
|
+
code_pattern: 'bg-cyan-500/20 text-cyan-400',
|
|
33
|
+
command: 'bg-orange-500/20 text-orange-400',
|
|
34
|
+
conversation: 'bg-slate-500/20 text-slate-400',
|
|
35
|
+
summary: 'bg-violet-500/20 text-violet-400',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const SENSITIVITY_COLORS: Record<string, string> = {
|
|
39
|
+
public: 'bg-green-500/20 text-green-400',
|
|
40
|
+
internal: 'bg-indigo-500/20 text-indigo-400',
|
|
41
|
+
restricted: 'bg-amber-500/20 text-amber-400',
|
|
42
|
+
confidential: 'bg-red-500/20 text-red-400',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const INTENT_COLORS: Record<string, string> = {
|
|
46
|
+
debugging: 'text-red-400',
|
|
47
|
+
architecture: 'text-blue-400',
|
|
48
|
+
onboarding: 'text-green-400',
|
|
49
|
+
policy: 'text-purple-400',
|
|
50
|
+
'how-to': 'text-amber-400',
|
|
51
|
+
review: 'text-pink-400',
|
|
52
|
+
security: 'text-red-500',
|
|
53
|
+
general: 'text-gray-400',
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- [ ] **Step 2: Update knowledge-card.tsx to import from constants**
|
|
58
|
+
|
|
59
|
+
Replace the local `TYPE_COLORS` and `SENSITIVITY_COLORS` definitions (lines 6-23) with:
|
|
60
|
+
```typescript
|
|
61
|
+
import { TYPE_COLORS, SENSITIVITY_COLORS } from './constants';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- [ ] **Step 3: Update context-tab.tsx to import from constants**
|
|
65
|
+
|
|
66
|
+
Replace the local `INTENT_COLORS` and `TYPE_COLORS` definitions (lines 7-28) with:
|
|
67
|
+
```typescript
|
|
68
|
+
import { INTENT_COLORS, TYPE_COLORS } from './constants';
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- [ ] **Step 4: Verify build**
|
|
72
|
+
|
|
73
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
74
|
+
Expected: Build succeeds with no errors
|
|
75
|
+
|
|
76
|
+
- [ ] **Step 5: Commit**
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git add src/components/cortex/constants.ts src/components/cortex/knowledge-card.tsx src/components/cortex/context-tab.tsx
|
|
80
|
+
git commit -m "refactor(cortex): extract shared color constants"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### Task 2: Add domain_context to LobeConfig Type
|
|
86
|
+
|
|
87
|
+
**Files:**
|
|
88
|
+
- Modify: `src/lib/cortex/types.ts:23-38`
|
|
89
|
+
|
|
90
|
+
- [ ] **Step 1: Add domain_context field**
|
|
91
|
+
|
|
92
|
+
In `src/lib/cortex/types.ts`, add `domain_context?: string;` to the `LobeConfig` interface after `isPrivate`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
export interface LobeConfig {
|
|
96
|
+
tags: string[];
|
|
97
|
+
excludeTags: string[];
|
|
98
|
+
excludedFrom: number[];
|
|
99
|
+
subscriptions: string[];
|
|
100
|
+
private: boolean;
|
|
101
|
+
isPrivate?: boolean;
|
|
102
|
+
domain_context?: string;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
No change to `DEFAULT_LOBE_CONFIG` — the field is optional and defaults to undefined.
|
|
107
|
+
|
|
108
|
+
- [ ] **Step 2: Verify build**
|
|
109
|
+
|
|
110
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
111
|
+
Expected: Build succeeds
|
|
112
|
+
|
|
113
|
+
- [ ] **Step 3: Commit**
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git add src/lib/cortex/types.ts
|
|
117
|
+
git commit -m "feat(cortex): add domain_context to LobeConfig type"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### Task 3: Add Domain Context to Lobe Settings UI
|
|
123
|
+
|
|
124
|
+
**Files:**
|
|
125
|
+
- Modify: `src/components/cortex/lobe-settings.tsx`
|
|
126
|
+
|
|
127
|
+
- [ ] **Step 1: Add domain context state and textarea**
|
|
128
|
+
|
|
129
|
+
After the tags section (after the closing `</div>` of the tags `space-y-1.5` div, around line 155), add:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
{/* Domain context */}
|
|
133
|
+
<div className="space-y-1.5">
|
|
134
|
+
<div className="text-[10px] text-zinc-500">
|
|
135
|
+
Domain Context
|
|
136
|
+
<span className="text-zinc-700 ml-1">— injected into distillation prompts for better extraction</span>
|
|
137
|
+
</div>
|
|
138
|
+
<textarea
|
|
139
|
+
value={config.domain_context || ''}
|
|
140
|
+
onChange={e => save({ domain_context: e.target.value })}
|
|
141
|
+
placeholder="Describe the domain expertise this lobe should focus on. E.g. 'This workspace covers CFB power plant engineering.'"
|
|
142
|
+
rows={3}
|
|
143
|
+
disabled={saving}
|
|
144
|
+
className="w-full px-2 py-1.5 bg-zinc-800 border border-zinc-700 rounded text-[11px] text-zinc-300 placeholder-zinc-600 focus:outline-none focus:border-purple-500/50 resize-y disabled:opacity-50"
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- [ ] **Step 2: Verify build**
|
|
150
|
+
|
|
151
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
152
|
+
Expected: Build succeeds
|
|
153
|
+
|
|
154
|
+
- [ ] **Step 3: Commit**
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
git add src/components/cortex/lobe-settings.tsx
|
|
158
|
+
git commit -m "feat(cortex): add domain context textarea to lobe settings"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### Task 4: Extend Status API with Quality Data
|
|
164
|
+
|
|
165
|
+
**Files:**
|
|
166
|
+
- Modify: `src/app/api/cortex/status/route.ts`
|
|
167
|
+
|
|
168
|
+
- [ ] **Step 1: Add quality assessment to status response**
|
|
169
|
+
|
|
170
|
+
After the `graphStats` calculation (around line 114) and before the `return NextResponse.json(...)`, add:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Quality assessment (aggregate across all lobes)
|
|
174
|
+
let quality = null;
|
|
175
|
+
try {
|
|
176
|
+
const addon = (await import('@/lib/cortex')).getCortexAddon?.();
|
|
177
|
+
if (addon?.assessLobe) {
|
|
178
|
+
// Get first workspace lobe for representative quality
|
|
179
|
+
const wsKeys = Object.keys(lobes).filter(k => k.startsWith('workspace/'));
|
|
180
|
+
if (wsKeys.length > 0) {
|
|
181
|
+
const wsId = parseInt(wsKeys[0].split('/')[1], 10);
|
|
182
|
+
quality = await addon.assessLobe(cortex.store, wsId);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} catch { /* addon doesn't support assess yet */ }
|
|
186
|
+
|
|
187
|
+
// Fallback: build quality from available data if addon doesn't have assessLobe
|
|
188
|
+
if (!quality) {
|
|
189
|
+
// Compute type distribution and sensitivity counts from browse data
|
|
190
|
+
const allUnits: any[] = [];
|
|
191
|
+
for (const layerKey of Object.keys(lobes)) {
|
|
192
|
+
try {
|
|
193
|
+
const items = await cortex.store.browse(layerKey, 100);
|
|
194
|
+
allUnits.push(...items);
|
|
195
|
+
} catch { /* skip */ }
|
|
196
|
+
}
|
|
197
|
+
if (allUnits.length > 0) {
|
|
198
|
+
const typeDist: Record<string, number> = {};
|
|
199
|
+
const sensCounts: Record<string, number> = {};
|
|
200
|
+
let confSum = 0;
|
|
201
|
+
let staleCount = 0;
|
|
202
|
+
for (const u of allUnits) {
|
|
203
|
+
typeDist[u.type] = (typeDist[u.type] || 0) + 1;
|
|
204
|
+
const sens = u.sensitivity || 'internal';
|
|
205
|
+
sensCounts[sens] = (sensCounts[sens] || 0) + 1;
|
|
206
|
+
confSum += u.confidence ?? 0;
|
|
207
|
+
if ((u.stale_score ?? 0) > 0.5) staleCount++;
|
|
208
|
+
}
|
|
209
|
+
const distilled = (typeDist.decision || 0) + (typeDist.pattern || 0) + (typeDist.preference || 0) + (typeDist.error_fix || 0);
|
|
210
|
+
quality = {
|
|
211
|
+
coverage_score: allUnits.length > 0 ? distilled / allUnits.length : 0,
|
|
212
|
+
type_distribution: typeDist,
|
|
213
|
+
avg_confidence: allUnits.length > 0 ? confSum / allUnits.length : 0,
|
|
214
|
+
stale_count: staleCount,
|
|
215
|
+
sensitivity_counts: sensCounts,
|
|
216
|
+
top_accessed: allUnits
|
|
217
|
+
.filter(u => (u.access_count ?? 0) > 0)
|
|
218
|
+
.sort((a, b) => (b.access_count ?? 0) - (a.access_count ?? 0))
|
|
219
|
+
.slice(0, 3)
|
|
220
|
+
.map(u => ({ text: u.text?.slice(0, 120), type: u.type, access_count: u.access_count })),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Then update the return statement to include `quality`:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
return NextResponse.json({
|
|
230
|
+
enabled: true,
|
|
231
|
+
status: 'healthy',
|
|
232
|
+
embedding_provider: cortex.embedding.name,
|
|
233
|
+
embedding_dimensions: cortex.embedding.dimensions,
|
|
234
|
+
distillation: config.ingestion?.distillation ?? false,
|
|
235
|
+
lobes,
|
|
236
|
+
totalCount,
|
|
237
|
+
totalSizeBytes,
|
|
238
|
+
usage,
|
|
239
|
+
graph: graphStats,
|
|
240
|
+
quality,
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
- [ ] **Step 2: Verify build**
|
|
245
|
+
|
|
246
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
247
|
+
Expected: Build succeeds
|
|
248
|
+
|
|
249
|
+
- [ ] **Step 3: Commit**
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git add src/app/api/cortex/status/route.ts
|
|
253
|
+
git commit -m "feat(cortex): add quality assessment data to status API"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### Task 5: Enhance Dashboard with Quality Panels
|
|
259
|
+
|
|
260
|
+
**Files:**
|
|
261
|
+
- Modify: `src/components/cortex/cortex-dashboard.tsx`
|
|
262
|
+
|
|
263
|
+
- [ ] **Step 1: Import shared constants**
|
|
264
|
+
|
|
265
|
+
Add at the top of the file:
|
|
266
|
+
```typescript
|
|
267
|
+
import { TYPE_COLORS, SENSITIVITY_COLORS } from './constants';
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
- [ ] **Step 2: Update StatusData interface**
|
|
271
|
+
|
|
272
|
+
Add `quality` to the interface (after line 33):
|
|
273
|
+
```typescript
|
|
274
|
+
quality: {
|
|
275
|
+
coverage_score: number;
|
|
276
|
+
type_distribution: Record<string, number>;
|
|
277
|
+
avg_confidence: number;
|
|
278
|
+
stale_count: number;
|
|
279
|
+
sensitivity_counts: Record<string, number>;
|
|
280
|
+
top_accessed: Array<{ text: string; type: string; access_count: number }>;
|
|
281
|
+
} | null;
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
- [ ] **Step 3: Replace Graph stat card with Coverage Score**
|
|
285
|
+
|
|
286
|
+
Replace the Graph `StatCard` (around line 117-121) with:
|
|
287
|
+
```tsx
|
|
288
|
+
<StatCard
|
|
289
|
+
label="Coverage"
|
|
290
|
+
value={data.quality ? data.quality.coverage_score.toFixed(2) : '—'}
|
|
291
|
+
sub={data.quality ? `${Math.round(data.quality.coverage_score * 100)}% distilled` : 'no data'}
|
|
292
|
+
color={data.quality
|
|
293
|
+
? data.quality.coverage_score > 0.7 ? 'text-green-400'
|
|
294
|
+
: data.quality.coverage_score > 0.3 ? 'text-amber-400'
|
|
295
|
+
: 'text-red-400'
|
|
296
|
+
: 'text-gray-500'}
|
|
297
|
+
/>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- [ ] **Step 4: Add Type Distribution and Lobe Health panels**
|
|
301
|
+
|
|
302
|
+
After the existing lobe breakdown section (before the distillation detail section), add:
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
{/* Quality panels */}
|
|
306
|
+
{data.quality && (
|
|
307
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
308
|
+
{/* Type distribution */}
|
|
309
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4">
|
|
310
|
+
<h3 className="text-xs font-medium text-gray-400 mb-3">Type Distribution</h3>
|
|
311
|
+
<BarChart
|
|
312
|
+
items={Object.entries(data.quality.type_distribution)
|
|
313
|
+
.sort((a, b) => b[1] - a[1])
|
|
314
|
+
.map(([type, count]) => ({
|
|
315
|
+
label: type.replace('_', ' '),
|
|
316
|
+
value: count,
|
|
317
|
+
color: TYPE_HEX[type] || '#7c3aed',
|
|
318
|
+
}))
|
|
319
|
+
}
|
|
320
|
+
maxValue={Math.max(...Object.values(data.quality.type_distribution), 1)}
|
|
321
|
+
/>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
{/* Lobe health */}
|
|
325
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4">
|
|
326
|
+
<h3 className="text-xs font-medium text-gray-400 mb-3">Lobe Health</h3>
|
|
327
|
+
<div className="space-y-3">
|
|
328
|
+
<div className="flex justify-between text-[11px]">
|
|
329
|
+
<span className="text-gray-500">Stale units</span>
|
|
330
|
+
<span className={data.quality.stale_count > 10 ? 'text-amber-400' : 'text-gray-300'}>
|
|
331
|
+
{data.quality.stale_count}
|
|
332
|
+
</span>
|
|
333
|
+
</div>
|
|
334
|
+
<div className="flex justify-between text-[11px]">
|
|
335
|
+
<span className="text-gray-500">Avg confidence</span>
|
|
336
|
+
<span className="text-gray-300">{data.quality.avg_confidence.toFixed(2)}</span>
|
|
337
|
+
</div>
|
|
338
|
+
{data.quality.top_accessed.length > 0 && (
|
|
339
|
+
<div>
|
|
340
|
+
<div className="text-[10px] text-gray-500 mb-1">Top accessed</div>
|
|
341
|
+
<div className="text-[11px] text-gray-400 truncate">
|
|
342
|
+
“{data.quality.top_accessed[0].text}”
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
346
|
+
{data.quality.sensitivity_counts && (
|
|
347
|
+
<div>
|
|
348
|
+
<div className="text-[10px] text-gray-500 mb-1">Sensitivity</div>
|
|
349
|
+
<div className="flex flex-wrap gap-1">
|
|
350
|
+
{Object.entries(data.quality.sensitivity_counts).map(([level, count]) => (
|
|
351
|
+
<span key={level} className={`text-[9px] px-1.5 py-0.5 rounded ${SENSITIVITY_COLORS[level] || SENSITIVITY_COLORS.internal}`}>
|
|
352
|
+
{count} {level}
|
|
353
|
+
</span>
|
|
354
|
+
))}
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
)}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Add `TYPE_HEX` as a const inside the component (after the `LOBE_COLORS` array) since BarChart uses inline styles, not Tailwind classes:
|
|
365
|
+
```typescript
|
|
366
|
+
const TYPE_HEX: Record<string, string> = {
|
|
367
|
+
decision: '#3b82f6', pattern: '#22c55e', preference: '#ec4899',
|
|
368
|
+
error_fix: '#f59e0b', context: '#6b7280', code_pattern: '#06b6d4',
|
|
369
|
+
command: '#f97316', conversation: '#64748b', summary: '#8b5cf6',
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
- [ ] **Step 5: Verify build**
|
|
374
|
+
|
|
375
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
376
|
+
Expected: Build succeeds
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 6: Commit**
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
git add src/components/cortex/cortex-dashboard.tsx
|
|
382
|
+
git commit -m "feat(cortex): add quality panels to dashboard"
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### Task 6: Merge Context Tab into Knowledge Tab
|
|
388
|
+
|
|
389
|
+
**Files:**
|
|
390
|
+
- Modify: `src/components/cortex/knowledge-tab.tsx`
|
|
391
|
+
- Delete: `src/components/cortex/context-tab.tsx`
|
|
392
|
+
|
|
393
|
+
- [ ] **Step 1: Add query analyzer section to knowledge-tab.tsx**
|
|
394
|
+
|
|
395
|
+
Add state and imports at the top:
|
|
396
|
+
```typescript
|
|
397
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
398
|
+
import { Search, ChevronDown, ChevronUp } from 'lucide-react';
|
|
399
|
+
import { api } from '@/lib/api';
|
|
400
|
+
import { KnowledgeCard } from './knowledge-card';
|
|
401
|
+
import { TYPE_COLORS, INTENT_COLORS } from './constants';
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Add state for the analyzer:
|
|
405
|
+
```typescript
|
|
406
|
+
const [analyzerOpen, setAnalyzerOpen] = useState(false);
|
|
407
|
+
const [analyzerQuery, setAnalyzerQuery] = useState('');
|
|
408
|
+
const [analyzerResult, setAnalyzerResult] = useState<any>(null);
|
|
409
|
+
const [analyzerLoading, setAnalyzerLoading] = useState(false);
|
|
410
|
+
|
|
411
|
+
const handleAnalyze = async () => {
|
|
412
|
+
if (!analyzerQuery.trim()) return;
|
|
413
|
+
setAnalyzerLoading(true);
|
|
414
|
+
try {
|
|
415
|
+
const res = await fetch(api(`/api/cortex/context?q=${encodeURIComponent(analyzerQuery)}&limit=5`));
|
|
416
|
+
if (res.ok) setAnalyzerResult(await res.json());
|
|
417
|
+
} catch {}
|
|
418
|
+
setAnalyzerLoading(false);
|
|
419
|
+
};
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
After the knowledge cards `</div>` (end of the scrollable area), add a collapsible Query Analyzer section. Use the full content from `context-tab.tsx` lines 46-170, wrapped in a collapsible container:
|
|
423
|
+
|
|
424
|
+
```tsx
|
|
425
|
+
{/* Query Analyzer */}
|
|
426
|
+
<div className="border-t border-white/5 mt-4 pt-4">
|
|
427
|
+
<button
|
|
428
|
+
onClick={() => setAnalyzerOpen(!analyzerOpen)}
|
|
429
|
+
className="flex items-center gap-2 text-xs text-gray-500 hover:text-gray-300 mb-3"
|
|
430
|
+
>
|
|
431
|
+
{analyzerOpen ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
|
|
432
|
+
Query Analyzer
|
|
433
|
+
</button>
|
|
434
|
+
{analyzerOpen && (
|
|
435
|
+
<div className="max-w-2xl">
|
|
436
|
+
{/* Paste the context-tab query input and results display here */}
|
|
437
|
+
{/* Use analyzerQuery/analyzerResult/analyzerLoading state */}
|
|
438
|
+
{/* ... full context-tab content adapted to use analyzer* state variables ... */}
|
|
439
|
+
</div>
|
|
440
|
+
)}
|
|
441
|
+
</div>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Copy the full JSX body from `context-tab.tsx` (the input, results grid, source weights, results list, conflicts, raw context sections) into the `{analyzerOpen && (...)}` block, replacing `query` → `analyzerQuery`, `result` → `analyzerResult`, `loading` → `analyzerLoading`, `handleAnalyze` stays the same name.
|
|
445
|
+
|
|
446
|
+
**Do NOT delete `context-tab.tsx` yet** — `page.tsx` still imports it. The file is removed in Task 11 when the page is updated to the 6-tab layout.
|
|
447
|
+
|
|
448
|
+
- [ ] **Step 2: Verify build**
|
|
449
|
+
|
|
450
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
451
|
+
Expected: Build succeeds (context-tab.tsx still exists, page.tsx still imports it)
|
|
452
|
+
|
|
453
|
+
- [ ] **Step 3: Commit**
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
git add src/components/cortex/knowledge-tab.tsx
|
|
457
|
+
git commit -m "feat(cortex): add query analyzer section to knowledge tab"
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
### Task 7: Create Curation API Routes
|
|
463
|
+
|
|
464
|
+
**Files:**
|
|
465
|
+
- Create: `src/app/api/cortex/curation/seed/route.ts`
|
|
466
|
+
- Create: `src/app/api/cortex/curation/assess/route.ts`
|
|
467
|
+
- Create: `src/app/api/cortex/curation/review/route.ts`
|
|
468
|
+
- Create: `src/app/api/cortex/curation/refine/route.ts`
|
|
469
|
+
- Create: `src/app/api/cortex/curation/publish/route.ts`
|
|
470
|
+
|
|
471
|
+
All routes follow the same pattern: auth check → get cortex → proxy to addon's `handleToolCall`.
|
|
472
|
+
|
|
473
|
+
All 5 curation routes use `handleToolCall` from `@/lib/cortex/mcp/server` — the same pattern as the existing `/api/cortex/mcp/call` route. This function internally delegates to the addon.
|
|
474
|
+
|
|
475
|
+
- [ ] **Step 1: Create seed route**
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
// src/app/api/cortex/curation/seed/route.ts
|
|
479
|
+
import { NextResponse } from 'next/server';
|
|
480
|
+
import type { NextRequest } from 'next/server';
|
|
481
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
482
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
483
|
+
import { handleToolCall } from '@/lib/cortex/mcp/server';
|
|
484
|
+
|
|
485
|
+
export async function POST(request: NextRequest) {
|
|
486
|
+
const user = getAuthUser(request);
|
|
487
|
+
return withUser(user, async () => {
|
|
488
|
+
if (!isCortexAvailable()) {
|
|
489
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
490
|
+
}
|
|
491
|
+
const cortex = await getCortex();
|
|
492
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
493
|
+
|
|
494
|
+
const body = await request.json();
|
|
495
|
+
const result = await handleToolCall('cortex_seed', body, cortex);
|
|
496
|
+
if (result.isError) {
|
|
497
|
+
return NextResponse.json({ error: JSON.parse(result.content[0].text) }, { status: 400 });
|
|
498
|
+
}
|
|
499
|
+
return NextResponse.json(JSON.parse(result.content[0].text));
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
- [ ] **Step 2: Create assess route**
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// src/app/api/cortex/curation/assess/route.ts
|
|
508
|
+
import { NextResponse } from 'next/server';
|
|
509
|
+
import type { NextRequest } from 'next/server';
|
|
510
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
511
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
512
|
+
import { handleToolCall } from '@/lib/cortex/mcp/server';
|
|
513
|
+
|
|
514
|
+
export async function GET(request: NextRequest) {
|
|
515
|
+
const user = getAuthUser(request);
|
|
516
|
+
return withUser(user, async () => {
|
|
517
|
+
if (!isCortexAvailable()) {
|
|
518
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
519
|
+
}
|
|
520
|
+
const cortex = await getCortex();
|
|
521
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
522
|
+
|
|
523
|
+
const url = new URL(request.url);
|
|
524
|
+
const workspace_id = parseInt(url.searchParams.get('workspace_id') || '0', 10);
|
|
525
|
+
if (!workspace_id) {
|
|
526
|
+
return NextResponse.json({ error: 'workspace_id is required' }, { status: 400 });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const result = await handleToolCall('cortex_assess', { workspace_id }, cortex);
|
|
530
|
+
if (result.isError) {
|
|
531
|
+
return NextResponse.json({ error: JSON.parse(result.content[0].text) }, { status: 400 });
|
|
532
|
+
}
|
|
533
|
+
return NextResponse.json(JSON.parse(result.content[0].text));
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
- [ ] **Step 3: Create review route**
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
// src/app/api/cortex/curation/review/route.ts
|
|
542
|
+
import { NextResponse } from 'next/server';
|
|
543
|
+
import type { NextRequest } from 'next/server';
|
|
544
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
545
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
546
|
+
import { handleToolCall } from '@/lib/cortex/mcp/server';
|
|
547
|
+
|
|
548
|
+
export async function GET(request: NextRequest) {
|
|
549
|
+
const user = getAuthUser(request);
|
|
550
|
+
return withUser(user, async () => {
|
|
551
|
+
if (!isCortexAvailable()) {
|
|
552
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
553
|
+
}
|
|
554
|
+
const cortex = await getCortex();
|
|
555
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
556
|
+
|
|
557
|
+
const url = new URL(request.url);
|
|
558
|
+
const workspace_id = parseInt(url.searchParams.get('workspace_id') || '0', 10);
|
|
559
|
+
const topic = url.searchParams.get('topic') || '';
|
|
560
|
+
const limit = parseInt(url.searchParams.get('limit') || '10', 10);
|
|
561
|
+
|
|
562
|
+
if (!workspace_id || !topic) {
|
|
563
|
+
return NextResponse.json({ error: 'workspace_id and topic are required' }, { status: 400 });
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const result = await handleToolCall('cortex_review', { workspace_id, topic, limit }, cortex);
|
|
567
|
+
if (result.isError) {
|
|
568
|
+
return NextResponse.json({ error: JSON.parse(result.content[0].text) }, { status: 400 });
|
|
569
|
+
}
|
|
570
|
+
return NextResponse.json(JSON.parse(result.content[0].text));
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
- [ ] **Step 4: Create refine route**
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
// src/app/api/cortex/curation/refine/route.ts
|
|
579
|
+
import { NextResponse } from 'next/server';
|
|
580
|
+
import type { NextRequest } from 'next/server';
|
|
581
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
582
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
583
|
+
import { handleToolCall } from '@/lib/cortex/mcp/server';
|
|
584
|
+
|
|
585
|
+
export async function POST(request: NextRequest) {
|
|
586
|
+
const user = getAuthUser(request);
|
|
587
|
+
return withUser(user, async () => {
|
|
588
|
+
if (!isCortexAvailable()) {
|
|
589
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
590
|
+
}
|
|
591
|
+
const cortex = await getCortex();
|
|
592
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
593
|
+
|
|
594
|
+
const body = await request.json();
|
|
595
|
+
const result = await handleToolCall('cortex_refine', body, cortex);
|
|
596
|
+
if (result.isError) {
|
|
597
|
+
return NextResponse.json({ error: JSON.parse(result.content[0].text) }, { status: 400 });
|
|
598
|
+
}
|
|
599
|
+
return NextResponse.json(JSON.parse(result.content[0].text));
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
- [ ] **Step 5: Create publish route**
|
|
605
|
+
|
|
606
|
+
```typescript
|
|
607
|
+
// src/app/api/cortex/curation/publish/route.ts
|
|
608
|
+
import { NextResponse } from 'next/server';
|
|
609
|
+
import type { NextRequest } from 'next/server';
|
|
610
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
611
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
612
|
+
import { handleToolCall } from '@/lib/cortex/mcp/server';
|
|
613
|
+
|
|
614
|
+
export async function POST(request: NextRequest) {
|
|
615
|
+
const user = getAuthUser(request);
|
|
616
|
+
return withUser(user, async () => {
|
|
617
|
+
if (!isCortexAvailable()) {
|
|
618
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
619
|
+
}
|
|
620
|
+
const cortex = await getCortex();
|
|
621
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
622
|
+
|
|
623
|
+
const body = await request.json();
|
|
624
|
+
const result = await handleToolCall('cortex_publish', body, cortex);
|
|
625
|
+
if (result.isError) {
|
|
626
|
+
return NextResponse.json({ error: JSON.parse(result.content[0].text) }, { status: 400 });
|
|
627
|
+
}
|
|
628
|
+
return NextResponse.json(JSON.parse(result.content[0].text));
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- [ ] **Step 6: Verify build**
|
|
634
|
+
|
|
635
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
636
|
+
Expected: Build succeeds
|
|
637
|
+
|
|
638
|
+
- [ ] **Step 7: Commit**
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
git add src/app/api/cortex/curation/
|
|
642
|
+
git commit -m "feat(cortex): add curation API routes (seed/assess/review/refine/publish)"
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
### Task 8: Create Marketplace API Routes
|
|
648
|
+
|
|
649
|
+
**Files:**
|
|
650
|
+
- Create: `src/app/api/cortex/marketplace/browse/route.ts`
|
|
651
|
+
- Create: `src/app/api/cortex/marketplace/preview/route.ts`
|
|
652
|
+
- Modify: `src/app/api/cortex/import/route.ts`
|
|
653
|
+
|
|
654
|
+
- [ ] **Step 1: Create browse route**
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
// src/app/api/cortex/marketplace/browse/route.ts
|
|
658
|
+
import { NextResponse } from 'next/server';
|
|
659
|
+
import type { NextRequest } from 'next/server';
|
|
660
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
661
|
+
import { getUserPaths } from '@/lib/config';
|
|
662
|
+
import fs from 'fs';
|
|
663
|
+
import path from 'path';
|
|
664
|
+
import { execSync } from 'child_process';
|
|
665
|
+
|
|
666
|
+
export async function GET(request: NextRequest) {
|
|
667
|
+
const user = getAuthUser(request);
|
|
668
|
+
return withUser(user, async () => {
|
|
669
|
+
const { spacesDir } = getUserPaths(user);
|
|
670
|
+
const marketDir = path.join(spacesDir, 'cortex', 'marketplace');
|
|
671
|
+
|
|
672
|
+
// Create directory if it doesn't exist
|
|
673
|
+
if (!fs.existsSync(marketDir)) {
|
|
674
|
+
fs.mkdirSync(marketDir, { recursive: true });
|
|
675
|
+
return NextResponse.json({ packs: [], directory: marketDir });
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const files = fs.readdirSync(marketDir).filter(f => f.endsWith('.cortexpack'));
|
|
679
|
+
const packs: any[] = [];
|
|
680
|
+
|
|
681
|
+
for (const filename of files) {
|
|
682
|
+
try {
|
|
683
|
+
const filePath = path.join(marketDir, filename);
|
|
684
|
+
// Extract manifest.json from the tar.gz without full unpack
|
|
685
|
+
const stdout = execSync(
|
|
686
|
+
`tar -xzf "${filePath}" -O manifest.json 2>/dev/null`,
|
|
687
|
+
{ encoding: 'utf-8', timeout: 5000 }
|
|
688
|
+
);
|
|
689
|
+
const manifest = JSON.parse(stdout);
|
|
690
|
+
packs.push({ filename, manifest });
|
|
691
|
+
} catch {
|
|
692
|
+
// Can't read manifest — list it with minimal info
|
|
693
|
+
const stat = fs.statSync(path.join(marketDir, filename));
|
|
694
|
+
packs.push({
|
|
695
|
+
filename,
|
|
696
|
+
manifest: {
|
|
697
|
+
version: 'unknown',
|
|
698
|
+
exportDate: stat.mtime.toISOString(),
|
|
699
|
+
unitCount: 0,
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return NextResponse.json({ packs, directory: marketDir });
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
- [ ] **Step 2: Create preview route**
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
// src/app/api/cortex/marketplace/preview/route.ts
|
|
714
|
+
import { NextResponse } from 'next/server';
|
|
715
|
+
import type { NextRequest } from 'next/server';
|
|
716
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
717
|
+
import { getUserPaths } from '@/lib/config';
|
|
718
|
+
import fs from 'fs';
|
|
719
|
+
import path from 'path';
|
|
720
|
+
import { execSync } from 'child_process';
|
|
721
|
+
|
|
722
|
+
export async function GET(request: NextRequest) {
|
|
723
|
+
const user = getAuthUser(request);
|
|
724
|
+
return withUser(user, async () => {
|
|
725
|
+
const url = new URL(request.url);
|
|
726
|
+
const filename = url.searchParams.get('file');
|
|
727
|
+
if (!filename) {
|
|
728
|
+
return NextResponse.json({ error: 'file parameter required' }, { status: 400 });
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Sanitize filename — prevent path traversal
|
|
732
|
+
const safe = path.basename(filename);
|
|
733
|
+
if (!safe.endsWith('.cortexpack')) {
|
|
734
|
+
return NextResponse.json({ error: 'Invalid file' }, { status: 400 });
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const { spacesDir } = getUserPaths(user);
|
|
738
|
+
const filePath = path.join(spacesDir, 'cortex', 'marketplace', safe);
|
|
739
|
+
if (!fs.existsSync(filePath)) {
|
|
740
|
+
return NextResponse.json({ error: 'Pack not found' }, { status: 404 });
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
try {
|
|
744
|
+
// Extract first 5 lines of knowledge.jsonl for preview
|
|
745
|
+
const stdout = execSync(
|
|
746
|
+
`tar -xzf "${filePath}" -O knowledge.jsonl 2>/dev/null | head -5`,
|
|
747
|
+
{ encoding: 'utf-8', timeout: 10000 }
|
|
748
|
+
);
|
|
749
|
+
const samples = stdout.trim().split('\n')
|
|
750
|
+
.filter(Boolean)
|
|
751
|
+
.map(line => {
|
|
752
|
+
try { const u = JSON.parse(line); return { text: u.text?.slice(0, 200), type: u.type }; }
|
|
753
|
+
catch { return null; }
|
|
754
|
+
})
|
|
755
|
+
.filter(Boolean);
|
|
756
|
+
return NextResponse.json({ samples });
|
|
757
|
+
} catch {
|
|
758
|
+
return NextResponse.json({ samples: [] });
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
- [ ] **Step 3: Update import route to support both FormData uploads and JSON marketplace imports**
|
|
765
|
+
|
|
766
|
+
Replace the entire `src/app/api/cortex/import/route.ts`:
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
import { NextResponse } from 'next/server';
|
|
770
|
+
import type { NextRequest } from 'next/server';
|
|
771
|
+
import path from 'path';
|
|
772
|
+
import fs from 'fs';
|
|
773
|
+
import os from 'os';
|
|
774
|
+
import { getAuthUser, withUser } from '@/lib/auth';
|
|
775
|
+
import { isCortexAvailable, getCortex } from '@/lib/cortex';
|
|
776
|
+
import { importCortexpack } from '@/lib/cortex/portability/importer';
|
|
777
|
+
import { getUserPaths } from '@/lib/config';
|
|
778
|
+
|
|
779
|
+
export async function POST(request: NextRequest) {
|
|
780
|
+
const user = getAuthUser(request);
|
|
781
|
+
return withUser(user, async () => {
|
|
782
|
+
if (!isCortexAvailable()) {
|
|
783
|
+
return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
|
|
784
|
+
}
|
|
785
|
+
const cortex = await getCortex();
|
|
786
|
+
if (!cortex) return NextResponse.json({ error: 'Cortex disabled' }, { status: 503 });
|
|
787
|
+
|
|
788
|
+
const contentType = request.headers.get('content-type') || '';
|
|
789
|
+
let packPath: string;
|
|
790
|
+
let cleanup = false;
|
|
791
|
+
|
|
792
|
+
if (contentType.includes('application/json')) {
|
|
793
|
+
// JSON mode: import from marketplace directory by filename
|
|
794
|
+
const body = await request.json();
|
|
795
|
+
const filename = body.marketplace_file;
|
|
796
|
+
if (!filename) {
|
|
797
|
+
return NextResponse.json({ error: 'marketplace_file is required' }, { status: 400 });
|
|
798
|
+
}
|
|
799
|
+
const safe = path.basename(filename);
|
|
800
|
+
const { spacesDir } = getUserPaths(user);
|
|
801
|
+
packPath = path.join(spacesDir, 'cortex', 'marketplace', safe);
|
|
802
|
+
if (!fs.existsSync(packPath)) {
|
|
803
|
+
return NextResponse.json({ error: 'Pack not found' }, { status: 404 });
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const targetLayer = body.target_layer || 'workspace';
|
|
807
|
+
const workspaceId = body.workspace_id;
|
|
808
|
+
const effectiveLayer = targetLayer === 'workspace' && workspaceId
|
|
809
|
+
? `workspace/${workspaceId}` : targetLayer;
|
|
810
|
+
|
|
811
|
+
importCortexpack(packPath, cortex.store, cortex.embedding, {
|
|
812
|
+
targetLayer: effectiveLayer,
|
|
813
|
+
mergeStrategy: (body.merge_strategy || 'merge') as any,
|
|
814
|
+
reEmbed: body.re_embed ?? false,
|
|
815
|
+
});
|
|
816
|
+
return NextResponse.json({ status: 'started' });
|
|
817
|
+
|
|
818
|
+
} else {
|
|
819
|
+
// FormData mode: file upload (existing behavior)
|
|
820
|
+
const formData = await request.formData();
|
|
821
|
+
const file = formData.get('file') as File;
|
|
822
|
+
if (!file) {
|
|
823
|
+
return NextResponse.json({ error: 'No file uploaded' }, { status: 400 });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const tmpPath = path.join(os.tmpdir(), `cortex-import-${Date.now()}.cortexpack`);
|
|
827
|
+
const bytes = await file.arrayBuffer();
|
|
828
|
+
fs.writeFileSync(tmpPath, Buffer.from(bytes));
|
|
829
|
+
|
|
830
|
+
const targetLayer = (formData.get('target_layer') as string) || 'workspace';
|
|
831
|
+
const mergeStrategy = (formData.get('merge_strategy') as string) || 'merge';
|
|
832
|
+
const reEmbed = formData.get('re_embed') === 'true';
|
|
833
|
+
const workspaceId = formData.get('workspace_id') as string | null;
|
|
834
|
+
const effectiveLayer = targetLayer === 'workspace' && workspaceId
|
|
835
|
+
? `workspace/${workspaceId}` : targetLayer;
|
|
836
|
+
|
|
837
|
+
importCortexpack(tmpPath, cortex.store, cortex.embedding, {
|
|
838
|
+
targetLayer: effectiveLayer,
|
|
839
|
+
mergeStrategy: mergeStrategy as any,
|
|
840
|
+
reEmbed,
|
|
841
|
+
}).finally(() => {
|
|
842
|
+
try { fs.unlinkSync(tmpPath); } catch { /* ignore */ }
|
|
843
|
+
});
|
|
844
|
+
return NextResponse.json({ status: 'started' });
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
- [ ] **Step 4: Verify build**
|
|
852
|
+
|
|
853
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
854
|
+
Expected: Build succeeds
|
|
855
|
+
|
|
856
|
+
- [ ] **Step 5: Commit**
|
|
857
|
+
|
|
858
|
+
```bash
|
|
859
|
+
git add src/app/api/cortex/marketplace/ src/app/api/cortex/import/route.ts
|
|
860
|
+
git commit -m "feat(cortex): add marketplace browse/preview APIs, update import route"
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
### Task 9: Build Curation Tab Component
|
|
866
|
+
|
|
867
|
+
**Files:**
|
|
868
|
+
- Create: `src/components/cortex/curation-tab.tsx`
|
|
869
|
+
|
|
870
|
+
- [ ] **Step 1: Create the curation tab with pipeline bar and all 5 steps**
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
// src/components/cortex/curation-tab.tsx
|
|
874
|
+
'use client';
|
|
875
|
+
|
|
876
|
+
import { useState, useEffect } from 'react';
|
|
877
|
+
import { Upload, BarChart3, Search, RefreshCw, Package, Loader2 } from 'lucide-react';
|
|
878
|
+
import { api } from '@/lib/api';
|
|
879
|
+
import { TYPE_COLORS } from './constants';
|
|
880
|
+
|
|
881
|
+
type Step = 'seed' | 'assess' | 'review' | 'refine' | 'publish';
|
|
882
|
+
|
|
883
|
+
const STEPS: { key: Step; label: string; icon: any }[] = [
|
|
884
|
+
{ key: 'seed', label: 'Seed', icon: Upload },
|
|
885
|
+
{ key: 'assess', label: 'Assess', icon: BarChart3 },
|
|
886
|
+
{ key: 'review', label: 'Review', icon: Search },
|
|
887
|
+
{ key: 'refine', label: 'Refine', icon: RefreshCw },
|
|
888
|
+
{ key: 'publish', label: 'Publish', icon: Package },
|
|
889
|
+
];
|
|
890
|
+
|
|
891
|
+
interface Workspace { id: number; name: string; color: string }
|
|
892
|
+
|
|
893
|
+
export function CurationTab() {
|
|
894
|
+
const [step, setStep] = useState<Step>('seed');
|
|
895
|
+
const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
|
|
896
|
+
const [workspaceId, setWorkspaceId] = useState<number>(0);
|
|
897
|
+
const [loading, setLoading] = useState(false);
|
|
898
|
+
const [error, setError] = useState<string | null>(null);
|
|
899
|
+
const [result, setResult] = useState<any>(null);
|
|
900
|
+
|
|
901
|
+
// Seed state
|
|
902
|
+
const [seedText, setSeedText] = useState('');
|
|
903
|
+
const [seedFormat, setSeedFormat] = useState('auto');
|
|
904
|
+
const [seedSource, setSeedSource] = useState('');
|
|
905
|
+
const [seedDistill, setSeedDistill] = useState(true);
|
|
906
|
+
|
|
907
|
+
// Review state
|
|
908
|
+
const [reviewTopic, setReviewTopic] = useState('');
|
|
909
|
+
const [reviewLimit, setReviewLimit] = useState(10);
|
|
910
|
+
|
|
911
|
+
// Refine state
|
|
912
|
+
const [domainContext, setDomainContext] = useState('');
|
|
913
|
+
const [refineTypes, setRefineTypes] = useState<string[]>(['decisions', 'patterns', 'preferences', 'error_fixes']);
|
|
914
|
+
|
|
915
|
+
// Publish state
|
|
916
|
+
const [pubName, setPubName] = useState('');
|
|
917
|
+
const [pubAuthor, setPubAuthor] = useState('');
|
|
918
|
+
const [pubDesc, setPubDesc] = useState('');
|
|
919
|
+
const [pubTags, setPubTags] = useState('');
|
|
920
|
+
const [pubVersion, setPubVersion] = useState('1.0.0');
|
|
921
|
+
const [pubLicense, setPubLicense] = useState('cc-by');
|
|
922
|
+
const [pubPreviewCount, setPubPreviewCount] = useState(3);
|
|
923
|
+
|
|
924
|
+
useEffect(() => {
|
|
925
|
+
fetch(api('/api/workspaces'))
|
|
926
|
+
.then(r => r.json())
|
|
927
|
+
.then(data => {
|
|
928
|
+
const list = Array.isArray(data) ? data : data.local || [];
|
|
929
|
+
setWorkspaces(list);
|
|
930
|
+
if (list.length > 0 && !workspaceId) setWorkspaceId(list[0].id);
|
|
931
|
+
})
|
|
932
|
+
.catch(() => {});
|
|
933
|
+
}, []);
|
|
934
|
+
|
|
935
|
+
const clearResult = () => { setResult(null); setError(null); };
|
|
936
|
+
|
|
937
|
+
const doSeed = async () => {
|
|
938
|
+
if (!seedText.trim() || !workspaceId) return;
|
|
939
|
+
setLoading(true); clearResult();
|
|
940
|
+
try {
|
|
941
|
+
const res = await fetch(api('/api/cortex/curation/seed'), {
|
|
942
|
+
method: 'POST',
|
|
943
|
+
headers: { 'Content-Type': 'application/json' },
|
|
944
|
+
body: JSON.stringify({
|
|
945
|
+
text: seedText, format: seedFormat, source_ref: seedSource,
|
|
946
|
+
workspace_id: workspaceId, distill: seedDistill,
|
|
947
|
+
}),
|
|
948
|
+
});
|
|
949
|
+
const data = await res.json();
|
|
950
|
+
if (!res.ok) throw new Error(data.error?.message || data.error || 'Seed failed');
|
|
951
|
+
setResult(data);
|
|
952
|
+
} catch (e: any) { setError(e.message); }
|
|
953
|
+
setLoading(false);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
const doAssess = async () => {
|
|
957
|
+
if (!workspaceId) return;
|
|
958
|
+
setLoading(true); clearResult();
|
|
959
|
+
try {
|
|
960
|
+
const res = await fetch(api(`/api/cortex/curation/assess?workspace_id=${workspaceId}`));
|
|
961
|
+
const data = await res.json();
|
|
962
|
+
if (!res.ok) throw new Error(data.error?.message || data.error || 'Assess failed');
|
|
963
|
+
setResult(data);
|
|
964
|
+
} catch (e: any) { setError(e.message); }
|
|
965
|
+
setLoading(false);
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const doReview = async () => {
|
|
969
|
+
if (!workspaceId || !reviewTopic.trim()) return;
|
|
970
|
+
setLoading(true); clearResult();
|
|
971
|
+
try {
|
|
972
|
+
const params = new URLSearchParams({
|
|
973
|
+
workspace_id: String(workspaceId), topic: reviewTopic, limit: String(reviewLimit),
|
|
974
|
+
});
|
|
975
|
+
const res = await fetch(api(`/api/cortex/curation/review?${params}`));
|
|
976
|
+
const data = await res.json();
|
|
977
|
+
if (!res.ok) throw new Error(data.error?.message || data.error || 'Review failed');
|
|
978
|
+
setResult(data);
|
|
979
|
+
} catch (e: any) { setError(e.message); }
|
|
980
|
+
setLoading(false);
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
const doRefine = async () => {
|
|
984
|
+
if (!workspaceId) return;
|
|
985
|
+
setLoading(true); clearResult();
|
|
986
|
+
try {
|
|
987
|
+
const res = await fetch(api('/api/cortex/curation/refine'), {
|
|
988
|
+
method: 'POST',
|
|
989
|
+
headers: { 'Content-Type': 'application/json' },
|
|
990
|
+
body: JSON.stringify({
|
|
991
|
+
workspace_id: workspaceId,
|
|
992
|
+
domain_context: domainContext || undefined,
|
|
993
|
+
types: refineTypes.length > 0 ? refineTypes : undefined,
|
|
994
|
+
}),
|
|
995
|
+
});
|
|
996
|
+
const data = await res.json();
|
|
997
|
+
if (!res.ok) throw new Error(data.error?.message || data.error || 'Refine failed');
|
|
998
|
+
setResult(data);
|
|
999
|
+
} catch (e: any) { setError(e.message); }
|
|
1000
|
+
setLoading(false);
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
const doPublish = async () => {
|
|
1004
|
+
if (!workspaceId || !pubName || !pubAuthor) return;
|
|
1005
|
+
setLoading(true); clearResult();
|
|
1006
|
+
try {
|
|
1007
|
+
const res = await fetch(api('/api/cortex/curation/publish'), {
|
|
1008
|
+
method: 'POST',
|
|
1009
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1010
|
+
body: JSON.stringify({
|
|
1011
|
+
workspace_id: workspaceId, author: pubAuthor, name: pubName,
|
|
1012
|
+
description: pubDesc, tags: pubTags.split(',').map(t => t.trim()).filter(Boolean),
|
|
1013
|
+
version: pubVersion, license: pubLicense, preview_count: pubPreviewCount,
|
|
1014
|
+
}),
|
|
1015
|
+
});
|
|
1016
|
+
const data = await res.json();
|
|
1017
|
+
if (!res.ok) throw new Error(data.error?.message || data.error || 'Publish failed');
|
|
1018
|
+
setResult(data);
|
|
1019
|
+
} catch (e: any) { setError(e.message); }
|
|
1020
|
+
setLoading(false);
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
return (
|
|
1024
|
+
<div className="p-6 max-w-4xl">
|
|
1025
|
+
{/* Workspace selector */}
|
|
1026
|
+
<div className="flex items-center gap-3 mb-6">
|
|
1027
|
+
<label className="text-xs text-gray-500">Workspace</label>
|
|
1028
|
+
<select
|
|
1029
|
+
value={workspaceId}
|
|
1030
|
+
onChange={e => { setWorkspaceId(parseInt(e.target.value, 10)); clearResult(); }}
|
|
1031
|
+
className="text-sm bg-white/5 border border-white/10 rounded-lg px-3 py-1.5 text-gray-300"
|
|
1032
|
+
>
|
|
1033
|
+
{workspaces.map(ws => (
|
|
1034
|
+
<option key={ws.id} value={ws.id}>{ws.name}</option>
|
|
1035
|
+
))}
|
|
1036
|
+
</select>
|
|
1037
|
+
</div>
|
|
1038
|
+
|
|
1039
|
+
{/* Pipeline bar */}
|
|
1040
|
+
<div className="flex items-center gap-0 mb-8">
|
|
1041
|
+
{STEPS.map((s, i) => {
|
|
1042
|
+
const Icon = s.icon;
|
|
1043
|
+
const active = step === s.key;
|
|
1044
|
+
return (
|
|
1045
|
+
<div key={s.key} className="flex items-center">
|
|
1046
|
+
{i > 0 && <div className={`w-8 h-px ${active ? 'bg-purple-500/40' : 'bg-white/[0.06]'}`} />}
|
|
1047
|
+
<button
|
|
1048
|
+
onClick={() => { setStep(s.key); clearResult(); }}
|
|
1049
|
+
className={`flex items-center gap-1.5 px-4 py-2 rounded-full text-xs font-medium transition-colors ${
|
|
1050
|
+
active
|
|
1051
|
+
? 'bg-purple-500/20 border border-purple-500/30 text-purple-400'
|
|
1052
|
+
: 'bg-white/[0.03] border border-white/[0.06] text-gray-500 hover:text-gray-300'
|
|
1053
|
+
}`}
|
|
1054
|
+
>
|
|
1055
|
+
<Icon className="w-3.5 h-3.5" />
|
|
1056
|
+
{s.label}
|
|
1057
|
+
</button>
|
|
1058
|
+
</div>
|
|
1059
|
+
);
|
|
1060
|
+
})}
|
|
1061
|
+
</div>
|
|
1062
|
+
|
|
1063
|
+
{/* Error banner */}
|
|
1064
|
+
{error && (
|
|
1065
|
+
<div className="mb-4 px-4 py-2 bg-red-500/10 border border-red-500/20 rounded-lg text-xs text-red-400">
|
|
1066
|
+
{error}
|
|
1067
|
+
</div>
|
|
1068
|
+
)}
|
|
1069
|
+
|
|
1070
|
+
{/* Step content */}
|
|
1071
|
+
<div className="min-h-[300px]">
|
|
1072
|
+
{step === 'seed' && (
|
|
1073
|
+
<div className="space-y-4">
|
|
1074
|
+
<textarea
|
|
1075
|
+
value={seedText}
|
|
1076
|
+
onChange={e => setSeedText(e.target.value)}
|
|
1077
|
+
placeholder="Paste document content here..."
|
|
1078
|
+
rows={8}
|
|
1079
|
+
className="w-full text-sm bg-white/5 border border-white/10 rounded-lg p-3 text-gray-300 placeholder-gray-600 focus:outline-none focus:border-purple-500/50 resize-y"
|
|
1080
|
+
/>
|
|
1081
|
+
<div className="flex gap-3">
|
|
1082
|
+
<div>
|
|
1083
|
+
<label className="text-[10px] text-gray-500 block mb-1">Format</label>
|
|
1084
|
+
<select value={seedFormat} onChange={e => setSeedFormat(e.target.value)}
|
|
1085
|
+
className="text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-gray-300">
|
|
1086
|
+
<option value="auto">Auto-detect</option>
|
|
1087
|
+
<option value="markdown">Markdown</option>
|
|
1088
|
+
<option value="plaintext">Plain text</option>
|
|
1089
|
+
<option value="csv">CSV</option>
|
|
1090
|
+
</select>
|
|
1091
|
+
</div>
|
|
1092
|
+
<div className="flex-1">
|
|
1093
|
+
<label className="text-[10px] text-gray-500 block mb-1">Source reference</label>
|
|
1094
|
+
<input value={seedSource} onChange={e => setSeedSource(e.target.value)}
|
|
1095
|
+
placeholder="filename.md or URL"
|
|
1096
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-gray-300 placeholder-gray-600" />
|
|
1097
|
+
</div>
|
|
1098
|
+
</div>
|
|
1099
|
+
<label className="flex items-center gap-2 text-xs text-gray-400">
|
|
1100
|
+
<input type="checkbox" checked={seedDistill} onChange={e => setSeedDistill(e.target.checked)}
|
|
1101
|
+
className="accent-purple-500" />
|
|
1102
|
+
Run distillation after seeding
|
|
1103
|
+
</label>
|
|
1104
|
+
<button onClick={doSeed} disabled={loading || !seedText.trim()}
|
|
1105
|
+
className="px-5 py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1106
|
+
{loading ? <><Loader2 className="w-3.5 h-3.5 inline animate-spin mr-1" /> Processing...</> : 'Seed Documents'}
|
|
1107
|
+
</button>
|
|
1108
|
+
{result && (
|
|
1109
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4 text-xs text-gray-300">
|
|
1110
|
+
<div>Chunks created: <span className="text-green-400">{result.chunksCreated}</span></div>
|
|
1111
|
+
<div>Chunks skipped: <span className="text-gray-500">{result.chunksSkipped}</span></div>
|
|
1112
|
+
{result.errors?.length > 0 && (
|
|
1113
|
+
<div className="text-red-400 mt-1">{result.errors.join(', ')}</div>
|
|
1114
|
+
)}
|
|
1115
|
+
</div>
|
|
1116
|
+
)}
|
|
1117
|
+
</div>
|
|
1118
|
+
)}
|
|
1119
|
+
|
|
1120
|
+
{step === 'assess' && (
|
|
1121
|
+
<div className="space-y-4">
|
|
1122
|
+
<button onClick={doAssess} disabled={loading}
|
|
1123
|
+
className="px-5 py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1124
|
+
{loading ? <><Loader2 className="w-3.5 h-3.5 inline animate-spin mr-1" /> Assessing...</> : 'Run Assessment'}
|
|
1125
|
+
</button>
|
|
1126
|
+
{result && (
|
|
1127
|
+
<div className="space-y-4">
|
|
1128
|
+
<div className="grid grid-cols-3 gap-3">
|
|
1129
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-3">
|
|
1130
|
+
<div className="text-[10px] text-gray-500 uppercase">Coverage</div>
|
|
1131
|
+
<div className={`text-xl font-semibold ${
|
|
1132
|
+
result.coverage_score > 0.7 ? 'text-green-400' : result.coverage_score > 0.3 ? 'text-amber-400' : 'text-red-400'
|
|
1133
|
+
}`}>{result.coverage_score?.toFixed(2)}</div>
|
|
1134
|
+
</div>
|
|
1135
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-3">
|
|
1136
|
+
<div className="text-[10px] text-gray-500 uppercase">Total Units</div>
|
|
1137
|
+
<div className="text-xl font-semibold text-white">{result.total_units}</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-3">
|
|
1140
|
+
<div className="text-[10px] text-gray-500 uppercase">Stale</div>
|
|
1141
|
+
<div className="text-xl font-semibold text-amber-400">{result.stale_count}</div>
|
|
1142
|
+
</div>
|
|
1143
|
+
</div>
|
|
1144
|
+
{result.type_distribution && (
|
|
1145
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4">
|
|
1146
|
+
<div className="text-[10px] text-gray-500 uppercase mb-2">Type Distribution</div>
|
|
1147
|
+
<div className="space-y-1.5">
|
|
1148
|
+
{Object.entries(result.type_distribution as Record<string, number>)
|
|
1149
|
+
.sort((a, b) => b[1] - a[1])
|
|
1150
|
+
.map(([type, count]) => (
|
|
1151
|
+
<div key={type} className="flex items-center gap-2">
|
|
1152
|
+
<span className={`text-[10px] px-1.5 py-0.5 rounded font-medium w-24 text-center ${TYPE_COLORS[type] || TYPE_COLORS.context}`}>
|
|
1153
|
+
{type.replace('_', ' ')}
|
|
1154
|
+
</span>
|
|
1155
|
+
<div className="flex-1 h-2 bg-white/[0.03] rounded-full overflow-hidden">
|
|
1156
|
+
<div className="h-full bg-purple-500/50 rounded-full"
|
|
1157
|
+
style={{ width: `${(count / Math.max(result.total_units, 1)) * 100}%` }} />
|
|
1158
|
+
</div>
|
|
1159
|
+
<span className="text-[10px] text-gray-500 w-10 text-right tabular-nums">{count}</span>
|
|
1160
|
+
</div>
|
|
1161
|
+
))}
|
|
1162
|
+
</div>
|
|
1163
|
+
</div>
|
|
1164
|
+
)}
|
|
1165
|
+
</div>
|
|
1166
|
+
)}
|
|
1167
|
+
</div>
|
|
1168
|
+
)}
|
|
1169
|
+
|
|
1170
|
+
{step === 'review' && (
|
|
1171
|
+
<div className="space-y-4">
|
|
1172
|
+
<div className="flex gap-3">
|
|
1173
|
+
<input value={reviewTopic} onChange={e => setReviewTopic(e.target.value)}
|
|
1174
|
+
onKeyDown={e => e.key === 'Enter' && doReview()}
|
|
1175
|
+
placeholder="Enter topic to review..."
|
|
1176
|
+
className="flex-1 text-sm bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-gray-300 placeholder-gray-600 focus:outline-none focus:border-purple-500/50" />
|
|
1177
|
+
<select value={reviewLimit} onChange={e => setReviewLimit(parseInt(e.target.value, 10))}
|
|
1178
|
+
className="text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-gray-300">
|
|
1179
|
+
<option value={10}>10</option>
|
|
1180
|
+
<option value={25}>25</option>
|
|
1181
|
+
<option value={50}>50</option>
|
|
1182
|
+
</select>
|
|
1183
|
+
<button onClick={doReview} disabled={loading || !reviewTopic.trim()}
|
|
1184
|
+
className="px-5 py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1185
|
+
{loading ? 'Reviewing...' : 'Review'}
|
|
1186
|
+
</button>
|
|
1187
|
+
</div>
|
|
1188
|
+
{result?.by_type && (
|
|
1189
|
+
<div className="space-y-3">
|
|
1190
|
+
<div className="text-xs text-gray-400">{result.total_matches} matches for “{result.topic}”</div>
|
|
1191
|
+
{Object.entries(result.by_type as Record<string, any[]>).map(([type, items]) => (
|
|
1192
|
+
<details key={type} open className="bg-white/[0.02] border border-white/[0.06] rounded-lg">
|
|
1193
|
+
<summary className="px-4 py-2 text-xs text-gray-300 cursor-pointer hover:bg-white/[0.02]">
|
|
1194
|
+
<span className={`inline-block px-1.5 py-0.5 rounded font-medium mr-2 ${TYPE_COLORS[type] || TYPE_COLORS.context}`}>
|
|
1195
|
+
{type.replace('_', ' ')}
|
|
1196
|
+
</span>
|
|
1197
|
+
({items.length})
|
|
1198
|
+
</summary>
|
|
1199
|
+
<div className="px-4 pb-3 space-y-2">
|
|
1200
|
+
{items.map((item: any, i: number) => (
|
|
1201
|
+
<div key={i} className="text-[11px] text-gray-400 border-l-2 border-white/5 pl-3 py-1">
|
|
1202
|
+
<div>{item.text}</div>
|
|
1203
|
+
<div className="text-[10px] text-gray-600 mt-0.5">
|
|
1204
|
+
confidence: {item.confidence?.toFixed(2)} · similarity: {item.similarity?.toFixed(3)}
|
|
1205
|
+
</div>
|
|
1206
|
+
</div>
|
|
1207
|
+
))}
|
|
1208
|
+
</div>
|
|
1209
|
+
</details>
|
|
1210
|
+
))}
|
|
1211
|
+
</div>
|
|
1212
|
+
)}
|
|
1213
|
+
</div>
|
|
1214
|
+
)}
|
|
1215
|
+
|
|
1216
|
+
{step === 'refine' && (
|
|
1217
|
+
<div className="space-y-4">
|
|
1218
|
+
{!result && (
|
|
1219
|
+
<div className="px-4 py-2 bg-amber-500/10 border border-amber-500/20 rounded-lg text-xs text-amber-400">
|
|
1220
|
+
Refine requires an LLM API key for distillation. If not configured, go to{' '}
|
|
1221
|
+
<button onClick={() => { /* parent would need to expose setTab — or use a link */ }}
|
|
1222
|
+
className="underline">Settings</button> to add one.
|
|
1223
|
+
</div>
|
|
1224
|
+
)}
|
|
1225
|
+
<div>
|
|
1226
|
+
<label className="text-xs text-gray-400 block mb-1">Domain Context</label>
|
|
1227
|
+
<textarea value={domainContext} onChange={e => setDomainContext(e.target.value)}
|
|
1228
|
+
placeholder="Describe the domain this lobe should focus on..."
|
|
1229
|
+
rows={3}
|
|
1230
|
+
className="w-full text-sm bg-white/5 border border-white/10 rounded-lg p-3 text-gray-300 placeholder-gray-600 focus:outline-none focus:border-purple-500/50 resize-y" />
|
|
1231
|
+
</div>
|
|
1232
|
+
<div>
|
|
1233
|
+
<label className="text-xs text-gray-400 block mb-2">Distillation passes</label>
|
|
1234
|
+
<div className="flex gap-3">
|
|
1235
|
+
{['decisions', 'patterns', 'preferences', 'error_fixes'].map(t => (
|
|
1236
|
+
<label key={t} className="flex items-center gap-1.5 text-xs text-gray-400">
|
|
1237
|
+
<input type="checkbox" checked={refineTypes.includes(t)}
|
|
1238
|
+
onChange={e => setRefineTypes(prev =>
|
|
1239
|
+
e.target.checked ? [...prev, t] : prev.filter(x => x !== t)
|
|
1240
|
+
)}
|
|
1241
|
+
className="accent-purple-500" />
|
|
1242
|
+
{t.replace('_', ' ')}
|
|
1243
|
+
</label>
|
|
1244
|
+
))}
|
|
1245
|
+
</div>
|
|
1246
|
+
</div>
|
|
1247
|
+
<button onClick={doRefine} disabled={loading}
|
|
1248
|
+
className="px-5 py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1249
|
+
{loading ? <><Loader2 className="w-3.5 h-3.5 inline animate-spin mr-1" /> Refining...</> : 'Refine Lobe'}
|
|
1250
|
+
</button>
|
|
1251
|
+
{result && (
|
|
1252
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4 text-xs text-gray-300 space-y-1">
|
|
1253
|
+
<div>Source units found: {result.source_units_found}</div>
|
|
1254
|
+
<div>Old distilled purged: {result.distilled_purged}</div>
|
|
1255
|
+
<div>New units created: <span className="text-green-400">{result.new_units_created}</span></div>
|
|
1256
|
+
{result.errors?.length > 0 && (
|
|
1257
|
+
<div className="text-red-400 mt-1">{result.errors.join(', ')}</div>
|
|
1258
|
+
)}
|
|
1259
|
+
</div>
|
|
1260
|
+
)}
|
|
1261
|
+
</div>
|
|
1262
|
+
)}
|
|
1263
|
+
|
|
1264
|
+
{step === 'publish' && (
|
|
1265
|
+
<div className="space-y-4">
|
|
1266
|
+
<div className="grid grid-cols-2 gap-3">
|
|
1267
|
+
<div>
|
|
1268
|
+
<label className="text-[10px] text-gray-500 block mb-1">Name *</label>
|
|
1269
|
+
<input value={pubName} onChange={e => setPubName(e.target.value)}
|
|
1270
|
+
placeholder="My Knowledge Pack"
|
|
1271
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300 placeholder-gray-600" />
|
|
1272
|
+
</div>
|
|
1273
|
+
<div>
|
|
1274
|
+
<label className="text-[10px] text-gray-500 block mb-1">Author *</label>
|
|
1275
|
+
<input value={pubAuthor} onChange={e => setPubAuthor(e.target.value)}
|
|
1276
|
+
placeholder="Your name"
|
|
1277
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300 placeholder-gray-600" />
|
|
1278
|
+
</div>
|
|
1279
|
+
</div>
|
|
1280
|
+
<div>
|
|
1281
|
+
<label className="text-[10px] text-gray-500 block mb-1">Description</label>
|
|
1282
|
+
<textarea value={pubDesc} onChange={e => setPubDesc(e.target.value)}
|
|
1283
|
+
placeholder="What this knowledge pack contains..."
|
|
1284
|
+
rows={2}
|
|
1285
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded p-2 text-gray-300 placeholder-gray-600 resize-y" />
|
|
1286
|
+
</div>
|
|
1287
|
+
<div className="grid grid-cols-3 gap-3">
|
|
1288
|
+
<div>
|
|
1289
|
+
<label className="text-[10px] text-gray-500 block mb-1">Tags (comma-separated)</label>
|
|
1290
|
+
<input value={pubTags} onChange={e => setPubTags(e.target.value)}
|
|
1291
|
+
placeholder="engineering, patterns"
|
|
1292
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300 placeholder-gray-600" />
|
|
1293
|
+
</div>
|
|
1294
|
+
<div>
|
|
1295
|
+
<label className="text-[10px] text-gray-500 block mb-1">Version</label>
|
|
1296
|
+
<input value={pubVersion} onChange={e => setPubVersion(e.target.value)}
|
|
1297
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300" />
|
|
1298
|
+
</div>
|
|
1299
|
+
<div>
|
|
1300
|
+
<label className="text-[10px] text-gray-500 block mb-1">License</label>
|
|
1301
|
+
<select value={pubLicense} onChange={e => setPubLicense(e.target.value)}
|
|
1302
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300">
|
|
1303
|
+
<option value="cc-by">CC-BY</option>
|
|
1304
|
+
<option value="cc-by-sa">CC-BY-SA</option>
|
|
1305
|
+
<option value="commercial">Commercial</option>
|
|
1306
|
+
<option value="free">Free</option>
|
|
1307
|
+
</select>
|
|
1308
|
+
</div>
|
|
1309
|
+
</div>
|
|
1310
|
+
<button onClick={doPublish} disabled={loading || !pubName || !pubAuthor}
|
|
1311
|
+
className="px-5 py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1312
|
+
{loading ? <><Loader2 className="w-3.5 h-3.5 inline animate-spin mr-1" /> Publishing...</> : 'Publish to Marketplace'}
|
|
1313
|
+
</button>
|
|
1314
|
+
{result && (
|
|
1315
|
+
<div className="space-y-2">
|
|
1316
|
+
{result.quality_warning && (
|
|
1317
|
+
<div className="px-4 py-2 bg-amber-500/10 border border-amber-500/20 rounded-lg text-xs text-amber-400">
|
|
1318
|
+
Coverage score is low ({result.quality?.coverage_score?.toFixed(2)}). Consider seeding more documents or refining.
|
|
1319
|
+
</div>
|
|
1320
|
+
)}
|
|
1321
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4 text-xs text-gray-300 space-y-1">
|
|
1322
|
+
<div>Output: <span className="text-gray-400 font-mono">{result.path}</span></div>
|
|
1323
|
+
<div>Units: {result.total_before} total, {result.excluded_sensitivity} excluded (sensitivity), {result.pii_scrubbed} PII-scrubbed</div>
|
|
1324
|
+
<div>Final pack: <span className="text-green-400">{result.final_units} units</span></div>
|
|
1325
|
+
</div>
|
|
1326
|
+
</div>
|
|
1327
|
+
)}
|
|
1328
|
+
</div>
|
|
1329
|
+
)}
|
|
1330
|
+
</div>
|
|
1331
|
+
</div>
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
- [ ] **Step 2: Verify build**
|
|
1337
|
+
|
|
1338
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
1339
|
+
Expected: Build succeeds (component not yet imported by page)
|
|
1340
|
+
|
|
1341
|
+
- [ ] **Step 3: Commit**
|
|
1342
|
+
|
|
1343
|
+
```bash
|
|
1344
|
+
git add src/components/cortex/curation-tab.tsx
|
|
1345
|
+
git commit -m "feat(cortex): add curation tab component with pipeline UI"
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
---
|
|
1349
|
+
|
|
1350
|
+
### Task 10: Build Marketplace Tab Components
|
|
1351
|
+
|
|
1352
|
+
**Files:**
|
|
1353
|
+
- Create: `src/components/cortex/marketplace-card.tsx`
|
|
1354
|
+
- Create: `src/components/cortex/import-dialog.tsx`
|
|
1355
|
+
- Create: `src/components/cortex/marketplace-tab.tsx`
|
|
1356
|
+
|
|
1357
|
+
- [ ] **Step 1: Create marketplace card**
|
|
1358
|
+
|
|
1359
|
+
```typescript
|
|
1360
|
+
// src/components/cortex/marketplace-card.tsx
|
|
1361
|
+
'use client';
|
|
1362
|
+
|
|
1363
|
+
import { useState } from 'react';
|
|
1364
|
+
import { Package, ChevronDown, ChevronUp } from 'lucide-react';
|
|
1365
|
+
import { TYPE_COLORS } from './constants';
|
|
1366
|
+
|
|
1367
|
+
interface PackManifest {
|
|
1368
|
+
version: string;
|
|
1369
|
+
exportDate: string;
|
|
1370
|
+
unitCount: number;
|
|
1371
|
+
marketplace?: {
|
|
1372
|
+
name: string;
|
|
1373
|
+
author: string;
|
|
1374
|
+
description: string;
|
|
1375
|
+
tags: string[];
|
|
1376
|
+
packageVersion: string;
|
|
1377
|
+
license: string;
|
|
1378
|
+
domain_context?: string;
|
|
1379
|
+
quality: {
|
|
1380
|
+
coverage_score: number;
|
|
1381
|
+
avg_confidence: number;
|
|
1382
|
+
type_distribution: Record<string, number>;
|
|
1383
|
+
total_units: number;
|
|
1384
|
+
};
|
|
1385
|
+
preview: Array<{ text: string; type: string }>;
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
interface Props {
|
|
1390
|
+
filename: string;
|
|
1391
|
+
manifest: PackManifest;
|
|
1392
|
+
onImport: (filename: string, manifest: PackManifest) => void;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
export function MarketplaceCard({ filename, manifest, onImport }: Props) {
|
|
1396
|
+
const [showPreview, setShowPreview] = useState(false);
|
|
1397
|
+
const mp = manifest.marketplace;
|
|
1398
|
+
|
|
1399
|
+
const coverageColor = mp
|
|
1400
|
+
? mp.quality.coverage_score > 0.7 ? 'text-green-400'
|
|
1401
|
+
: mp.quality.coverage_score > 0.3 ? 'text-amber-400'
|
|
1402
|
+
: 'text-red-400'
|
|
1403
|
+
: 'text-gray-500';
|
|
1404
|
+
|
|
1405
|
+
return (
|
|
1406
|
+
<div className="bg-white/[0.02] border border-white/[0.06] rounded-lg p-4 hover:border-white/10 transition-colors">
|
|
1407
|
+
<div className="flex items-start justify-between gap-3">
|
|
1408
|
+
<div className="flex-1 min-w-0">
|
|
1409
|
+
<div className="flex items-center gap-2">
|
|
1410
|
+
<Package className="w-4 h-4 text-purple-400 shrink-0" />
|
|
1411
|
+
<h3 className="text-sm font-medium text-gray-200 truncate">
|
|
1412
|
+
{mp?.name || filename}
|
|
1413
|
+
</h3>
|
|
1414
|
+
{mp && <span className="text-[10px] text-gray-600 shrink-0">v{mp.packageVersion}</span>}
|
|
1415
|
+
</div>
|
|
1416
|
+
{mp && <div className="text-[10px] text-gray-500 mt-0.5">by {mp.author}</div>}
|
|
1417
|
+
<p className="text-xs text-gray-400 mt-1 line-clamp-2">
|
|
1418
|
+
{mp?.description || `Exported ${new Date(manifest.exportDate).toLocaleDateString()}`}
|
|
1419
|
+
</p>
|
|
1420
|
+
</div>
|
|
1421
|
+
<div className="text-right shrink-0">
|
|
1422
|
+
<div className={`text-lg font-semibold tabular-nums ${coverageColor}`}>
|
|
1423
|
+
{mp ? mp.quality.coverage_score.toFixed(2) : '—'}
|
|
1424
|
+
</div>
|
|
1425
|
+
<div className="text-[10px] text-gray-600">{manifest.unitCount} units</div>
|
|
1426
|
+
</div>
|
|
1427
|
+
</div>
|
|
1428
|
+
|
|
1429
|
+
{mp?.tags && mp.tags.length > 0 && (
|
|
1430
|
+
<div className="flex flex-wrap gap-1 mt-2">
|
|
1431
|
+
{mp.tags.map(tag => (
|
|
1432
|
+
<span key={tag} className="text-[9px] px-1.5 py-0.5 bg-white/5 border border-white/[0.06] rounded text-gray-500">
|
|
1433
|
+
{tag}
|
|
1434
|
+
</span>
|
|
1435
|
+
))}
|
|
1436
|
+
{mp.license && (
|
|
1437
|
+
<span className="text-[9px] px-1.5 py-0.5 bg-purple-500/10 border border-purple-500/20 rounded text-purple-400">
|
|
1438
|
+
{mp.license}
|
|
1439
|
+
</span>
|
|
1440
|
+
)}
|
|
1441
|
+
</div>
|
|
1442
|
+
)}
|
|
1443
|
+
|
|
1444
|
+
{mp?.preview && mp.preview.length > 0 && (
|
|
1445
|
+
<div className="mt-2">
|
|
1446
|
+
<button onClick={() => setShowPreview(!showPreview)}
|
|
1447
|
+
className="flex items-center gap-1 text-[10px] text-gray-600 hover:text-gray-400">
|
|
1448
|
+
Preview {showPreview ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
|
|
1449
|
+
</button>
|
|
1450
|
+
{showPreview && (
|
|
1451
|
+
<div className="mt-1 space-y-1">
|
|
1452
|
+
{mp.preview.map((p, i) => (
|
|
1453
|
+
<div key={i} className="text-[10px] text-gray-500 border-l-2 border-white/5 pl-2 py-0.5">
|
|
1454
|
+
<span className={`inline-block px-1 py-0 rounded mr-1 ${TYPE_COLORS[p.type] || TYPE_COLORS.context}`}>
|
|
1455
|
+
{p.type}
|
|
1456
|
+
</span>
|
|
1457
|
+
{p.text.slice(0, 100)}
|
|
1458
|
+
</div>
|
|
1459
|
+
))}
|
|
1460
|
+
</div>
|
|
1461
|
+
)}
|
|
1462
|
+
</div>
|
|
1463
|
+
)}
|
|
1464
|
+
|
|
1465
|
+
<button onClick={() => onImport(filename, manifest)}
|
|
1466
|
+
className="mt-3 w-full py-1.5 text-xs bg-purple-600 hover:bg-purple-500 text-white rounded-lg">
|
|
1467
|
+
Import
|
|
1468
|
+
</button>
|
|
1469
|
+
</div>
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
- [ ] **Step 2: Create import dialog**
|
|
1475
|
+
|
|
1476
|
+
```typescript
|
|
1477
|
+
// src/components/cortex/import-dialog.tsx
|
|
1478
|
+
'use client';
|
|
1479
|
+
|
|
1480
|
+
import { useState, useEffect } from 'react';
|
|
1481
|
+
import { X, Loader2 } from 'lucide-react';
|
|
1482
|
+
import { api } from '@/lib/api';
|
|
1483
|
+
|
|
1484
|
+
interface Props {
|
|
1485
|
+
filename: string;
|
|
1486
|
+
hasDomainContext: boolean;
|
|
1487
|
+
onClose: () => void;
|
|
1488
|
+
onComplete: () => void;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
export function ImportDialog({ filename, hasDomainContext, onClose, onComplete }: Props) {
|
|
1492
|
+
const [targetLayer, setTargetLayer] = useState('workspace');
|
|
1493
|
+
const [workspaceId, setWorkspaceId] = useState('');
|
|
1494
|
+
const [mergeStrategy, setMergeStrategy] = useState('merge');
|
|
1495
|
+
const [reEmbed, setReEmbed] = useState(false);
|
|
1496
|
+
const [applyDomainCtx, setApplyDomainCtx] = useState(false);
|
|
1497
|
+
const [workspaces, setWorkspaces] = useState<{ id: number; name: string }[]>([]);
|
|
1498
|
+
const [importing, setImporting] = useState(false);
|
|
1499
|
+
const [error, setError] = useState<string | null>(null);
|
|
1500
|
+
|
|
1501
|
+
useEffect(() => {
|
|
1502
|
+
fetch(api('/api/workspaces'))
|
|
1503
|
+
.then(r => r.json())
|
|
1504
|
+
.then(data => {
|
|
1505
|
+
const list = Array.isArray(data) ? data : data.local || [];
|
|
1506
|
+
setWorkspaces(list);
|
|
1507
|
+
if (list.length > 0) setWorkspaceId(String(list[0].id));
|
|
1508
|
+
})
|
|
1509
|
+
.catch(() => {});
|
|
1510
|
+
}, []);
|
|
1511
|
+
|
|
1512
|
+
const handleImport = async () => {
|
|
1513
|
+
setImporting(true);
|
|
1514
|
+
setError(null);
|
|
1515
|
+
try {
|
|
1516
|
+
// Fetch the file from marketplace dir and upload it
|
|
1517
|
+
const formData = new FormData();
|
|
1518
|
+
const res = await fetch(api(`/api/cortex/marketplace/preview?file=${encodeURIComponent(filename)}`));
|
|
1519
|
+
// We need to send the file — fetch it as blob from the server
|
|
1520
|
+
// Actually, the import route expects a file upload. We'll pass the filename to a modified import.
|
|
1521
|
+
// For now, use a server-side import that reads from marketplace dir directly.
|
|
1522
|
+
const importRes = await fetch(api('/api/cortex/import'), {
|
|
1523
|
+
method: 'POST',
|
|
1524
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1525
|
+
body: JSON.stringify({
|
|
1526
|
+
marketplace_file: filename,
|
|
1527
|
+
target_layer: targetLayer,
|
|
1528
|
+
workspace_id: targetLayer === 'workspace' ? workspaceId : undefined,
|
|
1529
|
+
merge_strategy: mergeStrategy,
|
|
1530
|
+
re_embed: reEmbed,
|
|
1531
|
+
}),
|
|
1532
|
+
});
|
|
1533
|
+
if (!importRes.ok) {
|
|
1534
|
+
const data = await importRes.json();
|
|
1535
|
+
throw new Error(data.error || 'Import failed');
|
|
1536
|
+
}
|
|
1537
|
+
onComplete();
|
|
1538
|
+
} catch (e: any) {
|
|
1539
|
+
setError(e.message);
|
|
1540
|
+
}
|
|
1541
|
+
setImporting(false);
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
return (
|
|
1545
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60">
|
|
1546
|
+
<div className="bg-gray-900 border border-white/10 rounded-xl p-6 w-full max-w-md shadow-xl">
|
|
1547
|
+
<div className="flex items-center justify-between mb-4">
|
|
1548
|
+
<h3 className="text-sm font-medium text-gray-200">Import {filename}</h3>
|
|
1549
|
+
<button onClick={onClose} className="text-gray-500 hover:text-gray-300"><X className="w-4 h-4" /></button>
|
|
1550
|
+
</div>
|
|
1551
|
+
|
|
1552
|
+
<div className="space-y-4">
|
|
1553
|
+
<div>
|
|
1554
|
+
<label className="text-[10px] text-gray-500 block mb-1">Target layer</label>
|
|
1555
|
+
<div className="flex gap-2">
|
|
1556
|
+
{['personal', 'workspace', 'team'].map(l => (
|
|
1557
|
+
<button key={l} onClick={() => setTargetLayer(l)}
|
|
1558
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
1559
|
+
targetLayer === l ? 'bg-purple-500/20 text-purple-400 border border-purple-500/30' : 'bg-white/5 text-gray-500 border border-white/[0.06]'
|
|
1560
|
+
}`}>{l}</button>
|
|
1561
|
+
))}
|
|
1562
|
+
</div>
|
|
1563
|
+
</div>
|
|
1564
|
+
|
|
1565
|
+
{targetLayer === 'workspace' && (
|
|
1566
|
+
<div>
|
|
1567
|
+
<label className="text-[10px] text-gray-500 block mb-1">Workspace</label>
|
|
1568
|
+
<select value={workspaceId} onChange={e => setWorkspaceId(e.target.value)}
|
|
1569
|
+
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-gray-300">
|
|
1570
|
+
{workspaces.map(ws => <option key={ws.id} value={ws.id}>{ws.name}</option>)}
|
|
1571
|
+
</select>
|
|
1572
|
+
</div>
|
|
1573
|
+
)}
|
|
1574
|
+
|
|
1575
|
+
<div>
|
|
1576
|
+
<label className="text-[10px] text-gray-500 block mb-1">Merge strategy</label>
|
|
1577
|
+
<div className="flex gap-2">
|
|
1578
|
+
{['append', 'merge', 'replace'].map(s => (
|
|
1579
|
+
<button key={s} onClick={() => setMergeStrategy(s)}
|
|
1580
|
+
className={`px-3 py-1 text-xs rounded ${
|
|
1581
|
+
mergeStrategy === s ? 'bg-purple-500/20 text-purple-400 border border-purple-500/30' : 'bg-white/5 text-gray-500 border border-white/[0.06]'
|
|
1582
|
+
}`}>{s}</button>
|
|
1583
|
+
))}
|
|
1584
|
+
</div>
|
|
1585
|
+
</div>
|
|
1586
|
+
|
|
1587
|
+
<label className="flex items-center gap-2 text-xs text-gray-400">
|
|
1588
|
+
<input type="checkbox" checked={reEmbed} onChange={e => setReEmbed(e.target.checked)} className="accent-purple-500" />
|
|
1589
|
+
Re-generate embeddings (slower, matches your provider)
|
|
1590
|
+
</label>
|
|
1591
|
+
|
|
1592
|
+
{hasDomainContext && (
|
|
1593
|
+
<label className="flex items-center gap-2 text-xs text-gray-400">
|
|
1594
|
+
<input type="checkbox" checked={applyDomainCtx} onChange={e => setApplyDomainCtx(e.target.checked)} className="accent-purple-500" />
|
|
1595
|
+
Apply pack's domain context to target workspace
|
|
1596
|
+
</label>
|
|
1597
|
+
)}
|
|
1598
|
+
|
|
1599
|
+
{error && <div className="text-xs text-red-400">{error}</div>}
|
|
1600
|
+
|
|
1601
|
+
<button onClick={handleImport} disabled={importing}
|
|
1602
|
+
className="w-full py-2 text-sm bg-purple-600 hover:bg-purple-500 text-white rounded-lg disabled:opacity-50">
|
|
1603
|
+
{importing ? <><Loader2 className="w-3.5 h-3.5 inline animate-spin mr-1" /> Importing...</> : 'Start Import'}
|
|
1604
|
+
</button>
|
|
1605
|
+
</div>
|
|
1606
|
+
</div>
|
|
1607
|
+
</div>
|
|
1608
|
+
);
|
|
1609
|
+
}
|
|
1610
|
+
```
|
|
1611
|
+
|
|
1612
|
+
- [ ] **Step 3: Create marketplace tab**
|
|
1613
|
+
|
|
1614
|
+
```typescript
|
|
1615
|
+
// src/components/cortex/marketplace-tab.tsx
|
|
1616
|
+
'use client';
|
|
1617
|
+
|
|
1618
|
+
import { useState, useEffect } from 'react';
|
|
1619
|
+
import { FolderOpen, RefreshCw } from 'lucide-react';
|
|
1620
|
+
import { api } from '@/lib/api';
|
|
1621
|
+
import { MarketplaceCard } from './marketplace-card';
|
|
1622
|
+
import { ImportDialog } from './import-dialog';
|
|
1623
|
+
|
|
1624
|
+
export function MarketplaceTab() {
|
|
1625
|
+
const [packs, setPacks] = useState<any[]>([]);
|
|
1626
|
+
const [directory, setDirectory] = useState('');
|
|
1627
|
+
const [loading, setLoading] = useState(true);
|
|
1628
|
+
const [importTarget, setImportTarget] = useState<{ filename: string; manifest: any } | null>(null);
|
|
1629
|
+
|
|
1630
|
+
const fetchPacks = async () => {
|
|
1631
|
+
setLoading(true);
|
|
1632
|
+
try {
|
|
1633
|
+
const res = await fetch(api('/api/cortex/marketplace/browse'));
|
|
1634
|
+
if (res.ok) {
|
|
1635
|
+
const data = await res.json();
|
|
1636
|
+
setPacks(data.packs || []);
|
|
1637
|
+
setDirectory(data.directory || '');
|
|
1638
|
+
}
|
|
1639
|
+
} catch {}
|
|
1640
|
+
setLoading(false);
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
useEffect(() => { fetchPacks(); }, []);
|
|
1644
|
+
|
|
1645
|
+
return (
|
|
1646
|
+
<div className="p-6 max-w-4xl">
|
|
1647
|
+
<div className="flex items-center justify-between mb-6">
|
|
1648
|
+
<div>
|
|
1649
|
+
<h2 className="text-sm font-medium text-gray-200">Marketplace</h2>
|
|
1650
|
+
{directory && <div className="text-[10px] text-gray-600 font-mono mt-0.5">{directory}</div>}
|
|
1651
|
+
</div>
|
|
1652
|
+
<button onClick={fetchPacks} disabled={loading}
|
|
1653
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs bg-white/5 border border-white/[0.06] rounded-lg text-gray-400 hover:text-gray-300 disabled:opacity-50">
|
|
1654
|
+
<RefreshCw className={`w-3 h-3 ${loading ? 'animate-spin' : ''}`} />
|
|
1655
|
+
Refresh
|
|
1656
|
+
</button>
|
|
1657
|
+
</div>
|
|
1658
|
+
|
|
1659
|
+
{loading && <p className="text-sm text-gray-500 text-center py-12">Scanning marketplace...</p>}
|
|
1660
|
+
|
|
1661
|
+
{!loading && packs.length === 0 && (
|
|
1662
|
+
<div className="text-center py-16">
|
|
1663
|
+
<FolderOpen className="w-8 h-8 text-gray-700 mx-auto mb-3" />
|
|
1664
|
+
<p className="text-sm text-gray-500">No .cortexpack files found</p>
|
|
1665
|
+
<p className="text-[10px] text-gray-600 mt-1">
|
|
1666
|
+
Publish a lobe from the Curation tab, or drop .cortexpack files into the marketplace directory.
|
|
1667
|
+
</p>
|
|
1668
|
+
</div>
|
|
1669
|
+
)}
|
|
1670
|
+
|
|
1671
|
+
{!loading && packs.length > 0 && (
|
|
1672
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
1673
|
+
{packs.map(pack => (
|
|
1674
|
+
<MarketplaceCard
|
|
1675
|
+
key={pack.filename}
|
|
1676
|
+
filename={pack.filename}
|
|
1677
|
+
manifest={pack.manifest}
|
|
1678
|
+
onImport={(f, m) => setImportTarget({ filename: f, manifest: m })}
|
|
1679
|
+
/>
|
|
1680
|
+
))}
|
|
1681
|
+
</div>
|
|
1682
|
+
)}
|
|
1683
|
+
|
|
1684
|
+
{importTarget && (
|
|
1685
|
+
<ImportDialog
|
|
1686
|
+
filename={importTarget.filename}
|
|
1687
|
+
hasDomainContext={!!importTarget.manifest.marketplace?.domain_context}
|
|
1688
|
+
onClose={() => setImportTarget(null)}
|
|
1689
|
+
onComplete={() => { setImportTarget(null); fetchPacks(); }}
|
|
1690
|
+
/>
|
|
1691
|
+
)}
|
|
1692
|
+
</div>
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
```
|
|
1696
|
+
|
|
1697
|
+
- [ ] **Step 4: Verify build**
|
|
1698
|
+
|
|
1699
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
1700
|
+
Expected: Build succeeds
|
|
1701
|
+
|
|
1702
|
+
- [ ] **Step 5: Commit**
|
|
1703
|
+
|
|
1704
|
+
```bash
|
|
1705
|
+
git add src/components/cortex/marketplace-card.tsx src/components/cortex/import-dialog.tsx src/components/cortex/marketplace-tab.tsx
|
|
1706
|
+
git commit -m "feat(cortex): add marketplace tab with card grid and import dialog"
|
|
1707
|
+
```
|
|
1708
|
+
|
|
1709
|
+
---
|
|
1710
|
+
|
|
1711
|
+
### Task 11: Wire Up 6-Tab Cortex Page
|
|
1712
|
+
|
|
1713
|
+
**Files:**
|
|
1714
|
+
- Modify: `src/app/(desktop)/cortex/page.tsx`
|
|
1715
|
+
- Delete: `src/components/cortex/context-tab.tsx` (deferred from Task 6)
|
|
1716
|
+
|
|
1717
|
+
- [ ] **Step 1: Delete context-tab.tsx**
|
|
1718
|
+
|
|
1719
|
+
```bash
|
|
1720
|
+
rm src/components/cortex/context-tab.tsx
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
- [ ] **Step 2: Update the page to use 6 tabs**
|
|
1724
|
+
|
|
1725
|
+
Replace the entire file:
|
|
1726
|
+
|
|
1727
|
+
```typescript
|
|
1728
|
+
'use client';
|
|
1729
|
+
|
|
1730
|
+
import { useState, useEffect } from 'react';
|
|
1731
|
+
import dynamic from 'next/dynamic';
|
|
1732
|
+
import { api } from '@/lib/api';
|
|
1733
|
+
import { KnowledgeTab } from '@/components/cortex/knowledge-tab';
|
|
1734
|
+
import { CortexSettings } from '@/components/cortex/cortex-settings';
|
|
1735
|
+
import { CortexDashboard } from '@/components/cortex/cortex-dashboard';
|
|
1736
|
+
import { CurationTab } from '@/components/cortex/curation-tab';
|
|
1737
|
+
import { MarketplaceTab } from '@/components/cortex/marketplace-tab';
|
|
1738
|
+
|
|
1739
|
+
const EntityGraphView = dynamic(
|
|
1740
|
+
() => import('@/components/cortex/entity-graph').then(m => ({ default: m.EntityGraphView })),
|
|
1741
|
+
{ ssr: false, loading: () => <div className="flex-1 flex items-center justify-center text-gray-500 text-sm">Loading graph...</div> }
|
|
1742
|
+
);
|
|
1743
|
+
|
|
1744
|
+
type Tab = 'dashboard' | 'graph' | 'knowledge' | 'curation' | 'marketplace' | 'settings';
|
|
1745
|
+
|
|
1746
|
+
export default function CortexPage() {
|
|
1747
|
+
const [tab, setTab] = useState<Tab>('dashboard');
|
|
1748
|
+
const [stats, setStats] = useState<any>(null);
|
|
1749
|
+
useEffect(() => {
|
|
1750
|
+
fetch(api('/api/cortex/status'))
|
|
1751
|
+
.then(r => r.json())
|
|
1752
|
+
.then(setStats)
|
|
1753
|
+
.catch(() => {});
|
|
1754
|
+
}, []);
|
|
1755
|
+
|
|
1756
|
+
const tabs: { key: Tab; label: string }[] = [
|
|
1757
|
+
{ key: 'dashboard', label: 'Dashboard' },
|
|
1758
|
+
{ key: 'graph', label: 'Graph' },
|
|
1759
|
+
{ key: 'knowledge', label: 'Knowledge' },
|
|
1760
|
+
{ key: 'curation', label: 'Curation' },
|
|
1761
|
+
{ key: 'marketplace', label: 'Marketplace' },
|
|
1762
|
+
{ key: 'settings', label: 'Settings' },
|
|
1763
|
+
];
|
|
1764
|
+
|
|
1765
|
+
const totalKnowledge = stats
|
|
1766
|
+
? Object.values(stats.layers || {}).reduce((sum: number, l: any) => sum + (l.count || 0), 0)
|
|
1767
|
+
: 0;
|
|
1768
|
+
|
|
1769
|
+
return (
|
|
1770
|
+
<div className="flex flex-col h-screen bg-gray-950">
|
|
1771
|
+
<div className="flex items-center border-b border-white/5 px-4 shrink-0">
|
|
1772
|
+
<div className="flex">
|
|
1773
|
+
{tabs.map(t => (
|
|
1774
|
+
<button
|
|
1775
|
+
key={t.key}
|
|
1776
|
+
onClick={() => setTab(t.key)}
|
|
1777
|
+
className={`px-5 py-3 text-sm font-medium transition-colors border-b-2 ${
|
|
1778
|
+
tab === t.key
|
|
1779
|
+
? 'text-purple-400 border-purple-400'
|
|
1780
|
+
: 'text-gray-500 border-transparent hover:text-gray-300'
|
|
1781
|
+
}`}
|
|
1782
|
+
>
|
|
1783
|
+
{t.label}
|
|
1784
|
+
</button>
|
|
1785
|
+
))}
|
|
1786
|
+
</div>
|
|
1787
|
+
<div className="ml-auto text-xs text-gray-600">
|
|
1788
|
+
{totalKnowledge} knowledge units
|
|
1789
|
+
</div>
|
|
1790
|
+
</div>
|
|
1791
|
+
<div className="flex-1 overflow-y-auto">
|
|
1792
|
+
{tab === 'dashboard' && <CortexDashboard />}
|
|
1793
|
+
{tab === 'graph' && <EntityGraphView />}
|
|
1794
|
+
{tab === 'knowledge' && <KnowledgeTab />}
|
|
1795
|
+
{tab === 'curation' && <CurationTab />}
|
|
1796
|
+
{tab === 'marketplace' && <MarketplaceTab />}
|
|
1797
|
+
{tab === 'settings' && (
|
|
1798
|
+
<div className="p-6 max-w-2xl space-y-8">
|
|
1799
|
+
<CortexSettings />
|
|
1800
|
+
</div>
|
|
1801
|
+
)}
|
|
1802
|
+
</div>
|
|
1803
|
+
</div>
|
|
1804
|
+
);
|
|
1805
|
+
}
|
|
1806
|
+
```
|
|
1807
|
+
|
|
1808
|
+
- [ ] **Step 3: Verify build**
|
|
1809
|
+
|
|
1810
|
+
Run: `npx next build 2>&1 | tail -5`
|
|
1811
|
+
Expected: Build succeeds with no errors
|
|
1812
|
+
|
|
1813
|
+
- [ ] **Step 4: Commit**
|
|
1814
|
+
|
|
1815
|
+
```bash
|
|
1816
|
+
git rm src/components/cortex/context-tab.tsx
|
|
1817
|
+
git add src/app/(desktop)/cortex/page.tsx
|
|
1818
|
+
git commit -m "feat(cortex): wire 6-tab layout, remove context tab"
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
---
|
|
1822
|
+
|
|
1823
|
+
### Task 12: Final Build Verification
|
|
1824
|
+
|
|
1825
|
+
- [ ] **Step 1: Full build**
|
|
1826
|
+
|
|
1827
|
+
Run: `npx next build 2>&1 | tail -20`
|
|
1828
|
+
Expected: Build succeeds, all routes compiled
|
|
1829
|
+
|
|
1830
|
+
- [ ] **Step 2: Verify all new routes are compiled**
|
|
1831
|
+
|
|
1832
|
+
Look for these in the build output:
|
|
1833
|
+
- `/api/cortex/curation/seed`
|
|
1834
|
+
- `/api/cortex/curation/assess`
|
|
1835
|
+
- `/api/cortex/curation/review`
|
|
1836
|
+
- `/api/cortex/curation/refine`
|
|
1837
|
+
- `/api/cortex/curation/publish`
|
|
1838
|
+
- `/api/cortex/marketplace/browse`
|
|
1839
|
+
- `/api/cortex/marketplace/preview`
|
|
1840
|
+
|
|
1841
|
+
- [ ] **Step 3: Commit any build fixes if needed**
|
|
1842
|
+
|
|
1843
|
+
```bash
|
|
1844
|
+
git add -A
|
|
1845
|
+
git commit -m "fix(cortex): resolve build issues from UI integration"
|
|
1846
|
+
```
|