devchain-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +53 -0
- package/README.md +74 -0
- package/dist/cli.js +782 -0
- package/dist/drizzle/0000_flippant_the_spike.sql +220 -0
- package/dist/drizzle/0001_shiny_snowbird.sql +7 -0
- package/dist/drizzle/0002_high_zemo.sql +19 -0
- package/dist/drizzle/0003_noisy_scarecrow.sql +21 -0
- package/dist/drizzle/0004_mcp_metadata.sql +5 -0
- package/dist/drizzle/0005_agent_profile_instructions.sql +3 -0
- package/dist/drizzle/0006_wakeful_marvex.sql +15 -0
- package/dist/drizzle/0007_agent_profile_options.sql +7 -0
- package/dist/drizzle/0008_event_logs.sql +31 -0
- package/dist/drizzle/0009_chat_tables.sql +42 -0
- package/dist/drizzle/0010_amazing_the_initiative.sql +11 -0
- package/dist/drizzle/0011_curved_payback.sql +16 -0
- package/dist/drizzle/0012_terminal_activity.sql +8 -0
- package/dist/drizzle/0013_chat_activities.sql +18 -0
- package/dist/drizzle/0014_chat_activities_start_msg_nullable.sql +5 -0
- package/dist/drizzle/0015_projects_is_template.sql +4 -0
- package/dist/drizzle/0016_agent_profiles_project_scoped.sql +4 -0
- package/dist/drizzle/meta/0000_snapshot.json +1388 -0
- package/dist/drizzle/meta/0001_snapshot.json +1434 -0
- package/dist/drizzle/meta/0002_snapshot.json +1434 -0
- package/dist/drizzle/meta/0003_snapshot.json +1583 -0
- package/dist/drizzle/meta/0004_snapshot.json +1605 -0
- package/dist/drizzle/meta/0005_snapshot.json +1612 -0
- package/dist/drizzle/meta/0006_snapshot.json +1742 -0
- package/dist/drizzle/meta/0007_snapshot.json +1742 -0
- package/dist/drizzle/meta/0008_snapshot.json +1897 -0
- package/dist/drizzle/meta/0009_snapshot.json +2202 -0
- package/dist/drizzle/meta/0010_snapshot.json +2283 -0
- package/dist/drizzle/meta/0011_snapshot.json +2407 -0
- package/dist/drizzle/meta/_journal.json +125 -0
- package/dist/lib/interactive-cli.js +279 -0
- package/dist/server/app.module.d.ts +4 -0
- package/dist/server/app.module.js +64 -0
- package/dist/server/app.module.js.map +1 -0
- package/dist/server/common/config/env.config.d.ts +23 -0
- package/dist/server/common/config/env.config.js +60 -0
- package/dist/server/common/config/env.config.js.map +1 -0
- package/dist/server/common/config/feature-flags.d.ts +5 -0
- package/dist/server/common/config/feature-flags.js +8 -0
- package/dist/server/common/config/feature-flags.js.map +1 -0
- package/dist/server/common/errors/error-types.d.ts +30 -0
- package/dist/server/common/errors/error-types.js +63 -0
- package/dist/server/common/errors/error-types.js.map +1 -0
- package/dist/server/common/filters/http-exception.filter.d.ts +4 -0
- package/dist/server/common/filters/http-exception.filter.js +72 -0
- package/dist/server/common/filters/http-exception.filter.js.map +1 -0
- package/dist/server/common/filters/ws-exception.filter.d.ts +5 -0
- package/dist/server/common/filters/ws-exception.filter.js +102 -0
- package/dist/server/common/filters/ws-exception.filter.js.map +1 -0
- package/dist/server/common/logging/logger.d.ts +3 -0
- package/dist/server/common/logging/logger.js +29 -0
- package/dist/server/common/logging/logger.js.map +1 -0
- package/dist/server/common/middleware/request-id.middleware.d.ts +12 -0
- package/dist/server/common/middleware/request-id.middleware.js +24 -0
- package/dist/server/common/middleware/request-id.middleware.js.map +1 -0
- package/dist/server/main.d.ts +1 -0
- package/dist/server/main.js +94 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/modules/agents/agents.module.d.ts +2 -0
- package/dist/server/modules/agents/agents.module.js +22 -0
- package/dist/server/modules/agents/agents.module.js.map +1 -0
- package/dist/server/modules/agents/controllers/agents.controller.d.ts +12 -0
- package/dist/server/modules/agents/controllers/agents.controller.js +115 -0
- package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -0
- package/dist/server/modules/chat/chat.module.d.ts +2 -0
- package/dist/server/modules/chat/chat.module.js +28 -0
- package/dist/server/modules/chat/chat.module.js.map +1 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.d.ts +8 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.js +66 -0
- package/dist/server/modules/chat/controllers/chat-settings.controller.js.map +1 -0
- package/dist/server/modules/chat/controllers/chat.controller.d.ts +15 -0
- package/dist/server/modules/chat/controllers/chat.controller.js +152 -0
- package/dist/server/modules/chat/controllers/chat.controller.js.map +1 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.d.ts +31 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.js +17 -0
- package/dist/server/modules/chat/dtos/chat-settings.dto.js.map +1 -0
- package/dist/server/modules/chat/dtos/chat.dto.d.ts +309 -0
- package/dist/server/modules/chat/dtos/chat.dto.js +80 -0
- package/dist/server/modules/chat/dtos/chat.dto.js.map +1 -0
- package/dist/server/modules/chat/services/chat-settings.service.d.ts +12 -0
- package/dist/server/modules/chat/services/chat-settings.service.js +120 -0
- package/dist/server/modules/chat/services/chat-settings.service.js.map +1 -0
- package/dist/server/modules/chat/services/chat.service.d.ts +45 -0
- package/dist/server/modules/chat/services/chat.service.js +730 -0
- package/dist/server/modules/chat/services/chat.service.js.map +1 -0
- package/dist/server/modules/chat/services/invite-template.util.d.ts +17 -0
- package/dist/server/modules/chat/services/invite-template.util.js +70 -0
- package/dist/server/modules/chat/services/invite-template.util.js.map +1 -0
- package/dist/server/modules/core/controllers/health.controller.d.ts +7 -0
- package/dist/server/modules/core/controllers/health.controller.js +39 -0
- package/dist/server/modules/core/controllers/health.controller.js.map +1 -0
- package/dist/server/modules/core/controllers/preflight.controller.d.ts +10 -0
- package/dist/server/modules/core/controllers/preflight.controller.js +58 -0
- package/dist/server/modules/core/controllers/preflight.controller.js.map +1 -0
- package/dist/server/modules/core/core.module.d.ts +2 -0
- package/dist/server/modules/core/core.module.js +27 -0
- package/dist/server/modules/core/core.module.js.map +1 -0
- package/dist/server/modules/core/services/preflight.service.d.ts +44 -0
- package/dist/server/modules/core/services/preflight.service.js +400 -0
- package/dist/server/modules/core/services/preflight.service.js.map +1 -0
- package/dist/server/modules/documents/controllers/documents.controller.d.ts +12 -0
- package/dist/server/modules/documents/controllers/documents.controller.js +173 -0
- package/dist/server/modules/documents/controllers/documents.controller.js.map +1 -0
- package/dist/server/modules/documents/documents.module.d.ts +2 -0
- package/dist/server/modules/documents/documents.module.js +22 -0
- package/dist/server/modules/documents/documents.module.js.map +1 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.d.ts +10 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.js +94 -0
- package/dist/server/modules/epics/controllers/epic-comments.controller.js.map +1 -0
- package/dist/server/modules/epics/controllers/epics.controller.d.ts +16 -0
- package/dist/server/modules/epics/controllers/epics.controller.js +201 -0
- package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -0
- package/dist/server/modules/epics/epics.module.d.ts +2 -0
- package/dist/server/modules/epics/epics.module.js +28 -0
- package/dist/server/modules/epics/epics.module.js.map +1 -0
- package/dist/server/modules/epics/services/epics.service.d.ts +34 -0
- package/dist/server/modules/epics/services/epics.service.js +251 -0
- package/dist/server/modules/epics/services/epics.service.js.map +1 -0
- package/dist/server/modules/events/catalog/epic.assigned.d.ts +36 -0
- package/dist/server/modules/events/catalog/epic.assigned.js +19 -0
- package/dist/server/modules/events/catalog/epic.assigned.js.map +1 -0
- package/dist/server/modules/events/catalog/index.d.ts +71 -0
- package/dist/server/modules/events/catalog/index.js +15 -0
- package/dist/server/modules/events/catalog/index.js.map +1 -0
- package/dist/server/modules/events/catalog/session.crashed.d.ts +15 -0
- package/dist/server/modules/events/catalog/session.crashed.js +12 -0
- package/dist/server/modules/events/catalog/session.crashed.js.map +1 -0
- package/dist/server/modules/events/catalog/session.started.d.ts +21 -0
- package/dist/server/modules/events/catalog/session.started.js +14 -0
- package/dist/server/modules/events/catalog/session.started.js.map +1 -0
- package/dist/server/modules/events/catalog/session.stopped.d.ts +12 -0
- package/dist/server/modules/events/catalog/session.stopped.js +11 -0
- package/dist/server/modules/events/catalog/session.stopped.js.map +1 -0
- package/dist/server/modules/events/controllers/event-log.controller.d.ts +7 -0
- package/dist/server/modules/events/controllers/event-log.controller.js +75 -0
- package/dist/server/modules/events/controllers/event-log.controller.js.map +1 -0
- package/dist/server/modules/events/dtos/event-log.dto.d.ts +33 -0
- package/dist/server/modules/events/dtos/event-log.dto.js +3 -0
- package/dist/server/modules/events/dtos/event-log.dto.js.map +1 -0
- package/dist/server/modules/events/events.module.d.ts +2 -0
- package/dist/server/modules/events/events.module.js +45 -0
- package/dist/server/modules/events/events.module.js.map +1 -0
- package/dist/server/modules/events/index.d.ts +6 -0
- package/dist/server/modules/events/index.js +23 -0
- package/dist/server/modules/events/index.js.map +1 -0
- package/dist/server/modules/events/services/event-log.service.d.ts +38 -0
- package/dist/server/modules/events/services/event-log.service.js +272 -0
- package/dist/server/modules/events/services/event-log.service.js.map +1 -0
- package/dist/server/modules/events/services/events-stream.service.d.ts +23 -0
- package/dist/server/modules/events/services/events-stream.service.js +45 -0
- package/dist/server/modules/events/services/events-stream.service.js.map +1 -0
- package/dist/server/modules/events/services/events.service.d.ts +14 -0
- package/dist/server/modules/events/services/events.service.js +65 -0
- package/dist/server/modules/events/services/events.service.js.map +1 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.d.ts +20 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.js +47 -0
- package/dist/server/modules/events/subscribers/chat-message-broadcaster.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.d.ts +24 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js +81 -0
- package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.d.ts +25 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js +223 -0
- package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js.map +1 -0
- package/dist/server/modules/events/subscribers/index.d.ts +2 -0
- package/dist/server/modules/events/subscribers/index.js +12 -0
- package/dist/server/modules/events/subscribers/index.js.map +1 -0
- package/dist/server/modules/fs/fs.controller.d.ts +18 -0
- package/dist/server/modules/fs/fs.controller.js +56 -0
- package/dist/server/modules/fs/fs.controller.js.map +1 -0
- package/dist/server/modules/fs/fs.module.d.ts +2 -0
- package/dist/server/modules/fs/fs.module.js +20 -0
- package/dist/server/modules/fs/fs.module.js.map +1 -0
- package/dist/server/modules/mcp/constants.d.ts +9 -0
- package/dist/server/modules/mcp/constants.js +38 -0
- package/dist/server/modules/mcp/constants.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.d.ts +16 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js +790 -0
- package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.d.ts +12 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +676 -0
- package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.d.ts +50 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.js +115 -0
- package/dist/server/modules/mcp/controllers/mcp-test.controller.js.map +1 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +1094 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.js +257 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.d.ts +21 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.js +141 -0
- package/dist/server/modules/mcp/gateways/mcp.gateway.js.map +1 -0
- package/dist/server/modules/mcp/mcp.module.d.ts +2 -0
- package/dist/server/modules/mcp/mcp.module.js +83 -0
- package/dist/server/modules/mcp/mcp.module.js.map +1 -0
- package/dist/server/modules/mcp/services/instructions-resolver.d.ts +24 -0
- package/dist/server/modules/mcp/services/instructions-resolver.js +203 -0
- package/dist/server/modules/mcp/services/instructions-resolver.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.d.ts +52 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.js +327 -0
- package/dist/server/modules/mcp/services/mcp-provider-registration.service.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp-server.service.d.ts +6 -0
- package/dist/server/modules/mcp/services/mcp-server.service.js +26 -0
- package/dist/server/modules/mcp/services/mcp-server.service.js.map +1 -0
- package/dist/server/modules/mcp/services/mcp.service.d.ts +69 -0
- package/dist/server/modules/mcp/services/mcp.service.js +2056 -0
- package/dist/server/modules/mcp/services/mcp.service.js.map +1 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.d.ts +9 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.js +51 -0
- package/dist/server/modules/mcp/services/terminal-activity.service.js.map +1 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.d.ts +45 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.js +188 -0
- package/dist/server/modules/profiles/controllers/profiles.controller.js.map +1 -0
- package/dist/server/modules/profiles/dto.d.ts +75 -0
- package/dist/server/modules/profiles/dto.js +24 -0
- package/dist/server/modules/profiles/dto.js.map +1 -0
- package/dist/server/modules/profiles/profiles.module.d.ts +2 -0
- package/dist/server/modules/profiles/profiles.module.js +22 -0
- package/dist/server/modules/profiles/profiles.module.js.map +1 -0
- package/dist/server/modules/projects/controllers/projects.controller.d.ts +131 -0
- package/dist/server/modules/projects/controllers/projects.controller.js +196 -0
- package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -0
- package/dist/server/modules/projects/projects.module.d.ts +2 -0
- package/dist/server/modules/projects/projects.module.js +26 -0
- package/dist/server/modules/projects/projects.module.js.map +1 -0
- package/dist/server/modules/projects/services/projects.service.d.ts +138 -0
- package/dist/server/modules/projects/services/projects.service.js +564 -0
- package/dist/server/modules/projects/services/projects.service.js.map +1 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.d.ts +11 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.js +112 -0
- package/dist/server/modules/prompts/controllers/prompts.controller.js.map +1 -0
- package/dist/server/modules/prompts/prompts.module.d.ts +2 -0
- package/dist/server/modules/prompts/prompts.module.js +22 -0
- package/dist/server/modules/prompts/prompts.module.js.map +1 -0
- package/dist/server/modules/providers/adapters/claude.adapter.d.ts +9 -0
- package/dist/server/modules/providers/adapters/claude.adapter.js +46 -0
- package/dist/server/modules/providers/adapters/claude.adapter.js.map +1 -0
- package/dist/server/modules/providers/adapters/codex.adapter.d.ts +9 -0
- package/dist/server/modules/providers/adapters/codex.adapter.js +46 -0
- package/dist/server/modules/providers/adapters/codex.adapter.js.map +1 -0
- package/dist/server/modules/providers/adapters/index.d.ts +4 -0
- package/dist/server/modules/providers/adapters/index.js +21 -0
- package/dist/server/modules/providers/adapters/index.js.map +1 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.d.ts +8 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.js +42 -0
- package/dist/server/modules/providers/adapters/provider-adapter.factory.js.map +1 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.d.ts +18 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.js +3 -0
- package/dist/server/modules/providers/adapters/provider-adapter.interface.js.map +1 -0
- package/dist/server/modules/providers/controllers/providers.controller.d.ts +33 -0
- package/dist/server/modules/providers/controllers/providers.controller.js +399 -0
- package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -0
- package/dist/server/modules/providers/providers.module.d.ts +2 -0
- package/dist/server/modules/providers/providers.module.js +27 -0
- package/dist/server/modules/providers/providers.module.js.map +1 -0
- package/dist/server/modules/records/controllers/records.controller.d.ts +13 -0
- package/dist/server/modules/records/controllers/records.controller.js +83 -0
- package/dist/server/modules/records/controllers/records.controller.js.map +1 -0
- package/dist/server/modules/records/records.module.d.ts +2 -0
- package/dist/server/modules/records/records.module.js +22 -0
- package/dist/server/modules/records/records.module.js.map +1 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +13 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js +93 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +67 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.js +18 -0
- package/dist/server/modules/sessions/dtos/sessions.dto.js.map +1 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.d.ts +15 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.js +103 -0
- package/dist/server/modules/sessions/services/activity-tracker.service.js.map +1 -0
- package/dist/server/modules/sessions/services/sessions.service.d.ts +49 -0
- package/dist/server/modules/sessions/services/sessions.service.js +511 -0
- package/dist/server/modules/sessions/services/sessions.service.js.map +1 -0
- package/dist/server/modules/sessions/sessions.module.d.ts +2 -0
- package/dist/server/modules/sessions/sessions.module.js +36 -0
- package/dist/server/modules/sessions/sessions.module.js.map +1 -0
- package/dist/server/modules/sessions/utils/profile-options.d.ts +4 -0
- package/dist/server/modules/sessions/utils/profile-options.js +70 -0
- package/dist/server/modules/sessions/utils/profile-options.js.map +1 -0
- package/dist/server/modules/sessions/utils/template-renderer.d.ts +27 -0
- package/dist/server/modules/sessions/utils/template-renderer.js +38 -0
- package/dist/server/modules/sessions/utils/template-renderer.js.map +1 -0
- package/dist/server/modules/settings/controllers/settings.controller.d.ts +10 -0
- package/dist/server/modules/settings/controllers/settings.controller.js +77 -0
- package/dist/server/modules/settings/controllers/settings.controller.js.map +1 -0
- package/dist/server/modules/settings/dtos/settings.dto.d.ts +104 -0
- package/dist/server/modules/settings/dtos/settings.dto.js +44 -0
- package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -0
- package/dist/server/modules/settings/services/settings.service.d.ts +20 -0
- package/dist/server/modules/settings/services/settings.service.js +310 -0
- package/dist/server/modules/settings/services/settings.service.js.map +1 -0
- package/dist/server/modules/settings/settings.module.d.ts +2 -0
- package/dist/server/modules/settings/settings.module.js +26 -0
- package/dist/server/modules/settings/settings.module.js.map +1 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.d.ts +17 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.js +124 -0
- package/dist/server/modules/statuses/controllers/statuses.controller.js.map +1 -0
- package/dist/server/modules/statuses/statuses.module.d.ts +2 -0
- package/dist/server/modules/statuses/statuses.module.js +22 -0
- package/dist/server/modules/statuses/statuses.module.js.map +1 -0
- package/dist/server/modules/storage/db/db.config.d.ts +5 -0
- package/dist/server/modules/storage/db/db.config.js +19 -0
- package/dist/server/modules/storage/db/db.config.js.map +1 -0
- package/dist/server/modules/storage/db/db.module.d.ts +2 -0
- package/dist/server/modules/storage/db/db.module.js +22 -0
- package/dist/server/modules/storage/db/db.module.js.map +1 -0
- package/dist/server/modules/storage/db/db.provider.d.ts +3 -0
- package/dist/server/modules/storage/db/db.provider.js +61 -0
- package/dist/server/modules/storage/db/db.provider.js.map +1 -0
- package/dist/server/modules/storage/db/migrate.d.ts +1 -0
- package/dist/server/modules/storage/db/migrate.js +32 -0
- package/dist/server/modules/storage/db/migrate.js.map +1 -0
- package/dist/server/modules/storage/db/schema.d.ts +4058 -0
- package/dist/server/modules/storage/db/schema.js +393 -0
- package/dist/server/modules/storage/db/schema.js.map +1 -0
- package/dist/server/modules/storage/db/sqlite-raw.d.ts +3 -0
- package/dist/server/modules/storage/db/sqlite-raw.js +8 -0
- package/dist/server/modules/storage/db/sqlite-raw.js.map +1 -0
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +193 -0
- package/dist/server/modules/storage/interfaces/storage.interface.js +5 -0
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -0
- package/dist/server/modules/storage/local/local-storage.service.d.ts +105 -0
- package/dist/server/modules/storage/local/local-storage.service.js +2069 -0
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -0
- package/dist/server/modules/storage/models/domain.models.d.ts +152 -0
- package/dist/server/modules/storage/models/domain.models.js +3 -0
- package/dist/server/modules/storage/models/domain.models.js.map +1 -0
- package/dist/server/modules/storage/storage.module.d.ts +2 -0
- package/dist/server/modules/storage/storage.module.js +29 -0
- package/dist/server/modules/storage/storage.module.js.map +1 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.d.ts +103 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.js +50 -0
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.js.map +1 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +83 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.js +622 -0
- package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -0
- package/dist/server/modules/terminal/services/pty.service.d.ts +29 -0
- package/dist/server/modules/terminal/services/pty.service.js +236 -0
- package/dist/server/modules/terminal/services/pty.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.d.ts +40 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.js +356 -0
- package/dist/server/modules/terminal/services/terminal-emulator.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.d.ts +29 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.js +233 -0
- package/dist/server/modules/terminal/services/terminal-seed.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.d.ts +5 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.js +36 -0
- package/dist/server/modules/terminal/services/terminal-send-coordinator.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.d.ts +17 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.js +88 -0
- package/dist/server/modules/terminal/services/terminal-stream.service.js.map +1 -0
- package/dist/server/modules/terminal/services/tmux.service.d.ts +42 -0
- package/dist/server/modules/terminal/services/tmux.service.js +318 -0
- package/dist/server/modules/terminal/services/tmux.service.js.map +1 -0
- package/dist/server/modules/terminal/terminal.module.d.ts +2 -0
- package/dist/server/modules/terminal/terminal.module.js +47 -0
- package/dist/server/modules/terminal/terminal.module.js.map +1 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.d.ts +8 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js +48 -0
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js.map +1 -0
- package/dist/server/modules/terminal/utils/control-keys.d.ts +3 -0
- package/dist/server/modules/terminal/utils/control-keys.js +30 -0
- package/dist/server/modules/terminal/utils/control-keys.js.map +1 -0
- package/dist/server/modules/ui/ui.controller.d.ts +4 -0
- package/dist/server/modules/ui/ui.controller.js +55 -0
- package/dist/server/modules/ui/ui.controller.js.map +1 -0
- package/dist/server/modules/ui/ui.module.d.ts +2 -0
- package/dist/server/modules/ui/ui.module.js +22 -0
- package/dist/server/modules/ui/ui.module.js.map +1 -0
- package/dist/server/test-setup.d.ts +1 -0
- package/dist/server/test-setup.js +14 -0
- package/dist/server/test-setup.js.map +1 -0
- package/dist/server/tsconfig.tsbuildinfo +1 -0
- package/dist/server/ui/assets/index-DFChYYFN.css +32 -0
- package/dist/server/ui/assets/index-DpXRypHy.js +636 -0
- package/dist/server/ui/index.html +26 -0
- package/dist/templates/simple-codex.json +134 -0
- package/package.json +105 -0
- package/prebuilds/README.md +7 -0
|
@@ -0,0 +1,2069 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.LocalStorageService = void 0;
|
|
49
|
+
const common_1 = require("@nestjs/common");
|
|
50
|
+
const better_sqlite3_1 = require("drizzle-orm/better-sqlite3");
|
|
51
|
+
const db_provider_1 = require("../db/db.provider");
|
|
52
|
+
const error_types_1 = require("../../../common/errors/error-types");
|
|
53
|
+
const logger_1 = require("../../../common/logging/logger");
|
|
54
|
+
const sqlite_raw_1 = require("../db/sqlite-raw");
|
|
55
|
+
const feature_flags_1 = require("../../../common/config/feature-flags");
|
|
56
|
+
const logger = (0, logger_1.createLogger)('LocalStorageService');
|
|
57
|
+
let LocalStorageService = class LocalStorageService {
|
|
58
|
+
constructor(db) {
|
|
59
|
+
this.db = db;
|
|
60
|
+
logger.info('LocalStorageService initialized');
|
|
61
|
+
}
|
|
62
|
+
getFeatureFlags() {
|
|
63
|
+
return { ...feature_flags_1.DEFAULT_FEATURE_FLAGS };
|
|
64
|
+
}
|
|
65
|
+
async ensureValidEpicParent(projectId, parentId, childId) {
|
|
66
|
+
if (!parentId) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (childId && parentId === childId) {
|
|
70
|
+
throw new error_types_1.ValidationError('An epic cannot be its own parent.', {
|
|
71
|
+
epicId: childId,
|
|
72
|
+
parentId,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const parent = await this.getEpic(parentId);
|
|
76
|
+
if (parent.projectId !== projectId) {
|
|
77
|
+
throw new error_types_1.ValidationError('Parent epic must belong to the same project.', {
|
|
78
|
+
projectId,
|
|
79
|
+
parentProjectId: parent.projectId,
|
|
80
|
+
parentId,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (parent.parentId) {
|
|
84
|
+
throw new error_types_1.ValidationError('Cannot assign a sub-epic as a parent (one-level hierarchy).', {
|
|
85
|
+
parentId,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (childId) {
|
|
89
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
90
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
91
|
+
const descendants = await this.db
|
|
92
|
+
.select({ id: epics.id })
|
|
93
|
+
.from(epics)
|
|
94
|
+
.where(eq(epics.parentId, childId));
|
|
95
|
+
if (descendants.some((row) => row.id === parentId)) {
|
|
96
|
+
throw new error_types_1.ValidationError('Cannot assign a descendant as the parent epic.', {
|
|
97
|
+
parentId,
|
|
98
|
+
epicId: childId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async ensureValidAgent(projectId, agentId) {
|
|
104
|
+
if (!agentId) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const agent = await this.getAgent(agentId);
|
|
108
|
+
if (agent.projectId !== projectId) {
|
|
109
|
+
throw new error_types_1.ValidationError('Agent must belong to the same project as the epic.', {
|
|
110
|
+
projectId,
|
|
111
|
+
agentProjectId: agent.projectId,
|
|
112
|
+
agentId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async createProject(data) {
|
|
117
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
118
|
+
const now = new Date().toISOString();
|
|
119
|
+
const project = {
|
|
120
|
+
id: randomUUID(),
|
|
121
|
+
...data,
|
|
122
|
+
isTemplate: data.isTemplate ?? false,
|
|
123
|
+
createdAt: now,
|
|
124
|
+
updatedAt: now,
|
|
125
|
+
};
|
|
126
|
+
const { projects, statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
127
|
+
await this.db.transaction(async (tx) => {
|
|
128
|
+
await tx.insert(projects).values({
|
|
129
|
+
id: project.id,
|
|
130
|
+
name: project.name,
|
|
131
|
+
description: project.description,
|
|
132
|
+
rootPath: project.rootPath,
|
|
133
|
+
isTemplate: project.isTemplate,
|
|
134
|
+
createdAt: project.createdAt,
|
|
135
|
+
updatedAt: project.updatedAt,
|
|
136
|
+
});
|
|
137
|
+
const defaultStatuses = [
|
|
138
|
+
{ label: 'Proposed', color: '#6c757d', position: 0 },
|
|
139
|
+
{ label: 'In Progress', color: '#007bff', position: 1 },
|
|
140
|
+
{ label: 'Review', color: '#ffc107', position: 2 },
|
|
141
|
+
{ label: 'Done', color: '#28a745', position: 3 },
|
|
142
|
+
{ label: 'Blocked', color: '#dc3545', position: 4 },
|
|
143
|
+
];
|
|
144
|
+
for (const status of defaultStatuses) {
|
|
145
|
+
await tx.insert(statuses).values({
|
|
146
|
+
id: randomUUID(),
|
|
147
|
+
projectId: project.id,
|
|
148
|
+
label: status.label,
|
|
149
|
+
color: status.color,
|
|
150
|
+
position: status.position,
|
|
151
|
+
createdAt: now,
|
|
152
|
+
updatedAt: now,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
logger.info({ projectId: project.id }, 'Created project with default statuses (transactional)');
|
|
157
|
+
return project;
|
|
158
|
+
}
|
|
159
|
+
async createProjectWithTemplate(data, template) {
|
|
160
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
161
|
+
const now = new Date().toISOString();
|
|
162
|
+
const project = {
|
|
163
|
+
id: randomUUID(),
|
|
164
|
+
...data,
|
|
165
|
+
isTemplate: data.isTemplate ?? false,
|
|
166
|
+
createdAt: now,
|
|
167
|
+
updatedAt: now,
|
|
168
|
+
};
|
|
169
|
+
const { projects, statuses, prompts, agentProfiles, agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
170
|
+
const statusIdMap = {};
|
|
171
|
+
const promptIdMap = {};
|
|
172
|
+
const profileIdMap = {};
|
|
173
|
+
const agentIdMap = {};
|
|
174
|
+
const createdPrompts = [];
|
|
175
|
+
const sqlite = (0, sqlite_raw_1.getRawSqliteClient)(this.db);
|
|
176
|
+
if (!sqlite || typeof sqlite.exec !== 'function') {
|
|
177
|
+
throw new error_types_1.StorageError('Unable to access underlying SQLite client for transaction control');
|
|
178
|
+
}
|
|
179
|
+
sqlite.exec('BEGIN IMMEDIATE TRANSACTION');
|
|
180
|
+
try {
|
|
181
|
+
await this.db.insert(projects).values({
|
|
182
|
+
id: project.id,
|
|
183
|
+
name: project.name,
|
|
184
|
+
description: project.description,
|
|
185
|
+
rootPath: project.rootPath,
|
|
186
|
+
isTemplate: project.isTemplate,
|
|
187
|
+
createdAt: project.createdAt,
|
|
188
|
+
updatedAt: project.updatedAt,
|
|
189
|
+
});
|
|
190
|
+
for (const s of template.statuses.sort((a, b) => a.position - b.position)) {
|
|
191
|
+
const statusId = randomUUID();
|
|
192
|
+
await this.db.insert(statuses).values({
|
|
193
|
+
id: statusId,
|
|
194
|
+
projectId: project.id,
|
|
195
|
+
label: s.label,
|
|
196
|
+
color: s.color,
|
|
197
|
+
position: s.position,
|
|
198
|
+
createdAt: now,
|
|
199
|
+
updatedAt: now,
|
|
200
|
+
});
|
|
201
|
+
if (s.id)
|
|
202
|
+
statusIdMap[s.id] = statusId;
|
|
203
|
+
}
|
|
204
|
+
for (const p of template.prompts) {
|
|
205
|
+
const promptId = randomUUID();
|
|
206
|
+
await this.db.insert(prompts).values({
|
|
207
|
+
id: promptId,
|
|
208
|
+
projectId: project.id,
|
|
209
|
+
title: p.title,
|
|
210
|
+
content: p.content ?? '',
|
|
211
|
+
version: 1,
|
|
212
|
+
createdAt: now,
|
|
213
|
+
updatedAt: now,
|
|
214
|
+
});
|
|
215
|
+
if (p.id)
|
|
216
|
+
promptIdMap[p.id] = promptId;
|
|
217
|
+
createdPrompts.push({ id: promptId, title: p.title });
|
|
218
|
+
}
|
|
219
|
+
for (const prof of template.profiles) {
|
|
220
|
+
const profileId = randomUUID();
|
|
221
|
+
await this.db.insert(agentProfiles).values({
|
|
222
|
+
id: profileId,
|
|
223
|
+
projectId: project.id,
|
|
224
|
+
name: prof.name,
|
|
225
|
+
providerId: prof.providerId,
|
|
226
|
+
options: prof.options,
|
|
227
|
+
systemPrompt: null,
|
|
228
|
+
instructions: prof.instructions,
|
|
229
|
+
temperature: prof.temperature,
|
|
230
|
+
maxTokens: prof.maxTokens,
|
|
231
|
+
createdAt: now,
|
|
232
|
+
updatedAt: now,
|
|
233
|
+
});
|
|
234
|
+
if (prof.id)
|
|
235
|
+
profileIdMap[prof.id] = profileId;
|
|
236
|
+
}
|
|
237
|
+
for (const a of template.agents) {
|
|
238
|
+
const oldProfileId = a.profileId ?? '';
|
|
239
|
+
const newProfileId = oldProfileId && profileIdMap[oldProfileId] ? profileIdMap[oldProfileId] : undefined;
|
|
240
|
+
if (!newProfileId) {
|
|
241
|
+
throw new error_types_1.ValidationError(`Profile mapping missing for agent ${a.name}`, {
|
|
242
|
+
profileId: oldProfileId || null,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const agentId = randomUUID();
|
|
246
|
+
await this.db.insert(agents).values({
|
|
247
|
+
id: agentId,
|
|
248
|
+
projectId: project.id,
|
|
249
|
+
name: a.name,
|
|
250
|
+
profileId: newProfileId,
|
|
251
|
+
createdAt: now,
|
|
252
|
+
updatedAt: now,
|
|
253
|
+
});
|
|
254
|
+
if (a.id)
|
|
255
|
+
agentIdMap[a.id] = agentId;
|
|
256
|
+
}
|
|
257
|
+
sqlite.exec('COMMIT');
|
|
258
|
+
logger.info({ projectId: project.id, counts: template }, 'Created project with template (transactional)');
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
try {
|
|
262
|
+
sqlite.exec('ROLLBACK');
|
|
263
|
+
logger.info({ projectId: project.id }, 'Transaction rolled back successfully');
|
|
264
|
+
}
|
|
265
|
+
catch (rollbackError) {
|
|
266
|
+
logger.error({ rollbackError }, 'Failed to rollback transaction');
|
|
267
|
+
}
|
|
268
|
+
logger.error({ error, projectId: project.id }, 'Transaction failed');
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
project,
|
|
273
|
+
imported: {
|
|
274
|
+
prompts: template.prompts.length,
|
|
275
|
+
profiles: template.profiles.length,
|
|
276
|
+
agents: template.agents.length,
|
|
277
|
+
statuses: template.statuses.length,
|
|
278
|
+
},
|
|
279
|
+
mappings: {
|
|
280
|
+
promptIdMap,
|
|
281
|
+
profileIdMap,
|
|
282
|
+
agentIdMap,
|
|
283
|
+
statusIdMap,
|
|
284
|
+
},
|
|
285
|
+
initialPromptSet: false,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async getProject(id) {
|
|
289
|
+
const { projects } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
290
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
291
|
+
const result = await this.db.select().from(projects).where(eq(projects.id, id)).limit(1);
|
|
292
|
+
if (!result[0]) {
|
|
293
|
+
throw new error_types_1.NotFoundError('Project', id);
|
|
294
|
+
}
|
|
295
|
+
const row = result[0];
|
|
296
|
+
return {
|
|
297
|
+
id: row.id,
|
|
298
|
+
name: row.name,
|
|
299
|
+
description: row.description ?? null,
|
|
300
|
+
rootPath: row.rootPath,
|
|
301
|
+
isTemplate: Boolean(row.isTemplate ?? false),
|
|
302
|
+
createdAt: row.createdAt,
|
|
303
|
+
updatedAt: row.updatedAt,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
async findProjectByPath(path) {
|
|
307
|
+
const { projects } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
308
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
309
|
+
const result = await this.db
|
|
310
|
+
.select()
|
|
311
|
+
.from(projects)
|
|
312
|
+
.where(eq(projects.rootPath, path))
|
|
313
|
+
.limit(1);
|
|
314
|
+
if (!result[0])
|
|
315
|
+
return null;
|
|
316
|
+
const row = result[0];
|
|
317
|
+
return {
|
|
318
|
+
id: row.id,
|
|
319
|
+
name: row.name,
|
|
320
|
+
description: row.description ?? null,
|
|
321
|
+
rootPath: row.rootPath,
|
|
322
|
+
isTemplate: Boolean(row.isTemplate ?? false),
|
|
323
|
+
createdAt: row.createdAt,
|
|
324
|
+
updatedAt: row.updatedAt,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
async listProjects(options = {}) {
|
|
328
|
+
const { projects } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
329
|
+
const { sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
330
|
+
const limit = options.limit || 100;
|
|
331
|
+
const offset = options.offset || 0;
|
|
332
|
+
const items = await this.db.select().from(projects).limit(limit).offset(offset);
|
|
333
|
+
const countResult = await this.db.select({ count: sql `count(*)` }).from(projects);
|
|
334
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
335
|
+
const mapped = items.map((row) => ({
|
|
336
|
+
id: row.id,
|
|
337
|
+
name: row.name,
|
|
338
|
+
description: row.description ?? null,
|
|
339
|
+
rootPath: row.rootPath,
|
|
340
|
+
isTemplate: Boolean(row.isTemplate ?? false),
|
|
341
|
+
createdAt: row.createdAt,
|
|
342
|
+
updatedAt: row.updatedAt,
|
|
343
|
+
}));
|
|
344
|
+
return {
|
|
345
|
+
items: mapped,
|
|
346
|
+
total,
|
|
347
|
+
limit,
|
|
348
|
+
offset,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
async updateProject(id, data) {
|
|
352
|
+
const { projects } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
353
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
354
|
+
const now = new Date().toISOString();
|
|
355
|
+
await this.db
|
|
356
|
+
.update(projects)
|
|
357
|
+
.set({ ...data, updatedAt: now })
|
|
358
|
+
.where(eq(projects.id, id));
|
|
359
|
+
return this.getProject(id);
|
|
360
|
+
}
|
|
361
|
+
async deleteProject(id) {
|
|
362
|
+
const { projects, chatThreads, chatMessages, chatMembers, chatMessageTargets, chatMessageReads, chatThreadSessionInvites, chatActivities, sessions, transcripts, epicComments, records, recordTags, epicTags, epics, documents, documentTags, prompts, promptTags, agentProfilePrompts, agents, agentProfiles, tags, statuses, } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
363
|
+
const { eq, inArray } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
364
|
+
const projectEpics = await this.db
|
|
365
|
+
.select({ id: epics.id })
|
|
366
|
+
.from(epics)
|
|
367
|
+
.where(eq(epics.projectId, id));
|
|
368
|
+
const epicIds = projectEpics.map((e) => e.id);
|
|
369
|
+
const projectChatThreads = await this.db
|
|
370
|
+
.select({ id: chatThreads.id })
|
|
371
|
+
.from(chatThreads)
|
|
372
|
+
.where(eq(chatThreads.projectId, id));
|
|
373
|
+
const threadIds = projectChatThreads.map((t) => t.id);
|
|
374
|
+
const projectMessages = threadIds.length > 0
|
|
375
|
+
? await this.db
|
|
376
|
+
.select({ id: chatMessages.id })
|
|
377
|
+
.from(chatMessages)
|
|
378
|
+
.where(inArray(chatMessages.threadId, threadIds))
|
|
379
|
+
: [];
|
|
380
|
+
const messageIds = projectMessages.map((m) => m.id);
|
|
381
|
+
const projectAgents = await this.db
|
|
382
|
+
.select({ id: agents.id })
|
|
383
|
+
.from(agents)
|
|
384
|
+
.where(eq(agents.projectId, id));
|
|
385
|
+
const agentIds = projectAgents.map((a) => a.id);
|
|
386
|
+
const projectDocs = await this.db
|
|
387
|
+
.select({ id: documents.id })
|
|
388
|
+
.from(documents)
|
|
389
|
+
.where(eq(documents.projectId, id));
|
|
390
|
+
const docIds = projectDocs.map((d) => d.id);
|
|
391
|
+
const projectPrompts = await this.db
|
|
392
|
+
.select({ id: prompts.id })
|
|
393
|
+
.from(prompts)
|
|
394
|
+
.where(eq(prompts.projectId, id));
|
|
395
|
+
const promptIds = projectPrompts.map((p) => p.id);
|
|
396
|
+
const projectProfiles = await this.db
|
|
397
|
+
.select({ id: agentProfiles.id })
|
|
398
|
+
.from(agentProfiles)
|
|
399
|
+
.where(eq(agentProfiles.projectId, id));
|
|
400
|
+
const profileIds = projectProfiles.map((p) => p.id);
|
|
401
|
+
const projectTags = await this.db
|
|
402
|
+
.select({ id: tags.id })
|
|
403
|
+
.from(tags)
|
|
404
|
+
.where(eq(tags.projectId, id));
|
|
405
|
+
const tagIds = projectTags.map((t) => t.id);
|
|
406
|
+
const projectSessions = agentIds.length > 0
|
|
407
|
+
? await this.db
|
|
408
|
+
.select({ id: sessions.id })
|
|
409
|
+
.from(sessions)
|
|
410
|
+
.where(inArray(sessions.agentId, agentIds))
|
|
411
|
+
: [];
|
|
412
|
+
const sessionIds = projectSessions.map((s) => s.id);
|
|
413
|
+
if (messageIds.length > 0) {
|
|
414
|
+
await this.db.delete(chatMessageReads).where(inArray(chatMessageReads.messageId, messageIds));
|
|
415
|
+
await this.db
|
|
416
|
+
.delete(chatMessageTargets)
|
|
417
|
+
.where(inArray(chatMessageTargets.messageId, messageIds));
|
|
418
|
+
await this.db
|
|
419
|
+
.delete(chatThreadSessionInvites)
|
|
420
|
+
.where(inArray(chatThreadSessionInvites.inviteMessageId, messageIds));
|
|
421
|
+
}
|
|
422
|
+
if (agentIds.length > 0) {
|
|
423
|
+
await this.db.delete(chatMessageReads).where(inArray(chatMessageReads.agentId, agentIds));
|
|
424
|
+
await this.db.delete(chatMessageTargets).where(inArray(chatMessageTargets.agentId, agentIds));
|
|
425
|
+
await this.db
|
|
426
|
+
.delete(chatThreadSessionInvites)
|
|
427
|
+
.where(inArray(chatThreadSessionInvites.agentId, agentIds));
|
|
428
|
+
await this.db.delete(chatActivities).where(inArray(chatActivities.agentId, agentIds));
|
|
429
|
+
await this.db.delete(chatMembers).where(inArray(chatMembers.agentId, agentIds));
|
|
430
|
+
}
|
|
431
|
+
if (messageIds.length > 0) {
|
|
432
|
+
await this.db.delete(chatMessages).where(inArray(chatMessages.threadId, threadIds));
|
|
433
|
+
}
|
|
434
|
+
if (threadIds.length > 0) {
|
|
435
|
+
await this.db.delete(chatThreads).where(inArray(chatThreads.id, threadIds));
|
|
436
|
+
}
|
|
437
|
+
if (sessionIds.length > 0) {
|
|
438
|
+
await this.db.delete(transcripts).where(inArray(transcripts.sessionId, sessionIds));
|
|
439
|
+
await this.db.delete(sessions).where(inArray(sessions.id, sessionIds));
|
|
440
|
+
}
|
|
441
|
+
if (epicIds.length > 0) {
|
|
442
|
+
await this.db.delete(epicComments).where(inArray(epicComments.epicId, epicIds));
|
|
443
|
+
const projectRecords = await this.db
|
|
444
|
+
.select({ id: records.id })
|
|
445
|
+
.from(records)
|
|
446
|
+
.where(inArray(records.epicId, epicIds));
|
|
447
|
+
const recordIds = projectRecords.map((r) => r.id);
|
|
448
|
+
if (recordIds.length > 0) {
|
|
449
|
+
await this.db.delete(recordTags).where(inArray(recordTags.recordId, recordIds));
|
|
450
|
+
await this.db.delete(records).where(inArray(records.id, recordIds));
|
|
451
|
+
}
|
|
452
|
+
await this.db.delete(epicTags).where(inArray(epicTags.epicId, epicIds));
|
|
453
|
+
}
|
|
454
|
+
if (epicIds.length > 0) {
|
|
455
|
+
await this.db.delete(epics).where(inArray(epics.id, epicIds));
|
|
456
|
+
}
|
|
457
|
+
if (docIds.length > 0) {
|
|
458
|
+
await this.db.delete(documentTags).where(inArray(documentTags.documentId, docIds));
|
|
459
|
+
await this.db.delete(documents).where(inArray(documents.id, docIds));
|
|
460
|
+
}
|
|
461
|
+
if (promptIds.length > 0) {
|
|
462
|
+
await this.db.delete(promptTags).where(inArray(promptTags.promptId, promptIds));
|
|
463
|
+
await this.db
|
|
464
|
+
.delete(agentProfilePrompts)
|
|
465
|
+
.where(inArray(agentProfilePrompts.promptId, promptIds));
|
|
466
|
+
await this.db.delete(prompts).where(inArray(prompts.id, promptIds));
|
|
467
|
+
}
|
|
468
|
+
if (agentIds.length > 0) {
|
|
469
|
+
await this.db.delete(agents).where(inArray(agents.id, agentIds));
|
|
470
|
+
}
|
|
471
|
+
if (profileIds.length > 0) {
|
|
472
|
+
await this.db
|
|
473
|
+
.delete(agentProfilePrompts)
|
|
474
|
+
.where(inArray(agentProfilePrompts.profileId, profileIds));
|
|
475
|
+
await this.db.delete(agentProfiles).where(inArray(agentProfiles.id, profileIds));
|
|
476
|
+
}
|
|
477
|
+
if (tagIds.length > 0) {
|
|
478
|
+
await this.db.delete(tags).where(inArray(tags.id, tagIds));
|
|
479
|
+
}
|
|
480
|
+
await this.db.delete(statuses).where(eq(statuses.projectId, id));
|
|
481
|
+
await this.db.delete(projects).where(eq(projects.id, id));
|
|
482
|
+
logger.info({ projectId: id }, 'Deleted project and all related records');
|
|
483
|
+
}
|
|
484
|
+
async createStatus(data) {
|
|
485
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
486
|
+
const now = new Date().toISOString();
|
|
487
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
488
|
+
const status = {
|
|
489
|
+
id: randomUUID(),
|
|
490
|
+
...data,
|
|
491
|
+
createdAt: now,
|
|
492
|
+
updatedAt: now,
|
|
493
|
+
};
|
|
494
|
+
await this.db.insert(statuses).values({
|
|
495
|
+
id: status.id,
|
|
496
|
+
projectId: status.projectId,
|
|
497
|
+
label: status.label,
|
|
498
|
+
color: status.color,
|
|
499
|
+
position: status.position,
|
|
500
|
+
createdAt: status.createdAt,
|
|
501
|
+
updatedAt: status.updatedAt,
|
|
502
|
+
});
|
|
503
|
+
return status;
|
|
504
|
+
}
|
|
505
|
+
async getStatus(id) {
|
|
506
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
507
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
508
|
+
const result = await this.db.select().from(statuses).where(eq(statuses.id, id)).limit(1);
|
|
509
|
+
if (!result[0]) {
|
|
510
|
+
throw new error_types_1.NotFoundError('Status', id);
|
|
511
|
+
}
|
|
512
|
+
return result[0];
|
|
513
|
+
}
|
|
514
|
+
async listStatuses(projectId, options = {}) {
|
|
515
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
516
|
+
const { eq, asc, sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
517
|
+
const limit = options.limit || 100;
|
|
518
|
+
const offset = options.offset || 0;
|
|
519
|
+
const items = await this.db
|
|
520
|
+
.select()
|
|
521
|
+
.from(statuses)
|
|
522
|
+
.where(eq(statuses.projectId, projectId))
|
|
523
|
+
.orderBy(asc(statuses.position))
|
|
524
|
+
.limit(limit)
|
|
525
|
+
.offset(offset);
|
|
526
|
+
const totalResult = await this.db
|
|
527
|
+
.select({ count: sql `count(*)` })
|
|
528
|
+
.from(statuses)
|
|
529
|
+
.where(eq(statuses.projectId, projectId));
|
|
530
|
+
const total = Number(totalResult[0]?.count ?? 0);
|
|
531
|
+
return {
|
|
532
|
+
items: items,
|
|
533
|
+
total,
|
|
534
|
+
limit,
|
|
535
|
+
offset,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
async findStatusByName(projectId, name) {
|
|
539
|
+
const normalized = name.trim().toLowerCase();
|
|
540
|
+
if (!normalized) {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
544
|
+
const { and, eq, sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
545
|
+
const result = await this.db
|
|
546
|
+
.select()
|
|
547
|
+
.from(statuses)
|
|
548
|
+
.where(and(eq(statuses.projectId, projectId), sql `lower(${statuses.label}) = ${normalized}`))
|
|
549
|
+
.limit(1);
|
|
550
|
+
return result[0] ? result[0] : null;
|
|
551
|
+
}
|
|
552
|
+
async updateStatus(id, data) {
|
|
553
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
554
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
555
|
+
const now = new Date().toISOString();
|
|
556
|
+
await this.db
|
|
557
|
+
.update(statuses)
|
|
558
|
+
.set({ ...data, updatedAt: now })
|
|
559
|
+
.where(eq(statuses.id, id));
|
|
560
|
+
return this.getStatus(id);
|
|
561
|
+
}
|
|
562
|
+
async deleteStatus(id) {
|
|
563
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
564
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
565
|
+
await this.db.delete(statuses).where(eq(statuses.id, id));
|
|
566
|
+
}
|
|
567
|
+
async createEpic(data) {
|
|
568
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
569
|
+
const now = new Date().toISOString();
|
|
570
|
+
const { epics, epicTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
571
|
+
const epicId = randomUUID();
|
|
572
|
+
await this.ensureValidEpicParent(data.projectId, data.parentId ?? null, epicId);
|
|
573
|
+
await this.ensureValidAgent(data.projectId, data.agentId ?? null);
|
|
574
|
+
const epic = {
|
|
575
|
+
id: epicId,
|
|
576
|
+
projectId: data.projectId,
|
|
577
|
+
title: data.title,
|
|
578
|
+
description: data.description ?? null,
|
|
579
|
+
statusId: data.statusId,
|
|
580
|
+
parentId: data.parentId ?? null,
|
|
581
|
+
agentId: data.agentId ?? null,
|
|
582
|
+
version: 1,
|
|
583
|
+
data: data.data ?? null,
|
|
584
|
+
tags: data.tags ?? [],
|
|
585
|
+
createdAt: now,
|
|
586
|
+
updatedAt: now,
|
|
587
|
+
};
|
|
588
|
+
await this.db.insert(epics).values({
|
|
589
|
+
id: epic.id,
|
|
590
|
+
projectId: epic.projectId,
|
|
591
|
+
title: epic.title,
|
|
592
|
+
description: epic.description,
|
|
593
|
+
statusId: epic.statusId,
|
|
594
|
+
parentId: epic.parentId,
|
|
595
|
+
agentId: epic.agentId,
|
|
596
|
+
version: epic.version,
|
|
597
|
+
data: epic.data ? JSON.stringify(epic.data) : null,
|
|
598
|
+
createdAt: epic.createdAt,
|
|
599
|
+
updatedAt: epic.updatedAt,
|
|
600
|
+
});
|
|
601
|
+
if (epic.tags.length) {
|
|
602
|
+
for (const tagName of epic.tags) {
|
|
603
|
+
const { eq, and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
604
|
+
let tag = await this.db
|
|
605
|
+
.select()
|
|
606
|
+
.from(tags)
|
|
607
|
+
.where(and(eq(tags.name, tagName), or(eq(tags.projectId, data.projectId), isNull(tags.projectId))))
|
|
608
|
+
.limit(1);
|
|
609
|
+
if (!tag[0]) {
|
|
610
|
+
const newTag = await this.createTag({ projectId: data.projectId, name: tagName });
|
|
611
|
+
tag = [newTag];
|
|
612
|
+
}
|
|
613
|
+
await this.db.insert(epicTags).values({
|
|
614
|
+
epicId: epic.id,
|
|
615
|
+
tagId: tag[0].id,
|
|
616
|
+
createdAt: now,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return epic;
|
|
621
|
+
}
|
|
622
|
+
async getEpic(id) {
|
|
623
|
+
const { epics, epicTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
624
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
625
|
+
const result = await this.db.select().from(epics).where(eq(epics.id, id)).limit(1);
|
|
626
|
+
if (!result[0]) {
|
|
627
|
+
throw new error_types_1.NotFoundError('Epic', id);
|
|
628
|
+
}
|
|
629
|
+
const epicTagsResult = await this.db
|
|
630
|
+
.select({ tag: tags })
|
|
631
|
+
.from(epicTags)
|
|
632
|
+
.innerJoin(tags, eq(epicTags.tagId, tags.id))
|
|
633
|
+
.where(eq(epicTags.epicId, id));
|
|
634
|
+
return {
|
|
635
|
+
...result[0],
|
|
636
|
+
tags: epicTagsResult.map((et) => et.tag.name),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
async listEpics(projectId, options = {}) {
|
|
640
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
641
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
642
|
+
const limit = options.limit || 100;
|
|
643
|
+
const offset = options.offset || 0;
|
|
644
|
+
const items = await this.db
|
|
645
|
+
.select()
|
|
646
|
+
.from(epics)
|
|
647
|
+
.where(eq(epics.projectId, projectId))
|
|
648
|
+
.limit(limit)
|
|
649
|
+
.offset(offset);
|
|
650
|
+
const itemsWithTags = await Promise.all(items.map((item) => this.getEpic(item.id)));
|
|
651
|
+
return {
|
|
652
|
+
items: itemsWithTags,
|
|
653
|
+
total: items.length,
|
|
654
|
+
limit,
|
|
655
|
+
offset,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
async listEpicsByStatus(statusId, options = {}) {
|
|
659
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
660
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
661
|
+
const limit = options.limit || 100;
|
|
662
|
+
const offset = options.offset || 0;
|
|
663
|
+
const items = await this.db
|
|
664
|
+
.select()
|
|
665
|
+
.from(epics)
|
|
666
|
+
.where(eq(epics.statusId, statusId))
|
|
667
|
+
.limit(limit)
|
|
668
|
+
.offset(offset);
|
|
669
|
+
const itemsWithTags = await Promise.all(items.map((item) => this.getEpic(item.id)));
|
|
670
|
+
return {
|
|
671
|
+
items: itemsWithTags,
|
|
672
|
+
total: items.length,
|
|
673
|
+
limit,
|
|
674
|
+
offset,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
async listProjectEpics(projectId, options = {}) {
|
|
678
|
+
const { epics, statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
679
|
+
const { eq, and, sql, desc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
680
|
+
const limit = options.limit ?? 100;
|
|
681
|
+
const offset = options.offset ?? 0;
|
|
682
|
+
const conditions = [eq(epics.projectId, projectId)];
|
|
683
|
+
if (options.q) {
|
|
684
|
+
const search = options.q.trim().toLowerCase();
|
|
685
|
+
if (search.length) {
|
|
686
|
+
const pattern = `%${search}%`;
|
|
687
|
+
conditions.push(sql `(lower(${epics.title}) LIKE ${pattern} OR lower(ifnull(${epics.description}, '')) LIKE ${pattern})`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (options.statusId) {
|
|
691
|
+
conditions.push(eq(epics.statusId, options.statusId));
|
|
692
|
+
}
|
|
693
|
+
const listType = (options.type ?? 'active').toLowerCase();
|
|
694
|
+
let archivedFilter = null;
|
|
695
|
+
if (listType === 'active') {
|
|
696
|
+
archivedFilter = sql `lower(${statuses.label}) NOT LIKE '%archiv%'`;
|
|
697
|
+
}
|
|
698
|
+
else if (listType === 'archived') {
|
|
699
|
+
archivedFilter = sql `lower(${statuses.label}) LIKE '%archiv%'`;
|
|
700
|
+
}
|
|
701
|
+
if (archivedFilter) {
|
|
702
|
+
conditions.push(archivedFilter);
|
|
703
|
+
}
|
|
704
|
+
const whereClause = and(...conditions);
|
|
705
|
+
const totalResult = await this.db
|
|
706
|
+
.select({ count: sql `count(*)` })
|
|
707
|
+
.from(epics)
|
|
708
|
+
.innerJoin(statuses, eq(statuses.id, epics.statusId))
|
|
709
|
+
.where(whereClause);
|
|
710
|
+
const total = Number(totalResult[0]?.count ?? 0);
|
|
711
|
+
const rows = await this.db
|
|
712
|
+
.select({ id: epics.id })
|
|
713
|
+
.from(epics)
|
|
714
|
+
.innerJoin(statuses, eq(statuses.id, epics.statusId))
|
|
715
|
+
.where(whereClause)
|
|
716
|
+
.orderBy(desc(epics.updatedAt))
|
|
717
|
+
.limit(limit)
|
|
718
|
+
.offset(offset);
|
|
719
|
+
const items = await Promise.all(rows.map((row) => this.getEpic(row.id)));
|
|
720
|
+
return {
|
|
721
|
+
items,
|
|
722
|
+
total,
|
|
723
|
+
limit,
|
|
724
|
+
offset,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
async listAssignedEpics(projectId, options) {
|
|
728
|
+
if (!options.agentName?.trim()) {
|
|
729
|
+
throw new error_types_1.ValidationError('agentName is required to list assigned epics.', {
|
|
730
|
+
projectId,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
734
|
+
const { and, eq, sql, desc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
735
|
+
const limit = options.limit ?? 100;
|
|
736
|
+
const offset = options.offset ?? 0;
|
|
737
|
+
const agent = await this.getAgentByName(projectId, options.agentName);
|
|
738
|
+
const whereClause = and(eq(epics.projectId, projectId), eq(epics.agentId, agent.id));
|
|
739
|
+
const totalResult = await this.db
|
|
740
|
+
.select({ count: sql `count(*)` })
|
|
741
|
+
.from(epics)
|
|
742
|
+
.where(whereClause);
|
|
743
|
+
const total = Number(totalResult[0]?.count ?? 0);
|
|
744
|
+
const rows = await this.db
|
|
745
|
+
.select({ id: epics.id })
|
|
746
|
+
.from(epics)
|
|
747
|
+
.where(whereClause)
|
|
748
|
+
.orderBy(desc(epics.updatedAt))
|
|
749
|
+
.limit(limit)
|
|
750
|
+
.offset(offset);
|
|
751
|
+
const items = await Promise.all(rows.map((row) => this.getEpic(row.id)));
|
|
752
|
+
return {
|
|
753
|
+
items,
|
|
754
|
+
total,
|
|
755
|
+
limit,
|
|
756
|
+
offset,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
async createEpicForProject(projectId, input) {
|
|
760
|
+
const { statuses } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
761
|
+
const { eq, asc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
762
|
+
let statusId = input.statusId ?? null;
|
|
763
|
+
if (statusId) {
|
|
764
|
+
const status = await this.getStatus(statusId);
|
|
765
|
+
if (status.projectId !== projectId) {
|
|
766
|
+
throw new error_types_1.ValidationError('Status must belong to the target project.', {
|
|
767
|
+
statusId,
|
|
768
|
+
projectId,
|
|
769
|
+
statusProjectId: status.projectId,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
const defaultStatusResult = await this.db
|
|
775
|
+
.select({ id: statuses.id })
|
|
776
|
+
.from(statuses)
|
|
777
|
+
.where(eq(statuses.projectId, projectId))
|
|
778
|
+
.orderBy(asc(statuses.position))
|
|
779
|
+
.limit(1);
|
|
780
|
+
const defaultStatus = defaultStatusResult[0];
|
|
781
|
+
if (!defaultStatus) {
|
|
782
|
+
throw new error_types_1.ValidationError('Project has no statuses configured.', { projectId });
|
|
783
|
+
}
|
|
784
|
+
statusId = defaultStatus.id;
|
|
785
|
+
}
|
|
786
|
+
let agentId = input.agentId ?? null;
|
|
787
|
+
if (!agentId && input.agentName?.trim()) {
|
|
788
|
+
const agent = await this.getAgentByName(projectId, input.agentName);
|
|
789
|
+
agentId = agent.id;
|
|
790
|
+
}
|
|
791
|
+
await this.ensureValidAgent(projectId, agentId);
|
|
792
|
+
await this.ensureValidEpicParent(projectId, input.parentId ?? null);
|
|
793
|
+
return this.createEpic({
|
|
794
|
+
projectId,
|
|
795
|
+
title: input.title,
|
|
796
|
+
description: input.description ?? null,
|
|
797
|
+
statusId,
|
|
798
|
+
parentId: input.parentId ?? null,
|
|
799
|
+
agentId,
|
|
800
|
+
tags: input.tags ?? [],
|
|
801
|
+
data: null,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
async updateEpic(id, data, expectedVersion) {
|
|
805
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
806
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
807
|
+
const now = new Date().toISOString();
|
|
808
|
+
const current = await this.getEpic(id);
|
|
809
|
+
if (current.version !== expectedVersion) {
|
|
810
|
+
throw new error_types_1.OptimisticLockError('Epic', id, {
|
|
811
|
+
expectedVersion,
|
|
812
|
+
actualVersion: current.version,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (data.parentId !== undefined) {
|
|
816
|
+
await this.ensureValidEpicParent(current.projectId, data.parentId ?? null, id);
|
|
817
|
+
}
|
|
818
|
+
if (data.agentId !== undefined) {
|
|
819
|
+
await this.ensureValidAgent(current.projectId, data.agentId ?? null);
|
|
820
|
+
}
|
|
821
|
+
const updateData = { ...data };
|
|
822
|
+
if (data.data !== undefined) {
|
|
823
|
+
updateData.data = JSON.stringify(data.data);
|
|
824
|
+
}
|
|
825
|
+
for (const key of Object.keys(updateData)) {
|
|
826
|
+
if (updateData[key] === undefined) {
|
|
827
|
+
delete updateData[key];
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
await this.db
|
|
831
|
+
.update(epics)
|
|
832
|
+
.set({ ...updateData, version: expectedVersion + 1, updatedAt: now })
|
|
833
|
+
.where(eq(epics.id, id));
|
|
834
|
+
return this.getEpic(id);
|
|
835
|
+
}
|
|
836
|
+
async deleteEpic(id) {
|
|
837
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
838
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
839
|
+
const subEpics = await this.db
|
|
840
|
+
.select({ id: epics.id })
|
|
841
|
+
.from(epics)
|
|
842
|
+
.where(eq(epics.parentId, id));
|
|
843
|
+
for (const subEpic of subEpics) {
|
|
844
|
+
await this.deleteEpic(subEpic.id);
|
|
845
|
+
}
|
|
846
|
+
await this.db.delete(epics).where(eq(epics.id, id));
|
|
847
|
+
logger.info({ epicId: id, deletedSubEpics: subEpics.length }, 'Deleted epic and sub-epics');
|
|
848
|
+
}
|
|
849
|
+
async listSubEpics(parentId, options = {}) {
|
|
850
|
+
await this.getEpic(parentId);
|
|
851
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
852
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
853
|
+
const limit = options.limit || 100;
|
|
854
|
+
const offset = options.offset || 0;
|
|
855
|
+
const items = await this.db
|
|
856
|
+
.select()
|
|
857
|
+
.from(epics)
|
|
858
|
+
.where(eq(epics.parentId, parentId))
|
|
859
|
+
.limit(limit)
|
|
860
|
+
.offset(offset);
|
|
861
|
+
const itemsWithTags = await Promise.all(items.map((item) => this.getEpic(item.id)));
|
|
862
|
+
return {
|
|
863
|
+
items: itemsWithTags,
|
|
864
|
+
total: items.length,
|
|
865
|
+
limit,
|
|
866
|
+
offset,
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async countSubEpicsByStatus(parentId) {
|
|
870
|
+
await this.getEpic(parentId);
|
|
871
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
872
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
873
|
+
const rows = await this.db
|
|
874
|
+
.select({ statusId: epics.statusId })
|
|
875
|
+
.from(epics)
|
|
876
|
+
.where(eq(epics.parentId, parentId));
|
|
877
|
+
return rows.reduce((acc, row) => {
|
|
878
|
+
const key = row.statusId;
|
|
879
|
+
acc[key] = (acc[key] ?? 0) + 1;
|
|
880
|
+
return acc;
|
|
881
|
+
}, {});
|
|
882
|
+
}
|
|
883
|
+
async listEpicComments(epicId, options = {}) {
|
|
884
|
+
await this.getEpic(epicId);
|
|
885
|
+
const { epicComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
886
|
+
const { eq, asc, sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
887
|
+
const limit = options.limit || 100;
|
|
888
|
+
const offset = options.offset || 0;
|
|
889
|
+
const items = await this.db
|
|
890
|
+
.select()
|
|
891
|
+
.from(epicComments)
|
|
892
|
+
.where(eq(epicComments.epicId, epicId))
|
|
893
|
+
.orderBy(asc(epicComments.createdAt))
|
|
894
|
+
.limit(limit)
|
|
895
|
+
.offset(offset);
|
|
896
|
+
const totalResult = await this.db
|
|
897
|
+
.select({ count: sql `count(*)` })
|
|
898
|
+
.from(epicComments)
|
|
899
|
+
.where(eq(epicComments.epicId, epicId));
|
|
900
|
+
const total = Number(totalResult[0]?.count ?? 0);
|
|
901
|
+
return {
|
|
902
|
+
items: items,
|
|
903
|
+
total,
|
|
904
|
+
limit,
|
|
905
|
+
offset,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
async createEpicComment(data) {
|
|
909
|
+
await this.getEpic(data.epicId);
|
|
910
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
911
|
+
const { epicComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
912
|
+
const now = new Date().toISOString();
|
|
913
|
+
const comment = {
|
|
914
|
+
id: randomUUID(),
|
|
915
|
+
epicId: data.epicId,
|
|
916
|
+
authorName: data.authorName,
|
|
917
|
+
content: data.content,
|
|
918
|
+
createdAt: now,
|
|
919
|
+
updatedAt: now,
|
|
920
|
+
};
|
|
921
|
+
await this.db.insert(epicComments).values({
|
|
922
|
+
id: comment.id,
|
|
923
|
+
epicId: comment.epicId,
|
|
924
|
+
authorName: comment.authorName,
|
|
925
|
+
content: comment.content,
|
|
926
|
+
createdAt: comment.createdAt,
|
|
927
|
+
updatedAt: comment.updatedAt,
|
|
928
|
+
});
|
|
929
|
+
return comment;
|
|
930
|
+
}
|
|
931
|
+
async deleteEpicComment(id) {
|
|
932
|
+
const { epicComments } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
933
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
934
|
+
await this.db.delete(epicComments).where(eq(epicComments.id, id));
|
|
935
|
+
}
|
|
936
|
+
async createPrompt(data) {
|
|
937
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
938
|
+
const now = new Date().toISOString();
|
|
939
|
+
const { prompts, promptTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
940
|
+
const prompt = {
|
|
941
|
+
id: randomUUID(),
|
|
942
|
+
...data,
|
|
943
|
+
version: 1,
|
|
944
|
+
createdAt: now,
|
|
945
|
+
updatedAt: now,
|
|
946
|
+
};
|
|
947
|
+
await this.db.insert(prompts).values({
|
|
948
|
+
id: prompt.id,
|
|
949
|
+
projectId: prompt.projectId,
|
|
950
|
+
title: prompt.title,
|
|
951
|
+
content: prompt.content,
|
|
952
|
+
version: prompt.version,
|
|
953
|
+
createdAt: prompt.createdAt,
|
|
954
|
+
updatedAt: prompt.updatedAt,
|
|
955
|
+
});
|
|
956
|
+
if (data.tags?.length) {
|
|
957
|
+
for (const tagName of data.tags) {
|
|
958
|
+
const { eq, and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
959
|
+
let tag = await this.db
|
|
960
|
+
.select()
|
|
961
|
+
.from(tags)
|
|
962
|
+
.where(and(eq(tags.name, tagName), or(eq(tags.projectId, data.projectId || ''), isNull(tags.projectId))))
|
|
963
|
+
.limit(1);
|
|
964
|
+
if (!tag[0]) {
|
|
965
|
+
const newTag = await this.createTag({ projectId: data.projectId, name: tagName });
|
|
966
|
+
tag = [newTag];
|
|
967
|
+
}
|
|
968
|
+
await this.db.insert(promptTags).values({
|
|
969
|
+
promptId: prompt.id,
|
|
970
|
+
tagId: tag[0].id,
|
|
971
|
+
createdAt: now,
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
return prompt;
|
|
976
|
+
}
|
|
977
|
+
async getPrompt(id) {
|
|
978
|
+
const { prompts, promptTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
979
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
980
|
+
const result = await this.db.select().from(prompts).where(eq(prompts.id, id)).limit(1);
|
|
981
|
+
if (!result[0]) {
|
|
982
|
+
throw new error_types_1.NotFoundError('Prompt', id);
|
|
983
|
+
}
|
|
984
|
+
const promptTagsResult = await this.db
|
|
985
|
+
.select({ tag: tags })
|
|
986
|
+
.from(promptTags)
|
|
987
|
+
.innerJoin(tags, eq(promptTags.tagId, tags.id))
|
|
988
|
+
.where(eq(promptTags.promptId, id));
|
|
989
|
+
return {
|
|
990
|
+
...result[0],
|
|
991
|
+
tags: promptTagsResult.map((pt) => pt.tag.name),
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
async listPrompts(projectId, options = {}) {
|
|
995
|
+
const { prompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
996
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
997
|
+
const limit = options.limit || 100;
|
|
998
|
+
const offset = options.offset || 0;
|
|
999
|
+
const query = projectId ? eq(prompts.projectId, projectId) : undefined;
|
|
1000
|
+
const items = await this.db.select().from(prompts).where(query).limit(limit).offset(offset);
|
|
1001
|
+
const itemsWithTags = await Promise.all(items.map((item) => this.getPrompt(item.id)));
|
|
1002
|
+
return {
|
|
1003
|
+
items: itemsWithTags,
|
|
1004
|
+
total: items.length,
|
|
1005
|
+
limit,
|
|
1006
|
+
offset,
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
async updatePrompt(id, data, expectedVersion) {
|
|
1010
|
+
const { prompts, promptTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1011
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1012
|
+
const now = new Date().toISOString();
|
|
1013
|
+
logger.info({ id, data, expectedVersion }, 'updatePrompt called with data');
|
|
1014
|
+
const current = await this.getPrompt(id);
|
|
1015
|
+
if (current.version !== expectedVersion) {
|
|
1016
|
+
throw new error_types_1.OptimisticLockError('Prompt', id, {
|
|
1017
|
+
expectedVersion,
|
|
1018
|
+
actualVersion: current.version,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
const { tags: newTags, ...updateData } = data;
|
|
1022
|
+
logger.info({ newTags, updateData }, 'Separated tags from updateData');
|
|
1023
|
+
await this.db
|
|
1024
|
+
.update(prompts)
|
|
1025
|
+
.set({ ...updateData, version: expectedVersion + 1, updatedAt: now })
|
|
1026
|
+
.where(eq(prompts.id, id));
|
|
1027
|
+
logger.info('Updated prompt fields in database');
|
|
1028
|
+
if (newTags !== undefined) {
|
|
1029
|
+
logger.info({ newTags, newTagsLength: newTags.length }, 'Updating tags');
|
|
1030
|
+
await this.db.delete(promptTags).where(eq(promptTags.promptId, id));
|
|
1031
|
+
logger.info('Deleted existing prompt tags');
|
|
1032
|
+
if (newTags.length > 0) {
|
|
1033
|
+
for (const tagName of newTags) {
|
|
1034
|
+
logger.info({ tagName }, 'Processing tag');
|
|
1035
|
+
const { and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1036
|
+
let tag = await this.db
|
|
1037
|
+
.select()
|
|
1038
|
+
.from(tags)
|
|
1039
|
+
.where(and(eq(tags.name, tagName), or(eq(tags.projectId, current.projectId || ''), isNull(tags.projectId))))
|
|
1040
|
+
.limit(1);
|
|
1041
|
+
if (!tag[0]) {
|
|
1042
|
+
logger.info({ tagName }, 'Tag not found, creating new tag');
|
|
1043
|
+
const newTag = await this.createTag({ projectId: current.projectId, name: tagName });
|
|
1044
|
+
tag = [newTag];
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
logger.info({ tagName, tagId: tag[0].id }, 'Found existing tag');
|
|
1048
|
+
}
|
|
1049
|
+
await this.db.insert(promptTags).values({
|
|
1050
|
+
promptId: id,
|
|
1051
|
+
tagId: tag[0].id,
|
|
1052
|
+
createdAt: now,
|
|
1053
|
+
});
|
|
1054
|
+
logger.info({ tagName, tagId: tag[0].id }, 'Inserted prompt tag');
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
logger.info('No tags provided in update data');
|
|
1060
|
+
}
|
|
1061
|
+
return this.getPrompt(id);
|
|
1062
|
+
}
|
|
1063
|
+
async deletePrompt(id) {
|
|
1064
|
+
const { prompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1065
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1066
|
+
await this.db.delete(prompts).where(eq(prompts.id, id));
|
|
1067
|
+
}
|
|
1068
|
+
async getInitialSessionPrompt(projectId) {
|
|
1069
|
+
const { settings } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1070
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1071
|
+
let rawValue;
|
|
1072
|
+
try {
|
|
1073
|
+
const mapRows = await this.db
|
|
1074
|
+
.select({ value: settings.value })
|
|
1075
|
+
.from(settings)
|
|
1076
|
+
.where(eq(settings.key, 'initialSessionPromptIds'))
|
|
1077
|
+
.limit(1);
|
|
1078
|
+
const mapRaw = mapRows[0]?.value;
|
|
1079
|
+
const promptIdFromMap = this.extractPromptIdFromMap(mapRaw, projectId);
|
|
1080
|
+
if (promptIdFromMap) {
|
|
1081
|
+
rawValue = promptIdFromMap;
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
const result = await this.db
|
|
1085
|
+
.select({ value: settings.value })
|
|
1086
|
+
.from(settings)
|
|
1087
|
+
.where(eq(settings.key, 'initialSessionPromptId'))
|
|
1088
|
+
.limit(1);
|
|
1089
|
+
rawValue = result[0]?.value;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
catch (error) {
|
|
1093
|
+
logger.warn({ error }, 'Drizzle read failed for initialSessionPromptId; falling back to raw SQLite');
|
|
1094
|
+
try {
|
|
1095
|
+
const mapRow = (0, sqlite_raw_1.getRawSqliteClient)(this.db)
|
|
1096
|
+
.prepare('SELECT value FROM settings WHERE key = ? LIMIT 1')
|
|
1097
|
+
.get('initialSessionPromptIds');
|
|
1098
|
+
const fromMap = this.extractPromptIdFromMap(mapRow?.value, projectId);
|
|
1099
|
+
if (fromMap) {
|
|
1100
|
+
rawValue = fromMap;
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
const row = (0, sqlite_raw_1.getRawSqliteClient)(this.db)
|
|
1104
|
+
.prepare('SELECT value FROM settings WHERE key = ? LIMIT 1')
|
|
1105
|
+
.get('initialSessionPromptId');
|
|
1106
|
+
rawValue = row?.value;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
catch (sqliteError) {
|
|
1110
|
+
logger.error({ sqliteError }, 'Raw SQLite read failed for initialSessionPromptId');
|
|
1111
|
+
return null;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
const promptId = typeof rawValue === 'string' ? rawValue : this.extractPromptId(rawValue);
|
|
1115
|
+
logger.debug({ rawType: typeof rawValue, rawValue: safePreview(rawValue), promptId }, 'Resolved initial session prompt id from settings');
|
|
1116
|
+
if (!promptId) {
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
try {
|
|
1120
|
+
return await this.getPrompt(promptId);
|
|
1121
|
+
}
|
|
1122
|
+
catch (error) {
|
|
1123
|
+
if (error instanceof error_types_1.NotFoundError) {
|
|
1124
|
+
logger.warn({ promptId }, 'Initial session prompt not found');
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
throw error;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
extractPromptId(value) {
|
|
1131
|
+
if (value === undefined || value === null) {
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
if (typeof value === 'object') {
|
|
1135
|
+
if ('initialSessionPromptId' in value) {
|
|
1136
|
+
return this.extractPromptId(value.initialSessionPromptId);
|
|
1137
|
+
}
|
|
1138
|
+
if ('value' in value) {
|
|
1139
|
+
return this.extractPromptId(value.value);
|
|
1140
|
+
}
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
if (typeof value === 'string') {
|
|
1144
|
+
const trimmed = value.trim();
|
|
1145
|
+
if (!trimmed) {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
try {
|
|
1149
|
+
const parsed = JSON.parse(trimmed);
|
|
1150
|
+
return this.extractPromptId(parsed);
|
|
1151
|
+
}
|
|
1152
|
+
catch {
|
|
1153
|
+
}
|
|
1154
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
|
|
1155
|
+
return trimmed.slice(1, -1).trim() || null;
|
|
1156
|
+
}
|
|
1157
|
+
return trimmed;
|
|
1158
|
+
}
|
|
1159
|
+
return String(value).trim() || null;
|
|
1160
|
+
}
|
|
1161
|
+
extractPromptIdFromMap(value, projectId) {
|
|
1162
|
+
try {
|
|
1163
|
+
const obj = typeof value === 'string' ? JSON.parse(value) : value;
|
|
1164
|
+
if (obj && typeof obj === 'object') {
|
|
1165
|
+
const map = obj;
|
|
1166
|
+
if (projectId && typeof map[projectId] === 'string') {
|
|
1167
|
+
const v = map[projectId].trim();
|
|
1168
|
+
return v || null;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
catch {
|
|
1173
|
+
}
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
async listDocuments(filters = {}) {
|
|
1177
|
+
const { documents, documentTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1178
|
+
const { and, eq, isNull, like, or, desc, sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1179
|
+
const whereClauses = [];
|
|
1180
|
+
if (filters.projectId !== undefined) {
|
|
1181
|
+
whereClauses.push(filters.projectId === null
|
|
1182
|
+
? isNull(documents.projectId)
|
|
1183
|
+
: eq(documents.projectId, filters.projectId));
|
|
1184
|
+
}
|
|
1185
|
+
const tagKeys = this.normalizeTagList(filters.tagKeys);
|
|
1186
|
+
if (tagKeys.length) {
|
|
1187
|
+
for (const key of tagKeys) {
|
|
1188
|
+
whereClauses.push(sql `EXISTS (
|
|
1189
|
+
SELECT 1
|
|
1190
|
+
FROM ${documentTags} dt
|
|
1191
|
+
INNER JOIN ${tags} t ON t.id = dt.tag_id
|
|
1192
|
+
WHERE dt.document_id = ${documents.id}
|
|
1193
|
+
AND (
|
|
1194
|
+
(
|
|
1195
|
+
CASE
|
|
1196
|
+
WHEN instr(t.name, ':') > 0 THEN substr(t.name, 1, instr(t.name, ':') - 1)
|
|
1197
|
+
ELSE NULL
|
|
1198
|
+
END
|
|
1199
|
+
) = ${key}
|
|
1200
|
+
OR t.name = ${key}
|
|
1201
|
+
)
|
|
1202
|
+
)`);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
const searchTerm = filters.q?.trim();
|
|
1206
|
+
if (searchTerm) {
|
|
1207
|
+
const pattern = `%${searchTerm}%`;
|
|
1208
|
+
whereClauses.push(or(like(documents.title, pattern), like(documents.contentMd, pattern)));
|
|
1209
|
+
}
|
|
1210
|
+
const whereCondition = whereClauses.length === 0
|
|
1211
|
+
? undefined
|
|
1212
|
+
: whereClauses.length === 1
|
|
1213
|
+
? whereClauses[0]
|
|
1214
|
+
: and(...whereClauses);
|
|
1215
|
+
const rows = await (whereCondition
|
|
1216
|
+
? this.db.select().from(documents).where(whereCondition).orderBy(desc(documents.updatedAt))
|
|
1217
|
+
: this.db.select().from(documents).orderBy(desc(documents.updatedAt)));
|
|
1218
|
+
if (!rows.length) {
|
|
1219
|
+
return {
|
|
1220
|
+
items: [],
|
|
1221
|
+
total: 0,
|
|
1222
|
+
limit: filters.limit ?? 100,
|
|
1223
|
+
offset: filters.offset ?? 0,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
const documentsWithTags = await Promise.all(rows.map((row) => this.getDocument({ id: row.id })));
|
|
1227
|
+
let filtered = documentsWithTags;
|
|
1228
|
+
if (filters.tags?.length) {
|
|
1229
|
+
const requiredTags = this.normalizeTagList(filters.tags);
|
|
1230
|
+
filtered = documentsWithTags.filter((doc) => requiredTags.every((tag) => doc.tags.includes(tag)));
|
|
1231
|
+
}
|
|
1232
|
+
const limit = filters.limit ?? 100;
|
|
1233
|
+
const offset = filters.offset ?? 0;
|
|
1234
|
+
const items = filtered.slice(offset, offset + limit);
|
|
1235
|
+
return {
|
|
1236
|
+
items,
|
|
1237
|
+
total: filtered.length,
|
|
1238
|
+
limit,
|
|
1239
|
+
offset,
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
async getDocument(identifier) {
|
|
1243
|
+
const { documents, documentTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1244
|
+
const { eq, and, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1245
|
+
let whereCondition;
|
|
1246
|
+
if (identifier.id) {
|
|
1247
|
+
whereCondition = eq(documents.id, identifier.id);
|
|
1248
|
+
}
|
|
1249
|
+
else if (identifier.slug) {
|
|
1250
|
+
if (identifier.projectId === undefined) {
|
|
1251
|
+
throw new error_types_1.ValidationError('projectId is required when querying document by slug');
|
|
1252
|
+
}
|
|
1253
|
+
whereCondition =
|
|
1254
|
+
identifier.projectId === null
|
|
1255
|
+
? and(isNull(documents.projectId), eq(documents.slug, identifier.slug))
|
|
1256
|
+
: and(eq(documents.projectId, identifier.projectId), eq(documents.slug, identifier.slug));
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
throw new error_types_1.ValidationError('Document identifier requires either id or slug');
|
|
1260
|
+
}
|
|
1261
|
+
const result = await this.db.select().from(documents).where(whereCondition).limit(1);
|
|
1262
|
+
const record = result[0];
|
|
1263
|
+
if (!record) {
|
|
1264
|
+
const lookup = identifier.id ?? `${identifier.projectId ?? 'global'}:${identifier.slug}`;
|
|
1265
|
+
throw new error_types_1.NotFoundError('Document', lookup || 'unknown');
|
|
1266
|
+
}
|
|
1267
|
+
const tagRows = await this.db
|
|
1268
|
+
.select({ tag: tags })
|
|
1269
|
+
.from(documentTags)
|
|
1270
|
+
.innerJoin(tags, eq(documentTags.tagId, tags.id))
|
|
1271
|
+
.where(eq(documentTags.documentId, record.id));
|
|
1272
|
+
return {
|
|
1273
|
+
...record,
|
|
1274
|
+
projectId: record.projectId ?? null,
|
|
1275
|
+
tags: tagRows.map((row) => row.tag.name),
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
async createDocument(data) {
|
|
1279
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1280
|
+
const now = new Date().toISOString();
|
|
1281
|
+
const normalizedProjectId = data.projectId ?? null;
|
|
1282
|
+
const { documents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1283
|
+
const slugSource = data.slug ?? data.title;
|
|
1284
|
+
const slug = await this.generateDocumentSlug(normalizedProjectId, slugSource);
|
|
1285
|
+
const tags = this.normalizeTagList(data.tags);
|
|
1286
|
+
const id = randomUUID();
|
|
1287
|
+
await this.db.insert(documents).values({
|
|
1288
|
+
id,
|
|
1289
|
+
projectId: normalizedProjectId,
|
|
1290
|
+
title: data.title,
|
|
1291
|
+
slug,
|
|
1292
|
+
contentMd: data.contentMd,
|
|
1293
|
+
version: 1,
|
|
1294
|
+
archived: false,
|
|
1295
|
+
createdAt: now,
|
|
1296
|
+
updatedAt: now,
|
|
1297
|
+
});
|
|
1298
|
+
if (tags.length) {
|
|
1299
|
+
await this.setDocumentTags(id, tags, normalizedProjectId);
|
|
1300
|
+
}
|
|
1301
|
+
logger.info({ documentId: id }, 'Created document');
|
|
1302
|
+
return this.getDocument({ id });
|
|
1303
|
+
}
|
|
1304
|
+
async updateDocument(id, data) {
|
|
1305
|
+
const { documents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1306
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1307
|
+
const now = new Date().toISOString();
|
|
1308
|
+
const current = await this.getDocument({ id });
|
|
1309
|
+
if (data.version !== undefined && data.version !== current.version) {
|
|
1310
|
+
throw new error_types_1.OptimisticLockError('Document', id, {
|
|
1311
|
+
expectedVersion: data.version,
|
|
1312
|
+
actualVersion: current.version,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
const updatePayload = {
|
|
1316
|
+
updatedAt: now,
|
|
1317
|
+
version: current.version + 1,
|
|
1318
|
+
};
|
|
1319
|
+
if (data.title !== undefined) {
|
|
1320
|
+
updatePayload.title = data.title;
|
|
1321
|
+
}
|
|
1322
|
+
if (data.contentMd !== undefined) {
|
|
1323
|
+
updatePayload.contentMd = data.contentMd;
|
|
1324
|
+
}
|
|
1325
|
+
if (data.archived !== undefined) {
|
|
1326
|
+
updatePayload.archived = data.archived;
|
|
1327
|
+
}
|
|
1328
|
+
if (data.slug !== undefined) {
|
|
1329
|
+
updatePayload.slug = await this.generateDocumentSlug(current.projectId, data.slug, id);
|
|
1330
|
+
}
|
|
1331
|
+
await this.db.update(documents).set(updatePayload).where(eq(documents.id, id));
|
|
1332
|
+
if (data.tags !== undefined) {
|
|
1333
|
+
const tags = this.normalizeTagList(data.tags);
|
|
1334
|
+
await this.setDocumentTags(id, tags, current.projectId);
|
|
1335
|
+
}
|
|
1336
|
+
logger.info({ documentId: id }, 'Updated document');
|
|
1337
|
+
return this.getDocument({ id });
|
|
1338
|
+
}
|
|
1339
|
+
async deleteDocument(id) {
|
|
1340
|
+
const { documents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1341
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1342
|
+
await this.db.delete(documents).where(eq(documents.id, id));
|
|
1343
|
+
logger.info({ documentId: id }, 'Deleted document');
|
|
1344
|
+
}
|
|
1345
|
+
async createTag(data) {
|
|
1346
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1347
|
+
const now = new Date().toISOString();
|
|
1348
|
+
const { tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1349
|
+
const tag = {
|
|
1350
|
+
id: randomUUID(),
|
|
1351
|
+
...data,
|
|
1352
|
+
createdAt: now,
|
|
1353
|
+
updatedAt: now,
|
|
1354
|
+
};
|
|
1355
|
+
await this.db.insert(tags).values({
|
|
1356
|
+
id: tag.id,
|
|
1357
|
+
projectId: tag.projectId,
|
|
1358
|
+
name: tag.name,
|
|
1359
|
+
createdAt: tag.createdAt,
|
|
1360
|
+
updatedAt: tag.updatedAt,
|
|
1361
|
+
});
|
|
1362
|
+
return tag;
|
|
1363
|
+
}
|
|
1364
|
+
async getTag(id) {
|
|
1365
|
+
const { tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1366
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1367
|
+
const result = await this.db.select().from(tags).where(eq(tags.id, id)).limit(1);
|
|
1368
|
+
if (!result[0]) {
|
|
1369
|
+
throw new error_types_1.NotFoundError('Tag', id);
|
|
1370
|
+
}
|
|
1371
|
+
return result[0];
|
|
1372
|
+
}
|
|
1373
|
+
async listTags(projectId, options = {}) {
|
|
1374
|
+
const { tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1375
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1376
|
+
const limit = options.limit || 100;
|
|
1377
|
+
const offset = options.offset || 0;
|
|
1378
|
+
const query = projectId ? eq(tags.projectId, projectId) : undefined;
|
|
1379
|
+
const items = await this.db.select().from(tags).where(query).limit(limit).offset(offset);
|
|
1380
|
+
return {
|
|
1381
|
+
items: items,
|
|
1382
|
+
total: items.length,
|
|
1383
|
+
limit,
|
|
1384
|
+
offset,
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
async updateTag(id, data) {
|
|
1388
|
+
const { tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1389
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1390
|
+
const now = new Date().toISOString();
|
|
1391
|
+
await this.db
|
|
1392
|
+
.update(tags)
|
|
1393
|
+
.set({ ...data, updatedAt: now })
|
|
1394
|
+
.where(eq(tags.id, id));
|
|
1395
|
+
return this.getTag(id);
|
|
1396
|
+
}
|
|
1397
|
+
async deleteTag(id) {
|
|
1398
|
+
const { tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1399
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1400
|
+
await this.db.delete(tags).where(eq(tags.id, id));
|
|
1401
|
+
}
|
|
1402
|
+
async createProvider(data) {
|
|
1403
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1404
|
+
const now = new Date().toISOString();
|
|
1405
|
+
const { providers } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1406
|
+
const provider = {
|
|
1407
|
+
id: randomUUID(),
|
|
1408
|
+
name: data.name,
|
|
1409
|
+
binPath: data.binPath ?? null,
|
|
1410
|
+
mcpConfigured: data.mcpConfigured ?? false,
|
|
1411
|
+
mcpEndpoint: data.mcpEndpoint ?? null,
|
|
1412
|
+
mcpRegisteredAt: data.mcpRegisteredAt ?? null,
|
|
1413
|
+
createdAt: now,
|
|
1414
|
+
updatedAt: now,
|
|
1415
|
+
};
|
|
1416
|
+
await this.db.insert(providers).values({
|
|
1417
|
+
id: provider.id,
|
|
1418
|
+
name: provider.name,
|
|
1419
|
+
binPath: provider.binPath,
|
|
1420
|
+
mcpConfigured: provider.mcpConfigured,
|
|
1421
|
+
mcpEndpoint: provider.mcpEndpoint,
|
|
1422
|
+
mcpRegisteredAt: provider.mcpRegisteredAt,
|
|
1423
|
+
createdAt: provider.createdAt,
|
|
1424
|
+
updatedAt: provider.updatedAt,
|
|
1425
|
+
});
|
|
1426
|
+
logger.info({ providerId: provider.id, name: provider.name }, 'Created provider');
|
|
1427
|
+
return provider;
|
|
1428
|
+
}
|
|
1429
|
+
async getProvider(id) {
|
|
1430
|
+
const { providers } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1431
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1432
|
+
const result = await this.db.select().from(providers).where(eq(providers.id, id)).limit(1);
|
|
1433
|
+
if (!result[0]) {
|
|
1434
|
+
throw new error_types_1.NotFoundError('Provider', id);
|
|
1435
|
+
}
|
|
1436
|
+
return result[0];
|
|
1437
|
+
}
|
|
1438
|
+
async listProviders(options = {}) {
|
|
1439
|
+
const { providers } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1440
|
+
const limit = options.limit || 100;
|
|
1441
|
+
const offset = options.offset || 0;
|
|
1442
|
+
const items = await this.db.select().from(providers).limit(limit).offset(offset);
|
|
1443
|
+
return {
|
|
1444
|
+
items: items,
|
|
1445
|
+
total: items.length,
|
|
1446
|
+
limit,
|
|
1447
|
+
offset,
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
async updateProvider(id, data) {
|
|
1451
|
+
const { providers } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1452
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1453
|
+
const now = new Date().toISOString();
|
|
1454
|
+
const payload = Object.fromEntries(Object.entries({
|
|
1455
|
+
...data,
|
|
1456
|
+
updatedAt: now,
|
|
1457
|
+
}).filter(([, value]) => value !== undefined));
|
|
1458
|
+
await this.db.update(providers).set(payload).where(eq(providers.id, id));
|
|
1459
|
+
logger.info({ providerId: id }, 'Updated provider');
|
|
1460
|
+
return this.getProvider(id);
|
|
1461
|
+
}
|
|
1462
|
+
async deleteProvider(id) {
|
|
1463
|
+
const { providers } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1464
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1465
|
+
await this.db.delete(providers).where(eq(providers.id, id));
|
|
1466
|
+
logger.info({ providerId: id }, 'Deleted provider');
|
|
1467
|
+
}
|
|
1468
|
+
async getProviderMcpMetadata(id) {
|
|
1469
|
+
const provider = await this.getProvider(id);
|
|
1470
|
+
return {
|
|
1471
|
+
mcpConfigured: provider.mcpConfigured,
|
|
1472
|
+
mcpEndpoint: provider.mcpEndpoint,
|
|
1473
|
+
mcpRegisteredAt: provider.mcpRegisteredAt,
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
async updateProviderMcpMetadata(id, metadata) {
|
|
1477
|
+
const update = {};
|
|
1478
|
+
if (metadata.mcpConfigured !== undefined) {
|
|
1479
|
+
update.mcpConfigured = metadata.mcpConfigured;
|
|
1480
|
+
}
|
|
1481
|
+
if (metadata.mcpEndpoint !== undefined) {
|
|
1482
|
+
update.mcpEndpoint = metadata.mcpEndpoint ?? null;
|
|
1483
|
+
}
|
|
1484
|
+
if (metadata.mcpRegisteredAt !== undefined) {
|
|
1485
|
+
update.mcpRegisteredAt = metadata.mcpRegisteredAt ?? null;
|
|
1486
|
+
}
|
|
1487
|
+
return this.updateProvider(id, update);
|
|
1488
|
+
}
|
|
1489
|
+
async createAgentProfile(data) {
|
|
1490
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1491
|
+
const now = new Date().toISOString();
|
|
1492
|
+
const { agentProfiles } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1493
|
+
const profile = {
|
|
1494
|
+
id: randomUUID(),
|
|
1495
|
+
projectId: data.projectId ?? null,
|
|
1496
|
+
name: data.name,
|
|
1497
|
+
providerId: data.providerId,
|
|
1498
|
+
options: data.options ?? null,
|
|
1499
|
+
systemPrompt: data.systemPrompt ?? null,
|
|
1500
|
+
instructions: data.instructions ?? null,
|
|
1501
|
+
temperature: data.temperature ?? null,
|
|
1502
|
+
maxTokens: data.maxTokens ?? null,
|
|
1503
|
+
createdAt: now,
|
|
1504
|
+
updatedAt: now,
|
|
1505
|
+
};
|
|
1506
|
+
await this.db.insert(agentProfiles).values({
|
|
1507
|
+
id: profile.id,
|
|
1508
|
+
projectId: profile.projectId,
|
|
1509
|
+
name: profile.name,
|
|
1510
|
+
providerId: profile.providerId,
|
|
1511
|
+
options: profile.options,
|
|
1512
|
+
systemPrompt: profile.systemPrompt,
|
|
1513
|
+
instructions: profile.instructions,
|
|
1514
|
+
temperature: profile.temperature ? Math.round(profile.temperature * 100) : null,
|
|
1515
|
+
maxTokens: profile.maxTokens,
|
|
1516
|
+
createdAt: profile.createdAt,
|
|
1517
|
+
updatedAt: profile.updatedAt,
|
|
1518
|
+
});
|
|
1519
|
+
return profile;
|
|
1520
|
+
}
|
|
1521
|
+
async getAgentProfile(id) {
|
|
1522
|
+
const { agentProfiles } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1523
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1524
|
+
const result = await this.db
|
|
1525
|
+
.select()
|
|
1526
|
+
.from(agentProfiles)
|
|
1527
|
+
.where(eq(agentProfiles.id, id))
|
|
1528
|
+
.limit(1);
|
|
1529
|
+
if (!result[0]) {
|
|
1530
|
+
throw new error_types_1.NotFoundError('Agent profile', id);
|
|
1531
|
+
}
|
|
1532
|
+
const profile = result[0];
|
|
1533
|
+
return {
|
|
1534
|
+
...profile,
|
|
1535
|
+
temperature: profile.temperature ? profile.temperature / 100 : null,
|
|
1536
|
+
options: profile.options ?? null,
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
async listAgentProfiles(options = {}) {
|
|
1540
|
+
const { agentProfiles } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1541
|
+
const { eq, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1542
|
+
const limit = options.limit || 100;
|
|
1543
|
+
const offset = options.offset || 0;
|
|
1544
|
+
let whereClause;
|
|
1545
|
+
if (options.projectId !== undefined) {
|
|
1546
|
+
whereClause =
|
|
1547
|
+
options.projectId === null
|
|
1548
|
+
? isNull(agentProfiles.projectId)
|
|
1549
|
+
: eq(agentProfiles.projectId, options.projectId);
|
|
1550
|
+
}
|
|
1551
|
+
const items = await this.db
|
|
1552
|
+
.select()
|
|
1553
|
+
.from(agentProfiles)
|
|
1554
|
+
.where(whereClause)
|
|
1555
|
+
.limit(limit)
|
|
1556
|
+
.offset(offset);
|
|
1557
|
+
return {
|
|
1558
|
+
items: items.map((p) => ({
|
|
1559
|
+
...p,
|
|
1560
|
+
temperature: p.temperature ? p.temperature / 100 : null,
|
|
1561
|
+
options: p.options ?? null,
|
|
1562
|
+
})),
|
|
1563
|
+
total: items.length,
|
|
1564
|
+
limit,
|
|
1565
|
+
offset,
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
async updateAgentProfile(id, data) {
|
|
1569
|
+
const { agentProfiles } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1570
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1571
|
+
const now = new Date().toISOString();
|
|
1572
|
+
const updateData = { ...data };
|
|
1573
|
+
if (data.temperature !== undefined && data.temperature !== null) {
|
|
1574
|
+
updateData.temperature = Math.round(data.temperature * 100);
|
|
1575
|
+
}
|
|
1576
|
+
if (data.temperature === null) {
|
|
1577
|
+
updateData.temperature = null;
|
|
1578
|
+
}
|
|
1579
|
+
if (data.instructions !== undefined) {
|
|
1580
|
+
updateData.instructions = data.instructions ?? null;
|
|
1581
|
+
}
|
|
1582
|
+
if (data.options !== undefined) {
|
|
1583
|
+
updateData.options = data.options ?? null;
|
|
1584
|
+
}
|
|
1585
|
+
await this.db
|
|
1586
|
+
.update(agentProfiles)
|
|
1587
|
+
.set({ ...updateData, updatedAt: now })
|
|
1588
|
+
.where(eq(agentProfiles.id, id));
|
|
1589
|
+
return this.getAgentProfile(id);
|
|
1590
|
+
}
|
|
1591
|
+
async deleteAgentProfile(id) {
|
|
1592
|
+
const { agentProfiles } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1593
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1594
|
+
await this.db.delete(agentProfiles).where(eq(agentProfiles.id, id));
|
|
1595
|
+
}
|
|
1596
|
+
async setAgentProfilePrompts(profileId, promptIdsOrdered) {
|
|
1597
|
+
const { agentProfilePrompts, prompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1598
|
+
const { eq, inArray } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1599
|
+
const profile = await this.getAgentProfile(profileId);
|
|
1600
|
+
if (promptIdsOrdered.length > 0) {
|
|
1601
|
+
const items = await this.db
|
|
1602
|
+
.select({ id: prompts.id, projectId: prompts.projectId })
|
|
1603
|
+
.from(prompts)
|
|
1604
|
+
.where(inArray(prompts.id, promptIdsOrdered));
|
|
1605
|
+
const foundIds = new Set(items.map((i) => i.id));
|
|
1606
|
+
const missing = promptIdsOrdered.filter((id) => !foundIds.has(id));
|
|
1607
|
+
if (missing.length > 0) {
|
|
1608
|
+
throw new error_types_1.ValidationError('Unknown prompt ids provided', { missing });
|
|
1609
|
+
}
|
|
1610
|
+
const crossProject = items.filter((i) => i.projectId !== (profile.projectId ?? null));
|
|
1611
|
+
if (crossProject.length > 0) {
|
|
1612
|
+
throw new error_types_1.ValidationError('Cross-project prompts are not allowed for this profile', {
|
|
1613
|
+
profileProjectId: profile.projectId ?? null,
|
|
1614
|
+
promptIds: crossProject.map((i) => i.id),
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
await this.db.transaction(async (tx) => {
|
|
1619
|
+
await tx.delete(agentProfilePrompts).where(eq(agentProfilePrompts.profileId, profileId));
|
|
1620
|
+
if (promptIdsOrdered.length === 0)
|
|
1621
|
+
return;
|
|
1622
|
+
const base = new Date();
|
|
1623
|
+
const rows = promptIdsOrdered.map((pid, idx) => ({
|
|
1624
|
+
profileId,
|
|
1625
|
+
promptId: pid,
|
|
1626
|
+
createdAt: new Date(base.getTime() + idx).toISOString(),
|
|
1627
|
+
}));
|
|
1628
|
+
await tx.insert(agentProfilePrompts).values(rows);
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
async getAgentProfilePrompts(profileId) {
|
|
1632
|
+
const { agentProfilePrompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1633
|
+
const { eq, asc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1634
|
+
const rows = await this.db
|
|
1635
|
+
.select({ promptId: agentProfilePrompts.promptId, createdAt: agentProfilePrompts.createdAt })
|
|
1636
|
+
.from(agentProfilePrompts)
|
|
1637
|
+
.where(eq(agentProfilePrompts.profileId, profileId))
|
|
1638
|
+
.orderBy(asc(agentProfilePrompts.createdAt));
|
|
1639
|
+
return rows;
|
|
1640
|
+
}
|
|
1641
|
+
async getAgentProfileWithPrompts(id) {
|
|
1642
|
+
const profile = await this.getAgentProfile(id);
|
|
1643
|
+
const { agentProfilePrompts, prompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1644
|
+
const { eq, asc } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1645
|
+
const rows = await this.db
|
|
1646
|
+
.select({
|
|
1647
|
+
promptId: agentProfilePrompts.promptId,
|
|
1648
|
+
createdAt: agentProfilePrompts.createdAt,
|
|
1649
|
+
title: prompts.title,
|
|
1650
|
+
})
|
|
1651
|
+
.from(agentProfilePrompts)
|
|
1652
|
+
.innerJoin(prompts, eq(agentProfilePrompts.promptId, prompts.id))
|
|
1653
|
+
.where(eq(agentProfilePrompts.profileId, id))
|
|
1654
|
+
.orderBy(asc(agentProfilePrompts.createdAt));
|
|
1655
|
+
const promptsDetailed = rows.map((row, idx) => ({
|
|
1656
|
+
promptId: row.promptId,
|
|
1657
|
+
title: row.title,
|
|
1658
|
+
order: idx + 1,
|
|
1659
|
+
}));
|
|
1660
|
+
return { ...profile, prompts: promptsDetailed };
|
|
1661
|
+
}
|
|
1662
|
+
async listAgentProfilesWithPrompts(options = {}) {
|
|
1663
|
+
const base = await this.listAgentProfiles(options);
|
|
1664
|
+
if (!base.items.length)
|
|
1665
|
+
return { ...base, items: [] };
|
|
1666
|
+
const ids = base.items.map((p) => p.id);
|
|
1667
|
+
const { agentProfilePrompts, prompts } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1668
|
+
const { inArray, asc, eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1669
|
+
const rows = await this.db
|
|
1670
|
+
.select({
|
|
1671
|
+
profileId: agentProfilePrompts.profileId,
|
|
1672
|
+
promptId: agentProfilePrompts.promptId,
|
|
1673
|
+
createdAt: agentProfilePrompts.createdAt,
|
|
1674
|
+
title: prompts.title,
|
|
1675
|
+
})
|
|
1676
|
+
.from(agentProfilePrompts)
|
|
1677
|
+
.innerJoin(prompts, eq(agentProfilePrompts.promptId, prompts.id))
|
|
1678
|
+
.where(inArray(agentProfilePrompts.profileId, ids))
|
|
1679
|
+
.orderBy(asc(agentProfilePrompts.profileId), asc(agentProfilePrompts.createdAt));
|
|
1680
|
+
const grouped = new Map();
|
|
1681
|
+
for (const r of rows) {
|
|
1682
|
+
const pid = r.profileId;
|
|
1683
|
+
const arr = grouped.get(pid) ?? [];
|
|
1684
|
+
arr.push({
|
|
1685
|
+
promptId: r.promptId,
|
|
1686
|
+
title: r.title,
|
|
1687
|
+
createdAt: r.createdAt,
|
|
1688
|
+
});
|
|
1689
|
+
grouped.set(pid, arr);
|
|
1690
|
+
}
|
|
1691
|
+
const items = base.items.map((p) => {
|
|
1692
|
+
const arr = grouped.get(p.id) ?? [];
|
|
1693
|
+
const promptsDetailed = arr.map((row, idx) => ({
|
|
1694
|
+
promptId: row.promptId,
|
|
1695
|
+
title: row.title,
|
|
1696
|
+
order: idx + 1,
|
|
1697
|
+
}));
|
|
1698
|
+
return { ...p, prompts: promptsDetailed };
|
|
1699
|
+
});
|
|
1700
|
+
return { ...base, items };
|
|
1701
|
+
}
|
|
1702
|
+
async createAgent(data) {
|
|
1703
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1704
|
+
const now = new Date().toISOString();
|
|
1705
|
+
const { agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1706
|
+
const agent = {
|
|
1707
|
+
id: randomUUID(),
|
|
1708
|
+
...data,
|
|
1709
|
+
createdAt: now,
|
|
1710
|
+
updatedAt: now,
|
|
1711
|
+
};
|
|
1712
|
+
const profile = await this.getAgentProfile(agent.profileId);
|
|
1713
|
+
if (profile.projectId !== agent.projectId) {
|
|
1714
|
+
throw new error_types_1.ValidationError('Agent.profileId must belong to the same project as the agent.', {
|
|
1715
|
+
agentProjectId: agent.projectId,
|
|
1716
|
+
profileProjectId: profile.projectId,
|
|
1717
|
+
profileId: agent.profileId,
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
await this.db.insert(agents).values({
|
|
1721
|
+
id: agent.id,
|
|
1722
|
+
projectId: agent.projectId,
|
|
1723
|
+
profileId: agent.profileId,
|
|
1724
|
+
name: agent.name,
|
|
1725
|
+
createdAt: agent.createdAt,
|
|
1726
|
+
updatedAt: agent.updatedAt,
|
|
1727
|
+
});
|
|
1728
|
+
logger.info({ agentId: agent.id, projectId: agent.projectId }, 'Created agent');
|
|
1729
|
+
return agent;
|
|
1730
|
+
}
|
|
1731
|
+
async getAgent(id) {
|
|
1732
|
+
const { agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1733
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1734
|
+
const result = await this.db.select().from(agents).where(eq(agents.id, id)).limit(1);
|
|
1735
|
+
if (!result[0]) {
|
|
1736
|
+
throw new error_types_1.NotFoundError('Agent', id);
|
|
1737
|
+
}
|
|
1738
|
+
return result[0];
|
|
1739
|
+
}
|
|
1740
|
+
async listAgents(projectId, options = {}) {
|
|
1741
|
+
const { agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1742
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1743
|
+
const limit = options.limit || 100;
|
|
1744
|
+
const offset = options.offset || 0;
|
|
1745
|
+
const items = await this.db
|
|
1746
|
+
.select()
|
|
1747
|
+
.from(agents)
|
|
1748
|
+
.where(eq(agents.projectId, projectId))
|
|
1749
|
+
.limit(limit)
|
|
1750
|
+
.offset(offset);
|
|
1751
|
+
return {
|
|
1752
|
+
items: items,
|
|
1753
|
+
total: items.length,
|
|
1754
|
+
limit,
|
|
1755
|
+
offset,
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
async getAgentByName(projectId, name) {
|
|
1759
|
+
const { agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1760
|
+
const { and, eq, sql } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1761
|
+
const normalized = name.toLowerCase();
|
|
1762
|
+
const result = await this.db
|
|
1763
|
+
.select()
|
|
1764
|
+
.from(agents)
|
|
1765
|
+
.where(and(eq(agents.projectId, projectId), sql `lower(${agents.name}) = ${normalized}`))
|
|
1766
|
+
.limit(1);
|
|
1767
|
+
const record = result[0];
|
|
1768
|
+
if (!record) {
|
|
1769
|
+
throw new error_types_1.NotFoundError('Agent', `${projectId}:${name}`);
|
|
1770
|
+
}
|
|
1771
|
+
const agent = record;
|
|
1772
|
+
const profile = await this.getAgentProfile(agent.profileId);
|
|
1773
|
+
return { ...agent, profile };
|
|
1774
|
+
}
|
|
1775
|
+
async updateAgent(id, data) {
|
|
1776
|
+
const { agents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1777
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1778
|
+
const now = new Date().toISOString();
|
|
1779
|
+
if (data.projectId !== undefined || data.profileId !== undefined) {
|
|
1780
|
+
const current = await this.getAgent(id);
|
|
1781
|
+
const newProjectId = data.projectId ?? current.projectId;
|
|
1782
|
+
const newProfileId = data.profileId ?? current.profileId;
|
|
1783
|
+
const profile = await this.getAgentProfile(newProfileId);
|
|
1784
|
+
if (profile.projectId !== newProjectId) {
|
|
1785
|
+
throw new error_types_1.ValidationError('Agent.profileId must belong to the same project as the agent.', {
|
|
1786
|
+
agentProjectId: newProjectId,
|
|
1787
|
+
profileProjectId: profile.projectId,
|
|
1788
|
+
profileId: newProfileId,
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
await this.db
|
|
1793
|
+
.update(agents)
|
|
1794
|
+
.set({ ...data, updatedAt: now })
|
|
1795
|
+
.where(eq(agents.id, id));
|
|
1796
|
+
return this.getAgent(id);
|
|
1797
|
+
}
|
|
1798
|
+
async deleteAgent(id) {
|
|
1799
|
+
const { agents, sessions } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1800
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1801
|
+
const relatedSessions = await this.db.select().from(sessions).where(eq(sessions.agentId, id));
|
|
1802
|
+
const runningSessions = relatedSessions.filter((s) => s.status === 'running');
|
|
1803
|
+
if (runningSessions.length > 0) {
|
|
1804
|
+
throw new error_types_1.ConflictError(`Cannot delete agent: ${runningSessions.length} active session(s) are still running. Please terminate the active sessions first.`);
|
|
1805
|
+
}
|
|
1806
|
+
const completedSessions = relatedSessions.filter((s) => s.status === 'stopped' || s.status === 'failed');
|
|
1807
|
+
if (completedSessions.length > 0) {
|
|
1808
|
+
logger.info({ agentId: id, count: completedSessions.length }, 'Auto-deleting completed sessions for agent');
|
|
1809
|
+
for (const session of completedSessions) {
|
|
1810
|
+
await this.db.delete(sessions).where(eq(sessions.id, session.id));
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
await this.db.delete(agents).where(eq(agents.id, id));
|
|
1814
|
+
logger.info({ agentId: id, deletedSessions: completedSessions.length }, 'Deleted agent');
|
|
1815
|
+
}
|
|
1816
|
+
async createRecord(data) {
|
|
1817
|
+
const { randomUUID } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
1818
|
+
const now = new Date().toISOString();
|
|
1819
|
+
const { records, recordTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1820
|
+
const record = {
|
|
1821
|
+
id: randomUUID(),
|
|
1822
|
+
...data,
|
|
1823
|
+
version: 1,
|
|
1824
|
+
createdAt: now,
|
|
1825
|
+
updatedAt: now,
|
|
1826
|
+
};
|
|
1827
|
+
await this.db.insert(records).values({
|
|
1828
|
+
id: record.id,
|
|
1829
|
+
epicId: record.epicId,
|
|
1830
|
+
type: record.type,
|
|
1831
|
+
data: JSON.stringify(record.data),
|
|
1832
|
+
version: record.version,
|
|
1833
|
+
createdAt: record.createdAt,
|
|
1834
|
+
updatedAt: record.updatedAt,
|
|
1835
|
+
});
|
|
1836
|
+
if (data.tags?.length) {
|
|
1837
|
+
for (const tagName of data.tags) {
|
|
1838
|
+
const { eq, and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1839
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1840
|
+
const epic = await this.db.select().from(epics).where(eq(epics.id, data.epicId)).limit(1);
|
|
1841
|
+
const projectId = epic[0]?.projectId || null;
|
|
1842
|
+
let tag = await this.db
|
|
1843
|
+
.select()
|
|
1844
|
+
.from(tags)
|
|
1845
|
+
.where(and(eq(tags.name, tagName), or(eq(tags.projectId, projectId || ''), isNull(tags.projectId))))
|
|
1846
|
+
.limit(1);
|
|
1847
|
+
if (!tag[0]) {
|
|
1848
|
+
const newTag = await this.createTag({ projectId, name: tagName });
|
|
1849
|
+
tag = [newTag];
|
|
1850
|
+
}
|
|
1851
|
+
await this.db.insert(recordTags).values({
|
|
1852
|
+
recordId: record.id,
|
|
1853
|
+
tagId: tag[0].id,
|
|
1854
|
+
createdAt: now,
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
logger.info({ recordId: record.id, epicId: record.epicId, type: record.type }, 'Created record');
|
|
1859
|
+
return record;
|
|
1860
|
+
}
|
|
1861
|
+
async getRecord(id) {
|
|
1862
|
+
const { records, recordTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1863
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1864
|
+
const result = await this.db.select().from(records).where(eq(records.id, id)).limit(1);
|
|
1865
|
+
if (!result[0]) {
|
|
1866
|
+
throw new error_types_1.NotFoundError('Record', id);
|
|
1867
|
+
}
|
|
1868
|
+
const recordTagsResult = await this.db
|
|
1869
|
+
.select({ tag: tags })
|
|
1870
|
+
.from(recordTags)
|
|
1871
|
+
.innerJoin(tags, eq(recordTags.tagId, tags.id))
|
|
1872
|
+
.where(eq(recordTags.recordId, id));
|
|
1873
|
+
return {
|
|
1874
|
+
...result[0],
|
|
1875
|
+
data: result[0].data,
|
|
1876
|
+
tags: recordTagsResult.map((rt) => rt.tag.name),
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
async listRecords(epicId, options = {}) {
|
|
1880
|
+
const { records } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1881
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1882
|
+
const limit = options.limit || 100;
|
|
1883
|
+
const offset = options.offset || 0;
|
|
1884
|
+
const items = await this.db
|
|
1885
|
+
.select()
|
|
1886
|
+
.from(records)
|
|
1887
|
+
.where(eq(records.epicId, epicId))
|
|
1888
|
+
.limit(limit)
|
|
1889
|
+
.offset(offset);
|
|
1890
|
+
const itemsWithTags = await Promise.all(items.map((item) => this.getRecord(item.id)));
|
|
1891
|
+
return {
|
|
1892
|
+
items: itemsWithTags,
|
|
1893
|
+
total: items.length,
|
|
1894
|
+
limit,
|
|
1895
|
+
offset,
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
async updateRecord(id, data, expectedVersion) {
|
|
1899
|
+
const { records, recordTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1900
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1901
|
+
const now = new Date().toISOString();
|
|
1902
|
+
const current = await this.getRecord(id);
|
|
1903
|
+
if (current.version !== expectedVersion) {
|
|
1904
|
+
throw new error_types_1.OptimisticLockError('Record', id, {
|
|
1905
|
+
expectedVersion,
|
|
1906
|
+
actualVersion: current.version,
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
const updateData = {};
|
|
1910
|
+
if (data.data !== undefined) {
|
|
1911
|
+
updateData.data = JSON.stringify(data.data);
|
|
1912
|
+
}
|
|
1913
|
+
if (data.type !== undefined) {
|
|
1914
|
+
updateData.type = data.type;
|
|
1915
|
+
}
|
|
1916
|
+
await this.db
|
|
1917
|
+
.update(records)
|
|
1918
|
+
.set({ ...updateData, version: expectedVersion + 1, updatedAt: now })
|
|
1919
|
+
.where(eq(records.id, id));
|
|
1920
|
+
if (data.tags !== undefined) {
|
|
1921
|
+
await this.db.delete(recordTags).where(eq(recordTags.recordId, id));
|
|
1922
|
+
if (data.tags.length > 0) {
|
|
1923
|
+
const { epics } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1924
|
+
const epic = await this.db
|
|
1925
|
+
.select()
|
|
1926
|
+
.from(epics)
|
|
1927
|
+
.where(eq(epics.id, current.epicId))
|
|
1928
|
+
.limit(1);
|
|
1929
|
+
const projectId = epic[0]?.projectId || null;
|
|
1930
|
+
for (const tagName of data.tags) {
|
|
1931
|
+
const { and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1932
|
+
let tag = await this.db
|
|
1933
|
+
.select()
|
|
1934
|
+
.from(tags)
|
|
1935
|
+
.where(and(eq(tags.name, tagName), or(eq(tags.projectId, projectId || ''), isNull(tags.projectId))))
|
|
1936
|
+
.limit(1);
|
|
1937
|
+
if (!tag[0]) {
|
|
1938
|
+
const newTag = await this.createTag({ projectId, name: tagName });
|
|
1939
|
+
tag = [newTag];
|
|
1940
|
+
}
|
|
1941
|
+
await this.db.insert(recordTags).values({
|
|
1942
|
+
recordId: id,
|
|
1943
|
+
tagId: tag[0].id,
|
|
1944
|
+
createdAt: now,
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
logger.info({ recordId: id }, 'Updated record');
|
|
1950
|
+
return this.getRecord(id);
|
|
1951
|
+
}
|
|
1952
|
+
async deleteRecord(id) {
|
|
1953
|
+
const { records } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1954
|
+
const { eq } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1955
|
+
await this.db.delete(records).where(eq(records.id, id));
|
|
1956
|
+
logger.info({ recordId: id }, 'Deleted record');
|
|
1957
|
+
}
|
|
1958
|
+
async markMessageAsRead(messageId, agentId, readAt) {
|
|
1959
|
+
const { chatMessageReads } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
1960
|
+
const { eq, and } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
1961
|
+
const existing = await this.db
|
|
1962
|
+
.select()
|
|
1963
|
+
.from(chatMessageReads)
|
|
1964
|
+
.where(and(eq(chatMessageReads.messageId, messageId), eq(chatMessageReads.agentId, agentId)))
|
|
1965
|
+
.limit(1);
|
|
1966
|
+
if (!existing[0]) {
|
|
1967
|
+
await this.db.insert(chatMessageReads).values({
|
|
1968
|
+
messageId,
|
|
1969
|
+
agentId,
|
|
1970
|
+
readAt,
|
|
1971
|
+
});
|
|
1972
|
+
logger.info({ messageId, agentId }, 'Marked message as read');
|
|
1973
|
+
}
|
|
1974
|
+
else {
|
|
1975
|
+
logger.debug({ messageId, agentId }, 'Message already marked as read');
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
normalizeTagList(tags) {
|
|
1979
|
+
if (!tags?.length) {
|
|
1980
|
+
return [];
|
|
1981
|
+
}
|
|
1982
|
+
const unique = new Set();
|
|
1983
|
+
for (const tag of tags) {
|
|
1984
|
+
const trimmed = tag.trim();
|
|
1985
|
+
if (trimmed) {
|
|
1986
|
+
unique.add(trimmed);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
return Array.from(unique);
|
|
1990
|
+
}
|
|
1991
|
+
slugify(value) {
|
|
1992
|
+
return value
|
|
1993
|
+
.trim()
|
|
1994
|
+
.toLowerCase()
|
|
1995
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
1996
|
+
.replace(/-{2,}/g, '-')
|
|
1997
|
+
.replace(/^-+|-+$/g, '');
|
|
1998
|
+
}
|
|
1999
|
+
async generateDocumentSlug(projectId, desired, excludeId) {
|
|
2000
|
+
const { documents } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
2001
|
+
const { eq, and, isNull, ne } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
2002
|
+
const base = this.slugify(desired || 'document') || 'document';
|
|
2003
|
+
let candidate = base;
|
|
2004
|
+
let attempt = 1;
|
|
2005
|
+
while (true) {
|
|
2006
|
+
const projectCondition = projectId === null ? isNull(documents.projectId) : eq(documents.projectId, projectId);
|
|
2007
|
+
const slugCondition = eq(documents.slug, candidate);
|
|
2008
|
+
const whereClause = excludeId
|
|
2009
|
+
? and(slugCondition, projectCondition, ne(documents.id, excludeId))
|
|
2010
|
+
: and(slugCondition, projectCondition);
|
|
2011
|
+
const existing = await this.db
|
|
2012
|
+
.select({ id: documents.id })
|
|
2013
|
+
.from(documents)
|
|
2014
|
+
.where(whereClause)
|
|
2015
|
+
.limit(1);
|
|
2016
|
+
if (!existing[0]) {
|
|
2017
|
+
return candidate;
|
|
2018
|
+
}
|
|
2019
|
+
attempt += 1;
|
|
2020
|
+
candidate = `${base}-${attempt}`;
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
async setDocumentTags(documentId, tagNames, projectId) {
|
|
2024
|
+
const normalizedTags = this.normalizeTagList(tagNames);
|
|
2025
|
+
const { documentTags, tags } = await Promise.resolve().then(() => __importStar(require('../db/schema')));
|
|
2026
|
+
const { eq, and, or, isNull } = await Promise.resolve().then(() => __importStar(require('drizzle-orm')));
|
|
2027
|
+
await this.db.delete(documentTags).where(eq(documentTags.documentId, documentId));
|
|
2028
|
+
for (const tagName of normalizedTags) {
|
|
2029
|
+
const projectCondition = projectId === null
|
|
2030
|
+
? isNull(tags.projectId)
|
|
2031
|
+
: or(eq(tags.projectId, projectId), isNull(tags.projectId));
|
|
2032
|
+
const existing = await this.db
|
|
2033
|
+
.select()
|
|
2034
|
+
.from(tags)
|
|
2035
|
+
.where(and(eq(tags.name, tagName), projectCondition))
|
|
2036
|
+
.limit(1);
|
|
2037
|
+
let tagId = existing[0]?.id;
|
|
2038
|
+
if (!tagId) {
|
|
2039
|
+
const newTag = await this.createTag({ projectId, name: tagName });
|
|
2040
|
+
tagId = newTag.id;
|
|
2041
|
+
}
|
|
2042
|
+
await this.db.insert(documentTags).values({
|
|
2043
|
+
documentId,
|
|
2044
|
+
tagId,
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
exports.LocalStorageService = LocalStorageService;
|
|
2050
|
+
exports.LocalStorageService = LocalStorageService = __decorate([
|
|
2051
|
+
(0, common_1.Injectable)(),
|
|
2052
|
+
__param(0, (0, common_1.Inject)(db_provider_1.DB_CONNECTION)),
|
|
2053
|
+
__metadata("design:paramtypes", [better_sqlite3_1.BetterSQLite3Database])
|
|
2054
|
+
], LocalStorageService);
|
|
2055
|
+
function safePreview(v) {
|
|
2056
|
+
try {
|
|
2057
|
+
if (v === undefined)
|
|
2058
|
+
return 'undefined';
|
|
2059
|
+
if (v === null)
|
|
2060
|
+
return 'null';
|
|
2061
|
+
if (typeof v === 'string')
|
|
2062
|
+
return v.length > 200 ? v.slice(0, 200) + '…' : v;
|
|
2063
|
+
return JSON.stringify(v).slice(0, 200);
|
|
2064
|
+
}
|
|
2065
|
+
catch {
|
|
2066
|
+
return '[unserializable]';
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
//# sourceMappingURL=local-storage.service.js.map
|