Skip to content

Commit 0affaf1

Browse files
committed
refactor: narrow bundled channel entry surfaces
1 parent f42a06b commit 0affaf1

15 files changed

Lines changed: 75 additions & 34 deletions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Keep bundled channel entry imports narrow so bootstrap/discovery paths do
2+
// not drag IRC runtime/send/monitor surfaces into lightweight plugin loads.
3+
export { ircPlugin } from "./src/channel.js";

extensions/irc/index.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { describe, expect, it } from "vitest";
2+
import entry from "./index.js";
3+
import setupEntry from "./setup-entry.js";
4+
5+
describe("irc bundled entries", () => {
6+
it("loads the channel plugin without importing the broad api barrel", () => {
7+
const plugin = entry.loadChannelPlugin();
8+
expect(plugin.id).toBe("irc");
9+
});
10+
11+
it("loads the setup plugin without importing the broad api barrel", () => {
12+
const plugin = setupEntry.loadSetupPlugin();
13+
expect(plugin.id).toBe("irc");
14+
});
15+
});

extensions/irc/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ export default defineBundledChannelEntry({
66
description: "IRC channel plugin",
77
importMetaUrl: import.meta.url,
88
plugin: {
9-
specifier: "./api.js",
9+
specifier: "./channel-plugin-api.js",
1010
exportName: "ircPlugin",
1111
},
1212
runtime: {
13-
specifier: "./api.js",
13+
specifier: "./runtime-api.js",
1414
exportName: "setIrcRuntime",
1515
},
1616
});

extensions/irc/runtime-api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Keep the bundled runtime entry narrow so generic runtime activation does not
2+
// import the broad IRC API barrel just to install runtime state.
3+
export { setIrcRuntime } from "./src/runtime.js";

