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");
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
97
src/agents/sandbox-create-args.test.ts
Normal file
97
src/agents/sandbox-create-args.test.ts
Normal file
@@ -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"]),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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[];
|
||||
},
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user