Files
zeekay acaeda400a Eradicate OpenClaw — resolve merge conflicts and finish the rebrand
Three coordinated things:

1. Resolve the committed upstream/main merge conflicts. 94 files were
   sitting with raw <<<<<<< HEAD ... >>>>>>> upstream/main blocks. Every
   conflict had HanzoBot/Bot on HEAD and OpenClaw on upstream. Resolved
   by keeping HEAD everywhere — 540 conflict blocks.

2. Rewrite remaining OpenClaw text refs.
     OPENCLAW   → HANZO_BOT
     OpenClaw   → HanzoBot          (PascalCase type identifiers)
     openclaw   → bot               (codebase convention — botToken,
                                    botUsername etc.; avoids invalid
                                    hyphenated JS identifiers)
     openclaw[_]/openclaw[A-Z] → bot[_]/bot[A-Z]   (compound)
     ai.openclaw.x → ai.hanzo.bot.x  (JVM package path)

3. Delete or rename openclaw-named file paths (116 of them). Dead
   duplicates with a Bot-named canonical from the partial migration:
   deleted. Otherwise renamed.
     ai/openclaw/**           → deleted (ai/hanzo/bot/** canonical)
     Sources/OpenClaw*/       → deleted (Sources/Bot* canonical)
     Tests/OpenClawIPCTests/  → deleted (Tests/BotIPCTests canonical)
     OpenClawKit/             → deleted (BotKit canonical)
     openclaw-tools.*.ts      → deleted (bot-tools.*.ts canonical)
     openclaw-root.ts etc.    → deleted (bot-root.ts canonical)
     types.openclaw.ts        → deleted (types.bot.ts canonical)
     extensions/*/openclaw.plugin.json → renamed hanzo-bot.plugin.json
     docs/start/openclaw.md   → renamed hanzo-bot.md (+ zh-CN)
     docs/assets/openclaw-*.png, whatsapp-openclaw*.jpg → deleted
     scripts/.../openclaw-*   → deleted (bot/hanzo-bot parallels)
     openclaw.mjs             → deleted (hanzo-bot.mjs is package.bin)

Then patched 65 broken JS/TS import strings where hanzo-bot had ended
up inside an import path ('./types.hanzo-bot.js' → './types.bot.js'
etc., since the on-disk filename uses the brand-neutral bot- prefix).

Pre-existing lint debt cleaned up to get oxlint --type-aware to 0/0:
removed the dead i18n test that referenced a path no longer existing;
defined the missing DIDConfig and WalletConfig types in types.base.ts
(they were imported but never declared); dropped unused imports;
collapsed redundant type assertion and a tautological meta.bot lookup
that the openclaw→bot rename made redundant.

The a2ui.bundle.js generated artifact is kept at its pre-rebrand
contents — it gets regenerated by 'pnpm canvas:a2ui:bundle' from
sources I cannot rebuild in this commit; the source side is clean.

Verified: 0 occurrences of openclaw (case-insensitive) in tree source
content, 0 file or directory paths with openclaw in the name; oxlint
--type-aware src test reports 0/0.
2026-05-11 17:56:29 -07:00

181 lines
5.2 KiB
TypeScript

import fs from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
export type MemoryConfig = {
embedding: {
provider: "openai";
model: string;
apiKey: string;
baseUrl?: string;
dimensions?: number;
};
dbPath?: string;
autoCapture?: boolean;
autoRecall?: boolean;
captureMaxChars?: number;
};
export const MEMORY_CATEGORIES = ["preference", "fact", "decision", "entity", "other"] as const;
export type MemoryCategory = (typeof MEMORY_CATEGORIES)[number];
const DEFAULT_MODEL = "text-embedding-3-small";
export const DEFAULT_CAPTURE_MAX_CHARS = 500;
const LEGACY_STATE_DIRS: string[] = [];
function resolveDefaultDbPath(): string {
const home = homedir();
const preferred = join(home, ".bot", "memory", "lancedb");
try {
if (fs.existsSync(preferred)) {
return preferred;
}
} catch {
// best-effort
}
for (const legacy of LEGACY_STATE_DIRS) {
const candidate = join(home, legacy, "memory", "lancedb");
try {
if (fs.existsSync(candidate)) {
return candidate;
}
} catch {
// best-effort
}
}
return preferred;
}
const DEFAULT_DB_PATH = resolveDefaultDbPath();
const EMBEDDING_DIMENSIONS: Record<string, number> = {
"text-embedding-3-small": 1536,
"text-embedding-3-large": 3072,
};
function assertAllowedKeys(value: Record<string, unknown>, allowed: string[], label: string) {
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
if (unknown.length === 0) {
return;
}
throw new Error(`${label} has unknown keys: ${unknown.join(", ")}`);
}
export function vectorDimsForModel(model: string): number {
const dims = EMBEDDING_DIMENSIONS[model];
if (!dims) {
throw new Error(`Unsupported embedding model: ${model}`);
}
return dims;
}
function resolveEnvVars(value: string): string {
return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
const envValue = process.env[envVar];
if (!envValue) {
throw new Error(`Environment variable ${envVar} is not set`);
}
return envValue;
});
}
function resolveEmbeddingModel(embedding: Record<string, unknown>): string {
const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL;
if (typeof embedding.dimensions !== "number") {
vectorDimsForModel(model);
}
return model;
}
export const memoryConfigSchema = {
parse(value: unknown): MemoryConfig {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new Error("memory config required");
}
const cfg = value as Record<string, unknown>;
assertAllowedKeys(
cfg,
["embedding", "dbPath", "autoCapture", "autoRecall", "captureMaxChars"],
"memory config",
);
const embedding = cfg.embedding as Record<string, unknown> | undefined;
if (!embedding || typeof embedding.apiKey !== "string") {
throw new Error("embedding.apiKey is required");
}
assertAllowedKeys(embedding, ["apiKey", "model", "baseUrl", "dimensions"], "embedding config");
const model = resolveEmbeddingModel(embedding);
const captureMaxChars =
typeof cfg.captureMaxChars === "number" ? Math.floor(cfg.captureMaxChars) : undefined;
if (
typeof captureMaxChars === "number" &&
(captureMaxChars < 100 || captureMaxChars > 10_000)
) {
throw new Error("captureMaxChars must be between 100 and 10000");
}
return {
embedding: {
provider: "openai",
model,
apiKey: resolveEnvVars(embedding.apiKey),
baseUrl:
typeof embedding.baseUrl === "string" ? resolveEnvVars(embedding.baseUrl) : undefined,
dimensions: typeof embedding.dimensions === "number" ? embedding.dimensions : undefined,
},
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH,
autoCapture: cfg.autoCapture === true,
autoRecall: cfg.autoRecall !== false,
captureMaxChars: captureMaxChars ?? DEFAULT_CAPTURE_MAX_CHARS,
};
},
uiHints: {
"embedding.apiKey": {
label: "OpenAI API Key",
sensitive: true,
placeholder: "sk-proj-...",
help: "API key for OpenAI embeddings (or use ${OPENAI_API_KEY})",
},
"embedding.baseUrl": {
label: "Base URL",
placeholder: "https://api.openai.com/v1",
help: "Base URL for compatible providers (e.g. http://localhost:11434/v1)",
advanced: true,
},
"embedding.dimensions": {
label: "Dimensions",
placeholder: "1536",
help: "Vector dimensions for custom models (required for non-standard models)",
advanced: true,
},
"embedding.model": {
label: "Embedding Model",
placeholder: DEFAULT_MODEL,
help: "OpenAI embedding model to use",
},
dbPath: {
label: "Database Path",
placeholder: "~/.bot/memory/lancedb",
advanced: true,
},
autoCapture: {
label: "Auto-Capture",
help: "Automatically capture important information from conversations",
},
autoRecall: {
label: "Auto-Recall",
help: "Automatically inject relevant memories into context",
},
captureMaxChars: {
label: "Capture Max Chars",
help: "Maximum message length eligible for auto-capture",
advanced: true,
placeholder: String(DEFAULT_CAPTURE_MAX_CHARS),
},
},
};