fix(macos): check config file mode for gateway token/password resolution (#1022)

* fix: honor config gateway mode for credentials

* chore: oxfmt doctor platform notes

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Nima Karimi
2026-01-16 19:29:48 +00:00
committed by GitHub
parent 624ff09314
commit 25ae5f897e
9 changed files with 283 additions and 34 deletions

View File

@@ -1,9 +1,14 @@
import { execFile } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import type { ClawdbotConfig } from "../config/config.js";
import { note } from "../terminal/note.js";
const execFileAsync = promisify(execFile);
function resolveHomeDir(): string {
return process.env.HOME ?? os.homedir();
}
@@ -21,3 +26,47 @@ export async function noteMacLaunchAgentOverrides() {
].filter((line): line is string => Boolean(line));
note(lines.join("\n"), "Gateway (macOS)");
}
async function launchctlGetenv(name: string): Promise<string | undefined> {
try {
const result = await execFileAsync("/bin/launchctl", ["getenv", name], { encoding: "utf8" });
const value = String(result.stdout ?? "").trim();
return value.length > 0 ? value : undefined;
} catch {
return undefined;
}
}
function hasConfigGatewayCreds(cfg: ClawdbotConfig): boolean {
const localToken =
typeof cfg.gateway?.auth?.token === "string" ? cfg.gateway?.auth?.token.trim() : "";
const localPassword =
typeof cfg.gateway?.auth?.password === "string" ? cfg.gateway?.auth?.password.trim() : "";
const remoteToken =
typeof cfg.gateway?.remote?.token === "string" ? cfg.gateway?.remote?.token.trim() : "";
const remotePassword =
typeof cfg.gateway?.remote?.password === "string" ? cfg.gateway?.remote?.password.trim() : "";
return Boolean(localToken || localPassword || remoteToken || remotePassword);
}
export async function noteMacLaunchctlGatewayEnvOverrides(cfg: ClawdbotConfig) {
if (process.platform !== "darwin") return;
if (!hasConfigGatewayCreds(cfg)) return;
const envToken = await launchctlGetenv("CLAWDBOT_GATEWAY_TOKEN");
const envPassword = await launchctlGetenv("CLAWDBOT_GATEWAY_PASSWORD");
if (!envToken && !envPassword) return;
const lines = [
"- launchctl environment overrides detected (can cause confusing unauthorized errors).",
envToken ? "- `CLAWDBOT_GATEWAY_TOKEN` is set; it overrides config tokens." : undefined,
envPassword
? "- `CLAWDBOT_GATEWAY_PASSWORD` is set; it overrides config passwords."
: undefined,
"- Clear overrides and restart the app/gateway:",
envToken ? " launchctl unsetenv CLAWDBOT_GATEWAY_TOKEN" : undefined,
envPassword ? " launchctl unsetenv CLAWDBOT_GATEWAY_PASSWORD" : undefined,
].filter((line): line is string => Boolean(line));
note(lines.join("\n"), "Gateway (macOS)");
}

View File

@@ -26,7 +26,10 @@ import {
maybeScanExtraGatewayServices,
} from "./doctor-gateway-services.js";
import { noteSourceInstallIssues } from "./doctor-install.js";
import { noteMacLaunchAgentOverrides } from "./doctor-platform-notes.js";
import {
noteMacLaunchAgentOverrides,
noteMacLaunchctlGatewayEnvOverrides,
} from "./doctor-platform-notes.js";
import { createDoctorPrompter, type DoctorOptions } from "./doctor-prompter.js";
import { maybeRepairSandboxImages, noteSandboxScopeWarnings } from "./doctor-sandbox.js";
import { noteSecurityWarnings } from "./doctor-security.js";
@@ -160,6 +163,7 @@ export async function doctorCommand(
await maybeScanExtraGatewayServices(options);
await maybeRepairGatewayServiceConfig(cfg, resolveMode(cfg), runtime, prompter);
await noteMacLaunchAgentOverrides();
await noteMacLaunchctlGatewayEnvOverrides(cfg);
await noteSecurityWarnings(cfg);