@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,178 @@
|
|
|
1
|
+
import { shq } from "./redact.js";
|
|
2
|
+
// Pure conflict-fix helpers (work item #56): the git shell-script builders,
|
|
3
|
+
// their result parsers, the resolver prompt/result parser, and the note
|
|
4
|
+
// formatters. Deliberately dependency-light (no config/podman) so the contract
|
|
5
|
+
// is unit-testable without a container. The podman wrappers live in git.ts,
|
|
6
|
+
// which imports these one-way; this module must never import git.ts.
|
|
7
|
+
// ---- Conflict check --------------------------------------------------------
|
|
8
|
+
// Host-side conflict check. Order is load-bearing:
|
|
9
|
+
// 1. abort any half-finished rebase/merge a prior killed resolver left behind
|
|
10
|
+
// (a /stop or timeout mid-rebase); `|| true` so a clean tree is a no-op.
|
|
11
|
+
// 2. fetch origin so the target ref is current.
|
|
12
|
+
// 3. record the remote branch tip (TIP:<sha>) so the publish can pin its
|
|
13
|
+
// force-with-lease to the exact commit this run started from.
|
|
14
|
+
// 4. verify origin/<target> exists (a deleted target is an actionable failure,
|
|
15
|
+
// not an empty merge-tree success).
|
|
16
|
+
// 5. merge-tree --write-tree --name-only origin/<target> HEAD: exit 0 = clean,
|
|
17
|
+
// exit 1 = conflicts printed on stdout after the tree OID line.
|
|
18
|
+
export function conflictCheckScript(target, branch, extraHeader) {
|
|
19
|
+
return [
|
|
20
|
+
"cd /workspace",
|
|
21
|
+
"git rebase --abort 2>/dev/null || true",
|
|
22
|
+
"git merge --abort 2>/dev/null || true",
|
|
23
|
+
`${extraHeader} fetch origin`,
|
|
24
|
+
`echo "TIP:$(git rev-parse origin/${shq(branch)} 2>/dev/null || true)"`,
|
|
25
|
+
`git rev-parse --verify --quiet origin/${shq(target)} >/dev/null || { echo "CHECK_FAILED:missing-target"; exit 8; }`,
|
|
26
|
+
`git merge-tree --write-tree --name-only origin/${shq(target)} ${shq(branch)}`,
|
|
27
|
+
].join(" && ");
|
|
28
|
+
}
|
|
29
|
+
// Parse a conflict-check result. exit 0 = clean. exit 1 = merge-tree reported
|
|
30
|
+
// conflicts: stdout is the tree OID, then one path per line, then a blank line
|
|
31
|
+
// and human messages. Any other exit (8 missing target, fetch failure) → null.
|
|
32
|
+
export function parseConflictCheckResult(code, stdout) {
|
|
33
|
+
if (code === 0)
|
|
34
|
+
return { conflictFree: true, files: [] };
|
|
35
|
+
if (code !== 1)
|
|
36
|
+
return null;
|
|
37
|
+
// Strip the `TIP:` line the script prints before merge-tree, then skip the
|
|
38
|
+
// first remaining non-empty line (the merged-tree OID) and read paths up to
|
|
39
|
+
// the blank separator.
|
|
40
|
+
const lines = stdout.split("\n").filter((l) => !l.startsWith("TIP:"));
|
|
41
|
+
const files = [];
|
|
42
|
+
let sawOid = false;
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
if (line === "")
|
|
45
|
+
break;
|
|
46
|
+
if (!sawOid) {
|
|
47
|
+
sawOid = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (line.trim() !== "")
|
|
51
|
+
files.push(line.trim());
|
|
52
|
+
}
|
|
53
|
+
return { conflictFree: false, files };
|
|
54
|
+
}
|
|
55
|
+
// ---- Rewrite-aware publish -------------------------------------------------
|
|
56
|
+
// Compose the host-side publish script. Order is load-bearing:
|
|
57
|
+
// 1. HEAD guard (exit 7 WRONG_HEAD).
|
|
58
|
+
// 2. fallback commit for uncommitted agent work — but refuse if the staged
|
|
59
|
+
// diff carries conflict markers (exit 10 CONFLICT_MARKERS), so a killed
|
|
60
|
+
// resolver can't commit `<<<<<<<` into the branch.
|
|
61
|
+
// 3. fetch origin.
|
|
62
|
+
// 4. decide:
|
|
63
|
+
// unborn remote → plain push.
|
|
64
|
+
// origin is ancestor → ahead ? plain push : NO_COMMITS.
|
|
65
|
+
// origin == preRunTip → --force-with-lease pinned to preRunTip
|
|
66
|
+
// (we only rewrote our own earlier commits).
|
|
67
|
+
// else (remote moved) → REMOTE_MOVED, exit 9 (fail closed, no clobber).
|
|
68
|
+
// `force` (/force-push) short-circuits to an unconditional leased push.
|
|
69
|
+
export function publishScript(workItemNumber, branch, extraHeader, preRunTip, force = false) {
|
|
70
|
+
const b = shq(branch);
|
|
71
|
+
const lease = `--force-with-lease=refs/heads/${branch}:${preRunTip}`;
|
|
72
|
+
const markerCheck = `if git diff --cached -U0 | grep -qE '^\\+.*(<<<<<<<|=======|>>>>>>>)'; then echo "CONFLICT_MARKERS"; exit 10; fi`;
|
|
73
|
+
const decide = force
|
|
74
|
+
? `if ! git rev-parse --verify --quiet origin/${b} >/dev/null; then ${extraHeader} push origin ${b} && echo PUSHED; else ${extraHeader} push origin ${b} --force-with-lease && echo FORCE_PUSHED; fi`
|
|
75
|
+
: [
|
|
76
|
+
`if ! git rev-parse --verify --quiet origin/${b} >/dev/null; then`,
|
|
77
|
+
` ${extraHeader} push origin ${b} && echo PUSHED;`,
|
|
78
|
+
`elif git merge-base --is-ancestor origin/${b} HEAD; then`,
|
|
79
|
+
` if test -n "$(git log --format=%H origin/${b}..HEAD)"; then ${extraHeader} push origin ${b} && echo PUSHED; else echo NO_COMMITS; fi;`,
|
|
80
|
+
`elif test "$(git rev-parse origin/${b})" = ${shq(preRunTip)}; then`,
|
|
81
|
+
` ${extraHeader} push origin ${b} ${lease} && echo FORCE_PUSHED;`,
|
|
82
|
+
`else echo REMOTE_MOVED; exit 9; fi`,
|
|
83
|
+
].join("\n");
|
|
84
|
+
return [
|
|
85
|
+
"cd /workspace",
|
|
86
|
+
`test "$(git rev-parse --abbrev-ref HEAD)" = ${b} || { echo "WRONG_HEAD:$(git rev-parse --abbrev-ref HEAD)"; exit 7; }`,
|
|
87
|
+
`if test -n "$(git status --porcelain)"; then git add -A && { ${markerCheck}; } && git commit -m "chore: fallback commit for uncommitted agent work (#${workItemNumber})" --no-verify && echo FALLBACK_COMMITTED; fi`,
|
|
88
|
+
`${extraHeader} fetch origin`,
|
|
89
|
+
decide,
|
|
90
|
+
].join(" && ");
|
|
91
|
+
}
|
|
92
|
+
export function parsePublishResult(stdout) {
|
|
93
|
+
return {
|
|
94
|
+
pushed: stdout.includes("PUSHED") || stdout.includes("FORCE_PUSHED"),
|
|
95
|
+
forced: stdout.includes("FORCE_PUSHED"),
|
|
96
|
+
noCommits: stdout.includes("NO_COMMITS"),
|
|
97
|
+
fallbackCommitted: stdout.includes("FALLBACK_COMMITTED"),
|
|
98
|
+
remoteMoved: stdout.includes("REMOTE_MOVED"),
|
|
99
|
+
rewritten: stdout.includes("REWRITTEN"),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// ---- Resolver --------------------------------------------------------------
|
|
103
|
+
// The direct resolver prompt. Rebase-first, merge fallback; never push or switch
|
|
104
|
+
// branches (DevBot owns the push); print a machine-readable terminal marker.
|
|
105
|
+
export function resolverPrompt(branch, target) {
|
|
106
|
+
return [
|
|
107
|
+
`The branch \`${branch}\` conflicts with its merge target \`origin/${target}\`.`,
|
|
108
|
+
"",
|
|
109
|
+
"Resolve the conflict so the branch merges cleanly. Do exactly this:",
|
|
110
|
+
"",
|
|
111
|
+
`1. DevBot has already fetched origin for this run, so \`origin/${target}\` is current — do not fetch yourself.`,
|
|
112
|
+
`2. Rebase the work branch onto the target: \`git rebase origin/${target}\`.`,
|
|
113
|
+
"3. Resolve every conflict, stage the files, and let the rebase finish.",
|
|
114
|
+
`4. If a clean rebase is not viable (e.g. an irresolvable or binary conflict), run \`git rebase --abort\`, then \`git merge origin/${target}\`, resolve and commit the merge.`,
|
|
115
|
+
"5. Keep the resolved tree building where you reasonably can, but do not run long builds.",
|
|
116
|
+
"",
|
|
117
|
+
"Hard rules:",
|
|
118
|
+
`- Work only on \`${branch}\` — never switch branches.`,
|
|
119
|
+
"- **Do not push or fetch**; DevBot does both host-side. DevBot pushes after you finish.",
|
|
120
|
+
"- Do not amend or rewrite commits beyond what rebase requires.",
|
|
121
|
+
"",
|
|
122
|
+
"When done, end your reply with exactly one marker:",
|
|
123
|
+
`- \`RESOLVED:rebase\` or \`RESOLVED:merge\` on success, or`,
|
|
124
|
+
`- \`UNRESOLVED: <comma-separated file paths>\` if you could not resolve it.`,
|
|
125
|
+
].join("\n");
|
|
126
|
+
}
|
|
127
|
+
export function parseResolverResult(text) {
|
|
128
|
+
const resolved = text.match(/RESOLVED:(rebase|merge)/);
|
|
129
|
+
if (resolved)
|
|
130
|
+
return { resolved: true, strategy: resolved[1], files: [] };
|
|
131
|
+
const unresolved = text.match(/UNRESOLVED:\s*(.*)/);
|
|
132
|
+
const files = unresolved && unresolved[1]
|
|
133
|
+
? [...new Set(unresolved[1].split(",").map((s) => s.trim()).filter(Boolean))]
|
|
134
|
+
: [];
|
|
135
|
+
return { resolved: false, strategy: "", files };
|
|
136
|
+
}
|
|
137
|
+
// ---- Notes -----------------------------------------------------------------
|
|
138
|
+
// Sanitize a repo-controlled path before wrapping it in an inline-code span: a
|
|
139
|
+
// stray backtick would break the Markdown and could inject a mention.
|
|
140
|
+
function sanitizePath(p) {
|
|
141
|
+
return p.replace(/[`\r\n]/g, "").trim();
|
|
142
|
+
}
|
|
143
|
+
export const CONFLICT_FILES_CAP = 10;
|
|
144
|
+
export function formatConflictFixNote(target, strategy) {
|
|
145
|
+
return `🔧 Auto-resolved merge conflicts with \`${target}\` (${strategy}).`;
|
|
146
|
+
}
|
|
147
|
+
export function formatUnresolvedNote(target, files) {
|
|
148
|
+
const shown = files.slice(0, CONFLICT_FILES_CAP).map((f) => `\`${sanitizePath(f)}\``);
|
|
149
|
+
const overflow = files.length - shown.length;
|
|
150
|
+
const list = shown.length > 0
|
|
151
|
+
? shown.join(", ") + (overflow > 0 ? ` and ${overflow} more` : "")
|
|
152
|
+
: "the target branch";
|
|
153
|
+
return (`⚠️ Could not auto-resolve conflicts with \`${target}\`: ${list}. ` +
|
|
154
|
+
`The branch was pushed without the merge request. Resolve the conflicts manually and re-run \`/mr\`, ` +
|
|
155
|
+
`or disable conflict auto-fix in the project settings.`);
|
|
156
|
+
}
|
|
157
|
+
// Fix-only note for the standalone /fix-conflicts task: no publish happened, so
|
|
158
|
+
// the branch is untouched when resolution fails. Never mentions an MR step.
|
|
159
|
+
export function formatFixOnlyUnresolvedNote(target, files) {
|
|
160
|
+
const shown = files.slice(0, CONFLICT_FILES_CAP).map((f) => `\`${sanitizePath(f)}\``);
|
|
161
|
+
const overflow = files.length - shown.length;
|
|
162
|
+
const list = shown.length > 0
|
|
163
|
+
? shown.join(", ") + (overflow > 0 ? ` and ${overflow} more` : "")
|
|
164
|
+
: "the target branch";
|
|
165
|
+
return (`⚠️ Could not fix conflicts with \`${target}\`: ${list}. ` +
|
|
166
|
+
`The branch was left unchanged. Resolve the conflicts manually, then re-run \`/fix-conflicts\`.`);
|
|
167
|
+
}
|
|
168
|
+
// Fix-only note when the branch already merges cleanly — nothing to do.
|
|
169
|
+
export function formatNoConflictsNote(target) {
|
|
170
|
+
return `ℹ️ No merge conflicts with \`${target}\` — nothing to fix.`;
|
|
171
|
+
}
|
|
172
|
+
// Extract the `TIP:<sha>` line a script echoes. null when the ref is unborn or
|
|
173
|
+
// the marker is absent (the publish then treats the remote as unborn).
|
|
174
|
+
export function parseBranchTip(stdout) {
|
|
175
|
+
const m = stdout.match(/TIP:([0-9a-f]{7,40})/);
|
|
176
|
+
return m ? m[1] : null;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=conflict-fix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-fix.js","sourceRoot":"","sources":["../src/conflict-fix.ts"],"names":[],"mappings":"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"}
|
package/dist/cookie.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, scryptSync, randomBytes } from "node:crypto";
|
|
2
|
+
import { config } from "./config.js";
|
|
3
|
+
// ponytail: fixed scrypt salt. Fine for this single-instance bot — the key
|
|
4
|
+
// derivation is deterministic so cookies survive a restart. Upgrade path:
|
|
5
|
+
// per-deployment random salt stored alongside the db, if multi-instance.
|
|
6
|
+
const KEY = scryptSync(config.SESSION_KEY, "devbot-session-salt", 32);
|
|
7
|
+
const ALGO = "aes-256-gcm";
|
|
8
|
+
export function signCookie(payload) {
|
|
9
|
+
const iv = randomBytes(12);
|
|
10
|
+
const cipher = createCipheriv(ALGO, KEY, iv);
|
|
11
|
+
const plaintext = Buffer.from(JSON.stringify(payload), "utf8");
|
|
12
|
+
const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
13
|
+
const tag = cipher.getAuthTag();
|
|
14
|
+
return Buffer.concat([iv, tag, enc]).toString("base64");
|
|
15
|
+
}
|
|
16
|
+
export function readCookieWithReason(value) {
|
|
17
|
+
if (!value)
|
|
18
|
+
return { payload: null, reason: "missing" };
|
|
19
|
+
let buf;
|
|
20
|
+
try {
|
|
21
|
+
buf = Buffer.from(value, "base64");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { payload: null, reason: "base64" };
|
|
25
|
+
}
|
|
26
|
+
if (buf.length < 12 + 16)
|
|
27
|
+
return { payload: null, reason: "too-short" };
|
|
28
|
+
const iv = buf.subarray(0, 12);
|
|
29
|
+
const tag = buf.subarray(12, 28);
|
|
30
|
+
const enc = buf.subarray(28);
|
|
31
|
+
try {
|
|
32
|
+
const decipher = createDecipheriv(ALGO, KEY, iv);
|
|
33
|
+
decipher.setAuthTag(tag);
|
|
34
|
+
const dec = Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
35
|
+
let payload;
|
|
36
|
+
try {
|
|
37
|
+
const obj = JSON.parse(dec.toString("utf8"));
|
|
38
|
+
if (typeof obj !== "object" || obj === null ||
|
|
39
|
+
typeof obj.accessToken !== "string" ||
|
|
40
|
+
typeof obj.userId !== "number" ||
|
|
41
|
+
typeof obj.username !== "string" ||
|
|
42
|
+
typeof obj.exp !== "number") {
|
|
43
|
+
return { payload: null, reason: "json" };
|
|
44
|
+
}
|
|
45
|
+
payload = obj;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { payload: null, reason: "json" };
|
|
49
|
+
}
|
|
50
|
+
if (payload.exp < Math.floor(Date.now() / 1000))
|
|
51
|
+
return { payload: null, reason: "expired" };
|
|
52
|
+
return { payload, reason: "ok" };
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { payload: null, reason: "decrypt" };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export const SESSION_COOKIE = "devbot_session";
|
|
59
|
+
export const STATE_COOKIE = "devbot_oauth_state";
|
|
60
|
+
export function cookieOptions(maxAge) {
|
|
61
|
+
return {
|
|
62
|
+
httpOnly: true,
|
|
63
|
+
secure: config.PUBLIC_URL.startsWith("https:"),
|
|
64
|
+
sameSite: "lax",
|
|
65
|
+
path: "/",
|
|
66
|
+
// ponytail: Express/cookie use ms; call sites pass seconds. Multiply here.
|
|
67
|
+
maxAge: maxAge * 1000,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=cookie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie.js","sourceRoot":"","sources":["../src/cookie.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, scryptSync, randomBytes } from "node:crypto";
|
|
2
|
+
import { config } from "./config.js";
|
|
3
|
+
// ponytail: fixed scrypt salt — same posture as cookie.ts. The key is derived
|
|
4
|
+
// from SESSION_KEY (same env var, different salt → different key). Fine for
|
|
5
|
+
// single-instance; upgrade path: per-deployment random salt if multi-instance.
|
|
6
|
+
const KEY = scryptSync(config.SESSION_KEY, "devbot-db-encryption-salt", 32);
|
|
7
|
+
const ALGO = "aes-256-gcm";
|
|
8
|
+
export function encrypt(plaintext) {
|
|
9
|
+
const iv = randomBytes(12);
|
|
10
|
+
const cipher = createCipheriv(ALGO, KEY, iv);
|
|
11
|
+
const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
12
|
+
const tag = cipher.getAuthTag();
|
|
13
|
+
return JSON.stringify({ iv: iv.toString("base64"), tag: tag.toString("base64"), data: enc.toString("base64") });
|
|
14
|
+
}
|
|
15
|
+
export function decrypt(blob) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = JSON.parse(blob);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null; // not an encrypted blob
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const decipher = createDecipheriv(ALGO, KEY, Buffer.from(parsed.iv, "base64"));
|
|
25
|
+
decipher.setAuthTag(Buffer.from(parsed.tag, "base64"));
|
|
26
|
+
const dec = Buffer.concat([decipher.update(Buffer.from(parsed.data, "base64")), decipher.final()]);
|
|
27
|
+
return dec.toString("utf8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null; // tampered or wrong key
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Try decrypt first; if it fails, treat as plaintext (transparent migration
|
|
34
|
+
// from pre-encryption rows). ponytail: an attacker who writes plaintext to
|
|
35
|
+
// the DB bypasses encryption — acceptable since the DB is 0600 single-operator.
|
|
36
|
+
export function decryptOrPlaintext(blob) {
|
|
37
|
+
const dec = decrypt(blob);
|
|
38
|
+
return dec !== null ? dec : blob;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=crypto-util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-util.js","sourceRoot":"","sources":["../src/crypto-util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,8EAA8E;AAC9E,8EAA8E;AAC9E,iFAAiF;AACjF,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,WAAW,EAAE,2BAA2B,EAAE,EAAE,CAAC,CAAC;AAC5E,MAAM,IAAI,GAAG,aAAa,CAAC;AAE3B,MAAM,UAAU,OAAO,CAAC,SAAiB;IACvC,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAChC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAClH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,IAAI,MAAiD,CAAC;IACtD,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,wBAAwB;IACvC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/E,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;QACvD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACnG,OAAO,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,wBAAwB;IACvC,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,2EAA2E;AAC3E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { db } from "./db.js";
|
|
2
|
+
import { encrypt, decryptOrPlaintext } from "./crypto-util.js";
|
|
3
|
+
import { CustomRepoBodySchema, } from "./types/custom-repos.js";
|
|
4
|
+
export function initCustomReposTables() {
|
|
5
|
+
db.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS custom_repos (
|
|
7
|
+
project_id INTEGER NOT NULL,
|
|
8
|
+
kind TEXT NOT NULL,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
url TEXT NOT NULL,
|
|
11
|
+
token TEXT,
|
|
12
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
13
|
+
created_at INTEGER NOT NULL,
|
|
14
|
+
updated_at INTEGER NOT NULL,
|
|
15
|
+
PRIMARY KEY (project_id, kind, name)
|
|
16
|
+
)
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
function rowToRepo(row) {
|
|
20
|
+
return {
|
|
21
|
+
name: String(row.name),
|
|
22
|
+
url: String(row.url),
|
|
23
|
+
token: decryptOrPlaintext(String(row.token ?? "")),
|
|
24
|
+
enabled: row.enabled === 1,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function listCustomRepoSummaries(projectId, kind) {
|
|
28
|
+
const rows = db
|
|
29
|
+
.prepare("SELECT name, url, token, enabled FROM custom_repos WHERE project_id = ? AND kind = ? ORDER BY name")
|
|
30
|
+
.all(projectId, kind);
|
|
31
|
+
return rows.map((r) => {
|
|
32
|
+
const repo = rowToRepo(r);
|
|
33
|
+
return { name: repo.name, url: repo.url, enabled: repo.enabled, hasToken: repo.token.length > 0 };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function upsertCustomRepo(projectId, kind, name, body) {
|
|
37
|
+
const v = CustomRepoBodySchema.parse(body);
|
|
38
|
+
const existing = db
|
|
39
|
+
.prepare("SELECT token FROM custom_repos WHERE project_id = ? AND kind = ? AND name = ?")
|
|
40
|
+
.get(projectId, kind, name);
|
|
41
|
+
// Empty/absent token on edit keeps the stored value; a new public repo stores
|
|
42
|
+
// an empty token (encrypt("")).
|
|
43
|
+
const tokenBlob = v.token ? encrypt(v.token) : String(existing?.token ?? "");
|
|
44
|
+
const now = Math.floor(Date.now() / 1000);
|
|
45
|
+
db.prepare(`INSERT OR REPLACE INTO custom_repos
|
|
46
|
+
(project_id, kind, name, url, token, enabled, created_at, updated_at)
|
|
47
|
+
VALUES (?, ?, ?, ?, ?, ?, COALESCE((SELECT created_at FROM custom_repos WHERE project_id = ? AND kind = ? AND name = ?), ?), ?)`).run(projectId, kind, name, v.url, tokenBlob, v.enabled ? 1 : 0, projectId, kind, name, now, now);
|
|
48
|
+
}
|
|
49
|
+
export function deleteCustomRepo(projectId, kind, name) {
|
|
50
|
+
const r = db.prepare("DELETE FROM custom_repos WHERE project_id = ? AND kind = ? AND name = ?").run(projectId, kind, name);
|
|
51
|
+
return r.changes > 0;
|
|
52
|
+
}
|
|
53
|
+
export function deleteAllCustomRepos(projectId) {
|
|
54
|
+
try {
|
|
55
|
+
db.prepare("DELETE FROM custom_repos WHERE project_id = ?").run(projectId);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Table may not exist in focused unit tests that skip init.
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Resolve the repos a run should sync: global first, then project overrides by
|
|
62
|
+
// name. A disabled project repo removes a global repo of the same name; disabled
|
|
63
|
+
// global repos are excluded. Deterministic name order.
|
|
64
|
+
export function resolveCustomRepos(projectId, kind) {
|
|
65
|
+
const load = (pid) => {
|
|
66
|
+
let rows;
|
|
67
|
+
try {
|
|
68
|
+
rows = db
|
|
69
|
+
.prepare("SELECT name, url, token, enabled FROM custom_repos WHERE project_id = ? AND kind = ? ORDER BY name")
|
|
70
|
+
.all(pid, kind);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Table may not exist in focused unit tests that skip init.
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return rows.map(rowToRepo);
|
|
77
|
+
};
|
|
78
|
+
const merged = new Map();
|
|
79
|
+
for (const r of load(0))
|
|
80
|
+
if (r.enabled)
|
|
81
|
+
merged.set(r.name, r);
|
|
82
|
+
for (const r of load(projectId)) {
|
|
83
|
+
if (r.enabled)
|
|
84
|
+
merged.set(r.name, r);
|
|
85
|
+
else
|
|
86
|
+
merged.delete(r.name);
|
|
87
|
+
}
|
|
88
|
+
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=custom-repos-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-repos-store.js","sourceRoot":"","sources":["../src/custom-repos-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { config } from "./config.js";
|
|
2
|
+
import { log } from "./logger.js";
|
|
3
|
+
import { redact } from "./redact.js";
|
|
4
|
+
import { podman } from "./podman.js";
|
|
5
|
+
import { volumeName, xdgVolumeName, envArgs, logDriverArgs } from "./git.js";
|
|
6
|
+
import { gitExtraHeaderFor } from "./platform/git-auth.js";
|
|
7
|
+
// Sync admin-registered skills/subagent repositories into a work item's
|
|
8
|
+
// volumes. Modeled on src/mcp/peer-sync.ts:
|
|
9
|
+
//
|
|
10
|
+
// - Skills land in /workspace/.devbot-skills/<name>/ (inside the repo tree but
|
|
11
|
+
// git-excluded) so the agent's read/glob/grep and the skill tool can reach a
|
|
12
|
+
// skill's reference files. The xdg volume is masked from the agent, so skills
|
|
13
|
+
// cannot live there.
|
|
14
|
+
// - Agents are cloned into an xdg staging dir, then every *.md is flattened
|
|
15
|
+
// into the opencode-scanned config agents dir. opencode derives an agent's
|
|
16
|
+
// name from its path under the config dir, so cloning straight into
|
|
17
|
+
// agents/<repo>/ would leak the repo's folder layout into the /agent name.
|
|
18
|
+
//
|
|
19
|
+
// Auth is a host-scoped git extraHeader (src/platform/git-auth.ts), so no
|
|
20
|
+
// platform branching leaks here and a private repo's token is only sent to its
|
|
21
|
+
// own host. Public repos omit the header entirely.
|
|
22
|
+
//
|
|
23
|
+
// Every repo is best-effort: a failure emits a marker line and the script still
|
|
24
|
+
// exits 0, so one bad repo never blocks a run.
|
|
25
|
+
export const SKILLS_DIR = ".devbot-skills";
|
|
26
|
+
export const SKILLS_ROOT = `/workspace/${SKILLS_DIR}`;
|
|
27
|
+
export const XDG_HOME = "/home/devbot/.xdg";
|
|
28
|
+
export const AGENT_STAGING_ROOT = `${XDG_HOME}/custom-repos/agents`;
|
|
29
|
+
export const AGENT_CONFIG_DIR = `${XDG_HOME}/config/opencode/agents`;
|
|
30
|
+
export const OK_MARKER = "DEVBOT_CUSTOM_REPO_OK";
|
|
31
|
+
export const FAIL_MARKER = "DEVBOT_CUSTOM_REPO_FAIL";
|
|
32
|
+
// The opencode `skills.paths` entries for a set of skill repos. Emits both the
|
|
33
|
+
// repo root and its `skills/` subdir so either layout is discovered. Pure.
|
|
34
|
+
export function skillsPathsFor(repos) {
|
|
35
|
+
return repos.flatMap((r) => [`${SKILLS_ROOT}/${r.name}`, `${SKILLS_ROOT}/${r.name}/skills`]);
|
|
36
|
+
}
|
|
37
|
+
function shellQuote(s) {
|
|
38
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
39
|
+
}
|
|
40
|
+
// Pure shell-script builder. `headerFor(url)` returns a trailing-spaced git
|
|
41
|
+
// prefix (or "") for the repo's URL. Exported so scripts/check-custom-repos.ts
|
|
42
|
+
// can pin clone-vs-fetch, destinations, prune, and auth scoping without podman.
|
|
43
|
+
export function customReposSyncScript(kind, repos, headerFor) {
|
|
44
|
+
const base = kind === "skills" ? SKILLS_ROOT : AGENT_STAGING_ROOT;
|
|
45
|
+
const lines = [`mkdir -p ${base}`];
|
|
46
|
+
if (kind === "skills") {
|
|
47
|
+
// Drop skill dirs no longer configured. The configured names are embedded
|
|
48
|
+
// as a space-delimited set; each existing dir is kept only if present.
|
|
49
|
+
const known = repos.map((r) => r.name).join(" ");
|
|
50
|
+
lines.push(`for d in ${SKILLS_ROOT}/*/; do [ -d "$d" ] || continue; n=$(basename "$d"); ` +
|
|
51
|
+
`case " ${known} " in *" $n "*) ;; *) rm -rf "$d" ;; esac; done`);
|
|
52
|
+
}
|
|
53
|
+
for (const r of repos) {
|
|
54
|
+
const dir = `${base}/${r.name}`;
|
|
55
|
+
const header = headerFor(r.url);
|
|
56
|
+
const clone = `${header}git clone --depth 1 ${shellQuote(r.url)} ${shellQuote(dir)}`;
|
|
57
|
+
const fetch = `(cd ${shellQuote(dir)} && ${header}git fetch --depth 1 origin && git reset --hard FETCH_HEAD)`;
|
|
58
|
+
lines.push(`if [ -d ${shellQuote(dir)}/.git ]; then ` +
|
|
59
|
+
`{ ${fetch} && echo "${OK_MARKER}:${r.name}"; } || echo "${FAIL_MARKER}:${r.name}"; ` +
|
|
60
|
+
`else { ${clone} && echo "${OK_MARKER}:${r.name}"; } || echo "${FAIL_MARKER}:${r.name}"; fi`);
|
|
61
|
+
}
|
|
62
|
+
if (kind === "agents")
|
|
63
|
+
lines.push(flattenAgentScript());
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
// Clear the scanned agents dir and flatten every markdown file from the staging
|
|
67
|
+
// tree into it. Prints the resulting basenames (one per line) so the caller can
|
|
68
|
+
// learn the installed agent names.
|
|
69
|
+
export function flattenAgentScript() {
|
|
70
|
+
return (`rm -rf ${AGENT_CONFIG_DIR} && mkdir -p ${AGENT_CONFIG_DIR} && ` +
|
|
71
|
+
`find ${AGENT_STAGING_ROOT} -name '*.md' -type f -print0 2>/dev/null | ` +
|
|
72
|
+
`while IFS= read -r -d '' f; do cp "$f" "${AGENT_CONFIG_DIR}/$(basename "$f")"; done && ` +
|
|
73
|
+
`ls ${AGENT_CONFIG_DIR} 2>/dev/null`);
|
|
74
|
+
}
|
|
75
|
+
export async function syncCustomRepos(input) {
|
|
76
|
+
const enabled = input.repos.filter((r) => r.enabled);
|
|
77
|
+
if (enabled.length === 0)
|
|
78
|
+
return { synced: [], failed: [], agents: [] };
|
|
79
|
+
const tokenByUrl = new Map(enabled.map((r) => [r.url, r.token]));
|
|
80
|
+
const script = customReposSyncScript(input.kind, enabled, (url) => {
|
|
81
|
+
const token = tokenByUrl.get(url) ?? "";
|
|
82
|
+
return token ? `${gitExtraHeaderFor(token, "git", url)} ` : "";
|
|
83
|
+
});
|
|
84
|
+
const r = await podman([
|
|
85
|
+
"run", "--rm", ...logDriverArgs(), "--name", `devbot-wi-${input.workItemId}-${input.kind}sync`,
|
|
86
|
+
"-v", `${volumeName(input.workItemId)}:/workspace:Z`,
|
|
87
|
+
"-v", `${xdgVolumeName(input.workItemId)}:${XDG_HOME}:Z`,
|
|
88
|
+
...envArgs(input.envVars ?? []),
|
|
89
|
+
input.builderImage ?? config.BUILDER_IMAGE,
|
|
90
|
+
"sh", "-c", script,
|
|
91
|
+
], { timeoutMs: input.timeoutMs ?? 120_000 });
|
|
92
|
+
const synced = [];
|
|
93
|
+
const failed = [];
|
|
94
|
+
for (const repo of enabled) {
|
|
95
|
+
if (r.stdout.includes(`${OK_MARKER}:${repo.name}`))
|
|
96
|
+
synced.push(repo.name);
|
|
97
|
+
else
|
|
98
|
+
failed.push(repo.name);
|
|
99
|
+
}
|
|
100
|
+
if (failed.length > 0) {
|
|
101
|
+
log.warn("custom repo sync failed", {
|
|
102
|
+
kind: input.kind,
|
|
103
|
+
workItemId: input.workItemId,
|
|
104
|
+
failed,
|
|
105
|
+
code: r.code,
|
|
106
|
+
stderr: redact(r.stderr.slice(-256)),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const agents = input.kind === "agents"
|
|
110
|
+
? r.stdout
|
|
111
|
+
.split("\n")
|
|
112
|
+
.map((s) => s.trim())
|
|
113
|
+
.filter((s) => s.endsWith(".md"))
|
|
114
|
+
.map((s) => s.replace(/\.md$/, ""))
|
|
115
|
+
: [];
|
|
116
|
+
return { synced, failed, agents };
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=custom-repos-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-repos-sync.js","sourceRoot":"","sources":["../src/custom-repos-sync.ts"],"names":[],"mappings":"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"}
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
// Shared DB handle. All stores import this `db` instead of opening their own
|
|
3
|
+
// DatabaseSync — one handle, one WAL pragma, one busy_timeout, one close.
|
|
4
|
+
// ponytail: DEVBOT_DB_PATH override exists for tests; production uses devbot.db.
|
|
5
|
+
const DB_PATH = process.env.DEVBOT_DB_PATH ?? "devbot.db";
|
|
6
|
+
export const db = new DatabaseSync(DB_PATH);
|
|
7
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
8
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
9
|
+
export function closeDb() {
|
|
10
|
+
db.close();
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,6EAA6E;AAC7E,0EAA0E;AAC1E,iFAAiF;AACjF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,WAAW,CAAC;AAE1D,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;AAC5C,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;AACrC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;AAEtC,MAAM,UAAU,OAAO;IACrB,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// `.devbot/` scripts the runner executes inside the builder container.
|
|
2
|
+
// Today only `init.sh` exists (post-clone, first run only). Future scripts
|
|
3
|
+
// (e.g. `.devbot/post-sync.sh`, `.devbot/pre-run.sh`) slot in here without
|
|
4
|
+
// touching git.ts or runner.ts — add a phase function and call it from the
|
|
5
|
+
// appropriate runner step.
|
|
6
|
+
//
|
|
7
|
+
// All scripts are invoked via `sh` (not requiring the executable bit — common
|
|
8
|
+
// Windows-clone perms issue). A non-zero exit aborts the run with an
|
|
9
|
+
// actionable error note (see git.ts first-run branch).
|
|
10
|
+
// Shell snippet appended to the first-run clone command. Runs `.devbot/init.sh`
|
|
11
|
+
// if it exists (after clone, before opencode). The env vars configured per-
|
|
12
|
+
// project are already exported into the container via `--env NAME=VALUE` on
|
|
13
|
+
// the podman args, so init.sh sees them naturally. Exit 8 + DEVBOT_INIT_FAILED
|
|
14
|
+
// marker in stdout lets git.ts map to an actionable error reason.
|
|
15
|
+
export function buildPostCloneScript() {
|
|
16
|
+
return "if [ -f .devbot/init.sh ]; then sh .devbot/init.sh || { echo \"DEVBOT_INIT_FAILED:$?\"; exit 8; }; fi";
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=devbot-scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devbot-scripts.js","sourceRoot":"","sources":["../src/devbot-scripts.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,2BAA2B;AAC3B,EAAE;AACF,8EAA8E;AAC9E,qEAAqE;AACrE,uDAAuD;AAEvD,gFAAgF;AAChF,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,kEAAkE;AAClE,MAAM,UAAU,oBAAoB;IAClC,OAAO,uGAAuG,CAAC;AACjH,CAAC"}
|