mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 17:01:53 +03:00
chore: rename project to clawdbot
This commit is contained in:
@@ -4,17 +4,17 @@ import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
||||
|
||||
const DEFAULT_AGENT_DIR = path.join(CONFIG_DIR, "agent");
|
||||
|
||||
export function resolveClawdisAgentDir(): string {
|
||||
export function resolveClawdbotAgentDir(): string {
|
||||
const override =
|
||||
process.env.CLAWDIS_AGENT_DIR?.trim() ||
|
||||
process.env.CLAWDBOT_AGENT_DIR?.trim() ||
|
||||
process.env.PI_CODING_AGENT_DIR?.trim() ||
|
||||
DEFAULT_AGENT_DIR;
|
||||
return resolveUserPath(override);
|
||||
}
|
||||
|
||||
export function ensureClawdisAgentEnv(): string {
|
||||
const dir = resolveClawdisAgentDir();
|
||||
if (!process.env.CLAWDIS_AGENT_DIR) process.env.CLAWDIS_AGENT_DIR = dir;
|
||||
export function ensureClawdbotAgentEnv(): string {
|
||||
const dir = resolveClawdbotAgentDir();
|
||||
if (!process.env.CLAWDBOT_AGENT_DIR) process.env.CLAWDBOT_AGENT_DIR = dir;
|
||||
if (!process.env.PI_CODING_AGENT_DIR) process.env.PI_CODING_AGENT_DIR = dir;
|
||||
return dir;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createClawdisTools } from "./clawdis-tools.js";
|
||||
import { createClawdbotTools } from "./clawdbot-tools.js";
|
||||
|
||||
describe("gateway tool", () => {
|
||||
it("schedules SIGUSR1 restart", async () => {
|
||||
@@ -8,7 +8,7 @@ describe("gateway tool", () => {
|
||||
const kill = vi.spyOn(process, "kill").mockImplementation(() => true);
|
||||
|
||||
try {
|
||||
const tool = createClawdisTools().find(
|
||||
const tool = createClawdbotTools().find(
|
||||
(candidate) => candidate.name === "gateway",
|
||||
);
|
||||
expect(tool).toBeDefined();
|
||||
@@ -10,7 +10,7 @@ vi.mock("../media/image-ops.js", () => ({
|
||||
resizeToJpeg: vi.fn(async () => Buffer.from("jpeg")),
|
||||
}));
|
||||
|
||||
import { createClawdisTools } from "./clawdis-tools.js";
|
||||
import { createClawdbotTools } from "./clawdbot-tools.js";
|
||||
|
||||
describe("nodes camera_snap", () => {
|
||||
beforeEach(() => {
|
||||
@@ -35,7 +35,7 @@ describe("nodes camera_snap", () => {
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
});
|
||||
|
||||
const tool = createClawdisTools().find(
|
||||
const tool = createClawdbotTools().find(
|
||||
(candidate) => candidate.name === "nodes",
|
||||
);
|
||||
if (!tool) throw new Error("missing nodes tool");
|
||||
@@ -75,7 +75,7 @@ describe("nodes camera_snap", () => {
|
||||
throw new Error(`unexpected method: ${String(method)}`);
|
||||
});
|
||||
|
||||
const tool = createClawdisTools().find(
|
||||
const tool = createClawdbotTools().find(
|
||||
(candidate) => candidate.name === "nodes",
|
||||
);
|
||||
if (!tool) throw new Error("missing nodes tool");
|
||||
+5
-5
@@ -16,7 +16,7 @@ vi.mock("../config/config.js", () => ({
|
||||
resolveGatewayPort: () => 18789,
|
||||
}));
|
||||
|
||||
import { createClawdisTools } from "./clawdis-tools.js";
|
||||
import { createClawdbotTools } from "./clawdbot-tools.js";
|
||||
|
||||
describe("sessions tools", () => {
|
||||
it("sessions_list filters kinds and includes messages", async () => {
|
||||
@@ -67,7 +67,7 @@ describe("sessions tools", () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createClawdisTools().find(
|
||||
const tool = createClawdbotTools().find(
|
||||
(candidate) => candidate.name === "sessions_list",
|
||||
);
|
||||
expect(tool).toBeDefined();
|
||||
@@ -106,7 +106,7 @@ describe("sessions tools", () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createClawdisTools().find(
|
||||
const tool = createClawdbotTools().find(
|
||||
(candidate) => candidate.name === "sessions_history",
|
||||
);
|
||||
expect(tool).toBeDefined();
|
||||
@@ -190,7 +190,7 @@ describe("sessions tools", () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createClawdisTools({
|
||||
const tool = createClawdbotTools({
|
||||
agentSessionKey: requesterKey,
|
||||
agentSurface: "discord",
|
||||
}).find((candidate) => candidate.name === "sessions_send");
|
||||
@@ -340,7 +340,7 @@ describe("sessions tools", () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createClawdisTools({
|
||||
const tool = createClawdbotTools({
|
||||
agentSessionKey: requesterKey,
|
||||
agentSurface: "discord",
|
||||
}).find((candidate) => candidate.name === "sessions_send");
|
||||
@@ -10,7 +10,7 @@ import { createSessionsListTool } from "./tools/sessions-list-tool.js";
|
||||
import { createSessionsSendTool } from "./tools/sessions-send-tool.js";
|
||||
import { createSlackTool } from "./tools/slack-tool.js";
|
||||
|
||||
export function createClawdisTools(options?: {
|
||||
export function createClawdbotTools(options?: {
|
||||
browserControlUrl?: string;
|
||||
agentSessionKey?: string;
|
||||
agentSurface?: string;
|
||||
@@ -2,8 +2,8 @@
|
||||
// the agent reports a model id. This includes custom models.json entries.
|
||||
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { resolveClawdisAgentDir } from "./agent-paths.js";
|
||||
import { ensureClawdisModelsJson } from "./models-config.js";
|
||||
import { resolveClawdbotAgentDir } from "./agent-paths.js";
|
||||
import { ensureClawdbotModelsJson } from "./models-config.js";
|
||||
|
||||
type ModelEntry = { id: string; contextWindow?: number };
|
||||
|
||||
@@ -14,8 +14,8 @@ const loadPromise = (async () => {
|
||||
"@mariozechner/pi-coding-agent"
|
||||
);
|
||||
const cfg = loadConfig();
|
||||
await ensureClawdisModelsJson(cfg);
|
||||
const agentDir = resolveClawdisAgentDir();
|
||||
await ensureClawdbotModelsJson(cfg);
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
const authStorage = discoverAuthStorage(agentDir);
|
||||
const modelRegistry = discoverModels(authStorage, agentDir);
|
||||
const models = modelRegistry.getAll() as ModelEntry[];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type ClawdisConfig, loadConfig } from "../config/config.js";
|
||||
import { resolveClawdisAgentDir } from "./agent-paths.js";
|
||||
import { ensureClawdisModelsJson } from "./models-config.js";
|
||||
import { type ClawdbotConfig, loadConfig } from "../config/config.js";
|
||||
import { resolveClawdbotAgentDir } from "./agent-paths.js";
|
||||
import { ensureClawdbotModelsJson } from "./models-config.js";
|
||||
|
||||
export type ModelCatalogEntry = {
|
||||
id: string;
|
||||
@@ -25,7 +25,7 @@ export function resetModelCatalogCacheForTest() {
|
||||
}
|
||||
|
||||
export async function loadModelCatalog(params?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
useCache?: boolean;
|
||||
}): Promise<ModelCatalogEntry[]> {
|
||||
if (params?.useCache === false) {
|
||||
@@ -39,8 +39,8 @@ export async function loadModelCatalog(params?: {
|
||||
const models: ModelCatalogEntry[] = [];
|
||||
try {
|
||||
const cfg = params?.config ?? loadConfig();
|
||||
await ensureClawdisModelsJson(cfg);
|
||||
const agentDir = resolveClawdisAgentDir();
|
||||
await ensureClawdbotModelsJson(cfg);
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
const authStorage = piSdk.discoverAuthStorage(agentDir);
|
||||
const registry = piSdk.discoverModels(authStorage, agentDir) as
|
||||
| {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
||||
import { resolveConfiguredModelRef } from "./model-selection.js";
|
||||
|
||||
@@ -8,7 +8,7 @@ describe("resolveConfiguredModelRef", () => {
|
||||
it("parses provider/model from agent.model", () => {
|
||||
const cfg = {
|
||||
agent: { model: "openai/gpt-4.1-mini" },
|
||||
} satisfies ClawdisConfig;
|
||||
} satisfies ClawdbotConfig;
|
||||
|
||||
const resolved = resolveConfiguredModelRef({
|
||||
cfg,
|
||||
@@ -22,7 +22,7 @@ describe("resolveConfiguredModelRef", () => {
|
||||
it("falls back to anthropic when agent.model omits provider", () => {
|
||||
const cfg = {
|
||||
agent: { model: "claude-opus-4-5" },
|
||||
} satisfies ClawdisConfig;
|
||||
} satisfies ClawdbotConfig;
|
||||
|
||||
const resolved = resolveConfiguredModelRef({
|
||||
cfg,
|
||||
@@ -37,7 +37,7 @@ describe("resolveConfiguredModelRef", () => {
|
||||
});
|
||||
|
||||
it("falls back to defaults when agent.model is missing", () => {
|
||||
const cfg = {} satisfies ClawdisConfig;
|
||||
const cfg = {} satisfies ClawdbotConfig;
|
||||
|
||||
const resolved = resolveConfiguredModelRef({
|
||||
cfg,
|
||||
@@ -59,7 +59,7 @@ describe("resolveConfiguredModelRef", () => {
|
||||
Opus: "anthropic/claude-opus-4-5",
|
||||
},
|
||||
},
|
||||
} satisfies ClawdisConfig;
|
||||
} satisfies ClawdbotConfig;
|
||||
|
||||
const resolved = resolveConfiguredModelRef({
|
||||
cfg,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { ModelCatalogEntry } from "./model-catalog.js";
|
||||
|
||||
export type ModelRef = {
|
||||
@@ -38,7 +38,7 @@ export function parseModelRef(
|
||||
}
|
||||
|
||||
export function buildModelAliasIndex(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
defaultProvider: string;
|
||||
}): ModelAliasIndex {
|
||||
const rawAliases = params.cfg.agent?.modelAliases ?? {};
|
||||
@@ -84,7 +84,7 @@ export function resolveModelRefFromString(params: {
|
||||
}
|
||||
|
||||
export function resolveConfiguredModelRef(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
defaultProvider: string;
|
||||
defaultModel: string;
|
||||
}): ModelRef {
|
||||
@@ -108,7 +108,7 @@ export function resolveConfiguredModelRef(params: {
|
||||
}
|
||||
|
||||
export function buildAllowedModelSet(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
catalog: ModelCatalogEntry[];
|
||||
defaultProvider: string;
|
||||
}): {
|
||||
@@ -156,7 +156,7 @@ export function buildAllowedModelSet(params: {
|
||||
}
|
||||
|
||||
export function resolveThinkingDefault(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
provider: string;
|
||||
model: string;
|
||||
catalog?: ModelCatalogEntry[];
|
||||
|
||||
@@ -3,10 +3,10 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-models-"));
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-models-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
@@ -17,7 +17,7 @@ async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
}
|
||||
}
|
||||
|
||||
const MODELS_CONFIG: ClawdisConfig = {
|
||||
const MODELS_CONFIG: ClawdbotConfig = {
|
||||
models: {
|
||||
providers: {
|
||||
"custom-proxy": {
|
||||
@@ -55,12 +55,12 @@ describe("models config", () => {
|
||||
it("writes models.json for configured providers", async () => {
|
||||
await withTempHome(async () => {
|
||||
vi.resetModules();
|
||||
const { ensureClawdisModelsJson } = await import("./models-config.js");
|
||||
const { resolveClawdisAgentDir } = await import("./agent-paths.js");
|
||||
const { ensureClawdbotModelsJson } = await import("./models-config.js");
|
||||
const { resolveClawdbotAgentDir } = await import("./agent-paths.js");
|
||||
|
||||
await ensureClawdisModelsJson(MODELS_CONFIG);
|
||||
await ensureClawdbotModelsJson(MODELS_CONFIG);
|
||||
|
||||
const modelPath = path.join(resolveClawdisAgentDir(), "models.json");
|
||||
const modelPath = path.join(resolveClawdbotAgentDir(), "models.json");
|
||||
const raw = await fs.readFile(modelPath, "utf8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
providers: Record<string, { baseUrl?: string }>;
|
||||
@@ -75,10 +75,10 @@ describe("models config", () => {
|
||||
it("merges providers by default", async () => {
|
||||
await withTempHome(async () => {
|
||||
vi.resetModules();
|
||||
const { ensureClawdisModelsJson } = await import("./models-config.js");
|
||||
const { resolveClawdisAgentDir } = await import("./agent-paths.js");
|
||||
const { ensureClawdbotModelsJson } = await import("./models-config.js");
|
||||
const { resolveClawdbotAgentDir } = await import("./agent-paths.js");
|
||||
|
||||
const agentDir = resolveClawdisAgentDir();
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
await fs.mkdir(agentDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(agentDir, "models.json"),
|
||||
@@ -110,7 +110,7 @@ describe("models config", () => {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
await ensureClawdisModelsJson(MODELS_CONFIG);
|
||||
await ensureClawdbotModelsJson(MODELS_CONFIG);
|
||||
|
||||
const raw = await fs.readFile(path.join(agentDir, "models.json"), "utf8");
|
||||
const parsed = JSON.parse(raw) as {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
import { type ClawdisConfig, loadConfig } from "../config/config.js";
|
||||
import { type ClawdbotConfig, loadConfig } from "../config/config.js";
|
||||
import {
|
||||
ensureClawdisAgentEnv,
|
||||
resolveClawdisAgentDir,
|
||||
ensureClawdbotAgentEnv,
|
||||
resolveClawdbotAgentDir,
|
||||
} from "./agent-paths.js";
|
||||
|
||||
type ModelsConfig = NonNullable<ClawdisConfig["models"]>;
|
||||
type ModelsConfig = NonNullable<ClawdbotConfig["models"]>;
|
||||
|
||||
const DEFAULT_MODE: NonNullable<ModelsConfig["mode"]> = "merge";
|
||||
|
||||
@@ -24,17 +24,17 @@ async function readJson(pathname: string): Promise<unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureClawdisModelsJson(
|
||||
config?: ClawdisConfig,
|
||||
export async function ensureClawdbotModelsJson(
|
||||
config?: ClawdbotConfig,
|
||||
): Promise<{ agentDir: string; wrote: boolean }> {
|
||||
const cfg = config ?? loadConfig();
|
||||
const providers = cfg.models?.providers;
|
||||
if (!providers || Object.keys(providers).length === 0) {
|
||||
return { agentDir: resolveClawdisAgentDir(), wrote: false };
|
||||
return { agentDir: resolveClawdbotAgentDir(), wrote: false };
|
||||
}
|
||||
|
||||
const mode = cfg.models?.mode ?? DEFAULT_MODE;
|
||||
const agentDir = ensureClawdisAgentEnv();
|
||||
const agentDir = ensureClawdbotAgentEnv();
|
||||
const targetPath = path.join(agentDir, "models.json");
|
||||
|
||||
let mergedProviders = providers;
|
||||
|
||||
@@ -12,12 +12,12 @@ describe("buildEmbeddedSandboxInfo", () => {
|
||||
const sandbox = {
|
||||
enabled: true,
|
||||
sessionKey: "session:test",
|
||||
workspaceDir: "/tmp/clawdis-sandbox",
|
||||
containerName: "clawdis-sbx-test",
|
||||
workspaceDir: "/tmp/clawdbot-sandbox",
|
||||
containerName: "clawdbot-sbx-test",
|
||||
containerWorkdir: "/workspace",
|
||||
docker: {
|
||||
image: "clawdis-sandbox:bookworm-slim",
|
||||
containerPrefix: "clawdis-sbx-",
|
||||
image: "clawdbot-sandbox:bookworm-slim",
|
||||
containerPrefix: "clawdbot-sbx-",
|
||||
workdir: "/workspace",
|
||||
readOnlyRoot: true,
|
||||
tmpfs: ["/tmp"],
|
||||
@@ -33,13 +33,13 @@ describe("buildEmbeddedSandboxInfo", () => {
|
||||
browser: {
|
||||
controlUrl: "http://localhost:9222",
|
||||
noVncUrl: "http://localhost:6080",
|
||||
containerName: "clawdis-sbx-browser-test",
|
||||
containerName: "clawdbot-sbx-browser-test",
|
||||
},
|
||||
} satisfies SandboxContext;
|
||||
|
||||
expect(buildEmbeddedSandboxInfo(sandbox)).toEqual({
|
||||
enabled: true,
|
||||
workspaceDir: "/tmp/clawdis-sandbox",
|
||||
workspaceDir: "/tmp/clawdbot-sandbox",
|
||||
browserControlUrl: "http://localhost:9222",
|
||||
browserNoVncUrl: "http://localhost:6080",
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import type { ThinkLevel, VerboseLevel } from "../auto-reply/thinking.js";
|
||||
import { formatToolAggregate } from "../auto-reply/tool-meta.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { getMachineDisplayName } from "../infra/machine-name.js";
|
||||
import { createSubsystemLogger } from "../logging.js";
|
||||
import { splitMediaFromOutput } from "../media/parse.js";
|
||||
@@ -33,10 +33,10 @@ import {
|
||||
enqueueCommandInLane,
|
||||
} from "../process/command-queue.js";
|
||||
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
||||
import { resolveClawdisAgentDir } from "./agent-paths.js";
|
||||
import { resolveClawdbotAgentDir } from "./agent-paths.js";
|
||||
import type { BashElevatedDefaults } from "./bash-tools.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
|
||||
import { ensureClawdisModelsJson } from "./models-config.js";
|
||||
import { ensureClawdbotModelsJson } from "./models-config.js";
|
||||
import {
|
||||
buildBootstrapContextFiles,
|
||||
ensureSessionHeader,
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
subscribeEmbeddedPiSession,
|
||||
} from "./pi-embedded-subscribe.js";
|
||||
import { extractAssistantText } from "./pi-embedded-utils.js";
|
||||
import { createClawdisCodingTools } from "./pi-tools.js";
|
||||
import { createClawdbotCodingTools } from "./pi-tools.js";
|
||||
import { resolveSandboxContext } from "./sandbox.js";
|
||||
import {
|
||||
applySkillEnvOverrides,
|
||||
@@ -139,9 +139,9 @@ export function buildEmbeddedSandboxInfo(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveClawdisOAuthPath(): string {
|
||||
function resolveClawdbotOAuthPath(): string {
|
||||
const overrideDir =
|
||||
process.env.CLAWDIS_OAUTH_DIR?.trim() || DEFAULT_OAUTH_DIR;
|
||||
process.env.CLAWDBOT_OAUTH_DIR?.trim() || DEFAULT_OAUTH_DIR;
|
||||
return path.join(resolveUserPath(overrideDir), OAUTH_FILENAME);
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ function importLegacyOAuthIfNeeded(destPath: string): void {
|
||||
function ensureOAuthStorage(): void {
|
||||
if (oauthStorageConfigured) return;
|
||||
oauthStorageConfigured = true;
|
||||
const oauthPath = resolveClawdisOAuthPath();
|
||||
const oauthPath = resolveClawdbotOAuthPath();
|
||||
importLegacyOAuthIfNeeded(oauthPath);
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ export function resolveEmbeddedSessionLane(key: string) {
|
||||
}
|
||||
|
||||
function mapThinkingLevel(level?: ThinkLevel): ThinkingLevel {
|
||||
// pi-agent-core supports "xhigh" too; Clawdis doesn't surface it for now.
|
||||
// pi-agent-core supports "xhigh" too; Clawdbot doesn't surface it for now.
|
||||
if (!level) return "off";
|
||||
return level;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ function resolveModel(
|
||||
authStorage: ReturnType<typeof discoverAuthStorage>;
|
||||
modelRegistry: ReturnType<typeof discoverModels>;
|
||||
} {
|
||||
const resolvedAgentDir = agentDir ?? resolveClawdisAgentDir();
|
||||
const resolvedAgentDir = agentDir ?? resolveClawdbotAgentDir();
|
||||
const authStorage = discoverAuthStorage(resolvedAgentDir);
|
||||
const modelRegistry = discoverModels(authStorage, resolvedAgentDir);
|
||||
const model = modelRegistry.find(provider, modelId) as Model<Api> | null;
|
||||
@@ -341,7 +341,7 @@ async function getApiKeyForModel(
|
||||
const envKey = getEnvApiKey(model.provider);
|
||||
if (envKey) return envKey;
|
||||
if (isOAuthProvider(model.provider)) {
|
||||
const oauthPath = resolveClawdisOAuthPath();
|
||||
const oauthPath = resolveClawdbotOAuthPath();
|
||||
const storage = loadOAuthStorageAt(oauthPath);
|
||||
if (storage) {
|
||||
try {
|
||||
@@ -384,7 +384,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
surface?: string;
|
||||
sessionFile: string;
|
||||
workspaceDir: string;
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
skillsSnapshot?: SkillSnapshot;
|
||||
prompt: string;
|
||||
provider?: string;
|
||||
@@ -436,8 +436,8 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const provider =
|
||||
(params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
|
||||
const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
|
||||
await ensureClawdisModelsJson(params.config);
|
||||
const agentDir = resolveClawdisAgentDir();
|
||||
await ensureClawdbotModelsJson(params.config);
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
const { model, error, authStorage, modelRegistry } = resolveModel(
|
||||
provider,
|
||||
modelId,
|
||||
@@ -496,7 +496,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
await loadWorkspaceBootstrapFiles(resolvedWorkspace);
|
||||
const contextFiles = buildBootstrapContextFiles(bootstrapFiles);
|
||||
const promptSkills = resolvePromptSkills(skillsSnapshot, skillEntries);
|
||||
const tools = createClawdisCodingTools({
|
||||
const tools = createClawdbotCodingTools({
|
||||
bash: {
|
||||
...params.config?.agent?.bash,
|
||||
elevated: params.bashElevated,
|
||||
|
||||
+19
-19
@@ -4,11 +4,11 @@ import path from "node:path";
|
||||
|
||||
import sharp from "sharp";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createClawdisCodingTools } from "./pi-tools.js";
|
||||
import { createClawdbotCodingTools } from "./pi-tools.js";
|
||||
|
||||
describe("createClawdisCodingTools", () => {
|
||||
describe("createClawdbotCodingTools", () => {
|
||||
it("merges properties for union tool schemas", () => {
|
||||
const tools = createClawdisCodingTools();
|
||||
const tools = createClawdbotCodingTools();
|
||||
const browser = tools.find((tool) => tool.name === "browser");
|
||||
expect(browser).toBeDefined();
|
||||
const parameters = browser?.parameters as {
|
||||
@@ -24,7 +24,7 @@ describe("createClawdisCodingTools", () => {
|
||||
});
|
||||
|
||||
it("preserves union action values in merged schema", () => {
|
||||
const tools = createClawdisCodingTools();
|
||||
const tools = createClawdbotCodingTools();
|
||||
const toolNames = ["browser", "canvas", "nodes", "cron", "gateway"];
|
||||
|
||||
for (const name of toolNames) {
|
||||
@@ -75,33 +75,33 @@ describe("createClawdisCodingTools", () => {
|
||||
});
|
||||
|
||||
it("includes bash and process tools", () => {
|
||||
const tools = createClawdisCodingTools();
|
||||
const tools = createClawdbotCodingTools();
|
||||
expect(tools.some((tool) => tool.name === "bash")).toBe(true);
|
||||
expect(tools.some((tool) => tool.name === "process")).toBe(true);
|
||||
});
|
||||
|
||||
it("scopes discord tool to discord surface", () => {
|
||||
const other = createClawdisCodingTools({ surface: "whatsapp" });
|
||||
const other = createClawdbotCodingTools({ surface: "whatsapp" });
|
||||
expect(other.some((tool) => tool.name === "discord")).toBe(false);
|
||||
|
||||
const discord = createClawdisCodingTools({ surface: "discord" });
|
||||
const discord = createClawdbotCodingTools({ surface: "discord" });
|
||||
expect(discord.some((tool) => tool.name === "discord")).toBe(true);
|
||||
});
|
||||
|
||||
it("scopes slack tool to slack surface", () => {
|
||||
const other = createClawdisCodingTools({ surface: "whatsapp" });
|
||||
const other = createClawdbotCodingTools({ surface: "whatsapp" });
|
||||
expect(other.some((tool) => tool.name === "slack")).toBe(false);
|
||||
|
||||
const slack = createClawdisCodingTools({ surface: "slack" });
|
||||
const slack = createClawdbotCodingTools({ surface: "slack" });
|
||||
expect(slack.some((tool) => tool.name === "slack")).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps read tool image metadata intact", async () => {
|
||||
const tools = createClawdisCodingTools();
|
||||
const tools = createClawdbotCodingTools();
|
||||
const readTool = tools.find((tool) => tool.name === "read");
|
||||
expect(readTool).toBeDefined();
|
||||
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-read-"));
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-read-"));
|
||||
try {
|
||||
const imagePath = path.join(tmpDir, "sample.png");
|
||||
const png = await sharp({
|
||||
@@ -137,14 +137,14 @@ describe("createClawdisCodingTools", () => {
|
||||
});
|
||||
|
||||
it("returns text content without image blocks for text files", async () => {
|
||||
const tools = createClawdisCodingTools();
|
||||
const tools = createClawdbotCodingTools();
|
||||
const readTool = tools.find((tool) => tool.name === "read");
|
||||
expect(readTool).toBeDefined();
|
||||
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-read-"));
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-read-"));
|
||||
try {
|
||||
const textPath = path.join(tmpDir, "sample.txt");
|
||||
const contents = "Hello from clawdis read tool.";
|
||||
const contents = "Hello from clawdbot read tool.";
|
||||
await fs.writeFile(textPath, contents, "utf8");
|
||||
|
||||
const result = await readTool?.execute("tool-2", {
|
||||
@@ -171,12 +171,12 @@ describe("createClawdisCodingTools", () => {
|
||||
const sandbox = {
|
||||
enabled: true,
|
||||
sessionKey: "sandbox:test",
|
||||
workspaceDir: path.join(os.tmpdir(), "clawdis-sandbox"),
|
||||
containerName: "clawdis-sbx-test",
|
||||
workspaceDir: path.join(os.tmpdir(), "clawdbot-sandbox"),
|
||||
containerName: "clawdbot-sbx-test",
|
||||
containerWorkdir: "/workspace",
|
||||
docker: {
|
||||
image: "clawdis-sandbox:bookworm-slim",
|
||||
containerPrefix: "clawdis-sbx-",
|
||||
image: "clawdbot-sandbox:bookworm-slim",
|
||||
containerPrefix: "clawdbot-sbx-",
|
||||
workdir: "/workspace",
|
||||
readOnlyRoot: true,
|
||||
tmpfs: [],
|
||||
@@ -190,7 +190,7 @@ describe("createClawdisCodingTools", () => {
|
||||
deny: ["browser"],
|
||||
},
|
||||
};
|
||||
const tools = createClawdisCodingTools({ sandbox });
|
||||
const tools = createClawdbotCodingTools({ sandbox });
|
||||
expect(tools.some((tool) => tool.name === "bash")).toBe(true);
|
||||
expect(tools.some((tool) => tool.name === "read")).toBe(false);
|
||||
expect(tools.some((tool) => tool.name === "browser")).toBe(false);
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
createProcessTool,
|
||||
type ProcessToolDefaults,
|
||||
} from "./bash-tools.js";
|
||||
import { createClawdisTools } from "./clawdis-tools.js";
|
||||
import { createClawdbotTools } from "./clawdbot-tools.js";
|
||||
import type { SandboxContext, SandboxToolPolicy } from "./sandbox.js";
|
||||
import { assertSandboxPath } from "./sandbox-paths.js";
|
||||
import { sanitizeToolResultImages } from "./tool-images.js";
|
||||
@@ -332,7 +332,7 @@ function wrapSandboxPathGuard(tool: AnyAgentTool, root: string): AnyAgentTool {
|
||||
|
||||
function createSandboxedReadTool(root: string) {
|
||||
const base = createReadTool(root);
|
||||
return wrapSandboxPathGuard(createClawdisReadTool(base), root);
|
||||
return wrapSandboxPathGuard(createClawdbotReadTool(base), root);
|
||||
}
|
||||
|
||||
function createSandboxedWriteTool(root: string) {
|
||||
@@ -409,7 +409,7 @@ function createWhatsAppLoginTool(): AnyAgentTool {
|
||||
};
|
||||
}
|
||||
|
||||
function createClawdisReadTool(base: AnyAgentTool): AnyAgentTool {
|
||||
function createClawdbotReadTool(base: AnyAgentTool): AnyAgentTool {
|
||||
return {
|
||||
...base,
|
||||
execute: async (toolCallId, params, signal) => {
|
||||
@@ -447,7 +447,7 @@ function shouldIncludeSlackTool(surface?: string): boolean {
|
||||
return normalized === "slack" || normalized.startsWith("slack:");
|
||||
}
|
||||
|
||||
export function createClawdisCodingTools(options?: {
|
||||
export function createClawdbotCodingTools(options?: {
|
||||
bash?: BashToolDefaults & ProcessToolDefaults;
|
||||
surface?: string;
|
||||
sandbox?: SandboxContext | null;
|
||||
@@ -460,7 +460,7 @@ export function createClawdisCodingTools(options?: {
|
||||
if (tool.name === readTool.name) {
|
||||
return sandboxRoot
|
||||
? [createSandboxedReadTool(sandboxRoot)]
|
||||
: [createClawdisReadTool(tool)];
|
||||
: [createClawdbotReadTool(tool)];
|
||||
}
|
||||
if (tool.name === bashToolName) return [];
|
||||
if (sandboxRoot && (tool.name === "write" || tool.name === "edit")) {
|
||||
@@ -493,7 +493,7 @@ export function createClawdisCodingTools(options?: {
|
||||
bashTool as unknown as AnyAgentTool,
|
||||
processTool as unknown as AnyAgentTool,
|
||||
createWhatsAppLoginTool(),
|
||||
...createClawdisTools({
|
||||
...createClawdbotTools({
|
||||
browserControlUrl: sandbox?.browser?.controlUrl,
|
||||
agentSessionKey: options?.sessionKey,
|
||||
agentSurface: options?.surface,
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
buildSandboxCreateArgs,
|
||||
type SandboxDockerConfig,
|
||||
} from "./sandbox.js";
|
||||
|
||||
describe("buildSandboxCreateArgs", () => {
|
||||
it("includes hardening and resource flags", () => {
|
||||
const cfg: SandboxDockerConfig = {
|
||||
image: "clawdbot-sandbox:bookworm-slim",
|
||||
containerPrefix: "clawdbot-sbx-",
|
||||
workdir: "/workspace",
|
||||
readOnlyRoot: true,
|
||||
tmpfs: ["/tmp"],
|
||||
network: "none",
|
||||
user: "1000:1000",
|
||||
capDrop: ["ALL"],
|
||||
env: { LANG: "C.UTF-8" },
|
||||
pidsLimit: 256,
|
||||
memory: "512m",
|
||||
memorySwap: 1024,
|
||||
cpus: 1.5,
|
||||
ulimits: {
|
||||
nofile: { soft: 1024, hard: 2048 },
|
||||
nproc: 128,
|
||||
core: "0",
|
||||
},
|
||||
seccompProfile: "/tmp/seccomp.json",
|
||||
apparmorProfile: "clawdbot-sandbox",
|
||||
dns: ["1.1.1.1"],
|
||||
extraHosts: ["internal.service:10.0.0.5"],
|
||||
};
|
||||
|
||||
const args = buildSandboxCreateArgs({
|
||||
name: "clawdbot-sbx-test",
|
||||
cfg,
|
||||
sessionKey: "main",
|
||||
createdAtMs: 1700000000000,
|
||||
labels: { "clawdbot.sandboxBrowser": "1" },
|
||||
});
|
||||
|
||||
expect(args).toEqual(
|
||||
expect.arrayContaining([
|
||||
"create",
|
||||
"--name",
|
||||
"clawdbot-sbx-test",
|
||||
"--label",
|
||||
"clawdbot.sandbox=1",
|
||||
"--label",
|
||||
"clawdbot.sessionKey=main",
|
||||
"--label",
|
||||
"clawdbot.createdAtMs=1700000000000",
|
||||
"--label",
|
||||
"clawdbot.sandboxBrowser=1",
|
||||
"--read-only",
|
||||
"--tmpfs",
|
||||
"/tmp",
|
||||
"--network",
|
||||
"none",
|
||||
"--user",
|
||||
"1000:1000",
|
||||
"--cap-drop",
|
||||
"ALL",
|
||||
"--security-opt",
|
||||
"no-new-privileges",
|
||||
"--security-opt",
|
||||
"seccomp=/tmp/seccomp.json",
|
||||
"--security-opt",
|
||||
"apparmor=clawdbot-sandbox",
|
||||
"--dns",
|
||||
"1.1.1.1",
|
||||
"--add-host",
|
||||
"internal.service:10.0.0.5",
|
||||
"--pids-limit",
|
||||
"256",
|
||||
"--memory",
|
||||
"512m",
|
||||
"--memory-swap",
|
||||
"1024",
|
||||
"--cpus",
|
||||
"1.5",
|
||||
]),
|
||||
);
|
||||
|
||||
const ulimitValues: string[] = [];
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
if (args[i] === "--ulimit") {
|
||||
const value = args[i + 1];
|
||||
if (value) ulimitValues.push(value);
|
||||
}
|
||||
}
|
||||
expect(ulimitValues).toEqual(
|
||||
expect.arrayContaining(["nofile=1024:2048", "nproc=128", "core=0"]),
|
||||
);
|
||||
});
|
||||
});
|
||||
+130
-46
@@ -14,8 +14,8 @@ import {
|
||||
resolveProfile,
|
||||
} from "../browser/config.js";
|
||||
import { DEFAULT_CLAWD_BROWSER_COLOR } from "../browser/constants.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import { STATE_DIR_CLAWDIS } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { STATE_DIR_CLAWDBOT } from "../config/config.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import {
|
||||
@@ -56,6 +56,18 @@ export type SandboxDockerConfig = {
|
||||
capDrop: string[];
|
||||
env?: Record<string, string>;
|
||||
setupCommand?: string;
|
||||
pidsLimit?: number;
|
||||
memory?: string | number;
|
||||
memorySwap?: string | number;
|
||||
cpus?: number;
|
||||
ulimits?: Record<
|
||||
string,
|
||||
string | number | { soft?: number; hard?: number }
|
||||
>;
|
||||
seccompProfile?: string;
|
||||
apparmorProfile?: string;
|
||||
dns?: string[];
|
||||
extraHosts?: string[];
|
||||
};
|
||||
|
||||
export type SandboxPruneConfig = {
|
||||
@@ -92,11 +104,11 @@ export type SandboxContext = {
|
||||
|
||||
const DEFAULT_SANDBOX_WORKSPACE_ROOT = path.join(
|
||||
os.homedir(),
|
||||
".clawdis",
|
||||
".clawdbot",
|
||||
"sandboxes",
|
||||
);
|
||||
const DEFAULT_SANDBOX_IMAGE = "clawdis-sandbox:bookworm-slim";
|
||||
const DEFAULT_SANDBOX_CONTAINER_PREFIX = "clawdis-sbx-";
|
||||
const DEFAULT_SANDBOX_IMAGE = "clawdbot-sandbox:bookworm-slim";
|
||||
const DEFAULT_SANDBOX_CONTAINER_PREFIX = "clawdbot-sbx-";
|
||||
const DEFAULT_SANDBOX_WORKDIR = "/workspace";
|
||||
const DEFAULT_SANDBOX_IDLE_HOURS = 24;
|
||||
const DEFAULT_SANDBOX_MAX_AGE_DAYS = 7;
|
||||
@@ -109,13 +121,13 @@ const DEFAULT_TOOL_DENY = [
|
||||
"discord",
|
||||
"gateway",
|
||||
];
|
||||
const DEFAULT_SANDBOX_BROWSER_IMAGE = "clawdis-sandbox-browser:bookworm-slim";
|
||||
const DEFAULT_SANDBOX_BROWSER_PREFIX = "clawdis-sbx-browser-";
|
||||
const DEFAULT_SANDBOX_BROWSER_IMAGE = "clawdbot-sandbox-browser:bookworm-slim";
|
||||
const DEFAULT_SANDBOX_BROWSER_PREFIX = "clawdbot-sbx-browser-";
|
||||
const DEFAULT_SANDBOX_BROWSER_CDP_PORT = 9222;
|
||||
const DEFAULT_SANDBOX_BROWSER_VNC_PORT = 5900;
|
||||
const DEFAULT_SANDBOX_BROWSER_NOVNC_PORT = 6080;
|
||||
|
||||
const SANDBOX_STATE_DIR = path.join(STATE_DIR_CLAWDIS, "sandbox");
|
||||
const SANDBOX_STATE_DIR = path.join(STATE_DIR_CLAWDBOT, "sandbox");
|
||||
const SANDBOX_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "containers.json");
|
||||
const SANDBOX_BROWSER_REGISTRY_PATH = path.join(
|
||||
SANDBOX_STATE_DIR,
|
||||
@@ -170,7 +182,7 @@ function isToolAllowed(policy: SandboxToolPolicy, name: string) {
|
||||
return allow.includes(name.toLowerCase());
|
||||
}
|
||||
|
||||
function defaultSandboxConfig(cfg?: ClawdisConfig): SandboxConfig {
|
||||
function defaultSandboxConfig(cfg?: ClawdbotConfig): SandboxConfig {
|
||||
const agent = cfg?.agent?.sandbox;
|
||||
return {
|
||||
mode: agent?.mode ?? "off",
|
||||
@@ -183,11 +195,20 @@ function defaultSandboxConfig(cfg?: ClawdisConfig): SandboxConfig {
|
||||
workdir: agent?.docker?.workdir ?? DEFAULT_SANDBOX_WORKDIR,
|
||||
readOnlyRoot: agent?.docker?.readOnlyRoot ?? true,
|
||||
tmpfs: agent?.docker?.tmpfs ?? ["/tmp", "/var/tmp", "/run"],
|
||||
network: agent?.docker?.network ?? "bridge",
|
||||
network: agent?.docker?.network ?? "none",
|
||||
user: agent?.docker?.user,
|
||||
capDrop: agent?.docker?.capDrop ?? ["ALL"],
|
||||
env: agent?.docker?.env ?? { LANG: "C.UTF-8" },
|
||||
setupCommand: agent?.docker?.setupCommand,
|
||||
pidsLimit: agent?.docker?.pidsLimit,
|
||||
memory: agent?.docker?.memory,
|
||||
memorySwap: agent?.docker?.memorySwap,
|
||||
cpus: agent?.docker?.cpus,
|
||||
ulimits: agent?.docker?.ulimits,
|
||||
seccompProfile: agent?.docker?.seccompProfile,
|
||||
apparmorProfile: agent?.docker?.apparmorProfile,
|
||||
dns: agent?.docker?.dns,
|
||||
extraHosts: agent?.docker?.extraHosts,
|
||||
},
|
||||
browser: {
|
||||
enabled: agent?.browser?.enabled ?? false,
|
||||
@@ -428,6 +449,88 @@ async function ensureSandboxWorkspace(workspaceDir: string, seedFrom?: string) {
|
||||
await ensureAgentWorkspace({ dir: workspaceDir, ensureBootstrapFiles: true });
|
||||
}
|
||||
|
||||
function normalizeDockerLimit(value?: string | number) {
|
||||
if (value === undefined || value === null) return undefined;
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? String(value) : undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
function formatUlimitValue(
|
||||
name: string,
|
||||
value: string | number | { soft?: number; hard?: number },
|
||||
) {
|
||||
if (!name.trim()) return null;
|
||||
if (typeof value === "number" || typeof value === "string") {
|
||||
const raw = String(value).trim();
|
||||
return raw ? `${name}=${raw}` : null;
|
||||
}
|
||||
const soft =
|
||||
typeof value.soft === "number" ? Math.max(0, value.soft) : undefined;
|
||||
const hard =
|
||||
typeof value.hard === "number" ? Math.max(0, value.hard) : undefined;
|
||||
if (soft === undefined && hard === undefined) return null;
|
||||
if (soft === undefined) return `${name}=${hard}`;
|
||||
if (hard === undefined) return `${name}=${soft}`;
|
||||
return `${name}=${soft}:${hard}`;
|
||||
}
|
||||
|
||||
export function buildSandboxCreateArgs(params: {
|
||||
name: string;
|
||||
cfg: SandboxDockerConfig;
|
||||
sessionKey: string;
|
||||
createdAtMs?: number;
|
||||
labels?: Record<string, string>;
|
||||
}) {
|
||||
const createdAtMs = params.createdAtMs ?? Date.now();
|
||||
const args = ["create", "--name", params.name];
|
||||
args.push("--label", "clawdbot.sandbox=1");
|
||||
args.push("--label", `clawdbot.sessionKey=${params.sessionKey}`);
|
||||
args.push("--label", `clawdbot.createdAtMs=${createdAtMs}`);
|
||||
for (const [key, value] of Object.entries(params.labels ?? {})) {
|
||||
if (key && value) args.push("--label", `${key}=${value}`);
|
||||
}
|
||||
if (params.cfg.readOnlyRoot) args.push("--read-only");
|
||||
for (const entry of params.cfg.tmpfs) {
|
||||
args.push("--tmpfs", entry);
|
||||
}
|
||||
if (params.cfg.network) args.push("--network", params.cfg.network);
|
||||
if (params.cfg.user) args.push("--user", params.cfg.user);
|
||||
for (const cap of params.cfg.capDrop) {
|
||||
args.push("--cap-drop", cap);
|
||||
}
|
||||
args.push("--security-opt", "no-new-privileges");
|
||||
if (params.cfg.seccompProfile) {
|
||||
args.push("--security-opt", `seccomp=${params.cfg.seccompProfile}`);
|
||||
}
|
||||
if (params.cfg.apparmorProfile) {
|
||||
args.push("--security-opt", `apparmor=${params.cfg.apparmorProfile}`);
|
||||
}
|
||||
for (const entry of params.cfg.dns ?? []) {
|
||||
if (entry.trim()) args.push("--dns", entry);
|
||||
}
|
||||
for (const entry of params.cfg.extraHosts ?? []) {
|
||||
if (entry.trim()) args.push("--add-host", entry);
|
||||
}
|
||||
if (typeof params.cfg.pidsLimit === "number" && params.cfg.pidsLimit > 0) {
|
||||
args.push("--pids-limit", String(params.cfg.pidsLimit));
|
||||
}
|
||||
const memory = normalizeDockerLimit(params.cfg.memory);
|
||||
if (memory) args.push("--memory", memory);
|
||||
const memorySwap = normalizeDockerLimit(params.cfg.memorySwap);
|
||||
if (memorySwap) args.push("--memory-swap", memorySwap);
|
||||
if (typeof params.cfg.cpus === "number" && params.cfg.cpus > 0) {
|
||||
args.push("--cpus", String(params.cfg.cpus));
|
||||
}
|
||||
for (const [name, value] of Object.entries(params.cfg.ulimits ?? {})) {
|
||||
const formatted = formatUlimitValue(name, value);
|
||||
if (formatted) args.push("--ulimit", formatted);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
async function createSandboxContainer(params: {
|
||||
name: string;
|
||||
cfg: SandboxDockerConfig;
|
||||
@@ -437,20 +540,11 @@ async function createSandboxContainer(params: {
|
||||
const { name, cfg, workspaceDir, sessionKey } = params;
|
||||
await ensureDockerImage(cfg.image);
|
||||
|
||||
const args = ["create", "--name", name];
|
||||
args.push("--label", "clawdis.sandbox=1");
|
||||
args.push("--label", `clawdis.sessionKey=${sessionKey}`);
|
||||
args.push("--label", `clawdis.createdAtMs=${Date.now()}`);
|
||||
if (cfg.readOnlyRoot) args.push("--read-only");
|
||||
for (const entry of cfg.tmpfs) {
|
||||
args.push("--tmpfs", entry);
|
||||
}
|
||||
if (cfg.network) args.push("--network", cfg.network);
|
||||
if (cfg.user) args.push("--user", cfg.user);
|
||||
for (const cap of cfg.capDrop) {
|
||||
args.push("--cap-drop", cap);
|
||||
}
|
||||
args.push("--security-opt", "no-new-privileges");
|
||||
const args = buildSandboxCreateArgs({
|
||||
name,
|
||||
cfg,
|
||||
sessionKey,
|
||||
});
|
||||
args.push("--workdir", cfg.workdir);
|
||||
args.push("-v", `${workspaceDir}:${cfg.workdir}`);
|
||||
args.push(cfg.image, "sleep", "infinity");
|
||||
@@ -547,22 +641,12 @@ async function ensureSandboxBrowser(params: {
|
||||
const state = await dockerContainerState(containerName);
|
||||
if (!state.exists) {
|
||||
await ensureSandboxBrowserImage(params.cfg.browser.image);
|
||||
const args = ["create", "--name", containerName];
|
||||
args.push("--label", "clawdis.sandbox=1");
|
||||
args.push("--label", "clawdis.sandboxBrowser=1");
|
||||
args.push("--label", `clawdis.sessionKey=${params.sessionKey}`);
|
||||
args.push("--label", `clawdis.createdAtMs=${Date.now()}`);
|
||||
if (params.cfg.docker.readOnlyRoot) args.push("--read-only");
|
||||
for (const entry of params.cfg.docker.tmpfs) {
|
||||
args.push("--tmpfs", entry);
|
||||
}
|
||||
if (params.cfg.docker.network)
|
||||
args.push("--network", params.cfg.docker.network);
|
||||
if (params.cfg.docker.user) args.push("--user", params.cfg.docker.user);
|
||||
for (const cap of params.cfg.docker.capDrop) {
|
||||
args.push("--cap-drop", cap);
|
||||
}
|
||||
args.push("--security-opt", "no-new-privileges");
|
||||
const args = buildSandboxCreateArgs({
|
||||
name: containerName,
|
||||
cfg: params.cfg.docker,
|
||||
sessionKey: params.sessionKey,
|
||||
labels: { "clawdbot.sandboxBrowser": "1" },
|
||||
});
|
||||
args.push("-v", `${params.workspaceDir}:${params.cfg.docker.workdir}`);
|
||||
args.push("-p", `127.0.0.1::${params.cfg.browser.cdpPort}`);
|
||||
if (params.cfg.browser.enableNoVnc && !params.cfg.browser.headless) {
|
||||
@@ -570,19 +654,19 @@ async function ensureSandboxBrowser(params: {
|
||||
}
|
||||
args.push(
|
||||
"-e",
|
||||
`CLAWDIS_BROWSER_HEADLESS=${params.cfg.browser.headless ? "1" : "0"}`,
|
||||
`CLAWDBOT_BROWSER_HEADLESS=${params.cfg.browser.headless ? "1" : "0"}`,
|
||||
);
|
||||
args.push(
|
||||
"-e",
|
||||
`CLAWDIS_BROWSER_ENABLE_NOVNC=${
|
||||
`CLAWDBOT_BROWSER_ENABLE_NOVNC=${
|
||||
params.cfg.browser.enableNoVnc ? "1" : "0"
|
||||
}`,
|
||||
);
|
||||
args.push("-e", `CLAWDIS_BROWSER_CDP_PORT=${params.cfg.browser.cdpPort}`);
|
||||
args.push("-e", `CLAWDIS_BROWSER_VNC_PORT=${params.cfg.browser.vncPort}`);
|
||||
args.push("-e", `CLAWDBOT_BROWSER_CDP_PORT=${params.cfg.browser.cdpPort}`);
|
||||
args.push("-e", `CLAWDBOT_BROWSER_VNC_PORT=${params.cfg.browser.vncPort}`);
|
||||
args.push(
|
||||
"-e",
|
||||
`CLAWDIS_BROWSER_NOVNC_PORT=${params.cfg.browser.noVncPort}`,
|
||||
`CLAWDBOT_BROWSER_NOVNC_PORT=${params.cfg.browser.noVncPort}`,
|
||||
);
|
||||
args.push(params.cfg.browser.image);
|
||||
await execDocker(args);
|
||||
@@ -739,7 +823,7 @@ async function maybePruneSandboxes(cfg: SandboxConfig) {
|
||||
}
|
||||
|
||||
export async function resolveSandboxContext(params: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
sessionKey?: string;
|
||||
workspaceDir?: string;
|
||||
}): Promise<SandboxContext | null> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { runCommandWithTimeout } from "../process/exec.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ export type SkillInstallRequest = {
|
||||
skillName: string;
|
||||
installId: string;
|
||||
timeoutMs?: number;
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
};
|
||||
|
||||
export type SkillInstallResult = {
|
||||
@@ -73,7 +73,7 @@ function findInstallSpec(
|
||||
entry: SkillEntry,
|
||||
installId: string,
|
||||
): SkillInstallSpec | undefined {
|
||||
const specs = entry.clawdis?.install ?? [];
|
||||
const specs = entry.clawdbot?.install ?? [];
|
||||
for (const [index, spec] of specs.entries()) {
|
||||
if (resolveInstallId(spec, index) === installId) return spec;
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
import path from "node:path";
|
||||
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { CONFIG_DIR } from "../utils.js";
|
||||
import {
|
||||
hasBinary,
|
||||
@@ -66,7 +66,7 @@ export type SkillStatusReport = {
|
||||
};
|
||||
|
||||
function resolveSkillKey(entry: SkillEntry): string {
|
||||
return entry.clawdis?.skillKey ?? entry.skill.name;
|
||||
return entry.clawdbot?.skillKey ?? entry.skill.name;
|
||||
}
|
||||
|
||||
function selectPreferredInstallSpec(
|
||||
@@ -95,7 +95,7 @@ function normalizeInstallOptions(
|
||||
entry: SkillEntry,
|
||||
prefs: SkillsInstallPreferences,
|
||||
): SkillInstallOption[] {
|
||||
const install = entry.clawdis?.install ?? [];
|
||||
const install = entry.clawdbot?.install ?? [];
|
||||
if (install.length === 0) return [];
|
||||
const preferred = selectPreferredInstallSpec(install, prefs);
|
||||
if (!preferred) return [];
|
||||
@@ -131,7 +131,7 @@ function normalizeInstallOptions(
|
||||
|
||||
function buildSkillStatus(
|
||||
entry: SkillEntry,
|
||||
config?: ClawdisConfig,
|
||||
config?: ClawdbotConfig,
|
||||
prefs?: SkillsInstallPreferences,
|
||||
): SkillStatusEntry {
|
||||
const skillKey = resolveSkillKey(entry);
|
||||
@@ -139,19 +139,19 @@ function buildSkillStatus(
|
||||
const disabled = skillConfig?.enabled === false;
|
||||
const allowBundled = resolveBundledAllowlist(config);
|
||||
const blockedByAllowlist = !isBundledSkillAllowed(entry, allowBundled);
|
||||
const always = entry.clawdis?.always === true;
|
||||
const emoji = entry.clawdis?.emoji ?? entry.frontmatter.emoji;
|
||||
const always = entry.clawdbot?.always === true;
|
||||
const emoji = entry.clawdbot?.emoji ?? entry.frontmatter.emoji;
|
||||
const homepageRaw =
|
||||
entry.clawdis?.homepage ??
|
||||
entry.clawdbot?.homepage ??
|
||||
entry.frontmatter.homepage ??
|
||||
entry.frontmatter.website ??
|
||||
entry.frontmatter.url;
|
||||
const homepage = homepageRaw?.trim() ? homepageRaw.trim() : undefined;
|
||||
|
||||
const requiredBins = entry.clawdis?.requires?.bins ?? [];
|
||||
const requiredEnv = entry.clawdis?.requires?.env ?? [];
|
||||
const requiredConfig = entry.clawdis?.requires?.config ?? [];
|
||||
const requiredOs = entry.clawdis?.os ?? [];
|
||||
const requiredBins = entry.clawdbot?.requires?.bins ?? [];
|
||||
const requiredEnv = entry.clawdbot?.requires?.env ?? [];
|
||||
const requiredConfig = entry.clawdbot?.requires?.config ?? [];
|
||||
const requiredOs = entry.clawdbot?.os ?? [];
|
||||
|
||||
const missingBins = requiredBins.filter((bin) => !hasBinary(bin));
|
||||
const missingOs =
|
||||
@@ -163,7 +163,7 @@ function buildSkillStatus(
|
||||
for (const envName of requiredEnv) {
|
||||
if (process.env[envName]) continue;
|
||||
if (skillConfig?.env?.[envName]) continue;
|
||||
if (skillConfig?.apiKey && entry.clawdis?.primaryEnv === envName) {
|
||||
if (skillConfig?.apiKey && entry.clawdbot?.primaryEnv === envName) {
|
||||
continue;
|
||||
}
|
||||
missingEnv.push(envName);
|
||||
@@ -204,7 +204,7 @@ function buildSkillStatus(
|
||||
filePath: entry.skill.filePath,
|
||||
baseDir: entry.skill.baseDir,
|
||||
skillKey,
|
||||
primaryEnv: entry.clawdis?.primaryEnv,
|
||||
primaryEnv: entry.clawdbot?.primaryEnv,
|
||||
emoji,
|
||||
homepage,
|
||||
always,
|
||||
@@ -229,7 +229,7 @@ function buildSkillStatus(
|
||||
export function buildWorkspaceSkillStatus(
|
||||
workspaceDir: string,
|
||||
opts?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
managedSkillsDir?: string;
|
||||
entries?: SkillEntry[];
|
||||
},
|
||||
|
||||
+31
-31
@@ -37,7 +37,7 @@ ${body ?? `# ${name}\n`}
|
||||
|
||||
describe("buildWorkspaceSkillsPrompt", () => {
|
||||
it("returns empty prompt when skills dirs are missing", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
|
||||
const prompt = buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
@@ -48,7 +48,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("loads bundled skills when present", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const bundledSkillDir = path.join(bundledDir, "peekaboo");
|
||||
|
||||
@@ -69,7 +69,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("loads extra skill folders from config (lowest precedence)", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const extraDir = path.join(workspaceDir, ".extra");
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const managedDir = path.join(workspaceDir, ".managed");
|
||||
@@ -112,7 +112,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("loads skills from workspace skills/", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "demo-skill");
|
||||
|
||||
await writeSkill({
|
||||
@@ -131,7 +131,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("filters skills based on env/config gates", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "nano-banana-pro");
|
||||
const originalEnv = process.env.GEMINI_API_KEY;
|
||||
delete process.env.GEMINI_API_KEY;
|
||||
@@ -142,7 +142,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
name: "nano-banana-pro",
|
||||
description: "Generates images",
|
||||
metadata:
|
||||
'{"clawdis":{"requires":{"env":["GEMINI_API_KEY"]},"primaryEnv":"GEMINI_API_KEY"}}',
|
||||
'{"clawdbot":{"requires":{"env":["GEMINI_API_KEY"]},"primaryEnv":"GEMINI_API_KEY"}}',
|
||||
body: "# Nano Banana\n",
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("prefers workspace skills over managed skills", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const managedDir = path.join(workspaceDir, ".managed");
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const managedSkillDir = path.join(managedDir, "demo-skill");
|
||||
@@ -204,7 +204,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("gates by bins, config, and always", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillsDir = path.join(workspaceDir, "skills");
|
||||
const binDir = path.join(workspaceDir, "bin");
|
||||
const originalPath = process.env.PATH;
|
||||
@@ -213,32 +213,32 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
dir: path.join(skillsDir, "bin-skill"),
|
||||
name: "bin-skill",
|
||||
description: "Needs a bin",
|
||||
metadata: '{"clawdis":{"requires":{"bins":["fakebin"]}}}',
|
||||
metadata: '{"clawdbot":{"requires":{"bins":["fakebin"]}}}',
|
||||
});
|
||||
await writeSkill({
|
||||
dir: path.join(skillsDir, "anybin-skill"),
|
||||
name: "anybin-skill",
|
||||
description: "Needs any bin",
|
||||
metadata: '{"clawdis":{"requires":{"anyBins":["missingbin","fakebin"]}}}',
|
||||
metadata: '{"clawdbot":{"requires":{"anyBins":["missingbin","fakebin"]}}}',
|
||||
});
|
||||
await writeSkill({
|
||||
dir: path.join(skillsDir, "config-skill"),
|
||||
name: "config-skill",
|
||||
description: "Needs config",
|
||||
metadata: '{"clawdis":{"requires":{"config":["browser.enabled"]}}}',
|
||||
metadata: '{"clawdbot":{"requires":{"config":["browser.enabled"]}}}',
|
||||
});
|
||||
await writeSkill({
|
||||
dir: path.join(skillsDir, "always-skill"),
|
||||
name: "always-skill",
|
||||
description: "Always on",
|
||||
metadata: '{"clawdis":{"always":true,"requires":{"env":["MISSING"]}}}',
|
||||
metadata: '{"clawdbot":{"always":true,"requires":{"env":["MISSING"]}}}',
|
||||
});
|
||||
await writeSkill({
|
||||
dir: path.join(skillsDir, "env-skill"),
|
||||
name: "env-skill",
|
||||
description: "Needs env",
|
||||
metadata:
|
||||
'{"clawdis":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
'{"clawdbot":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -275,13 +275,13 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("uses skillKey for config lookups", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "alias-skill");
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
name: "alias-skill",
|
||||
description: "Uses skillKey",
|
||||
metadata: '{"clawdis":{"skillKey":"alias"}}',
|
||||
metadata: '{"clawdbot":{"skillKey":"alias"}}',
|
||||
});
|
||||
|
||||
const prompt = buildWorkspaceSkillsPrompt(workspaceDir, {
|
||||
@@ -292,7 +292,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
});
|
||||
|
||||
it("applies bundled allowlist without affecting workspace skills", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const bundledSkillDir = path.join(bundledDir, "peekaboo");
|
||||
const workspaceSkillDir = path.join(workspaceDir, "skills", "demo-skill");
|
||||
@@ -323,7 +323,7 @@ describe("buildWorkspaceSkillsPrompt", () => {
|
||||
|
||||
describe("loadWorkspaceSkillEntries", () => {
|
||||
it("handles an empty managed skills dir without throwing", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const managedDir = path.join(workspaceDir, ".managed");
|
||||
await fs.mkdir(managedDir, { recursive: true });
|
||||
|
||||
@@ -338,7 +338,7 @@ describe("loadWorkspaceSkillEntries", () => {
|
||||
|
||||
describe("buildWorkspaceSkillSnapshot", () => {
|
||||
it("returns an empty snapshot when skills dirs are missing", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
|
||||
const snapshot = buildWorkspaceSkillSnapshot(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
@@ -352,7 +352,7 @@ describe("buildWorkspaceSkillSnapshot", () => {
|
||||
|
||||
describe("buildWorkspaceSkillStatus", () => {
|
||||
it("reports missing requirements and install options", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "status-skill");
|
||||
|
||||
await writeSkill({
|
||||
@@ -360,7 +360,7 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
name: "status-skill",
|
||||
description: "Needs setup",
|
||||
metadata:
|
||||
'{"clawdis":{"requires":{"bins":["fakebin"],"env":["ENV_KEY"],"config":["browser.enabled"]},"install":[{"id":"brew","kind":"brew","formula":"fakebin","bins":["fakebin"],"label":"Install fakebin"}]}}',
|
||||
'{"clawdbot":{"requires":{"bins":["fakebin"],"env":["ENV_KEY"],"config":["browser.enabled"]},"install":[{"id":"brew","kind":"brew","formula":"fakebin","bins":["fakebin"],"label":"Install fakebin"}]}}',
|
||||
});
|
||||
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, {
|
||||
@@ -378,14 +378,14 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
});
|
||||
|
||||
it("respects OS-gated skills", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "os-skill");
|
||||
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
name: "os-skill",
|
||||
description: "Darwin only",
|
||||
metadata: '{"clawdis":{"os":["darwin"]}}',
|
||||
metadata: '{"clawdbot":{"os":["darwin"]}}',
|
||||
});
|
||||
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, {
|
||||
@@ -404,10 +404,10 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
});
|
||||
|
||||
it("marks bundled skills blocked by allowlist", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const bundledDir = path.join(workspaceDir, ".bundled");
|
||||
const bundledSkillDir = path.join(bundledDir, "peekaboo");
|
||||
const originalBundled = process.env.CLAWDIS_BUNDLED_SKILLS_DIR;
|
||||
const originalBundled = process.env.CLAWDBOT_BUNDLED_SKILLS_DIR;
|
||||
|
||||
await writeSkill({
|
||||
dir: bundledSkillDir,
|
||||
@@ -417,7 +417,7 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
});
|
||||
|
||||
try {
|
||||
process.env.CLAWDIS_BUNDLED_SKILLS_DIR = bundledDir;
|
||||
process.env.CLAWDBOT_BUNDLED_SKILLS_DIR = bundledDir;
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, {
|
||||
managedSkillsDir: path.join(workspaceDir, ".managed"),
|
||||
config: { skills: { allowBundled: ["other-skill"] } },
|
||||
@@ -429,9 +429,9 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
expect(skill?.eligible).toBe(false);
|
||||
} finally {
|
||||
if (originalBundled === undefined) {
|
||||
delete process.env.CLAWDIS_BUNDLED_SKILLS_DIR;
|
||||
delete process.env.CLAWDBOT_BUNDLED_SKILLS_DIR;
|
||||
} else {
|
||||
process.env.CLAWDIS_BUNDLED_SKILLS_DIR = originalBundled;
|
||||
process.env.CLAWDBOT_BUNDLED_SKILLS_DIR = originalBundled;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -439,14 +439,14 @@ describe("buildWorkspaceSkillStatus", () => {
|
||||
|
||||
describe("applySkillEnvOverrides", () => {
|
||||
it("sets and restores env vars", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "env-skill");
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
name: "env-skill",
|
||||
description: "Needs env",
|
||||
metadata:
|
||||
'{"clawdis":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
'{"clawdbot":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
});
|
||||
|
||||
const entries = loadWorkspaceSkillEntries(workspaceDir, {
|
||||
@@ -474,14 +474,14 @@ describe("applySkillEnvOverrides", () => {
|
||||
});
|
||||
|
||||
it("applies env overrides from snapshots", async () => {
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-"));
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-"));
|
||||
const skillDir = path.join(workspaceDir, "skills", "env-skill");
|
||||
await writeSkill({
|
||||
dir: skillDir,
|
||||
name: "env-skill",
|
||||
description: "Needs env",
|
||||
metadata:
|
||||
'{"clawdis":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
'{"clawdbot":{"requires":{"env":["ENV_KEY"]},"primaryEnv":"ENV_KEY"}}',
|
||||
});
|
||||
|
||||
const snapshot = buildWorkspaceSkillSnapshot(workspaceDir, {
|
||||
|
||||
+53
-53
@@ -8,7 +8,7 @@ import {
|
||||
type Skill,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
import type { ClawdisConfig, SkillConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig, SkillConfig } from "../config/config.js";
|
||||
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
||||
|
||||
export type SkillInstallSpec = {
|
||||
@@ -21,7 +21,7 @@ export type SkillInstallSpec = {
|
||||
module?: string;
|
||||
};
|
||||
|
||||
export type ClawdisSkillMetadata = {
|
||||
export type ClawdbotSkillMetadata = {
|
||||
always?: boolean;
|
||||
skillKey?: string;
|
||||
primaryEnv?: string;
|
||||
@@ -47,7 +47,7 @@ type ParsedSkillFrontmatter = Record<string, string>;
|
||||
export type SkillEntry = {
|
||||
skill: Skill;
|
||||
frontmatter: ParsedSkillFrontmatter;
|
||||
clawdis?: ClawdisSkillMetadata;
|
||||
clawdbot?: ClawdbotSkillMetadata;
|
||||
};
|
||||
|
||||
export type SkillSnapshot = {
|
||||
@@ -57,7 +57,7 @@ export type SkillSnapshot = {
|
||||
};
|
||||
|
||||
function resolveBundledSkillsDir(): string | undefined {
|
||||
const override = process.env.CLAWDIS_BUNDLED_SKILLS_DIR?.trim();
|
||||
const override = process.env.CLAWDBOT_BUNDLED_SKILLS_DIR?.trim();
|
||||
if (override) return override;
|
||||
|
||||
// bun --compile: ship a sibling `skills/` next to the executable.
|
||||
@@ -173,7 +173,7 @@ const DEFAULT_CONFIG_VALUES: Record<string, boolean> = {
|
||||
};
|
||||
|
||||
export function resolveSkillsInstallPreferences(
|
||||
config?: ClawdisConfig,
|
||||
config?: ClawdbotConfig,
|
||||
): SkillsInstallPreferences {
|
||||
const raw = config?.skills?.install;
|
||||
const preferBrew = raw?.preferBrew ?? true;
|
||||
@@ -195,7 +195,7 @@ export function resolveRuntimePlatform(): string {
|
||||
}
|
||||
|
||||
export function resolveConfigPath(
|
||||
config: ClawdisConfig | undefined,
|
||||
config: ClawdbotConfig | undefined,
|
||||
pathStr: string,
|
||||
) {
|
||||
const parts = pathStr.split(".").filter(Boolean);
|
||||
@@ -208,7 +208,7 @@ export function resolveConfigPath(
|
||||
}
|
||||
|
||||
export function isConfigPathTruthy(
|
||||
config: ClawdisConfig | undefined,
|
||||
config: ClawdbotConfig | undefined,
|
||||
pathStr: string,
|
||||
): boolean {
|
||||
const value = resolveConfigPath(config, pathStr);
|
||||
@@ -219,7 +219,7 @@ export function isConfigPathTruthy(
|
||||
}
|
||||
|
||||
export function resolveSkillConfig(
|
||||
config: ClawdisConfig | undefined,
|
||||
config: ClawdbotConfig | undefined,
|
||||
skillKey: string,
|
||||
): SkillConfig | undefined {
|
||||
const skills = config?.skills?.entries;
|
||||
@@ -237,7 +237,7 @@ function normalizeAllowlist(input: unknown): string[] | undefined {
|
||||
}
|
||||
|
||||
function isBundledSkill(entry: SkillEntry): boolean {
|
||||
return entry.skill.source === "clawdis-bundled";
|
||||
return entry.skill.source === "clawdbot-bundled";
|
||||
}
|
||||
|
||||
export function isBundledSkillAllowed(
|
||||
@@ -265,44 +265,44 @@ export function hasBinary(bin: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolveClawdisMetadata(
|
||||
function resolveClawdbotMetadata(
|
||||
frontmatter: ParsedSkillFrontmatter,
|
||||
): ClawdisSkillMetadata | undefined {
|
||||
): ClawdbotSkillMetadata | undefined {
|
||||
const raw = getFrontmatterValue(frontmatter, "metadata");
|
||||
if (!raw) return undefined;
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as { clawdis?: unknown };
|
||||
const parsed = JSON.parse(raw) as { clawdbot?: unknown };
|
||||
if (!parsed || typeof parsed !== "object") return undefined;
|
||||
const clawdis = (parsed as { clawdis?: unknown }).clawdis;
|
||||
if (!clawdis || typeof clawdis !== "object") return undefined;
|
||||
const clawdisObj = clawdis as Record<string, unknown>;
|
||||
const clawdbot = (parsed as { clawdbot?: unknown }).clawdbot;
|
||||
if (!clawdbot || typeof clawdbot !== "object") return undefined;
|
||||
const clawdbotObj = clawdbot as Record<string, unknown>;
|
||||
const requiresRaw =
|
||||
typeof clawdisObj.requires === "object" && clawdisObj.requires !== null
|
||||
? (clawdisObj.requires as Record<string, unknown>)
|
||||
typeof clawdbotObj.requires === "object" && clawdbotObj.requires !== null
|
||||
? (clawdbotObj.requires as Record<string, unknown>)
|
||||
: undefined;
|
||||
const installRaw = Array.isArray(clawdisObj.install)
|
||||
? (clawdisObj.install as unknown[])
|
||||
const installRaw = Array.isArray(clawdbotObj.install)
|
||||
? (clawdbotObj.install as unknown[])
|
||||
: [];
|
||||
const install = installRaw
|
||||
.map((entry) => parseInstallSpec(entry))
|
||||
.filter((entry): entry is SkillInstallSpec => Boolean(entry));
|
||||
const osRaw = normalizeStringList(clawdisObj.os);
|
||||
const osRaw = normalizeStringList(clawdbotObj.os);
|
||||
return {
|
||||
always:
|
||||
typeof clawdisObj.always === "boolean" ? clawdisObj.always : undefined,
|
||||
typeof clawdbotObj.always === "boolean" ? clawdbotObj.always : undefined,
|
||||
emoji:
|
||||
typeof clawdisObj.emoji === "string" ? clawdisObj.emoji : undefined,
|
||||
typeof clawdbotObj.emoji === "string" ? clawdbotObj.emoji : undefined,
|
||||
homepage:
|
||||
typeof clawdisObj.homepage === "string"
|
||||
? clawdisObj.homepage
|
||||
typeof clawdbotObj.homepage === "string"
|
||||
? clawdbotObj.homepage
|
||||
: undefined,
|
||||
skillKey:
|
||||
typeof clawdisObj.skillKey === "string"
|
||||
? clawdisObj.skillKey
|
||||
typeof clawdbotObj.skillKey === "string"
|
||||
? clawdbotObj.skillKey
|
||||
: undefined,
|
||||
primaryEnv:
|
||||
typeof clawdisObj.primaryEnv === "string"
|
||||
? clawdisObj.primaryEnv
|
||||
typeof clawdbotObj.primaryEnv === "string"
|
||||
? clawdbotObj.primaryEnv
|
||||
: undefined,
|
||||
os: osRaw.length > 0 ? osRaw : undefined,
|
||||
requires: requiresRaw
|
||||
@@ -321,53 +321,53 @@ function resolveClawdisMetadata(
|
||||
}
|
||||
|
||||
function resolveSkillKey(skill: Skill, entry?: SkillEntry): string {
|
||||
return entry?.clawdis?.skillKey ?? skill.name;
|
||||
return entry?.clawdbot?.skillKey ?? skill.name;
|
||||
}
|
||||
|
||||
function shouldIncludeSkill(params: {
|
||||
entry: SkillEntry;
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
}): boolean {
|
||||
const { entry, config } = params;
|
||||
const skillKey = resolveSkillKey(entry.skill, entry);
|
||||
const skillConfig = resolveSkillConfig(config, skillKey);
|
||||
const allowBundled = normalizeAllowlist(config?.skills?.allowBundled);
|
||||
const osList = entry.clawdis?.os ?? [];
|
||||
const osList = entry.clawdbot?.os ?? [];
|
||||
|
||||
if (skillConfig?.enabled === false) return false;
|
||||
if (!isBundledSkillAllowed(entry, allowBundled)) return false;
|
||||
if (osList.length > 0 && !osList.includes(resolveRuntimePlatform())) {
|
||||
return false;
|
||||
}
|
||||
if (entry.clawdis?.always === true) {
|
||||
if (entry.clawdbot?.always === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const requiredBins = entry.clawdis?.requires?.bins ?? [];
|
||||
const requiredBins = entry.clawdbot?.requires?.bins ?? [];
|
||||
if (requiredBins.length > 0) {
|
||||
for (const bin of requiredBins) {
|
||||
if (!hasBinary(bin)) return false;
|
||||
}
|
||||
}
|
||||
const requiredAnyBins = entry.clawdis?.requires?.anyBins ?? [];
|
||||
const requiredAnyBins = entry.clawdbot?.requires?.anyBins ?? [];
|
||||
if (requiredAnyBins.length > 0) {
|
||||
const anyFound = requiredAnyBins.some((bin) => hasBinary(bin));
|
||||
if (!anyFound) return false;
|
||||
}
|
||||
|
||||
const requiredEnv = entry.clawdis?.requires?.env ?? [];
|
||||
const requiredEnv = entry.clawdbot?.requires?.env ?? [];
|
||||
if (requiredEnv.length > 0) {
|
||||
for (const envName of requiredEnv) {
|
||||
if (process.env[envName]) continue;
|
||||
if (skillConfig?.env?.[envName]) continue;
|
||||
if (skillConfig?.apiKey && entry.clawdis?.primaryEnv === envName) {
|
||||
if (skillConfig?.apiKey && entry.clawdbot?.primaryEnv === envName) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const requiredConfig = entry.clawdis?.requires?.config ?? [];
|
||||
const requiredConfig = entry.clawdbot?.requires?.config ?? [];
|
||||
if (requiredConfig.length > 0) {
|
||||
for (const configPath of requiredConfig) {
|
||||
if (!isConfigPathTruthy(config, configPath)) return false;
|
||||
@@ -379,14 +379,14 @@ function shouldIncludeSkill(params: {
|
||||
|
||||
function filterSkillEntries(
|
||||
entries: SkillEntry[],
|
||||
config?: ClawdisConfig,
|
||||
config?: ClawdbotConfig,
|
||||
): SkillEntry[] {
|
||||
return entries.filter((entry) => shouldIncludeSkill({ entry, config }));
|
||||
}
|
||||
|
||||
export function applySkillEnvOverrides(params: {
|
||||
skills: SkillEntry[];
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
}) {
|
||||
const { skills, config } = params;
|
||||
const updates: Array<{ key: string; prev: string | undefined }> = [];
|
||||
@@ -404,7 +404,7 @@ export function applySkillEnvOverrides(params: {
|
||||
}
|
||||
}
|
||||
|
||||
const primaryEnv = entry.clawdis?.primaryEnv;
|
||||
const primaryEnv = entry.clawdbot?.primaryEnv;
|
||||
if (primaryEnv && skillConfig.apiKey && !process.env[primaryEnv]) {
|
||||
updates.push({ key: primaryEnv, prev: process.env[primaryEnv] });
|
||||
process.env[primaryEnv] = skillConfig.apiKey;
|
||||
@@ -421,7 +421,7 @@ export function applySkillEnvOverrides(params: {
|
||||
|
||||
export function applySkillEnvOverridesFromSnapshot(params: {
|
||||
snapshot?: SkillSnapshot;
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
}) {
|
||||
const { snapshot, config } = params;
|
||||
if (!snapshot) return () => {};
|
||||
@@ -463,7 +463,7 @@ export function applySkillEnvOverridesFromSnapshot(params: {
|
||||
function loadSkillEntries(
|
||||
workspaceDir: string,
|
||||
opts?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
managedSkillsDir?: string;
|
||||
bundledSkillsDir?: string;
|
||||
},
|
||||
@@ -494,23 +494,23 @@ function loadSkillEntries(
|
||||
const bundledSkills = bundledSkillsDir
|
||||
? loadSkills({
|
||||
dir: bundledSkillsDir,
|
||||
source: "clawdis-bundled",
|
||||
source: "clawdbot-bundled",
|
||||
})
|
||||
: [];
|
||||
const extraSkills = extraDirs.flatMap((dir) => {
|
||||
const resolved = resolveUserPath(dir);
|
||||
return loadSkills({
|
||||
dir: resolved,
|
||||
source: "clawdis-extra",
|
||||
source: "clawdbot-extra",
|
||||
});
|
||||
});
|
||||
const managedSkills = loadSkills({
|
||||
dir: managedSkillsDir,
|
||||
source: "clawdis-managed",
|
||||
source: "clawdbot-managed",
|
||||
});
|
||||
const workspaceSkills = loadSkills({
|
||||
dir: workspaceSkillsDir,
|
||||
source: "clawdis-workspace",
|
||||
source: "clawdbot-workspace",
|
||||
});
|
||||
|
||||
const merged = new Map<string, Skill>();
|
||||
@@ -532,7 +532,7 @@ function loadSkillEntries(
|
||||
return {
|
||||
skill,
|
||||
frontmatter,
|
||||
clawdis: resolveClawdisMetadata(frontmatter),
|
||||
clawdbot: resolveClawdbotMetadata(frontmatter),
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -542,7 +542,7 @@ function loadSkillEntries(
|
||||
export function buildWorkspaceSkillSnapshot(
|
||||
workspaceDir: string,
|
||||
opts?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
managedSkillsDir?: string;
|
||||
bundledSkillsDir?: string;
|
||||
entries?: SkillEntry[];
|
||||
@@ -555,7 +555,7 @@ export function buildWorkspaceSkillSnapshot(
|
||||
prompt: formatSkillsForPrompt(resolvedSkills),
|
||||
skills: eligible.map((entry) => ({
|
||||
name: entry.skill.name,
|
||||
primaryEnv: entry.clawdis?.primaryEnv,
|
||||
primaryEnv: entry.clawdbot?.primaryEnv,
|
||||
})),
|
||||
resolvedSkills,
|
||||
};
|
||||
@@ -564,7 +564,7 @@ export function buildWorkspaceSkillSnapshot(
|
||||
export function buildWorkspaceSkillsPrompt(
|
||||
workspaceDir: string,
|
||||
opts?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
managedSkillsDir?: string;
|
||||
bundledSkillsDir?: string;
|
||||
entries?: SkillEntry[];
|
||||
@@ -578,7 +578,7 @@ export function buildWorkspaceSkillsPrompt(
|
||||
export function loadWorkspaceSkillEntries(
|
||||
workspaceDir: string,
|
||||
opts?: {
|
||||
config?: ClawdisConfig;
|
||||
config?: ClawdbotConfig;
|
||||
managedSkillsDir?: string;
|
||||
bundledSkillsDir?: string;
|
||||
},
|
||||
@@ -588,12 +588,12 @@ export function loadWorkspaceSkillEntries(
|
||||
|
||||
export function filterWorkspaceSkillEntries(
|
||||
entries: SkillEntry[],
|
||||
config?: ClawdisConfig,
|
||||
config?: ClawdbotConfig,
|
||||
): SkillEntry[] {
|
||||
return filterSkillEntries(entries, config);
|
||||
}
|
||||
export function resolveBundledAllowlist(
|
||||
config?: ClawdisConfig,
|
||||
config?: ClawdbotConfig,
|
||||
): string[] | undefined {
|
||||
return normalizeAllowlist(config?.skills?.allowBundled);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function buildAgentSystemPromptAppend(params: {
|
||||
if (runtimeInfo?.model) runtimeLines.push(`Model: ${runtimeInfo.model}`);
|
||||
|
||||
const lines = [
|
||||
"You are Clawd, a personal assistant running inside Clawdis.",
|
||||
"You are Clawd, a personal assistant running inside Clawdbot.",
|
||||
"",
|
||||
"## Tooling",
|
||||
"Pi lists the standard tools above. This runtime enables:",
|
||||
@@ -101,11 +101,11 @@ export function buildAgentSystemPromptAppend(params: {
|
||||
ownerLine ?? "",
|
||||
ownerLine ? "" : "",
|
||||
"## Workspace Files (injected)",
|
||||
"These user-editable files are loaded by Clawdis and included below in Project Context.",
|
||||
"These user-editable files are loaded by Clawdbot and included below in Project Context.",
|
||||
"",
|
||||
"## Messaging Safety",
|
||||
"Never send streaming/partial replies to external messaging surfaces; only final replies should be delivered there.",
|
||||
"Clawdis handles message transport automatically; respond normally and your reply will be delivered to the current chat.",
|
||||
"Clawdbot handles message transport automatically; respond normally and your reply will be delivered to the current chat.",
|
||||
"",
|
||||
"## Reply Tags",
|
||||
"To request a native reply/quote on supported surfaces, include one tag in your reply:",
|
||||
@@ -126,7 +126,7 @@ export function buildAgentSystemPromptAppend(params: {
|
||||
"## Heartbeats",
|
||||
'If you receive a heartbeat poll (a user message containing just "HEARTBEAT"), and there is nothing that needs attention, reply exactly:',
|
||||
"HEARTBEAT_OK",
|
||||
'Clawdis treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack (and may discard it).',
|
||||
'Clawdbot treats a leading/trailing "HEARTBEAT_OK" as a heartbeat ack (and may discard it).',
|
||||
'If something needs attention, do NOT include "HEARTBEAT_OK"; reply with the alert text instead.',
|
||||
"",
|
||||
"## Runtime",
|
||||
|
||||
@@ -6,7 +6,7 @@ type ToolContentBlock = AgentToolResult<unknown>["content"][number];
|
||||
type ImageContentBlock = Extract<ToolContentBlock, { type: "image" }>;
|
||||
type TextContentBlock = Extract<ToolContentBlock, { type: "text" }>;
|
||||
|
||||
// Anthropic Messages API limitations (observed in Clawdis sessions):
|
||||
// Anthropic Messages API limitations (observed in Clawdbot sessions):
|
||||
// - Images over ~2000px per side can fail in multi-image requests.
|
||||
// - Images over 5MB are rejected by the API.
|
||||
//
|
||||
|
||||
@@ -196,7 +196,7 @@ function resolveBrowserBaseUrl(controlUrl?: string) {
|
||||
const resolved = resolveBrowserConfig(cfg.browser);
|
||||
if (!resolved.enabled && !controlUrl?.trim()) {
|
||||
throw new Error(
|
||||
"Browser control is disabled. Set browser.enabled=true in ~/.clawdis/clawdis.json.",
|
||||
"Browser control is disabled. Set browser.enabled=true in ~/.clawdbot/clawdbot.json.",
|
||||
);
|
||||
}
|
||||
const url = controlUrl?.trim() ? controlUrl.trim() : resolved.controlUrl;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import type {
|
||||
ClawdisConfig,
|
||||
ClawdbotConfig,
|
||||
DiscordActionConfig,
|
||||
} from "../../config/config.js";
|
||||
import { readStringParam } from "./common.js";
|
||||
@@ -51,7 +51,7 @@ type ActionGate = (
|
||||
|
||||
export async function handleDiscordAction(
|
||||
params: Record<string, unknown>,
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
): Promise<AgentToolResult<unknown>> {
|
||||
const action = readStringParam(params, "action", { required: true });
|
||||
const isActionEnabled: ActionGate = (key, defaultValue = true) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
|
||||
export type SessionKind = "main" | "group" | "cron" | "hook" | "node" | "other";
|
||||
|
||||
@@ -7,7 +7,7 @@ function normalizeKey(value?: string) {
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function resolveMainSessionAlias(cfg: ClawdisConfig) {
|
||||
export function resolveMainSessionAlias(cfg: ClawdbotConfig) {
|
||||
const mainKey = normalizeKey(cfg.session?.mainKey) ?? "main";
|
||||
const scope = cfg.session?.scope ?? "per-sender";
|
||||
const alias = scope === "global" ? "global" : mainKey;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
|
||||
const ANNOUNCE_SKIP_TOKEN = "ANNOUNCE_SKIP";
|
||||
const REPLY_SKIP_TOKEN = "REPLY_SKIP";
|
||||
@@ -123,7 +123,7 @@ export function isReplySkip(text?: string) {
|
||||
return (text ?? "").trim() === REPLY_SKIP_TOKEN;
|
||||
}
|
||||
|
||||
export function resolvePingPongTurns(cfg?: ClawdisConfig) {
|
||||
export function resolvePingPongTurns(cfg?: ClawdbotConfig) {
|
||||
const raw = cfg?.session?.agentToAgent?.maxPingPongTurns;
|
||||
const fallback = DEFAULT_PING_PONG_TURNS;
|
||||
if (typeof raw !== "number" || !Number.isFinite(raw)) return fallback;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
|
||||
import type { ClawdisConfig, SlackActionConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig, SlackActionConfig } from "../../config/config.js";
|
||||
import {
|
||||
deleteSlackMessage,
|
||||
editSlackMessage,
|
||||
@@ -33,7 +33,7 @@ type ActionGate = (
|
||||
|
||||
export async function handleSlackAction(
|
||||
params: Record<string, unknown>,
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
): Promise<AgentToolResult<unknown>> {
|
||||
const action = readStringParam(params, "action", { required: true });
|
||||
const isActionEnabled: ActionGate = (key, defaultValue = true) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ensureAgentWorkspace } from "./workspace.js";
|
||||
|
||||
describe("ensureAgentWorkspace", () => {
|
||||
it("creates directory and bootstrap files when missing", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-ws-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-ws-"));
|
||||
const nested = path.join(dir, "nested");
|
||||
const result = await ensureAgentWorkspace({
|
||||
dir: nested,
|
||||
@@ -30,7 +30,7 @@ describe("ensureAgentWorkspace", () => {
|
||||
});
|
||||
|
||||
it("does not overwrite existing AGENTS.md", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-ws-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-ws-"));
|
||||
const agentsPath = path.join(dir, "AGENTS.md");
|
||||
await fs.writeFile(agentsPath, "custom", "utf-8");
|
||||
await ensureAgentWorkspace({ dir, ensureBootstrapFiles: true });
|
||||
|
||||
@@ -13,7 +13,7 @@ export const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
|
||||
export const DEFAULT_USER_FILENAME = "USER.md";
|
||||
export const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
|
||||
|
||||
const DEFAULT_AGENTS_TEMPLATE = `# AGENTS.md - Clawdis Workspace
|
||||
const DEFAULT_AGENTS_TEMPLATE = `# AGENTS.md - Clawdbot Workspace
|
||||
|
||||
This folder is the assistant's working directory.
|
||||
|
||||
@@ -58,7 +58,7 @@ Describe who the assistant is, tone, and boundaries.
|
||||
const DEFAULT_TOOLS_TEMPLATE = `# TOOLS.md - User Tool Notes (editable)
|
||||
|
||||
This file is for *your* notes about external tools and conventions.
|
||||
It does not define which tools exist; Clawdis provides built-in tools internally.
|
||||
It does not define which tools exist; Clawdbot provides built-in tools internally.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -108,7 +108,7 @@ After the user chooses, update:
|
||||
- Timezone (optional)
|
||||
- Notes
|
||||
|
||||
3) ~/.clawdis/clawdis.json
|
||||
3) ~/.clawdbot/clawdbot.json
|
||||
Set identity.name, identity.theme, identity.emoji to match IDENTITY.md.
|
||||
|
||||
## Cleanup
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// unintentionally breaking on newlines. Using [\s\S] keeps newlines inside
|
||||
// the chunk so messages are only split when they truly exceed the limit.
|
||||
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
|
||||
export type TextChunkSurface =
|
||||
| "whatsapp"
|
||||
@@ -24,7 +24,7 @@ const DEFAULT_CHUNK_LIMIT_BY_SURFACE: Record<TextChunkSurface, number> = {
|
||||
};
|
||||
|
||||
export function resolveTextChunkLimit(
|
||||
cfg: ClawdisConfig | undefined,
|
||||
cfg: ClawdbotConfig | undefined,
|
||||
surface?: TextChunkSurface,
|
||||
): number {
|
||||
const surfaceOverride = (() => {
|
||||
|
||||
@@ -22,7 +22,7 @@ vi.mock("../agents/model-catalog.js", () => ({
|
||||
}));
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-stream-"));
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-stream-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
|
||||
@@ -35,7 +35,7 @@ vi.mock("../agents/model-catalog.js", () => ({
|
||||
}));
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-reply-"));
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-reply-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
|
||||
@@ -32,7 +32,7 @@ function makeResult(text: string) {
|
||||
}
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-queue-"));
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-queue-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
|
||||
@@ -26,7 +26,7 @@ const webMocks = vi.hoisted(() => ({
|
||||
vi.mock("../web/session.js", () => webMocks);
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = await fs.mkdtemp(join(tmpdir(), "clawdis-triggers-"));
|
||||
const base = await fs.mkdtemp(join(tmpdir(), "clawdbot-triggers-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
@@ -580,7 +580,7 @@ describe("trigger handling", () => {
|
||||
allowFrom: ["*"],
|
||||
},
|
||||
session: {
|
||||
store: join(tmpdir(), `clawdis-session-test-${Date.now()}.json`),
|
||||
store: join(tmpdir(), `clawdbot-session-test-${Date.now()}.json`),
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -619,7 +619,7 @@ describe("trigger handling", () => {
|
||||
allowFrom: ["*"],
|
||||
},
|
||||
session: {
|
||||
store: join(tmpdir(), `clawdis-session-test-${Date.now()}.json`),
|
||||
store: join(tmpdir(), `clawdbot-session-test-${Date.now()}.json`),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "../agents/workspace.js";
|
||||
import {
|
||||
type AgentElevatedAllowFromConfig,
|
||||
type ClawdisConfig,
|
||||
type ClawdbotConfig,
|
||||
loadConfig,
|
||||
} from "../config/config.js";
|
||||
import { resolveSessionTranscriptPath } from "../config/sessions.js";
|
||||
@@ -180,7 +180,7 @@ function isApprovedElevatedSender(params: {
|
||||
export async function getReplyFromConfig(
|
||||
ctx: MsgContext,
|
||||
opts?: GetReplyOptions,
|
||||
configOverride?: ClawdisConfig,
|
||||
configOverride?: ClawdbotConfig,
|
||||
): Promise<ReplyPayload | ReplyPayload[] | undefined> {
|
||||
const cfg = configOverride ?? loadConfig();
|
||||
const agentCfg = cfg.agent;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { resolveTextChunkLimit, type TextChunkSurface } from "../chunk.js";
|
||||
|
||||
const DEFAULT_BLOCK_STREAM_MIN = 800;
|
||||
@@ -23,7 +23,7 @@ function normalizeChunkSurface(surface?: string): TextChunkSurface | undefined {
|
||||
}
|
||||
|
||||
export function resolveBlockStreamingChunking(
|
||||
cfg: ClawdisConfig | undefined,
|
||||
cfg: ClawdbotConfig | undefined,
|
||||
surface?: string,
|
||||
): {
|
||||
minChars: number;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import {
|
||||
type SessionEntry,
|
||||
type SessionScope,
|
||||
saveSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { triggerClawdisRestart } from "../../infra/restart.js";
|
||||
import { triggerClawdbotRestart } from "../../infra/restart.js";
|
||||
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
||||
import { normalizeE164 } from "../../utils.js";
|
||||
import { resolveHeartbeatSeconds } from "../../web/reconnect.js";
|
||||
@@ -37,7 +37,7 @@ export type CommandContext = {
|
||||
|
||||
export function buildCommandContext(params: {
|
||||
ctx: MsgContext;
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
sessionKey?: string;
|
||||
isGroup: boolean;
|
||||
triggerBodyNormalized: string;
|
||||
@@ -99,7 +99,7 @@ export function buildCommandContext(params: {
|
||||
|
||||
export async function handleCommands(params: {
|
||||
ctx: MsgContext;
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
command: CommandContext;
|
||||
sessionEntry?: SessionEntry;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
@@ -226,11 +226,11 @@ export async function handleCommands(params: {
|
||||
);
|
||||
return { shouldContinue: false };
|
||||
}
|
||||
const restartMethod = triggerClawdisRestart();
|
||||
const restartMethod = triggerClawdbotRestart();
|
||||
return {
|
||||
shouldContinue: false,
|
||||
reply: {
|
||||
text: `⚙️ Restarting clawdis via ${restartMethod}; give me a few seconds to come back online.`,
|
||||
text: `⚙️ Restarting clawdbot via ${restartMethod}; give me a few seconds to come back online.`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
resolveConfiguredModelRef,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
|
||||
import { enqueueSystemEvent } from "../../infra/system-events.js";
|
||||
import { extractModelDirective } from "../model.js";
|
||||
@@ -134,7 +134,7 @@ export function isDirectiveOnly(params: {
|
||||
directives: InlineDirectives;
|
||||
cleanedBody: string;
|
||||
ctx: MsgContext;
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
isGroup: boolean;
|
||||
}): boolean {
|
||||
const { directives, cleanedBody, ctx, cfg, isGroup } = params;
|
||||
@@ -436,7 +436,7 @@ export async function persistInlineDirectives(params: {
|
||||
model: string;
|
||||
initialModelLabel: string;
|
||||
formatModelSwitchEvent: (label: string, alias?: string) => string;
|
||||
agentCfg: ClawdisConfig["agent"] | undefined;
|
||||
agentCfg: ClawdbotConfig["agent"] | undefined;
|
||||
}): Promise<{ provider: string; model: string; contextTokens: number }> {
|
||||
const {
|
||||
directives,
|
||||
@@ -551,7 +551,7 @@ export async function persistInlineDirectives(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveDefaultModel(params: { cfg: ClawdisConfig }): {
|
||||
export function resolveDefaultModel(params: { cfg: ClawdbotConfig }): {
|
||||
defaultProvider: string;
|
||||
defaultModel: string;
|
||||
aliasIndex: ModelAliasIndex;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type {
|
||||
GroupKeyResolution,
|
||||
SessionEntry,
|
||||
@@ -7,7 +7,7 @@ import { normalizeGroupActivation } from "../group-activation.js";
|
||||
import type { TemplateContext } from "../templating.js";
|
||||
|
||||
export function resolveGroupRequireMention(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
ctx: TemplateContext;
|
||||
groupResolution?: GroupKeyResolution;
|
||||
}): boolean {
|
||||
@@ -86,7 +86,7 @@ export function buildGroupIntro(params: {
|
||||
: "Activation: trigger-only (you are invoked only when explicitly mentioned; recent context may be included).";
|
||||
const silenceLine =
|
||||
activation === "always"
|
||||
? `If no response is needed, reply with exactly "${params.silentToken}" (no other text) so Clawdis stays silent.`
|
||||
? `If no response is needed, reply with exactly "${params.silentToken}" (no other text) so Clawdbot stays silent.`
|
||||
: undefined;
|
||||
const cautionLine =
|
||||
activation === "always"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { MsgContext } from "../templating.js";
|
||||
|
||||
export function stripStructuralPrefixes(text: string): string {
|
||||
@@ -18,7 +18,7 @@ export function stripStructuralPrefixes(text: string): string {
|
||||
export function stripMentions(
|
||||
text: string,
|
||||
ctx: MsgContext,
|
||||
cfg: ClawdisConfig | undefined,
|
||||
cfg: ClawdbotConfig | undefined,
|
||||
): string {
|
||||
let result = text;
|
||||
const patterns = cfg?.routing?.groupChat?.mentionPatterns ?? [];
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
resolveModelRefFromString,
|
||||
resolveThinkingDefault,
|
||||
} from "../../agents/model-selection.js";
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
|
||||
import type { ThinkLevel } from "./directives.js";
|
||||
|
||||
@@ -32,8 +32,8 @@ type ModelSelectionState = {
|
||||
};
|
||||
|
||||
export async function createModelSelectionState(params: {
|
||||
cfg: ClawdisConfig;
|
||||
agentCfg: ClawdisConfig["agent"] | undefined;
|
||||
cfg: ClawdbotConfig;
|
||||
agentCfg: ClawdbotConfig["agent"] | undefined;
|
||||
sessionEntry?: SessionEntry;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
sessionKey?: string;
|
||||
@@ -177,7 +177,7 @@ export function resolveModelDirectiveSelection(params: {
|
||||
}
|
||||
|
||||
export function resolveContextTokens(params: {
|
||||
agentCfg: ClawdisConfig["agent"] | undefined;
|
||||
agentCfg: ClawdbotConfig["agent"] | undefined;
|
||||
model: string;
|
||||
}): number {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { SkillSnapshot } from "../../agents/skills.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import type { ElevatedLevel, ThinkLevel, VerboseLevel } from "./directives.js";
|
||||
@@ -28,7 +28,7 @@ export type FollowupRun = {
|
||||
surface?: string;
|
||||
sessionFile: string;
|
||||
workspaceDir: string;
|
||||
config: ClawdisConfig;
|
||||
config: ClawdbotConfig;
|
||||
skillsSnapshot?: SkillSnapshot;
|
||||
provider: string;
|
||||
model: string;
|
||||
@@ -435,7 +435,7 @@ function defaultQueueModeForSurface(surface?: string): QueueMode {
|
||||
return "collect";
|
||||
}
|
||||
export function resolveQueueSettings(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
surface?: string;
|
||||
sessionEntry?: SessionEntry;
|
||||
inlineMode?: QueueMode;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js";
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
|
||||
import { buildProviderSummary } from "../../infra/provider-summary.js";
|
||||
import { drainSystemEvents } from "../../infra/system-events.js";
|
||||
|
||||
export async function prependSystemEvents(params: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
isMainSession: boolean;
|
||||
isNewSession: boolean;
|
||||
prefixedBodyBase: string;
|
||||
@@ -49,7 +49,7 @@ export async function ensureSkillSnapshot(params: {
|
||||
sessionId?: string;
|
||||
isFirstTurnInSession: boolean;
|
||||
workspaceDir: string;
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
}): Promise<{
|
||||
sessionEntry?: SessionEntry;
|
||||
skillsSnapshot?: SessionEntry["skillsSnapshot"];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import type { ClawdisConfig } from "../../config/config.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import {
|
||||
buildGroupDisplayName,
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
@@ -36,7 +36,7 @@ export type SessionInitResult = {
|
||||
|
||||
export async function initSessionState(params: {
|
||||
ctx: MsgContext;
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
}): Promise<SessionInitResult> {
|
||||
const { ctx, cfg } = params;
|
||||
const sessionCfg = cfg.session;
|
||||
|
||||
@@ -74,7 +74,7 @@ describe("buildStatusMessage", () => {
|
||||
});
|
||||
|
||||
it("prefers cached prompt tokens from the session log", async () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdis-status-"));
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-status-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = dir;
|
||||
try {
|
||||
@@ -83,11 +83,11 @@ describe("buildStatusMessage", () => {
|
||||
"./status.js"
|
||||
);
|
||||
|
||||
const storePath = path.join(dir, ".clawdis", "sessions", "sessions.json");
|
||||
const storePath = path.join(dir, ".clawdbot", "sessions", "sessions.json");
|
||||
const sessionId = "sess-1";
|
||||
const logPath = path.join(
|
||||
dir,
|
||||
".clawdis",
|
||||
".clawdbot",
|
||||
"sessions",
|
||||
`${sessionId}.jsonl`,
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
normalizeUsage,
|
||||
type UsageLike,
|
||||
} from "../agents/usage.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
resolveSessionTranscriptPath,
|
||||
type SessionEntry,
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { shortenHomePath } from "../utils.js";
|
||||
import type { ThinkLevel, VerboseLevel } from "./thinking.js";
|
||||
|
||||
type AgentConfig = NonNullable<ClawdisConfig["agent"]>;
|
||||
type AgentConfig = NonNullable<ClawdbotConfig["agent"]>;
|
||||
|
||||
type StatusArgs = {
|
||||
agent: AgentConfig;
|
||||
@@ -79,7 +79,7 @@ const readUsageFromSessionLog = (
|
||||
model?: string;
|
||||
}
|
||||
| undefined => {
|
||||
// Transcripts always live at: ~/.clawdis/sessions/<SessionId>.jsonl
|
||||
// Transcripts always live at: ~/.clawdbot/sessions/<SessionId>.jsonl
|
||||
if (!sessionId) return undefined;
|
||||
const logPath = resolveSessionTranscriptPath(sessionId);
|
||||
if (!fs.existsSync(logPath)) return undefined;
|
||||
@@ -166,7 +166,7 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
|
||||
const webLine = (() => {
|
||||
if (args.webLinked === false) {
|
||||
return "Web: not linked — run `clawdis login` to scan the QR.";
|
||||
return "Web: not linked — run `clawdbot login` to scan the QR.";
|
||||
}
|
||||
const authAge = formatAge(args.webAuthAgeMs);
|
||||
const heartbeat =
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("transcribeInboundAudio", () => {
|
||||
|
||||
it("downloads mediaUrl to temp file and returns transcript", async () => {
|
||||
const tmpBuf = Buffer.from("audio-bytes");
|
||||
const tmpFile = path.join(os.tmpdir(), `clawdis-audio-${Date.now()}.ogg`);
|
||||
const tmpFile = path.join(os.tmpdir(), `clawdbot-audio-${Date.now()}.ogg`);
|
||||
await fs.writeFile(tmpFile, tmpBuf);
|
||||
|
||||
const fetchMock = vi.fn(async () => ({
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
||||
import { runExec } from "../process/exec.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
@@ -14,7 +14,7 @@ export function isAudio(mediaType?: string | null) {
|
||||
}
|
||||
|
||||
export async function transcribeInboundAudio(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
ctx: MsgContext,
|
||||
runtime: RuntimeEnv,
|
||||
): Promise<{ text: string } | undefined> {
|
||||
@@ -32,7 +32,7 @@ export async function transcribeInboundAudio(
|
||||
const buffer = Buffer.from(arrayBuf);
|
||||
tmpPath = path.join(
|
||||
os.tmpdir(),
|
||||
`clawdis-audio-${crypto.randomUUID()}.ogg`,
|
||||
`clawdbot-audio-${crypto.randomUUID()}.ogg`,
|
||||
);
|
||||
await fs.writeFile(tmpPath, buffer);
|
||||
mediaPath = tmpPath;
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("browser chrome profile decoration", () => {
|
||||
|
||||
it("writes expected name + signed ARGB seed to Chrome prefs", async () => {
|
||||
const userDataDir = await fsp.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdis-chrome-test-"),
|
||||
path.join(os.tmpdir(), "clawdbot-chrome-test-"),
|
||||
);
|
||||
try {
|
||||
decorateClawdProfile(userDataDir, { color: DEFAULT_CLAWD_BROWSER_COLOR });
|
||||
@@ -71,7 +71,7 @@ describe("browser chrome profile decoration", () => {
|
||||
|
||||
it("best-effort writes name when color is invalid", async () => {
|
||||
const userDataDir = await fsp.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdis-chrome-test-"),
|
||||
path.join(os.tmpdir(), "clawdbot-chrome-test-"),
|
||||
);
|
||||
try {
|
||||
decorateClawdProfile(userDataDir, { color: "lobster-orange" });
|
||||
@@ -89,7 +89,7 @@ describe("browser chrome profile decoration", () => {
|
||||
|
||||
it("recovers from missing/invalid preference files", async () => {
|
||||
const userDataDir = await fsp.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdis-chrome-test-"),
|
||||
path.join(os.tmpdir(), "clawdbot-chrome-test-"),
|
||||
);
|
||||
try {
|
||||
await fsp.mkdir(path.join(userDataDir, "Default"), { recursive: true });
|
||||
@@ -116,7 +116,7 @@ describe("browser chrome profile decoration", () => {
|
||||
|
||||
it("is idempotent when rerun on an existing profile", async () => {
|
||||
const userDataDir = await fsp.mkdtemp(
|
||||
path.join(os.tmpdir(), "clawdis-chrome-test-"),
|
||||
path.join(os.tmpdir(), "clawdbot-chrome-test-"),
|
||||
);
|
||||
try {
|
||||
decorateClawdProfile(userDataDir, { color: DEFAULT_CLAWD_BROWSER_COLOR });
|
||||
|
||||
@@ -15,7 +15,7 @@ function enhanceBrowserFetchError(
|
||||
const code = extractErrorCode(cause) ?? extractErrorCode(err) ?? "";
|
||||
|
||||
const hint =
|
||||
"Start (or restart) the Clawdis gateway (Clawdis.app menubar, or `clawdis gateway`) and try again.";
|
||||
"Start (or restart) the Clawdbot gateway (Clawdbot.app menubar, or `clawdbot gateway`) and try again.";
|
||||
|
||||
if (code === "ECONNREFUSED") {
|
||||
return new Error(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import type { BrowserProfileConfig, ClawdisConfig } from "../config/config.js";
|
||||
import type { BrowserProfileConfig, ClawdbotConfig } from "../config/config.js";
|
||||
import { loadConfig, writeConfigFile } from "../config/config.js";
|
||||
import { resolveClawdUserDataDir } from "./chrome.js";
|
||||
import { parseHttpUrl, resolveProfile } from "./config.js";
|
||||
@@ -86,7 +86,7 @@ export function createBrowserProfilesService(ctx: BrowserRouteContext) {
|
||||
profileConfig = { cdpPort, color: profileColor };
|
||||
}
|
||||
|
||||
const nextConfig: ClawdisConfig = {
|
||||
const nextConfig: ClawdbotConfig = {
|
||||
...cfg,
|
||||
browser: {
|
||||
...cfg.browser,
|
||||
@@ -157,7 +157,7 @@ export function createBrowserProfilesService(ctx: BrowserRouteContext) {
|
||||
}
|
||||
|
||||
const { [name]: _removed, ...remainingProfiles } = profiles;
|
||||
const nextConfig: ClawdisConfig = {
|
||||
const nextConfig: ClawdbotConfig = {
|
||||
...cfg,
|
||||
browser: {
|
||||
...cfg.browser,
|
||||
|
||||
@@ -277,7 +277,7 @@ function createProfileContext(
|
||||
// HTTP responds but WebSocket fails - port in use by something else
|
||||
if (!profileState.running) {
|
||||
throw new Error(
|
||||
`Port ${profile.cdpPort} is in use for profile "${profile.name}" but not by clawdis. ` +
|
||||
`Port ${profile.cdpPort} is in use for profile "${profile.name}" but not by clawdbot. ` +
|
||||
`Run action=reset-profile profile=${profile.name} to kill the process.`,
|
||||
);
|
||||
}
|
||||
|
||||
+11
-11
@@ -5,9 +5,9 @@ import { fileURLToPath } from "node:url";
|
||||
|
||||
import { detectMime } from "../media/mime.js";
|
||||
|
||||
export const A2UI_PATH = "/__clawdis__/a2ui";
|
||||
export const CANVAS_HOST_PATH = "/__clawdis__/canvas";
|
||||
export const CANVAS_WS_PATH = "/__clawdis/ws";
|
||||
export const A2UI_PATH = "/__clawdbot__/a2ui";
|
||||
export const CANVAS_HOST_PATH = "/__clawdbot__/canvas";
|
||||
export const CANVAS_WS_PATH = "/__clawdbot/ws";
|
||||
|
||||
let cachedA2uiRootReal: string | null | undefined;
|
||||
let resolvingA2uiRoot: Promise<string | null> | null = null;
|
||||
@@ -98,9 +98,9 @@ export function injectCanvasLiveReload(html: string): string {
|
||||
(() => {
|
||||
// Cross-platform action bridge helper.
|
||||
// Works on:
|
||||
// - iOS: window.webkit.messageHandlers.clawdisCanvasA2UIAction.postMessage(...)
|
||||
// - Android: window.clawdisCanvasA2UIAction.postMessage(...)
|
||||
const actionHandlerName = "clawdisCanvasA2UIAction";
|
||||
// - iOS: window.webkit.messageHandlers.clawdbotCanvasA2UIAction.postMessage(...)
|
||||
// - Android: window.clawdbotCanvasA2UIAction.postMessage(...)
|
||||
const actionHandlerName = "clawdbotCanvasA2UIAction";
|
||||
function postToNode(payload) {
|
||||
try {
|
||||
const raw = typeof payload === "string" ? payload : JSON.stringify(payload);
|
||||
@@ -125,11 +125,11 @@ export function injectCanvasLiveReload(html: string): string {
|
||||
const action = { ...userAction, id };
|
||||
return postToNode({ userAction: action });
|
||||
}
|
||||
globalThis.Clawdis = globalThis.Clawdis ?? {};
|
||||
globalThis.Clawdis.postMessage = postToNode;
|
||||
globalThis.Clawdis.sendUserAction = sendUserAction;
|
||||
globalThis.clawdisPostMessage = postToNode;
|
||||
globalThis.clawdisSendUserAction = sendUserAction;
|
||||
globalThis.Clawdbot = globalThis.Clawdbot ?? {};
|
||||
globalThis.Clawdbot.postMessage = postToNode;
|
||||
globalThis.Clawdbot.sendUserAction = sendUserAction;
|
||||
globalThis.clawdbotPostMessage = postToNode;
|
||||
globalThis.clawdbotSendUserAction = sendUserAction;
|
||||
|
||||
try {
|
||||
const proto = location.protocol === "https:" ? "wss" : "ws";
|
||||
|
||||
@@ -17294,7 +17294,7 @@ const cardShadow = isAndroid ? "0 2px 10px rgba(0,0,0,.18)" : "0 10px 30px rgba(
|
||||
const buttonShadow = isAndroid ? "0 2px 10px rgba(6, 182, 212, 0.14)" : "0 10px 25px rgba(6, 182, 212, 0.18)";
|
||||
const statusShadow = isAndroid ? "0 2px 10px rgba(0, 0, 0, 0.18)" : "0 10px 24px rgba(0, 0, 0, 0.25)";
|
||||
const statusBlur = isAndroid ? "10px" : "14px";
|
||||
const clawdisTheme = {
|
||||
const clawdbotTheme = {
|
||||
components: {
|
||||
AudioPlayer: emptyClasses(),
|
||||
Button: emptyClasses(),
|
||||
@@ -17450,7 +17450,7 @@ const clawdisTheme = {
|
||||
Image: { borderRadius: "12px" }
|
||||
}
|
||||
};
|
||||
var ClawdisA2UIHost = class extends i$6 {
|
||||
var ClawdbotA2UIHost = class extends i$6 {
|
||||
static properties = {
|
||||
surfaces: { state: true },
|
||||
pendingAction: { state: true },
|
||||
@@ -17459,7 +17459,7 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
#processor = Data.createSignalA2uiMessageProcessor();
|
||||
#themeProvider = new i$3(this, {
|
||||
context: themeContext,
|
||||
initialValue: clawdisTheme
|
||||
initialValue: clawdbotTheme
|
||||
});
|
||||
surfaces = [];
|
||||
pendingAction = null;
|
||||
@@ -17472,10 +17472,10 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding:
|
||||
var(--clawdis-a2ui-inset-top, 0px)
|
||||
var(--clawdis-a2ui-inset-right, 0px)
|
||||
var(--clawdis-a2ui-inset-bottom, 0px)
|
||||
var(--clawdis-a2ui-inset-left, 0px);
|
||||
var(--clawdbot-a2ui-inset-top, 0px)
|
||||
var(--clawdbot-a2ui-inset-right, 0px)
|
||||
var(--clawdbot-a2ui-inset-bottom, 0px)
|
||||
var(--clawdbot-a2ui-inset-left, 0px);
|
||||
}
|
||||
|
||||
#surfaces {
|
||||
@@ -17484,14 +17484,14 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding-bottom: var(--clawdis-a2ui-scroll-pad-bottom, 0px);
|
||||
padding-bottom: var(--clawdbot-a2ui-scroll-pad-bottom, 0px);
|
||||
}
|
||||
|
||||
.status {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: var(--clawdis-a2ui-status-top, 12px);
|
||||
top: var(--clawdbot-a2ui-status-top, 12px);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -17512,7 +17512,7 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
bottom: var(--clawdis-a2ui-toast-bottom, 12px);
|
||||
bottom: var(--clawdbot-a2ui-toast-bottom, 12px);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -17538,7 +17538,7 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: var(--clawdis-a2ui-empty-top, var(--clawdis-a2ui-status-top, 12px));
|
||||
top: var(--clawdbot-a2ui-empty-top, var(--clawdbot-a2ui-status-top, 12px));
|
||||
text-align: center;
|
||||
opacity: 0.8;
|
||||
padding: 10px 12px;
|
||||
@@ -17570,20 +17570,20 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
`;
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
globalThis.clawdisA2UI = {
|
||||
globalThis.clawdbotA2UI = {
|
||||
applyMessages: (messages) => this.applyMessages(messages),
|
||||
reset: () => this.reset(),
|
||||
getSurfaces: () => Array.from(this.#processor.getSurfaces().keys())
|
||||
};
|
||||
this.addEventListener("a2uiaction", (evt) => this.#handleA2UIAction(evt));
|
||||
this.#statusListener = (evt) => this.#handleActionStatus(evt);
|
||||
globalThis.addEventListener("clawdis:a2ui-action-status", this.#statusListener);
|
||||
globalThis.addEventListener("clawdbot:a2ui-action-status", this.#statusListener);
|
||||
this.#syncSurfaces();
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this.#statusListener) {
|
||||
globalThis.removeEventListener("clawdis:a2ui-action-status", this.#statusListener);
|
||||
globalThis.removeEventListener("clawdbot:a2ui-action-status", this.#statusListener);
|
||||
this.#statusListener = null;
|
||||
}
|
||||
}
|
||||
@@ -17688,11 +17688,11 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
timestamp: new Date().toISOString(),
|
||||
...Object.keys(context).length ? { context } : {}
|
||||
};
|
||||
globalThis.__clawdisLastA2UIAction = userAction;
|
||||
const handler = globalThis.webkit?.messageHandlers?.clawdisCanvasA2UIAction ?? globalThis.clawdisCanvasA2UIAction;
|
||||
globalThis.__clawdbotLastA2UIAction = userAction;
|
||||
const handler = globalThis.webkit?.messageHandlers?.clawdbotCanvasA2UIAction ?? globalThis.clawdbotCanvasA2UIAction;
|
||||
if (handler?.postMessage) {
|
||||
try {
|
||||
if (handler === globalThis.clawdisCanvasA2UIAction) {
|
||||
if (handler === globalThis.clawdbotCanvasA2UIAction) {
|
||||
handler.postMessage(JSON.stringify({ userAction }));
|
||||
} else {
|
||||
handler.postMessage({ userAction });
|
||||
@@ -17765,4 +17765,4 @@ var ClawdisA2UIHost = class extends i$6 {
|
||||
</section>`;
|
||||
}
|
||||
};
|
||||
customElements.define("clawdis-a2ui-host", ClawdisA2UIHost);
|
||||
customElements.define("clawdbot-a2ui-host", ClawdbotA2UIHost);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Clawdis Canvas</title>
|
||||
<title>Clawdbot Canvas</title>
|
||||
<script>
|
||||
(() => {
|
||||
try {
|
||||
@@ -57,7 +57,7 @@
|
||||
backface-visibility: hidden;
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
animation: clawdis-grid-drift 140s ease-in-out infinite alternate;
|
||||
animation: clawdbot-grid-drift 140s ease-in-out infinite alternate;
|
||||
}
|
||||
:root[data-platform="android"] body::before { opacity: 0.80; }
|
||||
body::after {
|
||||
@@ -75,7 +75,7 @@
|
||||
backface-visibility: hidden;
|
||||
transform: translate3d(0,0,0);
|
||||
pointer-events: none;
|
||||
animation: clawdis-glow-drift 110s ease-in-out infinite alternate;
|
||||
animation: clawdbot-glow-drift 110s ease-in-out infinite alternate;
|
||||
}
|
||||
:root[data-platform="android"] body::after { opacity: 0.85; }
|
||||
@supports (mix-blend-mode: screen) {
|
||||
@@ -84,12 +84,12 @@
|
||||
@supports not (mix-blend-mode: screen) {
|
||||
body::after { opacity: 0.70; }
|
||||
}
|
||||
@keyframes clawdis-grid-drift {
|
||||
@keyframes clawdbot-grid-drift {
|
||||
0% { transform: translate3d(-12px, 8px, 0) rotate(-7deg); opacity: 0.40; }
|
||||
50% { transform: translate3d( 10px,-7px, 0) rotate(-6.6deg); opacity: 0.56; }
|
||||
100% { transform: translate3d(-8px, 6px, 0) rotate(-7.2deg); opacity: 0.42; }
|
||||
}
|
||||
@keyframes clawdis-glow-drift {
|
||||
@keyframes clawdbot-glow-drift {
|
||||
0% { transform: translate3d(-18px, 12px, 0) scale(1.02); opacity: 0.40; }
|
||||
50% { transform: translate3d( 14px,-10px, 0) scale(1.05); opacity: 0.52; }
|
||||
100% { transform: translate3d(-10px, 8px, 0) scale(1.03); opacity: 0.43; }
|
||||
@@ -103,14 +103,14 @@
|
||||
touch-action: none;
|
||||
z-index: 1;
|
||||
}
|
||||
:root[data-platform="android"] #clawdis-canvas {
|
||||
:root[data-platform="android"] #clawdbot-canvas {
|
||||
background:
|
||||
radial-gradient(1100px 800px at 20% 15%, rgba(42, 113, 255, 0.78), rgba(0,0,0,0) 58%),
|
||||
radial-gradient(900px 650px at 82% 28%, rgba(255, 0, 138, 0.66), rgba(0,0,0,0) 62%),
|
||||
radial-gradient(1000px 900px at 60% 88%, rgba(0, 209, 255, 0.58), rgba(0,0,0,0) 62%),
|
||||
#141c33;
|
||||
}
|
||||
#clawdis-status {
|
||||
#clawdbot-status {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: none;
|
||||
@@ -122,7 +122,7 @@
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
#clawdis-status .card {
|
||||
#clawdbot-status .card {
|
||||
width: min(560px, 88vw);
|
||||
text-align: left;
|
||||
padding: 14px 16px 12px;
|
||||
@@ -134,52 +134,52 @@
|
||||
-webkit-backdrop-filter: blur(18px) saturate(140%);
|
||||
backdrop-filter: blur(18px) saturate(140%);
|
||||
}
|
||||
#clawdis-status .title {
|
||||
#clawdbot-status .title {
|
||||
font: 600 12px/1.2 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
|
||||
letter-spacing: 0.45px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.7);
|
||||
}
|
||||
#clawdis-status .subtitle {
|
||||
#clawdbot-status .subtitle {
|
||||
margin-top: 8px;
|
||||
font: 500 13px/1.45 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
|
||||
color: rgba(255,255,255,0.9);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
clawdis-a2ui-host {
|
||||
clawdbot-a2ui-host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 4;
|
||||
--clawdis-a2ui-inset-top: 28px;
|
||||
--clawdis-a2ui-inset-right: 0px;
|
||||
--clawdis-a2ui-inset-bottom: 0px;
|
||||
--clawdis-a2ui-inset-left: 0px;
|
||||
--clawdis-a2ui-scroll-pad-bottom: 0px;
|
||||
--clawdis-a2ui-status-top: calc(50% - 18px);
|
||||
--clawdis-a2ui-empty-top: 18px;
|
||||
--clawdbot-a2ui-inset-top: 28px;
|
||||
--clawdbot-a2ui-inset-right: 0px;
|
||||
--clawdbot-a2ui-inset-bottom: 0px;
|
||||
--clawdbot-a2ui-inset-left: 0px;
|
||||
--clawdbot-a2ui-scroll-pad-bottom: 0px;
|
||||
--clawdbot-a2ui-status-top: calc(50% - 18px);
|
||||
--clawdbot-a2ui-empty-top: 18px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="clawdis-canvas"></canvas>
|
||||
<div id="clawdis-status">
|
||||
<canvas id="clawdbot-canvas"></canvas>
|
||||
<div id="clawdbot-status">
|
||||
<div class="card">
|
||||
<div class="title" id="clawdis-status-title">Ready</div>
|
||||
<div class="subtitle" id="clawdis-status-subtitle">Waiting for agent</div>
|
||||
<div class="title" id="clawdbot-status-title">Ready</div>
|
||||
<div class="subtitle" id="clawdbot-status-subtitle">Waiting for agent</div>
|
||||
</div>
|
||||
</div>
|
||||
<clawdis-a2ui-host></clawdis-a2ui-host>
|
||||
<clawdbot-a2ui-host></clawdbot-a2ui-host>
|
||||
<script src="a2ui.bundle.js"></script>
|
||||
<script>
|
||||
(() => {
|
||||
const canvas = document.getElementById('clawdis-canvas');
|
||||
const canvas = document.getElementById('clawdbot-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const statusEl = document.getElementById('clawdis-status');
|
||||
const titleEl = document.getElementById('clawdis-status-title');
|
||||
const subtitleEl = document.getElementById('clawdis-status-subtitle');
|
||||
const statusEl = document.getElementById('clawdbot-status');
|
||||
const titleEl = document.getElementById('clawdbot-status-title');
|
||||
const subtitleEl = document.getElementById('clawdbot-status-subtitle');
|
||||
const debugStatusEnabledByQuery = (() => {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -217,7 +217,7 @@
|
||||
statusEl.style.display = 'none';
|
||||
}
|
||||
|
||||
window.__clawdis = {
|
||||
window.__clawdbot = {
|
||||
canvas,
|
||||
ctx,
|
||||
setDebugStatusEnabled,
|
||||
|
||||
@@ -19,12 +19,12 @@ describe("canvas host", () => {
|
||||
const out = injectCanvasLiveReload("<html><body>Hello</body></html>");
|
||||
expect(out).toContain(CANVAS_WS_PATH);
|
||||
expect(out).toContain("location.reload");
|
||||
expect(out).toContain("clawdisCanvasA2UIAction");
|
||||
expect(out).toContain("clawdisSendUserAction");
|
||||
expect(out).toContain("clawdbotCanvasA2UIAction");
|
||||
expect(out).toContain("clawdbotSendUserAction");
|
||||
});
|
||||
|
||||
it("creates a default index.html when missing", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
|
||||
const server = await startCanvasHost({
|
||||
runtime: defaultRuntime,
|
||||
@@ -41,7 +41,7 @@ describe("canvas host", () => {
|
||||
const html = await res.text();
|
||||
expect(res.status).toBe(200);
|
||||
expect(html).toContain("Interactive test page");
|
||||
expect(html).toContain("clawdisSendUserAction");
|
||||
expect(html).toContain("clawdbotSendUserAction");
|
||||
expect(html).toContain(CANVAS_WS_PATH);
|
||||
} finally {
|
||||
await server.close();
|
||||
@@ -50,7 +50,7 @@ describe("canvas host", () => {
|
||||
});
|
||||
|
||||
it("skips live reload injection when disabled", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
await fs.writeFile(
|
||||
path.join(dir, "index.html"),
|
||||
"<html><body>no-reload</body></html>",
|
||||
@@ -86,7 +86,7 @@ describe("canvas host", () => {
|
||||
});
|
||||
|
||||
it("serves canvas content from the mounted base path", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
await fs.writeFile(
|
||||
path.join(dir, "index.html"),
|
||||
"<html><body>v1</body></html>",
|
||||
@@ -137,7 +137,7 @@ describe("canvas host", () => {
|
||||
});
|
||||
|
||||
it("reuses a handler without closing it twice", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
await fs.writeFile(
|
||||
path.join(dir, "index.html"),
|
||||
"<html><body>v1</body></html>",
|
||||
@@ -174,7 +174,7 @@ describe("canvas host", () => {
|
||||
});
|
||||
|
||||
it("serves HTML with injection and broadcasts reload on file changes", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
const index = path.join(dir, "index.html");
|
||||
await fs.writeFile(index, "<html><body>v1</body></html>", "utf8");
|
||||
|
||||
@@ -234,7 +234,7 @@ describe("canvas host", () => {
|
||||
});
|
||||
|
||||
it("serves the gateway-hosted A2UI scaffold", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-canvas-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
|
||||
const server = await startCanvasHost({
|
||||
runtime: defaultRuntime,
|
||||
@@ -246,19 +246,19 @@ describe("canvas host", () => {
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`http://127.0.0.1:${server.port}/__clawdis__/a2ui/`,
|
||||
`http://127.0.0.1:${server.port}/__clawdbot__/a2ui/`,
|
||||
);
|
||||
const html = await res.text();
|
||||
expect(res.status).toBe(200);
|
||||
expect(html).toContain("clawdis-a2ui-host");
|
||||
expect(html).toContain("clawdisCanvasA2UIAction");
|
||||
expect(html).toContain("clawdbot-a2ui-host");
|
||||
expect(html).toContain("clawdbotCanvasA2UIAction");
|
||||
|
||||
const bundleRes = await fetch(
|
||||
`http://127.0.0.1:${server.port}/__clawdis__/a2ui/a2ui.bundle.js`,
|
||||
`http://127.0.0.1:${server.port}/__clawdbot__/a2ui/a2ui.bundle.js`,
|
||||
);
|
||||
const js = await bundleRes.text();
|
||||
expect(bundleRes.status).toBe(200);
|
||||
expect(js).toContain("clawdisA2UI");
|
||||
expect(js).toContain("clawdbotA2UI");
|
||||
} finally {
|
||||
await server.close();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
|
||||
+10
-10
@@ -68,7 +68,7 @@ function defaultIndexHTML() {
|
||||
return `<!doctype html>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Clawdis Canvas</title>
|
||||
<title>Clawdbot Canvas</title>
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; background: #000; color: #fff; font: 16px/1.4 -apple-system, BlinkMacSystemFont, system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }
|
||||
.wrap { min-height: 100%; display: grid; place-items: center; padding: 24px; }
|
||||
@@ -86,7 +86,7 @@ function defaultIndexHTML() {
|
||||
<div class="wrap">
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
<h1>Clawdis Canvas</h1>
|
||||
<h1>Clawdbot Canvas</h1>
|
||||
<div class="sub">Interactive test page (auto-reload enabled)</div>
|
||||
</div>
|
||||
|
||||
@@ -107,26 +107,26 @@ function defaultIndexHTML() {
|
||||
const statusEl = document.getElementById("status");
|
||||
const log = (msg) => { logEl.textContent = String(msg); };
|
||||
|
||||
const hasIOS = () => !!(window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.clawdisCanvasA2UIAction);
|
||||
const hasAndroid = () => !!(window.clawdisCanvasA2UIAction && typeof window.clawdisCanvasA2UIAction.postMessage === "function");
|
||||
const hasHelper = () => typeof window.clawdisSendUserAction === "function";
|
||||
const hasIOS = () => !!(window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.clawdbotCanvasA2UIAction);
|
||||
const hasAndroid = () => !!(window.clawdbotCanvasA2UIAction && typeof window.clawdbotCanvasA2UIAction.postMessage === "function");
|
||||
const hasHelper = () => typeof window.clawdbotSendUserAction === "function";
|
||||
statusEl.innerHTML =
|
||||
"Bridge: " +
|
||||
(hasHelper() ? "<span class='ok'>ready</span>" : "<span class='bad'>missing</span>") +
|
||||
" · iOS=" + (hasIOS() ? "yes" : "no") +
|
||||
" · Android=" + (hasAndroid() ? "yes" : "no");
|
||||
|
||||
window.addEventListener("clawdis:a2ui-action-status", (ev) => {
|
||||
window.addEventListener("clawdbot:a2ui-action-status", (ev) => {
|
||||
const d = ev && ev.detail || {};
|
||||
log("Action status: id=" + (d.id || "?") + " ok=" + String(!!d.ok) + (d.error ? (" error=" + d.error) : ""));
|
||||
});
|
||||
|
||||
function send(name, sourceComponentId) {
|
||||
if (!hasHelper()) {
|
||||
log("No action bridge found. Ensure you're viewing this on an iOS/Android Clawdis node canvas.");
|
||||
log("No action bridge found. Ensure you're viewing this on an iOS/Android Clawdbot node canvas.");
|
||||
return;
|
||||
}
|
||||
const ok = window.clawdisSendUserAction({
|
||||
const ok = window.clawdbotSendUserAction({
|
||||
name,
|
||||
surfaceId: "main",
|
||||
sourceComponentId,
|
||||
@@ -184,7 +184,7 @@ async function resolveFilePath(rootReal: string, urlPath: string) {
|
||||
}
|
||||
|
||||
function isDisabledByEnv() {
|
||||
if (process.env.CLAWDIS_SKIP_CANVAS_HOST === "1") return true;
|
||||
if (process.env.CLAWDBOT_SKIP_CANVAS_HOST === "1") return true;
|
||||
if (process.env.NODE_ENV === "test") return true;
|
||||
if (process.env.VITEST) return true;
|
||||
return false;
|
||||
@@ -341,7 +341,7 @@ export async function createCanvasHostHandler(
|
||||
res.statusCode = 404;
|
||||
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||
res.end(
|
||||
`<!doctype html><meta charset="utf-8" /><title>Clawdis Canvas</title><pre>Missing file.\nCreate ${rootDir}/index.html</pre>`,
|
||||
`<!doctype html><meta charset="utf-8" /><title>Clawdbot Canvas</title><pre>Missing file.\nCreate ${rootDir}/index.html</pre>`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
export const browserCoreExamples = [
|
||||
"clawdis browser status",
|
||||
"clawdis browser start",
|
||||
"clawdis browser stop",
|
||||
"clawdis browser tabs",
|
||||
"clawdis browser open https://example.com",
|
||||
"clawdis browser focus abcd1234",
|
||||
"clawdis browser close abcd1234",
|
||||
"clawdis browser screenshot",
|
||||
"clawdis browser screenshot --full-page",
|
||||
"clawdis browser screenshot --ref 12",
|
||||
"clawdis browser snapshot",
|
||||
"clawdis browser snapshot --format aria --limit 200",
|
||||
"clawdbot browser status",
|
||||
"clawdbot browser start",
|
||||
"clawdbot browser stop",
|
||||
"clawdbot browser tabs",
|
||||
"clawdbot browser open https://example.com",
|
||||
"clawdbot browser focus abcd1234",
|
||||
"clawdbot browser close abcd1234",
|
||||
"clawdbot browser screenshot",
|
||||
"clawdbot browser screenshot --full-page",
|
||||
"clawdbot browser screenshot --ref 12",
|
||||
"clawdbot browser snapshot",
|
||||
"clawdbot browser snapshot --format aria --limit 200",
|
||||
];
|
||||
|
||||
export const browserActionExamples = [
|
||||
"clawdis browser navigate https://example.com",
|
||||
"clawdis browser resize 1280 720",
|
||||
"clawdis browser click 12 --double",
|
||||
'clawdis browser type 23 "hello" --submit',
|
||||
"clawdis browser press Enter",
|
||||
"clawdis browser hover 44",
|
||||
"clawdis browser drag 10 11",
|
||||
"clawdis browser select 9 OptionA OptionB",
|
||||
"clawdis browser upload /tmp/file.pdf",
|
||||
'clawdis browser fill --fields \'[{"ref":"1","value":"Ada"}]\'',
|
||||
"clawdis browser dialog --accept",
|
||||
'clawdis browser wait --text "Done"',
|
||||
"clawdis browser evaluate --fn '(el) => el.textContent' --ref 7",
|
||||
"clawdis browser console --level error",
|
||||
"clawdis browser pdf",
|
||||
"clawdbot browser navigate https://example.com",
|
||||
"clawdbot browser resize 1280 720",
|
||||
"clawdbot browser click 12 --double",
|
||||
'clawdbot browser type 23 "hello" --submit',
|
||||
"clawdbot browser press Enter",
|
||||
"clawdbot browser hover 44",
|
||||
"clawdbot browser drag 10 11",
|
||||
"clawdbot browser select 9 OptionA OptionB",
|
||||
"clawdbot browser upload /tmp/file.pdf",
|
||||
'clawdbot browser fill --fields \'[{"ref":"1","value":"Ada"}]\'',
|
||||
"clawdbot browser dialog --accept",
|
||||
'clawdbot browser wait --text "Done"',
|
||||
"clawdbot browser evaluate --fn '(el) => el.textContent' --ref 7",
|
||||
"clawdbot browser console --level error",
|
||||
"clawdbot browser pdf",
|
||||
];
|
||||
|
||||
@@ -18,7 +18,7 @@ export function registerBrowserCli(program: Command) {
|
||||
.description("Manage clawd's dedicated browser (Chrome/Chromium)")
|
||||
.option(
|
||||
"--url <url>",
|
||||
"Override browser control URL (default from ~/.clawdis/clawdis.json)",
|
||||
"Override browser control URL (default from ~/.clawdbot/clawdbot.json)",
|
||||
)
|
||||
.option("--profile <name>", "Browser profile name (default from config)")
|
||||
.option("--json", "Output machine-readable JSON", false)
|
||||
@@ -29,7 +29,7 @@ export function registerBrowserCli(program: Command) {
|
||||
.action(() => {
|
||||
browser.outputHelp();
|
||||
defaultRuntime.error(
|
||||
danger('Missing subcommand. Try: "clawdis browser status"'),
|
||||
danger('Missing subcommand. Try: "clawdbot browser status"'),
|
||||
);
|
||||
defaultRuntime.exit(1);
|
||||
});
|
||||
|
||||
@@ -499,7 +499,7 @@ export function registerCanvasCli(program: Command) {
|
||||
const { version, messageCount } = validateA2UIJsonl(jsonl);
|
||||
if (version === "v0.9") {
|
||||
throw new Error(
|
||||
"Detected A2UI v0.9 JSONL (createSurface). Clawdis currently supports v0.8 only.",
|
||||
"Detected A2UI v0.9 JSONL (createSurface). Clawdbot currently supports v0.8 only.",
|
||||
);
|
||||
}
|
||||
await invokeCanvas(opts, "canvas.a2ui.pushJSONL", { jsonl });
|
||||
|
||||
+6
-6
@@ -103,7 +103,7 @@ export function registerDnsCli(program: Command) {
|
||||
dns
|
||||
.command("setup")
|
||||
.description(
|
||||
"Set up CoreDNS to serve clawdis.internal for unicast DNS-SD (Wide-Area Bonjour)",
|
||||
"Set up CoreDNS to serve clawdbot.internal for unicast DNS-SD (Wide-Area Bonjour)",
|
||||
)
|
||||
.option(
|
||||
"--apply",
|
||||
@@ -122,7 +122,7 @@ export function registerDnsCli(program: Command) {
|
||||
`Detected tailnet IP: ${tailnetIPv4 ?? "—"}${tailnetIPv6 ? ` (v6 ${tailnetIPv6})` : ""}`,
|
||||
);
|
||||
console.log("");
|
||||
console.log("Recommended ~/.clawdis/clawdis.json:");
|
||||
console.log("Recommended ~/.clawdbot/clawdbot.json:");
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
@@ -138,7 +138,7 @@ export function registerDnsCli(program: Command) {
|
||||
console.log(
|
||||
`- Add nameserver: ${tailnetIPv4 ?? "<this machine's tailnet IPv4>"}`,
|
||||
);
|
||||
console.log(`- Restrict to domain (Split DNS): clawdis.internal`);
|
||||
console.log(`- Restrict to domain (Split DNS): clawdbot.internal`);
|
||||
|
||||
if (!opts.apply) {
|
||||
console.log("");
|
||||
@@ -160,7 +160,7 @@ export function registerDnsCli(program: Command) {
|
||||
const corefilePath = path.join(etcDir, "Corefile");
|
||||
const confDir = path.join(etcDir, "conf.d");
|
||||
const importGlob = path.join(confDir, "*.server");
|
||||
const serverPath = path.join(confDir, "clawdis.internal.server");
|
||||
const serverPath = path.join(confDir, "clawdbot.internal.server");
|
||||
|
||||
run("brew", ["list", "coredns"], { allowFailure: true });
|
||||
run("brew", ["install", "coredns"], {
|
||||
@@ -202,7 +202,7 @@ export function registerDnsCli(program: Command) {
|
||||
const serial = `${y}${m}${d}01`;
|
||||
|
||||
const zoneLines = [
|
||||
`; created by clawdis dns setup (will be overwritten by the gateway when wide-area discovery is enabled)`,
|
||||
`; created by clawdbot dns setup (will be overwritten by the gateway when wide-area discovery is enabled)`,
|
||||
`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`,
|
||||
`$TTL 60`,
|
||||
`@ IN SOA ns1 hostmaster ${serial} 7200 3600 1209600 60`,
|
||||
@@ -224,7 +224,7 @@ export function registerDnsCli(program: Command) {
|
||||
if (cfg.discovery?.wideArea?.enabled !== true) {
|
||||
console.log("");
|
||||
console.log(
|
||||
"Note: enable discovery.wideArea.enabled in ~/.clawdis/clawdis.json on the gateway and restart the gateway so it writes the DNS-SD zone.",
|
||||
"Note: enable discovery.wideArea.enabled in ~/.clawdbot/clawdbot.json on the gateway and restart the gateway so it writes the DNS-SD zone.",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
||||
|
||||
import type { Command } from "commander";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDIS,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
loadConfig,
|
||||
resolveGatewayPort,
|
||||
} from "../config/config.js";
|
||||
@@ -160,7 +160,7 @@ export function registerGatewayCli(program: Command) {
|
||||
)
|
||||
.option(
|
||||
"--token <token>",
|
||||
"Shared token required in connect.params.auth.token (default: CLAWDIS_GATEWAY_TOKEN env if set)",
|
||||
"Shared token required in connect.params.auth.token (default: CLAWDBOT_GATEWAY_TOKEN env if set)",
|
||||
)
|
||||
.option("--auth <mode>", 'Gateway auth mode ("token"|"password")')
|
||||
.option("--password <password>", "Password for auth mode=password")
|
||||
@@ -218,7 +218,7 @@ export function registerGatewayCli(program: Command) {
|
||||
return;
|
||||
}
|
||||
if (opts.token) {
|
||||
process.env.CLAWDIS_GATEWAY_TOKEN = String(opts.token);
|
||||
process.env.CLAWDBOT_GATEWAY_TOKEN = String(opts.token);
|
||||
}
|
||||
const authModeRaw = opts.auth ? String(opts.auth) : undefined;
|
||||
const authMode =
|
||||
@@ -305,7 +305,7 @@ export function registerGatewayCli(program: Command) {
|
||||
)
|
||||
.option(
|
||||
"--token <token>",
|
||||
"Shared token required in connect.params.auth.token (default: CLAWDIS_GATEWAY_TOKEN env if set)",
|
||||
"Shared token required in connect.params.auth.token (default: CLAWDBOT_GATEWAY_TOKEN env if set)",
|
||||
)
|
||||
.option("--auth <mode>", 'Gateway auth mode ("token"|"password")')
|
||||
.option("--password <password>", "Password for auth mode=password")
|
||||
@@ -404,7 +404,7 @@ export function registerGatewayCli(program: Command) {
|
||||
}
|
||||
}
|
||||
if (opts.token) {
|
||||
process.env.CLAWDIS_GATEWAY_TOKEN = String(opts.token);
|
||||
process.env.CLAWDBOT_GATEWAY_TOKEN = String(opts.token);
|
||||
}
|
||||
const authModeRaw = opts.auth ? String(opts.auth) : undefined;
|
||||
const authMode =
|
||||
@@ -430,12 +430,12 @@ export function registerGatewayCli(program: Command) {
|
||||
defaultRuntime.exit(1);
|
||||
return;
|
||||
}
|
||||
const configExists = fs.existsSync(CONFIG_PATH_CLAWDIS);
|
||||
const configExists = fs.existsSync(CONFIG_PATH_CLAWDBOT);
|
||||
const mode = cfg.gateway?.mode;
|
||||
if (!opts.allowUnconfigured && mode !== "local") {
|
||||
if (!configExists) {
|
||||
defaultRuntime.error(
|
||||
"Missing config. Run `clawdis setup` or set gateway.mode=local (or pass --allow-unconfigured).",
|
||||
"Missing config. Run `clawdbot setup` or set gateway.mode=local (or pass --allow-unconfigured).",
|
||||
);
|
||||
} else {
|
||||
defaultRuntime.error(
|
||||
|
||||
@@ -78,7 +78,7 @@ describe("gateway SIGTERM", () => {
|
||||
it("exits 0 on SIGTERM", { timeout: 90_000 }, async () => {
|
||||
const port = await getFreePort();
|
||||
const stateDir = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "clawdis-gateway-test-"),
|
||||
path.join(os.tmpdir(), "clawdbot-gateway-test-"),
|
||||
);
|
||||
const out: string[] = [];
|
||||
const err: string[] = [];
|
||||
@@ -100,14 +100,14 @@ describe("gateway SIGTERM", () => {
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
CLAWDIS_STATE_DIR: stateDir,
|
||||
CLAWDIS_CONFIG_PATH: path.join(stateDir, "clawdis.json"),
|
||||
CLAWDIS_SKIP_PROVIDERS: "1",
|
||||
CLAWDIS_SKIP_BROWSER_CONTROL_SERVER: "1",
|
||||
CLAWDIS_SKIP_CANVAS_HOST: "1",
|
||||
CLAWDBOT_STATE_DIR: stateDir,
|
||||
CLAWDBOT_CONFIG_PATH: path.join(stateDir, "clawdbot.json"),
|
||||
CLAWDBOT_SKIP_PROVIDERS: "1",
|
||||
CLAWDBOT_SKIP_BROWSER_CONTROL_SERVER: "1",
|
||||
CLAWDBOT_SKIP_CANVAS_HOST: "1",
|
||||
// Avoid port collisions with other test processes that may also start a bridge server.
|
||||
CLAWDIS_BRIDGE_HOST: "127.0.0.1",
|
||||
CLAWDIS_BRIDGE_PORT: "0",
|
||||
CLAWDBOT_BRIDGE_HOST: "127.0.0.1",
|
||||
CLAWDBOT_BRIDGE_PORT: "0",
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@ export function registerHooksCli(program: Command) {
|
||||
|
||||
gmail
|
||||
.command("setup")
|
||||
.description("Configure Gmail watch + Pub/Sub + Clawdis hooks")
|
||||
.description("Configure Gmail watch + Pub/Sub + Clawdbot hooks")
|
||||
.requiredOption("--account <email>", "Gmail account to watch")
|
||||
.option("--project <id>", "GCP project id (OAuth client owner)")
|
||||
.option("--topic <name>", "Pub/Sub topic name", DEFAULT_GMAIL_TOPIC)
|
||||
@@ -40,8 +40,8 @@ export function registerHooksCli(program: Command) {
|
||||
DEFAULT_GMAIL_SUBSCRIPTION,
|
||||
)
|
||||
.option("--label <label>", "Gmail label to watch", DEFAULT_GMAIL_LABEL)
|
||||
.option("--hook-url <url>", "Clawdis hook URL")
|
||||
.option("--hook-token <token>", "Clawdis hook token")
|
||||
.option("--hook-url <url>", "Clawdbot hook URL")
|
||||
.option("--hook-token <token>", "Clawdbot hook token")
|
||||
.option("--push-token <token>", "Push token for gog watch serve")
|
||||
.option(
|
||||
"--bind <host>",
|
||||
@@ -90,8 +90,8 @@ export function registerHooksCli(program: Command) {
|
||||
.option("--topic <topic>", "Pub/Sub topic path (projects/.../topics/..)")
|
||||
.option("--subscription <name>", "Pub/Sub subscription name")
|
||||
.option("--label <label>", "Gmail label to watch")
|
||||
.option("--hook-url <url>", "Clawdis hook URL")
|
||||
.option("--hook-token <token>", "Clawdis hook token")
|
||||
.option("--hook-url <url>", "Clawdbot hook URL")
|
||||
.option("--hook-token <token>", "Clawdbot hook token")
|
||||
.option("--push-token <token>", "Push token for gog watch serve")
|
||||
.option("--bind <host>", "gog watch serve bind host")
|
||||
.option("--port <port>", "gog watch serve port")
|
||||
|
||||
@@ -51,11 +51,11 @@ describe("nodes camera helpers", () => {
|
||||
tmpDir: "/tmp",
|
||||
id: "id1",
|
||||
});
|
||||
expect(p).toBe(path.join("/tmp", "clawdis-camera-snap-front-id1.jpg"));
|
||||
expect(p).toBe(path.join("/tmp", "clawdbot-camera-snap-front-id1.jpg"));
|
||||
});
|
||||
|
||||
it("writes base64 to file", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-test-"));
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-test-"));
|
||||
const out = path.join(dir, "x.bin");
|
||||
await writeBase64ToFile(out, "aGk=");
|
||||
await expect(fs.readFile(out, "utf8")).resolves.toBe("hi");
|
||||
|
||||
@@ -81,7 +81,7 @@ export function cameraTempPath(opts: {
|
||||
const ext = opts.ext.startsWith(".") ? opts.ext : `.${opts.ext}`;
|
||||
return path.join(
|
||||
tmpDir,
|
||||
`clawdis-camera-${opts.kind}${facingPart}-${id}${ext}`,
|
||||
`clawdbot-camera-${opts.kind}${facingPart}-${id}${ext}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,5 +37,5 @@ export function canvasSnapshotTempPath(opts: {
|
||||
const tmpDir = opts.tmpDir ?? os.tmpdir();
|
||||
const id = opts.id ?? randomUUID();
|
||||
const ext = opts.ext.startsWith(".") ? opts.ext : `.${opts.ext}`;
|
||||
return path.join(tmpDir, `clawdis-canvas-snapshot-${id}${ext}`);
|
||||
return path.join(tmpDir, `clawdbot-canvas-snapshot-${id}${ext}`);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,6 @@ describe("nodes screen helpers", () => {
|
||||
tmpDir: "/tmp",
|
||||
id: "id1",
|
||||
});
|
||||
expect(p).toBe("/tmp/clawdis-screen-record-id1.mp4");
|
||||
expect(p).toBe("/tmp/clawdbot-screen-record-id1.mp4");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ export function screenRecordTempPath(opts: {
|
||||
const tmpDir = opts.tmpDir ?? os.tmpdir();
|
||||
const id = opts.id ?? randomUUID();
|
||||
const ext = opts.ext.startsWith(".") ? opts.ext : `.${opts.ext}`;
|
||||
return path.join(tmpDir, `clawdis-screen-record-${id}${ext}`);
|
||||
return path.join(tmpDir, `clawdbot-screen-record-${id}${ext}`);
|
||||
}
|
||||
|
||||
export async function writeScreenRecordToFile(
|
||||
|
||||
@@ -381,7 +381,7 @@ describe("cli program", () => {
|
||||
|
||||
const out = String(runtime.log.mock.calls[0]?.[0] ?? "");
|
||||
const mediaPath = out.replace(/^MEDIA:/, "").trim();
|
||||
expect(mediaPath).toMatch(/clawdis-camera-clip-front-.*\.mp4$/);
|
||||
expect(mediaPath).toMatch(/clawdbot-camera-clip-front-.*\.mp4$/);
|
||||
|
||||
try {
|
||||
await expect(fs.readFile(mediaPath, "utf8")).resolves.toBe("hi");
|
||||
@@ -607,7 +607,7 @@ describe("cli program", () => {
|
||||
|
||||
const out = String(runtime.log.mock.calls[0]?.[0] ?? "");
|
||||
const mediaPath = out.replace(/^MEDIA:/, "").trim();
|
||||
expect(mediaPath).toMatch(/clawdis-canvas-snapshot-.*\.png$/);
|
||||
expect(mediaPath).toMatch(/clawdbot-canvas-snapshot-.*\.png$/);
|
||||
|
||||
try {
|
||||
await expect(fs.readFile(mediaPath, "utf8")).resolves.toBe("hi");
|
||||
@@ -645,7 +645,7 @@ describe("cli program", () => {
|
||||
|
||||
const out = String(runtime.log.mock.calls[0]?.[0] ?? "");
|
||||
const mediaPath = out.replace(/^MEDIA:/, "").trim();
|
||||
expect(mediaPath).toMatch(/clawdis-canvas-snapshot-.*\.png$/);
|
||||
expect(mediaPath).toMatch(/clawdbot-canvas-snapshot-.*\.png$/);
|
||||
|
||||
try {
|
||||
await expect(fs.readFile(mediaPath, "utf8")).resolves.toBe("hi");
|
||||
|
||||
+28
-28
@@ -34,12 +34,12 @@ export function buildProgram() {
|
||||
const TAGLINE =
|
||||
"Send, receive, and auto-reply on WhatsApp (web) and Telegram (bot).";
|
||||
|
||||
program.name("clawdis").description("").version(PROGRAM_VERSION);
|
||||
program.name("clawdbot").description("").version(PROGRAM_VERSION);
|
||||
|
||||
const formatIntroLine = (version: string, rich = true) => {
|
||||
const base = `📡 clawdis ${version} — ${TAGLINE}`;
|
||||
const base = `📡 clawdbot ${version} — ${TAGLINE}`;
|
||||
return rich && chalk.level > 0
|
||||
? `${chalk.bold.cyan("📡 clawdis")} ${chalk.white(version)} ${chalk.gray("—")} ${chalk.green(TAGLINE)}`
|
||||
? `${chalk.bold.cyan("📡 clawdbot")} ${chalk.white(version)} ${chalk.gray("—")} ${chalk.green(TAGLINE)}`
|
||||
: base;
|
||||
};
|
||||
|
||||
@@ -80,32 +80,32 @@ export function buildProgram() {
|
||||
.join("\n");
|
||||
defaultRuntime.error(
|
||||
danger(
|
||||
`Legacy config entries detected. Run "clawdis doctor" (or ask your agent) to migrate.\n${issues}`,
|
||||
`Legacy config entries detected. Run "clawdbot doctor" (or ask your agent) to migrate.\n${issues}`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
});
|
||||
const examples = [
|
||||
[
|
||||
"clawdis login --verbose",
|
||||
"clawdbot login --verbose",
|
||||
"Link personal WhatsApp Web and show QR + connection logs.",
|
||||
],
|
||||
[
|
||||
'clawdis send --to +15555550123 --message "Hi" --json',
|
||||
'clawdbot send --to +15555550123 --message "Hi" --json',
|
||||
"Send via your web session and print JSON result.",
|
||||
],
|
||||
["clawdis gateway --port 18789", "Run the WebSocket Gateway locally."],
|
||||
["clawdbot gateway --port 18789", "Run the WebSocket Gateway locally."],
|
||||
[
|
||||
"clawdis gateway --force",
|
||||
"clawdbot gateway --force",
|
||||
"Kill anything bound to the default gateway port, then start it.",
|
||||
],
|
||||
["clawdis gateway ...", "Gateway control via WebSocket."],
|
||||
["clawdbot gateway ...", "Gateway control via WebSocket."],
|
||||
[
|
||||
'clawdis agent --to +15555550123 --message "Run summary" --deliver',
|
||||
'clawdbot agent --to +15555550123 --message "Run summary" --deliver',
|
||||
"Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.",
|
||||
],
|
||||
[
|
||||
'clawdis send --provider telegram --to @mychat --message "Hi"',
|
||||
'clawdbot send --provider telegram --to @mychat --message "Hi"',
|
||||
"Send via your Telegram bot.",
|
||||
],
|
||||
] as const;
|
||||
@@ -121,7 +121,7 @@ export function buildProgram() {
|
||||
|
||||
program
|
||||
.command("setup")
|
||||
.description("Initialize ~/.clawdis/clawdis.json and the agent workspace")
|
||||
.description("Initialize ~/.clawdbot/clawdbot.json and the agent workspace")
|
||||
.option(
|
||||
"--workspace <dir>",
|
||||
"Agent workspace directory (default: ~/clawd; stored as agent.workspace)",
|
||||
@@ -327,10 +327,10 @@ export function buildProgram() {
|
||||
"after",
|
||||
`
|
||||
Examples:
|
||||
clawdis send --to +15555550123 --message "Hi"
|
||||
clawdis send --to +15555550123 --message "Hi" --media photo.jpg
|
||||
clawdis send --to +15555550123 --message "Hi" --dry-run # print payload only
|
||||
clawdis send --to +15555550123 --message "Hi" --json # machine-readable result`,
|
||||
clawdbot send --to +15555550123 --message "Hi"
|
||||
clawdbot send --to +15555550123 --message "Hi" --media photo.jpg
|
||||
clawdbot send --to +15555550123 --message "Hi" --dry-run # print payload only
|
||||
clawdbot send --to +15555550123 --message "Hi" --json # machine-readable result`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
@@ -377,10 +377,10 @@ Examples:
|
||||
"after",
|
||||
`
|
||||
Examples:
|
||||
clawdis agent --to +15555550123 --message "status update"
|
||||
clawdis agent --session-id 1234 --message "Summarize inbox" --thinking medium
|
||||
clawdis agent --to +15555550123 --message "Trace logs" --verbose on --json
|
||||
clawdis agent --to +15555550123 --message "Summon reply" --deliver
|
||||
clawdbot agent --to +15555550123 --message "status update"
|
||||
clawdbot agent --session-id 1234 --message "Summarize inbox" --thinking medium
|
||||
clawdbot agent --to +15555550123 --message "Trace logs" --verbose on --json
|
||||
clawdbot agent --to +15555550123 --message "Summon reply" --deliver
|
||||
`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
@@ -420,10 +420,10 @@ Examples:
|
||||
"after",
|
||||
`
|
||||
Examples:
|
||||
clawdis status # show linked account + session store summary
|
||||
clawdis status --json # machine-readable output
|
||||
clawdis status --deep # run provider probes (WA + Telegram + Discord + Slack + Signal)
|
||||
clawdis status --deep --timeout 5000 # tighten probe timeout`,
|
||||
clawdbot status # show linked account + session store summary
|
||||
clawdbot status --json # machine-readable output
|
||||
clawdbot status --deep # run provider probes (WA + Telegram + Discord + Slack + Signal)
|
||||
clawdbot status --deep --timeout 5000 # tighten probe timeout`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
@@ -501,10 +501,10 @@ Examples:
|
||||
"after",
|
||||
`
|
||||
Examples:
|
||||
clawdis sessions # list all sessions
|
||||
clawdis sessions --active 120 # only last 2 hours
|
||||
clawdis sessions --json # machine-readable output
|
||||
clawdis sessions --store ./tmp/sessions.json
|
||||
clawdbot sessions # list all sessions
|
||||
clawdbot sessions --active 120 # only last 2 hours
|
||||
clawdbot sessions --json # machine-readable output
|
||||
clawdbot sessions --store ./tmp/sessions.json
|
||||
|
||||
Shows token usage per session when the agent reports it; set agent.contextTokens to see % of your model window.`,
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.mock("../agents/model-catalog.js", () => ({
|
||||
|
||||
import { loadModelCatalog } from "../agents/model-catalog.js";
|
||||
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import * as configModule from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { agentCommand } from "./agent.js";
|
||||
@@ -39,7 +39,7 @@ const runtime: RuntimeEnv = {
|
||||
const configSpy = vi.spyOn(configModule, "loadConfig");
|
||||
|
||||
async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
const base = fs.mkdtempSync(path.join(os.tmpdir(), "clawdis-agent-"));
|
||||
const base = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-agent-"));
|
||||
const previousHome = process.env.HOME;
|
||||
process.env.HOME = base;
|
||||
try {
|
||||
@@ -53,9 +53,9 @@ async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
function mockConfig(
|
||||
home: string,
|
||||
storePath: string,
|
||||
routingOverrides?: Partial<NonNullable<ClawdisConfig["routing"]>>,
|
||||
agentOverrides?: Partial<NonNullable<ClawdisConfig["agent"]>>,
|
||||
telegramOverrides?: Partial<NonNullable<ClawdisConfig["telegram"]>>,
|
||||
routingOverrides?: Partial<NonNullable<ClawdbotConfig["routing"]>>,
|
||||
agentOverrides?: Partial<NonNullable<ClawdbotConfig["agent"]>>,
|
||||
telegramOverrides?: Partial<NonNullable<ClawdbotConfig["telegram"]>>,
|
||||
) {
|
||||
configSpy.mockReturnValue({
|
||||
agent: {
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
type VerboseLevel,
|
||||
} from "../auto-reply/thinking.js";
|
||||
import { type CliDeps, createDefaultDeps } from "../cli/deps.js";
|
||||
import { type ClawdisConfig, loadConfig } from "../config/config.js";
|
||||
import { type ClawdbotConfig, loadConfig } from "../config/config.js";
|
||||
import {
|
||||
DEFAULT_IDLE_MINUTES,
|
||||
loadSessionStore,
|
||||
@@ -77,7 +77,7 @@ type SessionResolution = {
|
||||
};
|
||||
|
||||
function resolveSession(opts: {
|
||||
cfg: ClawdisConfig;
|
||||
cfg: ClawdbotConfig;
|
||||
to?: string;
|
||||
sessionId?: string;
|
||||
}): SessionResolution {
|
||||
@@ -540,7 +540,7 @@ export async function agentCommand(
|
||||
}
|
||||
if (deliveryProvider === "webchat") {
|
||||
const err = new Error(
|
||||
"Delivering to WebChat is not supported via `clawdis agent`; use WhatsApp/Telegram or run with --deliver=false.",
|
||||
"Delivering to WebChat is not supported via `clawdbot agent`; use WhatsApp/Telegram or run with --deliver=false.",
|
||||
);
|
||||
if (!bestEffortDeliver) throw err;
|
||||
logDeliveryError(err);
|
||||
|
||||
+14
-14
@@ -11,9 +11,9 @@ import {
|
||||
text,
|
||||
} from "@clack/prompts";
|
||||
import { loginAnthropic, type OAuthCredentials } from "@mariozechner/pi-ai";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDIS,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
readConfigFileSnapshot,
|
||||
resolveGatewayPort,
|
||||
writeConfigFile,
|
||||
@@ -66,10 +66,10 @@ type ConfigureWizardParams = {
|
||||
};
|
||||
|
||||
async function promptGatewayConfig(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
runtime: RuntimeEnv,
|
||||
): Promise<{
|
||||
config: ClawdisConfig;
|
||||
config: ClawdbotConfig;
|
||||
port: number;
|
||||
token?: string;
|
||||
}> {
|
||||
@@ -221,9 +221,9 @@ async function promptGatewayConfig(
|
||||
}
|
||||
|
||||
async function promptAuthConfig(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
runtime: RuntimeEnv,
|
||||
): Promise<ClawdisConfig> {
|
||||
): Promise<ClawdbotConfig> {
|
||||
const authChoice = guardCancel(
|
||||
await select({
|
||||
message: "Model/auth choice",
|
||||
@@ -399,8 +399,8 @@ async function maybeInstallDaemon(params: {
|
||||
await resolveGatewayProgramArguments({ port: params.port, dev: devMode });
|
||||
const environment: Record<string, string | undefined> = {
|
||||
PATH: process.env.PATH,
|
||||
CLAWDIS_GATEWAY_TOKEN: params.gatewayToken,
|
||||
CLAWDIS_LAUNCHD_LABEL:
|
||||
CLAWDBOT_GATEWAY_TOKEN: params.gatewayToken,
|
||||
CLAWDBOT_LAUNCHD_LABEL:
|
||||
process.platform === "darwin" ? GATEWAY_LAUNCH_AGENT_LABEL : undefined,
|
||||
};
|
||||
await service.install({
|
||||
@@ -418,12 +418,12 @@ export async function runConfigureWizard(
|
||||
) {
|
||||
printWizardHeader(runtime);
|
||||
intro(
|
||||
opts.command === "update" ? "Clawdis update wizard" : "Clawdis configure",
|
||||
opts.command === "update" ? "Clawdbot update wizard" : "Clawdbot configure",
|
||||
);
|
||||
const prompter = createClackPrompter();
|
||||
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
let baseConfig: ClawdisConfig = snapshot.valid ? snapshot.config : {};
|
||||
let baseConfig: ClawdbotConfig = snapshot.valid ? snapshot.config : {};
|
||||
|
||||
if (snapshot.exists) {
|
||||
const title = snapshot.valid
|
||||
@@ -453,10 +453,10 @@ export async function runConfigureWizard(
|
||||
const localUrl = "ws://127.0.0.1:18789";
|
||||
const localProbe = await probeGatewayReachable({
|
||||
url: localUrl,
|
||||
token: process.env.CLAWDIS_GATEWAY_TOKEN,
|
||||
token: process.env.CLAWDBOT_GATEWAY_TOKEN,
|
||||
password:
|
||||
baseConfig.gateway?.auth?.password ??
|
||||
process.env.CLAWDIS_GATEWAY_PASSWORD,
|
||||
process.env.CLAWDBOT_GATEWAY_PASSWORD,
|
||||
});
|
||||
const remoteUrl = baseConfig.gateway?.remote?.url?.trim() ?? "";
|
||||
const remoteProbe = remoteUrl
|
||||
@@ -498,7 +498,7 @@ export async function runConfigureWizard(
|
||||
mode,
|
||||
});
|
||||
await writeConfigFile(remoteConfig);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
outro("Remote gateway configured.");
|
||||
return;
|
||||
}
|
||||
@@ -583,7 +583,7 @@ export async function runConfigureWizard(
|
||||
mode,
|
||||
});
|
||||
await writeConfigFile(nextConfig);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
|
||||
if (selected.includes("daemon")) {
|
||||
if (!selected.includes("gateway")) {
|
||||
|
||||
@@ -19,7 +19,7 @@ vi.mock("../agents/skills-status.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../config/config.js", () => ({
|
||||
CONFIG_PATH_CLAWDIS: "/tmp/clawdis.json",
|
||||
CONFIG_PATH_CLAWDBOT: "/tmp/clawdbot.json",
|
||||
readConfigFileSnapshot,
|
||||
writeConfigFile,
|
||||
migrateLegacyConfig,
|
||||
@@ -54,7 +54,7 @@ vi.mock("./onboard-helpers.js", () => ({
|
||||
describe("doctor", () => {
|
||||
it("migrates routing.allowFrom to whatsapp.allowFrom", async () => {
|
||||
readConfigFileSnapshot.mockResolvedValue({
|
||||
path: "/tmp/clawdis.json",
|
||||
path: "/tmp/clawdbot.json",
|
||||
exists: true,
|
||||
raw: "{}",
|
||||
parsed: { routing: { allowFrom: ["+15555550123"] } },
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { confirm, intro, note, outro } from "@clack/prompts";
|
||||
|
||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDIS,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
migrateLegacyConfig,
|
||||
readConfigFileSnapshot,
|
||||
writeConfigFile,
|
||||
@@ -20,16 +20,16 @@ import {
|
||||
printWizardHeader,
|
||||
} from "./onboard-helpers.js";
|
||||
|
||||
function resolveMode(cfg: ClawdisConfig): "local" | "remote" {
|
||||
function resolveMode(cfg: ClawdbotConfig): "local" | "remote" {
|
||||
return cfg.gateway?.mode === "remote" ? "remote" : "local";
|
||||
}
|
||||
|
||||
export async function doctorCommand(runtime: RuntimeEnv = defaultRuntime) {
|
||||
printWizardHeader(runtime);
|
||||
intro("Clawdis doctor");
|
||||
intro("Clawdbot doctor");
|
||||
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
let cfg: ClawdisConfig = snapshot.valid ? snapshot.config : {};
|
||||
let cfg: ClawdbotConfig = snapshot.valid ? snapshot.config : {};
|
||||
if (
|
||||
snapshot.exists &&
|
||||
!snapshot.valid &&
|
||||
@@ -130,7 +130,7 @@ export async function doctorCommand(runtime: RuntimeEnv = defaultRuntime) {
|
||||
|
||||
cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
|
||||
await writeConfigFile(cfg);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
|
||||
outro("Doctor complete.");
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ describe("getHealthSnapshot", () => {
|
||||
});
|
||||
|
||||
it("treats telegram.tokenFile as configured", async () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdis-health-"));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-health-"));
|
||||
const tokenFile = path.join(tmpDir, "telegram-token");
|
||||
fs.writeFileSync(tokenFile, "t-file\n", "utf-8");
|
||||
testConfig = { telegram: { tokenFile } };
|
||||
|
||||
@@ -125,7 +125,7 @@ export async function healthCommand(
|
||||
runtime.log(
|
||||
summary.web.linked
|
||||
? `Web: linked (auth age ${summary.web.authAgeMs ? `${Math.round(summary.web.authAgeMs / 60000)}m` : "unknown"})`
|
||||
: "Web: not linked (run clawdis login)",
|
||||
: "Web: not linked (run clawdbot login)",
|
||||
);
|
||||
if (summary.web.linked) {
|
||||
logWebSelfId(runtime, true);
|
||||
|
||||
@@ -4,8 +4,8 @@ import path from "node:path";
|
||||
import type { OAuthCredentials, OAuthProvider } from "@mariozechner/pi-ai";
|
||||
import { discoverAuthStorage } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
import { resolveClawdisAgentDir } from "../agents/agent-paths.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import { resolveClawdbotAgentDir } from "../agents/agent-paths.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { CONFIG_DIR } from "../utils.js";
|
||||
|
||||
export async function writeOAuthCredentials(
|
||||
@@ -29,12 +29,12 @@ export async function writeOAuthCredentials(
|
||||
}
|
||||
|
||||
export async function setAnthropicApiKey(key: string) {
|
||||
const agentDir = resolveClawdisAgentDir();
|
||||
const agentDir = resolveClawdbotAgentDir();
|
||||
const authStorage = discoverAuthStorage(agentDir);
|
||||
authStorage.set("anthropic", { type: "api_key", key });
|
||||
}
|
||||
|
||||
export function applyMinimaxConfig(cfg: ClawdisConfig): ClawdisConfig {
|
||||
export function applyMinimaxConfig(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
const allowed = new Set(cfg.agent?.allowedModels ?? []);
|
||||
allowed.add("anthropic/claude-opus-4-5");
|
||||
allowed.add("lmstudio/minimax-m2.1-gs32");
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
DEFAULT_AGENT_WORKSPACE_DIR,
|
||||
ensureAgentWorkspace,
|
||||
} from "../agents/workspace.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDIS } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { CONFIG_PATH_CLAWDBOT } from "../config/config.js";
|
||||
import { resolveSessionTranscriptsDir } from "../config/sessions.js";
|
||||
import { callGateway } from "../gateway/call.js";
|
||||
import { normalizeControlUiBasePath } from "../gateway/control-ui.js";
|
||||
@@ -29,7 +29,7 @@ export function guardCancel<T>(value: T, runtime: RuntimeEnv): T {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function summarizeExistingConfig(config: ClawdisConfig): string {
|
||||
export function summarizeExistingConfig(config: ClawdbotConfig): string {
|
||||
const rows: string[] = [];
|
||||
if (config.agent?.workspace)
|
||||
rows.push(`workspace: ${config.agent.workspace}`);
|
||||
@@ -64,9 +64,9 @@ export function printWizardHeader(runtime: RuntimeEnv) {
|
||||
}
|
||||
|
||||
export function applyWizardMetadata(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
params: { command: string; mode: "local" | "remote" },
|
||||
): ClawdisConfig {
|
||||
): ClawdbotConfig {
|
||||
const commit =
|
||||
process.env.GIT_COMMIT?.trim() || process.env.GIT_SHA?.trim() || undefined;
|
||||
return {
|
||||
@@ -145,7 +145,7 @@ export async function handleReset(
|
||||
workspaceDir: string,
|
||||
runtime: RuntimeEnv,
|
||||
) {
|
||||
await moveToTrash(CONFIG_PATH_CLAWDIS, runtime);
|
||||
await moveToTrash(CONFIG_PATH_CLAWDBOT, runtime);
|
||||
if (scope === "config") return;
|
||||
await moveToTrash(path.join(CONFIG_DIR, "credentials"), runtime);
|
||||
await moveToTrash(resolveSessionTranscriptsDir(), runtime);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
type ClawdisConfig,
|
||||
CONFIG_PATH_CLAWDIS,
|
||||
type ClawdbotConfig,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
readConfigFileSnapshot,
|
||||
resolveGatewayPort,
|
||||
writeConfigFile,
|
||||
@@ -32,7 +32,7 @@ export async function runNonInteractiveOnboarding(
|
||||
runtime: RuntimeEnv = defaultRuntime,
|
||||
) {
|
||||
const snapshot = await readConfigFileSnapshot();
|
||||
const baseConfig: ClawdisConfig = snapshot.valid ? snapshot.config : {};
|
||||
const baseConfig: ClawdbotConfig = snapshot.valid ? snapshot.config : {};
|
||||
const mode: OnboardMode = opts.mode ?? "local";
|
||||
|
||||
if (mode === "remote") {
|
||||
@@ -43,7 +43,7 @@ export async function runNonInteractiveOnboarding(
|
||||
return;
|
||||
}
|
||||
|
||||
let nextConfig: ClawdisConfig = {
|
||||
let nextConfig: ClawdbotConfig = {
|
||||
...baseConfig,
|
||||
gateway: {
|
||||
...baseConfig.gateway,
|
||||
@@ -56,7 +56,7 @@ export async function runNonInteractiveOnboarding(
|
||||
};
|
||||
nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode });
|
||||
await writeConfigFile(nextConfig);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
|
||||
const payload = {
|
||||
mode,
|
||||
@@ -76,7 +76,7 @@ export async function runNonInteractiveOnboarding(
|
||||
(opts.workspace ?? baseConfig.agent?.workspace ?? DEFAULT_WORKSPACE).trim(),
|
||||
);
|
||||
|
||||
let nextConfig: ClawdisConfig = {
|
||||
let nextConfig: ClawdbotConfig = {
|
||||
...baseConfig,
|
||||
agent: {
|
||||
...baseConfig.agent,
|
||||
@@ -200,7 +200,7 @@ export async function runNonInteractiveOnboarding(
|
||||
|
||||
nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode });
|
||||
await writeConfigFile(nextConfig);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`);
|
||||
await ensureWorkspaceAndSessions(workspaceDir, runtime);
|
||||
|
||||
if (opts.installDaemon) {
|
||||
@@ -212,8 +212,8 @@ export async function runNonInteractiveOnboarding(
|
||||
await resolveGatewayProgramArguments({ port, dev: devMode });
|
||||
const environment: Record<string, string | undefined> = {
|
||||
PATH: process.env.PATH,
|
||||
CLAWDIS_GATEWAY_TOKEN: gatewayToken,
|
||||
CLAWDIS_LAUNCHD_LABEL:
|
||||
CLAWDBOT_GATEWAY_TOKEN: gatewayToken,
|
||||
CLAWDBOT_LAUNCHD_LABEL:
|
||||
process.platform === "darwin" ? GATEWAY_LAUNCH_AGENT_LABEL : undefined,
|
||||
};
|
||||
await service.install({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { loginWeb } from "../provider-web.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { normalizeE164 } from "../utils.js";
|
||||
@@ -64,11 +64,11 @@ async function noteDiscordTokenHelp(prompter: WizardPrompter): Promise<void> {
|
||||
}
|
||||
|
||||
function buildSlackManifest(botName: string) {
|
||||
const safeName = botName.trim() || "Clawdis";
|
||||
const safeName = botName.trim() || "Clawdbot";
|
||||
const manifest = {
|
||||
display_information: {
|
||||
name: safeName,
|
||||
description: `${safeName} connector for Clawdis`,
|
||||
description: `${safeName} connector for Clawdbot`,
|
||||
},
|
||||
features: {
|
||||
bot_user: {
|
||||
@@ -82,7 +82,7 @@ function buildSlackManifest(botName: string) {
|
||||
slash_commands: [
|
||||
{
|
||||
command: "/clawd",
|
||||
description: "Send a message to Clawdis",
|
||||
description: "Send a message to Clawdbot",
|
||||
should_escape: false,
|
||||
},
|
||||
],
|
||||
@@ -153,7 +153,7 @@ async function noteSlackTokenHelp(
|
||||
);
|
||||
}
|
||||
|
||||
function setWhatsAppAllowFrom(cfg: ClawdisConfig, allowFrom?: string[]) {
|
||||
function setWhatsAppAllowFrom(cfg: ClawdbotConfig, allowFrom?: string[]) {
|
||||
return {
|
||||
...cfg,
|
||||
whatsapp: {
|
||||
@@ -164,10 +164,10 @@ function setWhatsAppAllowFrom(cfg: ClawdisConfig, allowFrom?: string[]) {
|
||||
}
|
||||
|
||||
async function promptWhatsAppAllowFrom(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
_runtime: RuntimeEnv,
|
||||
prompter: WizardPrompter,
|
||||
): Promise<ClawdisConfig> {
|
||||
): Promise<ClawdbotConfig> {
|
||||
const existingAllowFrom = cfg.whatsapp?.allowFrom ?? [];
|
||||
const existingLabel =
|
||||
existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
|
||||
@@ -236,11 +236,11 @@ async function promptWhatsAppAllowFrom(
|
||||
}
|
||||
|
||||
export async function setupProviders(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
runtime: RuntimeEnv,
|
||||
prompter: WizardPrompter,
|
||||
options?: { allowDisable?: boolean; allowSignalInstall?: boolean },
|
||||
): Promise<ClawdisConfig> {
|
||||
): Promise<ClawdbotConfig> {
|
||||
const whatsappLinked = await detectWhatsAppLinked();
|
||||
const telegramEnv = Boolean(process.env.TELEGRAM_BOT_TOKEN?.trim());
|
||||
const discordEnv = Boolean(process.env.DISCORD_BOT_TOKEN?.trim());
|
||||
@@ -330,7 +330,7 @@ export async function setupProviders(
|
||||
await prompter.note(
|
||||
[
|
||||
"Scan the QR with WhatsApp on your phone.",
|
||||
"Credentials are stored under ~/.clawdis/credentials/ for future runs.",
|
||||
"Credentials are stored under ~/.clawdbot/credentials/ for future runs.",
|
||||
].join("\n"),
|
||||
"WhatsApp linking",
|
||||
);
|
||||
@@ -349,7 +349,7 @@ export async function setupProviders(
|
||||
}
|
||||
} else if (!whatsappLinked) {
|
||||
await prompter.note(
|
||||
"Run `clawdis login` later to link WhatsApp.",
|
||||
"Run `clawdbot login` later to link WhatsApp.",
|
||||
"WhatsApp",
|
||||
);
|
||||
}
|
||||
@@ -483,7 +483,7 @@ export async function setupProviders(
|
||||
const slackBotName = String(
|
||||
await prompter.text({
|
||||
message: "Slack bot display name (used for manifest)",
|
||||
initialValue: "Clawdis",
|
||||
initialValue: "Clawdbot",
|
||||
}),
|
||||
).trim();
|
||||
if (!slackConfigured) {
|
||||
@@ -641,9 +641,9 @@ export async function setupProviders(
|
||||
|
||||
await prompter.note(
|
||||
[
|
||||
'Link device with: signal-cli link -n "Clawdis"',
|
||||
'Link device with: signal-cli link -n "Clawdbot"',
|
||||
"Scan QR in Signal → Linked Devices",
|
||||
"Then run: clawdis gateway call providers.status --params '{\"probe\":true}'",
|
||||
"Then run: clawdbot gateway call providers.status --params '{\"probe\":true}'",
|
||||
].join("\n"),
|
||||
"Signal next steps",
|
||||
);
|
||||
@@ -679,7 +679,7 @@ export async function setupProviders(
|
||||
|
||||
await prompter.note(
|
||||
[
|
||||
"Ensure Clawdis has Full Disk Access to Messages DB.",
|
||||
"Ensure Clawdbot has Full Disk Access to Messages DB.",
|
||||
"Grant Automation permission for Messages when prompted.",
|
||||
"List chats with: imsg chats --limit 20",
|
||||
].join("\n"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { GatewayBonjourBeacon } from "../infra/bonjour-discovery.js";
|
||||
import { discoverGatewayBeacons } from "../infra/bonjour-discovery.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
@@ -25,9 +25,9 @@ function ensureWsUrl(value: string): string {
|
||||
}
|
||||
|
||||
export async function promptRemoteGatewayConfig(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
prompter: WizardPrompter,
|
||||
): Promise<ClawdisConfig> {
|
||||
): Promise<ClawdbotConfig> {
|
||||
let selectedBeacon: GatewayBonjourBeacon | null = null;
|
||||
let suggestedUrl = cfg.gateway?.remote?.url ?? DEFAULT_GATEWAY_URL;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { installSkill } from "../agents/skills-install.js";
|
||||
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
|
||||
import type { ClawdisConfig } from "../config/config.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { detectBinary, resolveNodeManagerOptions } from "./onboard-helpers.js";
|
||||
@@ -30,10 +30,10 @@ function formatSkillHint(skill: {
|
||||
}
|
||||
|
||||
function upsertSkillEntry(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
skillKey: string,
|
||||
patch: { apiKey?: string },
|
||||
): ClawdisConfig {
|
||||
): ClawdbotConfig {
|
||||
const entries = { ...cfg.skills?.entries };
|
||||
const existing = (entries[skillKey] as { apiKey?: string } | undefined) ?? {};
|
||||
entries[skillKey] = { ...existing, ...patch };
|
||||
@@ -47,11 +47,11 @@ function upsertSkillEntry(
|
||||
}
|
||||
|
||||
export async function setupSkills(
|
||||
cfg: ClawdisConfig,
|
||||
cfg: ClawdbotConfig,
|
||||
workspaceDir: string,
|
||||
runtime: RuntimeEnv,
|
||||
prompter: WizardPrompter,
|
||||
): Promise<ClawdisConfig> {
|
||||
): Promise<ClawdbotConfig> {
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config: cfg });
|
||||
const eligible = report.skills.filter((s) => s.eligible);
|
||||
const missing = report.skills.filter(
|
||||
@@ -109,7 +109,7 @@ export async function setupSkills(
|
||||
options: resolveNodeManagerOptions(),
|
||||
})) as "npm" | "pnpm" | "bun";
|
||||
|
||||
let next: ClawdisConfig = {
|
||||
let next: ClawdbotConfig = {
|
||||
...cfg,
|
||||
skills: {
|
||||
...cfg.skills,
|
||||
@@ -166,7 +166,7 @@ export async function setupSkills(
|
||||
if (result.stderr) runtime.log(result.stderr.trim());
|
||||
else if (result.stdout) runtime.log(result.stdout.trim());
|
||||
runtime.log(
|
||||
"Tip: run `clawdis doctor` to review skills + requirements.",
|
||||
"Tip: run `clawdbot doctor` to review skills + requirements.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -7,20 +7,20 @@ import {
|
||||
DEFAULT_AGENT_WORKSPACE_DIR,
|
||||
ensureAgentWorkspace,
|
||||
} from "../agents/workspace.js";
|
||||
import { type ClawdisConfig, CONFIG_PATH_CLAWDIS } from "../config/config.js";
|
||||
import { type ClawdbotConfig, CONFIG_PATH_CLAWDBOT } from "../config/config.js";
|
||||
import { resolveSessionTranscriptsDir } from "../config/sessions.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
|
||||
async function readConfigFileRaw(): Promise<{
|
||||
exists: boolean;
|
||||
parsed: ClawdisConfig;
|
||||
parsed: ClawdbotConfig;
|
||||
}> {
|
||||
try {
|
||||
const raw = await fs.readFile(CONFIG_PATH_CLAWDIS, "utf-8");
|
||||
const raw = await fs.readFile(CONFIG_PATH_CLAWDBOT, "utf-8");
|
||||
const parsed = JSON5.parse(raw);
|
||||
if (parsed && typeof parsed === "object") {
|
||||
return { exists: true, parsed: parsed as ClawdisConfig };
|
||||
return { exists: true, parsed: parsed as ClawdbotConfig };
|
||||
}
|
||||
return { exists: true, parsed: {} };
|
||||
} catch {
|
||||
@@ -28,10 +28,10 @@ async function readConfigFileRaw(): Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
async function writeConfigFile(cfg: ClawdisConfig) {
|
||||
await fs.mkdir(path.dirname(CONFIG_PATH_CLAWDIS), { recursive: true });
|
||||
async function writeConfigFile(cfg: ClawdbotConfig) {
|
||||
await fs.mkdir(path.dirname(CONFIG_PATH_CLAWDBOT), { recursive: true });
|
||||
const json = JSON.stringify(cfg, null, 2).trimEnd().concat("\n");
|
||||
await fs.writeFile(CONFIG_PATH_CLAWDIS, json, "utf-8");
|
||||
await fs.writeFile(CONFIG_PATH_CLAWDBOT, json, "utf-8");
|
||||
}
|
||||
|
||||
export async function setupCommand(
|
||||
@@ -50,7 +50,7 @@ export async function setupCommand(
|
||||
const workspace =
|
||||
desiredWorkspace ?? agent.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR;
|
||||
|
||||
const next: ClawdisConfig = {
|
||||
const next: ClawdbotConfig = {
|
||||
...cfg,
|
||||
agent: {
|
||||
...agent,
|
||||
@@ -62,11 +62,11 @@ export async function setupCommand(
|
||||
await writeConfigFile(next);
|
||||
runtime.log(
|
||||
!existingRaw.exists
|
||||
? `Wrote ${CONFIG_PATH_CLAWDIS}`
|
||||
: `Updated ${CONFIG_PATH_CLAWDIS} (set agent.workspace)`,
|
||||
? `Wrote ${CONFIG_PATH_CLAWDBOT}`
|
||||
: `Updated ${CONFIG_PATH_CLAWDBOT} (set agent.workspace)`,
|
||||
);
|
||||
} else {
|
||||
runtime.log(`Config OK: ${CONFIG_PATH_CLAWDIS}`);
|
||||
runtime.log(`Config OK: ${CONFIG_PATH_CLAWDBOT}`);
|
||||
}
|
||||
|
||||
const ws = await ensureAgentWorkspace({
|
||||
|
||||
@@ -132,7 +132,7 @@ export async function installSignalCli(
|
||||
"https://api.github.com/repos/AsamK/signal-cli/releases/latest";
|
||||
const response = await fetch(apiUrl, {
|
||||
headers: {
|
||||
"User-Agent": "clawdis",
|
||||
"User-Agent": "clawdbot",
|
||||
Accept: "application/vnd.github+json",
|
||||
},
|
||||
});
|
||||
@@ -158,7 +158,7 @@ export async function installSignalCli(
|
||||
};
|
||||
}
|
||||
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdis-signal-"));
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-signal-"));
|
||||
const archivePath = path.join(tmpDir, assetName);
|
||||
|
||||
runtime.log(`Downloading signal-cli ${version} (${assetName})…`);
|
||||
|
||||
+56
-56
@@ -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();
|
||||
|
||||
+8
-8
@@ -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).',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
+15
-15
@@ -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 {
|
||||
|
||||
+29
-5
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user