chore: align with moltbot rename (upstream moltbot/moltbot)

- package.json: name moltbot, bin moltbot + clawdbot (compat shim)
- paths.ts: MOLTBOT_* preferred, CLAWDBOT_* legacy; default ~/.clawdbot; config moltbot.json; export STATE_DIR/CONFIG_PATH
- render.yaml: service name moltbot, disk moltbot-data; CLAWDBOT_* env vars and /data/.clawdbot (match upstream)
- render-start.sh: support CLAWDBOT_* and MOLTBOT_* for state/token; write moltbot.json
- Dockerfile: MOLTBOT_DOCKER_APT_PACKAGES, MOLTBOT_PREFER_PNPM
- config/io.ts: MOLTBOT_* env for gateway token and config cache
- Replace STATE_DIR_CLAWDBOT/CONFIG_PATH_CLAWDBOT with STATE_DIR_MOLTBOT/CONFIG_PATH_MOLTBOT across src; paths exports both for compat
This commit is contained in:
Ojus Save 2026-01-27 09:38:28 -08:00
parent ed70785316
commit 3518abc344
37 changed files with 227 additions and 178 deletions

View File

@ -8,10 +8,10 @@ RUN corepack enable
WORKDIR /app
ARG CLAWDBOT_DOCKER_APT_PACKAGES=""
RUN if [ -n "$CLAWDBOT_DOCKER_APT_PACKAGES" ]; then \
ARG MOLTBOT_DOCKER_APT_PACKAGES=""
RUN if [ -n "$MOLTBOT_DOCKER_APT_PACKAGES" ]; then \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $CLAWDBOT_DOCKER_APT_PACKAGES && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $MOLTBOT_DOCKER_APT_PACKAGES && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
fi
@ -28,7 +28,7 @@ RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
ENV CLAWDBOT_PREFER_PNPM=1
ENV MOLTBOT_PREFER_PNPM=1
RUN pnpm ui:install
RUN pnpm ui:build

View File

@ -1,5 +1,5 @@
{
"name": "clawdbot",
"name": "moltbot",
"version": "2026.1.25",
"description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
"type": "module",
@ -10,6 +10,7 @@
"./plugin-sdk/*": "./dist/plugin-sdk/*"
},
"bin": {
"moltbot": "dist/entry.js",
"clawdbot": "dist/entry.js"
},
"files": [

View File

@ -1,6 +1,6 @@
services:
- type: web
name: clawdbot
name: moltbot
runtime: docker
plan: starter
dockerCommand: /bin/sh scripts/render-start.sh
@ -34,6 +34,6 @@ services:
- key: DEEPGRAM_API_KEY
sync: false
disk:
name: clawdbot-data
name: moltbot-data
mountPath: /data
sizeGB: 1

View File

@ -5,6 +5,7 @@
echo "=== Render startup script ==="
echo "HOME=${HOME:-not set}"
echo "CLAWDBOT_STATE_DIR=${CLAWDBOT_STATE_DIR:-not set}"
echo "MOLTBOT_STATE_DIR=${MOLTBOT_STATE_DIR:-not set}"
echo "User: $(whoami 2>/dev/null || echo unknown)"
echo "UID: $(id -u 2>/dev/null || echo unknown)"
echo "PWD: $(pwd)"
@ -19,29 +20,30 @@ if [ -z "${HOME}" ]; then
echo "Set HOME to: ${HOME}"
fi
# Use CLAWDBOT_STATE_DIR if set and writable, otherwise use HOME/.clawdbot
CONFIG_DIR="${HOME}/.clawdbot"
if [ -n "${CLAWDBOT_STATE_DIR}" ]; then
# Test if we can write to it (disable exit on error for this test)
set +e
mkdir -p "${CLAWDBOT_STATE_DIR}" 2>/dev/null
touch "${CLAWDBOT_STATE_DIR}/.test" 2>/dev/null
if [ $? -eq 0 ]; then
rm -f "${CLAWDBOT_STATE_DIR}/.test" 2>/dev/null
CONFIG_DIR="${CLAWDBOT_STATE_DIR}"
echo "Using CLAWDBOT_STATE_DIR: ${CONFIG_DIR}"
else
echo "Warning: ${CLAWDBOT_STATE_DIR} not writable, using ${CONFIG_DIR}"
# Prefer MOLTBOT_STATE_DIR or CLAWDBOT_STATE_DIR (legacy), else HOME/.moltbot then HOME/.clawdbot
CONFIG_DIR="${HOME}/.moltbot"
for var in MOLTBOT_STATE_DIR CLAWDBOT_STATE_DIR; do
eval "val=\${${var}:-}"
if [ -n "$val" ]; then
set +e
mkdir -p "$val" 2>/dev/null
touch "$val/.test" 2>/dev/null
if [ $? -eq 0 ]; then
rm -f "$val/.test" 2>/dev/null
CONFIG_DIR="$val"
echo "Using ${var}: ${CONFIG_DIR}"
fi
set -e
break
fi
set -e
fi
done
CONFIG_FILE="${CONFIG_DIR}/clawdbot.json"
CONFIG_FILE="${CONFIG_DIR}/moltbot.json"
echo "Config dir: ${CONFIG_DIR}"
echo "Config file: ${CONFIG_FILE}"
# Create config directory (this should always work for HOME/.clawdbot)
# Create config directory (fallback: HOME/.moltbot or HOME/.clawdbot)
if ! mkdir -p "${CONFIG_DIR}" 2>/dev/null; then
echo "ERROR: Failed to create config directory: ${CONFIG_DIR}"
exit 1
@ -73,14 +75,17 @@ if [ ! -f "${CONFIG_FILE}" ]; then
exit 1
fi
# Set environment variables for gateway
# Set environment variables for gateway (app accepts MOLTBOT_* or CLAWDBOT_*)
export CLAWDBOT_STATE_DIR="${CONFIG_DIR}"
export CLAWDBOT_CONFIG_PATH="${CONFIG_FILE}"
export CLAWDBOT_CONFIG_CACHE_MS=0
export MOLTBOT_STATE_DIR="${CONFIG_DIR}"
export MOLTBOT_CONFIG_PATH="${CONFIG_FILE}"
export MOLTBOT_CONFIG_CACHE_MS=0
echo "=== Starting gateway ==="
echo "CLAWDBOT_STATE_DIR=${CLAWDBOT_STATE_DIR}"
echo "CLAWDBOT_CONFIG_PATH=${CLAWDBOT_CONFIG_PATH}"
echo "CONFIG_DIR=${CONFIG_DIR}"
echo "CONFIG_FILE=${CONFIG_FILE}"
# Verify node is available
if ! command -v node >/dev/null 2>&1; then
@ -103,13 +108,14 @@ fi
echo "Found dist/index.js"
# Check if token is set
if [ -z "${CLAWDBOT_GATEWAY_TOKEN}" ]; then
echo "ERROR: CLAWDBOT_GATEWAY_TOKEN is not set"
# Token: CLAWDBOT_GATEWAY_TOKEN (legacy) or MOLTBOT_GATEWAY_TOKEN
GATEWAY_TOKEN="${CLAWDBOT_GATEWAY_TOKEN:-${MOLTBOT_GATEWAY_TOKEN}}"
if [ -z "${GATEWAY_TOKEN}" ]; then
echo "ERROR: CLAWDBOT_GATEWAY_TOKEN or MOLTBOT_GATEWAY_TOKEN must be set"
exit 1
fi
echo "Token is set (length: ${#CLAWDBOT_GATEWAY_TOKEN})"
echo "Token is set (length: ${#GATEWAY_TOKEN})"
# Enable strict error handling for the final exec
set -e
@ -120,5 +126,5 @@ exec node dist/index.js gateway \
--port 8080 \
--bind lan \
--auth token \
--token "${CLAWDBOT_GATEWAY_TOKEN}" \
--token "${GATEWAY_TOKEN}" \
--allow-unconfigured

View File

@ -2,9 +2,9 @@ import os from "node:os";
import path from "node:path";
import { CHANNEL_IDS } from "../../channels/registry.js";
import { STATE_DIR_CLAWDBOT } from "../../config/config.js";
import { STATE_DIR_MOLTBOT } from "../../config/config.js";
export const DEFAULT_SANDBOX_WORKSPACE_ROOT = path.join(os.homedir(), ".clawdbot", "sandboxes");
export const DEFAULT_SANDBOX_WORKSPACE_ROOT = path.join(os.homedir(), ".moltbot", "sandboxes");
export const DEFAULT_SANDBOX_IMAGE = "clawdbot-sandbox:bookworm-slim";
export const DEFAULT_SANDBOX_CONTAINER_PREFIX = "clawdbot-sbx-";
@ -48,7 +48,7 @@ export const DEFAULT_SANDBOX_BROWSER_AUTOSTART_TIMEOUT_MS = 12_000;
export const SANDBOX_AGENT_WORKSPACE_MOUNT = "/agent";
const resolvedSandboxStateDir = STATE_DIR_CLAWDBOT ?? path.join(os.homedir(), ".clawdbot");
const resolvedSandboxStateDir = STATE_DIR_MOLTBOT ?? path.join(os.homedir(), ".moltbot");
export const SANDBOX_STATE_DIR = path.join(resolvedSandboxStateDir, "sandbox");
export const SANDBOX_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "containers.json");
export const SANDBOX_BROWSER_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "browsers.json");

