@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,150 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { logReq, requireSession } from "../http-util.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import { APP_VERSION } from "../version.js";
|
|
5
|
+
import { SESSION_COOKIE } from "../cookie.js";
|
|
6
|
+
import { getPlatform } from "../platform/index.js";
|
|
7
|
+
import { listTokens, countTokens, getToken, getProject, upsertToken, deleteToken } from "../token-store.js";
|
|
8
|
+
import { refreshManagedProjectPaths, MANAGED_REFRESH_DEADLINE_MS } from "../project-sync.js";
|
|
9
|
+
import { deleteAllMcpServers, deleteAllMcpAuth, deleteAllEnvVars, deleteAllProjectSettings, deleteAllAccessGrantsForProject, deleteAllAccessRulesForProject, deleteAllOutboundWebhooksForProject, deleteAllCustomRepos } from "./mcp-cleanup.js";
|
|
10
|
+
import { parseLimit, parseOffset, parseQuery } from "./query-params.js";
|
|
11
|
+
const OnboardBodySchema = z.object({
|
|
12
|
+
projects: z
|
|
13
|
+
.array(z.object({
|
|
14
|
+
id: z.number().int().positive(),
|
|
15
|
+
// Neutral project path from the Platform port's CandidateProject
|
|
16
|
+
// (`path`): GitLab path_with_namespace / Gitea full_name / GitHub
|
|
17
|
+
// owner/name. Matches `onboarding.*`'s `{ id, path }` signature.
|
|
18
|
+
path: z.string().min(1).max(255),
|
|
19
|
+
}))
|
|
20
|
+
.min(1)
|
|
21
|
+
.max(100),
|
|
22
|
+
});
|
|
23
|
+
export function mountAdminRoutes(app) {
|
|
24
|
+
app.get("/api/session", requireSession((req, res) => {
|
|
25
|
+
logReq(req, "debug", "session ok", { userId: req.session.userId });
|
|
26
|
+
return res.json({
|
|
27
|
+
user: { id: req.session.userId, username: req.session.username },
|
|
28
|
+
version: APP_VERSION,
|
|
29
|
+
});
|
|
30
|
+
}));
|
|
31
|
+
app.post("/api/logout", (req, res) => {
|
|
32
|
+
// ponytail: no session check — logout must work even if the cookie expired,
|
|
33
|
+
// so a stale session still clears itself.
|
|
34
|
+
res.clearCookie(SESSION_COOKIE, { path: "/" });
|
|
35
|
+
logReq(req, "info", "logout");
|
|
36
|
+
return res.status(204).end();
|
|
37
|
+
});
|
|
38
|
+
app.get("/api/projects", requireSession(async (req, res) => {
|
|
39
|
+
const q = req.query.q ?? "";
|
|
40
|
+
try {
|
|
41
|
+
const projects = await getPlatform().onboarding.listProjects(req.session.accessToken, q);
|
|
42
|
+
logReq(req, "debug", "list projects", { count: projects.length, q });
|
|
43
|
+
return res.json({ projects });
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
logReq(req, "error", "list projects failed", { err: e, q });
|
|
47
|
+
return res.status(500).json({ error: "failed to list projects" });
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
app.get("/api/managed", requireSession(async (req, res) => {
|
|
51
|
+
// Reconcile forge-side renames before rendering the list. The short deadline
|
|
52
|
+
// keeps a slow/unreachable forge from stalling the page; refresh never
|
|
53
|
+
// throws, so the list is always served (possibly with stale paths).
|
|
54
|
+
const limit = parseLimit(req.query.limit, 15, 100);
|
|
55
|
+
const offset = parseOffset(req.query.offset);
|
|
56
|
+
const q = parseQuery(req.query.q);
|
|
57
|
+
await refreshManagedProjectPaths({ deadlineMs: MANAGED_REFRESH_DEADLINE_MS });
|
|
58
|
+
const projects = listTokens(limit, offset, q);
|
|
59
|
+
const total = countTokens(q);
|
|
60
|
+
logReq(req, "debug", "list managed", { count: projects.length, total, limit, offset, q });
|
|
61
|
+
return res.json({ projects, total });
|
|
62
|
+
}));
|
|
63
|
+
app.post("/api/onboard", requireSession(async (req, res) => {
|
|
64
|
+
const parsed = OnboardBodySchema.safeParse(req.body);
|
|
65
|
+
if (!parsed.success) {
|
|
66
|
+
logReq(req, "warn", "onboard bad input", { issues: parsed.error.issues });
|
|
67
|
+
return res.status(400).json({ error: "bad request", issues: parsed.error.issues });
|
|
68
|
+
}
|
|
69
|
+
const results = [];
|
|
70
|
+
const platform = getPlatform();
|
|
71
|
+
for (const p of parsed.data.projects) {
|
|
72
|
+
try {
|
|
73
|
+
await platform.onboarding.addBot(req.session.accessToken, { id: p.id, path: p.path });
|
|
74
|
+
// Reuse the stored token so re-onboarding keeps the secret that the
|
|
75
|
+
// hook was created with. Forges that ignore secret updates
|
|
76
|
+
// (ForgeJo/Gitea) cannot be repaired later, so the persisted value and
|
|
77
|
+
// the forge's value must stay in lockstep.
|
|
78
|
+
const signingToken = getToken(p.id)?.signingToken ?? platform.ingress.generateWebhookSecret();
|
|
79
|
+
const { id: webhookId, created } = await platform.onboarding.findOrCreateWebhook(req.session.accessToken, { id: p.id, path: p.path }, `${config.PUBLIC_URL}/webhook`, signingToken);
|
|
80
|
+
// Created hooks already carry the secret; an existing hook needs an
|
|
81
|
+
// update. Drivers whose forge cannot change a secret after creation
|
|
82
|
+
// no-op with a warning (see docs/platforms.md).
|
|
83
|
+
if (!created) {
|
|
84
|
+
await platform.onboarding.setWebhookSecret(req.session.accessToken, { id: p.id, path: p.path }, webhookId, signingToken);
|
|
85
|
+
}
|
|
86
|
+
upsertToken({ projectId: p.id, projectPath: p.path, signingToken, webhookId });
|
|
87
|
+
try {
|
|
88
|
+
await platform.onboarding.testWebhook(req.session.accessToken, { id: p.id, path: p.path }, webhookId);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
logReq(req, "debug", "test webhook trigger failed", { projectId: p.id, err: e });
|
|
92
|
+
}
|
|
93
|
+
results.push({ ok: true, projectPath: p.path, created });
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
// ponytail: generic error to client — forge error bodies can leak internal
|
|
97
|
+
// hostnames/paths; keep the full message server-side only.
|
|
98
|
+
logReq(req, "warn", "onboard project failed", { projectId: p.id, err: e });
|
|
99
|
+
results.push({ ok: false, projectPath: p.path, error: "forge request failed" });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const okCount = results.filter((r) => r.ok).length;
|
|
103
|
+
logReq(req, "info", "onboard", { count: results.length, okCount });
|
|
104
|
+
return res.json({ results });
|
|
105
|
+
}));
|
|
106
|
+
app.post("/api/revoke/:id", requireSession(async (req, res) => {
|
|
107
|
+
const projectId = Number(req.params.id ?? "");
|
|
108
|
+
if (!Number.isFinite(projectId) || !Number.isInteger(projectId) || projectId <= 0) {
|
|
109
|
+
logReq(req, "warn", "revoke bad id", { id: req.params.id });
|
|
110
|
+
return res.status(400).json({ error: "invalid project id" });
|
|
111
|
+
}
|
|
112
|
+
const project = getProject(projectId);
|
|
113
|
+
if (!project) {
|
|
114
|
+
logReq(req, "warn", "revoke not managed", { projectId });
|
|
115
|
+
return res.status(404).json({ error: "not managed" });
|
|
116
|
+
}
|
|
117
|
+
let ok = true;
|
|
118
|
+
let error;
|
|
119
|
+
try {
|
|
120
|
+
await getPlatform().onboarding.deleteWebhook(req.session.accessToken, { id: projectId, path: project.projectPath }, project.webhookId);
|
|
121
|
+
await getPlatform().onboarding.removeBot(req.session.accessToken, { id: projectId, path: project.projectPath });
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
ok = false;
|
|
125
|
+
// ponytail: generic error to client — forge error bodies can leak internal
|
|
126
|
+
// hostnames/paths; keep the full message server-side only.
|
|
127
|
+
logReq(req, "warn", "revoke forge calls failed", { projectId, err: e });
|
|
128
|
+
error = "forge request failed";
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
// ponytail: delete token in finally so a half-revoked state still clears the
|
|
132
|
+
// local row; re-running revoke completes cleanup via the 404-as-success
|
|
133
|
+
// paths in deleteWebhook/removeBotFromProject.
|
|
134
|
+
deleteToken(projectId);
|
|
135
|
+
deleteAllMcpServers(projectId);
|
|
136
|
+
deleteAllMcpAuth(projectId);
|
|
137
|
+
deleteAllEnvVars(projectId);
|
|
138
|
+
deleteAllProjectSettings(projectId);
|
|
139
|
+
deleteAllAccessGrantsForProject(projectId);
|
|
140
|
+
deleteAllAccessRulesForProject(projectId);
|
|
141
|
+
deleteAllOutboundWebhooksForProject(projectId);
|
|
142
|
+
deleteAllCustomRepos(projectId);
|
|
143
|
+
}
|
|
144
|
+
logReq(req, "info", "revoke", { projectId, ok });
|
|
145
|
+
return res.json(ok
|
|
146
|
+
? { ok: true, projectPath: project.projectPath, note: "revoked" }
|
|
147
|
+
: { ok: false, projectPath: project.projectPath, error: error ?? "unknown error" });
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/admin/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { logReq, requireSession } from "../http-util.js";
|
|
2
|
+
import { getRunHistory } from "../run-history-store.js";
|
|
3
|
+
import { readArchivedLog } from "../run-log.js";
|
|
4
|
+
import { getProject } from "../token-store.js";
|
|
5
|
+
import { getPlatform } from "../platform/index.js";
|
|
6
|
+
// Run-history detail endpoint for the WebUI Runs page (work item #61). The list
|
|
7
|
+
// now comes from the unified GET /api/runs; this route serves one settled run's
|
|
8
|
+
// archived container log (a file on disk, referenced by run_history.log_file)
|
|
9
|
+
// rendered by the shared formatter. Session-gated like the other /api routes.
|
|
10
|
+
function projectWorkItemUrl(projectId, workItemNumber) {
|
|
11
|
+
return getPlatform().links.workItemUrl(getProject(projectId)?.projectPath ?? "", workItemNumber);
|
|
12
|
+
}
|
|
13
|
+
function parseIdParam(raw) {
|
|
14
|
+
const n = Number(Array.isArray(raw) ? raw[0] : raw);
|
|
15
|
+
return Number.isFinite(n) && Number.isInteger(n) && n > 0 ? n : null;
|
|
16
|
+
}
|
|
17
|
+
export function mountRunHistoryRoutes(app) {
|
|
18
|
+
app.get("/api/run-history/:id", requireSession((req, res) => {
|
|
19
|
+
const id = parseIdParam(req.params.id ?? "");
|
|
20
|
+
if (!id) {
|
|
21
|
+
logReq(req, "warn", "run history bad id", { id: req.params.id });
|
|
22
|
+
return res.status(400).json({ error: "invalid id" });
|
|
23
|
+
}
|
|
24
|
+
const row = getRunHistory(id);
|
|
25
|
+
if (!row) {
|
|
26
|
+
logReq(req, "debug", "run history not found", { id });
|
|
27
|
+
return res.status(404).json({ error: "not found" });
|
|
28
|
+
}
|
|
29
|
+
const archived = readArchivedLog(row.logFile);
|
|
30
|
+
logReq(req, "debug", "run history detail", { id, available: archived.available, reason: archived.reason });
|
|
31
|
+
return res.json({
|
|
32
|
+
run: {
|
|
33
|
+
...row,
|
|
34
|
+
workItemUrl: projectWorkItemUrl(row.projectId, row.workItemIid),
|
|
35
|
+
projectPath: getProject(row.projectId)?.projectPath ?? "",
|
|
36
|
+
logs: archived.text,
|
|
37
|
+
truncated: archived.truncated,
|
|
38
|
+
logAvailable: archived.available,
|
|
39
|
+
logReason: archived.reason,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=run-history-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-history-routes.js","sourceRoot":"","sources":["../../src/admin/run-history-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,gFAAgF;AAChF,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAE9E,SAAS,kBAAkB,CAAC,SAAiB,EAAE,cAAsB;IACnE,OAAO,WAAW,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,YAAY,CAAC,GAAY;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACpD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAoB;IACxD,GAAG,CAAC,GAAG,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC1D,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACjE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YACtD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3G,OAAO,GAAG,CAAC,IAAI,CAAC;YACd,GAAG,EAAE;gBACH,GAAG,GAAG;gBACN,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,WAAW,CAAC;gBAC/D,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,WAAW,IAAI,EAAE;gBACzD,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,YAAY,EAAE,QAAQ,CAAC,SAAS;gBAChC,SAAS,EAAE,QAAQ,CAAC,MAAM;aAC3B;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { logReq, requireSession } from "../http-util.js";
|
|
2
|
+
import { listInFlightDesc } from "../queue-store.js";
|
|
3
|
+
import { getProject } from "../token-store.js";
|
|
4
|
+
import { buildTaskViews, buildTaskQuota } from "../task-view.js";
|
|
5
|
+
import { globalQueueDepth, globalQueueMax, globalQueuePosition } from "../global-queue.js";
|
|
6
|
+
import { listRunHistoryBySeq, countRunHistory, listRunHistoryFilterOptions, } from "../run-history-store.js";
|
|
7
|
+
import { taskViewToRunView, historyToRunView, mergeRunViews, MAX_MERGE_WINDOW, } from "../run-view.js";
|
|
8
|
+
import { resolveModelForMode, resolveVariantForMode } from "../settings-store.js";
|
|
9
|
+
import { getPlatform } from "../platform/index.js";
|
|
10
|
+
import { resolveUsageWindow } from "../usage-stats.js";
|
|
11
|
+
import { matchesActiveRun, historyFilter, mergeFilterOptions, } from "../run-filters.js";
|
|
12
|
+
import { parseDateWindow, parseLimit, parseOffset, parsePositiveInt, parseQuery, parseString } from "./query-params.js";
|
|
13
|
+
// Unified runs listing for the WebUI Runs page (work item #61), with filters and
|
|
14
|
+
// work-item-title search (work item #88). One endpoint serves the in-flight
|
|
15
|
+
// queue, the settled run history, or both, merged on the shared task seq
|
|
16
|
+
// (seq DESC). `GET /api/tasks` and `GET /api/run-history` remain for the
|
|
17
|
+
// Overview recent-list and the existing clients.
|
|
18
|
+
function projectWorkItemUrl(projectId, workItemNumber) {
|
|
19
|
+
return getPlatform().links.workItemUrl(getProject(projectId)?.projectPath ?? "", workItemNumber);
|
|
20
|
+
}
|
|
21
|
+
function parseStatus(raw) {
|
|
22
|
+
const v = Array.isArray(raw) ? raw[0] : raw;
|
|
23
|
+
return v === "active" || v === "settled" || v === "errored" || v === "cancelled" ? v : "all";
|
|
24
|
+
}
|
|
25
|
+
// Build the TaskView projection once for either list function. Kept as a local
|
|
26
|
+
// so both the active and merged paths share the resolver wiring.
|
|
27
|
+
function viewsFor(rows) {
|
|
28
|
+
return buildTaskViews(rows, (projectId) => getProject(projectId)?.projectPath ?? "", (projectId, mode) => {
|
|
29
|
+
if (mode !== "plan" && mode !== "build")
|
|
30
|
+
return { model: "", variant: "" };
|
|
31
|
+
return {
|
|
32
|
+
model: resolveModelForMode(projectId, mode),
|
|
33
|
+
variant: resolveVariantForMode(projectId, mode),
|
|
34
|
+
};
|
|
35
|
+
}, projectWorkItemUrl, globalQueuePosition);
|
|
36
|
+
}
|
|
37
|
+
// A settled history row decorated with its project path + work item link.
|
|
38
|
+
function settledView(r) {
|
|
39
|
+
const v = historyToRunView(r);
|
|
40
|
+
v.projectPath = getProject(r.projectId)?.projectPath ?? "";
|
|
41
|
+
v.workItemUrl = projectWorkItemUrl(r.projectId, r.workItemIid);
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
export function mountRunRoutes(app) {
|
|
45
|
+
app.get("/api/runs", requireSession((req, res) => {
|
|
46
|
+
const status = parseStatus(req.query.status);
|
|
47
|
+
const limit = parseLimit(req.query.limit);
|
|
48
|
+
const offset = parseOffset(req.query.offset);
|
|
49
|
+
const quota = buildTaskQuota(globalQueueDepth(), globalQueueMax());
|
|
50
|
+
const projectId = parsePositiveInt(req.query.projectId);
|
|
51
|
+
const model = parseString(req.query.model);
|
|
52
|
+
const mode = parseString(req.query.mode);
|
|
53
|
+
const title = parseQuery(req.query.q);
|
|
54
|
+
const { days, from, to } = parseDateWindow(req.query);
|
|
55
|
+
const window = resolveUsageWindow({ days, ...(from !== undefined ? { from } : {}), ...(to !== undefined ? { to } : {}) }, Date.now());
|
|
56
|
+
const filters = {
|
|
57
|
+
...(projectId !== undefined ? { projectId } : {}),
|
|
58
|
+
...(model !== undefined ? { model } : {}),
|
|
59
|
+
...(mode !== undefined ? { mode } : {}),
|
|
60
|
+
...(title !== undefined ? { title } : {}),
|
|
61
|
+
window: { start: window.windowStart, end: window.windowEnd },
|
|
62
|
+
};
|
|
63
|
+
// In-flight rows are few (bounded by the queue); build them once for both
|
|
64
|
+
// the active listing and the filter-option union.
|
|
65
|
+
const activeViews = viewsFor(listInFlightDesc()).map(taskViewToRunView);
|
|
66
|
+
const filterOptions = mergeFilterOptions(listRunHistoryFilterOptions(), activeViews);
|
|
67
|
+
const projectPaths = {};
|
|
68
|
+
for (const p of filterOptions.projects)
|
|
69
|
+
projectPaths[String(p)] = getProject(p)?.projectPath ?? `#${p}`;
|
|
70
|
+
let runs;
|
|
71
|
+
let total;
|
|
72
|
+
if (status === "active") {
|
|
73
|
+
// Filters need the full set before slicing; build the views, filter, page.
|
|
74
|
+
const filtered = activeViews.filter((v) => matchesActiveRun(v, filters));
|
|
75
|
+
total = filtered.length;
|
|
76
|
+
runs = filtered.slice(offset, offset + limit);
|
|
77
|
+
}
|
|
78
|
+
else if (status === "settled" || status === "errored" || status === "cancelled") {
|
|
79
|
+
const hf = historyFilter(filters, status);
|
|
80
|
+
runs = listRunHistoryBySeq(limit, offset, hf).map(settledView);
|
|
81
|
+
total = countRunHistory(hf);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Merged: filter each source first (settled in SQL, active in memory),
|
|
85
|
+
// then merge. A seq can appear in both during the settle race, so the
|
|
86
|
+
// reachable total is the merged length, not the naive sum. Fetched within
|
|
87
|
+
// the capped merge window, then sliced.
|
|
88
|
+
const cap = MAX_MERGE_WINDOW;
|
|
89
|
+
const settled = listRunHistoryBySeq(cap, 0, historyFilter(filters, "all")).map(settledView);
|
|
90
|
+
const active = activeViews.filter((v) => matchesActiveRun(v, filters)).slice(0, cap);
|
|
91
|
+
const mergedAll = mergeRunViews(active, settled, cap, 0);
|
|
92
|
+
total = mergedAll.length;
|
|
93
|
+
runs = mergedAll.slice(offset, offset + limit);
|
|
94
|
+
}
|
|
95
|
+
logReq(req, "debug", "list runs", { status, count: runs.length, total, limit, offset });
|
|
96
|
+
return res.json({ runs, total, quota, filterOptions, projectPaths });
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=run-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-routes.js","sourceRoot":"","sources":["../../src/admin/run-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { logReq, requireSession } from "../http-util.js";
|
|
2
|
+
import { getProject } from "../token-store.js";
|
|
3
|
+
import { getProjectSettings, upsertProjectSettings } from "../settings-store.js";
|
|
4
|
+
import { ProjectSettingsSchema } from "../types/project-settings.js";
|
|
5
|
+
import { getGlobalSettings } from "../global-settings-store.js";
|
|
6
|
+
import { DEFAULT_BUILD_WORKFLOW_TEMPLATE, DEFAULT_MR_DESCRIPTION_TEMPLATE } from "../build-workflow.js";
|
|
7
|
+
import { config } from "../config.js";
|
|
8
|
+
import { getPlatform } from "../platform/index.js";
|
|
9
|
+
import { log } from "../logger.js";
|
|
10
|
+
import { validateAssigneeUsername } from "../mr-assignee.js";
|
|
11
|
+
// Merge the forge's CI-failure webhook event into the project's hook when the
|
|
12
|
+
// toggle is switched on. Best-effort: a forge failure must not block the
|
|
13
|
+
// settings save, but the caller surfaces the warning so the admin can re-onboard.
|
|
14
|
+
async function enableCiFailureEvents(req, projectId) {
|
|
15
|
+
const project = getProject(projectId);
|
|
16
|
+
const accessToken = req.session.accessToken;
|
|
17
|
+
if (!project || !accessToken)
|
|
18
|
+
return "not managed or no session";
|
|
19
|
+
try {
|
|
20
|
+
await getPlatform().onboarding.ensureCiFailureEvents(accessToken, { id: projectId, path: project.projectPath }, project.webhookId);
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
log.warn("ensureCiFailureEvents failed", { projectId, err: e });
|
|
25
|
+
return "could not update the forge webhook — re-onboard the project to enable CI events";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function parseProjectIdParam(raw) {
|
|
29
|
+
const id = Number(Array.isArray(raw) ? raw[0] : raw);
|
|
30
|
+
return Number.isFinite(id) && Number.isInteger(id) && id > 0 ? id : null;
|
|
31
|
+
}
|
|
32
|
+
// Global defaults shown as placeholders in the panel. builderImage falls back to
|
|
33
|
+
// the BUILDER_IMAGE env var when the global setting is unset, so the placeholder
|
|
34
|
+
// always shows the image a run would actually use. buildWorkflow/mrDescription
|
|
35
|
+
// fall back to the built-in templates so "Load default" always yields usable text.
|
|
36
|
+
function globalDefaults() {
|
|
37
|
+
const g = getGlobalSettings();
|
|
38
|
+
return {
|
|
39
|
+
planModel: g.planModel,
|
|
40
|
+
buildModel: g.buildModel,
|
|
41
|
+
planVariant: g.planVariant,
|
|
42
|
+
buildVariant: g.buildVariant,
|
|
43
|
+
plugins: g.plugins,
|
|
44
|
+
baseBranches: g.baseBranches,
|
|
45
|
+
builderImage: g.builderImage || config.BUILDER_IMAGE,
|
|
46
|
+
assignee: g.assignee,
|
|
47
|
+
buildWorkflow: g.buildWorkflow || DEFAULT_BUILD_WORKFLOW_TEMPLATE,
|
|
48
|
+
mrDescription: g.mrDescription || DEFAULT_MR_DESCRIPTION_TEMPLATE,
|
|
49
|
+
minRole: g.minRole,
|
|
50
|
+
requireAuthorOrAssignee: g.requireAuthorOrAssignee ? "true" : "false",
|
|
51
|
+
autoPush: g.autoPush ? "true" : "false",
|
|
52
|
+
autoMr: g.autoMr ? "true" : "false",
|
|
53
|
+
autoResolveConflicts: g.autoResolveConflicts ? "true" : "false",
|
|
54
|
+
ciFailAutoReport: g.ciFailAutoReport ? "true" : "false",
|
|
55
|
+
lspServers: g.lspServers.join(","),
|
|
56
|
+
lspDisabled: "",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function mountSettingsRoutes(app) {
|
|
60
|
+
// GET /api/projects/:id/settings — effective values + global defaults.
|
|
61
|
+
app.get("/api/projects/:id/settings", requireSession((req, res) => {
|
|
62
|
+
const projectId = parseProjectIdParam(req.params.id ?? "");
|
|
63
|
+
if (!projectId) {
|
|
64
|
+
logReq(req, "warn", "settings route bad id", { id: req.params.id });
|
|
65
|
+
return res.status(400).json({ error: "invalid project id" });
|
|
66
|
+
}
|
|
67
|
+
if (!getProject(projectId)) {
|
|
68
|
+
logReq(req, "warn", "settings route not managed", { projectId });
|
|
69
|
+
return res.status(404).json({ error: "not managed" });
|
|
70
|
+
}
|
|
71
|
+
const settings = getProjectSettings(projectId) ?? {
|
|
72
|
+
planModel: "", buildModel: "", planVariant: "", buildVariant: "", plugins: "", baseBranches: "", lspServers: "", lspDisabled: "", builderImage: "", buildWorkflow: "", mrDescription: "", minRole: "", requireAuthorOrAssignee: "", autoPush: "", autoMr: "", autoResolveConflicts: "", ciFailAutoReport: "", assignee: "",
|
|
73
|
+
};
|
|
74
|
+
logReq(req, "debug", "get settings", { projectId });
|
|
75
|
+
return res.json({ settings, defaults: globalDefaults() });
|
|
76
|
+
}));
|
|
77
|
+
// PUT /api/projects/:id/settings — whole-object upsert. "" = inherit.
|
|
78
|
+
app.put("/api/projects/:id/settings", requireSession(async (req, res) => {
|
|
79
|
+
const projectId = parseProjectIdParam(req.params.id ?? "");
|
|
80
|
+
if (!projectId) {
|
|
81
|
+
logReq(req, "warn", "settings route bad id", { id: req.params.id });
|
|
82
|
+
return res.status(400).json({ error: "invalid project id" });
|
|
83
|
+
}
|
|
84
|
+
if (!getProject(projectId)) {
|
|
85
|
+
logReq(req, "warn", "settings route not managed", { projectId });
|
|
86
|
+
return res.status(404).json({ error: "not managed" });
|
|
87
|
+
}
|
|
88
|
+
const parsed = ProjectSettingsSchema.safeParse(req.body);
|
|
89
|
+
if (!parsed.success) {
|
|
90
|
+
logReq(req, "warn", "settings put bad body", { projectId, issues: parsed.error.issues });
|
|
91
|
+
return res.status(400).json({ error: "bad request", issues: parsed.error.issues });
|
|
92
|
+
}
|
|
93
|
+
if (parsed.data.assignee && parsed.data.assignee !== "none") {
|
|
94
|
+
const check = await validateAssigneeUsername(parsed.data.assignee);
|
|
95
|
+
if (check !== "ok") {
|
|
96
|
+
logReq(req, "warn", "settings put bad assignee", { projectId, assignee: parsed.data.assignee, check });
|
|
97
|
+
return res.status(check === "unknown" ? 400 : 502).json(check === "unknown"
|
|
98
|
+
? { error: "bad request", issues: [{ path: ["assignee"], message: `unknown forge user @${parsed.data.assignee}` }] }
|
|
99
|
+
: { error: "forge lookup failed" });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
upsertProjectSettings(projectId, parsed.data);
|
|
103
|
+
// When the CI-failure toggle is explicitly turned on, make sure the forge
|
|
104
|
+
// webhook is subscribed to CI events. The bot token cannot manage hooks, so
|
|
105
|
+
// this uses the signed-in user's OAuth token. Best-effort: a warning is
|
|
106
|
+
// returned rather than failing the save.
|
|
107
|
+
let ciEventsWarning;
|
|
108
|
+
if (parsed.data.ciFailAutoReport === "true") {
|
|
109
|
+
ciEventsWarning = await enableCiFailureEvents(req, projectId);
|
|
110
|
+
}
|
|
111
|
+
logReq(req, "info", "upsert settings", { projectId, ciEventsWarning });
|
|
112
|
+
return res.json({
|
|
113
|
+
ok: true,
|
|
114
|
+
settings: parsed.data,
|
|
115
|
+
...(ciEventsWarning ? { warnings: { ciFailAutoReport: ciEventsWarning } } : {}),
|
|
116
|
+
});
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=settings-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-routes.js","sourceRoot":"","sources":["../../src/admin/settings-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { logReq, requireSession } from "../http-util.js";
|
|
2
|
+
import { listUsageRows } from "../run-history-store.js";
|
|
3
|
+
import { computeUsageStats, resolveUsageWindow } from "../usage-stats.js";
|
|
4
|
+
import { getProject } from "../token-store.js";
|
|
5
|
+
import { parseDateWindow, parseLimit, parseOffset, parsePositiveInt, parseString } from "./query-params.js";
|
|
6
|
+
// Token-usage dashboard endpoint (work item #66). Reads settled-run usage from
|
|
7
|
+
// run_history, aggregates in a pure module, and returns totals, a daily series,
|
|
8
|
+
// breakdowns, and the filter option lists the page's dropdowns need.
|
|
9
|
+
export function mountStatsRoutes(app) {
|
|
10
|
+
app.get("/api/stats/usage", requireSession((req, res) => {
|
|
11
|
+
const projectId = parsePositiveInt(req.query.projectId);
|
|
12
|
+
const model = parseString(req.query.model);
|
|
13
|
+
const mode = parseString(req.query.mode);
|
|
14
|
+
// A custom range (#77) needs both ends; otherwise fall back to `days`.
|
|
15
|
+
const { days, from, to } = parseDateWindow(req.query);
|
|
16
|
+
const filters = {
|
|
17
|
+
days,
|
|
18
|
+
...(from !== undefined ? { from } : {}),
|
|
19
|
+
...(to !== undefined ? { to } : {}),
|
|
20
|
+
...(projectId !== undefined ? { projectId } : {}),
|
|
21
|
+
...(model !== undefined ? { model } : {}),
|
|
22
|
+
...(mode !== undefined ? { mode } : {}),
|
|
23
|
+
};
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
// Fetch the previous window too so period-over-period deltas can be computed
|
|
26
|
+
// in the same pass.
|
|
27
|
+
const prevStart = resolveUsageWindow(filters, now).prevStart;
|
|
28
|
+
const rows = listUsageRows(prevStart);
|
|
29
|
+
const stats = computeUsageStats(rows, filters, now);
|
|
30
|
+
// Paginate the three list sections server-side. The aggregate already
|
|
31
|
+
// carried full totals (recentTotal/byModelTotal/byProjectTotal) before we
|
|
32
|
+
// slice, so the UI can size its pagers. `byMode` stays un-paginated.
|
|
33
|
+
const recentLimit = parseLimit(req.query.recentLimit);
|
|
34
|
+
const recentOffset = parseOffset(req.query.recentOffset);
|
|
35
|
+
const byModelLimit = parseLimit(req.query.byModelLimit);
|
|
36
|
+
const byModelOffset = parseOffset(req.query.byModelOffset);
|
|
37
|
+
const byProjectLimit = parseLimit(req.query.byProjectLimit);
|
|
38
|
+
const byProjectOffset = parseOffset(req.query.byProjectOffset);
|
|
39
|
+
stats.recent = stats.recent.slice(recentOffset, recentOffset + recentLimit);
|
|
40
|
+
stats.byModel = stats.byModel.slice(byModelOffset, byModelOffset + byModelLimit);
|
|
41
|
+
stats.byProject = stats.byProject.slice(byProjectOffset, byProjectOffset + byProjectLimit);
|
|
42
|
+
// A display-name map for the breakdown/recent tables; the aggregator stays
|
|
43
|
+
// free of token-store imports so it remains unit-testable.
|
|
44
|
+
const projectPaths = {};
|
|
45
|
+
for (const p of stats.filterOptions.projects)
|
|
46
|
+
projectPaths[String(p)] = getProject(p)?.projectPath ?? `#${p}`;
|
|
47
|
+
logReq(req, "debug", "usage stats", { days, projectId: filters.projectId, runs: stats.totals.runs });
|
|
48
|
+
return res.json({ stats, projectPaths });
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=stats-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-routes.js","sourceRoot":"","sources":["../../src/admin/stats-routes.ts"],"names":[],"mappings":"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"}
|