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
+4 -4
View File
@@ -3,7 +3,7 @@ import path from "node:path";
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
import { resolveUserPath } from "../utils.js";
import { resolveStateDir } from "./paths.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
export type DuplicateAgentDir = {
agentDir: string;
@@ -28,7 +28,7 @@ function canonicalizeAgentDir(agentDir: string): string {
return resolved;
}
function collectReferencedAgentIds(cfg: ClawdbotConfig): string[] {
function collectReferencedAgentIds(cfg: MoltbotConfig): string[] {
const ids = new Set<string>();
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents?.list : [];
@@ -54,7 +54,7 @@ function collectReferencedAgentIds(cfg: ClawdbotConfig): string[] {
}
function resolveEffectiveAgentDir(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
agentId: string,
deps?: { env?: NodeJS.ProcessEnv; homedir?: () => string },
): string {
@@ -69,7 +69,7 @@ function resolveEffectiveAgentDir(
}
export function findDuplicateAgentDirs(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
deps?: { env?: NodeJS.ProcessEnv; homedir?: () => string },
): DuplicateAgentDir[] {
const byDir = new Map<string, { agentDir: string; agentIds: string[] }>();
+3 -3
View File
@@ -1,9 +1,9 @@
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
export const DEFAULT_AGENT_MAX_CONCURRENT = 4;
export const DEFAULT_SUBAGENT_MAX_CONCURRENT = 8;
export function resolveAgentMaxConcurrent(cfg?: ClawdbotConfig): number {
export function resolveAgentMaxConcurrent(cfg?: MoltbotConfig): number {
const raw = cfg?.agents?.defaults?.maxConcurrent;
if (typeof raw === "number" && Number.isFinite(raw)) {
return Math.max(1, Math.floor(raw));
@@ -11,7 +11,7 @@ export function resolveAgentMaxConcurrent(cfg?: ClawdbotConfig): number {
return DEFAULT_AGENT_MAX_CONCURRENT;
}
export function resolveSubagentMaxConcurrent(cfg?: ClawdbotConfig): number {
export function resolveSubagentMaxConcurrent(cfg?: MoltbotConfig): number {
const raw = cfg?.agents?.defaults?.subagents?.maxConcurrent;
if (typeof raw === "number" && Number.isFinite(raw)) {
return Math.max(1, Math.floor(raw));
+5 -5
View File
@@ -3,7 +3,7 @@ import type { ChannelPlugin } from "../channels/plugins/types.js";
import type { PluginRegistry } from "../plugins/registry.js";
import { setActivePluginRegistry } from "../plugins/runtime.js";
import { resolveChannelCapabilities } from "./channel-capabilities.js";
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
describe("resolveChannelCapabilities", () => {
beforeEach(() => {
@@ -32,7 +32,7 @@ describe("resolveChannelCapabilities", () => {
},
},
},
} satisfies Partial<ClawdbotConfig>;
} satisfies Partial<MoltbotConfig>;
expect(
resolveChannelCapabilities({
@@ -53,7 +53,7 @@ describe("resolveChannelCapabilities", () => {
},
},
},
} satisfies Partial<ClawdbotConfig>;
} satisfies Partial<MoltbotConfig>;
expect(
resolveChannelCapabilities({
@@ -73,7 +73,7 @@ describe("resolveChannelCapabilities", () => {
},
},
},
} satisfies Partial<ClawdbotConfig>;
} satisfies Partial<MoltbotConfig>;
expect(
resolveChannelCapabilities({
@@ -96,7 +96,7 @@ describe("resolveChannelCapabilities", () => {
);
const cfg = {
channels: { msteams: { capabilities: [" polls ", ""] } },
} satisfies Partial<ClawdbotConfig>;
} satisfies Partial<MoltbotConfig>;
expect(
resolveChannelCapabilities({
+2 -2
View File
@@ -1,6 +1,6 @@
import { normalizeChannelId } from "../channels/plugins/index.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
import type { TelegramCapabilitiesConfig } from "./types.telegram.js";
type CapabilitiesConfig = TelegramCapabilitiesConfig;
@@ -45,7 +45,7 @@ function resolveAccountCapabilities(params: {
}
export function resolveChannelCapabilities(params: {
cfg?: Partial<ClawdbotConfig>;
cfg?: Partial<MoltbotConfig>;
channel?: string | null;
accountId?: string | null;
}): string[] | undefined {
@@ -46,7 +46,7 @@ describe("agent concurrency defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({}, null, 2),
"utf-8",
);
+3 -3
View File
@@ -3,17 +3,17 @@ import fs from "node:fs/promises";
import { describe, expect, it } from "vitest";
import { withTempHome } from "./test-helpers.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
describe("config backup rotation", () => {
it("keeps a 5-deep backup ring for config writes", async () => {
await withTempHome(async () => {
const { resolveConfigPath, writeConfigFile } = await import("./config.js");
const configPath = resolveConfigPath();
const buildConfig = (version: number): ClawdbotConfig =>
const buildConfig = (version: number): MoltbotConfig =>
({
agents: { list: [{ id: `v${version}` }] },
}) as ClawdbotConfig;
}) as MoltbotConfig;
for (let version = 0; version <= 6; version += 1) {
await writeConfigFile(buildConfig(version));
@@ -9,7 +9,7 @@ describe("config compaction settings", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -51,7 +51,7 @@ describe("config compaction settings", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
+1 -1
View File
@@ -19,7 +19,7 @@ describe("config discord", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
channels: {
+3 -3
View File
@@ -9,7 +9,7 @@ describe("config env vars", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
env: { vars: { OPENROUTER_API_KEY: "config-key" } },
@@ -33,7 +33,7 @@ describe("config env vars", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
env: { vars: { OPENROUTER_API_KEY: "config-key" } },
@@ -57,7 +57,7 @@ describe("config env vars", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
env: { vars: { GROQ_API_KEY: "gsk-config" } },
+10 -10
View File
@@ -20,7 +20,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -57,7 +57,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -94,7 +94,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
messages: {},
@@ -119,7 +119,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -159,11 +159,11 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
messages: {
messagePrefix: "[clawdbot]",
messagePrefix: "[moltbot]",
responsePrefix: "🦞",
},
channels: {
@@ -205,7 +205,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
models: {
@@ -254,7 +254,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -290,7 +290,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
messages: {},
@@ -319,7 +319,7 @@ describe("config identity defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -176,7 +176,7 @@ describe("legacy config detection", () => {
});
it("flags legacy config in snapshot", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -202,7 +202,7 @@ describe("legacy config detection", () => {
});
it("does not auto-migrate claude-cli auth profile mode on load", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -234,7 +234,7 @@ describe("legacy config detection", () => {
});
it("flags legacy provider sections in snapshot", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -260,7 +260,7 @@ describe("legacy config detection", () => {
});
it("flags routing.allowFrom in snapshot", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -286,7 +286,7 @@ describe("legacy config detection", () => {
});
it("rejects bindings[].match.provider on load", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -316,7 +316,7 @@ describe("legacy config detection", () => {
});
it("rejects bindings[].match.accountID on load", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -346,7 +346,7 @@ describe("legacy config detection", () => {
});
it("rejects session.sendPolicy.rules[].match.provider on load", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -380,7 +380,7 @@ describe("legacy config detection", () => {
});
it("rejects messages.queue.byProvider on load", async () => {
await withTempHome(async (home) => {
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
const configPath = path.join(home, ".clawdbot", "moltbot.json");
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(
configPath,
@@ -8,7 +8,7 @@ describe("multi-agent agentDir validation", () => {
it("rejects shared agents.list agentDir", async () => {
vi.resetModules();
const { validateConfigObject } = await import("./config.js");
const shared = path.join(tmpdir(), "clawdbot-shared-agentdir");
const shared = path.join(tmpdir(), "moltbot-shared-agentdir");
const res = validateConfigObject({
agents: {
list: [
@@ -29,7 +29,7 @@ describe("multi-agent agentDir validation", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
agents: {
@@ -35,57 +35,55 @@ describe("Nix integration (U3, U5, U9)", () => {
});
describe("U5: CONFIG_PATH and STATE_DIR env var overrides", () => {
it("STATE_DIR_CLAWDBOT defaults to ~/.clawdbot when env not set", async () => {
it("STATE_DIR defaults to ~/.clawdbot when env not set", async () => {
await withEnvOverride({ CLAWDBOT_STATE_DIR: undefined }, async () => {
const { STATE_DIR_CLAWDBOT } = await import("./config.js");
expect(STATE_DIR_CLAWDBOT).toMatch(/\.clawdbot$/);
const { STATE_DIR } = await import("./config.js");
expect(STATE_DIR).toMatch(/\.clawdbot$/);
});
});
it("STATE_DIR_CLAWDBOT respects CLAWDBOT_STATE_DIR override", async () => {
it("STATE_DIR respects CLAWDBOT_STATE_DIR override", async () => {
await withEnvOverride({ CLAWDBOT_STATE_DIR: "/custom/state/dir" }, async () => {
const { STATE_DIR_CLAWDBOT } = await import("./config.js");
expect(STATE_DIR_CLAWDBOT).toBe(path.resolve("/custom/state/dir"));
const { STATE_DIR } = await import("./config.js");
expect(STATE_DIR).toBe(path.resolve("/custom/state/dir"));
});
});
it("CONFIG_PATH_CLAWDBOT defaults to ~/.clawdbot/clawdbot.json when env not set", async () => {
it("CONFIG_PATH defaults to ~/.clawdbot/moltbot.json when env not set", async () => {
await withEnvOverride(
{ CLAWDBOT_CONFIG_PATH: undefined, CLAWDBOT_STATE_DIR: undefined },
async () => {
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
expect(CONFIG_PATH_CLAWDBOT).toMatch(/\.clawdbot[\\/]clawdbot\.json$/);
const { CONFIG_PATH } = await import("./config.js");
expect(CONFIG_PATH).toMatch(/\.clawdbot[\\/]moltbot\.json$/);
},
);
});
it("CONFIG_PATH_CLAWDBOT respects CLAWDBOT_CONFIG_PATH override", async () => {
await withEnvOverride({ CLAWDBOT_CONFIG_PATH: "/nix/store/abc/clawdbot.json" }, async () => {
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
expect(CONFIG_PATH_CLAWDBOT).toBe(path.resolve("/nix/store/abc/clawdbot.json"));
it("CONFIG_PATH respects CLAWDBOT_CONFIG_PATH override", async () => {
await withEnvOverride({ CLAWDBOT_CONFIG_PATH: "/nix/store/abc/moltbot.json" }, async () => {
const { CONFIG_PATH } = await import("./config.js");
expect(CONFIG_PATH).toBe(path.resolve("/nix/store/abc/moltbot.json"));
});
});
it("CONFIG_PATH_CLAWDBOT expands ~ in CLAWDBOT_CONFIG_PATH override", async () => {
it("CONFIG_PATH expands ~ in CLAWDBOT_CONFIG_PATH override", async () => {
await withTempHome(async (home) => {
await withEnvOverride({ CLAWDBOT_CONFIG_PATH: "~/.clawdbot/custom.json" }, async () => {
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
expect(CONFIG_PATH_CLAWDBOT).toBe(path.join(home, ".clawdbot", "custom.json"));
const { CONFIG_PATH } = await import("./config.js");
expect(CONFIG_PATH).toBe(path.join(home, ".clawdbot", "custom.json"));
});
});
});
it("CONFIG_PATH_CLAWDBOT uses STATE_DIR_CLAWDBOT when only state dir is overridden", async () => {
it("CONFIG_PATH uses STATE_DIR when only state dir is overridden", async () => {
await withEnvOverride(
{
CLAWDBOT_CONFIG_PATH: undefined,
CLAWDBOT_STATE_DIR: "/custom/state",
},
async () => {
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
expect(CONFIG_PATH_CLAWDBOT).toBe(
path.join(path.resolve("/custom/state"), "clawdbot.json"),
);
const { CONFIG_PATH } = await import("./config.js");
expect(CONFIG_PATH).toBe(path.join(path.resolve("/custom/state"), "moltbot.json"));
},
);
});
@@ -104,7 +102,7 @@ describe("Nix integration (U3, U5, U9)", () => {
"utf-8",
);
await fs.writeFile(
path.join(pluginDir, "clawdbot.plugin.json"),
path.join(pluginDir, "moltbot.plugin.json"),
JSON.stringify(
{
id: "demo-plugin",
@@ -116,7 +114,7 @@ describe("Nix integration (U3, U5, U9)", () => {
"utf-8",
);
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
plugins: {
@@ -198,7 +196,7 @@ describe("Nix integration (U3, U5, U9)", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({
channels: { telegram: { botToken: "123:ABC" } },
}),
@@ -218,7 +216,7 @@ describe("Nix integration (U3, U5, U9)", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({
channels: { telegram: { tokenFile: "/run/agenix/telegram-token" } },
}),
@@ -238,7 +236,7 @@ describe("Nix integration (U3, U5, U9)", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({
channels: {
telegram: {
+1 -1
View File
@@ -25,7 +25,7 @@ async function writePluginFixture(params: {
manifest.channels = params.channels;
}
await fs.writeFile(
path.join(params.dir, "clawdbot.plugin.json"),
path.join(params.dir, "moltbot.plugin.json"),
JSON.stringify(manifest, null, 2),
"utf-8",
);
@@ -19,7 +19,7 @@ describe("config strict validation", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({
agents: { list: [{ id: "pi" }] },
routing: { allowFrom: ["+15555550123"] },
+4 -4
View File
@@ -13,7 +13,7 @@ describe("config pruning defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({ agents: { defaults: {} } }, null, 2),
"utf-8",
);
@@ -41,7 +41,7 @@ describe("config pruning defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
auth: {
@@ -72,7 +72,7 @@ describe("config pruning defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify(
{
auth: {
@@ -110,7 +110,7 @@ describe("config pruning defaults", () => {
const configDir = path.join(home, ".clawdbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({ agents: { defaults: { contextPruning: { mode: "off" } } } }, null, 2),
"utf-8",
);
@@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest";
import { ClawdbotSchema } from "./zod-schema.js";
import { MoltbotSchema } from "./zod-schema.js";
describe("skills entries config schema", () => {
it("accepts custom fields under config", () => {
const res = ClawdbotSchema.safeParse({
const res = MoltbotSchema.safeParse({
skills: {
entries: {
"custom-skill": {
@@ -22,7 +22,7 @@ describe("skills entries config schema", () => {
});
it("rejects unknown top-level fields", () => {
const res = ClawdbotSchema.safeParse({
const res = MoltbotSchema.safeParse({
skills: {
entries: {
"custom-skill": {
@@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest";
import { ClawdbotSchema } from "./zod-schema.js";
import { MoltbotSchema } from "./zod-schema.js";
describe("telegram custom commands schema", () => {
it("normalizes custom commands", () => {
const res = ClawdbotSchema.safeParse({
const res = MoltbotSchema.safeParse({
channels: {
telegram: {
customCommands: [{ command: "/Backup", description: " Git backup " }],
@@ -21,7 +21,7 @@ describe("telegram custom commands schema", () => {
});
it("rejects custom commands with invalid names", () => {
const res = ClawdbotSchema.safeParse({
const res = MoltbotSchema.safeParse({
channels: {
telegram: {
customCommands: [{ command: "Bad-Name", description: "Override status" }],
+1 -1
View File
@@ -11,4 +11,4 @@ export * from "./paths.js";
export * from "./runtime-overrides.js";
export * from "./types.js";
export { validateConfigObject, validateConfigObjectWithPlugins } from "./validation.js";
export { ClawdbotSchema } from "./zod-schema.js";
export { MoltbotSchema } from "./zod-schema.js";
+12 -12
View File
@@ -1,7 +1,7 @@
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
import { parseModelRef } from "../agents/model-selection.js";
import { resolveTalkApiKey } from "./talk.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js";
import type { ModelDefinitionConfig } from "./types.models.js";
@@ -53,7 +53,7 @@ function resolveModelCost(
};
}
function resolveAnthropicDefaultAuthMode(cfg: ClawdbotConfig): AnthropicAuthDefaultsMode | null {
function resolveAnthropicDefaultAuthMode(cfg: MoltbotConfig): AnthropicAuthDefaultsMode | null {
const profiles = cfg.auth?.profiles ?? {};
const anthropicProfiles = Object.entries(profiles).filter(
([, profile]) => profile?.provider === "anthropic",
@@ -92,7 +92,7 @@ export type SessionDefaultsOptions = {
warnState?: WarnState;
};
export function applyMessageDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyMessageDefaults(cfg: MoltbotConfig): MoltbotConfig {
const messages = cfg.messages;
const hasAckScope = messages?.ackReactionScope !== undefined;
if (hasAckScope) return cfg;
@@ -106,9 +106,9 @@ export function applyMessageDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
}
export function applySessionDefaults(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
options: SessionDefaultsOptions = {},
): ClawdbotConfig {
): MoltbotConfig {
const session = cfg.session;
if (!session || session.mainKey === undefined) return cfg;
@@ -116,7 +116,7 @@ export function applySessionDefaults(
const warn = options.warn ?? console.warn;
const warnState = options.warnState ?? defaultWarnState;
const next: ClawdbotConfig = {
const next: MoltbotConfig = {
...cfg,
session: { ...session, mainKey: "main" },
};
@@ -129,7 +129,7 @@ export function applySessionDefaults(
return next;
}
export function applyTalkApiKey(config: ClawdbotConfig): ClawdbotConfig {
export function applyTalkApiKey(config: MoltbotConfig): MoltbotConfig {
const resolved = resolveTalkApiKey();
if (!resolved) return config;
const existing = config.talk?.apiKey?.trim();
@@ -143,7 +143,7 @@ export function applyTalkApiKey(config: ClawdbotConfig): ClawdbotConfig {
};
}
export function applyModelDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyModelDefaults(cfg: MoltbotConfig): MoltbotConfig {
let mutated = false;
let nextCfg = cfg;
@@ -238,7 +238,7 @@ export function applyModelDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
};
}
export function applyAgentDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyAgentDefaults(cfg: MoltbotConfig): MoltbotConfig {
const agents = cfg.agents;
const defaults = agents?.defaults;
const hasMax =
@@ -275,7 +275,7 @@ export function applyAgentDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
};
}
export function applyLoggingDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyLoggingDefaults(cfg: MoltbotConfig): MoltbotConfig {
const logging = cfg.logging;
if (!logging) return cfg;
if (logging.redactSensitive) return cfg;
@@ -288,7 +288,7 @@ export function applyLoggingDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
};
}
export function applyContextPruningDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyContextPruningDefaults(cfg: MoltbotConfig): MoltbotConfig {
const defaults = cfg.agents?.defaults;
if (!defaults) return cfg;
@@ -369,7 +369,7 @@ export function applyContextPruningDefaults(cfg: ClawdbotConfig): ClawdbotConfig
};
}
export function applyCompactionDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyCompactionDefaults(cfg: MoltbotConfig): MoltbotConfig {
const defaults = cfg.agents?.defaults;
if (!defaults) return cfg;
const compaction = defaults?.compaction;
+2 -2
View File
@@ -1,6 +1,6 @@
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
export function collectConfigEnvVars(cfg?: ClawdbotConfig): Record<string, string> {
export function collectConfigEnvVars(cfg?: MoltbotConfig): Record<string, string> {
const envConfig = cfg?.env;
if (!envConfig) return {};
+5 -5
View File
@@ -1,6 +1,6 @@
import type { ChannelId } from "../channels/plugins/types.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
import type { GroupToolPolicyBySenderConfig, GroupToolPolicyConfig } from "./types.tools.js";
export type GroupPolicyChannel = ChannelId;
@@ -80,7 +80,7 @@ export function resolveToolsBySender(
}
function resolveChannelGroups(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
channel: GroupPolicyChannel,
accountId?: string | null,
): ChannelGroups | undefined {
@@ -103,7 +103,7 @@ function resolveChannelGroups(
}
export function resolveChannelGroupPolicy(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: GroupPolicyChannel;
groupId?: string | null;
accountId?: string | null;
@@ -128,7 +128,7 @@ export function resolveChannelGroupPolicy(params: {
}
export function resolveChannelGroupRequireMention(params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: GroupPolicyChannel;
groupId?: string | null;
accountId?: string | null;
@@ -156,7 +156,7 @@ export function resolveChannelGroupRequireMention(params: {
export function resolveChannelGroupToolsPolicy(
params: {
cfg: ClawdbotConfig;
cfg: MoltbotConfig;
channel: GroupPolicyChannel;
groupId?: string | null;
accountId?: string | null;
+5 -5
View File
@@ -11,16 +11,16 @@ import {
const ROOT_DIR = path.parse(process.cwd()).root;
const CONFIG_DIR = path.join(ROOT_DIR, "config");
const ETC_CLAWDBOT_DIR = path.join(ROOT_DIR, "etc", "clawdbot");
const ETC_CLAWDBOT_DIR = path.join(ROOT_DIR, "etc", "moltbot");
const SHARED_DIR = path.join(ROOT_DIR, "shared");
const DEFAULT_BASE_PATH = path.join(CONFIG_DIR, "clawdbot.json");
const DEFAULT_BASE_PATH = path.join(CONFIG_DIR, "moltbot.json");
function configPath(...parts: string[]) {
return path.join(CONFIG_DIR, ...parts);
}
function etcClawdbotPath(...parts: string[]) {
function etcMoltbotPath(...parts: string[]) {
return path.join(ETC_CLAWDBOT_DIR, ...parts);
}
@@ -70,7 +70,7 @@ describe("resolveConfigIncludes", () => {
});
it("resolves absolute path $include", () => {
const absolute = etcClawdbotPath("agents.json");
const absolute = etcMoltbotPath("agents.json");
const files = { [absolute]: { list: [{ id: "main" }] } };
const obj = { agents: { $include: absolute } };
expect(resolve(obj, files)).toEqual({
@@ -283,7 +283,7 @@ describe("resolveConfigIncludes", () => {
it("resolves parent directory references", () => {
const files = { [sharedPath("common.json")]: { shared: true } };
const obj = { $include: "../../shared/common.json" };
expect(resolve(obj, files, configPath("sub", "clawdbot.json"))).toEqual({
expect(resolve(obj, files, configPath("sub", "moltbot.json"))).toEqual({
shared: true,
});
});
+17 -17
View File
@@ -30,9 +30,9 @@ import { findLegacyConfigIssues } from "./legacy.js";
import { normalizeConfigPaths } from "./normalize-paths.js";
import { resolveConfigPath, resolveStateDir } from "./paths.js";
import { applyConfigOverrides } from "./runtime-overrides.js";
import type { ClawdbotConfig, ConfigFileSnapshot, LegacyConfigIssue } from "./types.js";
import type { MoltbotConfig, ConfigFileSnapshot, LegacyConfigIssue } from "./types.js";
import { validateConfigObjectWithPlugins } from "./validation.js";
import { compareClawdbotVersions } from "./version.js";
import { compareMoltbotVersions } from "./version.js";
// Re-export for backwards compatibility
export { CircularIncludeError, ConfigIncludeError } from "./includes.js";
@@ -81,11 +81,11 @@ export function resolveConfigSnapshotHash(snapshot: {
return hashConfigRaw(snapshot.raw);
}
function coerceConfig(value: unknown): ClawdbotConfig {
function coerceConfig(value: unknown): MoltbotConfig {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return {};
}
return value as ClawdbotConfig;
return value as MoltbotConfig;
}
async function rotateConfigBackups(configPath: string, ioFs: typeof fs.promises): Promise<void> {
@@ -125,7 +125,7 @@ function warnOnConfigMiskeys(raw: unknown, logger: Pick<typeof console, "warn">)
}
}
function stampConfigVersion(cfg: ClawdbotConfig): ClawdbotConfig {
function stampConfigVersion(cfg: MoltbotConfig): MoltbotConfig {
const now = new Date().toISOString();
return {
...cfg,
@@ -137,19 +137,19 @@ function stampConfigVersion(cfg: ClawdbotConfig): ClawdbotConfig {
};
}
function warnIfConfigFromFuture(cfg: ClawdbotConfig, logger: Pick<typeof console, "warn">): void {
function warnIfConfigFromFuture(cfg: MoltbotConfig, logger: Pick<typeof console, "warn">): void {
const touched = cfg.meta?.lastTouchedVersion;
if (!touched) return;
const cmp = compareClawdbotVersions(VERSION, touched);
const cmp = compareMoltbotVersions(VERSION, touched);
if (cmp === null) return;
if (cmp < 0) {
logger.warn(
`Config was last written by a newer Clawdbot (${touched}); current version is ${VERSION}.`,
`Config was last written by a newer Moltbot (${touched}); current version is ${VERSION}.`,
);
}
}
function applyConfigEnv(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): void {
function applyConfigEnv(cfg: MoltbotConfig, env: NodeJS.ProcessEnv): void {
const entries = collectConfigEnvVars(cfg);
for (const [key, value] of Object.entries(entries)) {
if (env[key]?.trim()) continue;
@@ -188,7 +188,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
const deps = normalizeDeps(overrides);
const configPath = resolveConfigPathForDeps(deps);
function loadConfig(): ClawdbotConfig {
function loadConfig(): MoltbotConfig {
try {
if (!deps.fs.existsSync(configPath)) {
if (shouldEnableShellEnvFallback(deps.env) && !shouldDeferShellEnvFallback(deps.env)) {
@@ -213,7 +213,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
// Apply config.env to process.env BEFORE substitution so ${VAR} can reference config-defined vars
if (resolved && typeof resolved === "object" && "env" in resolved) {
applyConfigEnv(resolved as ClawdbotConfig, deps.env);
applyConfigEnv(resolved as MoltbotConfig, deps.env);
}
// Substitute ${VAR} env var references
@@ -222,7 +222,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
const resolvedConfig = substituted;
warnOnConfigMiskeys(resolvedConfig, deps.logger);
if (typeof resolvedConfig !== "object" || resolvedConfig === null) return {};
const preValidationDuplicates = findDuplicateAgentDirs(resolvedConfig as ClawdbotConfig, {
const preValidationDuplicates = findDuplicateAgentDirs(resolvedConfig as MoltbotConfig, {
env: deps.env,
homedir: deps.homedir,
});
@@ -372,7 +372,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
// Apply config.env to process.env BEFORE substitution so ${VAR} can reference config-defined vars
if (resolved && typeof resolved === "object" && "env" in resolved) {
applyConfigEnv(resolved as ClawdbotConfig, deps.env);
applyConfigEnv(resolved as MoltbotConfig, deps.env);
}
// Substitute ${VAR} env var references
@@ -454,7 +454,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
}
}
async function writeConfigFile(cfg: ClawdbotConfig) {
async function writeConfigFile(cfg: MoltbotConfig) {
clearConfigCache();
const validated = validateConfigObjectWithPlugins(cfg);
if (!validated.ok) {
@@ -528,7 +528,7 @@ const DEFAULT_CONFIG_CACHE_MS = 200;
let configCache: {
configPath: string;
expiresAt: number;
config: ClawdbotConfig;
config: MoltbotConfig;
} | null = null;
function resolveConfigCacheMs(env: NodeJS.ProcessEnv): number {
@@ -549,7 +549,7 @@ function clearConfigCache(): void {
configCache = null;
}
export function loadConfig(): ClawdbotConfig {
export function loadConfig(): MoltbotConfig {
const configPath = resolveConfigPath();
const now = Date.now();
if (shouldUseConfigCache(process.env)) {
@@ -578,7 +578,7 @@ export async function readConfigFileSnapshot(): Promise<ConfigFileSnapshot> {
}).readConfigFileSnapshot();
}
export async function writeConfigFile(cfg: ClawdbotConfig): Promise<void> {
export async function writeConfigFile(cfg: MoltbotConfig): Promise<void> {
clearConfigCache();
await createConfigIO({ configPath: resolveConfigPath() }).writeConfigFile(cfg);
}
+2 -2
View File
@@ -1,9 +1,9 @@
import { applyLegacyMigrations } from "./legacy.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
import { validateConfigObjectWithPlugins } from "./validation.js";
export function migrateLegacyConfig(raw: unknown): {
config: ClawdbotConfig | null;
config: MoltbotConfig | null;
changes: string[];
} {
const { next, changes } = applyLegacyMigrations(raw);
+3 -3
View File
@@ -1,18 +1,18 @@
import type { RuntimeEnv } from "../runtime.js";
import { displayPath } from "../utils.js";
import { CONFIG_PATH_CLAWDBOT } from "./paths.js";
import { CONFIG_PATH } from "./paths.js";
type LogConfigUpdatedOptions = {
path?: string;
suffix?: string;
};
export function formatConfigPath(path: string = CONFIG_PATH_CLAWDBOT): string {
export function formatConfigPath(path: string = CONFIG_PATH): string {
return displayPath(path);
}
export function logConfigUpdated(runtime: RuntimeEnv, opts: LogConfigUpdatedOptions = {}): void {
const path = formatConfigPath(opts.path ?? CONFIG_PATH_CLAWDBOT);
const path = formatConfigPath(opts.path ?? CONFIG_PATH);
const suffix = opts.suffix ? ` ${opts.suffix}` : "";
runtime.log(`Updated ${path}${suffix}`);
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { normalizeChannelId } from "../channels/plugins/index.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
import type { MarkdownTableMode } from "./types.base.js";
type MarkdownConfigEntry = {
@@ -44,7 +44,7 @@ function resolveMarkdownModeFromSection(
}
export function resolveMarkdownTableMode(params: {
cfg?: Partial<ClawdbotConfig>;
cfg?: Partial<MoltbotConfig>;
channel?: string | null;
accountId?: string | null;
}): MarkdownTableMode {
+3 -3
View File
@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
import type { WhatsAppConfig } from "./types.js";
export type MergeSectionOptions<T> = {
@@ -24,10 +24,10 @@ export function mergeConfigSection<T extends Record<string, unknown>>(
}
export function mergeWhatsAppConfig(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
patch: Partial<WhatsAppConfig>,
options?: MergeSectionOptions<WhatsAppConfig>,
): ClawdbotConfig {
): MoltbotConfig {
return {
...cfg,
channels: {
+5 -5
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { DEFAULT_CONTEXT_TOKENS } from "../agents/defaults.js";
import { applyModelDefaults } from "./defaults.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
describe("applyModelDefaults", () => {
it("adds default aliases when models are present", () => {
@@ -14,7 +14,7 @@ describe("applyModelDefaults", () => {
},
},
},
} satisfies ClawdbotConfig;
} satisfies MoltbotConfig;
const next = applyModelDefaults(cfg);
expect(next.agents?.defaults?.models?.["anthropic/claude-opus-4-5"]?.alias).toBe("opus");
@@ -30,7 +30,7 @@ describe("applyModelDefaults", () => {
},
},
},
} satisfies ClawdbotConfig;
} satisfies MoltbotConfig;
const next = applyModelDefaults(cfg);
@@ -47,7 +47,7 @@ describe("applyModelDefaults", () => {
},
},
},
} satisfies ClawdbotConfig;
} satisfies MoltbotConfig;
const next = applyModelDefaults(cfg);
@@ -69,7 +69,7 @@ describe("applyModelDefaults", () => {
},
},
},
} satisfies ClawdbotConfig;
} satisfies MoltbotConfig;
const next = applyModelDefaults(cfg);
const model = next.models?.providers?.myproxy?.models?.[0];
+2 -2
View File
@@ -13,7 +13,7 @@ describe("normalizeConfigPaths", () => {
const cfg = normalizeConfigPaths({
tools: { exec: { pathPrepend: ["~/bin"] } },
plugins: { load: { paths: ["~/plugins/a"] } },
logging: { file: "~/.clawdbot/logs/clawdbot.log" },
logging: { file: "~/.clawdbot/logs/moltbot.log" },
hooks: {
path: "~/.clawdbot/hooks.json5",
transformsDir: "~/hooks-xform",
@@ -47,7 +47,7 @@ describe("normalizeConfigPaths", () => {
});
expect(cfg.plugins?.load?.paths?.[0]).toBe(path.join(home, "plugins", "a"));
expect(cfg.logging?.file).toBe(path.join(home, ".clawdbot", "logs", "clawdbot.log"));
expect(cfg.logging?.file).toBe(path.join(home, ".clawdbot", "logs", "moltbot.log"));
expect(cfg.hooks?.path).toBe(path.join(home, ".clawdbot", "hooks.json5"));
expect(cfg.hooks?.transformsDir).toBe(path.join(home, "hooks-xform"));
expect(cfg.tools?.exec?.pathPrepend?.[0]).toBe(path.join(home, "bin"));
+2 -2
View File
@@ -1,5 +1,5 @@
import { resolveUserPath } from "../utils.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
const PATH_VALUE_RE = /^~(?=$|[\\/])/;
@@ -50,7 +50,7 @@ function normalizeAny(key: string | undefined, value: unknown): unknown {
* Goal: accept `~/...` consistently across config file + env overrides, while
* keeping the surface area small and predictable.
*/
export function normalizeConfigPaths(cfg: ClawdbotConfig): ClawdbotConfig {
export function normalizeConfigPaths(cfg: MoltbotConfig): MoltbotConfig {
if (!cfg || typeof cfg !== "object") return cfg;
normalizeAny(undefined, cfg);
return cfg;
+8 -8
View File
@@ -1,6 +1,6 @@
import os from "node:os";
import path from "node:path";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
/**
* Nix mode detection: When CLAWDBOT_NIX_MODE=1, the gateway is running under Nix.
@@ -39,12 +39,12 @@ function resolveUserPath(input: string): string {
return path.resolve(trimmed);
}
export const STATE_DIR_CLAWDBOT = resolveStateDir();
export const STATE_DIR = resolveStateDir();
/**
* Config file path (JSON5).
* Can be overridden via CLAWDBOT_CONFIG_PATH environment variable.
* Default: ~/.clawdbot/clawdbot.json (or $CLAWDBOT_STATE_DIR/clawdbot.json)
* Default: ~/.clawdbot/moltbot.json (or $CLAWDBOT_STATE_DIR/moltbot.json)
*/
export function resolveConfigPath(
env: NodeJS.ProcessEnv = process.env,
@@ -52,21 +52,21 @@ export function resolveConfigPath(
): string {
const override = env.CLAWDBOT_CONFIG_PATH?.trim();
if (override) return resolveUserPath(override);
return path.join(stateDir, "clawdbot.json");
return path.join(stateDir, "moltbot.json");
}
export const CONFIG_PATH_CLAWDBOT = resolveConfigPath();
export const CONFIG_PATH = resolveConfigPath();
export const DEFAULT_GATEWAY_PORT = 18789;
/**
* Gateway lock directory (ephemeral).
* Default: os.tmpdir()/clawdbot-<uid> (uid suffix when available).
* Default: os.tmpdir()/moltbot-<uid> (uid suffix when available).
*/
export function resolveGatewayLockDir(tmpdir: () => string = os.tmpdir): string {
const base = tmpdir();
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
const suffix = uid != null ? `clawdbot-${uid}` : "clawdbot";
const suffix = uid != null ? `moltbot-${uid}` : "moltbot";
return path.join(base, suffix);
}
@@ -97,7 +97,7 @@ export function resolveOAuthPath(
}
export function resolveGatewayPort(
cfg?: ClawdbotConfig,
cfg?: MoltbotConfig,
env: NodeJS.ProcessEnv = process.env,
): number {
const envRaw = env.CLAWDBOT_GATEWAY_PORT?.trim();
+20 -20
View File
@@ -1,4 +1,4 @@
import type { ClawdbotConfig } from "./config.js";
import type { MoltbotConfig } from "./config.js";
import {
getChatChannelMeta,
listChatChannels,
@@ -17,7 +17,7 @@ type PluginEnableChange = {
};
export type PluginAutoEnableResult = {
config: ClawdbotConfig;
config: MoltbotConfig;
changes: string[];
};
@@ -59,7 +59,7 @@ function accountsHaveKeys(value: unknown, keys: string[]): boolean {
}
function resolveChannelConfig(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
channelId: string,
): Record<string, unknown> | null {
const channels = cfg.channels as Record<string, unknown> | undefined;
@@ -67,7 +67,7 @@ function resolveChannelConfig(
return isRecord(entry) ? entry : null;
}
function isTelegramConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): boolean {
function isTelegramConfigured(cfg: MoltbotConfig, env: NodeJS.ProcessEnv): boolean {
if (hasNonEmptyString(env.TELEGRAM_BOT_TOKEN)) return true;
const entry = resolveChannelConfig(cfg, "telegram");
if (!entry) return false;
@@ -76,7 +76,7 @@ function isTelegramConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): bool
return recordHasKeys(entry);
}
function isDiscordConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): boolean {
function isDiscordConfigured(cfg: MoltbotConfig, env: NodeJS.ProcessEnv): boolean {
if (hasNonEmptyString(env.DISCORD_BOT_TOKEN)) return true;
const entry = resolveChannelConfig(cfg, "discord");
if (!entry) return false;
@@ -85,7 +85,7 @@ function isDiscordConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): boole
return recordHasKeys(entry);
}
function isSlackConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): boolean {
function isSlackConfigured(cfg: MoltbotConfig, env: NodeJS.ProcessEnv): boolean {
if (
hasNonEmptyString(env.SLACK_BOT_TOKEN) ||
hasNonEmptyString(env.SLACK_APP_TOKEN) ||
@@ -106,7 +106,7 @@ function isSlackConfigured(cfg: ClawdbotConfig, env: NodeJS.ProcessEnv): boolean
return recordHasKeys(entry);
}
function isSignalConfigured(cfg: ClawdbotConfig): boolean {
function isSignalConfigured(cfg: MoltbotConfig): boolean {
const entry = resolveChannelConfig(cfg, "signal");
if (!entry) return false;
if (
@@ -122,27 +122,27 @@ function isSignalConfigured(cfg: ClawdbotConfig): boolean {
return recordHasKeys(entry);
}
function isIMessageConfigured(cfg: ClawdbotConfig): boolean {
function isIMessageConfigured(cfg: MoltbotConfig): boolean {
const entry = resolveChannelConfig(cfg, "imessage");
if (!entry) return false;
if (hasNonEmptyString(entry.cliPath)) return true;
return recordHasKeys(entry);
}
function isWhatsAppConfigured(cfg: ClawdbotConfig): boolean {
function isWhatsAppConfigured(cfg: MoltbotConfig): boolean {
if (hasAnyWhatsAppAuth(cfg)) return true;
const entry = resolveChannelConfig(cfg, "whatsapp");
if (!entry) return false;
return recordHasKeys(entry);
}
function isGenericChannelConfigured(cfg: ClawdbotConfig, channelId: string): boolean {
function isGenericChannelConfigured(cfg: MoltbotConfig, channelId: string): boolean {
const entry = resolveChannelConfig(cfg, channelId);
return recordHasKeys(entry);
}
export function isChannelConfigured(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
channelId: string,
env: NodeJS.ProcessEnv = process.env,
): boolean {
@@ -164,7 +164,7 @@ export function isChannelConfigured(
}
}
function collectModelRefs(cfg: ClawdbotConfig): string[] {
function collectModelRefs(cfg: MoltbotConfig): string[] {
const refs: string[] = [];
const pushModelRef = (value: unknown) => {
if (typeof value === "string" && value.trim()) refs.push(value.trim());
@@ -208,7 +208,7 @@ function extractProviderFromModelRef(value: string): string | null {
return normalizeProviderId(trimmed.slice(0, slash));
}
function isProviderConfigured(cfg: ClawdbotConfig, providerId: string): boolean {
function isProviderConfigured(cfg: MoltbotConfig, providerId: string): boolean {
const normalized = normalizeProviderId(providerId);
const profiles = cfg.auth?.profiles;
@@ -237,7 +237,7 @@ function isProviderConfigured(cfg: ClawdbotConfig, providerId: string): boolean
}
function resolveConfiguredPlugins(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
env: NodeJS.ProcessEnv,
): PluginEnableChange[] {
const changes: PluginEnableChange[] = [];
@@ -269,12 +269,12 @@ function resolveConfiguredPlugins(
return changes;
}
function isPluginExplicitlyDisabled(cfg: ClawdbotConfig, pluginId: string): boolean {
function isPluginExplicitlyDisabled(cfg: MoltbotConfig, pluginId: string): boolean {
const entry = cfg.plugins?.entries?.[pluginId];
return entry?.enabled === false;
}
function isPluginDenied(cfg: ClawdbotConfig, pluginId: string): boolean {
function isPluginDenied(cfg: MoltbotConfig, pluginId: string): boolean {
const deny = cfg.plugins?.deny;
return Array.isArray(deny) && deny.includes(pluginId);
}
@@ -289,7 +289,7 @@ function resolvePreferredOverIds(pluginId: string): string[] {
}
function shouldSkipPreferredPluginAutoEnable(
cfg: ClawdbotConfig,
cfg: MoltbotConfig,
entry: PluginEnableChange,
configured: PluginEnableChange[],
): boolean {
@@ -305,7 +305,7 @@ function shouldSkipPreferredPluginAutoEnable(
return false;
}
function ensureAllowlisted(cfg: ClawdbotConfig, pluginId: string): ClawdbotConfig {
function ensureAllowlisted(cfg: MoltbotConfig, pluginId: string): MoltbotConfig {
const allow = cfg.plugins?.allow;
if (!Array.isArray(allow) || allow.includes(pluginId)) return cfg;
return {
@@ -317,7 +317,7 @@ function ensureAllowlisted(cfg: ClawdbotConfig, pluginId: string): ClawdbotConfi
};
}
function enablePluginEntry(cfg: ClawdbotConfig, pluginId: string): ClawdbotConfig {
function enablePluginEntry(cfg: MoltbotConfig, pluginId: string): MoltbotConfig {
const entries = {
...cfg.plugins?.entries,
[pluginId]: {
@@ -346,7 +346,7 @@ function formatAutoEnableChange(entry: PluginEnableChange): string {
}
export function applyPluginAutoEnable(params: {
config: ClawdbotConfig;
config: MoltbotConfig;
env?: NodeJS.ProcessEnv;
}): PluginAutoEnableResult {
const env = params.env ?? process.env;
+4 -4
View File
@@ -6,7 +6,7 @@ import {
setConfigOverride,
unsetConfigOverride,
} from "./runtime-overrides.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
describe("runtime overrides", () => {
beforeEach(() => {
@@ -15,8 +15,8 @@ describe("runtime overrides", () => {
it("sets and applies nested overrides", () => {
const cfg = {
messages: { responsePrefix: "[clawdbot]" },
} as ClawdbotConfig;
messages: { responsePrefix: "[moltbot]" },
} as MoltbotConfig;
setConfigOverride("messages.responsePrefix", "[debug]");
const next = applyConfigOverrides(cfg);
expect(next.messages?.responsePrefix).toBe("[debug]");
@@ -25,7 +25,7 @@ describe("runtime overrides", () => {
it("merges object overrides without clobbering siblings", () => {
const cfg = {
channels: { whatsapp: { dmPolicy: "pairing", allowFrom: ["+1"] } },
} as ClawdbotConfig;
} as MoltbotConfig;
setConfigOverride("channels.whatsapp.dmPolicy", "open");
const next = applyConfigOverrides(cfg);
expect(next.channels?.whatsapp?.dmPolicy).toBe("open");
+3 -3
View File
@@ -1,5 +1,5 @@
import { parseConfigPath, setConfigValueAtPath, unsetConfigValueAtPath } from "./config-paths.js";
import type { ClawdbotConfig } from "./types.js";
import type { MoltbotConfig } from "./types.js";
type OverrideTree = Record<string, unknown>;
@@ -64,7 +64,7 @@ export function unsetConfigOverride(pathRaw: string): {
return { ok: true, removed };
}
export function applyConfigOverrides(cfg: ClawdbotConfig): ClawdbotConfig {
export function applyConfigOverrides(cfg: MoltbotConfig): MoltbotConfig {
if (!overrides || Object.keys(overrides).length === 0) return cfg;
return mergeOverrides(cfg, overrides) as ClawdbotConfig;
return mergeOverrides(cfg, overrides) as MoltbotConfig;
}
+8 -8
View File
@@ -1,6 +1,6 @@
import { CHANNEL_IDS } from "../channels/registry.js";
import { VERSION } from "../version.js";
import { ClawdbotSchema } from "./zod-schema.js";
import { MoltbotSchema } from "./zod-schema.js";
export type ConfigUiHint = {
label?: string;
@@ -15,7 +15,7 @@ export type ConfigUiHint = {
export type ConfigUiHints = Record<string, ConfigUiHint>;
export type ConfigSchema = ReturnType<typeof ClawdbotSchema.toJSONSchema>;
export type ConfigSchema = ReturnType<typeof MoltbotSchema.toJSONSchema>;
type JsonSchemaNode = Record<string, unknown>;
@@ -364,7 +364,7 @@ const FIELD_LABELS: Record<string, string> = {
};
const FIELD_HELP: Record<string, string> = {
"meta.lastTouchedVersion": "Auto-set when Clawdbot writes the config.",
"meta.lastTouchedVersion": "Auto-set when Moltbot writes the config.",
"meta.lastTouchedAt": "ISO timestamp of the last config write (auto-set).",
"update.channel": 'Update channel for git + npm installs ("stable", "beta", or "dev").',
"update.checkOnStart": "Check for npm updates when the gateway starts (default: true).",
@@ -382,7 +382,7 @@ const FIELD_HELP: Record<string, string> = {
"Required by default for gateway access (unless using Tailscale Serve identity); required for non-loopback binds.",
"gateway.auth.password": "Required for Tailscale funnel.",
"gateway.controlUi.basePath":
"Optional URL prefix where the Control UI is served (e.g. /clawdbot).",
"Optional URL prefix where the Control UI is served (e.g. /moltbot).",
"gateway.controlUi.allowInsecureAuth":
"Allow Control UI auth over insecure HTTP (token-only; not recommended).",
"gateway.controlUi.dangerouslyDisableDeviceAuth":
@@ -557,7 +557,7 @@ const FIELD_HELP: Record<string, string> = {
"plugins.entries.*.enabled": "Overrides plugin enable/disable for this entry (restart required).",
"plugins.entries.*.config": "Plugin-defined config payload (schema is provided by the plugin).",
"plugins.installs":
"CLI-managed install metadata (used by `clawdbot plugins update` to locate install sources).",
"CLI-managed install metadata (used by `moltbot plugins update` to locate install sources).",
"plugins.installs.*.source": 'Install source ("npm", "archive", or "path").',
"plugins.installs.*.spec": "Original npm spec used for install (if source is npm).",
"plugins.installs.*.sourcePath": "Original archive/path used for install (if any).",
@@ -679,7 +679,7 @@ const FIELD_PLACEHOLDERS: Record<string, string> = {
"gateway.remote.url": "ws://host:18789",
"gateway.remote.tlsFingerprint": "sha256:ab12cd34…",
"gateway.remote.sshTarget": "user@host",
"gateway.controlUi.basePath": "/clawdbot",
"gateway.controlUi.basePath": "/moltbot",
"channels.mattermost.baseUrl": "https://chat.example.com",
"agents.list[].identity.avatar": "avatars/clawd.png",
};
@@ -948,11 +948,11 @@ function stripChannelSchema(schema: ConfigSchema): ConfigSchema {
function buildBaseConfigSchema(): ConfigSchemaResponse {
if (cachedBase) return cachedBase;
const schema = ClawdbotSchema.toJSONSchema({
const schema = MoltbotSchema.toJSONSchema({
target: "draft-07",
unrepresentable: "any",
});
schema.title = "ClawdbotConfig";
schema.title = "MoltbotConfig";
const hints = applySensitiveHints(buildBaseHints());
const next = {
schema: stripChannelSchema(schema),
+10 -10
View File
@@ -95,7 +95,7 @@ describe("sessions", () => {
it("updateLastRoute persists channel and target", async () => {
const mainSessionKey = "agent:main:main";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(
storePath,
@@ -148,7 +148,7 @@ describe("sessions", () => {
it("updateLastRoute prefers explicit deliveryContext", async () => {
const mainSessionKey = "agent:main:main";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "{}", "utf-8");
@@ -178,7 +178,7 @@ describe("sessions", () => {
it("updateLastRoute records origin + group metadata when ctx is provided", async () => {
const sessionKey = "agent:main:whatsapp:group:123@g.us";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "{}", "utf-8");
@@ -208,7 +208,7 @@ describe("sessions", () => {
it("updateSessionStoreEntry preserves existing fields when patching", async () => {
const sessionKey = "agent:main:main";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(
storePath,
@@ -238,7 +238,7 @@ describe("sessions", () => {
});
it("updateSessionStore preserves concurrent additions", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "{}", "utf-8");
@@ -257,7 +257,7 @@ describe("sessions", () => {
});
it("recovers from array-backed session stores", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "[]", "utf-8");
@@ -273,7 +273,7 @@ describe("sessions", () => {
});
it("normalizes last route fields on write", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(storePath, "{}", "utf-8");
@@ -299,7 +299,7 @@ describe("sessions", () => {
});
it("updateSessionStore keeps deletions when concurrent writes happen", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(
storePath,
@@ -331,7 +331,7 @@ describe("sessions", () => {
it("loadSessionStore auto-migrates legacy provider keys to channel keys", async () => {
const mainSessionKey = "agent:main:main";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(
storePath,
@@ -411,7 +411,7 @@ describe("sessions", () => {
it("updateSessionStoreEntry merges concurrent patches", async () => {
const mainSessionKey = "agent:main:main";
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-sessions-"));
const storePath = path.join(dir, "sessions.json");
await fs.writeFile(
storePath,
+1 -1
View File
@@ -99,7 +99,7 @@ export async function appendAssistantMessageToSessionTranscript(params: {
role: "assistant",
content: [{ type: "text", text: mirrorText }],
api: "openai-responses",
provider: "clawdbot",
provider: "moltbot",
model: "delivery-mirror",
usage: {
input: 0,
+1 -1
View File
@@ -3,7 +3,7 @@ import { vi } from "vitest";
import { withTempHome as withTempHomeBase } from "../../test/helpers/temp-home.js";
export async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
return withTempHomeBase(fn, { prefix: "clawdbot-config-" });
return withTempHomeBase(fn, { prefix: "moltbot-config-" });
}
/**
+4 -4
View File
@@ -24,9 +24,9 @@ import type { PluginsConfig } from "./types.plugins.js";
import type { SkillsConfig } from "./types.skills.js";
import type { ToolsConfig } from "./types.tools.js";
export type ClawdbotConfig = {
export type MoltbotConfig = {
meta?: {
/** Last clawdbot version that wrote this config. */
/** Last moltbot version that wrote this config. */
lastTouchedVersion?: string;
/** ISO timestamp when this config was last written. */
lastTouchedAt?: string;
@@ -65,7 +65,7 @@ export type ClawdbotConfig = {
};
browser?: BrowserConfig;
ui?: {
/** Accent color for Clawdbot UI chrome (hex). */
/** Accent color for Moltbot UI chrome (hex). */
seamColor?: string;
assistant?: {
/** Assistant display name for UI surfaces. */
@@ -113,7 +113,7 @@ export type ConfigFileSnapshot = {
raw: string | null;
parsed: unknown;
valid: boolean;
config: ClawdbotConfig;
config: MoltbotConfig;
hash?: string;
issues: ConfigValidationIssue[];
warnings: ConfigValidationIssue[];
+1 -1
View File
@@ -62,7 +62,7 @@ export type TalkConfig = {
export type GatewayControlUiConfig = {
/** If false, the Gateway will not serve the Control UI (default /). */
enabled?: boolean;
/** Optional base path prefix for the Control UI (e.g. "/clawdbot"). */
/** Optional base path prefix for the Control UI (e.g. "/moltbot"). */
basePath?: string;
/** Allow token-only auth over insecure HTTP (default: false). */
allowInsecureAuth?: boolean;
+1 -1
View File
@@ -23,7 +23,7 @@ export type IMessageAccountConfig = {
cliPath?: string;
/** Optional Messages db path override. */
dbPath?: string;
/** Remote host for SCP when attachments live on a different machine (e.g., clawdbot@192.168.64.3). */
/** Remote host for SCP when attachments live on a different machine (e.g., moltbot@192.168.64.3). */
remoteHost?: string;
/** Optional default send service (imessage|sms|auto). */
service?: "imessage" | "sms" | "auto";
+1 -1
View File
@@ -27,7 +27,7 @@ export type WhatsAppConfig = {
sendReadReceipts?: boolean;
/**
* Inbound message prefix (WhatsApp only).
* Default: `[{agents.list[].identity.name}]` (or `[clawdbot]`) when allowFrom is empty, else `""`.
* Default: `[{agents.list[].identity.name}]` (or `[moltbot]`) when allowFrom is empty, else `""`.
*/
messagePrefix?: string;
/** Direct message access policy (default: pairing). */
+9 -9
View File
@@ -12,8 +12,8 @@ import { validateJsonSchemaValue } from "../plugins/schema-validator.js";
import { findDuplicateAgentDirs, formatDuplicateAgentDirError } from "./agent-dirs.js";
import { applyAgentDefaults, applyModelDefaults, applySessionDefaults } from "./defaults.js";
import { findLegacyConfigIssues } from "./legacy.js";
import type { ClawdbotConfig, ConfigValidationIssue } from "./types.js";
import { ClawdbotSchema } from "./zod-schema.js";
import type { MoltbotConfig, ConfigValidationIssue } from "./types.js";
import { MoltbotSchema } from "./zod-schema.js";
const AVATAR_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
const AVATAR_DATA_RE = /^data:/i;
@@ -29,7 +29,7 @@ function isWorkspaceAvatarPath(value: string, workspaceDir: string): boolean {
return !path.isAbsolute(relative);
}
function validateIdentityAvatar(config: ClawdbotConfig): ConfigValidationIssue[] {
function validateIdentityAvatar(config: MoltbotConfig): ConfigValidationIssue[] {
const agents = config.agents?.list;
if (!Array.isArray(agents) || agents.length === 0) return [];
const issues: ConfigValidationIssue[] = [];
@@ -71,7 +71,7 @@ function validateIdentityAvatar(config: ClawdbotConfig): ConfigValidationIssue[]
export function validateConfigObject(
raw: unknown,
): { ok: true; config: ClawdbotConfig } | { ok: false; issues: ConfigValidationIssue[] } {
): { ok: true; config: MoltbotConfig } | { ok: false; issues: ConfigValidationIssue[] } {
const legacyIssues = findLegacyConfigIssues(raw);
if (legacyIssues.length > 0) {
return {
@@ -82,7 +82,7 @@ export function validateConfigObject(
})),
};
}
const validated = ClawdbotSchema.safeParse(raw);
const validated = MoltbotSchema.safeParse(raw);
if (!validated.success) {
return {
ok: false,
@@ -92,7 +92,7 @@ export function validateConfigObject(
})),
};
}
const duplicates = findDuplicateAgentDirs(validated.data as ClawdbotConfig);
const duplicates = findDuplicateAgentDirs(validated.data as MoltbotConfig);
if (duplicates.length > 0) {
return {
ok: false,
@@ -104,14 +104,14 @@ export function validateConfigObject(
],
};
}
const avatarIssues = validateIdentityAvatar(validated.data as ClawdbotConfig);
const avatarIssues = validateIdentityAvatar(validated.data as MoltbotConfig);
if (avatarIssues.length > 0) {
return { ok: false, issues: avatarIssues };
}
return {
ok: true,
config: applyModelDefaults(
applyAgentDefaults(applySessionDefaults(validated.data as ClawdbotConfig)),
applyAgentDefaults(applySessionDefaults(validated.data as MoltbotConfig)),
),
};
}
@@ -123,7 +123,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
export function validateConfigObjectWithPlugins(raw: unknown):
| {
ok: true;
config: ClawdbotConfig;
config: MoltbotConfig;
warnings: ConfigValidationIssue[];
}
| {
+5 -5
View File
@@ -1,4 +1,4 @@
export type ClawdbotVersion = {
export type MoltbotVersion = {
major: number;
minor: number;
patch: number;
@@ -7,7 +7,7 @@ export type ClawdbotVersion = {
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-(\d+))?/;
export function parseClawdbotVersion(raw: string | null | undefined): ClawdbotVersion | null {
export function parseMoltbotVersion(raw: string | null | undefined): MoltbotVersion | null {
if (!raw) return null;
const match = raw.trim().match(VERSION_RE);
if (!match) return null;
@@ -20,12 +20,12 @@ export function parseClawdbotVersion(raw: string | null | undefined): ClawdbotVe
};
}
export function compareClawdbotVersions(
export function compareMoltbotVersions(
a: string | null | undefined,
b: string | null | undefined,
): number | null {
const parsedA = parseClawdbotVersion(a);
const parsedB = parseClawdbotVersion(b);
const parsedA = parseMoltbotVersion(a);
const parsedB = parseMoltbotVersion(b);
if (!parsedA || !parsedB) return null;
if (parsedA.major !== parsedB.major) return parsedA.major < parsedB.major ? -1 : 1;
if (parsedA.minor !== parsedB.minor) return parsedA.minor < parsedB.minor ? -1 : 1;
+1 -1
View File
@@ -27,7 +27,7 @@ const NodeHostSchema = z
.strict()
.optional();
export const ClawdbotSchema = z
export const MoltbotSchema = z
.object({
meta: z
.object({