@glwhappen/web-code 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +718 -0
- package/README.de.md +250 -0
- package/README.ja.md +242 -0
- package/README.ko.md +242 -0
- package/README.md +252 -0
- package/README.ru.md +250 -0
- package/README.tr.md +252 -0
- package/README.zh-CN.md +242 -0
- package/dist/api-docs.html +879 -0
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/index-Ct6oPUQk.css +32 -0
- package/dist/assets/index-u6XmIqLb.js +1346 -0
- package/dist/assets/vendor-codemirror-OwyKSvPE.js +41 -0
- package/dist/assets/vendor-react-BGZc9oRE.js +59 -0
- package/dist/assets/vendor-xterm-CJZjLICi.js +66 -0
- package/dist/clear-cache.html +85 -0
- package/dist/convert-icons.md +53 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +9 -0
- package/dist/generate-icons.js +49 -0
- package/dist/icons/claude-ai-icon.svg +1 -0
- package/dist/icons/codex-white.svg +3 -0
- package/dist/icons/codex.svg +3 -0
- package/dist/icons/cursor-white.svg +12 -0
- package/dist/icons/cursor.svg +1 -0
- package/dist/icons/gemini-ai-icon.svg +1 -0
- package/dist/icons/icon-128x128.png +0 -0
- package/dist/icons/icon-128x128.svg +12 -0
- package/dist/icons/icon-144x144.png +0 -0
- package/dist/icons/icon-144x144.svg +12 -0
- package/dist/icons/icon-152x152.png +0 -0
- package/dist/icons/icon-152x152.svg +12 -0
- package/dist/icons/icon-192x192.png +0 -0
- package/dist/icons/icon-192x192.svg +12 -0
- package/dist/icons/icon-384x384.png +0 -0
- package/dist/icons/icon-384x384.svg +12 -0
- package/dist/icons/icon-512x512.png +0 -0
- package/dist/icons/icon-512x512.svg +12 -0
- package/dist/icons/icon-72x72.png +0 -0
- package/dist/icons/icon-72x72.svg +12 -0
- package/dist/icons/icon-96x96.png +0 -0
- package/dist/icons/icon-96x96.svg +12 -0
- package/dist/icons/icon-template.svg +12 -0
- package/dist/index.html +52 -0
- package/dist/logo-128.png +0 -0
- package/dist/logo-256.png +0 -0
- package/dist/logo-32.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-64.png +0 -0
- package/dist/logo.svg +17 -0
- package/dist/manifest.json +61 -0
- package/dist/screenshots/cli-selection.png +0 -0
- package/dist/screenshots/desktop-main.png +0 -0
- package/dist/screenshots/mobile-chat.png +0 -0
- package/dist/screenshots/tools-modal.png +0 -0
- package/dist/sw.js +124 -0
- package/dist-server/server/claude-sdk.js +738 -0
- package/dist-server/server/claude-sdk.js.map +1 -0
- package/dist-server/server/cli.js +641 -0
- package/dist-server/server/cli.js.map +1 -0
- package/dist-server/server/constants/config.js +6 -0
- package/dist-server/server/constants/config.js.map +1 -0
- package/dist-server/server/cursor-cli.js +271 -0
- package/dist-server/server/cursor-cli.js.map +1 -0
- package/dist-server/server/gemini-cli.js +539 -0
- package/dist-server/server/gemini-cli.js.map +1 -0
- package/dist-server/server/gemini-response-handler.js +72 -0
- package/dist-server/server/gemini-response-handler.js.map +1 -0
- package/dist-server/server/index.js +1340 -0
- package/dist-server/server/index.js.map +1 -0
- package/dist-server/server/load-env.js +32 -0
- package/dist-server/server/load-env.js.map +1 -0
- package/dist-server/server/middleware/auth.js +117 -0
- package/dist-server/server/middleware/auth.js.map +1 -0
- package/dist-server/server/modules/database/connection.js +125 -0
- package/dist-server/server/modules/database/connection.js.map +1 -0
- package/dist-server/server/modules/database/index.js +13 -0
- package/dist-server/server/modules/database/index.js.map +1 -0
- package/dist-server/server/modules/database/init-db.js +18 -0
- package/dist-server/server/modules/database/init-db.js.map +1 -0
- package/dist-server/server/modules/database/migrations.js +419 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -0
- package/dist-server/server/modules/database/repositories/api-keys.js +72 -0
- package/dist-server/server/modules/database/repositories/api-keys.js.map +1 -0
- package/dist-server/server/modules/database/repositories/app-config.js +47 -0
- package/dist-server/server/modules/database/repositories/app-config.js.map +1 -0
- package/dist-server/server/modules/database/repositories/credentials.js +68 -0
- package/dist-server/server/modules/database/repositories/credentials.js.map +1 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js +54 -0
- package/dist-server/server/modules/database/repositories/github-tokens.js.map +1 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js +72 -0
- package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js +67 -0
- package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/projects.db.js +185 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js +49 -0
- package/dist-server/server/modules/database/repositories/push-subscriptions.js.map +1 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js +31 -0
- package/dist-server/server/modules/database/repositories/scan-state.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +64 -0
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +150 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/users.js +116 -0
- package/dist-server/server/modules/database/repositories/users.js.map +1 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js +38 -0
- package/dist-server/server/modules/database/repositories/vapid-keys.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +150 -0
- package/dist-server/server/modules/database/schema.js.map +1 -0
- package/dist-server/server/modules/projects/index.js +4 -0
- package/dist-server/server/modules/projects/index.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +225 -0
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +220 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js +83 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js +99 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-star.service.js +60 -0
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js +171 -0
- package/dist-server/server/modules/projects/services/projects-has-taskmaster.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +213 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +129 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js +89 -0
- package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js +99 -0
- package/dist-server/server/modules/projects/tests/project-star.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js +88 -0
- package/dist-server/server/modules/projects/tests/projects-has-taskmaster.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +5 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +104 -0
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js +103 -0
- package/dist-server/server/modules/providers/list/claude/claude-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +116 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +546 -0
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +17 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js +107 -0
- package/dist-server/server/modules/providers/list/codex/codex-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +123 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +513 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +82 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +17 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +118 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js +80 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +105 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +545 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +17 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +254 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js +82 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +312 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +484 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +33 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +17 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +31 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -0
- package/dist-server/server/modules/providers/provider.routes.js +377 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -0
- package/dist-server/server/modules/providers/services/mcp.service.js +69 -0
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js +25 -0
- package/dist-server/server/modules/providers/services/provider-auth.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +984 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +56 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +269 -0
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +179 -0
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/skills.service.js +11 -0
- package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js +14 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js +102 -0
- package/dist-server/server/modules/providers/shared/mcp/mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +250 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/skills.test.js +226 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
- package/dist-server/server/modules/websocket/index.js +3 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +192 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +52 -0
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +360 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-auth.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +36 -0
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js +14 -0
- package/dist-server/server/modules/websocket/services/websocket-state.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js +32 -0
- package/dist-server/server/modules/websocket/services/websocket-writer.service.js.map +1 -0
- package/dist-server/server/openai-codex.js +418 -0
- package/dist-server/server/openai-codex.js.map +1 -0
- package/dist-server/server/routes/admin.js +109 -0
- package/dist-server/server/routes/admin.js.map +1 -0
- package/dist-server/server/routes/agent.js +1145 -0
- package/dist-server/server/routes/agent.js.map +1 -0
- package/dist-server/server/routes/auth.js +123 -0
- package/dist-server/server/routes/auth.js.map +1 -0
- package/dist-server/server/routes/commands.js +487 -0
- package/dist-server/server/routes/commands.js.map +1 -0
- package/dist-server/server/routes/cursor.js +49 -0
- package/dist-server/server/routes/cursor.js.map +1 -0
- package/dist-server/server/routes/gemini.js +25 -0
- package/dist-server/server/routes/gemini.js.map +1 -0
- package/dist-server/server/routes/git.js +1263 -0
- package/dist-server/server/routes/git.js.map +1 -0
- package/dist-server/server/routes/mcp-utils.js +29 -0
- package/dist-server/server/routes/mcp-utils.js.map +1 -0
- package/dist-server/server/routes/plugins.js +266 -0
- package/dist-server/server/routes/plugins.js.map +1 -0
- package/dist-server/server/routes/settings.js +259 -0
- package/dist-server/server/routes/settings.js.map +1 -0
- package/dist-server/server/routes/taskmaster.js +1360 -0
- package/dist-server/server/routes/taskmaster.js.map +1 -0
- package/dist-server/server/routes/user.js +115 -0
- package/dist-server/server/routes/user.js.map +1 -0
- package/dist-server/server/services/notification-orchestrator.js +177 -0
- package/dist-server/server/services/notification-orchestrator.js.map +1 -0
- package/dist-server/server/services/vapid-keys.js +27 -0
- package/dist-server/server/services/vapid-keys.js.map +1 -0
- package/dist-server/server/sessionManager.js +215 -0
- package/dist-server/server/sessionManager.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.js +103 -0
- package/dist-server/server/shared/claude-cli-path.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.test.js +45 -0
- package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
- package/dist-server/server/shared/default-user.js +29 -0
- package/dist-server/server/shared/default-user.js.map +1 -0
- package/dist-server/server/shared/frontmatter.js +16 -0
- package/dist-server/server/shared/frontmatter.js.map +1 -0
- package/dist-server/server/shared/interfaces.js +2 -0
- package/dist-server/server/shared/interfaces.js.map +1 -0
- package/dist-server/server/shared/types.js +2 -0
- package/dist-server/server/shared/types.js.map +1 -0
- package/dist-server/server/shared/utils.js +633 -0
- package/dist-server/server/shared/utils.js.map +1 -0
- package/dist-server/server/utils/colors.js +20 -0
- package/dist-server/server/utils/colors.js.map +1 -0
- package/dist-server/server/utils/commandParser.js +255 -0
- package/dist-server/server/utils/commandParser.js.map +1 -0
- package/dist-server/server/utils/gitConfig.js +36 -0
- package/dist-server/server/utils/gitConfig.js.map +1 -0
- package/dist-server/server/utils/mcp-detector.js +134 -0
- package/dist-server/server/utils/mcp-detector.js.map +1 -0
- package/dist-server/server/utils/plugin-loader.js +413 -0
- package/dist-server/server/utils/plugin-loader.js.map +1 -0
- package/dist-server/server/utils/plugin-process-manager.js +163 -0
- package/dist-server/server/utils/plugin-process-manager.js.map +1 -0
- package/dist-server/server/utils/runtime-paths.js +30 -0
- package/dist-server/server/utils/runtime-paths.js.map +1 -0
- package/dist-server/server/utils/taskmaster-websocket.js +124 -0
- package/dist-server/server/utils/taskmaster-websocket.js.map +1 -0
- package/dist-server/server/utils/url-detection.js +58 -0
- package/dist-server/server/utils/url-detection.js.map +1 -0
- package/dist-server/shared/modelConstants.js +99 -0
- package/dist-server/shared/modelConstants.js.map +1 -0
- package/dist-server/shared/networkHosts.js +20 -0
- package/dist-server/shared/networkHosts.js.map +1 -0
- package/package.json +169 -0
- package/scripts/fix-node-pty.js +67 -0
- package/server/claude-sdk.js +864 -0
- package/server/cli.js +688 -0
- package/server/constants/config.js +5 -0
- package/server/cursor-cli.js +334 -0
- package/server/gemini-cli.js +622 -0
- package/server/gemini-response-handler.js +79 -0
- package/server/index.js +1505 -0
- package/server/load-env.js +34 -0
- package/server/middleware/auth.js +142 -0
- package/server/modules/database/connection.ts +143 -0
- package/server/modules/database/index.ts +12 -0
- package/server/modules/database/init-db.ts +17 -0
- package/server/modules/database/migrations.ts +496 -0
- package/server/modules/database/repositories/api-keys.ts +119 -0
- package/server/modules/database/repositories/app-config.ts +53 -0
- package/server/modules/database/repositories/credentials.ts +106 -0
- package/server/modules/database/repositories/github-tokens.ts +100 -0
- package/server/modules/database/repositories/notification-preferences.ts +103 -0
- package/server/modules/database/repositories/projects.db.integration.test.ts +78 -0
- package/server/modules/database/repositories/projects.db.ts +210 -0
- package/server/modules/database/repositories/push-subscriptions.ts +80 -0
- package/server/modules/database/repositories/scan-state.db.ts +42 -0
- package/server/modules/database/repositories/sessions.db.integration.test.ts +78 -0
- package/server/modules/database/repositories/sessions.db.ts +230 -0
- package/server/modules/database/repositories/users.ts +186 -0
- package/server/modules/database/repositories/vapid-keys.ts +57 -0
- package/server/modules/database/schema.ts +159 -0
- package/server/modules/projects/index.ts +6 -0
- package/server/modules/projects/projects.routes.ts +292 -0
- package/server/modules/projects/services/project-clone.service.ts +327 -0
- package/server/modules/projects/services/project-delete.service.ts +95 -0
- package/server/modules/projects/services/project-management.service.ts +158 -0
- package/server/modules/projects/services/project-star.service.ts +78 -0
- package/server/modules/projects/services/projects-has-taskmaster.service.ts +257 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +355 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +186 -0
- package/server/modules/projects/tests/project-management.service.test.ts +122 -0
- package/server/modules/projects/tests/project-star.service.test.ts +128 -0
- package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +107 -0
- package/server/modules/providers/README.md +346 -0
- package/server/modules/providers/index.ts +5 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +124 -0
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +179 -0
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +642 -0
- package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
- package/server/modules/providers/list/claude/claude.provider.ts +24 -0
- package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -0
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +182 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +589 -0
- package/server/modules/providers/list/codex/codex-skills.provider.ts +100 -0
- package/server/modules/providers/list/codex/codex.provider.ts +24 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -0
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -0
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +155 -0
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +624 -0
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +24 -0
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +307 -0
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -0
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +407 -0
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +552 -0
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +24 -0
- package/server/modules/providers/provider.registry.ts +36 -0
- package/server/modules/providers/provider.routes.ts +488 -0
- package/server/modules/providers/services/mcp.service.ts +94 -0
- package/server/modules/providers/services/provider-auth.service.ts +26 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +1319 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +75 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +318 -0
- package/server/modules/providers/services/sessions.service.ts +240 -0
- package/server/modules/providers/services/skills.service.ts +15 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +29 -0
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -0
- package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
- package/server/modules/providers/tests/mcp.test.ts +293 -0
- package/server/modules/providers/tests/skills.test.ts +446 -0
- package/server/modules/websocket/README.md +267 -0
- package/server/modules/websocket/index.ts +2 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +275 -0
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +489 -0
- package/server/modules/websocket/services/websocket-auth.service.ts +54 -0
- package/server/modules/websocket/services/websocket-server.service.ts +58 -0
- package/server/modules/websocket/services/websocket-state.service.ts +16 -0
- package/server/modules/websocket/services/websocket-writer.service.ts +38 -0
- package/server/openai-codex.js +474 -0
- package/server/routes/admin.js +128 -0
- package/server/routes/agent.js +1246 -0
- package/server/routes/auth.js +144 -0
- package/server/routes/commands.js +556 -0
- package/server/routes/cursor.js +52 -0
- package/server/routes/gemini.js +30 -0
- package/server/routes/git.js +1493 -0
- package/server/routes/mcp-utils.js +31 -0
- package/server/routes/plugins.js +307 -0
- package/server/routes/settings.js +286 -0
- package/server/routes/taskmaster.js +1468 -0
- package/server/routes/user.js +123 -0
- package/server/services/notification-orchestrator.js +228 -0
- package/server/services/vapid-keys.js +36 -0
- package/server/sessionManager.js +248 -0
- package/server/shared/claude-cli-path.test.ts +61 -0
- package/server/shared/claude-cli-path.ts +139 -0
- package/server/shared/default-user.ts +30 -0
- package/server/shared/frontmatter.ts +18 -0
- package/server/shared/interfaces.ts +111 -0
- package/server/shared/types.ts +406 -0
- package/server/shared/utils.ts +763 -0
- package/server/tsconfig.json +36 -0
- package/server/utils/colors.js +21 -0
- package/server/utils/commandParser.js +305 -0
- package/server/utils/gitConfig.js +34 -0
- package/server/utils/mcp-detector.js +147 -0
- package/server/utils/plugin-loader.js +457 -0
- package/server/utils/plugin-process-manager.js +184 -0
- package/server/utils/runtime-paths.js +37 -0
- package/server/utils/taskmaster-websocket.js +135 -0
- package/server/utils/url-detection.js +71 -0
- package/shared/modelConstants.js +107 -0
- package/shared/networkHosts.js +22 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User credentials repository.
|
|
3
|
+
*
|
|
4
|
+
* Manages external service tokens (GitHub, GitLab, Bitbucket, etc.)
|
|
5
|
+
* stored per-user. Each credential has a type discriminator so multiple
|
|
6
|
+
* credential kinds can coexist in the same table.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
10
|
+
import type {
|
|
11
|
+
CreateCredentialResult,
|
|
12
|
+
CredentialPublicRow,
|
|
13
|
+
} from '@/shared/types.js';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Queries
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
export const credentialsDb = {
|
|
20
|
+
/** Stores a new credential and returns a safe (no raw value) result. */
|
|
21
|
+
createCredential(
|
|
22
|
+
userId: number,
|
|
23
|
+
credentialName: string,
|
|
24
|
+
credentialType: string,
|
|
25
|
+
credentialValue: string,
|
|
26
|
+
description: string | null = null
|
|
27
|
+
): CreateCredentialResult {
|
|
28
|
+
const db = getConnection();
|
|
29
|
+
const result = db
|
|
30
|
+
.prepare(
|
|
31
|
+
'INSERT INTO user_credentials (user_id, credential_name, credential_type, credential_value, description) VALUES (?, ?, ?, ?, ?)'
|
|
32
|
+
)
|
|
33
|
+
.run(userId, credentialName, credentialType, credentialValue, description);
|
|
34
|
+
return {
|
|
35
|
+
id: result.lastInsertRowid,
|
|
36
|
+
credentialName,
|
|
37
|
+
credentialType,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lists credentials for a user (excluding raw values).
|
|
43
|
+
* Optionally filters by credential type (e.g. 'github_token').
|
|
44
|
+
*/
|
|
45
|
+
getCredentials(
|
|
46
|
+
userId: number,
|
|
47
|
+
credentialType: string | null = null
|
|
48
|
+
): CredentialPublicRow[] {
|
|
49
|
+
const db = getConnection();
|
|
50
|
+
|
|
51
|
+
if (credentialType) {
|
|
52
|
+
return db
|
|
53
|
+
.prepare(
|
|
54
|
+
'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? AND credential_type = ? ORDER BY created_at DESC'
|
|
55
|
+
)
|
|
56
|
+
.all(userId, credentialType) as CredentialPublicRow[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return db
|
|
60
|
+
.prepare(
|
|
61
|
+
'SELECT id, credential_name, credential_type, description, created_at, is_active FROM user_credentials WHERE user_id = ? ORDER BY created_at DESC'
|
|
62
|
+
)
|
|
63
|
+
.all(userId) as CredentialPublicRow[];
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns the raw credential value for the most recent active
|
|
68
|
+
* credential of the given type, or null if none exists.
|
|
69
|
+
*/
|
|
70
|
+
getActiveCredential(
|
|
71
|
+
userId: number,
|
|
72
|
+
credentialType: string
|
|
73
|
+
): string | null {
|
|
74
|
+
const db = getConnection();
|
|
75
|
+
const row = db
|
|
76
|
+
.prepare(
|
|
77
|
+
'SELECT credential_value FROM user_credentials WHERE user_id = ? AND credential_type = ? AND is_active = 1 ORDER BY created_at DESC LIMIT 1'
|
|
78
|
+
)
|
|
79
|
+
.get(userId, credentialType) as { credential_value: string } | undefined;
|
|
80
|
+
return row?.credential_value ?? null;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/** Permanently removes a credential. Returns true if a row was deleted. */
|
|
84
|
+
deleteCredential(userId: number, credentialId: number): boolean {
|
|
85
|
+
const db = getConnection();
|
|
86
|
+
const result = db
|
|
87
|
+
.prepare('DELETE FROM user_credentials WHERE id = ? AND user_id = ?')
|
|
88
|
+
.run(credentialId, userId);
|
|
89
|
+
return result.changes > 0;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/** Enables or disables a credential without deleting it. */
|
|
93
|
+
toggleCredential(
|
|
94
|
+
userId: number,
|
|
95
|
+
credentialId: number,
|
|
96
|
+
isActive: boolean
|
|
97
|
+
): boolean {
|
|
98
|
+
const db = getConnection();
|
|
99
|
+
const result = db
|
|
100
|
+
.prepare(
|
|
101
|
+
'UPDATE user_credentials SET is_active = ? WHERE id = ? AND user_id = ?'
|
|
102
|
+
)
|
|
103
|
+
.run(isActive ? 1 : 0, credentialId, userId);
|
|
104
|
+
return result.changes > 0;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub tokens repository.
|
|
3
|
+
*
|
|
4
|
+
* Backward-compatible helper layer over generic credentials storage.
|
|
5
|
+
* Tokens are stored in `user_credentials` with `credential_type = 'github_token'`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
9
|
+
import { credentialsDb } from '@/modules/database/repositories/credentials.js';
|
|
10
|
+
import type {
|
|
11
|
+
CredentialPublicRow,
|
|
12
|
+
CreateCredentialResult,
|
|
13
|
+
} from '@/shared/types.js';
|
|
14
|
+
|
|
15
|
+
const GITHUB_TOKEN_TYPE = 'github_token';
|
|
16
|
+
|
|
17
|
+
type CredentialRow = {
|
|
18
|
+
id: number;
|
|
19
|
+
user_id: number;
|
|
20
|
+
credential_name: string;
|
|
21
|
+
credential_type: string;
|
|
22
|
+
credential_value: string;
|
|
23
|
+
description: string | null;
|
|
24
|
+
created_at: string;
|
|
25
|
+
is_active: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type GithubTokenLookup = CredentialRow & {
|
|
29
|
+
github_token: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const githubTokensDb = {
|
|
33
|
+
/** Creates a GitHub token credential entry. */
|
|
34
|
+
createGithubToken(
|
|
35
|
+
userId: number,
|
|
36
|
+
tokenName: string,
|
|
37
|
+
githubToken: string,
|
|
38
|
+
description: string | null = null
|
|
39
|
+
): CreateCredentialResult {
|
|
40
|
+
return credentialsDb.createCredential(
|
|
41
|
+
userId,
|
|
42
|
+
tokenName,
|
|
43
|
+
GITHUB_TOKEN_TYPE,
|
|
44
|
+
githubToken,
|
|
45
|
+
description
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** Returns all GitHub tokens (safe shape: no credential value). */
|
|
50
|
+
getGithubTokens(userId: number): CredentialPublicRow[] {
|
|
51
|
+
return credentialsDb.getCredentials(userId, GITHUB_TOKEN_TYPE);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/** Returns the most recent active GitHub token value for a user. */
|
|
55
|
+
getActiveGithubToken(userId: number): string | null {
|
|
56
|
+
return credentialsDb.getActiveCredential(userId, GITHUB_TOKEN_TYPE);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns a specific active GitHub token row by id/user, including
|
|
61
|
+
* a `github_token` compatibility field.
|
|
62
|
+
*/
|
|
63
|
+
getGithubTokenById(userId: number, tokenId: number): GithubTokenLookup | null {
|
|
64
|
+
const db = getConnection();
|
|
65
|
+
const row = db
|
|
66
|
+
.prepare(
|
|
67
|
+
`SELECT *
|
|
68
|
+
FROM user_credentials
|
|
69
|
+
WHERE id = ? AND user_id = ? AND credential_type = ? AND is_active = 1`
|
|
70
|
+
)
|
|
71
|
+
.get(tokenId, userId, GITHUB_TOKEN_TYPE) as CredentialRow | undefined;
|
|
72
|
+
|
|
73
|
+
if (!row) return null;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...row,
|
|
77
|
+
github_token: row.credential_value,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/** Updates active state for a GitHub token. */
|
|
82
|
+
updateGithubToken(
|
|
83
|
+
userId: number,
|
|
84
|
+
tokenId: number,
|
|
85
|
+
isActive: boolean
|
|
86
|
+
): boolean {
|
|
87
|
+
return credentialsDb.toggleCredential(userId, tokenId, isActive);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/** Deletes a GitHub token. */
|
|
91
|
+
deleteGithubToken(userId: number, tokenId: number): boolean {
|
|
92
|
+
return credentialsDb.deleteCredential(userId, tokenId);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Legacy alias used by existing routes
|
|
96
|
+
toggleGithubToken(userId: number, tokenId: number, isActive: boolean): boolean {
|
|
97
|
+
return githubTokensDb.updateGithubToken(userId, tokenId, isActive);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification preferences repository.
|
|
3
|
+
*
|
|
4
|
+
* Stores per-user notification channel/event preferences as JSON.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
8
|
+
|
|
9
|
+
type NotificationPreferences = {
|
|
10
|
+
channels: {
|
|
11
|
+
inApp: boolean;
|
|
12
|
+
webPush: boolean;
|
|
13
|
+
};
|
|
14
|
+
events: {
|
|
15
|
+
actionRequired: boolean;
|
|
16
|
+
stop: boolean;
|
|
17
|
+
error: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const DEFAULT_NOTIFICATION_PREFERENCES: NotificationPreferences = {
|
|
22
|
+
channels: {
|
|
23
|
+
inApp: false,
|
|
24
|
+
webPush: false,
|
|
25
|
+
},
|
|
26
|
+
events: {
|
|
27
|
+
actionRequired: true,
|
|
28
|
+
stop: true,
|
|
29
|
+
error: true,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function normalizeNotificationPreferences(value: unknown): NotificationPreferences {
|
|
34
|
+
const source = value && typeof value === 'object' ? (value as Record<string, any>) : {};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
channels: {
|
|
38
|
+
inApp: source.channels?.inApp === true,
|
|
39
|
+
webPush: source.channels?.webPush === true,
|
|
40
|
+
},
|
|
41
|
+
events: {
|
|
42
|
+
actionRequired: source.events?.actionRequired !== false,
|
|
43
|
+
stop: source.events?.stop !== false,
|
|
44
|
+
error: source.events?.error !== false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const notificationPreferencesDb = {
|
|
50
|
+
/** Returns the normalized preferences for a user, creating defaults on first read. */
|
|
51
|
+
getNotificationPreferences(userId: number): NotificationPreferences {
|
|
52
|
+
const db = getConnection();
|
|
53
|
+
const row = db
|
|
54
|
+
.prepare(
|
|
55
|
+
'SELECT preferences_json FROM user_notification_preferences WHERE user_id = ?'
|
|
56
|
+
)
|
|
57
|
+
.get(userId) as { preferences_json: string } | undefined;
|
|
58
|
+
|
|
59
|
+
if (!row) {
|
|
60
|
+
const defaults = normalizeNotificationPreferences(DEFAULT_NOTIFICATION_PREFERENCES);
|
|
61
|
+
db.prepare(
|
|
62
|
+
'INSERT INTO user_notification_preferences (user_id, preferences_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)'
|
|
63
|
+
).run(userId, JSON.stringify(defaults));
|
|
64
|
+
return defaults;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let parsed: unknown;
|
|
68
|
+
try {
|
|
69
|
+
parsed = JSON.parse(row.preferences_json);
|
|
70
|
+
} catch {
|
|
71
|
+
parsed = DEFAULT_NOTIFICATION_PREFERENCES;
|
|
72
|
+
}
|
|
73
|
+
return normalizeNotificationPreferences(parsed);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
/** Upserts normalized preferences for a user and returns the stored value. */
|
|
77
|
+
updateNotificationPreferences(
|
|
78
|
+
userId: number,
|
|
79
|
+
preferences: unknown
|
|
80
|
+
): NotificationPreferences {
|
|
81
|
+
const normalized = normalizeNotificationPreferences(preferences);
|
|
82
|
+
const db = getConnection();
|
|
83
|
+
|
|
84
|
+
db.prepare(
|
|
85
|
+
`INSERT INTO user_notification_preferences (user_id, preferences_json, updated_at)
|
|
86
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
87
|
+
ON CONFLICT(user_id) DO UPDATE SET
|
|
88
|
+
preferences_json = excluded.preferences_json,
|
|
89
|
+
updated_at = CURRENT_TIMESTAMP`
|
|
90
|
+
).run(userId, JSON.stringify(normalized));
|
|
91
|
+
|
|
92
|
+
return normalized;
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Legacy aliases used by existing services/routes
|
|
96
|
+
getPreferences(userId: number): NotificationPreferences {
|
|
97
|
+
return notificationPreferencesDb.getNotificationPreferences(userId);
|
|
98
|
+
},
|
|
99
|
+
updatePreferences(userId: number, preferences: unknown): NotificationPreferences {
|
|
100
|
+
return notificationPreferencesDb.updateNotificationPreferences(userId, preferences);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { closeConnection } from '@/modules/database/connection.js';
|
|
8
|
+
import { initializeDatabase } from '@/modules/database/init-db.js';
|
|
9
|
+
import { projectsDb } from '@/modules/database/repositories/projects.db.js';
|
|
10
|
+
import { userDb } from '@/modules/database/repositories/users.js';
|
|
11
|
+
|
|
12
|
+
async function withIsolatedDatabase(
|
|
13
|
+
runTest: (userId: number) => void | Promise<void>,
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
16
|
+
const tempDirectory = await mkdtemp(path.join(tmpdir(), 'projects-db-'));
|
|
17
|
+
const databasePath = path.join(tempDirectory, 'auth.db');
|
|
18
|
+
|
|
19
|
+
closeConnection();
|
|
20
|
+
process.env.DATABASE_PATH = databasePath;
|
|
21
|
+
await initializeDatabase();
|
|
22
|
+
|
|
23
|
+
const created = userDb.createUser('test-user', 'hash');
|
|
24
|
+
const userId = Number(created.id);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await runTest(userId);
|
|
28
|
+
} finally {
|
|
29
|
+
closeConnection();
|
|
30
|
+
if (previousDatabasePath === undefined) {
|
|
31
|
+
delete process.env.DATABASE_PATH;
|
|
32
|
+
} else {
|
|
33
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
34
|
+
}
|
|
35
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test('projectsDb.createProjectPath returns created for fresh paths', async () => {
|
|
40
|
+
await withIsolatedDatabase((userId) => {
|
|
41
|
+
const created = projectsDb.createProjectPath(userId, '/workspace/new-project');
|
|
42
|
+
|
|
43
|
+
assert.equal(created.outcome, 'created');
|
|
44
|
+
assert.ok(created.project);
|
|
45
|
+
assert.equal(created.project?.project_path, '/workspace/new-project');
|
|
46
|
+
assert.equal(created.project?.isArchived, 0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('projectsDb.createProjectPath returns reactivated_archived for archived duplicates', async () => {
|
|
51
|
+
await withIsolatedDatabase((userId) => {
|
|
52
|
+
const initial = projectsDb.createProjectPath(userId, '/workspace/archived-project', 'Archived Project');
|
|
53
|
+
assert.equal(initial.outcome, 'created');
|
|
54
|
+
assert.ok(initial.project);
|
|
55
|
+
|
|
56
|
+
projectsDb.updateProjectIsArchived(userId, '/workspace/archived-project', true);
|
|
57
|
+
|
|
58
|
+
const reused = projectsDb.createProjectPath(userId, '/workspace/archived-project', 'Renamed Project');
|
|
59
|
+
assert.equal(reused.outcome, 'reactivated_archived');
|
|
60
|
+
assert.ok(reused.project);
|
|
61
|
+
assert.equal(reused.project?.project_id, initial.project?.project_id);
|
|
62
|
+
assert.equal(reused.project?.isArchived, 0);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('projectsDb.createProjectPath returns active_conflict for active duplicates', async () => {
|
|
67
|
+
await withIsolatedDatabase((userId) => {
|
|
68
|
+
const initial = projectsDb.createProjectPath(userId, '/workspace/active-project');
|
|
69
|
+
assert.equal(initial.outcome, 'created');
|
|
70
|
+
assert.ok(initial.project);
|
|
71
|
+
|
|
72
|
+
const conflict = projectsDb.createProjectPath(userId, '/workspace/active-project');
|
|
73
|
+
assert.equal(conflict.outcome, 'active_conflict');
|
|
74
|
+
assert.ok(conflict.project);
|
|
75
|
+
assert.equal(conflict.project?.project_id, initial.project?.project_id);
|
|
76
|
+
assert.equal(conflict.project?.isArchived, 0);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
5
|
+
import type { CreateProjectPathResult, ProjectRepositoryRow } from '@/shared/types.js';
|
|
6
|
+
import { normalizeProjectPath } from '@/shared/utils.js';
|
|
7
|
+
|
|
8
|
+
function normalizeProjectDisplayName(projectPath: string, customProjectName: string | null): string {
|
|
9
|
+
const trimmedCustomName = typeof customProjectName === 'string' ? customProjectName.trim() : '';
|
|
10
|
+
if (trimmedCustomName.length > 0) {
|
|
11
|
+
return trimmedCustomName;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const directoryName = path.basename(projectPath);
|
|
15
|
+
return directoryName || projectPath;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const projectsDb = {
|
|
19
|
+
createProjectPath(userId: number, projectPath: string, customProjectName: string | null = null): CreateProjectPathResult {
|
|
20
|
+
const db = getConnection();
|
|
21
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
22
|
+
const normalizedProjectName = normalizeProjectDisplayName(normalizedProjectPath, customProjectName);
|
|
23
|
+
const attemptedId = randomUUID();
|
|
24
|
+
const row = db.prepare(`
|
|
25
|
+
INSERT INTO projects (project_id, user_id, project_path, custom_project_name, isArchived)
|
|
26
|
+
VALUES (?, ?, ?, ?, 0)
|
|
27
|
+
ON CONFLICT(user_id, project_path) DO UPDATE SET
|
|
28
|
+
isArchived = 0
|
|
29
|
+
WHERE projects.isArchived = 1
|
|
30
|
+
RETURNING project_id, project_path, custom_project_name, isStarred, isArchived
|
|
31
|
+
`).get(attemptedId, userId, normalizedProjectPath, normalizedProjectName) as ProjectRepositoryRow | undefined;
|
|
32
|
+
|
|
33
|
+
if (row) {
|
|
34
|
+
return {
|
|
35
|
+
outcome: row.project_id === attemptedId ? 'created' : 'reactivated_archived',
|
|
36
|
+
project: row,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const existingProject = projectsDb.getProjectPath(userId, normalizedProjectPath);
|
|
41
|
+
return {
|
|
42
|
+
outcome: 'active_conflict',
|
|
43
|
+
project: existingProject,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
getProjectPath(userId: number, projectPath: string): ProjectRepositoryRow | null {
|
|
48
|
+
const db = getConnection();
|
|
49
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
50
|
+
const row = db.prepare(`
|
|
51
|
+
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
52
|
+
FROM projects
|
|
53
|
+
WHERE user_id = ? AND project_path = ?
|
|
54
|
+
`).get(userId, normalizedProjectPath) as ProjectRepositoryRow | undefined;
|
|
55
|
+
|
|
56
|
+
return row ?? null;
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getProjectById(userId: number, projectId: string): ProjectRepositoryRow | null {
|
|
60
|
+
const db = getConnection();
|
|
61
|
+
const row = db.prepare(`
|
|
62
|
+
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
63
|
+
FROM projects
|
|
64
|
+
WHERE user_id = ? AND project_id = ?
|
|
65
|
+
`).get(userId, projectId) as ProjectRepositoryRow | undefined;
|
|
66
|
+
|
|
67
|
+
return row ?? null;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the absolute project directory from a database project_id.
|
|
72
|
+
*
|
|
73
|
+
* This is the canonical lookup used after the projectName → projectId migration:
|
|
74
|
+
* API routes receive the DB-assigned `projectId` and must resolve the real folder
|
|
75
|
+
* path through this helper before touching the filesystem. Returns `null` when the
|
|
76
|
+
* project row does not exist so callers can respond with a 404.
|
|
77
|
+
*/
|
|
78
|
+
getProjectPathById(userId: number, projectId: string): string | null {
|
|
79
|
+
const db = getConnection();
|
|
80
|
+
const row = db.prepare(`
|
|
81
|
+
SELECT project_path
|
|
82
|
+
FROM projects
|
|
83
|
+
WHERE user_id = ? AND project_id = ?
|
|
84
|
+
`).get(userId, projectId) as Pick<ProjectRepositoryRow, 'project_path'> | undefined;
|
|
85
|
+
|
|
86
|
+
return row?.project_path ?? null;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
getProjectPaths(userId: number): ProjectRepositoryRow[] {
|
|
90
|
+
const db = getConnection();
|
|
91
|
+
return db.prepare(`
|
|
92
|
+
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
93
|
+
FROM projects
|
|
94
|
+
WHERE user_id = ? AND isArchived = 0
|
|
95
|
+
`).all(userId) as ProjectRepositoryRow[];
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Archived rows are queried separately so archive-focused UIs can present
|
|
100
|
+
* hidden workspaces without reintroducing them into the active sidebar list.
|
|
101
|
+
*/
|
|
102
|
+
getArchivedProjectPaths(userId: number): ProjectRepositoryRow[] {
|
|
103
|
+
const db = getConnection();
|
|
104
|
+
return db.prepare(`
|
|
105
|
+
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
106
|
+
FROM projects
|
|
107
|
+
WHERE user_id = ? AND isArchived = 1
|
|
108
|
+
`).all(userId) as ProjectRepositoryRow[];
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
getCustomProjectName(userId: number, projectPath: string): string | null {
|
|
112
|
+
const db = getConnection();
|
|
113
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
114
|
+
const row = db.prepare(`
|
|
115
|
+
SELECT custom_project_name
|
|
116
|
+
FROM projects
|
|
117
|
+
WHERE user_id = ? AND project_path = ?
|
|
118
|
+
`).get(userId, normalizedProjectPath) as Pick<ProjectRepositoryRow, 'custom_project_name'> | undefined;
|
|
119
|
+
|
|
120
|
+
return row?.custom_project_name ?? null;
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
updateCustomProjectName(userId: number, projectPath: string, customProjectName: string | null): void {
|
|
124
|
+
const db = getConnection();
|
|
125
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
126
|
+
db.prepare(`
|
|
127
|
+
INSERT INTO projects (project_id, user_id, project_path, custom_project_name)
|
|
128
|
+
VALUES (?, ?, ?, ?)
|
|
129
|
+
ON CONFLICT(user_id, project_path) DO UPDATE SET custom_project_name = excluded.custom_project_name
|
|
130
|
+
`).run(randomUUID(), userId, normalizedProjectPath, customProjectName);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
updateCustomProjectNameById(userId: number, projectId: string, customProjectName: string | null): void {
|
|
134
|
+
const db = getConnection();
|
|
135
|
+
db.prepare(`
|
|
136
|
+
UPDATE projects
|
|
137
|
+
SET custom_project_name = ?
|
|
138
|
+
WHERE user_id = ? AND project_id = ?
|
|
139
|
+
`).run(customProjectName, userId, projectId);
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
updateProjectIsStarred(userId: number, projectPath: string, isStarred: boolean): void {
|
|
143
|
+
const db = getConnection();
|
|
144
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
145
|
+
db.prepare(`
|
|
146
|
+
UPDATE projects
|
|
147
|
+
SET isStarred = ?
|
|
148
|
+
WHERE user_id = ? AND project_path = ?
|
|
149
|
+
`).run(isStarred ? 1 : 0, userId, normalizedProjectPath);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
updateProjectIsStarredById(userId: number, projectId: string, isStarred: boolean): void {
|
|
153
|
+
const db = getConnection();
|
|
154
|
+
db.prepare(`
|
|
155
|
+
UPDATE projects
|
|
156
|
+
SET isStarred = ?
|
|
157
|
+
WHERE user_id = ? AND project_id = ?
|
|
158
|
+
`).run(isStarred ? 1 : 0, userId, projectId);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
updateProjectIsArchived(userId: number, projectPath: string, isArchived: boolean): void {
|
|
162
|
+
const db = getConnection();
|
|
163
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
164
|
+
db.prepare(`
|
|
165
|
+
UPDATE projects
|
|
166
|
+
SET isArchived = ?
|
|
167
|
+
WHERE user_id = ? AND project_path = ?
|
|
168
|
+
`).run(isArchived ? 1 : 0, userId, normalizedProjectPath);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
updateProjectIsArchivedById(userId: number, projectId: string, isArchived: boolean): void {
|
|
172
|
+
const db = getConnection();
|
|
173
|
+
db.prepare(`
|
|
174
|
+
UPDATE projects
|
|
175
|
+
SET isArchived = ?
|
|
176
|
+
WHERE user_id = ? AND project_id = ?
|
|
177
|
+
`).run(isArchived ? 1 : 0, userId, projectId);
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
deleteProjectPath(userId: number, projectPath: string): void {
|
|
181
|
+
const db = getConnection();
|
|
182
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
183
|
+
db.prepare(`
|
|
184
|
+
DELETE FROM projects
|
|
185
|
+
WHERE user_id = ? AND project_path = ?
|
|
186
|
+
`).run(userId, normalizedProjectPath);
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
deleteProjectById(userId: number, projectId: string): void {
|
|
190
|
+
const db = getConnection();
|
|
191
|
+
db.prepare(`
|
|
192
|
+
DELETE FROM projects
|
|
193
|
+
WHERE user_id = ? AND project_id = ?
|
|
194
|
+
`).run(userId, projectId);
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
isProjectPathUsedByOthers(userId: number, projectPath: string): { used: boolean; usernames: string[] } {
|
|
198
|
+
const db = getConnection();
|
|
199
|
+
const normalizedProjectPath = normalizeProjectPath(projectPath);
|
|
200
|
+
const rows = db.prepare(`
|
|
201
|
+
SELECT u.username FROM projects p
|
|
202
|
+
JOIN users u ON u.id = p.user_id
|
|
203
|
+
WHERE p.project_path = ? AND p.user_id != ? AND p.isArchived = 0 AND u.is_active = 1
|
|
204
|
+
`).all(normalizedProjectPath, userId) as Array<{ username: string }>;
|
|
205
|
+
return {
|
|
206
|
+
used: rows.length > 0,
|
|
207
|
+
usernames: rows.map(r => r.username),
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push subscriptions repository.
|
|
3
|
+
*
|
|
4
|
+
* Persists browser push subscription endpoints and keys per user.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
8
|
+
|
|
9
|
+
type PushSubscriptionLookupRow = {
|
|
10
|
+
endpoint: string;
|
|
11
|
+
keys_p256dh: string;
|
|
12
|
+
keys_auth: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const pushSubscriptionsDb = {
|
|
16
|
+
/** Upserts a push subscription endpoint for a user. */
|
|
17
|
+
createPushSubscription(
|
|
18
|
+
userId: number,
|
|
19
|
+
endpoint: string,
|
|
20
|
+
keysP256dh: string,
|
|
21
|
+
keysAuth: string
|
|
22
|
+
): void {
|
|
23
|
+
const db = getConnection();
|
|
24
|
+
db.prepare(
|
|
25
|
+
`INSERT INTO push_subscriptions (user_id, endpoint, keys_p256dh, keys_auth)
|
|
26
|
+
VALUES (?, ?, ?, ?)
|
|
27
|
+
ON CONFLICT(endpoint) DO UPDATE SET
|
|
28
|
+
user_id = excluded.user_id,
|
|
29
|
+
keys_p256dh = excluded.keys_p256dh,
|
|
30
|
+
keys_auth = excluded.keys_auth`
|
|
31
|
+
).run(userId, endpoint, keysP256dh, keysAuth);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/** Returns all subscriptions for a user. */
|
|
35
|
+
getPushSubscriptions(userId: number): PushSubscriptionLookupRow[] {
|
|
36
|
+
const db = getConnection();
|
|
37
|
+
return db
|
|
38
|
+
.prepare(
|
|
39
|
+
'SELECT endpoint, keys_p256dh, keys_auth FROM push_subscriptions WHERE user_id = ?'
|
|
40
|
+
)
|
|
41
|
+
.all(userId) as PushSubscriptionLookupRow[];
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/** Deletes one subscription by endpoint. */
|
|
45
|
+
deletePushSubscription(endpoint: string): void {
|
|
46
|
+
const db = getConnection();
|
|
47
|
+
db.prepare('DELETE FROM push_subscriptions WHERE endpoint = ?').run(endpoint);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/** Deletes all subscriptions for a user. */
|
|
51
|
+
deletePushSubscriptionsForUser(userId: number): void {
|
|
52
|
+
const db = getConnection();
|
|
53
|
+
db.prepare('DELETE FROM push_subscriptions WHERE user_id = ?').run(userId);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// Legacy aliases used by existing services/routes
|
|
57
|
+
saveSubscription(
|
|
58
|
+
userId: number,
|
|
59
|
+
endpoint: string,
|
|
60
|
+
keysP256dh: string,
|
|
61
|
+
keysAuth: string
|
|
62
|
+
): void {
|
|
63
|
+
pushSubscriptionsDb.createPushSubscription(
|
|
64
|
+
userId,
|
|
65
|
+
endpoint,
|
|
66
|
+
keysP256dh,
|
|
67
|
+
keysAuth
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
getSubscriptions(userId: number): PushSubscriptionLookupRow[] {
|
|
71
|
+
return pushSubscriptionsDb.getPushSubscriptions(userId);
|
|
72
|
+
},
|
|
73
|
+
removeSubscription(endpoint: string): void {
|
|
74
|
+
pushSubscriptionsDb.deletePushSubscription(endpoint);
|
|
75
|
+
},
|
|
76
|
+
removeAllForUser(userId: number): void {
|
|
77
|
+
pushSubscriptionsDb.deletePushSubscriptionsForUser(userId);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|