@cjhyy/code-shell-chat 0.9.17 → 0.9.19

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.
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Two properties matter more than they look:
11
11
  *
12
- * - Leaving is decided here, not by the host and never by a model. /mimi has
12
+ * - Leaving is decided by the host, never by a model. /mimi has
13
13
  * to work when the worker is wedged or the bound Session is unreachable,
14
14
  * otherwise a user can be stuck inside a Session with no way out.
15
15
  * - The reply for an accepted message is not this middleware's job. A Session
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Two properties matter more than they look:
11
11
  *
12
- * - Leaving is decided here, not by the host and never by a model. /mimi has
12
+ * - Leaving is decided by the host, never by a model. /mimi has
13
13
  * to work when the worker is wedged or the bound Session is unreachable,
14
14
  * otherwise a user can be stuck inside a Session with no way out.
15
15
  * - The reply for an accepted message is not this middleware's job. A Session
@@ -26,28 +26,23 @@ function isAddressable(message) {
26
26
  return Boolean(message.target?.trim() && message.senderId?.trim());
27
27
  }
28
28
  export function createBoundSessionChat(options) {
29
- const isDirectMessage = options.isDirectMessage ?? (() => true);
29
+ const isDirectMessage = options.isDirectMessage ?? ((message) => message.isDirectMessage === true);
30
30
  return async ({ message, reply }, next) => {
31
31
  if (!message.text.trim() && !message.attachments?.length)
32
32
  return next();
33
33
  if (!isAddressable(message))
34
34
  return next();
35
- let disposition;
36
- try {
37
- disposition = await options.desktop.routeBoundSessionMessage({
38
- channel: message.channel,
39
- target: message.target,
40
- senderId: message.senderId,
41
- ...(message.messageId ? { messageId: message.messageId } : {}),
42
- text: message.text,
43
- isDirectMessage: isDirectMessage(message),
44
- });
45
- }
46
- catch {
47
- // The bridge being unreachable must not swallow the message: fall
48
- // through so Mimi still answers, which is the pre-binding behavior.
49
- return next();
50
- }
35
+ // A transport timeout is ambiguous: the Work Session may already have
36
+ // accepted this message. Leave retry to the durable inbox instead of also
37
+ // handing the same message to Mimi.
38
+ const disposition = await options.desktop.routeBoundSessionMessage({
39
+ channel: message.channel,
40
+ target: message.target,
41
+ senderId: message.senderId,
42
+ ...(message.messageId ? { messageId: message.messageId } : {}),
43
+ text: message.text,
44
+ isDirectMessage: isDirectMessage(message),
45
+ });
51
46
  switch (disposition.kind) {
52
47
  case "not-bound":
53
48
  return next();
package/dist/channel.d.ts CHANGED
@@ -62,6 +62,8 @@ export interface ChannelMessage {
62
62
  channel: string;
63
63
  target: string;
64
64
  senderId: string;
65
+ /** Adapter-authenticated conversation kind; missing means unknown, never private. */
66
+ isDirectMessage?: boolean;
65
67
  text: string;
66
68
  attachments?: readonly ChatAttachment[];
67
69
  messageId?: string;
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import { createChannelAdapterAsync } from "./adapter-factory.js";
7
7
  import { ChatGateway, createAllowlistMiddleware, createRateLimitMiddleware, } from "./chat-gateway.js";
8
8
  import { defaultGatewayConfigPath, loadGatewayConfig } from "./config.js";
9
9
  import { DesktopControlClient } from "./desktop-control-client.js";
10
+ import { createBoundSessionChat } from "./bound-session-chat.js";
10
11
  import { CODE_SHELL_REMOTE_COMMANDS, createCodeShellRemoteCommands, createMimiPetChat, MIMI_MIN_CONCURRENT_MESSAGES_PER_TARGET, } from "./gateway.js";
11
12
  import { loginCodeShellWechat } from "./wechat-login.js";
12
13
  import { defaultWechatDataDirectory } from "./wechat-storage.js";
@@ -76,6 +77,7 @@ async function main(args = process.argv.slice(2)) {
76
77
  ]))));
