fix: --no-auth override, workflow revision in-place fix, state-driven heartbeats
Three orthogonal fixes bundled together so they re-land as a unit after earlier worktree-based reverts kept wiping them individually. 1. `--no-auth` flag now actually disables auth. Previously a stale FUSION_DAEMON_TOKEN in .env silently re-armed bearer-token auth despite the CLI flag. Added a `noAuth` option to ServerOptions; auth-middleware's isDaemonAuthActive/getDaemonToken short-circuit to false/undefined when set; CLI plumbs opts.noAuth through both createServer call sites. 2. Workflow review failures no longer reset every completed step. Previously a single CSS nit from a workflow reviewer could drag 5+ already-approved steps back through plan review, code review, and re-execution because determineRevisionResetStart fuzzy-matched feedback tokens against step names. handleWorkflowRevisionRequest, handleWorkflowStepFailure, and sendTaskBackForFix now call a new reopenLastStepForRevision helper that flips only the last non-pending step back to pending (with currentStep rewind via a newly-accepted updateTask field) — all earlier done steps stay done, and the agent applies the feedback as an in-place patch per the updated PROMPT.md instructions. determineRevisionResetStart stays exported as @deprecated so existing unit tests still link. 3. Heartbeat scheduling is now state-driven. Previously a non-ephemeral agent with a stale runtimeConfig.enabled=false on disk would never tick and the Pause/Resume button couldn't arm the timer without also flipping that hidden flag. HeartbeatTriggerScheduler's watchAgentLifecycle now registers on transitions into active/running and clears on transitions out; the tick and assignment-trigger guards key off state + ephemeral classification. InProcessRuntime's created/updated listeners and startup scan mirror the same semantics. runtimeConfig.enabled is only retained for ephemeral (task-worker) opt-out. Tests updated: agent-heartbeat.test.ts — one test renamed from "skips registration when enabled is false" (obsolete behavior) to "registers regardless of the legacy enabled flag"; 4 assignment-watching tests now pass a realistic `state: "active"` on mock agents. 207 heartbeat tests + 330 executor tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,13 +33,15 @@ function isApiPath(path: string): boolean {
|
||||
/**
|
||||
* Check if daemon auth should be active.
|
||||
* Auth is enabled when FUSION_DAEMON_TOKEN env var is set OR daemon options are provided.
|
||||
* Always returns false when options.noAuth is true (CLI --no-auth override).
|
||||
*/
|
||||
export function isDaemonAuthActive(options?: { daemon?: { token: string } }): boolean {
|
||||
// Check explicit daemon option
|
||||
export function isDaemonAuthActive(options?: { daemon?: { token: string }; noAuth?: boolean }): boolean {
|
||||
if (options?.noAuth) {
|
||||
return false;
|
||||
}
|
||||
if (options?.daemon?.token) {
|
||||
return true;
|
||||
}
|
||||
// Check environment variable
|
||||
if (process.env.FUSION_DAEMON_TOKEN) {
|
||||
return true;
|
||||
}
|
||||
@@ -48,8 +50,12 @@ export function isDaemonAuthActive(options?: { daemon?: { token: string } }): bo
|
||||
|
||||
/**
|
||||
* Get the daemon token from options or environment.
|
||||
* Returns undefined when options.noAuth is true, regardless of env.
|
||||
*/
|
||||
export function getDaemonToken(options?: { daemon?: { token: string } }): string | undefined {
|
||||
export function getDaemonToken(options?: { daemon?: { token: string }; noAuth?: boolean }): string | undefined {
|
||||
if (options?.noAuth) {
|
||||
return undefined;
|
||||
}
|
||||
if (options?.daemon?.token) {
|
||||
return options.daemon.token;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,10 @@ export interface ServerOptions {
|
||||
/** Daemon mode configuration with bearer token authentication.
|
||||
* When provided, all API requests (except /api/health) require valid bearer token. */
|
||||
daemon?: { token: string };
|
||||
/** Explicitly disable bearer-token auth, ignoring FUSION_DAEMON_TOKEN /
|
||||
* FUSION_DASHBOARD_TOKEN env vars. Used by `fn dashboard --no-auth` so a
|
||||
* stale token in a project .env doesn't silently override the flag. */
|
||||
noAuth?: boolean;
|
||||
/** Optional TLS credentials. When provided, the server is served over HTTP/2
|
||||
* with HTTP/1.1 fallback (allowHTTP1:true) — this lifts the browser's
|
||||
* per-origin connection cap so long-lived SSE streams no longer starve
|
||||
@@ -398,7 +402,9 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
|
||||
// that then captures ?token= from the URL and injects a Bearer header on every
|
||||
// /api/* call. WebSocket upgrades are gated separately in setupTerminalWebSocket /
|
||||
// setupBadgeWebSocket.
|
||||
const daemonToken = options?.daemon?.token ?? process.env.FUSION_DAEMON_TOKEN;
|
||||
const daemonToken = options?.noAuth
|
||||
? undefined
|
||||
: options?.daemon?.token ?? process.env.FUSION_DAEMON_TOKEN;
|
||||
if (daemonToken) {
|
||||
app.use(createAuthMiddleware(daemonToken));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user