View File

@ -1,6 +1,6 @@
import path from "node:path";
import { STATE_DIR_CLAWDBOT } from "../config/paths.js";
import { STATE_DIR_MOLTBOT } from "../config/paths.js";
import { loadJsonFile, saveJsonFile } from "../infra/json-file.js";
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
import type { SubagentRunRecord } from "./subagent-registry.js";
@ -31,7 +31,7 @@ type LegacySubagentRunRecord = PersistedSubagentRunRecord & {
};
export function resolveSubagentRegistryPath(): string {
return path.join(STATE_DIR_CLAWDBOT, "subagents", "runs.json");
return path.join(STATE_DIR_MOLTBOT, "subagents", "runs.json");
}
export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {

View File

@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
import type { Command } from "commander";
import { STATE_DIR_CLAWDBOT } from "../config/paths.js";
import { STATE_DIR_MOLTBOT } from "../config/paths.js";
import { danger, info } from "../globals.js";
import { copyToClipboard } from "../infra/clipboard.js";
import { defaultRuntime } from "../runtime.js";
@ -20,7 +20,7 @@ function bundledExtensionRootDir() {
}
function installedExtensionRootDir() {
return path.join(STATE_DIR_CLAWDBOT, "browser", "chrome-extension");
return path.join(STATE_DIR_MOLTBOT, "browser", "chrome-extension");
}
function hasManifest(dir: string) {
@ -36,7 +36,7 @@ export async function installChromeExtension(opts?: {
throw new Error("Bundled Chrome extension is missing. Reinstall Clawdbot and try again.");
}
const stateDir = opts?.stateDir ?? STATE_DIR_CLAWDBOT;
const stateDir = opts?.stateDir ?? STATE_DIR_MOLTBOT;
const dest = path.join(stateDir, "browser", "chrome-extension");
fs.mkdirSync(path.dirname(dest), { recursive: true });

View File

@ -4,7 +4,7 @@ import path from "node:path";
import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
import { handleReset } from "../../commands/onboard-helpers.js";
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js";
import { CONFIG_PATH_MOLTBOT, writeConfigFile } from "../../config/config.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveUserPath, shortenHomePath } from "../../utils.js";
@ -89,7 +89,7 @@ export async function ensureDevGatewayConfig(opts: { reset?: boolean }) {
await handleReset("full", workspace, defaultRuntime);
}
const configExists = fs.existsSync(CONFIG_PATH_CLAWDBOT);
const configExists = fs.existsSync(CONFIG_PATH_MOLTBOT);
if (!opts.reset && configExists) return;
await writeConfigFile({
@ -117,6 +117,6 @@ export async function ensureDevGatewayConfig(opts: { reset?: boolean }) {
},
});
await ensureDevWorkspace(workspace);
defaultRuntime.log(`Dev config ready: ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`);
defaultRuntime.log(`Dev config ready: ${shortenHomePath(CONFIG_PATH_MOLTBOT)}`);
defaultRuntime.log(`Dev workspace ready: ${shortenHomePath(resolveUserPath(workspace))}`);
}

View File

@ -3,7 +3,7 @@ import fs from "node:fs";
import type { Command } from "commander";
import type { GatewayAuthMode } from "../../config/config.js";
import {
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
loadConfig,
readConfigFileSnapshot,
resolveGatewayPort,
@ -157,7 +157,7 @@ async function runGatewayCommand(opts: GatewayRunOpts) {
const passwordRaw = toOptionString(opts.password);
const tokenRaw = toOptionString(opts.token);
const configExists = fs.existsSync(CONFIG_PATH_CLAWDBOT);
const configExists = fs.existsSync(CONFIG_PATH_MOLTBOT);
const mode = cfg.gateway?.mode;
if (!opts.allowUnconfigured && mode !== "local") {
if (!configExists) {

View File

@ -1,7 +1,7 @@
import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { ClawdbotConfig } from "../config/config.js";
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
import { CONFIG_PATH_MOLTBOT } from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { note } from "../terminal/note.js";
import { shortenHomePath } from "../utils.js";
@ -52,7 +52,7 @@ export async function removeChannelConfigWizard(
const label = getChannelPlugin(channel)?.meta.label ?? channel;
const confirmed = guardCancel(
await confirm({
message: `Delete ${label} configuration from ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}?`,
message: `Delete ${label} configuration from ${shortenHomePath(CONFIG_PATH_MOLTBOT)}?`,
initialValue: false,
}),
runtime,

View File

@ -30,7 +30,7 @@ vi.mock("@clack/prompts", () => ({
}));
vi.mock("../config/config.js", () => ({
CONFIG_PATH_CLAWDBOT: "~/.clawdbot/clawdbot.json",
CONFIG_PATH_MOLTBOT: "~/.moltbot/moltbot.json",
readConfigFileSnapshot: mocks.readConfigFileSnapshot,
writeConfigFile: mocks.writeConfigFile,
resolveGatewayPort: mocks.resolveGatewayPort,

View File

@ -3,7 +3,7 @@ import type { ZodIssue } from "zod";
import type { ClawdbotConfig } from "../config/config.js";
import {
ClawdbotSchema,
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
migrateLegacyConfig,
readConfigFileSnapshot,
} from "../config/config.js";
@ -214,5 +214,5 @@ export async function loadAndMaybeMigrateDoctorConfig(params: {
noteOpencodeProviderOverrides(cfg);
return { cfg, path: snapshot.path ?? CONFIG_PATH_CLAWDBOT, shouldWriteConfig };
return { cfg, path: snapshot.path ?? CONFIG_PATH_MOLTBOT, shouldWriteConfig };
}

View File

@ -34,7 +34,7 @@ beforeEach(() => {
durationMs: 0,
});
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -135,7 +135,7 @@ const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {
const loadClawdbotPlugins = vi.fn().mockReturnValue({ plugins: [], diagnostics: [] });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -181,7 +181,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
createConfigIO,
readConfigFileSnapshot,
writeConfigFile,
@ -327,7 +327,7 @@ vi.mock("./doctor-state-migrations.js", () => ({
describe("doctor command", () => {
it("runs legacy state migrations in non-interactive mode without prompting", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},

View File

@ -34,7 +34,7 @@ beforeEach(() => {
durationMs: 0,
});
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -180,7 +180,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
createConfigIO,
readConfigFileSnapshot,
writeConfigFile,
@ -326,7 +326,7 @@ vi.mock("./doctor-state-migrations.js", () => ({
describe("doctor command", () => {
it("migrates routing.allowFrom to channels.whatsapp.allowFrom", { timeout: 60_000 }, async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: { routing: { allowFrom: ["+15555550123"] } },
@ -370,7 +370,7 @@ describe("doctor command", () => {
it("migrates legacy gateway services", { timeout: 60_000 }, async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -424,7 +424,7 @@ describe("doctor command", () => {
});
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},

View File

@ -34,7 +34,7 @@ beforeEach(() => {
durationMs: 0,
});
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -180,7 +180,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
createConfigIO,
readConfigFileSnapshot,
writeConfigFile,
@ -326,7 +326,7 @@ vi.mock("./doctor-state-migrations.js", () => ({
describe("doctor command", () => {
it("runs legacy state migrations in yes mode without prompting", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -385,7 +385,7 @@ describe("doctor command", () => {
it("skips gateway restarts in non-interactive mode", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -417,7 +417,7 @@ describe("doctor command", () => {
it("migrates anthropic oauth config profile id when only email profile exists", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},

View File

@ -11,7 +11,7 @@ import {
} from "../agents/model-selection.js";
import { formatCliCommand } from "../cli/command-format.js";
import type { ClawdbotConfig } from "../config/config.js";
import { CONFIG_PATH_CLAWDBOT, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
import { CONFIG_PATH_MOLTBOT, readConfigFileSnapshot, writeConfigFile } from "../config/config.js";
import { logConfigUpdated } from "../config/logging.js";
import { resolveGatewayService } from "../daemon/service.js";
import { resolveGatewayAuth } from "../gateway/auth.js";
@ -90,7 +90,7 @@ export async function doctorCommand(
});
let cfg: ClawdbotConfig = configResult.cfg;
const configPath = configResult.path ?? CONFIG_PATH_CLAWDBOT;
const configPath = configResult.path ?? CONFIG_PATH_MOLTBOT;
if (!cfg.gateway?.mode) {
const lines = [
"gateway.mode is unset; gateway start will be blocked.",
@ -174,7 +174,7 @@ export async function doctorCommand(
}
}
await noteStateIntegrity(cfg, prompter, configResult.path ?? CONFIG_PATH_CLAWDBOT);
await noteStateIntegrity(cfg, prompter, configResult.path ?? CONFIG_PATH_MOLTBOT);
cfg = await maybeRepairSandboxImages(cfg, runtime, prompter);
noteSandboxScopeWarnings(cfg);
@ -272,7 +272,7 @@ export async function doctorCommand(
cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
await writeConfigFile(cfg);
logConfigUpdated(runtime);
const backupPath = `${CONFIG_PATH_CLAWDBOT}.bak`;
const backupPath = `${CONFIG_PATH_MOLTBOT}.bak`;
if (fs.existsSync(backupPath)) {
runtime.log(`Backup: ${shortenHomePath(backupPath)}`);
}

View File

@ -34,7 +34,7 @@ beforeEach(() => {
durationMs: 0,
});
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -180,7 +180,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
createConfigIO,
readConfigFileSnapshot,
writeConfigFile,
@ -326,7 +326,7 @@ vi.mock("./doctor-state-migrations.js", () => ({
describe("doctor command", () => {
it("warns when per-agent sandbox docker/browser/prune overrides are ignored under shared scope", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -383,7 +383,7 @@ describe("doctor command", () => {
it("warns when extra workspace directories exist", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},

View File

@ -34,7 +34,7 @@ beforeEach(() => {
durationMs: 0,
});
legacyReadConfigFileSnapshot.mockReset().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -133,7 +133,7 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
const ensureAuthProfileStore = vi.fn().mockReturnValue({ version: 1, profiles: {} });
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: false,
raw: null,
parsed: {},
@ -180,7 +180,7 @@ vi.mock("../config/config.js", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
createConfigIO,
readConfigFileSnapshot,
writeConfigFile,
@ -330,7 +330,7 @@ vi.mock("./doctor-update.js", () => ({
describe("doctor command", () => {
it("warns when the state directory is missing", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -358,7 +358,7 @@ describe("doctor command", () => {
it("warns about opencode provider overrides", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},
@ -392,7 +392,7 @@ describe("doctor command", () => {
it("skips gateway auth warning when CLAWDBOT_GATEWAY_TOKEN is set", async () => {
readConfigFileSnapshot.mockResolvedValue({
path: "/tmp/clawdbot.json",
path: "/tmp/moltbot.json",
exists: true,
raw: "{}",
parsed: {},

View File

@ -17,8 +17,8 @@ const discoverAuthStorage = vi.fn().mockReturnValue({});
const discoverModels = vi.fn();
vi.mock("../config/config.js", () => ({
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
STATE_DIR_CLAWDBOT: "/tmp/clawdbot-state",
CONFIG_PATH_MOLTBOT: "/tmp/moltbot.json",
STATE_DIR_MOLTBOT: "/tmp/moltbot-state",
loadConfig,
}));

View File

@ -5,7 +5,7 @@ const writeConfigFile = vi.fn().mockResolvedValue(undefined);
const loadConfig = vi.fn().mockReturnValue({});
vi.mock("../config/config.js", () => ({
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
CONFIG_PATH_MOLTBOT: "/tmp/clawdbot.json",
readConfigFileSnapshot,
writeConfigFile,
loadConfig,

View File

@ -17,7 +17,7 @@ import {
resolveConfiguredModelRef,
resolveModelRefFromString,
} from "../../agents/model-selection.js";
import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js";
import { CONFIG_PATH_MOLTBOT, loadConfig } from "../../config/config.js";
import { getShellEnvAppliedKeys, shouldEnableShellEnvFallback } from "../../infra/shell-env.js";
import { withProgressTotals } from "../../cli/progress.js";
import {
@ -294,7 +294,7 @@ export async function modelsStatusCommand(
runtime.log(
JSON.stringify(
{
configPath: CONFIG_PATH_CLAWDBOT,
configPath: CONFIG_PATH_MOLTBOT,
agentDir,
defaultModel: defaultLabel,
resolvedDefault: resolvedLabel,
@ -341,7 +341,7 @@ export async function modelsStatusCommand(
rawModel && rawModel !== resolvedLabel ? `${resolvedLabel} (from ${rawModel})` : resolvedLabel;
runtime.log(
`${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, shortenHomePath(CONFIG_PATH_CLAWDBOT))}`,
`${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, shortenHomePath(CONFIG_PATH_MOLTBOT))}`,
);
runtime.log(
`${label("Agent dir")}${colorize(rich, theme.muted, ":")} ${colorize(

View File

@ -7,7 +7,7 @@ import { cancel, isCancel } from "@clack/prompts";
import { DEFAULT_AGENT_WORKSPACE_DIR, ensureAgentWorkspace } from "../agents/workspace.js";
import type { ClawdbotConfig } from "../config/config.js";
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
import { CONFIG_PATH_MOLTBOT } from "../config/config.js";
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
import { callGateway } from "../gateway/call.js";
import { normalizeControlUiBasePath } from "../gateway/control-ui-shared.js";
@ -274,7 +274,7 @@ export async function moveToTrash(pathname: string, runtime: RuntimeEnv): Promis
}
export async function handleReset(scope: ResetScope, workspaceDir: string, runtime: RuntimeEnv) {
await moveToTrash(CONFIG_PATH_CLAWDBOT, runtime);
await moveToTrash(CONFIG_PATH_MOLTBOT, runtime);
if (scope === "config") return;
await moveToTrash(path.join(CONFIG_DIR, "credentials"), runtime);
await moveToTrash(resolveSessionTranscriptsDirForAgent(), runtime);

View File

@ -56,8 +56,8 @@ describe("onboard (non-interactive): Vercel AI Gateway", () => {
runtime,
);
const { CONFIG_PATH_CLAWDBOT } = await import("../config/config.js");
const cfg = JSON.parse(await fs.readFile(CONFIG_PATH_CLAWDBOT, "utf8")) as {
const { CONFIG_PATH_MOLTBOT } = await import("../config/config.js");
const cfg = JSON.parse(await fs.readFile(CONFIG_PATH_MOLTBOT, "utf8")) as {
auth?: {
profiles?: Record<string, { provider?: string; mode?: string }>;
};

View File

@ -59,8 +59,8 @@ describe("onboard (non-interactive): token auth", () => {
runtime,
);
const { CONFIG_PATH_CLAWDBOT } = await import("../config/config.js");
const cfg = JSON.parse(await fs.readFile(CONFIG_PATH_CLAWDBOT, "utf8")) as {
const { CONFIG_PATH_MOLTBOT } = await import("../config/config.js");
const cfg = JSON.parse(await fs.readFile(CONFIG_PATH_MOLTBOT, "utf8")) as {
auth?: {
profiles?: Record<string, { provider?: string; mode?: string }>;
};

View File

@ -3,7 +3,7 @@ import fs from "node:fs/promises";
import JSON5 from "json5";
import { DEFAULT_AGENT_WORKSPACE_DIR, ensureAgentWorkspace } from "../agents/workspace.js";
import { type ClawdbotConfig, CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../config/config.js";
import { type ClawdbotConfig, CONFIG_PATH_MOLTBOT, writeConfigFile } from "../config/config.js";
import { formatConfigPath, logConfigUpdated } from "../config/logging.js";
import { resolveSessionTranscriptsDir } from "../config/sessions.js";
import type { RuntimeEnv } from "../runtime.js";
@ -15,7 +15,7 @@ async function readConfigFileRaw(): Promise<{
parsed: ClawdbotConfig;
}> {
try {
const raw = await fs.readFile(CONFIG_PATH_CLAWDBOT, "utf-8");
const raw = await fs.readFile(CONFIG_PATH_MOLTBOT, "utf-8");
const parsed = JSON5.parse(raw);
if (parsed && typeof parsed === "object") {
return { exists: true, parsed: parsed as ClawdbotConfig };

View File

@ -35,56 +35,56 @@ 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 () => {
await withEnvOverride({ CLAWDBOT_STATE_DIR: undefined }, async () => {
const { STATE_DIR_CLAWDBOT } = await import("./config.js");
expect(STATE_DIR_CLAWDBOT).toMatch(/\.clawdbot$/);
it("STATE_DIR_MOLTBOT defaults to ~/.moltbot when env not set", async () => {
await withEnvOverride({ MOLTBOT_STATE_DIR: undefined }, async () => {
const { STATE_DIR_MOLTBOT } = await import("./config.js");
expect(STATE_DIR_MOLTBOT).toMatch(/\.moltbot$/);
});
});
it("STATE_DIR_CLAWDBOT 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"));
it("STATE_DIR_MOLTBOT respects MOLTBOT_STATE_DIR override", async () => {
await withEnvOverride({ MOLTBOT_STATE_DIR: "/custom/state/dir" }, async () => {
const { STATE_DIR_MOLTBOT } = await import("./config.js");
expect(STATE_DIR_MOLTBOT).toBe(path.resolve("/custom/state/dir"));
});
});
it("CONFIG_PATH_CLAWDBOT defaults to ~/.clawdbot/clawdbot.json when env not set", async () => {
it("CONFIG_PATH_MOLTBOT defaults to ~/.moltbot/moltbot.json when env not set", async () => {
await withEnvOverride(
{ CLAWDBOT_CONFIG_PATH: undefined, CLAWDBOT_STATE_DIR: undefined },
{ MOLTBOT_CONFIG_PATH: undefined, MOLTBOT_STATE_DIR: undefined },
async () => {
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
expect(CONFIG_PATH_CLAWDBOT).toMatch(/\.clawdbot[\\/]clawdbot\.json$/);
const { CONFIG_PATH_MOLTBOT } = await import("./config.js");
expect(CONFIG_PATH_MOLTBOT).toMatch(/\.moltbot[\\/]clawdbot\.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_MOLTBOT respects MOLTBOT_CONFIG_PATH override", async () => {
await withEnvOverride({ MOLTBOT_CONFIG_PATH: "/nix/store/abc/moltbot.json" }, async () => {
const { CONFIG_PATH_MOLTBOT } = await import("./config.js");
expect(CONFIG_PATH_MOLTBOT).toBe(path.resolve("/nix/store/abc/moltbot.json"));
});
});
it("CONFIG_PATH_CLAWDBOT expands ~ in CLAWDBOT_CONFIG_PATH override", async () => {
it("CONFIG_PATH_MOLTBOT expands ~ in MOLTBOT_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"));
await withEnvOverride({ MOLTBOT_CONFIG_PATH: "~/.moltbot/custom.json" }, async () => {
const { CONFIG_PATH_MOLTBOT } = await import("./config.js");
expect(CONFIG_PATH_MOLTBOT).toBe(path.join(home, ".moltbot", "custom.json"));
});
});
});
it("CONFIG_PATH_CLAWDBOT uses STATE_DIR_CLAWDBOT when only state dir is overridden", async () => {
it("CONFIG_PATH_MOLTBOT uses STATE_DIR_MOLTBOT when only state dir is overridden", async () => {
await withEnvOverride(
{
CLAWDBOT_CONFIG_PATH: undefined,
CLAWDBOT_STATE_DIR: "/custom/state",
MOLTBOT_CONFIG_PATH: undefined,
MOLTBOT_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_MOLTBOT } = await import("./config.js");
expect(CONFIG_PATH_MOLTBOT).toBe(
path.join(path.resolve("/custom/state"), "moltbot.json"),
);
},
);
@ -94,7 +94,7 @@ describe("Nix integration (U3, U5, U9)", () => {
describe("U5b: tilde expansion for config paths", () => {
it("expands ~ in common path-ish config fields", async () => {
await withTempHome(async (home) => {
const configDir = path.join(home, ".clawdbot");
const configDir = path.join(home, ".moltbot");
await fs.mkdir(configDir, { recursive: true });
const pluginDir = path.join(home, "plugins", "demo-plugin");
await fs.mkdir(pluginDir, { recursive: true });
@ -116,7 +116,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: {
@ -130,7 +130,7 @@ describe("Nix integration (U3, U5, U9)", () => {
{
id: "main",
workspace: "~/ws-agent",
agentDir: "~/.clawdbot/agents/main",
agentDir: "~/.moltbot/agents/main",
sandbox: { workspaceRoot: "~/sandbox-root" },
},
],
@ -139,7 +139,7 @@ describe("Nix integration (U3, U5, U9)", () => {
whatsapp: {
accounts: {
personal: {
authDir: "~/.clawdbot/credentials/wa-personal",
authDir: "~/.moltbot/credentials/wa-personal",
},
},
},
@ -159,11 +159,11 @@ describe("Nix integration (U3, U5, U9)", () => {
expect(cfg.agents?.defaults?.workspace).toBe(path.join(home, "ws-default"));
expect(cfg.agents?.list?.[0]?.workspace).toBe(path.join(home, "ws-agent"));
expect(cfg.agents?.list?.[0]?.agentDir).toBe(
path.join(home, ".clawdbot", "agents", "main"),
path.join(home, ".moltbot", "agents", "main"),
);
expect(cfg.agents?.list?.[0]?.sandbox?.workspaceRoot).toBe(path.join(home, "sandbox-root"));
expect(cfg.channels?.whatsapp?.accounts?.personal?.authDir).toBe(
path.join(home, ".clawdbot", "credentials", "wa-personal"),
path.join(home, ".moltbot", "credentials", "wa-personal"),
);
});
});
@ -195,10 +195,10 @@ describe("Nix integration (U3, U5, U9)", () => {
describe("U9: telegram.tokenFile schema validation", () => {
it("accepts config with only botToken", async () => {
await withTempHome(async (home) => {
const configDir = path.join(home, ".clawdbot");
const configDir = path.join(home, ".moltbot");
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" } },
}),
@ -215,10 +215,10 @@ describe("Nix integration (U3, U5, U9)", () => {
it("accepts config with only tokenFile", async () => {
await withTempHome(async (home) => {
const configDir = path.join(home, ".clawdbot");
const configDir = path.join(home, ".moltbot");
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" } },
}),
@ -235,10 +235,10 @@ describe("Nix integration (U3, U5, U9)", () => {
it("accepts config with both botToken and tokenFile", async () => {
await withTempHome(async (home) => {
const configDir = path.join(home, ".clawdbot");
const configDir = path.join(home, ".moltbot");
await fs.mkdir(configDir, { recursive: true });
await fs.writeFile(
path.join(configDir, "clawdbot.json"),
path.join(configDir, "moltbot.json"),
JSON.stringify({
channels: {
telegram: {

View File

@ -53,8 +53,8 @@ const SHELL_ENV_EXPECTED_KEYS = [
"DISCORD_BOT_TOKEN",
"SLACK_BOT_TOKEN",
"SLACK_APP_TOKEN",
"CLAWDBOT_GATEWAY_TOKEN",
"CLAWDBOT_GATEWAY_PASSWORD",
"MOLTBOT_GATEWAY_TOKEN",
"MOLTBOT_GATEWAY_PASSWORD",
];
const CONFIG_BACKUP_COUNT = 5;
@ -522,7 +522,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
}
// NOTE: These wrappers intentionally do *not* cache the resolved config path at
// module scope. `CLAWDBOT_CONFIG_PATH` (and friends) are expected to work even
// module scope. `MOLTBOT_CONFIG_PATH` (and friends) are expected to work even
// when set after the module has been imported (tests, one-off scripts, etc.).
const DEFAULT_CONFIG_CACHE_MS = 200;
let configCache: {
@ -532,7 +532,7 @@ let configCache: {
} | null = null;
function resolveConfigCacheMs(env: NodeJS.ProcessEnv): number {
const raw = env.CLAWDBOT_CONFIG_CACHE_MS?.trim();
const raw = env.MOLTBOT_CONFIG_CACHE_MS?.trim();
if (raw === "" || raw === "0") return 0;
if (!raw) return DEFAULT_CONFIG_CACHE_MS;
const parsed = Number.parseInt(raw, 10);
@ -541,7 +541,7 @@ function resolveConfigCacheMs(env: NodeJS.ProcessEnv): number {
}
function shouldUseConfigCache(env: NodeJS.ProcessEnv): boolean {
if (env.CLAWDBOT_DISABLE_CONFIG_CACHE?.trim()) return false;
if (env.MOLTBOT_DISABLE_CONFIG_CACHE?.trim()) return false;
return resolveConfigCacheMs(env) > 0;
}

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_MOLTBOT } 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_MOLTBOT): 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_MOLTBOT);
const suffix = opts.suffix ? ` ${opts.suffix}` : "";
runtime.log(`Updated ${path}${suffix}`);
}

View File

@ -15,18 +15,30 @@ export function resolveIsNixMode(env: NodeJS.ProcessEnv = process.env): boolean
export const isNixMode = resolveIsNixMode();
const LEGACY_STATE_DIRNAME = ".clawdbot";
const NEW_STATE_DIRNAME = ".moltbot";
const CONFIG_FILENAME = "moltbot.json";
function legacyStateDir(homedir: () => string = os.homedir): string {
return path.join(homedir(), LEGACY_STATE_DIRNAME);
}
function newStateDir(homedir: () => string = os.homedir): string {
return path.join(homedir(), NEW_STATE_DIRNAME);
}
/**
* State directory for mutable data (sessions, logs, caches).
* Can be overridden via CLAWDBOT_STATE_DIR environment variable.
* Default: ~/.clawdbot
* Can be overridden via MOLTBOT_STATE_DIR (preferred) or CLAWDBOT_STATE_DIR (legacy).
* Default: ~/.clawdbot (legacy default for compatibility)
*/
export function resolveStateDir(
env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir,
): string {
const override = env.CLAWDBOT_STATE_DIR?.trim();
const override = env.MOLTBOT_STATE_DIR?.trim() || env.CLAWDBOT_STATE_DIR?.trim();
if (override) return resolveUserPath(override);
return path.join(homedir(), ".clawdbot");
return legacyStateDir(homedir);
}
function resolveUserPath(input: string): string {
@ -39,34 +51,64 @@ function resolveUserPath(input: string): string {
return path.resolve(trimmed);
}
export const STATE_DIR_CLAWDBOT = resolveStateDir();
/** @deprecated Use STATE_DIR. Kept for compatibility during migration. */
export const STATE_DIR_MOLTBOT = 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)
* Can be overridden via MOLTBOT_CONFIG_PATH (preferred) or CLAWDBOT_CONFIG_PATH (legacy).
* Default: ~/.clawdbot/moltbot.json (or $*_STATE_DIR/moltbot.json)
*/
export function resolveConfigPath(
env: NodeJS.ProcessEnv = process.env,
stateDir: string = resolveStateDir(env, os.homedir),
): string {
const override = env.CLAWDBOT_CONFIG_PATH?.trim();
const override = env.MOLTBOT_CONFIG_PATH?.trim() || env.CLAWDBOT_CONFIG_PATH?.trim();
if (override) return resolveUserPath(override);
return path.join(stateDir, "clawdbot.json");
return path.join(stateDir, CONFIG_FILENAME);
}
export const CONFIG_PATH_CLAWDBOT = resolveConfigPath();
/** @deprecated Use CONFIG_PATH. Kept for compatibility during migration. */
export const CONFIG_PATH_MOLTBOT = resolveConfigPath();
export const CONFIG_PATH = resolveConfigPath();
/**
* Resolve default config path candidates across new + legacy locations.
* Order: explicit config path state-dir-derived paths new default legacy default.
*/
export function resolveDefaultConfigCandidates(
env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir,
): string[] {
const explicit = env.MOLTBOT_CONFIG_PATH?.trim() || env.CLAWDBOT_CONFIG_PATH?.trim();
if (explicit) return [resolveUserPath(explicit)];
const candidates: string[] = [];
const moltbotStateDir = env.MOLTBOT_STATE_DIR?.trim();
if (moltbotStateDir) {
candidates.push(path.join(resolveUserPath(moltbotStateDir), CONFIG_FILENAME));
}
const legacyStateDirOverride = env.CLAWDBOT_STATE_DIR?.trim();
if (legacyStateDirOverride) {
candidates.push(path.join(resolveUserPath(legacyStateDirOverride), CONFIG_FILENAME));
}
candidates.push(path.join(newStateDir(homedir), CONFIG_FILENAME));
candidates.push(path.join(legacyStateDir(homedir), CONFIG_FILENAME));
return candidates;
}
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);
}
@ -77,7 +119,7 @@ const OAUTH_FILENAME = "oauth.json";
*
* Precedence:
* - `CLAWDBOT_OAUTH_DIR` (explicit override)
* - `CLAWDBOT_STATE_DIR/credentials` (canonical server/default)
* - `$*_STATE_DIR/credentials` (canonical server/default)
* - `~/.clawdbot/credentials` (legacy default)
*/
export function resolveOAuthDir(

View File

@ -2,7 +2,7 @@ import path from "node:path";
import { pathToFileURL } from "node:url";
import {
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
type HookMappingConfig,
type HooksConfig,
} from "../config/config.js";
@ -125,7 +125,7 @@ export function resolveHookMappings(hooks?: HooksConfig): HookMappingResolved[]
}
if (mappings.length === 0) return [];
const configDir = path.dirname(CONFIG_PATH_CLAWDBOT);
const configDir = path.dirname(CONFIG_PATH_MOLTBOT);
const transformsDir = hooks?.transformsDir
? resolvePath(configDir, hooks.transformsDir)
: configDir;

View File

@ -1,6 +1,6 @@
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
import {
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
loadConfig,
parseConfigJson5,
readConfigFileSnapshot,
@ -186,7 +186,7 @@ export const configHandlers: GatewayRequestHandlers = {
true,
{
ok: true,
path: CONFIG_PATH_CLAWDBOT,
path: CONFIG_PATH_MOLTBOT,
config: validated.config,
},
undefined,
@ -284,7 +284,7 @@ export const configHandlers: GatewayRequestHandlers = {
doctorHint: formatDoctorNonInteractiveHint(),
stats: {
mode: "config.patch",
root: CONFIG_PATH_CLAWDBOT,
root: CONFIG_PATH_MOLTBOT,
},
};
let sentinelPath: string | null = null;
@ -301,7 +301,7 @@ export const configHandlers: GatewayRequestHandlers = {
true,
{
ok: true,
path: CONFIG_PATH_CLAWDBOT,
path: CONFIG_PATH_MOLTBOT,
config: validated.config,
restart,
sentinel: {
@ -381,7 +381,7 @@ export const configHandlers: GatewayRequestHandlers = {
doctorHint: formatDoctorNonInteractiveHint(),
stats: {
mode: "config.apply",
root: CONFIG_PATH_CLAWDBOT,
root: CONFIG_PATH_MOLTBOT,
},
};
let sentinelPath: string | null = null;
@ -398,7 +398,7 @@ export const configHandlers: GatewayRequestHandlers = {
true,
{
ok: true,
path: CONFIG_PATH_CLAWDBOT,
path: CONFIG_PATH_MOLTBOT,
config: validated.config,
restart,
sentinel: {

View File

@ -66,7 +66,7 @@ describe("gateway server auth/connect", () => {
});
test("connect (req) handshake returns hello-ok payload", async () => {
const { CONFIG_PATH_CLAWDBOT, STATE_DIR_CLAWDBOT } = await import("../config/config.js");
const { CONFIG_PATH_MOLTBOT, STATE_DIR_MOLTBOT } = await import("../config/config.js");
const ws = await openWs(port);
const res = await connectReq(ws);
@ -78,8 +78,8 @@ describe("gateway server auth/connect", () => {
}
| undefined;
expect(payload?.type).toBe("hello-ok");
expect(payload?.snapshot?.configPath).toBe(CONFIG_PATH_CLAWDBOT);
expect(payload?.snapshot?.stateDir).toBe(STATE_DIR_CLAWDBOT);
expect(payload?.snapshot?.configPath).toBe(CONFIG_PATH_MOLTBOT);
expect(payload?.snapshot?.stateDir).toBe(STATE_DIR_MOLTBOT);
ws.close();
});

View File

@ -6,7 +6,7 @@ import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js
import { createDefaultDeps } from "../cli/deps.js";
import { formatCliCommand } from "../cli/command-format.js";
import {
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
isNixMode,
loadConfig,
migrateLegacyConfig,
@ -541,7 +541,7 @@ export async function startGatewayServer(
warn: (msg) => logReload.warn(msg),
error: (msg) => logReload.error(msg),
},
watchPath: CONFIG_PATH_CLAWDBOT,
watchPath: CONFIG_PATH_MOLTBOT,
});
const close = createGatewayCloseHandler({

View File

@ -1,6 +1,6 @@
import { resolveDefaultAgentId } from "../../agents/agent-scope.js";
import { getHealthSnapshot, type HealthSummary } from "../../commands/health.js";
import { CONFIG_PATH_CLAWDBOT, STATE_DIR_CLAWDBOT, loadConfig } from "../../config/config.js";
import { CONFIG_PATH_MOLTBOT, STATE_DIR_MOLTBOT, loadConfig } from "../../config/config.js";
import { resolveMainSessionKey } from "../../config/sessions.js";
import { normalizeMainKey } from "../../routing/session-key.js";
import { listSystemPresence } from "../../infra/system-presence.js";
@ -28,8 +28,8 @@ export function buildGatewaySnapshot(): Snapshot {
stateVersion: { presence: presenceVersion, health: healthVersion },
uptimeMs,
// Surface resolved paths so UIs can display the true config location.
configPath: CONFIG_PATH_CLAWDBOT,
stateDir: STATE_DIR_CLAWDBOT,
configPath: CONFIG_PATH_MOLTBOT,
stateDir: STATE_DIR_MOLTBOT,
sessionDefaults: {
defaultAgentId,
mainKey,

View File

@ -350,10 +350,10 @@ vi.mock("../config/config.js", async () => {
return {
...actual,
get CONFIG_PATH_CLAWDBOT() {
get CONFIG_PATH_MOLTBOT() {
return resolveConfigPath();
},
get STATE_DIR_CLAWDBOT() {
get STATE_DIR_MOLTBOT() {
return path.dirname(resolveConfigPath());
},
get isNixMode() {

View File

@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
import {
type ClawdbotConfig,
CONFIG_PATH_CLAWDBOT,
CONFIG_PATH_MOLTBOT,
loadConfig,
readConfigFileSnapshot,
resolveGatewayPort,
@ -99,7 +99,7 @@ export async function runGmailSetup(opts: GmailSetupOptions) {
const configSnapshot = await readConfigFileSnapshot();
if (!configSnapshot.valid) {
throw new Error(`Config invalid: ${CONFIG_PATH_CLAWDBOT}`);
throw new Error(`Config invalid: ${CONFIG_PATH_MOLTBOT}`);
}
const baseConfig = configSnapshot.config;
@ -277,7 +277,7 @@ export async function runGmailSetup(opts: GmailSetupOptions) {
defaultRuntime.log(`- subscription: ${subscription}`);
defaultRuntime.log(`- push endpoint: ${pushEndpoint}`);
defaultRuntime.log(`- hook url: ${hookUrl}`);
defaultRuntime.log(`- config: ${displayPath(CONFIG_PATH_CLAWDBOT)}`);
defaultRuntime.log(`- config: ${displayPath(CONFIG_PATH_MOLTBOT)}`);
defaultRuntime.log(`Next: ${formatCliCommand("clawdbot webhooks gmail run")}`);
}

View File

@ -1,5 +1,5 @@
import type { ClawdbotConfig } from "../config/config.js";
import { STATE_DIR_CLAWDBOT } from "../config/paths.js";
import { STATE_DIR_MOLTBOT } from "../config/paths.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import type { PluginRegistry } from "./registry.js";
@ -25,7 +25,7 @@ export async function startPluginServices(params: {
await service.start({
config: params.config,
workspaceDir: params.workspaceDir,
stateDir: STATE_DIR_CLAWDBOT,
stateDir: STATE_DIR_MOLTBOT,
logger: {
info: (msg) => log.info(msg),
warn: (msg) => log.warn(msg),
@ -40,7 +40,7 @@ export async function startPluginServices(params: {
service.stop?.({
config: params.config,
workspaceDir: params.workspaceDir,
stateDir: STATE_DIR_CLAWDBOT,
stateDir: STATE_DIR_MOLTBOT,
logger: {
info: (msg) => log.info(msg),
warn: (msg) => log.warn(msg),