@wascartech/devbot 0.0.0-stage → 2.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 +202 -0
- package/README.md +638 -2
- package/dist/access-grant-store.js +99 -0
- package/dist/access-grant-store.js.map +1 -0
- package/dist/access-resolve.js +11 -0
- package/dist/access-resolve.js.map +1 -0
- package/dist/access-rule-store.js +99 -0
- package/dist/access-rule-store.js.map +1 -0
- package/dist/admin/access-routes.js +73 -0
- package/dist/admin/access-routes.js.map +1 -0
- package/dist/admin/access-rule-routes.js +107 -0
- package/dist/admin/access-rule-routes.js.map +1 -0
- package/dist/admin/custom-repos-routes.js +93 -0
- package/dist/admin/custom-repos-routes.js.map +1 -0
- package/dist/admin/env-routes.js +74 -0
- package/dist/admin/env-routes.js.map +1 -0
- package/dist/admin/global-settings-routes.js +254 -0
- package/dist/admin/global-settings-routes.js.map +1 -0
- package/dist/admin/mcp-cleanup.js +12 -0
- package/dist/admin/mcp-cleanup.js.map +1 -0
- package/dist/admin/mcp-routes.js +169 -0
- package/dist/admin/mcp-routes.js.map +1 -0
- package/dist/admin/query-params.js +80 -0
- package/dist/admin/query-params.js.map +1 -0
- package/dist/admin/routes.js +150 -0
- package/dist/admin/routes.js.map +1 -0
- package/dist/admin/run-history-routes.js +44 -0
- package/dist/admin/run-history-routes.js.map +1 -0
- package/dist/admin/run-routes.js +99 -0
- package/dist/admin/run-routes.js.map +1 -0
- package/dist/admin/settings-routes.js +119 -0
- package/dist/admin/settings-routes.js.map +1 -0
- package/dist/admin/stats-routes.js +51 -0
- package/dist/admin/stats-routes.js.map +1 -0
- package/dist/admin/task-routes.js +213 -0
- package/dist/admin/task-routes.js.map +1 -0
- package/dist/admin/test-routes.js +38 -0
- package/dist/admin/test-routes.js.map +1 -0
- package/dist/admin/volume-routes.js +82 -0
- package/dist/admin/volume-routes.js.map +1 -0
- package/dist/admin/webhook-routes.js +174 -0
- package/dist/admin/webhook-routes.js.map +1 -0
- package/dist/agents.js +48 -0
- package/dist/agents.js.map +1 -0
- package/dist/assignee-value.js +6 -0
- package/dist/assignee-value.js.map +1 -0
- package/dist/attachments.js +76 -0
- package/dist/attachments.js.map +1 -0
- package/dist/auto-commands.js +88 -0
- package/dist/auto-commands.js.map +1 -0
- package/dist/auto-features.js +32 -0
- package/dist/auto-features.js.map +1 -0
- package/dist/base-branch-store.js +40 -0
- package/dist/base-branch-store.js.map +1 -0
- package/dist/base-branches.js +9 -0
- package/dist/base-branches.js.map +1 -0
- package/dist/build-workflow.js +116 -0
- package/dist/build-workflow.js.map +1 -0
- package/dist/change-request-lifecycle.js +16 -0
- package/dist/change-request-lifecycle.js.map +1 -0
- package/dist/change-request-store.js +72 -0
- package/dist/change-request-store.js.map +1 -0
- package/dist/ci-failure.js +80 -0
- package/dist/ci-failure.js.map +1 -0
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/conflict-fix.js +178 -0
- package/dist/conflict-fix.js.map +1 -0
- package/dist/cookie.js +70 -0
- package/dist/cookie.js.map +1 -0
- package/dist/crypto-util.js +40 -0
- package/dist/crypto-util.js.map +1 -0
- package/dist/custom-repos-store.js +90 -0
- package/dist/custom-repos-store.js.map +1 -0
- package/dist/custom-repos-sync.js +118 -0
- package/dist/custom-repos-sync.js.map +1 -0
- package/dist/db.js +12 -0
- package/dist/db.js.map +1 -0
- package/dist/devbot-scripts.js +18 -0
- package/dist/devbot-scripts.js.map +1 -0
- package/dist/devcontainer.js +141 -0
- package/dist/devcontainer.js.map +1 -0
- package/dist/env-config-store.js +132 -0
- package/dist/env-config-store.js.map +1 -0
- package/dist/error-log-store.js +193 -0
- package/dist/error-log-store.js.map +1 -0
- package/dist/git.js +605 -0
- package/dist/git.js.map +1 -0
- package/dist/global-queue.js +127 -0
- package/dist/global-queue.js.map +1 -0
- package/dist/global-settings-store.js +307 -0
- package/dist/global-settings-store.js.map +1 -0
- package/dist/help.js +41 -0
- package/dist/help.js.map +1 -0
- package/dist/http-csp.js +15 -0
- package/dist/http-csp.js.map +1 -0
- package/dist/http-util.js +40 -0
- package/dist/http-util.js.map +1 -0
- package/dist/idempotency.js +50 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/lifecycle.js +234 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/logger.js +33 -0
- package/dist/logger.js.map +1 -0
- package/dist/lsp-servers.js +81 -0
- package/dist/lsp-servers.js.map +1 -0
- package/dist/mcp/auth.js +62 -0
- package/dist/mcp/auth.js.map +1 -0
- package/dist/mcp/effective-mcp.js +42 -0
- package/dist/mcp/effective-mcp.js.map +1 -0
- package/dist/mcp/oauth-refresh.js +138 -0
- package/dist/mcp/oauth-refresh.js.map +1 -0
- package/dist/mcp/peer-sync.js +109 -0
- package/dist/mcp/peer-sync.js.map +1 -0
- package/dist/mcp/registry.js +54 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/runner-bridge.js +93 -0
- package/dist/mcp/runner-bridge.js.map +1 -0
- package/dist/mcp/server.js +173 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/_helpers.js +88 -0
- package/dist/mcp/tools/_helpers.js.map +1 -0
- package/dist/mcp/tools/attachments.js +117 -0
- package/dist/mcp/tools/attachments.js.map +1 -0
- package/dist/mcp/tools/cicd.js +105 -0
- package/dist/mcp/tools/cicd.js.map +1 -0
- package/dist/mcp/tools/discovery.js +34 -0
- package/dist/mcp/tools/discovery.js.map +1 -0
- package/dist/mcp/tools/issues.js +56 -0
- package/dist/mcp/tools/issues.js.map +1 -0
- package/dist/mcp/tools/merge_requests.js +67 -0
- package/dist/mcp/tools/merge_requests.js.map +1 -0
- package/dist/mcp/tools/metadata.js +127 -0
- package/dist/mcp/tools/metadata.js.map +1 -0
- package/dist/mcp/tools/peer_sync.js +43 -0
- package/dist/mcp/tools/peer_sync.js.map +1 -0
- package/dist/mcp/tools/repository.js +178 -0
- package/dist/mcp/tools/repository.js.map +1 -0
- package/dist/mcp/tools/work_tracking.js +125 -0
- package/dist/mcp/tools/work_tracking.js.map +1 -0
- package/dist/mcp/tools/workitems.js +30 -0
- package/dist/mcp/tools/workitems.js.map +1 -0
- package/dist/mcp/volume-files.js +187 -0
- package/dist/mcp/volume-files.js.map +1 -0
- package/dist/mcp-auth-store.js +134 -0
- package/dist/mcp-auth-store.js.map +1 -0
- package/dist/mcp-config-store.js +83 -0
- package/dist/mcp-config-store.js.map +1 -0
- package/dist/mention.js +512 -0
- package/dist/mention.js.map +1 -0
- package/dist/migrate.js +73 -0
- package/dist/migrate.js.map +1 -0
- package/dist/mr-assignee.js +71 -0
- package/dist/mr-assignee.js.map +1 -0
- package/dist/mr-content.js +90 -0
- package/dist/mr-content.js.map +1 -0
- package/dist/oauth-routes.js +54 -0
- package/dist/oauth-routes.js.map +1 -0
- package/dist/onboarding-allowlist.js +32 -0
- package/dist/onboarding-allowlist.js.map +1 -0
- package/dist/opencode.js +291 -0
- package/dist/opencode.js.map +1 -0
- package/dist/outbound-webhook-store.js +229 -0
- package/dist/outbound-webhook-store.js.map +1 -0
- package/dist/outbound-webhook.js +314 -0
- package/dist/outbound-webhook.js.map +1 -0
- package/dist/path-sandbox.js +200 -0
- package/dist/path-sandbox.js.map +1 -0
- package/dist/platform/git-auth.js +34 -0
- package/dist/platform/git-auth.js.map +1 -0
- package/dist/platform/gitealike/attachments.js +62 -0
- package/dist/platform/gitealike/attachments.js.map +1 -0
- package/dist/platform/gitealike/base.js +935 -0
- package/dist/platform/gitealike/base.js.map +1 -0
- package/dist/platform/gitealike/forgejo.js +16 -0
- package/dist/platform/gitealike/forgejo.js.map +1 -0
- package/dist/platform/gitealike/gitea.js +16 -0
- package/dist/platform/gitealike/gitea.js.map +1 -0
- package/dist/platform/gitealike/http.js +152 -0
- package/dist/platform/gitealike/http.js.map +1 -0
- package/dist/platform/gitealike/schemas.js +284 -0
- package/dist/platform/gitealike/schemas.js.map +1 -0
- package/dist/platform/github/attachments.js +52 -0
- package/dist/platform/github/attachments.js.map +1 -0
- package/dist/platform/github/http.js +153 -0
- package/dist/platform/github/http.js.map +1 -0
- package/dist/platform/github/index.js +936 -0
- package/dist/platform/github/index.js.map +1 -0
- package/dist/platform/github/schemas.js +308 -0
- package/dist/platform/github/schemas.js.map +1 -0
- package/dist/platform/github/zip.js +85 -0
- package/dist/platform/github/zip.js.map +1 -0
- package/dist/platform/gitlab/admin.js +105 -0
- package/dist/platform/gitlab/admin.js.map +1 -0
- package/dist/platform/gitlab/api.js +229 -0
- package/dist/platform/gitlab/api.js.map +1 -0
- package/dist/platform/gitlab/attachments.js +79 -0
- package/dist/platform/gitlab/attachments.js.map +1 -0
- package/dist/platform/gitlab/http.js +165 -0
- package/dist/platform/gitlab/http.js.map +1 -0
- package/dist/platform/gitlab/index.js +630 -0
- package/dist/platform/gitlab/index.js.map +1 -0
- package/dist/platform/gitlab/oauth.js +58 -0
- package/dist/platform/gitlab/oauth.js.map +1 -0
- package/dist/platform/gitlab/read.js +182 -0
- package/dist/platform/gitlab/read.js.map +1 -0
- package/dist/platform/gitlab/schemas-read.js +274 -0
- package/dist/platform/gitlab/schemas-read.js.map +1 -0
- package/dist/platform/gitlab/schemas.js +251 -0
- package/dist/platform/gitlab/schemas.js.map +1 -0
- package/dist/platform/gitlab/signature.js +52 -0
- package/dist/platform/gitlab/signature.js.map +1 -0
- package/dist/platform/index.js +38 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/port.js +26 -0
- package/dist/platform/port.js.map +1 -0
- package/dist/platform/types.js +80 -0
- package/dist/platform/types.js.map +1 -0
- package/dist/podman.js +119 -0
- package/dist/podman.js.map +1 -0
- package/dist/project-sync.js +69 -0
- package/dist/project-sync.js.map +1 -0
- package/dist/prompt.js +47 -0
- package/dist/prompt.js.map +1 -0
- package/dist/queue-store.js +236 -0
- package/dist/queue-store.js.map +1 -0
- package/dist/queue.js +129 -0
- package/dist/queue.js.map +1 -0
- package/dist/redact.js +23 -0
- package/dist/redact.js.map +1 -0
- package/dist/run-files.js +297 -0
- package/dist/run-files.js.map +1 -0
- package/dist/run-filters.js +54 -0
- package/dist/run-filters.js.map +1 -0
- package/dist/run-history-store.js +229 -0
- package/dist/run-history-store.js.map +1 -0
- package/dist/run-log-sink.js +270 -0
- package/dist/run-log-sink.js.map +1 -0
- package/dist/run-log.js +97 -0
- package/dist/run-log.js.map +1 -0
- package/dist/run-usage.js +29 -0
- package/dist/run-usage.js.map +1 -0
- package/dist/run-view.js +112 -0
- package/dist/run-view.js.map +1 -0
- package/dist/runner.js +1094 -0
- package/dist/runner.js.map +1 -0
- package/dist/runtime-apply.js +26 -0
- package/dist/runtime-apply.js.map +1 -0
- package/dist/runtime-settings.js +71 -0
- package/dist/runtime-settings.js.map +1 -0
- package/dist/runtime-state.js +19 -0
- package/dist/runtime-state.js.map +1 -0
- package/dist/server.js +119 -0
- package/dist/server.js.map +1 -0
- package/dist/settings-store.js +320 -0
- package/dist/settings-store.js.map +1 -0
- package/dist/status.js +121 -0
- package/dist/status.js.map +1 -0
- package/dist/stop.js +56 -0
- package/dist/stop.js.map +1 -0
- package/dist/sweeps.js +87 -0
- package/dist/sweeps.js.map +1 -0
- package/dist/task-log.js +279 -0
- package/dist/task-log.js.map +1 -0
- package/dist/task-view.js +62 -0
- package/dist/task-view.js.map +1 -0
- package/dist/token-store.js +68 -0
- package/dist/token-store.js.map +1 -0
- package/dist/types/access.js +74 -0
- package/dist/types/access.js.map +1 -0
- package/dist/types/custom-repos.js +28 -0
- package/dist/types/custom-repos.js.map +1 -0
- package/dist/types/env-config.js +25 -0
- package/dist/types/env-config.js.map +1 -0
- package/dist/types/env.js +190 -0
- package/dist/types/env.js.map +1 -0
- package/dist/types/global-settings.js +178 -0
- package/dist/types/global-settings.js.map +1 -0
- package/dist/types/lsp-servers.js +145 -0
- package/dist/types/lsp-servers.js.map +1 -0
- package/dist/types/mcp-config.js +77 -0
- package/dist/types/mcp-config.js.map +1 -0
- package/dist/types/opencode.js +59 -0
- package/dist/types/opencode.js.map +1 -0
- package/dist/types/outbound-webhook.js +56 -0
- package/dist/types/outbound-webhook.js.map +1 -0
- package/dist/types/project-settings.js +114 -0
- package/dist/types/project-settings.js.map +1 -0
- package/dist/types/provider-registry.js +37 -0
- package/dist/types/provider-registry.js.map +1 -0
- package/dist/types/runtime-settings.js +78 -0
- package/dist/types/runtime-settings.js.map +1 -0
- package/dist/usage-stats.js +159 -0
- package/dist/usage-stats.js.map +1 -0
- package/dist/version.js +19 -0
- package/dist/version.js.map +1 -0
- package/dist/volume-gc.js +193 -0
- package/dist/volume-gc.js.map +1 -0
- package/dist/volume-manage.js +191 -0
- package/dist/volume-manage.js.map +1 -0
- package/dist/volume-store.js +122 -0
- package/dist/volume-store.js.map +1 -0
- package/dist/webhook.js +1106 -0
- package/dist/webhook.js.map +1 -0
- package/dist/work-item-auto-store.js +75 -0
- package/dist/work-item-auto-store.js.map +1 -0
- package/dist/work-item-branch-store.js +37 -0
- package/dist/work-item-branch-store.js.map +1 -0
- package/migrations/0001_initial.sql +59 -0
- package/migrations/0002_queue_status.sql +15 -0
- package/migrations/0003_work_item_base_branch.sql +14 -0
- package/migrations/0004_project_settings.sql +15 -0
- package/migrations/0005_global_settings.sql +34 -0
- package/migrations/0006_model_variants.sql +8 -0
- package/migrations/0007_container_error_logs.sql +21 -0
- package/migrations/0008_project_access_grants.sql +16 -0
- package/migrations/0009_drop_imported_legacy.sql +4 -0
- package/migrations/0010_container_error_log_model.sql +6 -0
- package/migrations/0011_container_error_log_title.sql +5 -0
- package/migrations/0012_pending_task_cancel.sql +6 -0
- package/migrations/0013_builder_image.sql +6 -0
- package/migrations/0014_build_workflow.sql +7 -0
- package/migrations/0015_access_rules.sql +24 -0
- package/migrations/0016_trigger_policy.sql +17 -0
- package/migrations/0017_attachment_upload.sql +8 -0
- package/migrations/0018_work_item_branch.sql +11 -0
- package/migrations/0019_auto_push_mr.sql +18 -0
- package/migrations/0020_outbound_webhooks.sql +47 -0
- package/migrations/0021_runtime_settings.sql +12 -0
- package/migrations/0022_auto_resolve_conflicts.sql +17 -0
- package/migrations/0023_ci_fail_auto_report.sql +27 -0
- package/migrations/0024_work_item_auto_override.sql +22 -0
- package/migrations/0025_run_history.sql +37 -0
- package/migrations/0026_run_history_task_seq.sql +6 -0
- package/migrations/0027_custom_repos.sql +11 -0
- package/migrations/0028_run_usage.sql +14 -0
- package/migrations/0029_drop_ocr_langs.sql +6 -0
- package/migrations/0030_mr_assignee.sql +9 -0
- package/migrations/0031_lsp_servers.sql +14 -0
- package/opencode-config/managed/devbot-agent-policy.md +61 -0
- package/opencode-config/managed/opencode.json +77 -0
- package/opencode-config/opencode.json +211 -0
- package/opencode-config/plugins/devbot-path-guard.js +224 -0
- package/package.json +157 -4
- package/public/assets/DashboardSidebarCollapse-rIuzCmVm.js +1 -0
- package/public/assets/DashboardToolbar-DFCUqZ4Z.js +1 -0
- package/public/assets/ErrorLogModal.vue_vue_type_script_setup_true_lang-BCyNIf1E.js +1 -0
- package/public/assets/ErrorLogsPage-DyYK2Rop.js +1 -0
- package/public/assets/LogSlideover.vue_vue_type_script_setup_true_lang-33xie7TE.js +1 -0
- package/public/assets/OverviewPage-CFfa0uwg.js +1 -0
- package/public/assets/PageGrid-CBROIU87.js +1 -0
- package/public/assets/PeriodRangePicker.vue_vue_type_script_setup_true_lang-DiFUNem7.js +1 -0
- package/public/assets/ProjectDetailPage-CreyvUit.js +1 -0
- package/public/assets/ProjectsPage-CFx8-elJ.js +1 -0
- package/public/assets/RunDetailSlideover.vue_vue_type_script_setup_true_lang-DopYYmCT.js +1 -0
- package/public/assets/RunsPage-CRnnXU4P.js +1 -0
- package/public/assets/Select-Bvx32Diz.js +1 -0
- package/public/assets/SelectMenu-BjMxc4VB.js +1 -0
- package/public/assets/SettingsPage-Bb6LM3wO.js +60 -0
- package/public/assets/TimeCell.vue_vue_type_script_setup_true_lang-DW_UB0Fu.js +1 -0
- package/public/assets/UsagePage-BLMDEsv-.js +332 -0
- package/public/assets/UsagePage-DodEeAjB.css +1 -0
- package/public/assets/VolumesPage-CxcLc3L2.js +1 -0
- package/public/assets/WebhookConfigPanel.vue_vue_type_script_setup_true_lang-B2V2GF6b.js +1 -0
- package/public/assets/WorkItemCell.vue_vue_type_script_setup_true_lang-BQn6vTjd.js +1 -0
- package/public/assets/index-BMbMibyb.js +4 -0
- package/public/assets/index-C5RBBrPj.js +30 -0
- package/public/assets/index-CgX29UgC.css +1 -0
- package/public/assets/usage-format-jsb31_O8.js +1 -0
- package/public/assets/useManagedProjects-8rtjRqVJ.js +1 -0
- package/public/assets/useRouteTab-DfwrcMic.js +1 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/index.html +15 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Webhook idempotency — dedupes webhook-id values within the same TTL window
|
|
2
|
+
// as the signature replay guard (300s). Closes the in-process idempotency gap
|
|
3
|
+
// where a restart mid-run + a forge webhook retry double-runs the same work.
|
|
4
|
+
// ponytail: in-process table, not a distributed store — fine for single-host
|
|
5
|
+
// deploy. Upgrade path: a shared Redis SETNX if HA lands.
|
|
6
|
+
import { db } from "./db.js";
|
|
7
|
+
const TTL_SEC = 300; // match the replay guard in webhook-signature.ts
|
|
8
|
+
export function initIdempotencyTable() {
|
|
9
|
+
db.exec(`
|
|
10
|
+
CREATE TABLE IF NOT EXISTS seen_webhook_ids (
|
|
11
|
+
webhook_id TEXT PRIMARY KEY,
|
|
12
|
+
seen_at INTEGER NOT NULL
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
// Returns true if this webhook-id was already processed within the TTL window.
|
|
17
|
+
// Otherwise records the id and returns false.
|
|
18
|
+
// Atomic: uses INSERT OR IGNORE then checks changes to avoid the read-then-write
|
|
19
|
+
// race where two concurrent webhooks both pass the SELECT before either writes.
|
|
20
|
+
export function isDuplicateWebhook(webhookId) {
|
|
21
|
+
const now = Math.floor(Date.now() / 1000);
|
|
22
|
+
// First, check if a fresh row exists (within TTL).
|
|
23
|
+
const row = db
|
|
24
|
+
.prepare("SELECT seen_at FROM seen_webhook_ids WHERE webhook_id = ?")
|
|
25
|
+
.get(webhookId);
|
|
26
|
+
if (row && now - row.seen_at < TTL_SEC)
|
|
27
|
+
return true;
|
|
28
|
+
// No fresh row — try to insert. INSERT OR IGNORE avoids the race: if another
|
|
29
|
+
// concurrent call inserted the same id between our SELECT and INSERT, this
|
|
30
|
+
// returns changes=0 and we treat it as a duplicate.
|
|
31
|
+
const r = db.prepare("INSERT OR IGNORE INTO seen_webhook_ids (webhook_id, seen_at) VALUES (?, ?)").run(webhookId, now);
|
|
32
|
+
if (r.changes === 0) {
|
|
33
|
+
// Row already exists (another concurrent call won the race). Check if it's
|
|
34
|
+
// fresh — if so, duplicate. If expired, refresh it.
|
|
35
|
+
const existing = db.prepare("SELECT seen_at FROM seen_webhook_ids WHERE webhook_id = ?").get(webhookId);
|
|
36
|
+
if (existing && now - existing.seen_at < TTL_SEC)
|
|
37
|
+
return true;
|
|
38
|
+
// Expired — refresh.
|
|
39
|
+
db.prepare("UPDATE seen_webhook_ids SET seen_at = ? WHERE webhook_id = ?").run(now, webhookId);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Best-effort purge of expired rows. Called on startup so the table doesn't
|
|
45
|
+
// grow unbounded across long uptimes.
|
|
46
|
+
export function purgeExpiredWebhookIds() {
|
|
47
|
+
const cutoff = Math.floor(Date.now() / 1000) - TTL_SEC;
|
|
48
|
+
db.prepare("DELETE FROM seen_webhook_ids WHERE seen_at < ?").run(cutoff);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=idempotency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,4DAA4D;AAC5D,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,iDAAiD;AAEtE,MAAM,UAAU,oBAAoB;IAClC,EAAE,CAAC,IAAI,CAAC;;;;;GAKP,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,8CAA8C;AAC9C,iFAAiF;AACjF,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,mDAAmD;IACnD,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,2DAA2D,CAAC;SACpE,GAAG,CAAC,SAAS,CAAoC,CAAC;IACrD,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,OAAO;QAAE,OAAO,IAAI,CAAC;IACpD,6EAA6E;IAC7E,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,4EAA4E,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACvH,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACpB,2EAA2E;QAC3E,oDAAoD;QACpD,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAC,GAAG,CAAC,SAAS,CAAoC,CAAC;QAC3I,IAAI,QAAQ,IAAI,GAAG,GAAG,QAAQ,CAAC,OAAO,GAAG,OAAO;YAAE,OAAO,IAAI,CAAC;QAC9D,qBAAqB;QACrB,EAAE,CAAC,OAAO,CAAC,8DAA8D,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,sCAAsC;AACtC,MAAM,UAAU,sBAAsB;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC;IACvD,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { chmodSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { config } from "./config.js";
|
|
4
|
+
import { log } from "./logger.js";
|
|
5
|
+
import { closeDb } from "./db.js";
|
|
6
|
+
import { migrate } from "./migrate.js";
|
|
7
|
+
import { initDb } from "./token-store.js";
|
|
8
|
+
import { initMcpTables, purgeExpiredMcpTokens } from "./mcp/auth.js";
|
|
9
|
+
import { initMcpConfigTables } from "./mcp-config-store.js";
|
|
10
|
+
import { initMcpAuthTables } from "./mcp-auth-store.js";
|
|
11
|
+
import { initEnvVarsTables } from "./env-config-store.js";
|
|
12
|
+
import { initProjectSettingsTables } from "./settings-store.js";
|
|
13
|
+
import { initGlobalSettingsTables, hasUsableConfig } from "./global-settings-store.js";
|
|
14
|
+
import { initCustomReposTables } from "./custom-repos-store.js";
|
|
15
|
+
import { initQueueTables, resetOrphanedRunning, purgeStale, workItemsWithQueued, listPending, purgeAllPending, purgeCancelled } from "./queue-store.js";
|
|
16
|
+
import { initRunHistoryTables } from "./run-history-store.js";
|
|
17
|
+
import { purgeRunLogs } from "./run-log.js";
|
|
18
|
+
import { initErrorLogTables, purgeErrorLogsOlderThan } from "./error-log-store.js";
|
|
19
|
+
import { initIdempotencyTable, purgeExpiredWebhookIds } from "./idempotency.js";
|
|
20
|
+
import { initVolumeTables } from "./volume-store.js";
|
|
21
|
+
import { purgeInactiveVolumes, reconcileVolumes } from "./volume-gc.js";
|
|
22
|
+
import { refreshManagedProjectPaths } from "./project-sync.js";
|
|
23
|
+
import { initBaseBranchTable } from "./base-branch-store.js";
|
|
24
|
+
import { initWorkItemBranchTable } from "./work-item-branch-store.js";
|
|
25
|
+
import { initWorkItemAutoTable } from "./work-item-auto-store.js";
|
|
26
|
+
import { initChangeRequestTable } from "./change-request-store.js";
|
|
27
|
+
import { initAccessGrantTables } from "./access-grant-store.js";
|
|
28
|
+
import { initAccessRuleTables } from "./access-rule-store.js";
|
|
29
|
+
import { initOutboundWebhookTables, purgeDeliveriesOlderThan } from "./outbound-webhook-store.js";
|
|
30
|
+
import { startWebhookWorker } from "./outbound-webhook.js";
|
|
31
|
+
import { dispatch } from "./webhook.js";
|
|
32
|
+
import { syncActiveFromDb } from "./global-queue.js";
|
|
33
|
+
import { applyRuntimeSettings } from "./runtime-apply.js";
|
|
34
|
+
import { runtime } from "./runtime-state.js";
|
|
35
|
+
import { MCP_REFRESH_HORIZON_MS } from "./sweeps.js";
|
|
36
|
+
import { refreshDueMcpAuth } from "./mcp/oauth-refresh.js";
|
|
37
|
+
// ponytail: kill leaked devbot-wi-* containers on process exit. Containers
|
|
38
|
+
// are started with --rm, but if devbot dies mid-run the podman client dies
|
|
39
|
+
// while conmon keeps the container alive → orphans. Filter by name prefix,
|
|
40
|
+
// podman rm -f stops+removes in one call. Volumes preserved (WIP). Sync
|
|
41
|
+
// spawnSync because signal handlers can't await.
|
|
42
|
+
function killDevbotContainers() {
|
|
43
|
+
try {
|
|
44
|
+
const prefix = config.INSTANCE_ID ? `${config.INSTANCE_ID}-devbot-wi-` : "devbot-wi-";
|
|
45
|
+
const list = spawnSync("podman", ["ps", "-a", "--filter", `name=${prefix}`, "--format", "{{.Names}}"], { encoding: "utf8" });
|
|
46
|
+
if (list.status !== 0)
|
|
47
|
+
return;
|
|
48
|
+
const names = list.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
49
|
+
if (names.length === 0)
|
|
50
|
+
return;
|
|
51
|
+
spawnSync("podman", ["rm", "-f", ...names], { encoding: "utf8" });
|
|
52
|
+
log.info("cleanup killed devbot containers", { count: names.length, names });
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// best-effort; never throw from a shutdown handler
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let cleaningUp = false;
|
|
59
|
+
function shutdown(sig) {
|
|
60
|
+
if (cleaningUp)
|
|
61
|
+
return;
|
|
62
|
+
cleaningUp = true;
|
|
63
|
+
log.info("shutting down", { sig });
|
|
64
|
+
killDevbotContainers();
|
|
65
|
+
closeDb();
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
// Called from server.ts after all routes are mounted. Handles DB init, startup
|
|
69
|
+
// replay, chmod, signal handlers, and app.listen.
|
|
70
|
+
export function bootstrap(app, mcpApp) {
|
|
71
|
+
// Migrations first — authoritative schema authority. Creates all tables via
|
|
72
|
+
// 0001 (baseline) and applies 0002 (queue status columns) if pending.
|
|
73
|
+
migrate();
|
|
74
|
+
// Belt-and-braces idempotent inits (harmless if migrate already created the
|
|
75
|
+
// tables; covers test DBs that skip migrate).
|
|
76
|
+
initDb();
|
|
77
|
+
initMcpTables();
|
|
78
|
+
initMcpConfigTables();
|
|
79
|
+
initMcpAuthTables();
|
|
80
|
+
initEnvVarsTables();
|
|
81
|
+
initProjectSettingsTables();
|
|
82
|
+
initGlobalSettingsTables();
|
|
83
|
+
initCustomReposTables();
|
|
84
|
+
initQueueTables();
|
|
85
|
+
initErrorLogTables();
|
|
86
|
+
initRunHistoryTables();
|
|
87
|
+
initIdempotencyTable();
|
|
88
|
+
initVolumeTables();
|
|
89
|
+
initBaseBranchTable();
|
|
90
|
+
initWorkItemBranchTable();
|
|
91
|
+
initWorkItemAutoTable();
|
|
92
|
+
initAccessGrantTables();
|
|
93
|
+
initAccessRuleTables();
|
|
94
|
+
initOutboundWebhookTables();
|
|
95
|
+
initChangeRequestTable();
|
|
96
|
+
purgeExpiredMcpTokens();
|
|
97
|
+
purgeExpiredWebhookIds();
|
|
98
|
+
// Publish the effective runtime settings (WebUI override -> env -> default)
|
|
99
|
+
// before anything reads them, and arm the periodic sweeps. The settings PUT
|
|
100
|
+
// re-runs both live.
|
|
101
|
+
applyRuntimeSettings();
|
|
102
|
+
if (!hasUsableConfig()) {
|
|
103
|
+
log.warn("bot not configured; mention runs will be blocked until global settings are set");
|
|
104
|
+
}
|
|
105
|
+
// MCP token refresh: when enabled, run one sweep at startup so a token that
|
|
106
|
+
// expired (or is about to) while the bot was down is renewed without waiting
|
|
107
|
+
// for the first interval. Gated on the runtime toggle — no sweep when off.
|
|
108
|
+
// Best-effort and fire-and-forget.
|
|
109
|
+
if (runtime().mcpRefreshEnabled) {
|
|
110
|
+
void refreshDueMcpAuth(MCP_REFRESH_HORIZON_MS).catch((e) => log.warn("startup mcp token refresh failed", { err: e }));
|
|
111
|
+
}
|
|
112
|
+
// Volume GC: reconcile pre-existing podman volumes into the ledger (orphan
|
|
113
|
+
// placeholders, silent purge), then run an immediate sweep. Both are
|
|
114
|
+
// best-effort and fire-and-forget so a slow podman doesn't block listen.
|
|
115
|
+
// The periodic sweep is armed by applyRuntimeSettings above.
|
|
116
|
+
void reconcileVolumes().catch((e) => log.warn("volume reconcile failed", { err: e }));
|
|
117
|
+
void purgeInactiveVolumes().catch((e) => log.warn("startup volume purge failed", { err: e }));
|
|
118
|
+
// Project-path reconciliation: heal stored project paths after a forge-side
|
|
119
|
+
// rename/transfer. Immediate fire-and-forget sweep, then a periodic one (the
|
|
120
|
+
// timer is armed by applyRuntimeSettings above). Best-effort — never blocks
|
|
121
|
+
// listen.
|
|
122
|
+
if (runtime().projectSyncEnabled) {
|
|
123
|
+
void refreshManagedProjectPaths().catch((e) => log.warn("project path sync failed", { err: e }));
|
|
124
|
+
}
|
|
125
|
+
// Startup replay: the table IS the queue now. Reset orphaned running rows
|
|
126
|
+
// (crash mid-run left them running) back to queued so they re-enter the FIFO.
|
|
127
|
+
// Re-derive the in-memory `active` counter from the DB. Then re-dispatch each
|
|
128
|
+
// orphaned row by calling dispatch() (which appends a fresh row + enqueues a
|
|
129
|
+
// worker). This preserves the old replay semantics: orphans get re-processed.
|
|
130
|
+
const orphaned = resetOrphanedRunning();
|
|
131
|
+
if (orphaned > 0) {
|
|
132
|
+
log.info("replaying orphaned running tasks from prior run", { count: orphaned });
|
|
133
|
+
}
|
|
134
|
+
// Drop tasks that were stopped but never replayed cleanly (crash between the
|
|
135
|
+
// stop and the runner unwinding). purgeCancelled runs after the orphan reset
|
|
136
|
+
// so a stopped running row (now 'queued' with the flag set) is deleted here,
|
|
137
|
+
// never replayed below.
|
|
138
|
+
const purgedCancelled = purgeCancelled();
|
|
139
|
+
if (purgedCancelled > 0) {
|
|
140
|
+
log.info("purged cancelled tasks not replayed", { count: purgedCancelled });
|
|
141
|
+
}
|
|
142
|
+
syncActiveFromDb();
|
|
143
|
+
// Re-dispatch: for each work item that has queued rows, call dispatch() with
|
|
144
|
+
// the row's payload. dispatch appends a fresh row + enqueues a worker. But
|
|
145
|
+
// the rows are already in the DB (they survived the crash). We don't need to
|
|
146
|
+
// re-append — we just need to spawn workers for them. The simplest way: list
|
|
147
|
+
// work items with queued rows and spawn workers by calling enqueue with a
|
|
148
|
+
// no-op handler... but that would double-enqueue. Instead, we use the
|
|
149
|
+
// listPending + dispatch path: read each queued row, delete it, and re-dispatch
|
|
150
|
+
// (which re-appends + enqueues). This matches the old purgeAllPending + replay.
|
|
151
|
+
// Actually, simpler: the rows are already queued. We just need to spawn
|
|
152
|
+
// workers. queue.ts doesn't export a "spawn worker" function, but dispatch()
|
|
153
|
+
// does appendPending + enqueue. So: read each queued row, delete it, call
|
|
154
|
+
// dispatch with its payload. This re-appends (new seq) + enqueues. Same as old.
|
|
155
|
+
if (orphaned > 0 || workItemsWithQueued().length > 0) {
|
|
156
|
+
const pending = listPending();
|
|
157
|
+
if (pending.length > 0) {
|
|
158
|
+
log.info("replaying queued tasks from prior run", { count: pending.length });
|
|
159
|
+
purgeAllPending();
|
|
160
|
+
for (const p of pending) {
|
|
161
|
+
try {
|
|
162
|
+
const input = JSON.parse(p.payload);
|
|
163
|
+
dispatch(input);
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
log.warn("pending task replay failed, dropping", { seq: p.seq, err: e instanceof Error ? e.message : String(e) });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Housekeeping: purge stale rows older than 7 days so the table doesn't grow
|
|
172
|
+
// forever. Done rows are deleted on completion; this catches crash leftovers.
|
|
173
|
+
const purged = purgeStale(7 * 24 * 3600);
|
|
174
|
+
if (purged > 0)
|
|
175
|
+
log.debug("purged stale queue rows", { count: purged });
|
|
176
|
+
// Error-log retention: purge rows older than ERROR_LOG_RETENTION_DAYS at
|
|
177
|
+
// startup (0 disables purging). The daily sweep is armed by applyRuntimeSettings.
|
|
178
|
+
if (runtime().errorLogRetentionDays > 0) {
|
|
179
|
+
const purgedLogs = purgeErrorLogsOlderThan(runtime().errorLogRetentionDays);
|
|
180
|
+
if (purgedLogs > 0)
|
|
181
|
+
log.info("purged expired error logs", { count: purgedLogs, days: runtime().errorLogRetentionDays });
|
|
182
|
+
}
|
|
183
|
+
// Run-log file retention (work item #61): unlink files older than
|
|
184
|
+
// RUN_LOG_TTL_HOURS at startup; the history rows survive with log_file
|
|
185
|
+
// cleared. 0 = keep forever. The hourly sweep is armed by applyRuntimeSettings.
|
|
186
|
+
if (runtime().runLogTtlHours > 0) {
|
|
187
|
+
const purgedRunLogs = purgeRunLogs(runtime().runLogTtlHours);
|
|
188
|
+
if (purgedRunLogs > 0)
|
|
189
|
+
log.info("purged expired run logs", { count: purgedRunLogs, hours: runtime().runLogTtlHours });
|
|
190
|
+
}
|
|
191
|
+
// Outbound-webhook delivery retention: purge old delivery rows at startup
|
|
192
|
+
// (0 disables purging). The delivery worker is started below.
|
|
193
|
+
if (config.WEBHOOK_RETENTION_DAYS > 0) {
|
|
194
|
+
const purgedDeliveries = purgeDeliveriesOlderThan(config.WEBHOOK_RETENTION_DAYS);
|
|
195
|
+
if (purgedDeliveries > 0) {
|
|
196
|
+
log.info("purged expired outbound deliveries", { count: purgedDeliveries, days: config.WEBHOOK_RETENTION_DAYS });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// ponytail: tighten DB file perms to 0600 so signing tokens aren't
|
|
200
|
+
// world-readable on shared hosts. Container single-user model already
|
|
201
|
+
// covers this; belt-and-braces for bare metal.
|
|
202
|
+
try {
|
|
203
|
+
chmodSync("devbot.db", 0o600);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// file may not exist on first run before initDb creates it; ignore
|
|
207
|
+
}
|
|
208
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
209
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
210
|
+
app.listen(config.PORT, () => {
|
|
211
|
+
log.info("devbot listening", { port: config.PORT, dryRun: config.DRY_RUN });
|
|
212
|
+
});
|
|
213
|
+
mcpApp.listen(config.MCP_PORT, config.MCP_HOST, () => {
|
|
214
|
+
log.info("mcp listening", { port: config.MCP_PORT, host: config.MCP_HOST });
|
|
215
|
+
});
|
|
216
|
+
// Outbound-webhook delivery worker (+ crash recovery for rows left
|
|
217
|
+
// 'delivering'). Timer is .unref()'d like the sweeps above.
|
|
218
|
+
startWebhookWorker();
|
|
219
|
+
// Periodic outbound-delivery retention sweep (daily).
|
|
220
|
+
if (config.WEBHOOK_RETENTION_DAYS > 0) {
|
|
221
|
+
setInterval(() => {
|
|
222
|
+
try {
|
|
223
|
+
const n = purgeDeliveriesOlderThan(config.WEBHOOK_RETENTION_DAYS);
|
|
224
|
+
if (n > 0)
|
|
225
|
+
log.info("outbound delivery sweep purged", { count: n, days: config.WEBHOOK_RETENTION_DAYS });
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
log.warn("outbound delivery sweep failed", { err: e });
|
|
229
|
+
}
|
|
230
|
+
}, 24 * 60 * 60_000).unref();
|
|
231
|
+
log.info("outbound delivery retention scheduled", { days: config.WEBHOOK_RETENTION_DAYS });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const RANK = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
2
|
+
const MIN = RANK[process.env.LOG_LEVEL ?? "info"] ?? RANK.info;
|
|
3
|
+
function ts() {
|
|
4
|
+
return new Date().toISOString();
|
|
5
|
+
}
|
|
6
|
+
// ponytail: JSON.stringify(new Error(...)) yields "{}" (no own enumerable
|
|
7
|
+
// props), which silently eats API failures logged via log.error("x", e).
|
|
8
|
+
// Surface message + stack so postNote/etc. failures are debuggable.
|
|
9
|
+
function serialize(extra) {
|
|
10
|
+
if (typeof extra === "string")
|
|
11
|
+
return extra;
|
|
12
|
+
if (extra instanceof Error)
|
|
13
|
+
return JSON.stringify({ message: extra.message, stack: extra.stack });
|
|
14
|
+
return JSON.stringify(extra);
|
|
15
|
+
}
|
|
16
|
+
function emit(level, msg, extra) {
|
|
17
|
+
if (RANK[level] < MIN)
|
|
18
|
+
return;
|
|
19
|
+
const line = `[${ts()}] ${level.toUpperCase()} ${msg}`;
|
|
20
|
+
if (extra !== undefined) {
|
|
21
|
+
console[level === "debug" ? "log" : level](line, serialize(extra));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console[level === "debug" ? "log" : level](line);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const log = {
|
|
28
|
+
debug: (msg, extra) => emit("debug", msg, extra),
|
|
29
|
+
info: (msg, extra) => emit("info", msg, extra),
|
|
30
|
+
warn: (msg, extra) => emit("warn", msg, extra),
|
|
31
|
+
error: (msg, extra) => emit("error", msg, extra),
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAA0B,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACjF,MAAM,GAAG,GAAG,IAAI,CAAE,OAAO,CAAC,GAAG,CAAC,SAAmB,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC;AAE1E,SAAS,EAAE;IACT,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,sEAAsE;AACtE,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IAClG,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,IAAI,CAAC,KAAY,EAAE,GAAW,EAAE,KAAe;IACtD,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG;QAAE,OAAO;IAC9B,MAAM,IAAI,GAAG,IAAI,EAAE,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,IAAI,GAAG,EAAE,CAAC;IACvD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,KAAK,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC;IAClE,IAAI,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IAChE,IAAI,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IAChE,KAAK,EAAE,CAAC,GAAW,EAAE,KAAe,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC;CACnE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { LSP_SERVER_META } from "./types/lsp-servers.js";
|
|
2
|
+
// Pure id list shared by the settings schema, the browser UI, and the runner.
|
|
3
|
+
// Keep this module free of node:fs/node:logger imports: the WebUI bundle pulls
|
|
4
|
+
// it in through the provider-registry types. scripts/check-opencode-lsp.ts
|
|
5
|
+
// proves this table matches the enabled ids declared in the shipped
|
|
6
|
+
// opencode-config/opencode.json.
|
|
7
|
+
export const LSP_SERVER_IDS = LSP_SERVER_META.map((server) => server.id);
|
|
8
|
+
export const DEFAULT_LSP_SERVERS = [...LSP_SERVER_IDS];
|
|
9
|
+
export function isLspServerId(id) {
|
|
10
|
+
return LSP_SERVER_IDS.includes(id);
|
|
11
|
+
}
|
|
12
|
+
export function splitLspServerList(raw) {
|
|
13
|
+
return raw
|
|
14
|
+
.split(",")
|
|
15
|
+
.map((id) => id.trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
// The subtractive project list accepts commas and/or whitespace. Match known
|
|
19
|
+
// ids greedily first so the space in "php intelephense" does not split it;
|
|
20
|
+
// unknown tokens are still returned so validation can reject them.
|
|
21
|
+
export function splitLspDisabledList(raw) {
|
|
22
|
+
const known = [...LSP_SERVER_IDS].sort((a, b) => b.length - a.length);
|
|
23
|
+
const out = [];
|
|
24
|
+
let rest = raw.trim();
|
|
25
|
+
while (rest) {
|
|
26
|
+
rest = rest.replace(/^[\s,]+/, "");
|
|
27
|
+
if (!rest)
|
|
28
|
+
break;
|
|
29
|
+
const match = known.find((id) => {
|
|
30
|
+
if (!rest.startsWith(id))
|
|
31
|
+
return false;
|
|
32
|
+
const next = rest[id.length];
|
|
33
|
+
return next === undefined || /[\s,]/.test(next);
|
|
34
|
+
});
|
|
35
|
+
if (match) {
|
|
36
|
+
out.push(match);
|
|
37
|
+
rest = rest.slice(match.length);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const token = /^[^\s,]+/.exec(rest);
|
|
41
|
+
if (!token) {
|
|
42
|
+
rest = rest.slice(1);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
out.push(token[0]);
|
|
46
|
+
rest = rest.slice(token[0].length);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
export function isLspDisabledListValid(raw) {
|
|
51
|
+
return splitLspDisabledList(raw).every(isLspServerId);
|
|
52
|
+
}
|
|
53
|
+
// Deduplicate and return the canonical shipped order.
|
|
54
|
+
export function normalizeLspServers(ids) {
|
|
55
|
+
const selected = new Set(ids);
|
|
56
|
+
return LSP_SERVER_IDS.filter((id) => selected.has(id));
|
|
57
|
+
}
|
|
58
|
+
// Resolve the project override first, then the global list, then subtract the
|
|
59
|
+
// project's disabled ids. Disabled ids that are not in the resolved list are a
|
|
60
|
+
// no-op (the global default may change later).
|
|
61
|
+
export function resolveLspServerList(projectList, globalList, disabled) {
|
|
62
|
+
const base = projectList
|
|
63
|
+
? normalizeLspServers(splitLspServerList(projectList))
|
|
64
|
+
: normalizeLspServers(globalList);
|
|
65
|
+
const removed = new Set(splitLspDisabledList(disabled));
|
|
66
|
+
return base.filter((id) => !removed.has(id));
|
|
67
|
+
}
|
|
68
|
+
// A missing/blank blob means "use the default list". A JSON array is passed
|
|
69
|
+
// through so the zod schema can reject unknown ids rather than silently
|
|
70
|
+
// dropping an operator's typo.
|
|
71
|
+
export function lspServersFromStoredJson(raw) {
|
|
72
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
73
|
+
return [...DEFAULT_LSP_SERVERS];
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(raw);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [...DEFAULT_LSP_SERVERS];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=lsp-servers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lsp-servers.js","sourceRoot":"","sources":["../src/lsp-servers.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/auth.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { db } from "../db.js";
|
|
3
|
+
import { log } from "../logger.js";
|
|
4
|
+
// Per-thread MCP bearer tokens. Separate table from project_tokens (which are
|
|
5
|
+
// webhook HMAC signing keys — different lifecycle, different lookup key) but
|
|
6
|
+
// same DB file (devbot.db) so one chmod 0600 covers both.
|
|
7
|
+
//
|
|
8
|
+
// Scope: each row carries the project_id + work_item_id the token is good for.
|
|
9
|
+
// The MCP auth layer reads project_id from the row and injects it into the
|
|
10
|
+
// tool-call context; tools never accept a project_id argument from the client.
|
|
11
|
+
// This is the scope enforcement — a token minted for project A cannot read
|
|
12
|
+
// project B's issues even if the forge bot token itself is global.
|
|
13
|
+
export function initMcpTables() {
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS mcp_thread_tokens (
|
|
16
|
+
token TEXT PRIMARY KEY,
|
|
17
|
+
project_id INTEGER NOT NULL,
|
|
18
|
+
work_item_id INTEGER NOT NULL,
|
|
19
|
+
created_at INTEGER NOT NULL,
|
|
20
|
+
exp_at INTEGER NOT NULL
|
|
21
|
+
)
|
|
22
|
+
`);
|
|
23
|
+
// Belt-and-braces for test DBs that skip migrate(); migration 0017 is the
|
|
24
|
+
// authoritative source. 0 = "number unknown" (never a valid work-item number).
|
|
25
|
+
try {
|
|
26
|
+
db.exec("ALTER TABLE mcp_thread_tokens ADD COLUMN work_item_number INTEGER NOT NULL DEFAULT 0");
|
|
27
|
+
}
|
|
28
|
+
catch { /* exists */ }
|
|
29
|
+
}
|
|
30
|
+
export function mintMcpToken(input) {
|
|
31
|
+
const token = randomBytes(32).toString("hex");
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
const expAt = now + input.ttlMs;
|
|
34
|
+
db.prepare(`INSERT OR REPLACE INTO mcp_thread_tokens
|
|
35
|
+
(token, project_id, work_item_id, work_item_number, created_at, exp_at)
|
|
36
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(token, input.projectId, input.workItemId, input.workItemNumber, Math.floor(now / 1000), Math.floor(expAt / 1000));
|
|
37
|
+
return { token, expAt };
|
|
38
|
+
}
|
|
39
|
+
export function verifyMcpToken(token) {
|
|
40
|
+
const row = db
|
|
41
|
+
.prepare("SELECT project_id, work_item_id, work_item_number, exp_at FROM mcp_thread_tokens WHERE token = ?")
|
|
42
|
+
.get(token);
|
|
43
|
+
if (!row)
|
|
44
|
+
return null;
|
|
45
|
+
if (row.exp_at * 1000 < Date.now())
|
|
46
|
+
return null;
|
|
47
|
+
return { projectId: row.project_id, workItemId: row.work_item_id, workItemNumber: row.work_item_number };
|
|
48
|
+
}
|
|
49
|
+
export function deleteMcpToken(token) {
|
|
50
|
+
db.prepare("DELETE FROM mcp_thread_tokens WHERE token = ?").run(token);
|
|
51
|
+
}
|
|
52
|
+
// Best-effort purge of expired rows. Called on devbot startup so the table
|
|
53
|
+
// doesn't grow unbounded across restarts. ponytail: not a periodic sweep —
|
|
54
|
+
// per-run delete in the runner's finally block covers the steady state; this
|
|
55
|
+
// only catches tokens left behind by a crash mid-run.
|
|
56
|
+
export function purgeExpiredMcpTokens() {
|
|
57
|
+
const now = Math.floor(Date.now() / 1000);
|
|
58
|
+
const r = db.prepare("DELETE FROM mcp_thread_tokens WHERE exp_at < ?").run(now);
|
|
59
|
+
if (r.changes > 0)
|
|
60
|
+
log.debug("mcp token purge", { expired: r.changes });
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/mcp/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,8EAA8E;AAC9E,6EAA6E;AAC7E,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,+EAA+E;AAC/E,2EAA2E;AAC3E,mEAAmE;AAEnE,MAAM,UAAU,aAAa;IAC3B,EAAE,CAAC,IAAI,CAAC;;;;;;;;GAQP,CAAC,CAAC;IACH,0EAA0E;IAC1E,+EAA+E;IAC/E,IAAI,CAAC;QAAC,EAAE,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;AACjI,CAAC;AAOD,MAAM,UAAU,YAAY,CAAC,KAK5B;IACC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;IAChC,EAAE,CAAC,OAAO,CACR;;+BAE2B,CAC5B,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;IACxH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,kGAAkG,CAAC;SAC3G,GAAG,CAAC,KAAK,CAEC,CAAC;IACd,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC;AAC3G,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,EAAE,CAAC,OAAO,CAAC,+CAA+C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAED,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,sDAAsD;AACtD,MAAM,UAAU,qBAAqB;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChF,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC;QAAE,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { listMcpServers, listGlobalMcpServers } from "../mcp-config-store.js";
|
|
2
|
+
// The full effective picture (for the WebUI): every global server (including
|
|
3
|
+
// ones shadowed by a project, flagged), then every project server. Runtime
|
|
4
|
+
// precedence is applied by mergedMcpServers.
|
|
5
|
+
export function effectiveMcpServers(projectId) {
|
|
6
|
+
const project = listMcpServers(projectId);
|
|
7
|
+
const projectNames = new Set(project.map((s) => s.name));
|
|
8
|
+
const out = [];
|
|
9
|
+
for (const s of listGlobalMcpServers()) {
|
|
10
|
+
out.push({
|
|
11
|
+
name: s.name,
|
|
12
|
+
config: s.config,
|
|
13
|
+
origin: "global",
|
|
14
|
+
overriddenByProject: projectNames.has(s.name),
|
|
15
|
+
overridesGlobal: false,
|
|
16
|
+
enabled: s.config.enabled !== false,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
for (const s of project) {
|
|
20
|
+
out.push({
|
|
21
|
+
name: s.name,
|
|
22
|
+
config: s.config,
|
|
23
|
+
origin: "project",
|
|
24
|
+
overriddenByProject: false,
|
|
25
|
+
overridesGlobal: listGlobalMcpServers().some((g) => g.name === s.name),
|
|
26
|
+
enabled: s.config.enabled !== false,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
// The name -> config map a run passes to buildOpencodeConfig. Global first then
|
|
32
|
+
// project (project wins on a name collision). Disabled entries are kept (with
|
|
33
|
+
// enabled:false) so the run config records the operator's intent, matching the
|
|
34
|
+
// prior `[...global, ...project]` spread in prepareMcpRun.
|
|
35
|
+
export function mergedMcpServers(projectId) {
|
|
36
|
+
const out = {};
|
|
37
|
+
const scoped = [...listGlobalMcpServers(), ...listMcpServers(projectId)];
|
|
38
|
+
for (const s of scoped)
|
|
39
|
+
out[s.name] = s.config;
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=effective-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effective-mcp.js","sourceRoot":"","sources":["../../src/mcp/effective-mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAwB,MAAM,wBAAwB,CAAC;AAuBpG,6EAA6E;AAC7E,2EAA2E;AAC3E,6CAA6C;AAC7C,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzD,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,oBAAoB,EAAE,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,MAAM,EAAE,QAAQ;YAChB,mBAAmB,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,eAAe,EAAE,KAAK;YACtB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;SACpC,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,MAAM,EAAE,SAAS;YACjB,mBAAmB,EAAE,KAAK;YAC1B,eAAe,EAAE,oBAAoB,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC;YACtE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,KAAK;SACpC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,GAAG,GAAoC,EAAE,CAAC;IAChD,MAAM,MAAM,GAAsB,CAAC,GAAG,oBAAoB,EAAE,EAAE,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5F,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,OAAO,GAAG,CAAC;AACb,CAAC"}
|