fix: daemon status guidance and telegram fetch

This commit is contained in:
Peter Steinberger
2026-01-08 08:39:55 +01:00
parent 5b397c0f15
commit 9a11325cc9
5 changed files with 47 additions and 22 deletions

View File

@@ -106,15 +106,14 @@ export function createSessionsSpawnTool(opts?: {
const allowAgents =
resolveAgentConfig(cfg, requesterAgentId)?.subagents?.allowAgents ??
[];
const allowAny = allowAgents.some(
(value) => value.trim() === "*",
);
const allowAny = allowAgents.some((value) => value.trim() === "*");
const normalizedTargetId = targetAgentId.toLowerCase();
const allowSet = new Set(
allowAgents
.filter((value) => value.trim() && value.trim() !== "*")
.map((value) => normalizeAgentId(value)),
.map((value) => normalizeAgentId(value).toLowerCase()),
);
if (!allowAny && !allowSet.has(targetAgentId)) {
if (!allowAny && !allowSet.has(normalizedTargetId)) {
const allowedText = allowAny
? "*"
: allowSet.size > 0

View File

@@ -130,6 +130,7 @@ export type DaemonInstallOptions = {
port?: string | number;
runtime?: string;
token?: string;
force?: boolean;
};
function parsePort(raw: unknown): number | null {
@@ -192,6 +193,14 @@ function safeDaemonEnv(env: Record<string, string> | undefined): string[] {
return lines;
}
function normalizeListenerAddress(raw: string): string {
let value = raw.trim();
if (!value) return value;
value = value.replace(/^TCP\s+/i, "");
value = value.replace(/\s+\(LISTEN\)\s*$/i, "");
return value.trim();
}
async function probeGatewayStatus(opts: {
url: string;
token?: string;
@@ -330,7 +339,7 @@ async function gatherDaemonStatus(opts: {
const serviceEnv = command?.environment ?? undefined;
const mergedDaemonEnv = {
...(process.env as Record<string, string | undefined>),
...(serviceEnv ?? {}),
...(serviceEnv ?? undefined),
} satisfies Record<string, string | undefined>;
const cliConfigPath = resolveConfigPath(
@@ -389,7 +398,7 @@ async function gatherDaemonStatus(opts: {
const probeUrl = probeUrlOverride ?? `ws://${probeHost}:${daemonPort}`;
const probeNote =
!probeUrlOverride && bindMode === "lan"
? "Local probe uses loopback (127.0.0.1); gateway bind=lan listens on 0.0.0.0."
? "Local probe uses loopback (127.0.0.1). bind=lan listens on 0.0.0.0 (all interfaces); use a LAN IP for remote clients."
: !probeUrlOverride && bindMode === "loopback"
? "Loopback-only gateway; only local clients can connect."
: undefined;
@@ -539,6 +548,9 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) {
defaultRuntime.error(
"Root cause: CLI and daemon are using different config paths (likely a profile/state-dir mismatch).",
);
defaultRuntime.error(
"Fix: rerun `clawdbot daemon install --force` from the same --profile / CLAWDBOT_STATE_DIR you expect.",
);
}
}
if (status.gateway) {
@@ -610,7 +622,7 @@ function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) {
const addrs = Array.from(
new Set(
status.port.listeners
.map((l) => l.address?.trim())
.map((l) => (l.address ? normalizeListenerAddress(l.address) : ""))
.filter((v): v is string => Boolean(v)),
),
);
@@ -729,8 +741,11 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
return;
}
if (loaded) {
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
return;
if (!opts.force) {
defaultRuntime.log(`Gateway service already ${service.loadedText}.`);
defaultRuntime.log("Reinstall with: clawdbot daemon install --force");
return;
}
}
const devMode =
@@ -896,6 +911,7 @@ export function registerDaemonCli(program: Command) {
.option("--port <port>", "Gateway port")
.option("--runtime <runtime>", "Daemon runtime (node|bun). Default: node")
.option("--token <token>", "Gateway token (token auth)")
.option("--force", "Reinstall/overwrite if already installed", false)
.action(async (opts) => {
await runDaemonInstall(opts);
});

View File

@@ -19,14 +19,18 @@ function formatSpawnDetail(result: {
}): string {
const clean = (value: string | Buffer | null | undefined) => {
const text =
typeof value === "string"
? value
: value
? value.toString()
: "";
typeof value === "string" ? value : value ? value.toString() : "";
return text.replace(/\s+/g, " ").trim();
};
if (result.error) return String(result.error);
if (result.error) {
if (result.error instanceof Error) return result.error.message;
if (typeof result.error === "string") return result.error;
try {
return JSON.stringify(result.error);
} catch {
return "unknown error";
}
}
const stderr = clean(result.stderr);
if (stderr) return stderr;
const stdout = clean(result.stdout);
@@ -42,6 +46,9 @@ function normalizeSystemdUnit(raw?: string): string {
}
export function triggerClawdbotRestart(): RestartAttempt {
if (process.env.VITEST || process.env.NODE_ENV === "test") {
return { ok: true, method: "supervisor", detail: "test mode" };
}
const tried: string[] = [];
if (process.platform !== "darwin") {
if (process.platform === "linux") {

View File

@@ -1,13 +1,15 @@
// Bun compatibility: force native fetch under Bun; keep grammY defaults on Node.
// Ensure native fetch is used when available (Bun + Node 18+).
export function resolveTelegramFetch(
proxyFetch?: typeof fetch,
): typeof fetch | undefined {
if (proxyFetch) return proxyFetch;
const isBun = "Bun" in globalThis || Boolean(process?.versions?.bun);
if (!isBun) return undefined;
const fetchImpl = globalThis.fetch;
const isBun = "Bun" in globalThis || Boolean(process?.versions?.bun);
if (!fetchImpl) {
throw new Error("fetch is not available; set telegram.proxy in config");
if (isBun) {
throw new Error("fetch is not available; set telegram.proxy in config");
}
return undefined;
}
return fetchImpl;
}