77
78
  gateway.use(createRateLimitMiddleware(config.runtime.maxMessagesPerUserPerMinute));
78
79
  gateway.use(createCodeShellRemoteCommands({ desktop }));
80
+ gateway.use(createBoundSessionChat({ desktop }));
79
81
  gateway.use(createMimiPetChat({ desktop, channels: adapters }));
80
82
  console.log(`[code-shell-chat] gateway 已启动:${config.channels.map(({ channel }) => channel).join(", ")}`);
81
83
  const gatewayTask = gateway.run(shutdown.signal);
package/dist/dingtalk.js CHANGED
@@ -17,6 +17,7 @@ export function parseDingTalkTextMessage(data, messageId) {
17
17
  channel: "dingtalk",
18
18
  target: message.conversationId,
19
19
  senderId: message.senderStaffId || message.senderId,
20
+ isDirectMessage: message.conversationType === "1",
20
21
  text: message.text.content,
21
22
  ...(messageId ? { messageId } : {}),
22
23
  metadata: {
package/dist/discord.js CHANGED
@@ -43,6 +43,7 @@ export class DiscordAdapter {
43
43
  channel: this.channel,
44
44
  target: message.channelId,
45
45
  senderId: message.author.id,
46
+ isDirectMessage: message.channel.type === ChannelType.DM,
46
47
  text: message.content,
47
48
  messageId: message.id,
48
49
  ...(attachments.length > 0 ? { attachments } : {}),
@@ -91,6 +92,7 @@ export class DiscordAdapter {
91
92
  channel: this.channel,
92
93
  target,
93
94
  senderId: interaction.user.id,
95
+ isDirectMessage: interaction.channel?.type === ChannelType.DM,
94
96
  text: `/${interaction.commandName}`,
95
97
  messageId: interaction.id,
96
98
  }));
package/dist/gateway.js CHANGED
@@ -186,6 +186,7 @@ async function buildMimiOutgoingReplies(options, message, capabilities, configur
186
186
  channel: message.channel,
187
187
  target: message.target,
188
188
  senderId: message.senderId,
189
+ isDirectMessage: message.isDirectMessage === true,
189
190
  ...(message.messageId ? { messageId: message.messageId } : {}),
190
191
  capabilities,
191
192
  channels: gatewayChannelCatalog(message.channel, capabilities, configuredChannels),
package/dist/lark.js CHANGED
@@ -37,6 +37,7 @@ export class LarkAdapter {
37
37
  channel: this.channel,
38
38
  target: event.message.chat_id,
39
39
  senderId: sender,
40
+ isDirectMessage: event.message.chat_type === "p2p",
40
41
  text,
41
42
  ...(event.message.message_id ? { messageId: event.message.message_id } : {}),
42
43
  ...(attachment ? { attachments: [attachment] } : {}),
package/dist/line.js CHANGED
@@ -73,6 +73,7 @@ export class LineAdapter {
73
73
  channel: this.channel,
74
74
  target,
75
75
  senderId,
76
+ isDirectMessage: event.source?.type === "user",
76
77
  text,
77
78
  ...(id ? { messageId: id } : {}),
78
79
  ...(attachment ? { attachments: [attachment] } : {}),
@@ -143,6 +143,7 @@ export class MattermostAdapter {
143
143
  channel: this.channel,
144
144
  target: post.channel_id,
145
145
  senderId: post.user_id,
146
+ isDirectMessage: event.data.channel_type === "D",
146
147
  text: post.message ?? "",
147
148
  ...(post.id ? { messageId: post.id } : {}),
148
149
  ...(attachments.length > 0 ? { attachments } : {}),
@@ -77,6 +77,8 @@ export interface PetChatRequest {
77
77
  channel: string;
78
78
  target: string;
79
79
  senderId: string;
80
+ /** Adapter-authenticated private-chat signal; absent for legacy clients. */
81
+ isDirectMessage?: boolean;
80
82
  messageId?: string;
81
83
  /** Adapter-declared implementation capabilities for this exact route. */
82
84
  capabilities: ChannelCapabilities;
package/dist/slack.js CHANGED
@@ -58,6 +58,7 @@ export class SlackAdapter {
58
58
  channel: this.channel,
59
59
  target: event.channel,
60
60
  senderId: event.user,
61
+ isDirectMessage: event.channel_type === "im",
61
62
  text,
62
63
  ...(event.client_msg_id || event.ts
63
64
  ? { messageId: String(event.client_msg_id ?? event.ts) }
@@ -73,10 +74,17 @@ export class SlackAdapter {
73
74
  await ack();
74
75
  return;
75
76
  }
77
+ // A user can name a channel "directmessage"; only Slack's conversation
78
+ // metadata can distinguish a DM from a private channel or group DM.
79
+ const isDirectMessage = await this.web.conversations
80
+ .info({ channel: body.channel_id })
81
+ .then(({ channel }) => channel?.is_im === true)
82
+ .catch(() => false);
76
83
  await dispatchSafely(handler, {
77
84
  channel: this.channel,
78
85
  target: body.channel_id,
79
86
  senderId: body.user_id,
87
+ isDirectMessage,
80
88
  text: [body.command, typeof body.text === "string" ? body.text : ""]
81
89
  .filter(Boolean)
82
90
  .join(" "),
package/dist/teams.js CHANGED
@@ -97,6 +97,7 @@ export class TeamsAdapter {
97
97
  channel: this.channel,
98
98
  target,
99
99
  senderId,
100
+ isDirectMessage: activity.conversation.conversationType === "personal",
100
101
  text,
101
102
  ...(activity.id ? { messageId: activity.id } : {}),
102
103
  ...(attachments.length > 0 ? { attachments } : {}),
package/dist/telegram.js CHANGED
@@ -181,6 +181,7 @@ function toChannelMessage(update, download) {
181
181
  channel: "telegram",
182
182
  target: String(message.chat.id),
183
183
  senderId: String(sender),
184
+ isDirectMessage: message.chat.type === "private",
184
185
  text,
185
186
  ...(attachments.length > 0 ? { attachments } : {}),
186
187
  ...(message.message_id === undefined ? {} : { messageId: String(message.message_id) }),
package/dist/wechat.js CHANGED
@@ -470,6 +470,7 @@ export class WechatAdapter {
470
470
  channel: this.channel,
471
471
  target: senderId,
472
472
  senderId,
473
+ isDirectMessage: !raw.group_id,
473
474
  text,
474
475
  ...(attachments.length > 0 ? { attachments } : {}),
475
476
  messageId: raw.message_id === undefined ? undefined : String(raw.message_id),
package/dist/wecom.js CHANGED
@@ -24,6 +24,7 @@ export class WeComAdapter {
24
24
  channel: this.channel,
25
25
  target: message.chatid ?? message.from.userid,
26
26
  senderId: message.from.userid,
27
+ isDirectMessage: message.chattype === "single",
27
28
  text: message.text.content,
28
29
  ...(frame.headers?.req_id
29
30
  ? { messageId: frame.headers.req_id }
@@ -120,6 +121,7 @@ export class WeComAdapter {
120
121
  channel: this.channel,
121
122
  target: message.chatid ?? message.from.userid,
122
123
  senderId: message.from.userid,
124
+ isDirectMessage: message.chattype === "single",
123
125
  text,
124
126
  messageId: message.msgid,
125
127
  ...(attachments.length > 0 ? { attachments } : {}),
package/dist/whatsapp.js CHANGED
@@ -51,6 +51,7 @@ export class WhatsAppAdapter {
51
51
  channel: this.channel,
52
52
  target: message.from,
53
53
  senderId: message.from,
54
+ isDirectMessage: !message.group_id,
54
55
  text,
55
56
  ...(message.id ? { messageId: message.id } : {}),
56
57
  ...(media?.attachment ? { attachments: [media.attachment] } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-chat",
3
- "version": "0.9.17",
3
+ "version": "0.9.19",
4
4
  "description": "A standalone, transport-agnostic multi-channel chat gateway for Node.js and Bun.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",