Upgrade to v0.39.0: reapply fork patches on clean upstream base
Some checks failed
PR Checks / Lint (push) Has been cancelled
PR Checks / Typecheck (push) Has been cancelled
PR Checks / Build (push) Has been cancelled
PR Checks / Test shard 1/4 (push) Has been cancelled
PR Checks / Test shard 2/4 (push) Has been cancelled
PR Checks / Test shard 3/4 (push) Has been cancelled
PR Checks / Test shard 4/4 (push) Has been cancelled

Strategy: reset main to upstream/main (v0.39.0) and reapply only the
fork-specific patches that survive — most older patches (plugin-loader
routing, heartbeat resolver, autoload, cross-spawn, scope-resolution
fixes) were absorbed by upstream between v0.28.1 → v0.39.0.

Surviving patches:
- Dockerfile: keep single-stage Coolify-compatible build (upstream went
  multi-stage which breaks our bind-mount strategy for
  /home/s/.codex, /home/s/.claude, /usr/lib/node_modules)
- packages/engine/src/sandbox-tester-gate.ts: shared Phase-3 pre-merge
  gate module (Opus 4.7 + OpenSandbox + Playwright smoke)
- packages/engine/src/executor.ts: call runSandboxTesterGate from
  finalizeAlreadyReviewedTask recovery path
- packages/engine/src/project-engine.ts: call applySandboxTesterGate
  from primary direct-merge dispatch (the path that actually fires in
  prod — recovery path only catches stuck tasks)
- packages/dashboard/src/auth-middleware.ts: registerPluginExemptPath
  for webhook endpoints (Grafana, Gitea) that must bypass daemon-token
- packages/dashboard/src/plugin-routes.ts: auto-register plugin-defined
  routes as daemon-exempt so external plugins (telemetry-watcher,
  gitea-issues) work out of the box

Tested locally: pnpm install + pnpm -r build green, no TS errors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
semih
2026-06-03 18:41:04 +03:00
parent 04a5cd196c
commit 511f930d20
6 changed files with 251 additions and 66 deletions

View File

@@ -19,6 +19,24 @@ export const TOKEN_QUERY_PARAM = "fn_token";
/** Paths that are exempt from authentication (liveness probes). */
const EXEMPT_PATHS = ["/api/health"];
/**
* Plugin-registered paths that should bypass daemon-token auth.
* Used for webhook endpoints (Grafana, Gitea, GitHub, etc.) that need to be
* reachable from external services without holding the daemon token.
*/
const PLUGIN_EXEMPT_PATHS = new Set<string>();
/**
* Register a webhook path defined by a plugin so daemon auth lets it through.
* Paths must start with `/api/plugins/`. Idempotent.
*/
export function registerPluginExemptPath(path: string): void {
if (!path.startsWith("/api/plugins/")) {
throw new Error(`registerPluginExemptPath: path must start with /api/plugins/ (got: ${path})`);
}
PLUGIN_EXEMPT_PATHS.add(path);
}
/**
* Only /api/* paths are gated by this middleware. The SPA shell (index.html,
* /assets/*, favicon, etc.) must load unauthenticated so the frontend JS can
@@ -66,7 +84,13 @@ export function getDaemonToken(options?: { daemon?: { token: string }; noAuth?:
* Check if a request path is exempt from authentication.
*/
function isExemptPath(path: string): boolean {
return EXEMPT_PATHS.some((exempt) => path === exempt || path.startsWith(exempt + "/"));
if (EXEMPT_PATHS.some((exempt) => path === exempt || path.startsWith(exempt + "/"))) {
return true;
}
for (const exempt of PLUGIN_EXEMPT_PATHS) {
if (path === exempt || path.startsWith(exempt + "/")) return true;
}
return false;
}
/**

View File

@@ -32,6 +32,7 @@ import {
notFound,
} from "./api-error.js";
import { getOrCreateProjectStore } from "./project-store-resolver.js";
import { registerPluginExemptPath } from "./auth-middleware.js";
// PluginRunner interface for optional plugin runner
@@ -627,6 +628,15 @@ export function createPluginRouter(
router.delete(fullPath, handler);
break;
}
// Webhook endpoints (Grafana, Gitea, GitHub) must be reachable without the
// daemon token. Plugin-defined routes handle their own auth via webhook
// secrets in plugin settings; bypass daemon auth here.
try {
registerPluginExemptPath(`/api/plugins${fullPath}`);
} catch {
// ignore — non-/api/plugins paths are rejected; safe to skip.
}
}
}