chore: rename project to clawdbot
This commit is contained in:
@@ -5,7 +5,7 @@ import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-config-"));
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-config-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
@@ -60,10 +60,10 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("derives mentionPatterns when identity is set", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
identity: { name: "Samantha", theme: "helpful sloth", emoji: "🦥" },
|
||||
@@ -89,10 +89,10 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("does not override explicit values", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
identity: {
|
||||
@@ -124,14 +124,14 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("supports provider textChunkLimit config", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
messages: {
|
||||
messagePrefix: "[clawdis]",
|
||||
messagePrefix: "[clawdbot]",
|
||||
responsePrefix: "🦞",
|
||||
// legacy field should be ignored (moved to providers)
|
||||
textChunkLimit: 9999,
|
||||
@@ -167,10 +167,10 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("respects empty responsePrefix to disable identity defaults", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
identity: { name: "Samantha", theme: "helpful sloth", emoji: "🦥" },
|
||||
@@ -193,10 +193,10 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("does not synthesize agent/session when absent", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
identity: { name: "Samantha", theme: "helpful sloth", emoji: "🦥" },
|
||||
@@ -224,10 +224,10 @@ describe("config identity defaults", () => {
|
||||
|
||||
it("does not derive responsePrefix from identity emoji", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
identity: { name: "Clawd", theme: "space lobster", emoji: "🦞" },
|
||||
@@ -262,10 +262,10 @@ describe("config discord", () => {
|
||||
|
||||
it("loads discord guild map + dm group settings", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
discord: {
|
||||
@@ -309,29 +309,29 @@ describe("config discord", () => {
|
||||
|
||||
describe("Nix integration (U3, U5, U9)", () => {
|
||||
describe("U3: isNixMode env var detection", () => {
|
||||
it("isNixMode is false when CLAWDIS_NIX_MODE is not set", async () => {
|
||||
await withEnvOverride({ CLAWDIS_NIX_MODE: undefined }, async () => {
|
||||
it("isNixMode is false when CLAWDBOT_NIX_MODE is not set", async () => {
|
||||
await withEnvOverride({ CLAWDBOT_NIX_MODE: undefined }, async () => {
|
||||
const { isNixMode } = await import("./config.js");
|
||||
expect(isNixMode).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("isNixMode is false when CLAWDIS_NIX_MODE is empty", async () => {
|
||||
await withEnvOverride({ CLAWDIS_NIX_MODE: "" }, async () => {
|
||||
it("isNixMode is false when CLAWDBOT_NIX_MODE is empty", async () => {
|
||||
await withEnvOverride({ CLAWDBOT_NIX_MODE: "" }, async () => {
|
||||
const { isNixMode } = await import("./config.js");
|
||||
expect(isNixMode).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("isNixMode is false when CLAWDIS_NIX_MODE is not '1'", async () => {
|
||||
await withEnvOverride({ CLAWDIS_NIX_MODE: "true" }, async () => {
|
||||
it("isNixMode is false when CLAWDBOT_NIX_MODE is not '1'", async () => {
|
||||
await withEnvOverride({ CLAWDBOT_NIX_MODE: "true" }, async () => {
|
||||
const { isNixMode } = await import("./config.js");
|
||||
expect(isNixMode).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("isNixMode is true when CLAWDIS_NIX_MODE=1", async () => {
|
||||
await withEnvOverride({ CLAWDIS_NIX_MODE: "1" }, async () => {
|
||||
it("isNixMode is true when CLAWDBOT_NIX_MODE=1", async () => {
|
||||
await withEnvOverride({ CLAWDBOT_NIX_MODE: "1" }, async () => {
|
||||
const { isNixMode } = await import("./config.js");
|
||||
expect(isNixMode).toBe(true);
|
||||
});
|
||||
@@ -339,52 +339,52 @@ describe("Nix integration (U3, U5, U9)", () => {
|
||||
});
|
||||
|
||||
describe("U5: CONFIG_PATH and STATE_DIR env var overrides", () => {
|
||||
it("STATE_DIR_CLAWDIS defaults to ~/.clawdis when env not set", async () => {
|
||||
await withEnvOverride({ CLAWDIS_STATE_DIR: undefined }, async () => {
|
||||
const { STATE_DIR_CLAWDIS } = await import("./config.js");
|
||||
expect(STATE_DIR_CLAWDIS).toMatch(/\.clawdis$/);
|
||||
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_CLAWDIS respects CLAWDIS_STATE_DIR override", async () => {
|
||||
it("STATE_DIR_CLAWDBOT respects CLAWDBOT_STATE_DIR override", async () => {
|
||||
await withEnvOverride(
|
||||
{ CLAWDIS_STATE_DIR: "/custom/state/dir" },
|
||||
{ CLAWDBOT_STATE_DIR: "/custom/state/dir" },
|
||||
async () => {
|
||||
const { STATE_DIR_CLAWDIS } = await import("./config.js");
|
||||
expect(STATE_DIR_CLAWDIS).toBe("/custom/state/dir");
|
||||
const { STATE_DIR_CLAWDBOT } = await import("./config.js");
|
||||
expect(STATE_DIR_CLAWDBOT).toBe("/custom/state/dir");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("CONFIG_PATH_CLAWDIS defaults to ~/.clawdis/clawdis.json when env not set", async () => {
|
||||
it("CONFIG_PATH_CLAWDBOT defaults to ~/.clawdbot/clawdbot.json when env not set", async () => {
|
||||
await withEnvOverride(
|
||||
{ CLAWDIS_CONFIG_PATH: undefined, CLAWDIS_STATE_DIR: undefined },
|
||||
{ CLAWDBOT_CONFIG_PATH: undefined, CLAWDBOT_STATE_DIR: undefined },
|
||||
async () => {
|
||||
const { CONFIG_PATH_CLAWDIS } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDIS).toMatch(/\.clawdis\/clawdis\.json$/);
|
||||
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDBOT).toMatch(/\.clawdbot\/clawdbot\.json$/);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("CONFIG_PATH_CLAWDIS respects CLAWDIS_CONFIG_PATH override", async () => {
|
||||
it("CONFIG_PATH_CLAWDBOT respects CLAWDBOT_CONFIG_PATH override", async () => {
|
||||
await withEnvOverride(
|
||||
{ CLAWDIS_CONFIG_PATH: "/nix/store/abc/clawdis.json" },
|
||||
{ CLAWDBOT_CONFIG_PATH: "/nix/store/abc/clawdbot.json" },
|
||||
async () => {
|
||||
const { CONFIG_PATH_CLAWDIS } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDIS).toBe("/nix/store/abc/clawdis.json");
|
||||
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDBOT).toBe("/nix/store/abc/clawdbot.json");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("CONFIG_PATH_CLAWDIS uses STATE_DIR_CLAWDIS when only state dir is overridden", async () => {
|
||||
it("CONFIG_PATH_CLAWDBOT uses STATE_DIR_CLAWDBOT when only state dir is overridden", async () => {
|
||||
await withEnvOverride(
|
||||
{
|
||||
CLAWDIS_CONFIG_PATH: undefined,
|
||||
CLAWDIS_STATE_DIR: "/custom/state",
|
||||
CLAWDBOT_CONFIG_PATH: undefined,
|
||||
CLAWDBOT_STATE_DIR: "/custom/state",
|
||||
},
|
||||
async () => {
|
||||
const { CONFIG_PATH_CLAWDIS } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDIS).toBe("/custom/state/clawdis.json");
|
||||
const { CONFIG_PATH_CLAWDBOT } = await import("./config.js");
|
||||
expect(CONFIG_PATH_CLAWDBOT).toBe("/custom/state/clawdbot.json");
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -392,7 +392,7 @@ describe("Nix integration (U3, U5, U9)", () => {
|
||||
|
||||
describe("U6: gateway port resolution", () => {
|
||||
it("uses default when env and config are unset", async () => {
|
||||
await withEnvOverride({ CLAWDIS_GATEWAY_PORT: undefined }, async () => {
|
||||
await withEnvOverride({ CLAWDBOT_GATEWAY_PORT: undefined }, async () => {
|
||||
const { DEFAULT_GATEWAY_PORT, resolveGatewayPort } = await import(
|
||||
"./config.js"
|
||||
);
|
||||
@@ -400,15 +400,15 @@ describe("Nix integration (U3, U5, U9)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers CLAWDIS_GATEWAY_PORT over config", async () => {
|
||||
await withEnvOverride({ CLAWDIS_GATEWAY_PORT: "19001" }, async () => {
|
||||
it("prefers CLAWDBOT_GATEWAY_PORT over config", async () => {
|
||||
await withEnvOverride({ CLAWDBOT_GATEWAY_PORT: "19001" }, async () => {
|
||||
const { resolveGatewayPort } = await import("./config.js");
|
||||
expect(resolveGatewayPort({ gateway: { port: 19002 } })).toBe(19001);
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to config when env is invalid", async () => {
|
||||
await withEnvOverride({ CLAWDIS_GATEWAY_PORT: "nope" }, async () => {
|
||||
await withEnvOverride({ CLAWDBOT_GATEWAY_PORT: "nope" }, async () => {
|
||||
const { resolveGatewayPort } = await import("./config.js");
|
||||
expect(resolveGatewayPort({ gateway: { port: 19003 } })).toBe(19003);
|
||||
});
|
||||
@@ -418,10 +418,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, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify({
|
||||
telegram: { botToken: "123:ABC" },
|
||||
}),
|
||||
@@ -438,10 +438,10 @@ describe("Nix integration (U3, U5, U9)", () => {
|
||||
|
||||
it("accepts config with only tokenFile", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify({
|
||||
telegram: { tokenFile: "/run/agenix/telegram-token" },
|
||||
}),
|
||||
@@ -458,10 +458,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, ".clawdis");
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdis.json"),
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify({
|
||||
telegram: {
|
||||
botToken: "fallback:token",
|
||||
@@ -643,7 +643,7 @@ describe("legacy config detection", () => {
|
||||
|
||||
it("surfaces legacy issues in snapshot", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configPath = path.join(home, ".clawdis", "clawdis.json");
|
||||
const configPath = path.join(home, ".clawdbot", "clawdbot.json");
|
||||
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
||||
await fs.writeFile(
|
||||
configPath,
|
||||
|
||||
@@ -9,4 +9,4 @@ export { migrateLegacyConfig } from "./legacy-migrate.js";
|
||||
export * from "./paths.js";
|
||||
export * from "./types.js";
|
||||
export { validateConfigObject } from "./validation.js";
|
||||
export { ClawdisSchema } from "./zod-schema.js";
|
||||
export { ClawdbotSchema } from "./zod-schema.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { resolveTalkApiKey } from "./talk.js";
|
||||
import type { ClawdisConfig } from "./types.js";
|
||||
import type { ClawdbotConfig } from "./types.js";
|
||||
|
||||
type WarnState = { warned: boolean };
|
||||
|
||||
@@ -14,7 +14,7 @@ function escapeRegExp(text: string): string {
|
||||
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
export function applyIdentityDefaults(cfg: ClawdisConfig): ClawdisConfig {
|
||||
export function applyIdentityDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const identity = cfg.identity;
|
||||
if (!identity) return cfg;
|
||||
|
||||
@@ -24,7 +24,7 @@ export function applyIdentityDefaults(cfg: ClawdisConfig): ClawdisConfig {
|
||||
const groupChat = routing.groupChat ?? {};
|
||||
|
||||
let mutated = false;
|
||||
const next: ClawdisConfig = { ...cfg };
|
||||
const next: ClawdbotConfig = { ...cfg };
|
||||
|
||||
if (name && !groupChat.mentionPatterns) {
|
||||
const parts = name.split(/\s+/).filter(Boolean).map(escapeRegExp);
|
||||
@@ -41,9 +41,9 @@ export function applyIdentityDefaults(cfg: ClawdisConfig): ClawdisConfig {
|
||||
}
|
||||
|
||||
export function applySessionDefaults(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
options: SessionDefaultsOptions = {},
|
||||
): ClawdisConfig {
|
||||
): ClawdbotConfig {
|
||||
const session = cfg.session;
|
||||
if (!session || session.mainKey === undefined) return cfg;
|
||||
|
||||
@@ -51,7 +51,7 @@ export function applySessionDefaults(
|
||||
const warn = options.warn ?? console.warn;
|
||||
const warnState = options.warnState ?? defaultWarnState;
|
||||
|
||||
const next: ClawdisConfig = {
|
||||
const next: ClawdbotConfig = {
|
||||
...cfg,
|
||||
session: { ...session, mainKey: "main" },
|
||||
};
|
||||
@@ -64,7 +64,7 @@ export function applySessionDefaults(
|
||||
return next;
|
||||
}
|
||||
|
||||
export function applyTalkApiKey(config: ClawdisConfig): ClawdisConfig {
|
||||
export function applyTalkApiKey(config: ClawdbotConfig): ClawdbotConfig {
|
||||
const resolved = resolveTalkApiKey();
|
||||
if (!resolved) return config;
|
||||
const existing = config.talk?.apiKey?.trim();
|
||||
|
||||
@@ -11,17 +11,17 @@ import {
|
||||
} from "./defaults.js";
|
||||
import { findLegacyConfigIssues } from "./legacy.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDIS,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
resolveConfigPath,
|
||||
resolveStateDir,
|
||||
} from "./paths.js";
|
||||
import type {
|
||||
ClawdisConfig,
|
||||
ClawdbotConfig,
|
||||
ConfigFileSnapshot,
|
||||
LegacyConfigIssue,
|
||||
} from "./types.js";
|
||||
import { validateConfigObject } from "./validation.js";
|
||||
import { ClawdisSchema } from "./zod-schema.js";
|
||||
import { ClawdbotSchema } from "./zod-schema.js";
|
||||
|
||||
export type ParseConfigJson5Result =
|
||||
| { ok: true; parsed: unknown }
|
||||
@@ -67,13 +67,13 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
const deps = normalizeDeps(overrides);
|
||||
const configPath = resolveConfigPathForDeps(deps);
|
||||
|
||||
function loadConfig(): ClawdisConfig {
|
||||
function loadConfig(): ClawdbotConfig {
|
||||
try {
|
||||
if (!deps.fs.existsSync(configPath)) return {};
|
||||
const raw = deps.fs.readFileSync(configPath, "utf-8");
|
||||
const parsed = deps.json5.parse(raw);
|
||||
if (typeof parsed !== "object" || parsed === null) return {};
|
||||
const validated = ClawdisSchema.safeParse(parsed);
|
||||
const validated = ClawdbotSchema.safeParse(parsed);
|
||||
if (!validated.success) {
|
||||
deps.logger.error("Invalid config:");
|
||||
for (const iss of validated.error.issues) {
|
||||
@@ -82,7 +82,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
return {};
|
||||
}
|
||||
return applySessionDefaults(
|
||||
applyIdentityDefaults(validated.data as ClawdisConfig),
|
||||
applyIdentityDefaults(validated.data as ClawdbotConfig),
|
||||
);
|
||||
} catch (err) {
|
||||
deps.logger.error(`Failed to read config at ${configPath}`, err);
|
||||
@@ -165,7 +165,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
async function writeConfigFile(cfg: ClawdisConfig) {
|
||||
async function writeConfigFile(cfg: ClawdbotConfig) {
|
||||
await deps.fs.promises.mkdir(path.dirname(configPath), {
|
||||
recursive: true,
|
||||
});
|
||||
@@ -181,7 +181,7 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
const defaultIO = createConfigIO({ configPath: CONFIG_PATH_CLAWDIS });
|
||||
const defaultIO = createConfigIO({ configPath: CONFIG_PATH_CLAWDBOT });
|
||||
|
||||
export const loadConfig = defaultIO.loadConfig;
|
||||
export const readConfigFileSnapshot = defaultIO.readConfigFileSnapshot;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { applyLegacyMigrations } from "./legacy.js";
|
||||
import type { ClawdisConfig } from "./types.js";
|
||||
import type { ClawdbotConfig } from "./types.js";
|
||||
import { validateConfigObject } from "./validation.js";
|
||||
|
||||
export function migrateLegacyConfig(raw: unknown): {
|
||||
config: ClawdisConfig | null;
|
||||
config: ClawdbotConfig | null;
|
||||
changes: string[];
|
||||
} {
|
||||
const { next, changes } = applyLegacyMigrations(raw);
|
||||
|
||||
@@ -15,17 +15,17 @@ const LEGACY_CONFIG_RULES: LegacyConfigRule[] = [
|
||||
{
|
||||
path: ["routing", "allowFrom"],
|
||||
message:
|
||||
"routing.allowFrom was removed; use whatsapp.allowFrom instead (run `clawdis doctor` to migrate).",
|
||||
"routing.allowFrom was removed; use whatsapp.allowFrom instead (run `clawdbot doctor` to migrate).",
|
||||
},
|
||||
{
|
||||
path: ["routing", "groupChat", "requireMention"],
|
||||
message:
|
||||
'routing.groupChat.requireMention was removed; use whatsapp/telegram/imessage groups defaults (e.g. whatsapp.groups."*".requireMention) instead (run `clawdis doctor` to migrate).',
|
||||
'routing.groupChat.requireMention was removed; use whatsapp/telegram/imessage groups defaults (e.g. whatsapp.groups."*".requireMention) instead (run `clawdbot doctor` to migrate).',
|
||||
},
|
||||
{
|
||||
path: ["telegram", "requireMention"],
|
||||
message:
|
||||
'telegram.requireMention was removed; use telegram.groups."*".requireMention instead (run `clawdis doctor` to migrate).',
|
||||
'telegram.requireMention was removed; use telegram.groups."*".requireMention instead (run `clawdbot doctor` to migrate).',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import type { ClawdisConfig } from "./types.js";
|
||||
import type { ClawdbotConfig } from "./types.js";
|
||||
|
||||
/**
|
||||
* Nix mode detection: When CLAWDIS_NIX_MODE=1, the gateway is running under Nix.
|
||||
* Nix mode detection: When CLAWDBOT_NIX_MODE=1, the gateway is running under Nix.
|
||||
* In this mode:
|
||||
* - No auto-install flows should be attempted
|
||||
* - Missing dependencies should produce actionable Nix-specific error messages
|
||||
@@ -13,50 +13,50 @@ import type { ClawdisConfig } from "./types.js";
|
||||
export function resolveIsNixMode(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): boolean {
|
||||
return env.CLAWDIS_NIX_MODE === "1";
|
||||
return env.CLAWDBOT_NIX_MODE === "1";
|
||||
}
|
||||
|
||||
export const isNixMode = resolveIsNixMode();
|
||||
|
||||
/**
|
||||
* State directory for mutable data (sessions, logs, caches).
|
||||
* Can be overridden via CLAWDIS_STATE_DIR environment variable.
|
||||
* Default: ~/.clawdis
|
||||
* Can be overridden via CLAWDBOT_STATE_DIR environment variable.
|
||||
* Default: ~/.clawdbot
|
||||
*/
|
||||
export function resolveStateDir(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
homedir: () => string = os.homedir,
|
||||
): string {
|
||||
const override = env.CLAWDIS_STATE_DIR?.trim();
|
||||
const override = env.CLAWDBOT_STATE_DIR?.trim();
|
||||
if (override) return override;
|
||||
return path.join(homedir(), ".clawdis");
|
||||
return path.join(homedir(), ".clawdbot");
|
||||
}
|
||||
|
||||
export const STATE_DIR_CLAWDIS = resolveStateDir();
|
||||
export const STATE_DIR_CLAWDBOT = resolveStateDir();
|
||||
|
||||
/**
|
||||
* Config file path (JSON5).
|
||||
* Can be overridden via CLAWDIS_CONFIG_PATH environment variable.
|
||||
* Default: ~/.clawdis/clawdis.json (or $CLAWDIS_STATE_DIR/clawdis.json)
|
||||
* Can be overridden via CLAWDBOT_CONFIG_PATH environment variable.
|
||||
* Default: ~/.clawdbot/clawdbot.json (or $CLAWDBOT_STATE_DIR/clawdbot.json)
|
||||
*/
|
||||
export function resolveConfigPath(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
stateDir: string = resolveStateDir(env, os.homedir),
|
||||
): string {
|
||||
const override = env.CLAWDIS_CONFIG_PATH?.trim();
|
||||
const override = env.CLAWDBOT_CONFIG_PATH?.trim();
|
||||
if (override) return override;
|
||||
return path.join(stateDir, "clawdis.json");
|
||||
return path.join(stateDir, "clawdbot.json");
|
||||
}
|
||||
|
||||
export const CONFIG_PATH_CLAWDIS = resolveConfigPath();
|
||||
export const CONFIG_PATH_CLAWDBOT = resolveConfigPath();
|
||||
|
||||
export const DEFAULT_GATEWAY_PORT = 18789;
|
||||
|
||||
export function resolveGatewayPort(
|
||||
cfg?: ClawdisConfig,
|
||||
cfg?: ClawdbotConfig,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): number {
|
||||
const envRaw = env.CLAWDIS_GATEWAY_PORT?.trim();
|
||||
const envRaw = env.CLAWDBOT_GATEWAY_PORT?.trim();
|
||||
if (envRaw) {
|
||||
const parsed = Number.parseInt(envRaw, 10);
|
||||
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { VERSION } from "../version.js";
|
||||
import { ClawdisSchema } from "./zod-schema.js";
|
||||
import { ClawdbotSchema } from "./zod-schema.js";
|
||||
|
||||
export type ConfigUiHint = {
|
||||
label?: string;
|
||||
@@ -14,7 +14,7 @@ export type ConfigUiHint = {
|
||||
|
||||
export type ConfigUiHints = Record<string, ConfigUiHint>;
|
||||
|
||||
export type ConfigSchema = ReturnType<typeof ClawdisSchema.toJSONSchema>;
|
||||
export type ConfigSchema = ReturnType<typeof ClawdbotSchema.toJSONSchema>;
|
||||
|
||||
export type ConfigSchemaResponse = {
|
||||
schema: ConfigSchema;
|
||||
@@ -106,7 +106,7 @@ const FIELD_HELP: Record<string, string> = {
|
||||
"Required for multi-machine access or non-loopback binds.",
|
||||
"gateway.auth.password": "Required for Tailscale funnel.",
|
||||
"gateway.controlUi.basePath":
|
||||
"Optional URL prefix where the Control UI is served (e.g. /clawdis).",
|
||||
"Optional URL prefix where the Control UI is served (e.g. /clawdbot).",
|
||||
"gateway.reload.mode":
|
||||
'Hot reload strategy for config changes ("hybrid" recommended).',
|
||||
"gateway.reload.debounceMs":
|
||||
@@ -117,7 +117,7 @@ const FIELD_HELP: Record<string, string> = {
|
||||
|
||||
const FIELD_PLACEHOLDERS: Record<string, string> = {
|
||||
"gateway.remote.url": "ws://host:18789",
|
||||
"gateway.controlUi.basePath": "/clawdis",
|
||||
"gateway.controlUi.basePath": "/clawdbot",
|
||||
};
|
||||
|
||||
const SENSITIVE_PATTERNS = [/token/i, /password/i, /secret/i, /api.?key/i];
|
||||
@@ -164,11 +164,11 @@ let cached: ConfigSchemaResponse | null = null;
|
||||
|
||||
export function buildConfigSchema(): ConfigSchemaResponse {
|
||||
if (cached) return cached;
|
||||
const schema = ClawdisSchema.toJSONSchema({
|
||||
const schema = ClawdbotSchema.toJSONSchema({
|
||||
target: "draft-07",
|
||||
unrepresentable: "any",
|
||||
});
|
||||
schema.title = "ClawdisConfig";
|
||||
schema.title = "ClawdbotConfig";
|
||||
const hints = applySensitiveHints(buildBaseHints());
|
||||
const next = {
|
||||
schema,
|
||||
|
||||
@@ -95,7 +95,7 @@ describe("sessions", () => {
|
||||
});
|
||||
|
||||
it("updateLastRoute persists channel and target", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-sessions-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));
|
||||
const storePath = path.join(dir, "sessions.json");
|
||||
await fs.writeFile(
|
||||
storePath,
|
||||
|
||||
@@ -84,7 +84,7 @@ export type SessionSkillSnapshot = {
|
||||
};
|
||||
|
||||
export function resolveSessionTranscriptsDir(): string {
|
||||
return path.join(os.homedir(), ".clawdis", "sessions");
|
||||
return path.join(os.homedir(), ".clawdbot", "sessions");
|
||||
}
|
||||
|
||||
export function resolveDefaultSessionStorePath(): string {
|
||||
|
||||
@@ -443,7 +443,7 @@ export type RoutingConfig = {
|
||||
};
|
||||
|
||||
export type MessagesConfig = {
|
||||
messagePrefix?: string; // Prefix added to all inbound messages (default: "[clawdis]" if no allowFrom, else "")
|
||||
messagePrefix?: string; // Prefix added to all inbound messages (default: "[clawdbot]" if no allowFrom, else "")
|
||||
responsePrefix?: string; // Prefix auto-added to all outbound replies (e.g., "🦞")
|
||||
timestampPrefix?: boolean | string; // true/false or IANA timezone string (default: true with UTC)
|
||||
};
|
||||
@@ -499,7 +499,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. "/clawdis"). */
|
||||
/** Optional base path prefix for the Control UI (e.g. "/clawdbot"). */
|
||||
basePath?: string;
|
||||
};
|
||||
|
||||
@@ -636,7 +636,7 @@ export type ModelsConfig = {
|
||||
providers?: Record<string, ModelProviderConfig>;
|
||||
};
|
||||
|
||||
export type ClawdisConfig = {
|
||||
export type ClawdbotConfig = {
|
||||
identity?: {
|
||||
name?: string;
|
||||
theme?: string;
|
||||
@@ -652,7 +652,7 @@ export type ClawdisConfig = {
|
||||
logging?: LoggingConfig;
|
||||
browser?: BrowserConfig;
|
||||
ui?: {
|
||||
/** Accent color for Clawdis UI chrome (hex). */
|
||||
/** Accent color for Clawdbot UI chrome (hex). */
|
||||
seamColor?: string;
|
||||
};
|
||||
skills?: SkillsConfig;
|
||||
@@ -761,6 +761,30 @@ export type ClawdisConfig = {
|
||||
env?: Record<string, string>;
|
||||
/** Optional setup command run once after container creation. */
|
||||
setupCommand?: string;
|
||||
/** Limit container PIDs (0 = Docker default). */
|
||||
pidsLimit?: number;
|
||||
/** Limit container memory (e.g. 512m, 2g, or bytes as number). */
|
||||
memory?: string | number;
|
||||
/** Limit container memory swap (same format as memory). */
|
||||
memorySwap?: string | number;
|
||||
/** Limit container CPU shares (e.g. 0.5, 1, 2). */
|
||||
cpus?: number;
|
||||
/**
|
||||
* Set ulimit values by name (e.g. nofile, nproc).
|
||||
* Use "soft:hard" string, a number, or { soft, hard }.
|
||||
*/
|
||||
ulimits?: Record<
|
||||
string,
|
||||
string | number | { soft?: number; hard?: number }
|
||||
>;
|
||||
/** Seccomp profile (path or profile name). */
|
||||
seccompProfile?: string;
|
||||
/** AppArmor profile name. */
|
||||
apparmorProfile?: string;
|
||||
/** DNS servers (e.g. ["1.1.1.1", "8.8.8.8"]). */
|
||||
dns?: string[];
|
||||
/** Extra host mappings (e.g. ["api.local:10.0.0.2"]). */
|
||||
extraHosts?: string[];
|
||||
};
|
||||
/** Optional sandboxed browser settings. */
|
||||
browser?: {
|
||||
@@ -822,7 +846,7 @@ export type ConfigFileSnapshot = {
|
||||
raw: string | null;
|
||||
parsed: unknown;
|
||||
valid: boolean;
|
||||
config: ClawdisConfig;
|
||||
config: ClawdbotConfig;
|
||||
issues: ConfigValidationIssue[];
|
||||
legacyIssues: LegacyConfigIssue[];
|
||||
};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { applyIdentityDefaults, applySessionDefaults } from "./defaults.js";
|
||||
import { findLegacyConfigIssues } from "./legacy.js";
|
||||
import type { ClawdisConfig, ConfigValidationIssue } from "./types.js";
|
||||
import { ClawdisSchema } from "./zod-schema.js";
|
||||
import type { ClawdbotConfig, ConfigValidationIssue } from "./types.js";
|
||||
import { ClawdbotSchema } from "./zod-schema.js";
|
||||
|
||||
export function validateConfigObject(
|
||||
raw: unknown,
|
||||
):
|
||||
| { ok: true; config: ClawdisConfig }
|
||||
| { ok: true; config: ClawdbotConfig }
|
||||
| { ok: false; issues: ConfigValidationIssue[] } {
|
||||
const legacyIssues = findLegacyConfigIssues(raw);
|
||||
if (legacyIssues.length > 0) {
|
||||
@@ -18,7 +18,7 @@ export function validateConfigObject(
|
||||
})),
|
||||
};
|
||||
}
|
||||
const validated = ClawdisSchema.safeParse(raw);
|
||||
const validated = ClawdbotSchema.safeParse(raw);
|
||||
if (!validated.success) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -31,7 +31,7 @@ export function validateConfigObject(
|
||||
return {
|
||||
ok: true,
|
||||
config: applySessionDefaults(
|
||||
applyIdentityDefaults(validated.data as ClawdisConfig),
|
||||
applyIdentityDefaults(validated.data as ClawdbotConfig),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ const HooksGmailSchema = z
|
||||
})
|
||||
.optional();
|
||||
|
||||
export const ClawdisSchema = z.object({
|
||||
export const ClawdbotSchema = z.object({
|
||||
identity: z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
@@ -444,6 +444,27 @@ export const ClawdisSchema = z.object({
|
||||
capDrop: z.array(z.string()).optional(),
|
||||
env: z.record(z.string(), z.string()).optional(),
|
||||
setupCommand: z.string().optional(),
|
||||
pidsLimit: z.number().int().positive().optional(),
|
||||
memory: z.union([z.string(), z.number()]).optional(),
|
||||
memorySwap: z.union([z.string(), z.number()]).optional(),
|
||||
cpus: z.number().positive().optional(),
|
||||
ulimits: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.union([
|
||||
z.string(),
|
||||
z.number(),
|
||||
z.object({
|
||||
soft: z.number().int().nonnegative().optional(),
|
||||
hard: z.number().int().nonnegative().optional(),
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.optional(),
|
||||
seccompProfile: z.string().optional(),
|
||||
apparmorProfile: z.string().optional(),
|
||||
dns: z.array(z.string()).optional(),
|
||||
extraHosts: z.array(z.string()).optional(),
|
||||
})
|
||||
.optional(),
|
||||
browser: z
|
||||
|
||||
Reference in New Issue
Block a user