refactor: adopt chat plugin builder in tlon

This commit is contained in:
Peter Steinberger
2026-03-22 21:57:07 +00:00
parent cb4ae1a56d
commit 3a68e87f84

View File

@@ -2,7 +2,7 @@ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import { createHybridChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createChatChannelPlugin, type ChannelPlugin } from "openclaw/plugin-sdk/core";
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
import { createRuntimeOutboundDelegates } from "openclaw/plugin-sdk/outbound-runtime";
import { buildComputedAccountStatusSnapshot } from "../api.js";
@@ -55,57 +55,116 @@ const tlonConfigAdapter = createHybridChannelConfigAdapter({
allowFrom.map((entry) => normalizeShip(String(entry))).filter(Boolean),
});
export const tlonPlugin: ChannelPlugin = {
id: TLON_CHANNEL_ID,
meta: {
export const tlonPlugin = createChatChannelPlugin({
base: {
id: TLON_CHANNEL_ID,
label: "Tlon",
selectionLabel: "Tlon (Urbit)",
docsPath: "/channels/tlon",
docsLabel: "tlon",
blurb: "Decentralized messaging on Urbit",
aliases: ["urbit"],
order: 90,
},
capabilities: {
chatTypes: ["direct", "group", "thread"],
media: true,
reply: true,
threads: true,
},
setup: tlonSetupAdapter,
setupWizard: tlonSetupWizardProxy,
reload: { configPrefixes: ["channels.tlon"] },
configSchema: tlonChannelConfigSchema,
config: {
...tlonConfigAdapter,
isConfigured: (account) => account.configured,
describeAccount: (account) =>
describeAccountSnapshot({
account,
configured: account.configured,
extra: {
ship: account.ship,
url: account.url,
},
}),
},
messaging: {
normalizeTarget: (target) => {
const parsed = parseTlonTarget(target);
if (!parsed) {
return target.trim();
}
if (parsed.kind === "dm") {
return parsed.ship;
}
return parsed.nest;
meta: {
id: TLON_CHANNEL_ID,
label: "Tlon",
selectionLabel: "Tlon (Urbit)",
docsPath: "/channels/tlon",
docsLabel: "tlon",
blurb: "Decentralized messaging on Urbit",
aliases: ["urbit"],
order: 90,
},
targetResolver: {
looksLikeId: (target) => Boolean(parseTlonTarget(target)),
hint: formatTargetHint(),
capabilities: {
chatTypes: ["direct", "group", "thread"],
media: true,
reply: true,
threads: true,
},
setup: tlonSetupAdapter,
setupWizard: tlonSetupWizardProxy,
reload: { configPrefixes: ["channels.tlon"] },
configSchema: tlonChannelConfigSchema,
config: {
...tlonConfigAdapter,
isConfigured: (account) => account.configured,
describeAccount: (account) =>
describeAccountSnapshot({
account,
configured: account.configured,
extra: {
ship: account.ship,
url: account.url,
},
}),
},
messaging: {
normalizeTarget: (target) => {
const parsed = parseTlonTarget(target);
if (!parsed) {
return target.trim();
}
if (parsed.kind === "dm") {
return parsed.ship;
}
return parsed.nest;
},
targetResolver: {
looksLikeId: (target) => Boolean(parseTlonTarget(target)),
hint: formatTargetHint(),
},
resolveOutboundSessionRoute: (params) => resolveTlonOutboundSessionRoute(params),
},
status: {
defaultRuntime: {
accountId: DEFAULT_ACCOUNT_ID,
running: false,
lastStartAt: null,
lastStopAt: null,
lastError: null,
},
collectStatusIssues: (accounts) => {
return accounts.flatMap((account) => {
if (!account.configured) {
return [
{
channel: TLON_CHANNEL_ID,
accountId: account.accountId,
kind: "config",
message: "Account not configured (missing ship, code, or url)",
},
];
}
return [];
});
},
buildChannelSummary: ({ snapshot }) => {
const s = snapshot as { configured?: boolean; ship?: string; url?: string };
return {
configured: s.configured ?? false,
ship: s.ship ?? null,
url: s.url ?? null,
};
},
probeAccount: async ({ account }) => {
if (!account.configured || !account.ship || !account.url || !account.code) {
return { ok: false, error: "Not configured" };
}
return await (await loadTlonChannelRuntime()).probeTlonAccount(account as never);
},
buildAccountSnapshot: ({ account, runtime, probe }) =>
buildComputedAccountStatusSnapshot(
{
accountId: account.accountId,
name: account.name ?? undefined,
enabled: account.enabled,
configured: account.configured,
runtime,
probe,
},
{
ship: account.ship,
url: account.url,
},
),
},
gateway: {
startAccount: async (ctx) =>
await (await loadTlonChannelRuntime()).startTlonGatewayAccount(ctx),
},
resolveOutboundSessionRoute: (params) => resolveTlonOutboundSessionRoute(params),
},
outbound: {
deliveryMode: "direct",
@@ -117,61 +176,4 @@ export const tlonPlugin: ChannelPlugin = {
sendMedia: { resolve: (runtime) => runtime.tlonRuntimeOutbound.sendMedia },
}),
},
status: {
defaultRuntime: {
accountId: "default",
running: false,
lastStartAt: null,
lastStopAt: null,
lastError: null,
},
collectStatusIssues: (accounts) => {
return accounts.flatMap((account) => {
if (!account.configured) {
return [
{
channel: TLON_CHANNEL_ID,
accountId: account.accountId,
kind: "config",
message: "Account not configured (missing ship, code, or url)",
},
];
}
return [];
});
},
buildChannelSummary: ({ snapshot }) => {
const s = snapshot as { configured?: boolean; ship?: string; url?: string };
return {
configured: s.configured ?? false,
ship: s.ship ?? null,
url: s.url ?? null,
};
},
probeAccount: async ({ account }) => {
if (!account.configured || !account.ship || !account.url || !account.code) {
return { ok: false, error: "Not configured" };
}
return await (await loadTlonChannelRuntime()).probeTlonAccount(account as never);
},
buildAccountSnapshot: ({ account, runtime, probe }) =>
buildComputedAccountStatusSnapshot(
{
accountId: account.accountId,
name: account.name,
enabled: account.enabled,
configured: account.configured,
runtime,
probe,
},
{
ship: account.ship,
url: account.url,
},
),
},
gateway: {
startAccount: async (ctx) =>
await (await loadTlonChannelRuntime()).startTlonGatewayAccount(ctx),
},
};
});