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
package/dist/cli.js
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Devchain CLI
|
|
4
|
+
* start command: boots the local app API + UI, picks a port, and opens browser.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* eslint-disable no-console */
|
|
8
|
+
|
|
9
|
+
const { Command } = require('commander');
|
|
10
|
+
const getPort = require('get-port');
|
|
11
|
+
const open = require('open');
|
|
12
|
+
const { join, dirname, basename } = require('path');
|
|
13
|
+
const { existsSync, writeFileSync, readFileSync, unlinkSync, mkdirSync } = require('fs');
|
|
14
|
+
const { homedir, platform } = require('os');
|
|
15
|
+
const { spawn, execSync } = require('child_process');
|
|
16
|
+
const { InteractiveCLI } = require('./lib/interactive-cli');
|
|
17
|
+
|
|
18
|
+
async function waitForHealth(url, { timeoutMs = 15000, intervalMs = 250 } = {}) {
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
while (Date.now() - start < timeoutMs) {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(url);
|
|
23
|
+
if (res.ok) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
} catch (_) {
|
|
27
|
+
// not ready yet
|
|
28
|
+
}
|
|
29
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveOpenOptions() {
|
|
35
|
+
const val = process.env.DEVCHAIN_BROWSER || process.env.BROWSER;
|
|
36
|
+
if (val && typeof val === 'string' && val.trim()) {
|
|
37
|
+
const parts = val.trim().split(/\s+/);
|
|
38
|
+
return { app: { name: parts[0], arguments: parts.slice(1) } };
|
|
39
|
+
}
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function fetchWithTimeout(url, options = {}, timeoutMs = 2000) {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const id = setTimeout(() => controller.abort(), timeoutMs);
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(url, { ...(options || {}), signal: controller.signal });
|
|
48
|
+
return res;
|
|
49
|
+
} finally {
|
|
50
|
+
clearTimeout(id);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isBinaryInstalled(cmd) {
|
|
55
|
+
try {
|
|
56
|
+
const out = execSync(`which ${cmd}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
|
|
57
|
+
.trim();
|
|
58
|
+
return out || null;
|
|
59
|
+
} catch (_) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function detectInstalledProviders() {
|
|
65
|
+
const detected = new Map();
|
|
66
|
+
const codexPath = isBinaryInstalled('codex');
|
|
67
|
+
const claudePath = isBinaryInstalled('claude');
|
|
68
|
+
if (codexPath) detected.set('codex', codexPath);
|
|
69
|
+
if (claudePath) detected.set('claude', claudePath);
|
|
70
|
+
return detected; // Map<name, absolutePath>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function ensureProvidersInDb(baseUrl, detected, log) {
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(`${baseUrl}/api/providers`);
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
log('warn', 'Failed to fetch providers; skipping ensure');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const data = await res.json();
|
|
81
|
+
const existing = new Set((data?.items || []).map((p) => p.name));
|
|
82
|
+
const toCreate = Array.from(detected.keys()).filter((n) => !existing.has(n));
|
|
83
|
+
for (const name of toCreate) {
|
|
84
|
+
const body = {
|
|
85
|
+
name,
|
|
86
|
+
// pass command name to allow normalization; controller validates presence on PATH
|
|
87
|
+
binPath: name,
|
|
88
|
+
};
|
|
89
|
+
try {
|
|
90
|
+
const createRes = await fetch(`${baseUrl}/api/providers`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: { 'Content-Type': 'application/json' },
|
|
93
|
+
body: JSON.stringify(body),
|
|
94
|
+
});
|
|
95
|
+
if (createRes.ok) {
|
|
96
|
+
log('info', 'Created provider', { name });
|
|
97
|
+
} else {
|
|
98
|
+
const errText = await createRes.text();
|
|
99
|
+
log('warn', 'Failed to create provider', { name, status: createRes.status, errText });
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {
|
|
102
|
+
log('warn', 'Error creating provider', { name, error: e instanceof Error ? e.message : String(e) });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
log('warn', 'Provider DB ensure failed', { error: e instanceof Error ? e.message : String(e) });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function validateMcpForProviders(baseUrl, cli, opts, log, projectPath) {
|
|
111
|
+
try {
|
|
112
|
+
// Fetch all providers
|
|
113
|
+
const res = await fetch(`${baseUrl}/api/providers`);
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
if (opts.foreground) {
|
|
116
|
+
log('warn', 'Failed to fetch providers for MCP validation; skipping', { status: res.status });
|
|
117
|
+
} else {
|
|
118
|
+
cli.warn('Skipping MCP validation (failed to fetch providers)');
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const data = await res.json();
|
|
124
|
+
const providers = data?.items || [];
|
|
125
|
+
if (providers.length === 0) {
|
|
126
|
+
if (opts.foreground) {
|
|
127
|
+
log('info', 'No providers to validate for MCP');
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Interactive: show spinner
|
|
133
|
+
const spinner = opts.foreground ? null : cli.spinner('Validating MCP');
|
|
134
|
+
if (spinner) spinner.start();
|
|
135
|
+
|
|
136
|
+
const results = [];
|
|
137
|
+
for (const provider of providers) {
|
|
138
|
+
try {
|
|
139
|
+
const body = projectPath ? JSON.stringify({ projectPath }) : JSON.stringify({});
|
|
140
|
+
const ensureRes = await fetch(`${baseUrl}/api/providers/${provider.id}/mcp/ensure`, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
body,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (ensureRes.ok) {
|
|
147
|
+
const result = await ensureRes.json();
|
|
148
|
+
results.push({
|
|
149
|
+
name: provider.name,
|
|
150
|
+
action: result.action,
|
|
151
|
+
success: true,
|
|
152
|
+
endpoint: result.endpoint,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (opts.foreground) {
|
|
156
|
+
log('info', 'MCP validation complete', {
|
|
157
|
+
provider: provider.name,
|
|
158
|
+
action: result.action,
|
|
159
|
+
endpoint: result.endpoint,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
const errText = await ensureRes.text();
|
|
164
|
+
results.push({
|
|
165
|
+
name: provider.name,
|
|
166
|
+
success: false,
|
|
167
|
+
error: errText,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (opts.foreground) {
|
|
171
|
+
log('warn', 'MCP validation failed', {
|
|
172
|
+
provider: provider.name,
|
|
173
|
+
status: ensureRes.status,
|
|
174
|
+
error: errText,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} catch (e) {
|
|
179
|
+
results.push({
|
|
180
|
+
name: provider.name,
|
|
181
|
+
success: false,
|
|
182
|
+
error: e instanceof Error ? e.message : String(e),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
if (opts.foreground) {
|
|
186
|
+
log('warn', 'MCP validation error', {
|
|
187
|
+
provider: provider.name,
|
|
188
|
+
error: e instanceof Error ? e.message : String(e),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (spinner) spinner.stop('✓');
|
|
195
|
+
|
|
196
|
+
// Display results in interactive mode
|
|
197
|
+
if (!opts.foreground) {
|
|
198
|
+
for (const result of results) {
|
|
199
|
+
if (result.success) {
|
|
200
|
+
const actionText = {
|
|
201
|
+
added: 'configured',
|
|
202
|
+
fixed_mismatch: 'fixed',
|
|
203
|
+
already_configured: 'ready',
|
|
204
|
+
}[result.action] || result.action;
|
|
205
|
+
cli.success(`${result.name}: ${actionText}`);
|
|
206
|
+
} else {
|
|
207
|
+
cli.error(`${result.name}: validation failed`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (e) {
|
|
212
|
+
if (opts.foreground) {
|
|
213
|
+
log('warn', 'MCP validation step failed', { error: e instanceof Error ? e.message : String(e) });
|
|
214
|
+
} else {
|
|
215
|
+
cli.warn('MCP validation failed');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function parseDbPath(db) {
|
|
221
|
+
if (!db) return {};
|
|
222
|
+
// If a directory was provided, use it as DB_PATH and keep default filename
|
|
223
|
+
// If a file path was provided, split into dir + filename
|
|
224
|
+
try {
|
|
225
|
+
const dir = dirname(db);
|
|
226
|
+
const file = basename(db);
|
|
227
|
+
const looksLikeFile = /\.[a-zA-Z0-9]+$/.test(file);
|
|
228
|
+
if (looksLikeFile) {
|
|
229
|
+
return { DB_PATH: dir, DB_FILENAME: file };
|
|
230
|
+
}
|
|
231
|
+
return { DB_PATH: db };
|
|
232
|
+
} catch {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getPidFilePath() {
|
|
238
|
+
const devchainDir = join(homedir(), '.devchain');
|
|
239
|
+
if (!existsSync(devchainDir)) {
|
|
240
|
+
mkdirSync(devchainDir, { recursive: true });
|
|
241
|
+
}
|
|
242
|
+
return join(devchainDir, 'devchain.pid');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function writePidFile(port) {
|
|
246
|
+
const pidFile = getPidFilePath();
|
|
247
|
+
const data = JSON.stringify({ pid: process.pid, port, timestamp: Date.now() });
|
|
248
|
+
writeFileSync(pidFile, data, 'utf8');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function readPidFile() {
|
|
252
|
+
const pidFile = getPidFilePath();
|
|
253
|
+
if (!existsSync(pidFile)) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
const data = readFileSync(pidFile, 'utf8');
|
|
258
|
+
return JSON.parse(data);
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function removePidFile() {
|
|
265
|
+
const pidFile = getPidFilePath();
|
|
266
|
+
if (existsSync(pidFile)) {
|
|
267
|
+
unlinkSync(pidFile);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function isProcessRunning(pid) {
|
|
272
|
+
try {
|
|
273
|
+
// Sending signal 0 checks if process exists without killing it
|
|
274
|
+
process.kill(pid, 0);
|
|
275
|
+
return true;
|
|
276
|
+
} catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function isTmuxInstalled() {
|
|
282
|
+
try {
|
|
283
|
+
execSync('which tmux', { stdio: 'ignore' });
|
|
284
|
+
return true;
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function getOSType() {
|
|
291
|
+
const plat = platform();
|
|
292
|
+
if (plat === 'darwin') return 'macos';
|
|
293
|
+
if (plat === 'win32') return 'windows';
|
|
294
|
+
|
|
295
|
+
// Detect Linux distribution
|
|
296
|
+
try {
|
|
297
|
+
const release = execSync('cat /etc/os-release', { encoding: 'utf8' });
|
|
298
|
+
if (/debian|ubuntu/i.test(release)) return 'debian';
|
|
299
|
+
if (/fedora/i.test(release)) return 'fedora';
|
|
300
|
+
if (/rhel|centos|rocky|alma/i.test(release)) return 'rhel';
|
|
301
|
+
if (/arch/i.test(release)) return 'arch';
|
|
302
|
+
} catch {
|
|
303
|
+
// Fallback if /etc/os-release doesn't exist
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return 'linux-generic';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function getTmuxErrorMessage(osType) {
|
|
310
|
+
const baseMessage = 'Error: tmux is not installed\n\n' +
|
|
311
|
+
'Devchain requires tmux for terminal session management.\n\n';
|
|
312
|
+
|
|
313
|
+
const verifyMessage = '\nAfter installation, verify with:\n' +
|
|
314
|
+
' which tmux\n\n' +
|
|
315
|
+
'For advanced users: bypass this check with DEVCHAIN_SKIP_TMUX_CHECK=1\n';
|
|
316
|
+
|
|
317
|
+
switch (osType) {
|
|
318
|
+
case 'macos':
|
|
319
|
+
return baseMessage +
|
|
320
|
+
'To install tmux on macOS, run:\n' +
|
|
321
|
+
' brew install tmux' +
|
|
322
|
+
verifyMessage;
|
|
323
|
+
|
|
324
|
+
case 'debian':
|
|
325
|
+
return baseMessage +
|
|
326
|
+
'To install tmux on Debian/Ubuntu, run:\n' +
|
|
327
|
+
' sudo apt update && sudo apt install tmux' +
|
|
328
|
+
verifyMessage;
|
|
329
|
+
|
|
330
|
+
case 'fedora':
|
|
331
|
+
return baseMessage +
|
|
332
|
+
'To install tmux on Fedora, run:\n' +
|
|
333
|
+
' sudo dnf install tmux' +
|
|
334
|
+
verifyMessage;
|
|
335
|
+
|
|
336
|
+
case 'rhel':
|
|
337
|
+
return baseMessage +
|
|
338
|
+
'To install tmux on RHEL/CentOS, run:\n' +
|
|
339
|
+
' sudo yum install tmux' +
|
|
340
|
+
verifyMessage;
|
|
341
|
+
|
|
342
|
+
case 'arch':
|
|
343
|
+
return baseMessage +
|
|
344
|
+
'To install tmux on Arch Linux, run:\n' +
|
|
345
|
+
' sudo pacman -S tmux' +
|
|
346
|
+
verifyMessage;
|
|
347
|
+
|
|
348
|
+
case 'linux-generic':
|
|
349
|
+
default:
|
|
350
|
+
return baseMessage +
|
|
351
|
+
'To install tmux, use your distribution\'s package manager:\n' +
|
|
352
|
+
' - Debian/Ubuntu: sudo apt install tmux\n' +
|
|
353
|
+
' - Fedora: sudo dnf install tmux\n' +
|
|
354
|
+
' - RHEL/CentOS: sudo yum install tmux\n' +
|
|
355
|
+
' - Arch: sudo pacman -S tmux\n\n' +
|
|
356
|
+
'Or visit: https://github.com/tmux/tmux/wiki/Installing' +
|
|
357
|
+
verifyMessage;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function main(argv) {
|
|
362
|
+
const program = new Command();
|
|
363
|
+
program
|
|
364
|
+
.name('devchain')
|
|
365
|
+
.description('Devchain — Local-first AI agent orchestration')
|
|
366
|
+
.version('0.1.0');
|
|
367
|
+
|
|
368
|
+
const startCommand = program
|
|
369
|
+
.command('start [args...]')
|
|
370
|
+
.description('Start the Devchain local app')
|
|
371
|
+
.option('-p, --port <number>', 'Port to listen on (default: 3000 or next free)')
|
|
372
|
+
.option('-f, --foreground', 'Run in foreground with interactive output (default). Shows user-friendly startup with colors and spinners.')
|
|
373
|
+
.option('-d, --detach', 'Run in background as a detached process. Use "devchain stop" to stop it.')
|
|
374
|
+
.option('--no-open', 'Do not open a browser window')
|
|
375
|
+
.option('--db <path>', 'Path to database directory or file (overrides DB_PATH/DB_FILENAME)')
|
|
376
|
+
.option('--project <path>', 'Initial project root path; creates project if missing')
|
|
377
|
+
.option(
|
|
378
|
+
'--log-level <level>',
|
|
379
|
+
'Set log verbosity: error (errors only), warn, info, debug, or trace. ' +
|
|
380
|
+
'Default: "error" (clean) in interactive mode, "info" in foreground. ' +
|
|
381
|
+
'Respects LOG_LEVEL env var if set.'
|
|
382
|
+
)
|
|
383
|
+
.action(async (args, opts) => {
|
|
384
|
+
// Check if "help" was passed as an argument
|
|
385
|
+
if (args && args.length > 0 && args[0] === 'help') {
|
|
386
|
+
startCommand.help();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
// Normalize defaults for negatable options (Commander may leave undefined)
|
|
390
|
+
if (typeof opts.open === 'undefined') {
|
|
391
|
+
opts.open = true;
|
|
392
|
+
}
|
|
393
|
+
// Detached mode only if explicitly requested
|
|
394
|
+
const shouldDetach = opts.detach === true;
|
|
395
|
+
|
|
396
|
+
// If detached mode, respawn as a background process
|
|
397
|
+
if (shouldDetach) {
|
|
398
|
+
const args = process.argv.slice(2).filter(arg => arg !== '-d' && arg !== '--detach');
|
|
399
|
+
// Add internal flag to prevent infinite respawning
|
|
400
|
+
args.push('--internal-detached-child');
|
|
401
|
+
|
|
402
|
+
const child = spawn(process.execPath, [__filename, ...args], {
|
|
403
|
+
detached: true,
|
|
404
|
+
stdio: 'ignore',
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
child.unref();
|
|
408
|
+
|
|
409
|
+
console.log(`Devchain is starting in background (PID ${child.pid})...`);
|
|
410
|
+
console.log('Use "devchain stop" to stop it.');
|
|
411
|
+
process.exit(0);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Initialize interactive CLI (user-friendly output unless in foreground mode)
|
|
415
|
+
const cli = new InteractiveCLI({
|
|
416
|
+
interactive: !opts.foreground,
|
|
417
|
+
colors: true,
|
|
418
|
+
spinners: true
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const log = (level, msg, extra) => {
|
|
422
|
+
const entry = { level, msg, time: new Date().toISOString(), ...(extra || {}) };
|
|
423
|
+
console.log(JSON.stringify(entry));
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// Show startup banner (interactive mode only)
|
|
427
|
+
if (!opts.foreground) {
|
|
428
|
+
cli.blank();
|
|
429
|
+
cli.info('Starting Devchain...');
|
|
430
|
+
cli.blank();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Tmux preflight check
|
|
434
|
+
const skipTmuxCheck = process.env.DEVCHAIN_SKIP_TMUX_CHECK === '1';
|
|
435
|
+
if (skipTmuxCheck) {
|
|
436
|
+
if (opts.foreground) {
|
|
437
|
+
log('info', 'Skipping tmux check (DEVCHAIN_SKIP_TMUX_CHECK=1)', { skipReason: 'env_var' });
|
|
438
|
+
} else {
|
|
439
|
+
cli.info('Skipping tmux check (DEVCHAIN_SKIP_TMUX_CHECK=1)');
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
const osType = getOSType();
|
|
443
|
+
|
|
444
|
+
if (osType === 'windows') {
|
|
445
|
+
if (opts.foreground) {
|
|
446
|
+
log('info', 'Skipping tmux check on Windows', { skipReason: 'windows', platform: 'win32' });
|
|
447
|
+
} else {
|
|
448
|
+
cli.info('Skipping tmux check on Windows');
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
// Interactive: show step
|
|
452
|
+
if (!opts.foreground) {
|
|
453
|
+
cli.step('Checking tmux');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Check if tmux is installed
|
|
457
|
+
if (!isTmuxInstalled()) {
|
|
458
|
+
if (opts.foreground) {
|
|
459
|
+
log('error', 'tmux not found; aborting startup', { platform: osType, checked: 'which tmux' });
|
|
460
|
+
} else {
|
|
461
|
+
cli.stepDone('✗ not found');
|
|
462
|
+
cli.blank();
|
|
463
|
+
}
|
|
464
|
+
console.error('\n' + getTmuxErrorMessage(osType));
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// tmux found, log success
|
|
469
|
+
try {
|
|
470
|
+
const tmuxPath = execSync('which tmux', { encoding: 'utf8' }).trim();
|
|
471
|
+
if (opts.foreground) {
|
|
472
|
+
log('info', 'tmux found', { tmuxPath });
|
|
473
|
+
} else {
|
|
474
|
+
cli.stepDone('✓ found');
|
|
475
|
+
}
|
|
476
|
+
} catch {
|
|
477
|
+
// Shouldn't happen since we just checked, but handle gracefully
|
|
478
|
+
if (opts.foreground) {
|
|
479
|
+
log('info', 'tmux check passed');
|
|
480
|
+
} else {
|
|
481
|
+
cli.stepDone('✓');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Provider detection (Linux/macOS only)
|
|
488
|
+
const skipProviderCheck = process.env.DEVCHAIN_SKIP_PROVIDER_CHECK === '1';
|
|
489
|
+
const plat = platform();
|
|
490
|
+
if (skipProviderCheck) {
|
|
491
|
+
if (opts.foreground) {
|
|
492
|
+
log('info', 'Skipping provider check (DEVCHAIN_SKIP_PROVIDER_CHECK=1)', {
|
|
493
|
+
skipReason: 'env_var',
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
cli.info('Skipping provider check (DEVCHAIN_SKIP_PROVIDER_CHECK=1)');
|
|
497
|
+
}
|
|
498
|
+
} else if (plat === 'win32') {
|
|
499
|
+
if (opts.foreground) {
|
|
500
|
+
log('info', 'Skipping provider check on Windows', { skipReason: 'windows' });
|
|
501
|
+
} else {
|
|
502
|
+
cli.info('Skipping provider check on Windows');
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
// Interactive: show step
|
|
506
|
+
if (!opts.foreground) {
|
|
507
|
+
cli.step('Detecting providers');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const providersDetected = detectInstalledProviders();
|
|
511
|
+
if (providersDetected.size === 0) {
|
|
512
|
+
// Provide guidance and exit prior to server startup
|
|
513
|
+
const guide = [
|
|
514
|
+
'No provider binaries detected on PATH. Install at least one provider and retry.',
|
|
515
|
+
'Checked: "which codex" and "which claude"',
|
|
516
|
+
'Examples:',
|
|
517
|
+
' - Install Codex: npm i -g @twitech/codex-cli (example) or follow provider docs',
|
|
518
|
+
' - Install Claude: brew install claude-cli (example) or follow provider docs',
|
|
519
|
+
'Advanced: bypass with DEVCHAIN_SKIP_PROVIDER_CHECK=1',
|
|
520
|
+
].join('\n');
|
|
521
|
+
if (opts.foreground) {
|
|
522
|
+
log('error', 'No providers found; aborting startup', {
|
|
523
|
+
checked: ['which codex', 'which claude'],
|
|
524
|
+
});
|
|
525
|
+
} else {
|
|
526
|
+
cli.stepDone('✗ none found');
|
|
527
|
+
cli.blank();
|
|
528
|
+
}
|
|
529
|
+
console.error('\n' + guide + '\n');
|
|
530
|
+
process.exit(1);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// stash on opts for later ensure step after server ready
|
|
534
|
+
opts.__providersDetected = providersDetected;
|
|
535
|
+
const providerNames = Array.from(providersDetected.keys());
|
|
536
|
+
|
|
537
|
+
if (opts.foreground) {
|
|
538
|
+
log('info', 'Detected providers', {
|
|
539
|
+
providers: Array.from(providersDetected.entries()).map(([name, p]) => ({ name, path: p })),
|
|
540
|
+
});
|
|
541
|
+
} else {
|
|
542
|
+
cli.stepDone(`✓ ${providerNames.join(', ')}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const preferPort = opts.port ? Number(opts.port) : 3000;
|
|
547
|
+
const port = await getPort({ port: preferPort });
|
|
548
|
+
|
|
549
|
+
// Apply env before requiring the server
|
|
550
|
+
process.env.PORT = String(port);
|
|
551
|
+
process.env.HOST = process.env.HOST || '127.0.0.1';
|
|
552
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
|
553
|
+
const dbEnv = parseDbPath(opts.db);
|
|
554
|
+
if (dbEnv.DB_PATH) process.env.DB_PATH = dbEnv.DB_PATH;
|
|
555
|
+
if (dbEnv.DB_FILENAME) process.env.DB_FILENAME = dbEnv.DB_FILENAME;
|
|
556
|
+
|
|
557
|
+
// Set log level with priority: --log-level flag > existing env/dotenv > mode defaults
|
|
558
|
+
if (opts.logLevel) {
|
|
559
|
+
// Highest priority: explicit CLI flag always wins
|
|
560
|
+
process.env.LOG_LEVEL = opts.logLevel;
|
|
561
|
+
} else if (!process.env.LOG_LEVEL) {
|
|
562
|
+
// No LOG_LEVEL set anywhere: use mode defaults
|
|
563
|
+
// Interactive mode: only show errors (clean output)
|
|
564
|
+
// Foreground mode: show all logs (debugging)
|
|
565
|
+
// Use Pino log levels: silent, fatal, error, warn, info, debug, trace
|
|
566
|
+
process.env.LOG_LEVEL = opts.foreground ? 'info' : 'error';
|
|
567
|
+
}
|
|
568
|
+
// If LOG_LEVEL is already set in env or .env file, respect it
|
|
569
|
+
|
|
570
|
+
// Ensure built server exists
|
|
571
|
+
// Prefer bundled server in dist/server (copied at pack-time); fallback to workspace path.
|
|
572
|
+
let serverEntry = join(__dirname, '..', 'dist', 'server', 'main.js');
|
|
573
|
+
if (!existsSync(serverEntry)) {
|
|
574
|
+
serverEntry = join(__dirname, '..', 'apps', 'local-app', 'dist', 'main.js');
|
|
575
|
+
}
|
|
576
|
+
if (!existsSync(serverEntry)) {
|
|
577
|
+
log('error', 'Built server not found. Please build first (pnpm --filter local-app build).', {
|
|
578
|
+
expected: serverEntry,
|
|
579
|
+
});
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Start server (main.js bootstraps immediately)
|
|
584
|
+
const spinner = opts.foreground ? null : cli.spinner('Starting server');
|
|
585
|
+
if (spinner) spinner.start();
|
|
586
|
+
|
|
587
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
588
|
+
require(serverEntry);
|
|
589
|
+
|
|
590
|
+
const baseUrl = `http://${process.env.HOST}:${port}`;
|
|
591
|
+
const ready = await waitForHealth(`${baseUrl}/health`);
|
|
592
|
+
if (!ready) {
|
|
593
|
+
log('error', 'Server did not become ready in time', { url: baseUrl });
|
|
594
|
+
if (spinner) {
|
|
595
|
+
spinner.stop('✗ timeout');
|
|
596
|
+
cli.blank();
|
|
597
|
+
}
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (spinner) {
|
|
602
|
+
spinner.stop('✓ ready', true);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (opts.foreground) {
|
|
606
|
+
log('info', `Devchain is running at ${baseUrl}`);
|
|
607
|
+
log('info', `API docs: ${baseUrl}/api/docs`);
|
|
608
|
+
console.log(`\nDevchain is running at ${baseUrl}`);
|
|
609
|
+
console.log(`API docs: ${baseUrl}/api/docs`);
|
|
610
|
+
console.log('Press Ctrl+C to stop.\n');
|
|
611
|
+
} else {
|
|
612
|
+
cli.blank();
|
|
613
|
+
cli.success(`Server ready at ${baseUrl}`);
|
|
614
|
+
cli.info(`API docs: ${baseUrl}/api/docs`);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Ensure provider rows exist (idempotent) before opening UI
|
|
618
|
+
if (opts.__providersDetected && opts.__providersDetected.size > 0) {
|
|
619
|
+
await ensureProvidersInDb(baseUrl, opts.__providersDetected, log);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Determine startup path for MCP validation and URL
|
|
623
|
+
const startupPath = opts.project && typeof opts.project === 'string' && opts.project.trim()
|
|
624
|
+
? opts.project.trim()
|
|
625
|
+
: process.cwd();
|
|
626
|
+
|
|
627
|
+
// Validate MCP for all providers (with project context)
|
|
628
|
+
await validateMcpForProviders(baseUrl, cli, opts, log, startupPath);
|
|
629
|
+
|
|
630
|
+
// Determine URL to open based on project path
|
|
631
|
+
let urlToOpen = baseUrl;
|
|
632
|
+
try {
|
|
633
|
+
const byPathUrl = `${baseUrl}/api/projects/by-path?path=${encodeURIComponent(startupPath)}`;
|
|
634
|
+
const resByPath = await fetchWithTimeout(byPathUrl, {}, 2500);
|
|
635
|
+
if (resByPath.ok) {
|
|
636
|
+
const project = await resByPath.json();
|
|
637
|
+
urlToOpen = `${baseUrl}/projects?projectId=${encodeURIComponent(project.id)}`;
|
|
638
|
+
if (opts.foreground) {
|
|
639
|
+
log('info', 'Resolved startup path to existing project', { startupPath, projectId: project.id });
|
|
640
|
+
} else if (opts.open) {
|
|
641
|
+
cli.info(`Opening project: ${project.name}`);
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
// 404 or invalid — fall back to newProjectPath to prefill dialog
|
|
645
|
+
urlToOpen = `${baseUrl}/projects?newProjectPath=${encodeURIComponent(startupPath)}`;
|
|
646
|
+
if (opts.foreground) {
|
|
647
|
+
log('info', 'No project at startup path; prefill create dialog', { startupPath });
|
|
648
|
+
} else if (opts.open) {
|
|
649
|
+
cli.info('Opening Projects page (create new project)');
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
} catch (e) {
|
|
653
|
+
// Network/timeouts: still prefer prefilled create dialog
|
|
654
|
+
urlToOpen = `${baseUrl}/projects?newProjectPath=${encodeURIComponent(startupPath)}`;
|
|
655
|
+
if (opts.foreground) {
|
|
656
|
+
log('warn', 'Failed to resolve startup path; opening create dialog', {
|
|
657
|
+
error: e instanceof Error ? e.message : String(e),
|
|
658
|
+
});
|
|
659
|
+
} else if (opts.open) {
|
|
660
|
+
cli.info('Opening Projects page (create new project)');
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Always print the App URL so the user can click/copy it
|
|
665
|
+
if (opts.foreground) {
|
|
666
|
+
console.log(`App: ${urlToOpen}`);
|
|
667
|
+
} else {
|
|
668
|
+
cli.info(`App: ${urlToOpen}`);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Final blank line for clean output
|
|
672
|
+
if (!opts.foreground) {
|
|
673
|
+
cli.blank();
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Write PID file for stop command
|
|
677
|
+
writePidFile(port);
|
|
678
|
+
|
|
679
|
+
// Clean up PID file on exit
|
|
680
|
+
process.on('SIGINT', () => {
|
|
681
|
+
console.log('\nShutting down Devchain...');
|
|
682
|
+
removePidFile();
|
|
683
|
+
process.exit(0);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
process.on('SIGTERM', () => {
|
|
687
|
+
console.log('\nShutting down Devchain...');
|
|
688
|
+
removePidFile();
|
|
689
|
+
process.exit(0);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
if (opts.open) {
|
|
693
|
+
if (!opts.foreground) {
|
|
694
|
+
cli.info(`Opening ${urlToOpen}`);
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const openOpts = resolveOpenOptions();
|
|
698
|
+
await open(urlToOpen, openOpts);
|
|
699
|
+
} catch (e) {
|
|
700
|
+
// Fall back to printing URL without failing the process
|
|
701
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
702
|
+
if (opts.foreground) {
|
|
703
|
+
log('warn', 'Failed to open browser automatically', { error: msg, url: urlToOpen });
|
|
704
|
+
} else {
|
|
705
|
+
cli.warn('Failed to open browser automatically');
|
|
706
|
+
console.log(`Open this URL in your browser: ${urlToOpen}`);
|
|
707
|
+
}
|
|
708
|
+
// Linux fallback to xdg-open when available
|
|
709
|
+
try {
|
|
710
|
+
if (platform() === 'linux') {
|
|
711
|
+
spawn('xdg-open', [urlToOpen], { stdio: 'ignore', detached: true }).unref();
|
|
712
|
+
}
|
|
713
|
+
} catch (_) {
|
|
714
|
+
// ignore
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
program
|
|
721
|
+
.command('stop')
|
|
722
|
+
.description('Stop the running Devchain instance')
|
|
723
|
+
.action(() => {
|
|
724
|
+
const pidData = readPidFile();
|
|
725
|
+
|
|
726
|
+
if (!pidData) {
|
|
727
|
+
console.log('No running Devchain instance found.');
|
|
728
|
+
process.exit(1);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const { pid, port } = pidData;
|
|
732
|
+
|
|
733
|
+
if (!isProcessRunning(pid)) {
|
|
734
|
+
console.log(`Devchain process (PID ${pid}) is not running. Cleaning up stale PID file.`);
|
|
735
|
+
removePidFile();
|
|
736
|
+
process.exit(1);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
console.log(`Stopping Devchain (PID ${pid}, port ${port})...`);
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
process.kill(pid, 'SIGTERM');
|
|
743
|
+
|
|
744
|
+
// Wait a bit for graceful shutdown
|
|
745
|
+
let attempts = 0;
|
|
746
|
+
const checkInterval = setInterval(() => {
|
|
747
|
+
attempts++;
|
|
748
|
+
if (!isProcessRunning(pid)) {
|
|
749
|
+
clearInterval(checkInterval);
|
|
750
|
+
removePidFile();
|
|
751
|
+
console.log('Devchain stopped successfully.');
|
|
752
|
+
process.exit(0);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (attempts > 20) {
|
|
756
|
+
// After 2 seconds, force kill
|
|
757
|
+
clearInterval(checkInterval);
|
|
758
|
+
console.log('Graceful shutdown timed out, forcing...');
|
|
759
|
+
try {
|
|
760
|
+
process.kill(pid, 'SIGKILL');
|
|
761
|
+
removePidFile();
|
|
762
|
+
console.log('Devchain stopped (forced).');
|
|
763
|
+
} catch (err) {
|
|
764
|
+
console.error('Failed to stop Devchain:', err.message);
|
|
765
|
+
process.exit(1);
|
|
766
|
+
}
|
|
767
|
+
process.exit(0);
|
|
768
|
+
}
|
|
769
|
+
}, 100);
|
|
770
|
+
} catch (err) {
|
|
771
|
+
console.error('Failed to stop Devchain:', err.message);
|
|
772
|
+
process.exit(1);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
await program.parseAsync(argv);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
main(process.argv).catch((err) => {
|
|
780
|
+
console.error('Fatal error:', err);
|
|
781
|
+
process.exit(1);
|
|
782
|
+
});
|