refactor: rename clawdbot to moltbot with legacy compat

This commit is contained in:
Peter Steinberger
2026-01-27 12:19:58 +00:00
parent 83460df96f
commit 6d16a658e5
1839 changed files with 11250 additions and 11199 deletions

View File

@@ -9,7 +9,7 @@ import { extractArchive, resolveArchiveKind, resolvePackedRootDir } from "./arch
const tempDirs: string[] = [];
async function makeTempDir() {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-archive-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-archive-"));
tempDirs.push(dir);
return dir;
}

View File

@@ -17,8 +17,8 @@ describe("bonjour-discovery", () => {
if (domain === "local.") {
return {
stdout: [
"Add 2 3 local. _clawdbot-gw._tcp. Peter\\226\\128\\153s Mac Studio Gateway",
"Add 2 3 local. _clawdbot-gw._tcp. Laptop Gateway",
"Add 2 3 local. _moltbot-gw._tcp. Peter\\226\\128\\153s Mac Studio Gateway",
"Add 2 3 local. _moltbot-gw._tcp. Laptop Gateway",
"",
].join("\n"),
stderr: "",
@@ -30,7 +30,7 @@ describe("bonjour-discovery", () => {
if (domain === WIDE_AREA_DISCOVERY_DOMAIN) {
return {
stdout: [
`Add 2 3 ${WIDE_AREA_DISCOVERY_DOMAIN} _clawdbot-gw._tcp. Tailnet Gateway`,
`Add 2 3 ${WIDE_AREA_DISCOVERY_DOMAIN} _moltbot-gw._tcp. Tailnet Gateway`,
"",
].join("\n"),
stderr: "",
@@ -65,7 +65,7 @@ describe("bonjour-discovery", () => {
return {
stdout: [
`${instance}._clawdbot-gw._tcp. can be reached at ${host}:18789`,
`${instance}._moltbot-gw._tcp. can be reached at ${host}:18789`,
txtParts.join(" "),
"",
].join("\n"),
@@ -112,7 +112,7 @@ describe("bonjour-discovery", () => {
const domain = argv[3] ?? "";
if (argv[0] === "dns-sd" && argv[1] === "-B" && domain === "local.") {
return {
stdout: ["Add 2 3 local. _clawdbot-gw._tcp. Studio Gateway", ""].join("\n"),
stdout: ["Add 2 3 local. _moltbot-gw._tcp. Studio Gateway", ""].join("\n"),
stderr: "",
code: 0,
signal: null,
@@ -123,7 +123,7 @@ describe("bonjour-discovery", () => {
if (argv[0] === "dns-sd" && argv[1] === "-L") {
return {
stdout: [
"Studio Gateway._clawdbot-gw._tcp. can be reached at studio.local:18789",
"Studio Gateway._moltbot-gw._tcp. can be reached at studio.local:18789",
"txtvers=1 displayName=Peter\\226\\128\\153s\\032Mac\\032Studio lanHost=studio.local gatewayPort=18789 sshPort=22",
"",
].join("\n"),
@@ -164,6 +164,9 @@ describe("bonjour-discovery", () => {
it("falls back to tailnet DNS probing for wide-area when split DNS is not configured", async () => {
const calls: Array<{ argv: string[]; timeoutMs: number }> = [];
const zone = WIDE_AREA_DISCOVERY_DOMAIN.replace(/\.$/, "");
const serviceBase = `_moltbot-gw._tcp.${zone}`;
const studioService = `studio-gateway.${serviceBase}`;
const run = vi.fn(async (argv: string[], options: { timeoutMs: number }) => {
calls.push({ argv, timeoutMs: options.timeoutMs });
@@ -200,13 +203,9 @@ describe("bonjour-discovery", () => {
const qname = argv[argv.length - 2] ?? "";
const qtype = argv[argv.length - 1] ?? "";
if (
server === "100.123.224.76" &&
qtype === "PTR" &&
qname === "_clawdbot-gw._tcp.clawdbot.internal"
) {
if (server === "100.123.224.76" && qtype === "PTR" && qname === serviceBase) {
return {
stdout: `studio-gateway._clawdbot-gw._tcp.clawdbot.internal.\n`,
stdout: `${studioService}.\n`,
stderr: "",
code: 0,
signal: null,
@@ -214,13 +213,9 @@ describe("bonjour-discovery", () => {
};
}
if (
server === "100.123.224.76" &&
qtype === "SRV" &&
qname === "studio-gateway._clawdbot-gw._tcp.clawdbot.internal"
) {
if (server === "100.123.224.76" && qtype === "SRV" && qname === studioService) {
return {
stdout: `0 0 18789 studio.clawdbot.internal.\n`,
stdout: `0 0 18789 studio.${zone}.\n`,
stderr: "",
code: 0,
signal: null,
@@ -228,11 +223,7 @@ describe("bonjour-discovery", () => {
};
}
if (
server === "100.123.224.76" &&
qtype === "TXT" &&
qname === "studio-gateway._clawdbot-gw._tcp.clawdbot.internal"
) {
if (server === "100.123.224.76" && qtype === "TXT" && qname === studioService) {
return {
stdout: [
`"displayName=Studio"`,
@@ -240,7 +231,7 @@ describe("bonjour-discovery", () => {
`"transport=gateway"`,
`"sshPort=22"`,
`"tailnetDns=peters-mac-studio-1.sheep-coho.ts.net"`,
`"cliPath=/opt/homebrew/bin/clawdbot"`,
`"cliPath=/opt/homebrew/bin/moltbot"`,
"",
].join(" "),
stderr: "",
@@ -266,12 +257,12 @@ describe("bonjour-discovery", () => {
domain: WIDE_AREA_DISCOVERY_DOMAIN,
instanceName: "studio-gateway",
displayName: "Studio",
host: "studio.clawdbot.internal",
host: `studio.${zone}`,
port: 18789,
tailnetDns: "peters-mac-studio-1.sheep-coho.ts.net",
gatewayPort: 18789,
sshPort: 22,
cliPath: "/opt/homebrew/bin/clawdbot",
cliPath: "/opt/homebrew/bin/moltbot",
}),
]);
@@ -295,12 +286,12 @@ describe("bonjour-discovery", () => {
await discoverGatewayBeacons({
platform: "darwin",
timeoutMs: 1,
domains: ["local", "clawdbot.internal"],
domains: ["local", "moltbot.internal"],
run: run as unknown as typeof runCommandWithTimeout,
});
expect(calls.filter((c) => c[1] === "-B").map((c) => c[3])).toEqual(
expect.arrayContaining(["local.", "clawdbot.internal."]),
expect.arrayContaining(["local.", "moltbot.internal."]),
);
calls.length = 0;

View File

@@ -166,9 +166,9 @@ function parseDnsSdBrowse(stdout: string): string[] {
const instances = new Set<string>();
for (const raw of stdout.split("\n")) {
const line = raw.trim();
if (!line || !line.includes("_clawdbot-gw._tcp")) continue;
if (!line || !line.includes("_moltbot-gw._tcp")) continue;
if (!line.includes("Add")) continue;
const match = line.match(/_clawdbot-gw\._tcp\.?\s+(.+)$/);
const match = line.match(/_moltbot-gw\._tcp\.?\s+(.+)$/);
if (match?.[1]) {
instances.add(decodeDnsSdEscapes(match[1].trim()));
}
@@ -225,13 +225,13 @@ async function discoverViaDnsSd(
timeoutMs: number,
run: typeof runCommandWithTimeout,
): Promise<GatewayBonjourBeacon[]> {
const browse = await run(["dns-sd", "-B", "_clawdbot-gw._tcp", domain], {
const browse = await run(["dns-sd", "-B", "_moltbot-gw._tcp", domain], {
timeoutMs,
});
const instances = parseDnsSdBrowse(browse.stdout);
const results: GatewayBonjourBeacon[] = [];
for (const instance of instances) {
const resolved = await run(["dns-sd", "-L", instance, "_clawdbot-gw._tcp", domain], {
const resolved = await run(["dns-sd", "-L", instance, "_moltbot-gw._tcp", domain], {
timeoutMs,
});
const parsed = parseDnsSdResolve(resolved.stdout, instance);
@@ -268,7 +268,7 @@ async function discoverWideAreaViaTailnetDns(
// Keep scans bounded: this is a fallback and should not block long.
ips = ips.slice(0, 40);
const probeName = `_clawdbot-gw._tcp.${domain.replace(/\.$/, "")}`;
const probeName = `_moltbot-gw._tcp.${domain.replace(/\.$/, "")}`;
const concurrency = 6;
let nextIndex = 0;
@@ -312,7 +312,7 @@ async function discoverWideAreaViaTailnetDns(
if (budget <= 0) break;
const ptrName = ptr.trim().replace(/\.$/, "");
if (!ptrName) continue;
const instanceName = ptrName.replace(/\.?_clawdbot-gw\._tcp\..*$/, "");
const instanceName = ptrName.replace(/\.?_moltbot-gw\._tcp\..*$/, "");
const srv = await run(["dig", "+short", "+time=1", "+tries=1", nameserverArg, ptrName, "SRV"], {
timeoutMs: Math.max(1, Math.min(350, budget)),
@@ -371,9 +371,9 @@ function parseAvahiBrowse(stdout: string): GatewayBonjourBeacon[] {
for (const raw of stdout.split("\n")) {
const line = raw.trimEnd();
if (!line) continue;
if (line.startsWith("=") && line.includes("_clawdbot-gw._tcp")) {
if (line.startsWith("=") && line.includes("_moltbot-gw._tcp")) {
if (current) results.push(current);
const marker = " _clawdbot-gw._tcp";
const marker = " _moltbot-gw._tcp";
const idx = line.indexOf(marker);
const left = idx >= 0 ? line.slice(0, idx).trim() : line;
const parts = left.split(/\s+/);
@@ -429,7 +429,7 @@ async function discoverViaAvahi(
timeoutMs: number,
run: typeof runCommandWithTimeout,
): Promise<GatewayBonjourBeacon[]> {
const args = ["avahi-browse", "-rt", "_clawdbot-gw._tcp"];
const args = ["avahi-browse", "-rt", "_moltbot-gw._tcp"];
if (domain && domain !== "local.") {
// avahi-browse wants a plain domain (no trailing dot)
args.push("-d", domain.replace(/\.$/, ""));

View File

@@ -111,12 +111,12 @@ describe("gateway bonjour advertiser", () => {
gatewayPort: 18789,
sshPort: 2222,
tailnetDns: "host.tailnet.ts.net",
cliPath: "/opt/homebrew/bin/clawdbot",
cliPath: "/opt/homebrew/bin/moltbot",
});
expect(createService).toHaveBeenCalledTimes(1);
const [gatewayCall] = createService.mock.calls as Array<[Record<string, unknown>]>;
expect(gatewayCall?.[0]?.type).toBe("clawdbot-gw");
expect(gatewayCall?.[0]?.type).toBe("moltbot-gw");
const gatewayType = asString(gatewayCall?.[0]?.type, "");
expect(gatewayType.length).toBeLessThanOrEqual(15);
expect(gatewayCall?.[0]?.port).toBe(18789);
@@ -126,7 +126,7 @@ describe("gateway bonjour advertiser", () => {
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.gatewayPort).toBe("18789");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.sshPort).toBe("2222");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.cliPath).toBe(
"/opt/homebrew/bin/clawdbot",
"/opt/homebrew/bin/moltbot",
);
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.transport).toBe("gateway");
@@ -163,7 +163,7 @@ describe("gateway bonjour advertiser", () => {
const started = await startGatewayBonjourAdvertiser({
gatewayPort: 18789,
sshPort: 2222,
cliPath: "/opt/homebrew/bin/clawdbot",
cliPath: "/opt/homebrew/bin/moltbot",
minimal: true,
});
@@ -364,7 +364,7 @@ describe("gateway bonjour advertiser", () => {
});
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
expect(gatewayCall?.[0]?.name).toBe("Mac (Clawdbot)");
expect(gatewayCall?.[0]?.name).toBe("Mac (Moltbot)");
expect(gatewayCall?.[0]?.domain).toBe("local");
expect(gatewayCall?.[0]?.hostname).toBe("Mac");
expect((gatewayCall?.[0]?.txt as Record<string, string>)?.lanHost).toBe("Mac.local");

View File

@@ -36,12 +36,12 @@ function isDisabledByEnv() {
function safeServiceName(name: string) {
const trimmed = name.trim();
return trimmed.length > 0 ? trimmed : "Clawdbot";
return trimmed.length > 0 ? trimmed : "Moltbot";
}
function prettifyInstanceName(name: string) {
const normalized = name.trim().replace(/\s+/g, " ");
return normalized.replace(/\s+\(Clawdbot\)\s*$/i, "").trim() || normalized;
return normalized.replace(/\s+\(Moltbot\)\s*$/i, "").trim() || normalized;
}
type BonjourService = {
@@ -95,11 +95,11 @@ export async function startGatewayBonjourAdvertiser(
.hostname()
.replace(/\.local$/i, "")
.split(".")[0]
.trim() || "clawdbot";
.trim() || "moltbot";
const instanceName =
typeof opts.instanceName === "string" && opts.instanceName.trim()
? opts.instanceName.trim()
: `${hostname} (Clawdbot)`;
: `${hostname} (Moltbot)`;
const displayName = prettifyInstanceName(instanceName);
const txtBase: Record<string, string> = {
@@ -140,7 +140,7 @@ export async function startGatewayBonjourAdvertiser(
const gateway = responder.createService({
name: safeServiceName(instanceName),
type: "clawdbot-gw",
type: "moltbot-gw",
protocol: Protocol.TCP,
port: opts.gatewayPort,
domain: "local",

View File

@@ -8,7 +8,7 @@ import { resolveBrewExecutable, resolveBrewPathDirs } from "./brew.js";
describe("brew helpers", () => {
it("resolves brew from ~/.linuxbrew/bin when executable exists", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-brew-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-brew-"));
try {
const homebrewBin = path.join(tmp, ".linuxbrew", "bin");
await fs.mkdir(homebrewBin, { recursive: true });
@@ -24,7 +24,7 @@ describe("brew helpers", () => {
});
it("prefers HOMEBREW_PREFIX/bin/brew when present", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-brew-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-brew-"));
try {
const prefix = path.join(tmp, "prefix");
const prefixBin = path.join(prefix, "bin");

View File

@@ -1,6 +1,6 @@
import { listChannelPlugins } from "../channels/plugins/index.js";
import type { ChannelAccountSnapshot, ChannelPlugin } from "../channels/plugins/types.js";
import { type ClawdbotConfig, loadConfig } from "../config/config.js";
import { type MoltbotConfig, loadConfig } from "../config/config.js";
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";
import { theme } from "../terminal/theme.js";
@@ -34,7 +34,7 @@ const accountLine = (label: string, details: string[]) =>
const resolveAccountEnabled = (
plugin: ChannelPlugin,
account: unknown,
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
): boolean => {
if (plugin.config.isEnabled) {
return plugin.config.isEnabled(account, cfg);
@@ -47,7 +47,7 @@ const resolveAccountEnabled = (
const resolveAccountConfigured = async (
plugin: ChannelPlugin,
account: unknown,
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
): Promise<boolean> => {
if (plugin.config.isConfigured) {
return await plugin.config.isConfigured(account, cfg);
@@ -58,7 +58,7 @@ const resolveAccountConfigured = async (
const buildAccountSnapshot = (params: {
plugin: ChannelPlugin;
account: unknown;
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
accountId: string;
enabled: boolean;
configured: boolean;
@@ -76,7 +76,7 @@ const buildAccountSnapshot = (params: {
const formatAllowFrom = (params: {
plugin: ChannelPlugin;
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
accountId?: string | null;
allowFrom: Array<string | number>;
}) => {
@@ -93,7 +93,7 @@ const formatAllowFrom = (params: {
const buildAccountDetails = (params: {
entry: ChannelAccountEntry;
plugin: ChannelPlugin;
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
includeAllowFrom: boolean;
}): string[] => {
const details: string[] = [];
@@ -129,7 +129,7 @@ const buildAccountDetails = (params: {
};
export async function buildChannelSummary(
cfg?: ClawdbotConfig,
cfg?: MoltbotConfig,
options?: ChannelSummaryOptions,
): Promise<string[]> {
const effective = cfg ?? loadConfig();

View File

@@ -8,7 +8,7 @@ import { resolveControlUiDistIndexPath, resolveControlUiRepoRoot } from "./contr
describe("control UI assets helpers", () => {
it("resolves repo root from src argv1", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-ui-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-ui-"));
try {
await fs.mkdir(path.join(tmp, "ui"), { recursive: true });
await fs.writeFile(path.join(tmp, "ui", "vite.config.ts"), "export {};\n");
@@ -23,7 +23,7 @@ describe("control UI assets helpers", () => {
});
it("resolves repo root from dist argv1", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-ui-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-ui-"));
try {
await fs.mkdir(path.join(tmp, "ui"), { recursive: true });
await fs.writeFile(path.join(tmp, "ui", "vite.config.ts"), "export {};\n");

View File

@@ -11,7 +11,7 @@ import {
describe("device pairing tokens", () => {
test("preserves existing token scopes when rotating without scopes", async () => {
const baseDir = await mkdtemp(join(tmpdir(), "clawdbot-device-pairing-"));
const baseDir = await mkdtemp(join(tmpdir(), "moltbot-device-pairing-"));
const request = await requestDevicePairing(
{
deviceId: "device-1",

View File

@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
export type DiagnosticSessionState = "idle" | "processing" | "waiting";
@@ -149,7 +149,7 @@ export type DiagnosticEventInput = DiagnosticEventPayload extends infer Event
let seq = 0;
const listeners = new Set<(evt: DiagnosticEventPayload) => void>();
export function isDiagnosticsEnabled(config?: ClawdbotConfig): boolean {
export function isDiagnosticsEnabled(config?: MoltbotConfig): boolean {
return config?.diagnostics?.enabled === true;
}

View File

@@ -1,13 +1,13 @@
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { isDiagnosticFlagEnabled, resolveDiagnosticFlags } from "./diagnostic-flags.js";
describe("diagnostic flags", () => {
it("merges config + env flags", () => {
const cfg = {
diagnostics: { flags: ["telegram.http", "cache.*"] },
} as ClawdbotConfig;
} as MoltbotConfig;
const env = {
CLAWDBOT_DIAGNOSTICS: "foo,bar",
} as NodeJS.ProcessEnv;

View File

@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
const DIAGNOSTICS_ENV = "CLAWDBOT_DIAGNOSTICS";
@@ -32,7 +32,7 @@ function uniqueFlags(flags: string[]): string[] {
}
export function resolveDiagnosticFlags(
cfg?: ClawdbotConfig,
cfg?: MoltbotConfig,
env: NodeJS.ProcessEnv = process.env,
): string[] {
const configFlags = Array.isArray(cfg?.diagnostics?.flags) ? cfg?.diagnostics?.flags : [];
@@ -62,7 +62,7 @@ export function matchesDiagnosticFlag(flag: string, enabledFlags: string[]): boo
export function isDiagnosticFlagEnabled(
flag: string,
cfg?: ClawdbotConfig,
cfg?: MoltbotConfig,
env: NodeJS.ProcessEnv = process.env,
): boolean {
const flags = resolveDiagnosticFlags(cfg, env);

View File

@@ -16,7 +16,7 @@ describe("loadDotEnv", () => {
const prevEnv = { ...process.env };
const prevCwd = process.cwd();
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-dotenv-test-"));
const base = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-dotenv-test-"));
const cwdDir = path.join(base, "cwd");
const stateDir = path.join(base, "state");
@@ -48,7 +48,7 @@ describe("loadDotEnv", () => {
const prevEnv = { ...process.env };
const prevCwd = process.cwd();
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-dotenv-test-"));
const base = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-dotenv-test-"));
const cwdDir = path.join(base, "cwd");
const stateDir = path.join(base, "state");

View File

@@ -1,6 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { createExecApprovalForwarder } from "./exec-approval-forwarder.js";
const baseRequest = {
@@ -24,7 +24,7 @@ describe("exec approval forwarder", () => {
const deliver = vi.fn().mockResolvedValue([]);
const cfg = {
approvals: { exec: { enabled: true, mode: "session" } },
} as ClawdbotConfig;
} as MoltbotConfig;
const forwarder = createExecApprovalForwarder({
getConfig: () => cfg,
@@ -59,7 +59,7 @@ describe("exec approval forwarder", () => {
targets: [{ channel: "telegram", to: "123" }],
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const forwarder = createExecApprovalForwarder({
getConfig: () => cfg,

View File

@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js";
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
import type {
@@ -52,11 +52,11 @@ export type ExecApprovalForwarder = {
};
export type ExecApprovalForwarderDeps = {
getConfig?: () => ClawdbotConfig;
getConfig?: () => MoltbotConfig;
deliver?: typeof deliverOutboundPayloads;
nowMs?: () => number;
resolveSessionTarget?: (params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
request: ExecApprovalRequest;
}) => ExecApprovalForwardTarget | null;
};
@@ -136,7 +136,7 @@ function buildExpiredMessage(request: ExecApprovalRequest) {
}
function defaultResolveSessionTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
request: ExecApprovalRequest;
}): ExecApprovalForwardTarget | null {
const sessionKey = params.request.request.sessionKey?.trim();
@@ -159,7 +159,7 @@ function defaultResolveSessionTarget(params: {
}
async function deliverToTargets(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
targets: ForwardTarget[];
text: string;
deliver: typeof deliverOutboundPayloads;

View File

@@ -29,7 +29,7 @@ function makePathEnv(binDir: string): NodeJS.ProcessEnv {
}
function makeTempDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-exec-approvals-"));
return fs.mkdtempSync(path.join(os.tmpdir(), "moltbot-exec-approvals-"));
}
describe("exec approvals allowlist matching", () => {

View File

@@ -10,8 +10,8 @@ import { acquireGatewayLock, GatewayLockError } from "./gateway-lock.js";
import { resolveConfigPath, resolveGatewayLockDir, resolveStateDir } from "../config/paths.js";
async function makeEnv() {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gateway-lock-"));
const configPath = path.join(dir, "clawdbot.json");
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-gateway-lock-"));
const configPath = path.join(dir, "moltbot.json");
await fs.writeFile(configPath, "{}", "utf8");
await fs.mkdir(resolveGatewayLockDir(), { recursive: true });
return {

View File

@@ -81,7 +81,7 @@ function isGatewayArgv(args: string[]): boolean {
}
const exe = normalized[0] ?? "";
return exe.endsWith("/clawdbot") || exe === "clawdbot";
return exe.endsWith("/moltbot") || exe === "moltbot";
}
function readLinuxCmdline(pid: number): string[] | null {

View File

@@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as replyModule from "../auto-reply/reply.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveMainSessionKey } from "../config/sessions.js";
import { runHeartbeatOnce } from "./heartbeat-runner.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
@@ -31,11 +31,11 @@ beforeEach(() => {
describe("resolveHeartbeatIntervalMs", () => {
it("respects ackMaxChars for heartbeat acks", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -93,11 +93,11 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("sends HEARTBEAT_OK when visibility.showOk is true", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -155,11 +155,11 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("skips heartbeat LLM calls when visibility disables all output", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -222,11 +222,11 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("skips delivery for markup-wrapped HEARTBEAT_OK", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -283,13 +283,13 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("does not regress updatedAt when restoring heartbeat sessions", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const originalUpdatedAt = 1000;
const bumpedUpdatedAt = 2000;
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -356,11 +356,11 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("skips WhatsApp delivery when not linked or running", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -416,13 +416,13 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("passes through accountId for telegram heartbeats", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
const prevTelegramToken = process.env.TELEGRAM_BOT_TOKEN;
process.env.TELEGRAM_BOT_TOKEN = "";
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -484,13 +484,13 @@ describe("resolveHeartbeatIntervalMs", () => {
});
it("does not pre-resolve telegram accountId (allows config-only account tokens)", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
const prevTelegramToken = process.env.TELEGRAM_BOT_TOKEN;
process.env.TELEGRAM_BOT_TOKEN = "";
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,

View File

@@ -4,7 +4,7 @@ import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { HEARTBEAT_PROMPT } from "../auto-reply/heartbeat.js";
import * as replyModule from "../auto-reply/reply.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import {
resolveAgentIdFromSessionKey,
resolveAgentMainSessionKey,
@@ -95,7 +95,7 @@ describe("resolveHeartbeatPrompt", () => {
});
it("uses a trimmed override when configured", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { heartbeat: { prompt: " ping " } } },
};
expect(resolveHeartbeatPrompt(cfg)).toBe("ping");
@@ -104,7 +104,7 @@ describe("resolveHeartbeatPrompt", () => {
describe("isHeartbeatEnabledForAgent", () => {
it("enables only explicit heartbeat agents when configured", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: { heartbeat: { every: "30m" } },
list: [{ id: "main" }, { id: "ops", heartbeat: { every: "1h" } }],
@@ -115,7 +115,7 @@ describe("isHeartbeatEnabledForAgent", () => {
});
it("falls back to default agent when no explicit heartbeat entries", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: { heartbeat: { every: "30m" } },
list: [{ id: "main" }, { id: "ops" }],
@@ -133,7 +133,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
};
it("respects target none", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { heartbeat: { target: "none" } } },
};
expect(resolveHeartbeatDeliveryTarget({ cfg, entry: baseEntry })).toEqual({
@@ -146,7 +146,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("uses last route by default", () => {
const cfg: ClawdbotConfig = {};
const cfg: MoltbotConfig = {};
const entry = {
...baseEntry,
lastChannel: "whatsapp" as const,
@@ -162,7 +162,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("normalizes explicit WhatsApp targets when allowFrom is '*'", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
heartbeat: { target: "whatsapp", to: "whatsapp:(555) 123" },
@@ -180,7 +180,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("skips when last route is webchat", () => {
const cfg: ClawdbotConfig = {};
const cfg: MoltbotConfig = {};
const entry = {
...baseEntry,
lastChannel: "webchat" as const,
@@ -196,7 +196,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("applies allowFrom fallback for WhatsApp targets", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { heartbeat: { target: "whatsapp", to: "+1999" } } },
channels: { whatsapp: { allowFrom: ["+1555", "+1666"] } },
};
@@ -216,7 +216,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("keeps WhatsApp group targets even with allowFrom set", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { whatsapp: { allowFrom: ["+1555"] } },
};
const entry = {
@@ -234,7 +234,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("normalizes prefixed WhatsApp group targets for heartbeat delivery", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { whatsapp: { allowFrom: ["+1555"] } },
};
const entry = {
@@ -252,7 +252,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("keeps explicit telegram targets", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { heartbeat: { target: "telegram", to: "123" } } },
};
expect(resolveHeartbeatDeliveryTarget({ cfg, entry: baseEntry })).toEqual({
@@ -265,7 +265,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
});
it("prefers per-agent heartbeat overrides when provided", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { heartbeat: { target: "telegram", to: "123" } } },
};
const heartbeat = { target: "whatsapp", to: "+1555" } as const;
@@ -287,7 +287,7 @@ describe("resolveHeartbeatDeliveryTarget", () => {
describe("runHeartbeatOnce", () => {
it("skips when agent heartbeat is not enabled", async () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: { heartbeat: { every: "30m" } },
list: [{ id: "main" }, { id: "ops", heartbeat: { every: "1h" } }],
@@ -302,7 +302,7 @@ describe("runHeartbeatOnce", () => {
});
it("skips outside active hours", async () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
userTimezone: "UTC",
@@ -326,11 +326,11 @@ describe("runHeartbeatOnce", () => {
});
it("uses the last non-empty payload for delivery", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -384,11 +384,11 @@ describe("runHeartbeatOnce", () => {
});
it("uses per-agent heartbeat overrides and session keys", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
heartbeat: { every: "30m", prompt: "Default prompt" },
@@ -454,12 +454,12 @@ describe("runHeartbeatOnce", () => {
});
it("runs heartbeats in the explicit session key when configured", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const groupId = "120363401234567890@g.us";
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -537,11 +537,11 @@ describe("runHeartbeatOnce", () => {
});
it("suppresses duplicate heartbeat payloads within 24h", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -593,11 +593,11 @@ describe("runHeartbeatOnce", () => {
});
it("can include reasoning payloads when enabled", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -665,11 +665,11 @@ describe("runHeartbeatOnce", () => {
});
it("delivers reasoning even when the main heartbeat reply is HEARTBEAT_OK", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,
@@ -736,11 +736,11 @@ describe("runHeartbeatOnce", () => {
});
it("loads the default agent session from templated stores", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storeTemplate = path.join(tmpDir, "agents", "{agentId}", "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: { workspace: tmpDir, heartbeat: { every: "5m" } },
list: [{ id: "work", default: true }],
@@ -800,7 +800,7 @@ describe("runHeartbeatOnce", () => {
});
it("skips heartbeat when HEARTBEAT.md is effectively empty (saves API calls)", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const workspaceDir = path.join(tmpDir, "workspace");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
@@ -814,7 +814,7 @@ describe("runHeartbeatOnce", () => {
"utf-8",
);
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: workspaceDir,
@@ -872,7 +872,7 @@ describe("runHeartbeatOnce", () => {
});
it("runs heartbeat when HEARTBEAT.md has actionable content", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const workspaceDir = path.join(tmpDir, "workspace");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
@@ -886,7 +886,7 @@ describe("runHeartbeatOnce", () => {
"utf-8",
);
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: workspaceDir,
@@ -942,7 +942,7 @@ describe("runHeartbeatOnce", () => {
});
it("runs heartbeat when HEARTBEAT.md does not exist (lets LLM decide)", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const workspaceDir = path.join(tmpDir, "workspace");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
@@ -950,7 +950,7 @@ describe("runHeartbeatOnce", () => {
await fs.mkdir(workspaceDir, { recursive: true });
// Don't create HEARTBEAT.md - it doesn't exist
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: workspaceDir,

View File

@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { startHeartbeatRunner } from "./heartbeat-runner.js";
describe("startHeartbeatRunner", () => {
@@ -17,7 +17,7 @@ describe("startHeartbeatRunner", () => {
const runner = startHeartbeatRunner({
cfg: {
agents: { defaults: { heartbeat: { every: "30m" } } },
} as ClawdbotConfig,
} as MoltbotConfig,
runOnce: runSpy,
});
@@ -36,7 +36,7 @@ describe("startHeartbeatRunner", () => {
{ id: "ops", heartbeat: { every: "15m" } },
],
},
} as ClawdbotConfig);
} as MoltbotConfig);
await vi.advanceTimersByTimeAsync(10 * 60_000 + 1_000);

View File

@@ -4,7 +4,7 @@ import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
import * as replyModule from "../auto-reply/reply.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveMainSessionKey } from "../config/sessions.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { createPluginRuntime } from "../plugins/runtime/index.js";
@@ -36,11 +36,11 @@ beforeEach(() => {
describe("runHeartbeatOnce", () => {
it("uses the delivery target as sender when lastTo differs", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-hb-"));
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-hb-"));
const storePath = path.join(tmpDir, "sessions.json");
const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
try {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: {
defaults: {
workspace: tmpDir,

View File

@@ -22,7 +22,7 @@ import type { ReplyPayload } from "../auto-reply/types.js";
import { getChannelPlugin } from "../channels/plugins/index.js";
import type { ChannelHeartbeatDeps } from "../channels/plugins/types.js";
import { parseDurationMs } from "../cli/parse-duration.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { loadConfig } from "../config/config.js";
import {
canonicalizeMainSessionAlias,
@@ -97,7 +97,7 @@ const EXEC_EVENT_PROMPT =
"Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. " +
"If it failed, explain what went wrong.";
function resolveActiveHoursTimezone(cfg: ClawdbotConfig, raw?: string): string {
function resolveActiveHoursTimezone(cfg: MoltbotConfig, raw?: string): string {
const trimmed = raw?.trim();
if (!trimmed || trimmed === "user") {
return resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
@@ -149,7 +149,7 @@ function resolveMinutesInTimeZone(nowMs: number, timeZone: string): number | nul
}
function isWithinActiveHours(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
heartbeat?: HeartbeatConfig,
nowMs?: number,
): boolean {
@@ -181,15 +181,15 @@ type HeartbeatAgentState = {
export type HeartbeatRunner = {
stop: () => void;
updateConfig: (cfg: ClawdbotConfig) => void;
updateConfig: (cfg: MoltbotConfig) => void;
};
function hasExplicitHeartbeatAgents(cfg: ClawdbotConfig) {
function hasExplicitHeartbeatAgents(cfg: MoltbotConfig) {
const list = cfg.agents?.list ?? [];
return list.some((entry) => Boolean(entry?.heartbeat));
}
export function isHeartbeatEnabledForAgent(cfg: ClawdbotConfig, agentId?: string): boolean {
export function isHeartbeatEnabledForAgent(cfg: MoltbotConfig, agentId?: string): boolean {
const resolvedAgentId = normalizeAgentId(agentId ?? resolveDefaultAgentId(cfg));
const list = cfg.agents?.list ?? [];
const hasExplicit = hasExplicitHeartbeatAgents(cfg);
@@ -201,10 +201,7 @@ export function isHeartbeatEnabledForAgent(cfg: ClawdbotConfig, agentId?: string
return resolvedAgentId === resolveDefaultAgentId(cfg);
}
function resolveHeartbeatConfig(
cfg: ClawdbotConfig,
agentId?: string,
): HeartbeatConfig | undefined {
function resolveHeartbeatConfig(cfg: MoltbotConfig, agentId?: string): HeartbeatConfig | undefined {
const defaults = cfg.agents?.defaults?.heartbeat;
if (!agentId) return defaults;
const overrides = resolveAgentConfig(cfg, agentId)?.heartbeat;
@@ -213,7 +210,7 @@ function resolveHeartbeatConfig(
}
export function resolveHeartbeatSummaryForAgent(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
agentId?: string,
): HeartbeatSummary {
const defaults = cfg.agents?.defaults?.heartbeat;
@@ -260,7 +257,7 @@ export function resolveHeartbeatSummaryForAgent(
};
}
function resolveHeartbeatAgents(cfg: ClawdbotConfig): HeartbeatAgent[] {
function resolveHeartbeatAgents(cfg: MoltbotConfig): HeartbeatAgent[] {
const list = cfg.agents?.list ?? [];
if (hasExplicitHeartbeatAgents(cfg)) {
return list
@@ -276,7 +273,7 @@ function resolveHeartbeatAgents(cfg: ClawdbotConfig): HeartbeatAgent[] {
}
export function resolveHeartbeatIntervalMs(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
overrideEvery?: string,
heartbeat?: HeartbeatConfig,
) {
@@ -298,11 +295,11 @@ export function resolveHeartbeatIntervalMs(
return ms;
}
export function resolveHeartbeatPrompt(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
export function resolveHeartbeatPrompt(cfg: MoltbotConfig, heartbeat?: HeartbeatConfig) {
return resolveHeartbeatPromptText(heartbeat?.prompt ?? cfg.agents?.defaults?.heartbeat?.prompt);
}
function resolveHeartbeatAckMaxChars(cfg: ClawdbotConfig, heartbeat?: HeartbeatConfig) {
function resolveHeartbeatAckMaxChars(cfg: MoltbotConfig, heartbeat?: HeartbeatConfig) {
return Math.max(
0,
heartbeat?.ackMaxChars ??
@@ -312,7 +309,7 @@ function resolveHeartbeatAckMaxChars(cfg: ClawdbotConfig, heartbeat?: HeartbeatC
}
function resolveHeartbeatSession(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
agentId?: string,
heartbeat?: HeartbeatConfig,
) {
@@ -431,7 +428,7 @@ function normalizeHeartbeatReply(
}
export async function runHeartbeatOnce(opts: {
cfg?: ClawdbotConfig;
cfg?: MoltbotConfig;
agentId?: string;
heartbeat?: HeartbeatConfig;
reason?: string;
@@ -758,7 +755,7 @@ export async function runHeartbeatOnce(opts: {
}
export function startHeartbeatRunner(opts: {
cfg?: ClawdbotConfig;
cfg?: MoltbotConfig;
runtime?: RuntimeEnv;
abortSignal?: AbortSignal;
runOnce?: typeof runHeartbeatOnce;
@@ -804,7 +801,7 @@ export function startHeartbeatRunner(opts: {
state.timer.unref?.();
};
const updateConfig = (cfg: ClawdbotConfig) => {
const updateConfig = (cfg: MoltbotConfig) => {
if (state.stopped) return;
const now = Date.now();
const prevAgents = state.agents;

View File

@@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveHeartbeatVisibility } from "./heartbeat-visibility.js";
describe("resolveHeartbeatVisibility", () => {
it("returns default values when no config is provided", () => {
const cfg = {} as ClawdbotConfig;
const cfg = {} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "telegram" });
expect(result).toEqual({
@@ -25,7 +25,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "telegram" });
@@ -52,7 +52,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "telegram" });
@@ -88,7 +88,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({
cfg,
@@ -120,7 +120,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({
cfg,
@@ -151,7 +151,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "telegram" });
@@ -174,7 +174,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({
cfg,
@@ -195,7 +195,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "whatsapp" });
@@ -215,7 +215,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "discord" });
@@ -237,7 +237,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "slack" });
@@ -259,7 +259,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "webchat" });
@@ -271,7 +271,7 @@ describe("resolveHeartbeatVisibility", () => {
});
it("webchat returns defaults when no channel defaults configured", () => {
const cfg = {} as ClawdbotConfig;
const cfg = {} as MoltbotConfig;
const result = resolveHeartbeatVisibility({ cfg, channel: "webchat" });
@@ -291,7 +291,7 @@ describe("resolveHeartbeatVisibility", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = resolveHeartbeatVisibility({
cfg,

View File

@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import type { ChannelHeartbeatVisibilityConfig } from "../config/types.channels.js";
import type { GatewayMessageChannel } from "../utils/message-channel.js";
@@ -20,7 +20,7 @@ const DEFAULT_VISIBILITY: ResolvedHeartbeatVisibility = {
* For webchat, uses channels.defaults.heartbeat since webchat doesn't have per-channel config.
*/
export function resolveHeartbeatVisibility(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: GatewayMessageChannel;
accountId?: string;
}): ResolvedHeartbeatVisibility {

View File

@@ -28,7 +28,7 @@ describe("isMainModule", () => {
it("returns false when running under PM2 but this module is imported", () => {
expect(
isMainModule({
currentFile: "/repo/node_modules/clawdbot/dist/index.js",
currentFile: "/repo/node_modules/moltbot/dist/index.js",
argv: ["node", "/repo/app.js"],
cwd: "/repo",
env: { pm_exec_path: "/repo/app.js", pm_id: "0" },

View File

@@ -24,7 +24,7 @@ function fallbackHostName() {
os
.hostname()
.replace(/\.local$/i, "")
.trim() || "clawdbot"
.trim() || "moltbot"
);
}

View File

@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const CORE_PACKAGE_NAMES = new Set(["moltbot", "clawdbot"]);
const CORE_PACKAGE_NAMES = new Set(["moltbot", "moltbot"]);
async function readPackageName(dir: string): Promise<string | null> {
try {
@@ -39,7 +39,7 @@ function candidateDirsFromArgv1(argv1: string): string[] {
return candidates;
}
export async function resolveClawdbotPackageRoot(opts: {
export async function resolveMoltbotPackageRoot(opts: {
cwd?: string;
argv1?: string;
moduleUrl?: string;

View File

@@ -12,7 +12,7 @@ vi.mock("./targets.js", async () => {
};
});
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { resolveAgentDeliveryPlan, resolveAgentOutboundTarget } from "./agent-delivery.js";
describe("agent delivery helpers", () => {
@@ -45,7 +45,7 @@ describe("agent delivery helpers", () => {
});
const resolved = resolveAgentOutboundTarget({
cfg: {} as ClawdbotConfig,
cfg: {} as MoltbotConfig,
plan,
targetMode: "implicit",
});
@@ -68,7 +68,7 @@ describe("agent delivery helpers", () => {
mocks.resolveOutboundTarget.mockClear();
const resolved = resolveAgentOutboundTarget({
cfg: {} as ClawdbotConfig,
cfg: {} as MoltbotConfig,
plan,
targetMode: "explicit",
validateExplicitTarget: false,

View File

@@ -14,7 +14,7 @@ import {
resolveSessionDeliveryTarget,
type SessionDeliveryTarget,
} from "./targets.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import type { OutboundTargetResolution } from "./targets.js";
export type AgentDeliveryPlan = {
@@ -98,7 +98,7 @@ export function resolveAgentDeliveryPlan(params: {
}
export function resolveAgentOutboundTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
plan: AgentDeliveryPlan;
targetMode?: ChannelOutboundTargetMode;
validateExplicitTarget?: boolean;

View File

@@ -1,6 +1,6 @@
import { listChannelPlugins } from "../../channels/plugins/index.js";
import type { ChannelPlugin } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import {
listDeliverableMessageChannels,
type DeliverableMessageChannel,
@@ -21,7 +21,7 @@ function isAccountEnabled(account: unknown): boolean {
return enabled !== false;
}
async function isPluginConfigured(plugin: ChannelPlugin, cfg: ClawdbotConfig): Promise<boolean> {
async function isPluginConfigured(plugin: ChannelPlugin, cfg: MoltbotConfig): Promise<boolean> {
const accountIds = plugin.config.listAccountIds(cfg);
if (accountIds.length === 0) return false;
@@ -40,7 +40,7 @@ async function isPluginConfigured(plugin: ChannelPlugin, cfg: ClawdbotConfig): P
}
export async function listConfiguredMessageChannels(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
): Promise<MessageChannelId[]> {
const channels: MessageChannelId[] = [];
for (const plugin of listChannelPlugins()) {
@@ -53,7 +53,7 @@ export async function listConfiguredMessageChannels(
}
export async function resolveMessageChannelSelection(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel?: string | null;
}): Promise<{ channel: MessageChannelId; configured: MessageChannelId[] }> {
const normalized = normalizeMessageChannel(params.channel);

View File

@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { signalOutbound } from "../../channels/plugins/outbound/signal.js";
import { telegramOutbound } from "../../channels/plugins/outbound/telegram.js";
import { whatsappOutbound } from "../../channels/plugins/outbound/whatsapp.js";
@@ -38,7 +38,7 @@ describe("deliverOutboundPayloads", () => {
});
it("chunks telegram markdown and passes through accountId", async () => {
const sendTelegram = vi.fn().mockResolvedValue({ messageId: "m1", chatId: "c1" });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { telegram: { botToken: "tok-1", textChunkLimit: 2 } },
};
const prevTelegramToken = process.env.TELEGRAM_BOT_TOKEN;
@@ -71,7 +71,7 @@ describe("deliverOutboundPayloads", () => {
it("passes explicit accountId to sendTelegram", async () => {
const sendTelegram = vi.fn().mockResolvedValue({ messageId: "m1", chatId: "c1" });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { telegram: { botToken: "tok-1", textChunkLimit: 2 } },
};
@@ -93,7 +93,7 @@ describe("deliverOutboundPayloads", () => {
it("uses signal media maxBytes from config", async () => {
const sendSignal = vi.fn().mockResolvedValue({ messageId: "s1", timestamp: 123 });
const cfg: ClawdbotConfig = { channels: { signal: { mediaMaxMb: 2 } } };
const cfg: MoltbotConfig = { channels: { signal: { mediaMaxMb: 2 } } };
const results = await deliverOutboundPayloads({
cfg,
@@ -118,7 +118,7 @@ describe("deliverOutboundPayloads", () => {
it("chunks Signal markdown using the format-first chunker", async () => {
const sendSignal = vi.fn().mockResolvedValue({ messageId: "s1", timestamp: 123 });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { signal: { textChunkLimit: 20 } },
};
const text = `Intro\\n\\n\`\`\`\`md\\n${"y".repeat(60)}\\n\`\`\`\\n\\nOutro`;
@@ -152,7 +152,7 @@ describe("deliverOutboundPayloads", () => {
.fn()
.mockResolvedValueOnce({ messageId: "w1", toJid: "jid" })
.mockResolvedValueOnce({ messageId: "w2", toJid: "jid" });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { whatsapp: { textChunkLimit: 2 } },
};
@@ -170,7 +170,7 @@ describe("deliverOutboundPayloads", () => {
it("respects newline chunk mode for WhatsApp", async () => {
const sendWhatsApp = vi.fn().mockResolvedValue({ messageId: "w1", toJid: "jid" });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { whatsapp: { textChunkLimit: 4000, chunkMode: "newline" } },
};
@@ -229,7 +229,7 @@ describe("deliverOutboundPayloads", () => {
]),
);
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { matrix: { textChunkLimit: 4000, chunkMode: "newline" } },
};
const text = "```js\nconst a = 1;\nconst b = 2;\n```\nAfter";
@@ -256,7 +256,7 @@ describe("deliverOutboundPayloads", () => {
},
]),
);
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
agents: { defaults: { mediaMaxMb: 3 } },
};
@@ -293,7 +293,7 @@ describe("deliverOutboundPayloads", () => {
.mockRejectedValueOnce(new Error("fail"))
.mockResolvedValueOnce({ messageId: "w2", toJid: "jid" });
const onError = vi.fn();
const cfg: ClawdbotConfig = {};
const cfg: MoltbotConfig = {};
const results = await deliverOutboundPayloads({
cfg,
@@ -313,7 +313,7 @@ describe("deliverOutboundPayloads", () => {
it("passes normalized payload to onError", async () => {
const sendWhatsApp = vi.fn().mockRejectedValue(new Error("boom"));
const onError = vi.fn();
const cfg: ClawdbotConfig = {};
const cfg: MoltbotConfig = {};
await deliverOutboundPayloads({
cfg,
@@ -334,7 +334,7 @@ describe("deliverOutboundPayloads", () => {
it("mirrors delivered output when mirror options are provided", async () => {
const sendTelegram = vi.fn().mockResolvedValue({ messageId: "m1", chatId: "c1" });
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { telegram: { botToken: "tok-1", textChunkLimit: 2 } },
};
mocks.appendAssistantMessageToSessionTranscript.mockClear();

View File

@@ -8,7 +8,7 @@ import type { ReplyPayload } from "../../auto-reply/types.js";
import { resolveChannelMediaMaxBytes } from "../../channels/plugins/media-limits.js";
import { loadChannelOutboundAdapter } from "../../channels/plugins/outbound/load.js";
import type { ChannelOutboundAdapter } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { resolveMarkdownTableMode } from "../../config/markdown-tables.js";
import type { sendMessageDiscord } from "../../discord/send.js";
import type { sendMessageIMessage } from "../../imessage/send.js";
@@ -82,7 +82,7 @@ function throwIfAborted(abortSignal?: AbortSignal): void {
// Channel docking: outbound delivery delegates to plugin.outbound adapters.
async function createChannelHandler(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: Exclude<OutboundChannel, "none">;
to: string;
accountId?: string;
@@ -114,7 +114,7 @@ async function createChannelHandler(params: {
function createPluginHandler(params: {
outbound?: ChannelOutboundAdapter;
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: Exclude<OutboundChannel, "none">;
to: string;
accountId?: string;
@@ -175,7 +175,7 @@ function createPluginHandler(params: {
}
export async function deliverOutboundPayloads(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: Exclude<OutboundChannel, "none">;
to: string;
accountId?: string;

View File

@@ -1,5 +1,5 @@
import type { ChannelDirectoryEntryKind, ChannelId } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
type CacheEntry<T> = {
value: T;
@@ -21,11 +21,11 @@ export function buildDirectoryCacheKey(key: DirectoryCacheKey): string {
export class DirectoryCache<T> {
private readonly cache = new Map<string, CacheEntry<T>>();
private lastConfigRef: ClawdbotConfig | null = null;
private lastConfigRef: MoltbotConfig | null = null;
constructor(private readonly ttlMs: number) {}
get(key: string, cfg: ClawdbotConfig): T | undefined {
get(key: string, cfg: MoltbotConfig): T | undefined {
this.resetIfConfigChanged(cfg);
const entry = this.cache.get(key);
if (!entry) return undefined;
@@ -36,7 +36,7 @@ export class DirectoryCache<T> {
return entry.value;
}
set(key: string, value: T, cfg: ClawdbotConfig): void {
set(key: string, value: T, cfg: MoltbotConfig): void {
this.resetIfConfigChanged(cfg);
this.cache.set(key, { value, fetchedAt: Date.now() });
}
@@ -47,12 +47,12 @@ export class DirectoryCache<T> {
}
}
clear(cfg?: ClawdbotConfig): void {
clear(cfg?: MoltbotConfig): void {
this.cache.clear();
if (cfg) this.lastConfigRef = cfg;
}
private resetIfConfigChanged(cfg: ClawdbotConfig): void {
private resetIfConfigChanged(cfg: MoltbotConfig): void {
if (this.lastConfigRef && this.lastConfigRef !== cfg) {
this.cache.clear();
}

View File

@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { createIMessageTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
import { slackPlugin } from "../../../extensions/slack/src/channel.js";
@@ -26,7 +26,7 @@ const slackConfig = {
appToken: "xapp-test",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const whatsappConfig = {
channels: {
@@ -34,7 +34,7 @@ const whatsappConfig = {
allowFrom: ["*"],
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
describe("runMessageAction context isolation", () => {
beforeEach(async () => {
@@ -263,7 +263,7 @@ describe("runMessageAction context isolation", () => {
token: "tg-test",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = await runMessageAction({
cfg: multiConfig,
@@ -305,7 +305,7 @@ describe("runMessageAction context isolation", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
await expect(
runMessageAction({
@@ -423,7 +423,7 @@ describe("runMessageAction sendAttachment hydration", () => {
password: "test-password",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const result = await runMessageAction({
cfg,
@@ -491,7 +491,7 @@ describe("runMessageAction accountId defaults", () => {
it("propagates defaultAccountId into params", async () => {
await runMessageAction({
cfg: {} as ClawdbotConfig,
cfg: {} as MoltbotConfig,
action: "send",
params: {
channel: "discord",

View File

@@ -1,6 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
import { slackPlugin } from "../../../extensions/slack/src/channel.js";
@@ -39,7 +39,7 @@ const slackConfig = {
appToken: "xapp-test",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
describe("runMessageAction Slack threading", () => {
beforeEach(async () => {

View File

@@ -15,7 +15,7 @@ import type {
ChannelMessageActionName,
ChannelThreadingToolContext,
} from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import {
isDeliverableMessageChannel,
normalizeMessageChannel,
@@ -54,7 +54,7 @@ export type MessageActionRunnerGateway = {
};
export type RunMessageActionParams = {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
action: ChannelMessageActionName;
params: Record<string, unknown>;
defaultAccountId?: string;
@@ -168,7 +168,7 @@ function applyCrossContextMessageDecoration({
}
async function maybeApplyCrossContextMarker(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
action: ChannelMessageActionName;
target: string;
@@ -224,7 +224,7 @@ function resolveSlackAutoThreadId(params: {
}
function resolveAttachmentMaxBytes(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
accountId?: string | null;
}): number | undefined {
@@ -298,7 +298,7 @@ function normalizeBase64Payload(params: { base64?: string; contentType?: string
}
async function hydrateSetGroupIconParams(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
accountId?: string | null;
args: Record<string, unknown>;
@@ -355,7 +355,7 @@ async function hydrateSetGroupIconParams(params: {
}
async function hydrateSendAttachmentParams(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
accountId?: string | null;
args: Record<string, unknown>;
@@ -444,7 +444,7 @@ function parseCardParam(params: Record<string, unknown>): void {
}
}
async function resolveChannel(cfg: ClawdbotConfig, params: Record<string, unknown>) {
async function resolveChannel(cfg: MoltbotConfig, params: Record<string, unknown>) {
const channelHint = readStringParam(params, "channel");
const selection = await resolveMessageChannelSelection({
cfg,
@@ -454,7 +454,7 @@ async function resolveChannel(cfg: ClawdbotConfig, params: Record<string, unknow
}
async function resolveActionTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
action: ChannelMessageActionName;
args: Record<string, unknown>;
@@ -499,7 +499,7 @@ async function resolveActionTarget(params: {
}
type ResolvedActionContext = {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
params: Record<string, unknown>;
channel: ChannelId;
accountId?: string | null;

View File

@@ -1,6 +1,6 @@
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
import type { ChannelId } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { loadConfig } from "../../config/config.js";
import { callGateway, randomIdempotencyKey } from "../../gateway/call.js";
import type { PollInput } from "../../polls.js";
@@ -41,7 +41,7 @@ type MessageSendParams = {
dryRun?: boolean;
bestEffort?: boolean;
deps?: OutboundSendDeps;
cfg?: ClawdbotConfig;
cfg?: MoltbotConfig;
gateway?: MessageGatewayOptions;
idempotencyKey?: string;
mirror?: {
@@ -71,7 +71,7 @@ type MessagePollParams = {
durationHours?: number;
channel?: string;
dryRun?: boolean;
cfg?: ClawdbotConfig;
cfg?: MoltbotConfig;
gateway?: MessageGatewayOptions;
idempotencyKey?: string;
};

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import {
applyCrossContextDecoration,
buildCrossContextDecoration,
@@ -14,13 +14,13 @@ const slackConfig = {
appToken: "xapp-test",
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const discordConfig = {
channels: {
discord: {},
},
} as ClawdbotConfig;
} as MoltbotConfig;
describe("outbound policy", () => {
it("blocks cross-provider sends by default", () => {
@@ -41,7 +41,7 @@ describe("outbound policy", () => {
tools: {
message: { crossContext: { allowAcrossProviders: true } },
},
} as ClawdbotConfig;
} as MoltbotConfig;
expect(() =>
enforceCrossContextPolicy({
@@ -58,7 +58,7 @@ describe("outbound policy", () => {
const cfg = {
...slackConfig,
tools: { message: { crossContext: { allowWithinProvider: false } } },
} as ClawdbotConfig;
} as MoltbotConfig;
expect(() =>
enforceCrossContextPolicy({

View File

@@ -4,7 +4,7 @@ import type {
ChannelMessageActionName,
ChannelThreadingToolContext,
} from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { getChannelMessageAdapter } from "./channel-adapters.js";
import { formatTargetDisplay, lookupDirectoryDisplay } from "./target-resolver.js";
@@ -74,7 +74,7 @@ export function enforceCrossContextPolicy(params: {
action: ChannelMessageActionName;
args: Record<string, unknown>;
toolContext?: ChannelThreadingToolContext;
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
}): void {
const currentTarget = params.toolContext?.currentChannelId?.trim();
if (!currentTarget) return;
@@ -112,7 +112,7 @@ export function enforceCrossContextPolicy(params: {
}
export async function buildCrossContextDecoration(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
target: string;
toolContext?: ChannelThreadingToolContext;

View File

@@ -1,7 +1,7 @@
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import { dispatchChannelMessageAction } from "../../channels/plugins/message-actions.js";
import type { ChannelId, ChannelThreadingToolContext } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { appendAssistantMessageToSessionTranscript } from "../../config/sessions.js";
import type { GatewayClientMode, GatewayClientName } from "../../utils/message-channel.js";
import type { OutboundSendDeps } from "./deliver.js";
@@ -18,7 +18,7 @@ export type OutboundGatewayContext = {
};
export type OutboundSendContext = {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
params: Record<string, unknown>;
accountId?: string | null;

View File

@@ -1,9 +1,9 @@
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { resolveOutboundSessionRoute } from "./outbound-session.js";
const baseConfig = {} as ClawdbotConfig;
const baseConfig = {} as MoltbotConfig;
describe("resolveOutboundSessionRoute", () => {
it("builds Slack thread session keys", async () => {
@@ -36,7 +36,7 @@ describe("resolveOutboundSessionRoute", () => {
});
it("treats Telegram usernames as DMs when unresolved", async () => {
const cfg = { session: { dmScope: "per-channel-peer" } } as ClawdbotConfig;
const cfg = { session: { dmScope: "per-channel-peer" } } as MoltbotConfig;
const route = await resolveOutboundSessionRoute({
cfg,
channel: "telegram",
@@ -56,7 +56,7 @@ describe("resolveOutboundSessionRoute", () => {
alice: ["discord:123"],
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const route = await resolveOutboundSessionRoute({
cfg,
@@ -81,7 +81,7 @@ describe("resolveOutboundSessionRoute", () => {
});
it("treats Zalo Personal DM targets as direct sessions", async () => {
const cfg = { session: { dmScope: "per-channel-peer" } } as ClawdbotConfig;
const cfg = { session: { dmScope: "per-channel-peer" } } as MoltbotConfig;
const route = await resolveOutboundSessionRoute({
cfg,
channel: "zalouser",
@@ -102,7 +102,7 @@ describe("resolveOutboundSessionRoute", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const route = await resolveOutboundSessionRoute({
cfg,

View File

@@ -1,7 +1,7 @@
import type { MsgContext } from "../../auto-reply/templating.js";
import { getChannelPlugin } from "../../channels/plugins/index.js";
import type { ChannelId } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { recordSessionMetaFromInbound, resolveStorePath } from "../../config/sessions.js";
import { parseDiscordTarget } from "../../discord/targets.js";
import { parseIMessageTarget, normalizeIMessageHandle } from "../../imessage/targets.js";
@@ -37,7 +37,7 @@ export type OutboundSessionRoute = {
};
export type ResolveOutboundSessionRouteParams = {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
agentId: string;
accountId?: string | null;
@@ -100,7 +100,7 @@ function inferPeerKind(params: {
}
function buildBaseSessionKey(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
agentId: string;
channel: ChannelId;
peer: RoutePeer;
@@ -116,7 +116,7 @@ function buildBaseSessionKey(params: {
// Best-effort mpim detection: allowlist/config, then Slack API (if token available).
async function resolveSlackChannelType(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
accountId?: string | null;
channelId: string;
}): Promise<"channel" | "group" | "dm" | "unknown"> {
@@ -805,7 +805,7 @@ export async function resolveOutboundSessionRoute(
}
export async function ensureOutboundSessionEntry(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
agentId: string;
channel: ChannelId;
accountId?: string | null;

View File

@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { ChannelDirectoryEntry } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { resetDirectoryCache, resolveMessagingTarget } from "./target-resolver.js";
const mocks = vi.hoisted(() => ({
@@ -16,7 +16,7 @@ vi.mock("../../channels/plugins/index.js", () => ({
}));
describe("resolveMessagingTarget (directory fallback)", () => {
const cfg = {} as ClawdbotConfig;
const cfg = {} as MoltbotConfig;
beforeEach(() => {
mocks.listGroups.mockReset();

View File

@@ -4,7 +4,7 @@ import type {
ChannelDirectoryEntryKind,
ChannelId,
} from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
import { buildDirectoryCacheKey, DirectoryCache } from "./directory-cache.js";
import {
@@ -30,7 +30,7 @@ export type ResolveMessagingTargetResult =
| { ok: false; error: Error; candidates?: ChannelDirectoryEntry[] };
export async function resolveChannelTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
input: string;
accountId?: string | null;
@@ -177,7 +177,7 @@ function resolveMatch(params: {
}
async function listDirectoryEntries(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
accountId?: string | null;
kind: ChannelDirectoryEntryKind;
@@ -213,7 +213,7 @@ async function listDirectoryEntries(params: {
}
async function getDirectoryEntries(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
accountId?: string | null;
kind: ChannelDirectoryEntryKind;
@@ -281,7 +281,7 @@ function pickAmbiguousMatch(
}
export async function resolveMessagingTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
input: string;
accountId?: string | null;
@@ -397,7 +397,7 @@ export async function resolveMessagingTarget(params: {
}
export async function lookupDirectoryDisplay(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: ChannelId;
targetId: string;
accountId?: string | null;

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import { setActivePluginRegistry } from "../../plugins/runtime.js";
import { createTestRegistry } from "../../test-utils/channel-plugins.js";
@@ -18,7 +18,7 @@ describe("resolveOutboundTarget", () => {
});
it("falls back to whatsapp allowFrom via config", () => {
const cfg: ClawdbotConfig = {
const cfg: MoltbotConfig = {
channels: { whatsapp: { allowFrom: ["+1555"] } },
};
const res = resolveOutboundTarget({

View File

@@ -1,7 +1,7 @@
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
import { formatCliCommand } from "../../cli/command-format.js";
import type { ChannelId, ChannelOutboundTargetMode } from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { MoltbotConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions.js";
import type { AgentDefaultsConfig } from "../../config/types.agent-defaults.js";
import { deliveryContextFromSession } from "../../utils/delivery-context.js";
@@ -119,7 +119,7 @@ export function resolveOutboundTarget(params: {
channel: GatewayMessageChannel;
to?: string;
allowFrom?: string[];
cfg?: ClawdbotConfig;
cfg?: MoltbotConfig;
accountId?: string | null;
mode?: ChannelOutboundTargetMode;
}): OutboundTargetResolution {
@@ -127,7 +127,7 @@ export function resolveOutboundTarget(params: {
return {
ok: false,
error: new Error(
`Delivering to WebChat is not supported via \`${formatCliCommand("clawdbot agent")}\`; use WhatsApp/Telegram or run with --deliver=false.`,
`Delivering to WebChat is not supported via \`${formatCliCommand("moltbot agent")}\`; use WhatsApp/Telegram or run with --deliver=false.`,
),
};
}
@@ -172,7 +172,7 @@ export function resolveOutboundTarget(params: {
}
export function resolveHeartbeatDeliveryTarget(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
entry?: SessionEntry;
heartbeat?: AgentDefaultsConfig["heartbeat"];
}): OutboundTarget {
@@ -289,7 +289,7 @@ function resolveHeartbeatSenderId(params: {
}
export function resolveHeartbeatSenderContext(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
entry?: SessionEntry;
delivery: OutboundTarget;
}): HeartbeatSenderContext {

View File

@@ -4,15 +4,15 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
import { ensureClawdbotCliOnPath } from "./path-env.js";
import { ensureMoltbotCliOnPath } from "./path-env.js";
describe("ensureClawdbotCliOnPath", () => {
it("prepends the bundled app bin dir when a sibling clawdbot exists", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-path-"));
describe("ensureMoltbotCliOnPath", () => {
it("prepends the bundled app bin dir when a sibling moltbot exists", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-path-"));
try {
const appBinDir = path.join(tmp, "AppBin");
await fs.mkdir(appBinDir, { recursive: true });
const cliPath = path.join(appBinDir, "clawdbot");
const cliPath = path.join(appBinDir, "moltbot");
await fs.writeFile(cliPath, "#!/bin/sh\necho ok\n", "utf-8");
await fs.chmod(cliPath, 0o755);
@@ -21,7 +21,7 @@ describe("ensureClawdbotCliOnPath", () => {
process.env.PATH = "/usr/bin";
delete process.env.CLAWDBOT_PATH_BOOTSTRAPPED;
try {
ensureClawdbotCliOnPath({
ensureMoltbotCliOnPath({
execPath: cliPath,
cwd: tmp,
homeDir: tmp,
@@ -45,7 +45,7 @@ describe("ensureClawdbotCliOnPath", () => {
process.env.PATH = "/bin";
process.env.CLAWDBOT_PATH_BOOTSTRAPPED = "1";
try {
ensureClawdbotCliOnPath({
ensureMoltbotCliOnPath({
execPath: "/tmp/does-not-matter",
cwd: "/tmp",
homeDir: "/tmp",
@@ -60,20 +60,20 @@ describe("ensureClawdbotCliOnPath", () => {
});
it("prepends mise shims when available", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-path-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-path-"));
const originalPath = process.env.PATH;
const originalFlag = process.env.CLAWDBOT_PATH_BOOTSTRAPPED;
const originalMiseDataDir = process.env.MISE_DATA_DIR;
try {
const appBinDir = path.join(tmp, "AppBin");
await fs.mkdir(appBinDir, { recursive: true });
const appCli = path.join(appBinDir, "clawdbot");
const appCli = path.join(appBinDir, "moltbot");
await fs.writeFile(appCli, "#!/bin/sh\necho ok\n", "utf-8");
await fs.chmod(appCli, 0o755);
const localBinDir = path.join(tmp, "node_modules", ".bin");
await fs.mkdir(localBinDir, { recursive: true });
const localCli = path.join(localBinDir, "clawdbot");
const localCli = path.join(localBinDir, "moltbot");
await fs.writeFile(localCli, "#!/bin/sh\necho ok\n", "utf-8");
await fs.chmod(localCli, 0o755);
@@ -84,7 +84,7 @@ describe("ensureClawdbotCliOnPath", () => {
process.env.PATH = "/usr/bin";
delete process.env.CLAWDBOT_PATH_BOOTSTRAPPED;
ensureClawdbotCliOnPath({
ensureMoltbotCliOnPath({
execPath: appCli,
cwd: tmp,
homeDir: tmp,
@@ -110,7 +110,7 @@ describe("ensureClawdbotCliOnPath", () => {
});
it("prepends Linuxbrew dirs when present", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-path-"));
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-path-"));
const originalPath = process.env.PATH;
const originalFlag = process.env.CLAWDBOT_PATH_BOOTSTRAPPED;
const originalHomebrewPrefix = process.env.HOMEBREW_PREFIX;
@@ -131,7 +131,7 @@ describe("ensureClawdbotCliOnPath", () => {
delete process.env.HOMEBREW_BREW_FILE;
delete process.env.XDG_BIN_HOME;
ensureClawdbotCliOnPath({
ensureMoltbotCliOnPath({
execPath: path.join(execDir, "node"),
cwd: tmp,
homeDir: tmp,

View File

@@ -5,7 +5,7 @@ import { isTruthyEnvValue } from "./env.js";
import { resolveBrewPathDirs } from "./brew.js";
type EnsureClawdbotPathOpts = {
type EnsureMoltbotPathOpts = {
execPath?: string;
cwd?: string;
homeDir?: string;
@@ -48,7 +48,7 @@ function mergePath(params: { existing: string; prepend: string[] }): string {
return merged.join(path.delimiter);
}
function candidateBinDirs(opts: EnsureClawdbotPathOpts): string[] {
function candidateBinDirs(opts: EnsureMoltbotPathOpts): string[] {
const execPath = opts.execPath ?? process.execPath;
const cwd = opts.cwd ?? process.cwd();
const homeDir = opts.homeDir ?? os.homedir();
@@ -56,19 +56,19 @@ function candidateBinDirs(opts: EnsureClawdbotPathOpts): string[] {
const candidates: string[] = [];
// Bundled macOS app: `clawdbot` lives next to the executable (process.execPath).
// Bundled macOS app: `moltbot` lives next to the executable (process.execPath).
try {
const execDir = path.dirname(execPath);
const siblingClawdbot = path.join(execDir, "clawdbot");
if (isExecutable(siblingClawdbot)) candidates.push(execDir);
const siblingMoltbot = path.join(execDir, "moltbot");
if (isExecutable(siblingMoltbot)) candidates.push(execDir);
} catch {
// ignore
}
// Project-local installs (best effort): if a `node_modules/.bin/clawdbot` exists near cwd,
// Project-local installs (best effort): if a `node_modules/.bin/moltbot` exists near cwd,
// include it. This helps when running under launchd or other minimal PATH environments.
const localBinDir = path.join(cwd, "node_modules", ".bin");
if (isExecutable(path.join(localBinDir, "clawdbot"))) candidates.push(localBinDir);
if (isExecutable(path.join(localBinDir, "moltbot"))) candidates.push(localBinDir);
const miseDataDir = process.env.MISE_DATA_DIR ?? path.join(homeDir, ".local", "share", "mise");
const miseShims = path.join(miseDataDir, "shims");
@@ -91,10 +91,10 @@ function candidateBinDirs(opts: EnsureClawdbotPathOpts): string[] {
}
/**
* Best-effort PATH bootstrap so skills that require the `clawdbot` CLI can run
* Best-effort PATH bootstrap so skills that require the `moltbot` CLI can run
* under launchd/minimal environments (and inside the macOS app bundle).
*/
export function ensureClawdbotCliOnPath(opts: EnsureClawdbotPathOpts = {}) {
export function ensureMoltbotCliOnPath(opts: EnsureMoltbotPathOpts = {}) {
if (isTruthyEnvValue(process.env.CLAWDBOT_PATH_BOOTSTRAPPED)) return;
process.env.CLAWDBOT_PATH_BOOTSTRAPPED = "1";

View File

@@ -3,7 +3,7 @@ import type { PortListener, PortListenerKind, PortUsage } from "./ports-types.js
export function classifyPortListener(listener: PortListener, port: number): PortListenerKind {
const raw = `${listener.commandLine ?? ""} ${listener.command ?? ""}`.trim().toLowerCase();
if (raw.includes("clawdbot")) return "gateway";
if (raw.includes("moltbot")) return "gateway";
if (raw.includes("ssh")) {
const portToken = String(port);
const tunnelPattern = new RegExp(
@@ -21,7 +21,7 @@ export function buildPortHints(listeners: PortListener[], port: number): string[
const hints: string[] = [];
if (kinds.has("gateway")) {
hints.push(
`Gateway already running locally. Stop it (${formatCliCommand("clawdbot gateway stop")}) or use a different port.`,
`Gateway already running locally. Stop it (${formatCliCommand("moltbot gateway stop")}) or use a different port.`,
);
}
if (kinds.has("ssh")) {

View File

@@ -37,7 +37,7 @@ describe("ports helpers", () => {
expect(
classifyPortListener(
{
commandLine: "node /Users/me/Projects/clawdbot/dist/entry.js gateway",
commandLine: "node /Users/me/Projects/moltbot/dist/entry.js gateway",
},
18789,
),

View File

@@ -63,10 +63,10 @@ export async function handlePortError(
if (details) {
runtime.error(info("Port listener details:"));
runtime.error(details);
if (/clawdbot|src\/index\.ts|dist\/index\.js/.test(details)) {
if (/moltbot|src\/index\.ts|dist\/index\.js/.test(details)) {
runtime.error(
warn(
"It looks like another clawdbot instance is already running. Stop it or pick a different port.",
"It looks like another moltbot instance is already running. Stop it or pick a different port.",
),
);
}

View File

@@ -104,7 +104,7 @@ export async function fetchClaudeUsage(
{
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "clawdbot",
"User-Agent": "moltbot",
Accept: "application/json",
"anthropic-version": "2023-06-01",
"anthropic-beta": "oauth-2025-04-20",

View File

@@ -289,7 +289,7 @@ export async function fetchMinimaxUsage(
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"MM-API-Source": "Clawdbot",
"MM-API-Source": "Moltbot",
},
},
timeoutMs,

View File

@@ -330,7 +330,7 @@ describe("provider usage loading", () => {
env: {
CLAWDBOT_STATE_DIR: (home) => path.join(home, ".clawdbot"),
},
prefix: "clawdbot-provider-usage-",
prefix: "moltbot-provider-usage-",
},
);
});

View File

@@ -17,7 +17,7 @@ describe("restart sentinel", () => {
beforeEach(async () => {
prevStateDir = process.env.CLAWDBOT_STATE_DIR;
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sentinel-"));
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sentinel-"));
process.env.CLAWDBOT_STATE_DIR = tempDir;
});

View File

@@ -56,7 +56,7 @@ const SENTINEL_FILENAME = "restart-sentinel.json";
export function formatDoctorNonInteractiveHint(
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
): string {
return `Run: ${formatCliCommand("clawdbot doctor --non-interactive", env)}`;
return `Run: ${formatCliCommand("moltbot doctor --non-interactive", env)}`;
}
export function resolveRestartSentinelPath(env: NodeJS.ProcessEnv = process.env): string {

View File

@@ -87,7 +87,7 @@ function normalizeSystemdUnit(raw?: string, profile?: string): string {
return unit.endsWith(".service") ? unit : `${unit}.service`;
}
export function triggerClawdbotRestart(): RestartAttempt {
export function triggerMoltbotRestart(): RestartAttempt {
if (process.env.VITEST || process.env.NODE_ENV === "test") {
return { ok: true, method: "supervisor", detail: "test mode" };
}

View File

@@ -75,11 +75,11 @@ export function assertSupportedRuntime(
runtime.error(
[
"clawdbot requires Node >=22.0.0.",
"moltbot requires Node >=22.0.0.",
`Detected: ${runtimeLabel} (exec: ${execLabel}).`,
`PATH searched: ${details.pathEnv}`,
"Install Node: https://nodejs.org/en/download",
"Upgrade Node and re-run clawdbot.",
"Upgrade Node and re-run moltbot.",
].join("\n"),
);
runtime.exit(1);

View File

@@ -4,12 +4,12 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { loadCostUsageSummary, loadSessionCostSummary } from "./session-cost-usage.js";
describe("session cost usage", () => {
it("aggregates daily totals with log cost and pricing fallback", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-cost-"));
const root = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-cost-"));
const sessionsDir = path.join(root, "agents", "main", "sessions");
await fs.mkdir(sessionsDir, { recursive: true });
const sessionFile = path.join(sessionsDir, "sess-1.jsonl");
@@ -92,7 +92,7 @@ describe("session cost usage", () => {
},
},
},
} as ClawdbotConfig;
} as MoltbotConfig;
const originalState = process.env.CLAWDBOT_STATE_DIR;
process.env.CLAWDBOT_STATE_DIR = root;
@@ -108,7 +108,7 @@ describe("session cost usage", () => {
});
it("summarizes a single session file", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-cost-session-"));
const root = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-cost-session-"));
const sessionFile = path.join(root, "session.jsonl");
const now = new Date();

View File

@@ -4,7 +4,7 @@ import readline from "node:readline";
import type { NormalizedUsage, UsageLike } from "../agents/usage.js";
import { normalizeUsage } from "../agents/usage.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import type { SessionEntry } from "../config/sessions/types.js";
import {
resolveSessionFilePath,
@@ -138,7 +138,7 @@ const applyCostTotal = (totals: CostUsageTotals, costTotal: number | undefined)
async function scanUsageFile(params: {
filePath: string;
config?: ClawdbotConfig;
config?: MoltbotConfig;
onEntry: (entry: ParsedUsageEntry) => void;
}): Promise<void> {
const fileStream = fs.createReadStream(params.filePath, { encoding: "utf-8" });
@@ -170,7 +170,7 @@ async function scanUsageFile(params: {
export async function loadCostUsageSummary(params?: {
days?: number;
config?: ClawdbotConfig;
config?: MoltbotConfig;
agentId?: string;
}): Promise<CostUsageSummary> {
const days = Math.max(1, Math.floor(params?.days ?? 30));
@@ -233,7 +233,7 @@ export async function loadSessionCostSummary(params: {
sessionId?: string;
sessionEntry?: SessionEntry;
sessionFile?: string;
config?: ClawdbotConfig;
config?: MoltbotConfig;
}): Promise<SessionCostSummary | null> {
const sessionFile =
params.sessionFile ??

View File

@@ -74,7 +74,7 @@ export function loadShellEnvFallback(opts: ShellEnvFallbackOptions): ShellEnvFal
});
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
logger.warn(`[clawdbot] shell env fallback failed: ${msg}`);
logger.warn(`[moltbot] shell env fallback failed: ${msg}`);
lastAppliedKeys = [];
return { ok: false, error: msg, applied: [] };
}

View File

@@ -1,7 +1,7 @@
import type { SkillEligibilityContext, SkillEntry } from "../agents/skills.js";
import { loadWorkspaceSkillEntries } from "../agents/skills.js";
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { listNodePairing, updatePairedNodeMetadata } from "./node-pairing.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { bumpSkillsSnapshotVersion } from "../agents/skills/refresh.js";
@@ -156,7 +156,7 @@ export function recordRemoteNodeBins(nodeId: string, bins: string[]) {
upsertNode({ nodeId, bins });
}
function listWorkspaceDirs(cfg: ClawdbotConfig): string[] {
function listWorkspaceDirs(cfg: MoltbotConfig): string[] {
const dirs = new Set<string>();
const list = cfg.agents?.list;
if (Array.isArray(list)) {
@@ -173,10 +173,10 @@ function listWorkspaceDirs(cfg: ClawdbotConfig): string[] {
function collectRequiredBins(entries: SkillEntry[], targetPlatform: string): string[] {
const bins = new Set<string>();
for (const entry of entries) {
const os = entry.clawdbot?.os ?? [];
const os = entry.metadata?.os ?? [];
if (os.length > 0 && !os.includes(targetPlatform)) continue;
const required = entry.clawdbot?.requires?.bins ?? [];
const anyBins = entry.clawdbot?.requires?.anyBins ?? [];
const required = entry.metadata?.requires?.bins ?? [];
const anyBins = entry.metadata?.requires?.anyBins ?? [];
for (const bin of required) {
if (bin.trim()) bins.add(bin.trim());
}
@@ -227,7 +227,7 @@ export async function refreshRemoteNodeBins(params: {
platform?: string;
deviceFamily?: string;
commands?: string[];
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
timeoutMs?: number;
}) {
if (!remoteRegistry) return;
@@ -304,7 +304,7 @@ export function getRemoteSkillEligibility(): SkillEligibilityContext["remote"] |
};
}
export async function refreshRemoteBinsForConnectedNodes(cfg: ClawdbotConfig) {
export async function refreshRemoteBinsForConnectedNodes(cfg: MoltbotConfig) {
if (!remoteRegistry) return;
const connected = remoteRegistry.listConnected();
for (const node of connected) {

View File

@@ -7,7 +7,7 @@ import { readSessionStoreJson5 } from "./state-migrations.fs.js";
describe("state migrations fs", () => {
it("treats array session stores as invalid", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-session-store-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-session-store-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "[]", "utf-8");

View File

@@ -3,7 +3,7 @@ import os from "node:os";
import path from "node:path";
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveOAuthDir, resolveStateDir } from "../config/paths.js";
import type { SessionEntry } from "../config/sessions.js";
import type { SessionScope } from "../config/sessions/types.js";
@@ -268,7 +268,7 @@ export function resetAutoMigrateLegacyAgentDirForTest() {
}
export async function detectLegacyStateMigrations(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
env?: NodeJS.ProcessEnv;
homedir?: () => string;
}): Promise<LegacyStateDetection> {
@@ -559,7 +559,7 @@ export async function runLegacyStateMigrations(params: {
}
export async function autoMigrateLegacyAgentDir(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
env?: NodeJS.ProcessEnv;
homedir?: () => string;
log?: MigrationLogger;
@@ -574,7 +574,7 @@ export async function autoMigrateLegacyAgentDir(params: {
}
export async function autoMigrateLegacyState(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
env?: NodeJS.ProcessEnv;
homedir?: () => string;
log?: MigrationLogger;

View File

@@ -1,11 +1,11 @@
import { beforeEach, describe, expect, it } from "vitest";
import { prependSystemEvents } from "../auto-reply/reply/session-updates.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { MoltbotConfig } from "../config/config.js";
import { resolveMainSessionKey } from "../config/sessions.js";
import { enqueueSystemEvent, peekSystemEvents, resetSystemEventsForTest } from "./system-events.js";
const cfg = {} as unknown as ClawdbotConfig;
const cfg = {} as unknown as MoltbotConfig;
const mainKey = resolveMainSessionKey(cfg);
describe("system events (session routing)", () => {

View File

@@ -8,7 +8,7 @@ describe("system-presence", () => {
const instanceIdLower = instanceIdUpper.toLowerCase();
upsertPresence(instanceIdUpper, {
host: "clawdbot",
host: "moltbot",
mode: "ui",
instanceId: instanceIdUpper,
reason: "connect",
@@ -39,7 +39,7 @@ describe("system-presence", () => {
upsertPresence(deviceId, {
deviceId,
host: "clawdbot",
host: "moltbot",
roles: ["operator"],
scopes: ["operator.admin"],
reason: "connect",

View File

@@ -345,7 +345,7 @@ export async function ensureFunnel(
runtime.error("Failed to enable Tailscale Funnel. Is it allowed on your tailnet?");
runtime.error(
info(
`Tip: Funnel is optional for CLAWDBOT. You can keep running the web gateway without it: \`${formatCliCommand("clawdbot gateway")}\``,
`Tip: Funnel is optional for Moltbot. You can keep running the web gateway without it: \`${formatCliCommand("moltbot gateway")}\``,
),
);
if (shouldLogVerbose()) {

View File

@@ -56,7 +56,7 @@ async function generateSelfSignedCert(params: {
"-out",
params.certPath,
"-subj",
"/CN=clawdbot-gateway",
"/CN=moltbot-gateway",
]);
await fs.chmod(params.keyPath, 0o600).catch(() => {});
await fs.chmod(params.certPath, 0o600).catch(() => {});

View File

@@ -96,7 +96,7 @@ export function isUnhandledRejectionHandled(reason: unknown): boolean {
if (handler(reason)) return true;
} catch (err) {
console.error(
"[clawdbot] Unhandled rejection handler failed:",
"[moltbot] Unhandled rejection handler failed:",
err instanceof Error ? (err.stack ?? err.message) : err,
);
}
@@ -111,18 +111,18 @@ export function installUnhandledRejectionHandler(): void {
// AbortError is typically an intentional cancellation (e.g., during shutdown)
// Log it but don't crash - these are expected during graceful shutdown
if (isAbortError(reason)) {
console.warn("[clawdbot] Suppressed AbortError:", formatUncaughtError(reason));
console.warn("[moltbot] Suppressed AbortError:", formatUncaughtError(reason));
return;
}
// Transient network errors (fetch failed, connection reset, etc.) shouldn't crash
// These are temporary connectivity issues that will resolve on their own
if (isTransientNetworkError(reason)) {
console.error("[clawdbot] Network error (non-fatal):", formatUncaughtError(reason));
console.error("[moltbot] Network error (non-fatal):", formatUncaughtError(reason));
return;
}
console.error("[clawdbot] Unhandled promise rejection:", formatUncaughtError(reason));
console.error("[moltbot] Unhandled promise rejection:", formatUncaughtError(reason));
process.exit(1);
});
}

View File

@@ -303,7 +303,7 @@ export async function fetchNpmTagVersion(params: {
const tag = params.tag;
try {
const res = await fetchWithTimeout(
`https://registry.npmjs.org/clawdbot/${encodeURIComponent(tag)}`,
`https://registry.npmjs.org/moltbot/${encodeURIComponent(tag)}`,
timeoutMs,
);
if (!res.ok) {

View File

@@ -51,7 +51,7 @@ export async function resolveGlobalPackageRoot(
): Promise<string | null> {
const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);
if (!root) return null;
return path.join(root, "clawdbot");
return path.join(root, "moltbot");
}
export async function detectGlobalInstallManagerForRoot(
@@ -75,13 +75,13 @@ export async function detectGlobalInstallManagerForRoot(
const globalRoot = res.stdout.trim();
if (!globalRoot) continue;
const globalReal = await tryRealpath(globalRoot);
const expected = path.join(globalReal, "clawdbot");
const expected = path.join(globalReal, "moltbot");
if (path.resolve(expected) === path.resolve(pkgReal)) return manager;
}
const bunGlobalRoot = resolveBunGlobalRoot();
const bunGlobalReal = await tryRealpath(bunGlobalRoot);
const bunExpected = path.join(bunGlobalReal, "clawdbot");
const bunExpected = path.join(bunGlobalReal, "moltbot");
if (path.resolve(bunExpected) === path.resolve(pkgReal)) return "bun";
return null;
@@ -94,11 +94,11 @@ export async function detectGlobalInstallManagerByPresence(
for (const manager of ["npm", "pnpm"] as const) {
const root = await resolveGlobalRoot(manager, runCommand, timeoutMs);
if (!root) continue;
if (await pathExists(path.join(root, "clawdbot"))) return manager;
if (await pathExists(path.join(root, "moltbot"))) return manager;
}
const bunRoot = resolveBunGlobalRoot();
if (await pathExists(path.join(bunRoot, "clawdbot"))) return "bun";
if (await pathExists(path.join(bunRoot, "moltbot"))) return "bun";
return null;
}

View File

@@ -26,7 +26,7 @@ describe("runGatewayUpdate", () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-"));
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-"));
});
afterEach(async () => {
@@ -37,7 +37,7 @@ describe("runGatewayUpdate", () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
const { runner, calls } = createRunner({
@@ -62,7 +62,7 @@ describe("runGatewayUpdate", () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
const { runner, calls } = createRunner({
@@ -95,7 +95,7 @@ describe("runGatewayUpdate", () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
const stableTag = "v1.0.1-1";
@@ -113,7 +113,7 @@ describe("runGatewayUpdate", () => {
"pnpm build": { stdout: "" },
"pnpm ui:build": { stdout: "" },
[`git -C ${tempDir} checkout -- dist/control-ui/`]: { stdout: "" },
"pnpm clawdbot doctor --non-interactive": { stdout: "" },
"pnpm moltbot doctor --non-interactive": { stdout: "" },
});
const result = await runGatewayUpdate({
@@ -131,7 +131,7 @@ describe("runGatewayUpdate", () => {
it("skips update when no git root", async () => {
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "clawdbot", packageManager: "pnpm@8.0.0" }),
JSON.stringify({ name: "moltbot", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
await fs.writeFile(path.join(tempDir, "pnpm-lock.yaml"), "", "utf-8");
@@ -155,11 +155,11 @@ describe("runGatewayUpdate", () => {
it("updates global npm installs when detected", async () => {
const nodeModules = path.join(tempDir, "node_modules");
const pkgRoot = path.join(nodeModules, "clawdbot");
const pkgRoot = path.join(nodeModules, "moltbot");
await fs.mkdir(pkgRoot, { recursive: true });
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
@@ -173,10 +173,10 @@ describe("runGatewayUpdate", () => {
if (key === "npm root -g") {
return { stdout: nodeModules, stderr: "", code: 0 };
}
if (key === "npm i -g clawdbot@latest") {
if (key === "npm i -g moltbot@latest") {
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
JSON.stringify({ name: "moltbot", version: "2.0.0" }),
"utf-8",
);
return { stdout: "ok", stderr: "", code: 0 };
@@ -197,16 +197,16 @@ describe("runGatewayUpdate", () => {
expect(result.mode).toBe("npm");
expect(result.before?.version).toBe("1.0.0");
expect(result.after?.version).toBe("2.0.0");
expect(calls.some((call) => call === "npm i -g clawdbot@latest")).toBe(true);
expect(calls.some((call) => call === "npm i -g moltbot@latest")).toBe(true);
});
it("updates global npm installs with tag override", async () => {
const nodeModules = path.join(tempDir, "node_modules");
const pkgRoot = path.join(nodeModules, "clawdbot");
const pkgRoot = path.join(nodeModules, "moltbot");
await fs.mkdir(pkgRoot, { recursive: true });
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
@@ -220,10 +220,10 @@ describe("runGatewayUpdate", () => {
if (key === "npm root -g") {
return { stdout: nodeModules, stderr: "", code: 0 };
}
if (key === "npm i -g clawdbot@beta") {
if (key === "npm i -g moltbot@beta") {
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
JSON.stringify({ name: "moltbot", version: "2.0.0" }),
"utf-8",
);
return { stdout: "ok", stderr: "", code: 0 };
@@ -245,7 +245,7 @@ describe("runGatewayUpdate", () => {
expect(result.mode).toBe("npm");
expect(result.before?.version).toBe("1.0.0");
expect(result.after?.version).toBe("2.0.0");
expect(calls.some((call) => call === "npm i -g clawdbot@beta")).toBe(true);
expect(calls.some((call) => call === "npm i -g moltbot@beta")).toBe(true);
});
it("updates global bun installs when detected", async () => {
@@ -255,11 +255,11 @@ describe("runGatewayUpdate", () => {
try {
const bunGlobalRoot = path.join(bunInstall, "install", "global", "node_modules");
const pkgRoot = path.join(bunGlobalRoot, "clawdbot");
const pkgRoot = path.join(bunGlobalRoot, "moltbot");
await fs.mkdir(pkgRoot, { recursive: true });
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "1.0.0" }),
JSON.stringify({ name: "moltbot", version: "1.0.0" }),
"utf-8",
);
@@ -276,10 +276,10 @@ describe("runGatewayUpdate", () => {
if (key === "pnpm root -g") {
return { stdout: "", stderr: "", code: 1 };
}
if (key === "bun add -g clawdbot@latest") {
if (key === "bun add -g moltbot@latest") {
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "clawdbot", version: "2.0.0" }),
JSON.stringify({ name: "moltbot", version: "2.0.0" }),
"utf-8",
);
return { stdout: "ok", stderr: "", code: 0 };
@@ -297,14 +297,14 @@ describe("runGatewayUpdate", () => {
expect(result.mode).toBe("bun");
expect(result.before?.version).toBe("1.0.0");
expect(result.after?.version).toBe("2.0.0");
expect(calls.some((call) => call === "bun add -g clawdbot@latest")).toBe(true);
expect(calls.some((call) => call === "bun add -g moltbot@latest")).toBe(true);
} finally {
if (oldBunInstall === undefined) delete process.env.BUN_INSTALL;
else process.env.BUN_INSTALL = oldBunInstall;
}
});
it("rejects git roots that are not a clawdbot checkout", async () => {
it("rejects git roots that are not a moltbot checkout", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue(tempDir);
const { runner, calls } = createRunner({
@@ -320,7 +320,7 @@ describe("runGatewayUpdate", () => {
cwdSpy.mockRestore();
expect(result.status).toBe("error");
expect(result.reason).toBe("not-clawdbot-root");
expect(result.reason).toBe("not-moltbot-root");
expect(calls.some((call) => call.includes("status --porcelain"))).toBe(false);
});
});

View File

@@ -67,7 +67,7 @@ const MAX_LOG_CHARS = 8000;
const PREFLIGHT_MAX_COMMITS = 10;
const START_DIRS = ["cwd", "argv1", "process"];
const DEFAULT_PACKAGE_NAME = "moltbot";
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "clawdbot"]);
const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "moltbot"]);
function normalizeDir(value?: string | null) {
if (!value) return null;
@@ -291,7 +291,7 @@ function managerInstallArgs(manager: "pnpm" | "bun" | "npm") {
function normalizeTag(tag?: string) {
const trimmed = tag?.trim();
if (!trimmed) return "latest";
if (trimmed.startsWith("clawdbot@")) return trimmed.slice("clawdbot@".length);
if (trimmed.startsWith("moltbot@")) return trimmed.slice("moltbot@".length);
if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) {
return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
}
@@ -347,7 +347,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
status: "error",
mode: "unknown",
root: gitRoot,
reason: "not-clawdbot-root",
reason: "not-moltbot-root",
steps: [],
durationMs: Date.now() - startedAt,
};
@@ -502,7 +502,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
}
const manager = await detectPackageManager(gitRoot);
const preflightRoot = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-preflight-"));
const preflightRoot = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-preflight-"));
const worktreeDir = path.join(preflightRoot, "worktree");
const worktreeStep = await runStep(
step(
@@ -689,8 +689,8 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
const doctorStep = await runStep(
step(
"clawdbot doctor",
managerScriptArgs(manager, "clawdbot", ["doctor", "--non-interactive"]),
"moltbot doctor",
managerScriptArgs(manager, "moltbot", ["doctor", "--non-interactive"]),
gitRoot,
{ CLAWDBOT_UPDATE_IN_PROGRESS: "1" },
),

View File

@@ -5,8 +5,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { UpdateCheckResult } from "./update-check.js";
vi.mock("./clawdbot-root.js", () => ({
resolveClawdbotPackageRoot: vi.fn(),
vi.mock("./moltbot-root.js", () => ({
resolveMoltbotPackageRoot: vi.fn(),
}));
vi.mock("./update-check.js", async () => {
@@ -30,7 +30,7 @@ describe("update-startup", () => {
beforeEach(async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-01-17T10:00:00Z"));
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-update-check-"));
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-update-check-"));
process.env.CLAWDBOT_STATE_DIR = tempDir;
delete process.env.VITEST;
process.env.NODE_ENV = "test";
@@ -43,13 +43,13 @@ describe("update-startup", () => {
});
it("logs update hint for npm installs when newer tag exists", async () => {
const { resolveClawdbotPackageRoot } = await import("./clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("./moltbot-root.js");
const { checkUpdateStatus, resolveNpmChannelTag } = await import("./update-check.js");
const { runGatewayUpdateCheck } = await import("./update-startup.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue("/opt/clawdbot");
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue("/opt/moltbot");
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: "/opt/clawdbot",
root: "/opt/moltbot",
installKind: "package",
packageManager: "npm",
} satisfies UpdateCheckResult);
@@ -77,13 +77,13 @@ describe("update-startup", () => {
});
it("uses latest when beta tag is older than release", async () => {
const { resolveClawdbotPackageRoot } = await import("./clawdbot-root.js");
const { resolveMoltbotPackageRoot } = await import("./moltbot-root.js");
const { checkUpdateStatus, resolveNpmChannelTag } = await import("./update-check.js");
const { runGatewayUpdateCheck } = await import("./update-startup.js");
vi.mocked(resolveClawdbotPackageRoot).mockResolvedValue("/opt/clawdbot");
vi.mocked(resolveMoltbotPackageRoot).mockResolvedValue("/opt/moltbot");
vi.mocked(checkUpdateStatus).mockResolvedValue({
root: "/opt/clawdbot",
root: "/opt/moltbot",
installKind: "package",
packageManager: "npm",
} satisfies UpdateCheckResult);

View File

@@ -3,7 +3,7 @@ import path from "node:path";
import type { loadConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import { resolveClawdbotPackageRoot } from "./clawdbot-root.js";
import { resolveMoltbotPackageRoot } from "./moltbot-root.js";
import { compareSemverStrings, resolveNpmChannelTag, checkUpdateStatus } from "./update-check.js";
import { normalizeUpdateChannel, DEFAULT_PACKAGE_CHANNEL } from "./update-channels.js";
import { VERSION } from "../version.js";
@@ -57,7 +57,7 @@ export async function runGatewayUpdateCheck(params: {
if (now - lastCheckedAt < UPDATE_CHECK_INTERVAL_MS) return;
}
const root = await resolveClawdbotPackageRoot({
const root = await resolveMoltbotPackageRoot({
moduleUrl: import.meta.url,
argv1: process.argv[1],
cwd: process.cwd(),
@@ -93,7 +93,7 @@ export async function runGatewayUpdateCheck(params: {
state.lastNotifiedVersion !== resolved.version || state.lastNotifiedTag !== tag;
if (shouldNotify) {
params.log.info(
`update available (${tag}): v${resolved.version} (current v${VERSION}). Run: ${formatCliCommand("clawdbot update")}`,
`update available (${tag}): v${resolved.version} (current v${VERSION}). Run: ${formatCliCommand("moltbot update")}`,
);
nextState.lastNotifiedVersion = resolved.version;
nextState.lastNotifiedTag = tag;

View File

@@ -12,14 +12,14 @@ import {
describe("voicewake store", () => {
it("returns defaults when missing", async () => {
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-voicewake-"));
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-voicewake-"));
const cfg = await loadVoiceWakeConfig(baseDir);
expect(cfg.triggers).toEqual(defaultVoiceWakeTriggers());
expect(cfg.updatedAtMs).toBe(0);
});
it("sanitizes and persists triggers", async () => {
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-voicewake-"));
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-voicewake-"));
const saved = await setVoiceWakeTriggers([" hi ", "", " there "], baseDir);
expect(saved.triggers).toEqual(["hi", "there"]);
expect(saved.updatedAtMs).toBeGreaterThan(0);
@@ -30,7 +30,7 @@ describe("voicewake store", () => {
});
it("falls back to defaults when triggers empty", async () => {
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-voicewake-"));
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-voicewake-"));
const saved = await setVoiceWakeTriggers(["", " "], baseDir);
expect(saved.triggers).toEqual(defaultVoiceWakeTriggers());
});

View File

@@ -1,4 +1,4 @@
const warningFilterKey = Symbol.for("clawdbot.warning-filter");
const warningFilterKey = Symbol.for("moltbot.warning-filter");
type Warning = Error & {
code?: string;

View File

@@ -3,35 +3,35 @@ import { describe, expect, it } from "vitest";
import { renderWideAreaGatewayZoneText, WIDE_AREA_DISCOVERY_DOMAIN } from "./widearea-dns.js";
describe("wide-area DNS-SD zone rendering", () => {
it("renders a clawdbot.internal zone with gateway PTR/SRV/TXT records", () => {
it("renders a moltbot.internal zone with gateway PTR/SRV/TXT records", () => {
const txt = renderWideAreaGatewayZoneText({
serial: 2025121701,
gatewayPort: 18789,
displayName: "Mac Studio (Clawdbot)",
displayName: "Mac Studio (Moltbot)",
tailnetIPv4: "100.123.224.76",
tailnetIPv6: "fd7a:115c:a1e0::8801:e04c",
hostLabel: "studio-london",
instanceLabel: "studio-london",
sshPort: 22,
cliPath: "/opt/homebrew/bin/clawdbot",
cliPath: "/opt/homebrew/bin/moltbot",
});
expect(txt).toContain(`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`);
expect(txt).toContain(`studio-london IN A 100.123.224.76`);
expect(txt).toContain(`studio-london IN AAAA fd7a:115c:a1e0::8801:e04c`);
expect(txt).toContain(`_clawdbot-gw._tcp IN PTR studio-london._clawdbot-gw._tcp`);
expect(txt).toContain(`studio-london._clawdbot-gw._tcp IN SRV 0 0 18789 studio-london`);
expect(txt).toContain(`displayName=Mac Studio (Clawdbot)`);
expect(txt).toContain(`_moltbot-gw._tcp IN PTR studio-london._moltbot-gw._tcp`);
expect(txt).toContain(`studio-london._moltbot-gw._tcp IN SRV 0 0 18789 studio-london`);
expect(txt).toContain(`displayName=Mac Studio (Moltbot)`);
expect(txt).toContain(`gatewayPort=18789`);
expect(txt).toContain(`sshPort=22`);
expect(txt).toContain(`cliPath=/opt/homebrew/bin/clawdbot`);
expect(txt).toContain(`cliPath=/opt/homebrew/bin/moltbot`);
});
it("includes tailnetDns when provided", () => {
const txt = renderWideAreaGatewayZoneText({
serial: 2025121701,
gatewayPort: 18789,
displayName: "Mac Studio (Clawdbot)",
displayName: "Mac Studio (Moltbot)",
tailnetIPv4: "100.123.224.76",
tailnetDns: "peters-mac-studio-1.sheep-coho.ts.net",
hostLabel: "studio-london",

View File

@@ -4,8 +4,8 @@ import path from "node:path";
import { CONFIG_DIR, ensureDir } from "../utils.js";
export const WIDE_AREA_DISCOVERY_DOMAIN = "clawdbot.internal.";
export const WIDE_AREA_ZONE_FILENAME = "clawdbot.internal.db";
export const WIDE_AREA_DISCOVERY_DOMAIN = "moltbot.internal.";
export const WIDE_AREA_ZONE_FILENAME = "moltbot.internal.db";
export function getWideAreaZonePath(): string {
return path.join(CONFIG_DIR, "dns", WIDE_AREA_ZONE_FILENAME);
@@ -51,7 +51,7 @@ function extractSerial(zoneText: string): number | null {
}
function extractContentHash(zoneText: string): string | null {
const match = zoneText.match(/^\s*;\s*clawdbot-content-hash:\s*(\S+)\s*$/m);
const match = zoneText.match(/^\s*;\s*moltbot-content-hash:\s*(\S+)\s*$/m);
return match?.[1] ?? null;
}
@@ -80,9 +80,9 @@ export type WideAreaGatewayZoneOpts = {
};
function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string {
const hostname = os.hostname().split(".")[0] ?? "clawdbot";
const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "clawdbot");
const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "clawdbot-gw");
const hostname = os.hostname().split(".")[0] ?? "moltbot";
const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "moltbot");
const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "moltbot-gw");
const txt = [
`displayName=${opts.displayName.trim() || hostname}`,
@@ -119,9 +119,9 @@ function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string
records.push(`${hostLabel} IN AAAA ${opts.tailnetIPv6}`);
}
records.push(`_clawdbot-gw._tcp IN PTR ${instanceLabel}._clawdbot-gw._tcp`);
records.push(`${instanceLabel}._clawdbot-gw._tcp IN SRV 0 0 ${opts.gatewayPort} ${hostLabel}`);
records.push(`${instanceLabel}._clawdbot-gw._tcp IN TXT ${txt.map(txtQuote).join(" ")}`);
records.push(`_moltbot-gw._tcp IN PTR ${instanceLabel}._moltbot-gw._tcp`);
records.push(`${instanceLabel}._moltbot-gw._tcp IN SRV 0 0 ${opts.gatewayPort} ${hostLabel}`);
records.push(`${instanceLabel}._moltbot-gw._tcp IN TXT ${txt.map(txtQuote).join(" ")}`);
const contentBody = `${records.join("\n")}\n`;
const hashBody = `${records
@@ -131,7 +131,7 @@ function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string
.join("\n")}\n`;
const contentHash = computeContentHash(hashBody);
return `; clawdbot-content-hash: ${contentHash}\n${contentBody}`;
return `; moltbot-content-hash: ${contentHash}\n${contentBody}`;
}
export function renderWideAreaGatewayZoneText(