chore: run format and fix sandbox browser timeouts

This commit is contained in:
Peter Steinberger
2026-01-16 09:18:53 +00:00
parent 7c34883267
commit 4965727f39
32 changed files with 17548 additions and 15412 deletions

View File

@@ -191,8 +191,8 @@ extension CronJobEditor {
func applyDeleteAfterRun(
to root: inout [String: Any],
scheduleKind: ScheduleKind? = nil,
deleteAfterRun: Bool? = nil
) {
deleteAfterRun: Bool? = nil)
{
let resolvedSchedule = scheduleKind ?? self.scheduleKind
let resolvedDelete = deleteAfterRun ?? self.deleteAfterRun
if resolvedSchedule == .at {

View File

@@ -58,9 +58,7 @@ export function stripThoughtSignatures<T>(
if (!block || typeof block !== "object") return block;
const rec = block as ContentBlockWithSignature;
const stripSnake = shouldStripSignature(rec.thought_signature);
const stripCamel = includeCamelCase
? shouldStripSignature(rec.thoughtSignature)
: false;
const stripCamel = includeCamelCase ? shouldStripSignature(rec.thoughtSignature) : false;
if (!stripSnake && !stripCamel) {
return block;
}

View File

@@ -162,7 +162,7 @@ describe("sanitizeSessionHistory (google thinking)", () => {
id: "call_1",
name: "read",
arguments: { path: "/tmp/foo" },
thoughtSignature: "{\"id\":1}",
thoughtSignature: '{"id":1}',
},
{
type: "toolCall",
@@ -192,7 +192,7 @@ describe("sanitizeSessionHistory (google thinking)", () => {
{ type: "text", text: "ok" },
{
type: "text",
text: "[Tool Call: read (ID: call_1)]\nArguments: {\n \"path\": \"/tmp/foo\"\n}",
text: '[Tool Call: read (ID: call_1)]\nArguments: {\n "path": "/tmp/foo"\n}',
},
{
type: "toolCall",

View File

@@ -11,9 +11,7 @@ vi.mock("./pi-embedded-helpers.js", async () => {
...actual,
isGoogleModelApi: vi.fn(),
downgradeGeminiHistory: vi.fn(),
sanitizeSessionMessagesImages: vi
.fn()
.mockImplementation(async (msgs) => msgs),
sanitizeSessionMessagesImages: vi.fn().mockImplementation(async (msgs) => msgs),
};
});
@@ -30,9 +28,7 @@ describe("sanitizeSessionHistory", () => {
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(helpers.sanitizeSessionMessagesImages).mockImplementation(
async (msgs) => msgs,
);
vi.mocked(helpers.sanitizeSessionMessagesImages).mockImplementation(async (msgs) => msgs);
// Default mock implementation
vi.mocked(helpers.downgradeGeminiHistory).mockImplementation((msgs) => {
if (!msgs) return [];

View File

@@ -82,12 +82,13 @@ function findUnsupportedSchemaKeywords(schema: unknown, path: string): string[]
return violations;
}
export function sanitizeToolsForGoogle<TSchemaType extends TSchema = TSchema, TResult = unknown>(
params: {
tools: AgentTool<TSchemaType, TResult>[];
provider: string;
},
): AgentTool<TSchemaType, TResult>[] {
export function sanitizeToolsForGoogle<
TSchemaType extends TSchema = TSchema,
TResult = unknown,
>(params: {
tools: AgentTool<TSchemaType, TResult>[];
provider: string;
}): AgentTool<TSchemaType, TResult>[] {
if (params.provider !== "google-antigravity" && params.provider !== "google-gemini-cli") {
return params.tools;
}
@@ -95,7 +96,9 @@ export function sanitizeToolsForGoogle<TSchemaType extends TSchema = TSchema, TR
if (!tool.parameters || typeof tool.parameters !== "object") return tool;
return {
...tool,
parameters: cleanToolSchemaForGemini(tool.parameters as Record<string, unknown>) as TSchemaType,
parameters: cleanToolSchemaForGemini(
tool.parameters as Record<string, unknown>,
) as TSchemaType,
};
});
}

View File

@@ -22,10 +22,7 @@ export type SubscribeEmbeddedPiSessionParams = {
blockReplyChunking?: BlockReplyChunking;
onPartialReply?: (payload: { text?: string; mediaUrls?: string[] }) => void | Promise<void>;
onAssistantMessageStart?: () => void | Promise<void>;
onAgentEvent?: (evt: {
stream: string;
data: Record<string, unknown>;
}) => void | Promise<void>;
onAgentEvent?: (evt: { stream: string; data: Record<string, unknown> }) => void | Promise<void>;
enforceFinalTag?: boolean;
};

View File

@@ -51,6 +51,8 @@ function buildSandboxBrowserResolvedConfig(params: {
cdpProtocol: "http",
cdpHost,
cdpIsLoopback: true,
remoteCdpTimeoutMs: 1500,
remoteCdpHandshakeTimeoutMs: 3000,
color: DEFAULT_CLAWD_BROWSER_COLOR,
executablePath: undefined,
headless: params.headless,

View File

@@ -15,9 +15,7 @@ describe("cdp.helpers", () => {
it("adds basic auth headers when credentials are present", () => {
const headers = getHeadersWithAuth("https://user:pass@example.com");
expect(headers.Authorization).toBe(
`Basic ${Buffer.from("user:pass").toString("base64")}`,
);
expect(headers.Authorization).toBe(`Basic ${Buffer.from("user:pass").toString("base64")}`);
});
it("keeps preexisting authorization headers", () => {

View File

@@ -28,15 +28,10 @@ export function isLoopbackHost(host: string) {
);
}
export function getHeadersWithAuth(
url: string,
headers: Record<string, string> = {},
) {
export function getHeadersWithAuth(url: string, headers: Record<string, string> = {}) {
try {
const parsed = new URL(url);
const hasAuthHeader = Object.keys(headers).some(
(key) => key.toLowerCase() === "authorization",
);
const hasAuthHeader = Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
if (hasAuthHeader) return headers;
if (parsed.username || parsed.password) {
const auth = Buffer.from(`${parsed.username}:${parsed.password}`).toString("base64");
@@ -103,18 +98,11 @@ function createCdpSender(ws: WebSocket) {
return { send, closeWithError };
}
export async function fetchJson<T>(
url: string,
timeoutMs = 1500,
init?: RequestInit,
): Promise<T> {
export async function fetchJson<T>(url: string, timeoutMs = 1500, init?: RequestInit): Promise<T> {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeoutMs);
try {
const headers = getHeadersWithAuth(
url,
(init?.headers as Record<string, string>) || {},
);
const headers = getHeadersWithAuth(url, (init?.headers as Record<string, string>) || {});
const res = await fetch(url, { ...init, headers, signal: ctrl.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as T;
@@ -123,18 +111,11 @@ export async function fetchJson<T>(
}
}
export async function fetchOk(
url: string,
timeoutMs = 1500,
init?: RequestInit,
): Promise<void> {
export async function fetchOk(url: string, timeoutMs = 1500, init?: RequestInit): Promise<void> {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeoutMs);
try {
const headers = getHeadersWithAuth(
url,
(init?.headers as Record<string, string>) || {},
);
const headers = getHeadersWithAuth(url, (init?.headers as Record<string, string>) || {});
const res = await fetch(url, { ...init, headers, signal: ctrl.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
} finally {

View File

@@ -1,9 +1,4 @@
import {
appendCdpPath,
fetchJson,
isLoopbackHost,
withCdpSocket,
} from "./cdp.helpers.js";
import { appendCdpPath, fetchJson, isLoopbackHost, withCdpSocket } from "./cdp.helpers.js";
export { appendCdpPath, fetchJson, fetchOk, getHeadersWithAuth } from "./cdp.helpers.js";

View File

@@ -25,7 +25,9 @@ describe("browser default executable detection", () => {
vi.mocked(execFileSync).mockImplementation((cmd, args) => {
const argsStr = Array.isArray(args) ? args.join(" ") : "";
if (cmd === "/usr/bin/plutil" && argsStr.includes("LSHandlers")) {
return JSON.stringify([{ LSHandlerURLScheme: "http", LSHandlerRoleAll: "com.google.Chrome" }]);
return JSON.stringify([
{ LSHandlerURLScheme: "http", LSHandlerRoleAll: "com.google.Chrome" },
]);
}
if (cmd === "/usr/bin/osascript" && argsStr.includes("path to application id")) {
return "/Applications/Google Chrome.app";
@@ -55,7 +57,9 @@ describe("browser default executable detection", () => {
vi.mocked(execFileSync).mockImplementation((cmd, args) => {
const argsStr = Array.isArray(args) ? args.join(" ") : "";
if (cmd === "/usr/bin/plutil" && argsStr.includes("LSHandlers")) {
return JSON.stringify([{ LSHandlerURLScheme: "http", LSHandlerRoleAll: "com.apple.Safari" }]);
return JSON.stringify([
{ LSHandlerURLScheme: "http", LSHandlerRoleAll: "com.apple.Safari" },
]);
}
return "";
});

View File

@@ -119,7 +119,12 @@ function inferKindFromIdentifier(identifier: string): BrowserExecutable["kind"]
if (id.includes("edge")) return "edge";
if (id.includes("chromium")) return "chromium";
if (id.includes("canary")) return "canary";
if (id.includes("opera") || id.includes("vivaldi") || id.includes("yandex") || id.includes("thebrowser")) {
if (
id.includes("opera") ||
id.includes("vivaldi") ||
id.includes("yandex") ||
id.includes("thebrowser")
) {
return "chromium";
}
return "chrome";
@@ -131,13 +136,12 @@ function inferKindFromExecutableName(name: string): BrowserExecutable["kind"] {
if (lower.includes("edge") || lower.includes("msedge")) return "edge";
if (lower.includes("chromium")) return "chromium";
if (lower.includes("canary") || lower.includes("sxs")) return "canary";
if (lower.includes("opera") || lower.includes("vivaldi") || lower.includes("yandex")) return "chromium";
if (lower.includes("opera") || lower.includes("vivaldi") || lower.includes("yandex"))
return "chromium";
return "chrome";
}
function detectDefaultChromiumExecutable(
platform: NodeJS.Platform,
): BrowserExecutable | null {
function detectDefaultChromiumExecutable(platform: NodeJS.Platform): BrowserExecutable | null {
if (platform === "darwin") return detectDefaultChromiumExecutableMac();
if (platform === "linux") return detectDefaultChromiumExecutableLinux();
if (platform === "win32") return detectDefaultChromiumExecutableWindows();
@@ -227,8 +231,7 @@ function detectDefaultChromiumExecutableLinux(): BrowserExecutable | null {
function detectDefaultChromiumExecutableWindows(): BrowserExecutable | null {
const progId = readWindowsProgId();
const command =
(progId ? readWindowsCommandForProgId(progId) : null) ||
readWindowsCommandForProgId("http");
(progId ? readWindowsCommandForProgId(progId) : null) || readWindowsCommandForProgId("http");
if (!command) return null;
const expanded = expandWindowsEnvVars(command);
const exePath = extractWindowsExecutablePath(expanded);
@@ -285,7 +288,7 @@ function splitExecLine(line: string): string[] {
let quoteChar = "";
for (let i = 0; i < line.length; i += 1) {
const ch = line[i];
if ((ch === "\"" || ch === "'") && (!inQuotes || ch === quoteChar)) {
if ((ch === '"' || ch === "'") && (!inQuotes || ch === quoteChar)) {
if (inQuotes) {
inQuotes = false;
quoteChar = "";
@@ -342,7 +345,7 @@ function readWindowsCommandForProgId(progId: string): string | null {
function expandWindowsEnvVars(value: string): string {
return value.replace(/%([^%]+)%/g, (_match, name) => {
const key = String(name ?? "").trim();
return key ? process.env[key] ?? `%${key}%` : _match;
return key ? (process.env[key] ?? `%${key}%`) : _match;
});
}

View File

@@ -267,9 +267,7 @@ async function connectBrowser(cdpUrl: string): Promise<ConnectedBrowser> {
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
const timeout = 5000 + attempt * 2000;
const wsUrl = await getChromeWebSocketUrl(normalized, timeout).catch(
() => null,
);
const wsUrl = await getChromeWebSocketUrl(normalized, timeout).catch(() => null);
const endpoint = wsUrl ?? normalized;
const headers = getHeadersWithAuth(endpoint);
const browser = await chromium.connectOverCDP(endpoint, { timeout, headers });

View File

@@ -1,11 +1,6 @@
import fs from "node:fs";
import {
appendCdpPath,
createTargetViaCdp,
getHeadersWithAuth,
normalizeCdpWsUrl,
} from "./cdp.js";
import { appendCdpPath, createTargetViaCdp, getHeadersWithAuth, normalizeCdpWsUrl } from "./cdp.js";
import {
isChromeCdpReady,
isChromeReachable,
@@ -55,10 +50,7 @@ async function fetchJson<T>(url: string, timeoutMs = 1500, init?: RequestInit):
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeoutMs);
try {
const headers = getHeadersWithAuth(
url,
(init?.headers as Record<string, string>) || {},
);
const headers = getHeadersWithAuth(url, (init?.headers as Record<string, string>) || {});
const res = await fetch(url, { ...init, headers, signal: ctrl.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as T;
@@ -71,10 +63,7 @@ async function fetchOk(url: string, timeoutMs = 1500, init?: RequestInit): Promi
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), timeoutMs);
try {
const headers = getHeadersWithAuth(
url,
(init?.headers as Record<string, string>) || {},
);
const headers = getHeadersWithAuth(url, (init?.headers as Record<string, string>) || {});
const res = await fetch(url, { ...init, headers, signal: ctrl.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
} finally {

File diff suppressed because one or more lines are too long

View File

@@ -238,7 +238,11 @@ export function registerConfigCli(program: Command) {
defaultRuntime.log(JSON.stringify(res.value ?? null, null, 2));
return;
}
if (typeof res.value === "string" || typeof res.value === "number" || typeof res.value === "boolean") {
if (
typeof res.value === "string" ||
typeof res.value === "number" ||
typeof res.value === "boolean"
) {
defaultRuntime.log(String(res.value));
return;
}

View File

@@ -225,7 +225,11 @@ describe("daemon-cli coverage", () => {
});
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
const parsed = JSON.parse(jsonLine ?? "{}") as { ok?: boolean; action?: string; result?: string };
const parsed = JSON.parse(jsonLine ?? "{}") as {
ok?: boolean;
action?: string;
result?: string;
};
expect(parsed.ok).toBe(true);
expect(parsed.action).toBe("install");
expect(parsed.result).toBe("installed");

View File

@@ -1,8 +1,5 @@
import type { Command } from "commander";
import {
listPairingChannels,
notifyPairingApproved,
} from "../channels/plugins/pairing.js";
import { listPairingChannels, notifyPairingApproved } from "../channels/plugins/pairing.js";
import { normalizeChannelId } from "../channels/plugins/index.js";
import { loadConfig } from "../config/config.js";
import { resolvePairingIdLabel } from "../pairing/pairing-labels.js";

View File

@@ -378,9 +378,7 @@ export function registerPluginsCli(program: Command) {
continue;
}
if (record.source !== "npm") {
defaultRuntime.log(
chalk.yellow(`Skipping "${pluginId}" (source: ${record.source}).`),
);
defaultRuntime.log(chalk.yellow(`Skipping "${pluginId}" (source: ${record.source}).`));
continue;
}
if (!record.spec) {
@@ -412,9 +410,7 @@ export function registerPluginsCli(program: Command) {
if (currentVersion && probe.version && currentVersion === probe.version) {
defaultRuntime.log(`${pluginId} is up to date (${currentLabel}).`);
} else {
defaultRuntime.log(
`Would update ${pluginId}: ${currentLabel}${nextVersion}.`,
);
defaultRuntime.log(`Would update ${pluginId}: ${currentLabel}${nextVersion}.`);
}
continue;
}

View File

@@ -372,8 +372,6 @@ async function resolveSshTarget(
});
if (!target) return { target: rawTarget, identity: identity ?? undefined };
const identityFile =
identity ??
config.identityFiles.find((entry) => entry.trim().length > 0)?.trim() ??
undefined;
identity ?? config.identityFiles.find((entry) => entry.trim().length > 0)?.trim() ?? undefined;
return { target, identity: identityFile };
}

View File

@@ -291,9 +291,7 @@ export async function statusAllCommand(
: gatewayProbe?.error
? `unreachable (${gatewayProbe.error})`
: "unreachable";
const gatewayAuth = gatewayReachable
? ` · auth ${formatGatewayAuthUsed(probeAuth)}`
: "";
const gatewayAuth = gatewayReachable ? ` · auth ${formatGatewayAuthUsed(probeAuth)}` : "";
const gatewaySelfLine =
gatewaySelf?.host || gatewaySelf?.ip || gatewaySelf?.version || gatewaySelf?.platform
? [

View File

@@ -28,18 +28,18 @@ describe("config discord", () => {
dm: {
enabled: true,
allowFrom: ["steipete"],
groupEnabled: true,
groupChannels: ["clawd-dm"],
},
actions: {
emojiUploads: true,
stickerUploads: false,
channels: true,
},
guilds: {
"123": {
slug: "friends-of-clawd",
requireMention: false,
groupEnabled: true,
groupChannels: ["clawd-dm"],
},
actions: {
emojiUploads: true,
stickerUploads: false,
channels: true,
},
guilds: {
"123": {
slug: "friends-of-clawd",
requireMention: false,
users: ["steipete"],
channels: {
general: { allow: true },

View File

@@ -306,7 +306,8 @@ const FIELD_HELP: Record<string, string> = {
"plugins.installs.*.source": 'Install source ("npm", "archive", or "path").',
"plugins.installs.*.spec": "Original npm spec used for install (if source is npm).",
"plugins.installs.*.sourcePath": "Original archive/path used for install (if any).",
"plugins.installs.*.installPath": "Resolved install directory (usually ~/.clawdbot/extensions/<id>).",
"plugins.installs.*.installPath":
"Resolved install directory (usually ~/.clawdbot/extensions/<id>).",
"plugins.installs.*.version": "Version recorded at install time (if available).",
"plugins.installs.*.installedAt": "ISO timestamp of last install/update.",
"agents.defaults.model.primary": "Primary model (provider/model).",

View File

@@ -10,11 +10,9 @@ import {
export const SessionSchema = z
.object({
scope: z.union([z.literal("per-sender"), z.literal("global")]).optional(),
dmScope: z.union([
z.literal("main"),
z.literal("per-peer"),
z.literal("per-channel-peer"),
]).optional(),
dmScope: z
.union([z.literal("main"), z.literal("per-peer"), z.literal("per-channel-peer")])
.optional(),
resetTriggers: z.array(z.string()).optional(),
idleMinutes: z.number().int().positive().optional(),
heartbeatIdleMinutes: z.number().int().positive().optional(),

View File

@@ -15,7 +15,7 @@ describe("parseSystemdExecStart", () => {
});
it("preserves quoted arguments", () => {
const execStart = "/usr/bin/clawdbot gateway start --name \"My Bot\"";
const execStart = '/usr/bin/clawdbot gateway start --name "My Bot"';
expect(parseSystemdExecStart(execStart)).toEqual([
"/usr/bin/clawdbot",
"gateway",

View File

@@ -1,9 +1,6 @@
import { timingSafeEqual } from "node:crypto";
import type { IncomingMessage } from "node:http";
import type {
GatewayAuthConfig,
GatewayTailscaleMode,
} from "../config/config.js";
import type { GatewayAuthConfig, GatewayTailscaleMode } from "../config/config.js";
export type ResolvedGatewayAuthMode = "none" | "token" | "password";
export type ResolvedGatewayAuth = {
@@ -62,14 +59,13 @@ function isLocalDirectRequest(req?: IncomingMessage): boolean {
if (!isLoopbackAddress(clientIp)) return false;
const host = getHostName(req.headers?.host);
const hostIsLocal =
host === "localhost" || host === "127.0.0.1" || host === "::1";
const hostIsLocal = host === "localhost" || host === "127.0.0.1" || host === "::1";
const hostIsTailscaleServe = host.endsWith(".ts.net");
const hasForwarded = Boolean(
req.headers?.["x-forwarded-for"] ||
req.headers?.["x-real-ip"] ||
req.headers?.["x-forwarded-host"],
req.headers?.["x-real-ip"] ||
req.headers?.["x-forwarded-host"],
);
return (hostIsLocal || hostIsTailscaleServe) && !hasForwarded;
@@ -81,17 +77,11 @@ function getTailscaleUser(req?: IncomingMessage): TailscaleUser | null {
if (typeof login !== "string" || !login.trim()) return null;
const nameRaw = req.headers["tailscale-user-name"];
const profilePic = req.headers["tailscale-user-profile-pic"];
const name =
typeof nameRaw === "string" && nameRaw.trim()
? nameRaw.trim()
: login.trim();
const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : login.trim();
return {
login: login.trim(),
name,
profilePic:
typeof profilePic === "string" && profilePic.trim()
? profilePic.trim()
: undefined,
profilePic: typeof profilePic === "string" && profilePic.trim() ? profilePic.trim() : undefined,
};
}
@@ -99,17 +89,14 @@ function hasTailscaleProxyHeaders(req?: IncomingMessage): boolean {
if (!req) return false;
return Boolean(
req.headers["x-forwarded-for"] &&
req.headers["x-forwarded-proto"] &&
req.headers["x-forwarded-host"],
req.headers["x-forwarded-proto"] &&
req.headers["x-forwarded-host"],
);
}
function isTailscaleProxyRequest(req?: IncomingMessage): boolean {
if (!req) return false;
return (
isLoopbackAddress(req.socket?.remoteAddress) &&
hasTailscaleProxyHeaders(req)
);
return isLoopbackAddress(req.socket?.remoteAddress) && hasTailscaleProxyHeaders(req);
}
export function resolveGatewayAuth(params: {
@@ -120,13 +107,11 @@ export function resolveGatewayAuth(params: {
const authConfig = params.authConfig ?? {};
const env = params.env ?? process.env;
const token = authConfig.token ?? env.CLAWDBOT_GATEWAY_TOKEN ?? undefined;
const password =
authConfig.password ?? env.CLAWDBOT_GATEWAY_PASSWORD ?? undefined;
const password = authConfig.password ?? env.CLAWDBOT_GATEWAY_PASSWORD ?? undefined;
const mode: ResolvedGatewayAuth["mode"] =
authConfig.mode ?? (password ? "password" : token ? "token" : "none");
const allowTailscale =
authConfig.allowTailscale ??
(params.tailscaleMode === "serve" && mode !== "password");
authConfig.allowTailscale ?? (params.tailscaleMode === "serve" && mode !== "password");
return {
mode,
token,
@@ -142,9 +127,7 @@ export function assertGatewayAuthConfigured(auth: ResolvedGatewayAuth): void {
);
}
if (auth.mode === "password" && !auth.password) {
throw new Error(
"gateway auth mode is password, but no password was configured",
);
throw new Error("gateway auth mode is password, but no password was configured");
}
}

View File

@@ -162,7 +162,9 @@ export async function startGatewayNodeBridge(params: {
});
if (started.port > 0) {
const scheme = params.bridgeTls?.enabled ? "tls" : "tcp";
params.logBridge.info(`listening on ${scheme}://${params.bridgeHost}:${started.port} (node)`);
params.logBridge.info(
`listening on ${scheme}://${params.bridgeHost}:${started.port} (node)`,
);
return { bridge: started, nodePresenceTimers };
}
} catch (err) {

View File

@@ -48,7 +48,8 @@ export async function startNodeBridgeServer(opts: NodeBridgeServerOpts): Promise
const loopbackHost = "127.0.0.1";
const listeners: Array<{ host: string; server: net.Server }> = [];
const createServer = () => (opts.tls ? tls.createServer(opts.tls, onConnection) : net.createServer(onConnection));
const createServer = () =>
opts.tls ? tls.createServer(opts.tls, onConnection) : net.createServer(onConnection);
const primary = createServer();
await new Promise<void>((resolve, reject) => {
const onError = (err: Error) => reject(err);

View File

@@ -145,9 +145,7 @@ function parseBinProbePayload(payloadJSON: string | null | undefined): string[]
try {
const parsed = JSON.parse(payloadJSON) as { stdout?: unknown; bins?: unknown };
if (Array.isArray(parsed.bins)) {
return parsed.bins
.map((bin) => String(bin).trim())
.filter(Boolean);
return parsed.bins.map((bin) => String(bin).trim()).filter(Boolean);
}
if (typeof parsed.stdout === "string") {
return parsed.stdout

View File

@@ -3,7 +3,10 @@ import type { PluginInstallRecord } from "../config/types.plugins.js";
export type PluginInstallUpdate = PluginInstallRecord & { pluginId: string };
export function recordPluginInstall(cfg: ClawdbotConfig, update: PluginInstallUpdate): ClawdbotConfig {
export function recordPluginInstall(
cfg: ClawdbotConfig,
update: PluginInstallUpdate,
): ClawdbotConfig {
const { pluginId, ...record } = update;
const installs = {
...cfg.plugins?.installs,

View File

@@ -70,11 +70,9 @@ export const registerTelegramNativeCommands = ({
) => Promise<unknown>;
};
if (typeof api.setMyCommands === "function") {
api
.setMyCommands(allCommands)
.catch((err) => {
runtime.error?.(danger(`telegram setMyCommands failed: ${String(err)}`));
});
api.setMyCommands(allCommands).catch((err) => {
runtime.error?.(danger(`telegram setMyCommands failed: ${String(err)}`));
});
} else {
logVerbose("telegram: setMyCommands unavailable; skipping registration");
}

View File

@@ -235,9 +235,7 @@ describe("createTelegramBot", () => {
expect(nativeStatus).toBeDefined();
expect(registered).toContainEqual({ command: "custom_backup", description: "Git backup" });
expect(registered).not.toContainEqual({ command: "status", description: "Custom status" });
expect(registered.filter((command) => command.command === "status")).toEqual([
nativeStatus,
]);
expect(registered.filter((command) => command.command === "status")).toEqual([nativeStatus]);
expect(errorSpy).toHaveBeenCalled();
});