extensions/irc/setup-entry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
33
export default defineBundledChannelSetupEntry({
44
importMetaUrl: import.meta.url,
55
plugin: {
6-
specifier: "./api.js",
6+
specifier: "./channel-plugin-api.js",
77
exportName: "ircPlugin",
88
},
99
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Runtime-only IRC helpers for lazy chat plugin hooks.
2+
// Keeping this boundary separate keeps bundled entry loads off monitor/send.
3+
export { monitorIrcProvider } from "./monitor.js";
4+
export { sendMessageIrc } from "./send.js";

extensions/irc/src/channel.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import {
3737
} from "./channel-api.js";
3838
import { IrcChannelConfigSchema } from "./config-schema.js";
3939
import { collectIrcMutableAllowlistWarnings } from "./doctor.js";
40-
import { monitorIrcProvider } from "./monitor.js";
4140
import {
4241
normalizeIrcMessagingTarget,
4342
looksLikeIrcTargetId,
@@ -48,7 +47,6 @@ import { resolveIrcGroupMatch, resolveIrcRequireMention } from "./policy.js";
4847
import { probeIrc } from "./probe.js";
4948
import { getIrcRuntime } from "./runtime.js";
5049
import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./secret-contract.js";
51-
import { sendMessageIrc } from "./send.js";
5250
import { ircSetupAdapter } from "./setup-core.js";
5351
import { ircSetupWizard } from "./setup-surface.js";
5452
import type { CoreConfig, IrcProbe } from "./types.js";
@@ -66,6 +64,15 @@ const meta = {
6664
markdownCapable: true,
6765
};
6866

67+
type IrcChannelRuntimeModule = typeof import("./channel-runtime.js");
68+
69+
let ircChannelRuntimePromise: Promise<IrcChannelRuntimeModule> | undefined;
70+
71+
async function loadIrcChannelRuntime(): Promise<IrcChannelRuntimeModule> {
72+
ircChannelRuntimePromise ??= import("./channel-runtime.js");
73+
return await ircChannelRuntimePromise;
74+
}
75+
6976
function normalizePairingTarget(raw: string): string {
7077
const normalized = normalizeIrcAllowEntry(raw);
7178
if (!normalized) {
@@ -325,6 +332,7 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = createChat
325332
ctx.log?.info(
326333
`[${account.accountId}] starting IRC provider (${account.host}:${account.port}${account.tls ? " tls" : ""})`,
327334
);
335+
const { monitorIrcProvider } = await loadIrcChannelRuntime();
328336
await runStoppablePassiveMonitor({
329337
abortSignal: ctx.abortSignal,
330338
start: async () =>
@@ -349,6 +357,7 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = createChat
349357
if (!target) {
350358
throw new Error(`invalid IRC pairing id: ${id}`);
351359
}
360+
const { sendMessageIrc } = await loadIrcChannelRuntime();
352361
await sendMessageIrc(target, message);
353362
},
354363
},
@@ -367,18 +376,22 @@ export const ircPlugin: ChannelPlugin<ResolvedIrcAccount, IrcProbe> = createChat
367376
},
368377
attachedResults: {
369378
channel: "irc",
370-
sendText: async ({ cfg, to, text, accountId, replyToId }) =>
371-
await sendMessageIrc(to, text, {
379+
sendText: async ({ cfg, to, text, accountId, replyToId }) => {
380+
const { sendMessageIrc } = await loadIrcChannelRuntime();
381+
return await sendMessageIrc(to, text, {
372382
cfg: cfg as CoreConfig,
373383
accountId: accountId ?? undefined,
374384
replyTo: replyToId ?? undefined,
375-
}),
376-
sendMedia: async ({ cfg, to, text, mediaUrl, accountId, replyToId }) =>
377-
await sendMessageIrc(to, mediaUrl ? `${text}\n\nAttachment: ${mediaUrl}` : text, {
385+
});
386+
},
387+
sendMedia: async ({ cfg, to, text, mediaUrl, accountId, replyToId }) => {
388+
const { sendMessageIrc } = await loadIrcChannelRuntime();
389+
return await sendMessageIrc(to, mediaUrl ? `${text}\n\nAttachment: ${mediaUrl}` : text, {
378390
cfg: cfg as CoreConfig,
379391
accountId: accountId ?? undefined,
380392
replyTo: replyToId ?? undefined,
381-
}),
393+
});
394+
},
382395
},
383396
},
384397
});

extensions/irc/src/setup.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ import type { CoreConfig } from "./types.js";
2828

2929
const hoisted = vi.hoisted(() => ({
3030
monitorIrcProvider: vi.fn(),
31+
sendMessageIrc: vi.fn(),
3132
}));
3233

33-
vi.mock("./monitor.js", async () => {
34-
const actual = await vi.importActual<typeof import("./monitor.js")>("./monitor.js");
34+
vi.mock("./channel-runtime.js", () => {
3535
return {
36-
...actual,
3736
monitorIrcProvider: hoisted.monitorIrcProvider,
37+
sendMessageIrc: hoisted.sendMessageIrc,
3838
};
3939
});
4040

extensions/lobster/src/lobster-taskflow.test.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ type BoundTaskFlow = ReturnType<
77
NonNullable<OpenClawPluginApi["runtime"]>["taskFlow"]["bindSession"]
88
>;
99

10-
function expectManagedFlowFailure(
11-
result: Awaited<ReturnType<typeof runManagedLobsterFlow | typeof resumeManagedLobsterFlow>>,
12-
) {
13-
expect(result.ok).toBe(false);
14-
if (result.ok) {
15-
throw new Error("Expected managed Lobster flow to fail");
16-
}
17-
return result;
18-
}
19-
2010
function createFakeTaskFlow(overrides?: Partial<BoundTaskFlow>) {
2111
const baseFlow = {
2212
flowId: "flow-1",
@@ -170,8 +160,11 @@ describe("runManagedLobsterFlow", () => {
170160
goal: "Run Lobster workflow",
171161
});
172162

173-
const failure = expectManagedFlowFailure(result);
174-
expect(failure.error.message).toBe("boom");
163+
expect(result.ok).toBe(false);
164+
if (result.ok) {
165+
throw new Error("expected managed Lobster flow to fail");
166+
}
167+
expect(result.error.message).toBe("boom");
175168
expect(taskFlow.fail).toHaveBeenCalledWith({
176169
flowId: "flow-1",
177170
expectedRevision: 1,
@@ -198,8 +191,11 @@ describe("runManagedLobsterFlow", () => {
198191
goal: "Run Lobster workflow",
199192
});
200193

201-
const failure = expectManagedFlowFailure(result);
202-
expect(failure.error.message).toBe("crashed");
194+
expect(result.ok).toBe(false);
195+
if (result.ok) {
196+
throw new Error("expected managed Lobster flow to fail");
197+
}
198+
expect(result.error.message).toBe("crashed");
203199
expect(taskFlow.fail).toHaveBeenCalledWith({
204200
flowId: "flow-1",
205201
expectedRevision: 1,
@@ -274,8 +270,11 @@ describe("resumeManagedLobsterFlow", () => {
274270
},
275271
});
276272

277-
const failure = expectManagedFlowFailure(result);
278-
expect(failure.error.message).toMatch(/revision_conflict/);
273+
expect(result.ok).toBe(false);
274+
if (result.ok) {
275+
throw new Error("expected resumed Lobster flow to fail");
276+
}
277+
expect(result.error.message).toMatch(/revision_conflict/);
279278
expect(runner.run).not.toHaveBeenCalled();
280279
});
281280

extensions/lobster/src/lobster-taskflow.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ function toJsonLike(value: unknown, seen = new WeakSet<object>()): JsonLike {
101101
seen.delete(value);
102102
return jsonObject;
103103
}
104-
default:
105-
return null;
106104
}
107105
return null;
108106
}

0 commit comments

Comments
 (0)