@nextclaw/channel-plugin-feishu 0.2.20 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/channel.test.ts +65 -0
- package/src/channel.ts +32 -10
package/package.json
CHANGED
package/src/channel.test.ts
CHANGED
|
@@ -2,11 +2,18 @@ import type { OpenClawConfig } from "./nextclaw-sdk/feishu.js";
|
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
|
|
4
4
|
const probeFeishuMock = vi.hoisted(() => vi.fn());
|
|
5
|
+
const monitorFeishuProviderMock = vi.hoisted(() => vi.fn());
|
|
6
|
+
const stopFeishuMonitorMock = vi.hoisted(() => vi.fn());
|
|
5
7
|
|
|
6
8
|
vi.mock("./probe.js", () => ({
|
|
7
9
|
probeFeishu: probeFeishuMock,
|
|
8
10
|
}));
|
|
9
11
|
|
|
12
|
+
vi.mock("./monitor.js", () => ({
|
|
13
|
+
monitorFeishuProvider: monitorFeishuProviderMock,
|
|
14
|
+
stopFeishuMonitor: stopFeishuMonitorMock,
|
|
15
|
+
}));
|
|
16
|
+
|
|
10
17
|
import { feishuPlugin } from "./channel.js";
|
|
11
18
|
|
|
12
19
|
describe("feishuPlugin.status.probeAccount", () => {
|
|
@@ -45,4 +52,62 @@ describe("feishuPlugin.status.probeAccount", () => {
|
|
|
45
52
|
);
|
|
46
53
|
expect(result).toMatchObject({ ok: true, appId: "cli_main" });
|
|
47
54
|
});
|
|
55
|
+
|
|
56
|
+
it("starts gateway monitor without blocking service startup", async () => {
|
|
57
|
+
let resolveMonitor!: () => void;
|
|
58
|
+
const monitorDone = new Promise<void>((resolve) => {
|
|
59
|
+
resolveMonitor = resolve;
|
|
60
|
+
});
|
|
61
|
+
monitorFeishuProviderMock.mockReturnValueOnce(monitorDone);
|
|
62
|
+
|
|
63
|
+
const abortController = new AbortController();
|
|
64
|
+
const ctx = {
|
|
65
|
+
cfg: {
|
|
66
|
+
channels: {
|
|
67
|
+
feishu: {
|
|
68
|
+
enabled: true,
|
|
69
|
+
appId: "cli_default",
|
|
70
|
+
appSecret: "secret_default",
|
|
71
|
+
connectionMode: "websocket",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
} as OpenClawConfig,
|
|
75
|
+
accountId: "default",
|
|
76
|
+
abortSignal: abortController.signal,
|
|
77
|
+
setStatus: vi.fn(),
|
|
78
|
+
log: {
|
|
79
|
+
info: vi.fn(),
|
|
80
|
+
error: vi.fn(),
|
|
81
|
+
},
|
|
82
|
+
runtime: {
|
|
83
|
+
log: vi.fn(),
|
|
84
|
+
info: vi.fn(),
|
|
85
|
+
warn: vi.fn(),
|
|
86
|
+
error: vi.fn(),
|
|
87
|
+
debug: vi.fn(),
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const timeoutToken = Symbol("timeout");
|
|
92
|
+
const started = await Promise.race([
|
|
93
|
+
feishuPlugin.gateway?.startAccount?.(ctx),
|
|
94
|
+
new Promise<symbol>((resolve) => setTimeout(() => resolve(timeoutToken), 20)),
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
expect(started).not.toBe(timeoutToken);
|
|
98
|
+
expect(monitorFeishuProviderMock).toHaveBeenCalledWith({
|
|
99
|
+
config: ctx.cfg,
|
|
100
|
+
runtime: ctx.runtime,
|
|
101
|
+
abortSignal: ctx.abortSignal,
|
|
102
|
+
accountId: "default",
|
|
103
|
+
});
|
|
104
|
+
expect(ctx.setStatus).toHaveBeenCalledWith({ accountId: "default", port: null });
|
|
105
|
+
expect(typeof (started as { stop?: () => Promise<void> }).stop).toBe("function");
|
|
106
|
+
|
|
107
|
+
abortController.abort();
|
|
108
|
+
resolveMonitor();
|
|
109
|
+
await (started as { stop?: () => Promise<void> }).stop?.();
|
|
110
|
+
|
|
111
|
+
expect(stopFeishuMonitorMock).toHaveBeenCalledWith("default");
|
|
112
|
+
});
|
|
48
113
|
});
|
package/src/channel.ts
CHANGED
|
@@ -109,7 +109,8 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
|
|
109
109
|
},
|
|
110
110
|
agentPrompt: {
|
|
111
111
|
messageToolHints: () => [
|
|
112
|
-
"- Feishu targeting: omit `target`
|
|
112
|
+
"- Feishu targeting: omit `target` only when replying in the current Feishu conversation. For proactive sends from UI/CLI/another channel, pass an explicit target such as `user:open_id` or `chat:chat_id`.",
|
|
113
|
+
"- If the current session is not Feishu, never rely on `channel=feishu` alone; resolve the route first (for example from an existing Feishu session) and then send to that explicit target.",
|
|
113
114
|
"- Feishu supports interactive cards for rich messages.",
|
|
114
115
|
],
|
|
115
116
|
},
|
|
@@ -351,19 +352,40 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
|
|
|
351
352
|
},
|
|
352
353
|
gateway: {
|
|
353
354
|
startAccount: async (ctx) => {
|
|
354
|
-
const { monitorFeishuProvider } = await import("./monitor.js");
|
|
355
|
+
const { monitorFeishuProvider, stopFeishuMonitor } = await import("./monitor.js");
|
|
355
356
|
const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId });
|
|
357
|
+
const accountId = account.accountId;
|
|
356
358
|
const port = account.config?.webhookPort ?? null;
|
|
357
|
-
ctx.setStatus({ accountId
|
|
359
|
+
ctx.setStatus({ accountId, port });
|
|
358
360
|
ctx.log?.info(
|
|
359
|
-
`starting feishu[${
|
|
361
|
+
`starting feishu[${accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
|
|
360
362
|
);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
363
|
+
|
|
364
|
+
// Start the long-running monitor in the background so service startup can continue
|
|
365
|
+
// into ChannelManager.startAll() for other channels.
|
|
366
|
+
const monitorTask = (async () => {
|
|
367
|
+
try {
|
|
368
|
+
await monitorFeishuProvider({
|
|
369
|
+
config: ctx.cfg,
|
|
370
|
+
runtime: ctx.runtime,
|
|
371
|
+
abortSignal: ctx.abortSignal,
|
|
372
|
+
accountId,
|
|
373
|
+
});
|
|
374
|
+
} catch (error) {
|
|
375
|
+
if (ctx.abortSignal?.aborted) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const message = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
379
|
+
ctx.log?.error?.(`feishu[${accountId}]: gateway monitor stopped unexpectedly: ${message}`);
|
|
380
|
+
}
|
|
381
|
+
})();
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
stop: async () => {
|
|
385
|
+
stopFeishuMonitor(accountId);
|
|
386
|
+
await monitorTask;
|
|
387
|
+
},
|
|
388
|
+
};
|
|
367
389
|
},
|
|
368
390
|
},
|
|
369
391
|
};
|