mirror of
https://github.com/hanzoai/bot.git
synced 2026-08-07 13:16:04 +00:00
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.
7.2 KiB
7.2 KiB
summary, read_when, title
| summary | read_when | title | ||
|---|---|---|---|---|
| Plan: one clean plugin SDK + runtime for all messaging connectors |
|
Plugin SDK Refactor |
Plugin SDK + Runtime Refactor Plan
Goal: every messaging connector is a plugin (bundled or external) using one stable API.
No plugin imports from src/** directly. All dependencies go through the SDK or runtime.
Why now
- Current connectors mix patterns: direct core imports, dist-only bridges, and custom helpers.
- This makes upgrades brittle and blocks a clean external plugin surface.
Target architecture (two layers)
1) Plugin SDK (compile-time, stable, publishable)
Scope: types, helpers, and config utilities. No runtime state, no side effects.
Contents (examples):
- Types:
ChannelPlugin, adapters,ChannelMeta,ChannelCapabilities,ChannelDirectoryEntry. - Config helpers:
buildChannelConfigSchema,setAccountEnabledInConfigSection,deleteAccountFromConfigSection,applyAccountNameToChannelSection. - Pairing helpers:
PAIRING_APPROVED_MESSAGE,formatPairingApproveHint. - Onboarding helpers:
promptChannelAccessConfig,addWildcardAllowFrom, onboarding types. - Tool param helpers:
createActionGate,readStringParam,readNumberParam,readReactionParams,jsonResult. - Docs link helper:
formatDocsLink.
Delivery:
- Publish as
hanzo-bot/plugin-sdk(or export from core underhanzo-bot/plugin-sdk). - Semver with explicit stability guarantees.
2) Plugin Runtime (execution surface, injected)
Scope: everything that touches core runtime behavior.
Accessed via HanzoBotPluginApi.runtime so plugins never import src/**.
Proposed surface (minimal but complete):
export type PluginRuntime = {
channel: {
text: {
chunkMarkdownText(text: string, limit: number): string[];
resolveTextChunkLimit(cfg: HanzoBotConfig, channel: string, accountId?: string): number;
hasControlCommand(text: string, cfg: HanzoBotConfig): boolean;
};
reply: {
dispatchReplyWithBufferedBlockDispatcher(params: {
ctx: unknown;
cfg: unknown;
dispatcherOptions: {
deliver: (payload: {
text?: string;
mediaUrls?: string[];
mediaUrl?: string;
}) => void | Promise<void>;
onError?: (err: unknown, info: { kind: string }) => void;
};
}): Promise<void>;
createReplyDispatcherWithTyping?: unknown; // adapter for Teams-style flows
};
routing: {
resolveAgentRoute(params: {
cfg: unknown;
channel: string;
accountId: string;
peer: { kind: RoutePeerKind; id: string };
}): { sessionKey: string; accountId: string };
};
pairing: {
buildPairingReply(params: { channel: string; idLine: string; code: string }): string;
readAllowFromStore(channel: string): Promise<string[]>;
upsertPairingRequest(params: {
channel: string;
id: string;
meta?: { name?: string };
}): Promise<{ code: string; created: boolean }>;
};
media: {
fetchRemoteMedia(params: { url: string }): Promise<{ buffer: Buffer; contentType?: string }>;
saveMediaBuffer(
buffer: Uint8Array,
contentType: string | undefined,
direction: "inbound" | "outbound",
maxBytes: number,
): Promise<{ path: string; contentType?: string }>;
};
mentions: {
buildMentionRegexes(cfg: HanzoBotConfig, agentId?: string): RegExp[];
matchesMentionPatterns(text: string, regexes: RegExp[]): boolean;
};
groups: {
resolveGroupPolicy(
cfg: HanzoBotConfig,
channel: string,
accountId: string,
groupId: string,
): {
allowlistEnabled: boolean;
allowed: boolean;
groupConfig?: unknown;
defaultConfig?: unknown;
};
resolveRequireMention(
cfg: HanzoBotConfig,
channel: string,
accountId: string,
groupId: string,
override?: boolean,
): boolean;
};
debounce: {
createInboundDebouncer<T>(opts: {
debounceMs: number;
buildKey: (v: T) => string | null;
shouldDebounce: (v: T) => boolean;
onFlush: (entries: T[]) => Promise<void>;
onError?: (err: unknown) => void;
}): { push: (v: T) => void; flush: () => Promise<void> };
resolveInboundDebounceMs(cfg: HanzoBotConfig, channel: string): number;
};
commands: {
resolveCommandAuthorizedFromAuthorizers(params: {
useAccessGroups: boolean;
authorizers: Array<{ configured: boolean; allowed: boolean }>;
}): boolean;
};
};
logging: {
shouldLogVerbose(): boolean;
getChildLogger(name: string): PluginLogger;
};
state: {
resolveStateDir(cfg: HanzoBotConfig): string;
};
};
Notes:
- Runtime is the only way to access core behavior.
- SDK is intentionally small and stable.
- Each runtime method maps to an existing core implementation (no duplication).
Migration plan (phased, safe)
Phase 0: scaffolding
- Introduce
hanzo-bot/plugin-sdk. - Add
api.runtimetoHanzoBotPluginApiwith the surface above. - Maintain existing imports during a transition window (deprecation warnings).
Phase 1: bridge cleanup (low risk)
- Replace per-extension
core-bridge.tswithapi.runtime. - Migrate BlueBubbles, Zalo, Zalo Personal first (already close).
- Remove duplicated bridge code.
Phase 2: light direct-import plugins
- Migrate Matrix to SDK + runtime.
- Validate onboarding, directory, group mention logic.
Phase 3: heavy direct-import plugins
- Migrate MS Teams (largest set of runtime helpers).
- Ensure reply/typing semantics match current behavior.
Phase 4: iMessage pluginization
- Move iMessage into
extensions/imessage. - Replace direct core calls with
api.runtime. - Keep config keys, CLI behavior, and docs intact.
Phase 5: enforcement
- Add lint rule / CI check: no
extensions/**imports fromsrc/**. - Add plugin SDK/version compatibility checks (runtime + SDK semver).
Compatibility and versioning
- SDK: semver, published, documented changes.
- Runtime: versioned per core release. Add
api.runtime.version. - Plugins declare a required runtime range (e.g.,
botRuntime: ">=2026.2.0").
Testing strategy
- Adapter-level unit tests (runtime functions exercised with real core implementation).
- Golden tests per plugin: ensure no behavior drift (routing, pairing, allowlist, mention gating).
- A single end-to-end plugin sample used in CI (install + run + smoke).
Open questions
- Where to host SDK types: separate package or core export?
- Runtime type distribution: in SDK (types only) or in core?
- How to expose docs links for bundled vs external plugins?
- Do we allow limited direct core imports for in-repo plugins during transition?
Success criteria
- All channel connectors are plugins using SDK + runtime.
- No
extensions/**imports fromsrc/**. - New connector templates depend only on SDK + runtime.
- External plugins can be developed and updated without core source access.
Related docs: Plugins, Channels, Configuration.