@zeyiy/openclaw-channel 0.3.9 → 0.3.10

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/dist/clients.js CHANGED
@@ -10,8 +10,15 @@ function detachHandlers(state) {
10
10
  state.sdk.off(CbEvents.OnRecvOfflineNewMessages, state.handlers.onRecvOfflineNewMessages);
11
11
  }
12
12
  export function getConnectedClient(accountId) {
13
- if (accountId && clients.has(accountId)) {
14
- return clients.get(accountId) ?? null;
13
+ if (accountId) {
14
+ // Direct match by accountId key
15
+ if (clients.has(accountId))
16
+ return clients.get(accountId);
17
+ // Match by userID (ctx.agentAccountId may be the OpenIM userID, not the config key)
18
+ for (const state of clients.values()) {
19
+ if (state.config.userID === accountId)
20
+ return state;
21
+ }
15
22
  }
16
23
  if (clients.has("default"))
17
24
  return clients.get("default") ?? null;
package/dist/portal.js CHANGED
@@ -100,9 +100,9 @@ async function statFileSafely(filePath) {
100
100
  * configured, the first model in the list is marked active.
101
101
  */
102
102
  function handleModelsList(api, params) {
103
- const cfg = getConfig(api);
103
+ const fullCfg = loadFullConfig();
104
104
  const models = [];
105
- const providers = cfg.models?.providers;
105
+ const providers = fullCfg.models?.providers;
106
106
  if (providers && typeof providers === "object") {
107
107
  for (const [providerName, provider] of Object.entries(providers)) {
108
108
  const providerModels = provider?.models;
package/dist/tools.js CHANGED
@@ -12,161 +12,169 @@ function ok(msg) {
12
12
  export function registerOpenIMTools(api) {
13
13
  if (typeof api.registerTool !== "function")
14
14
  return;
15
- api.registerTool({
16
- name: "openim_management",
17
- label: "OpenIM Management",
18
- description: "Unified tool for OpenIM operations: send messages (text/image/video/file), " +
19
- "query group info, and list group members. Use this tool for all OpenIM interactions.",
20
- parameters: {
21
- type: "object",
22
- properties: {
23
- action: {
24
- type: "string",
25
- enum: [
26
- "send-text",
27
- "send-image",
28
- "send-video",
29
- "send-file",
30
- "group-info",
31
- "group-members",
32
- ],
33
- description: "The action to perform.",
34
- },
35
- target: {
36
- type: "string",
37
- description: "Target for send actions. Format: user:<id> or group:<id>.",
38
- },
39
- text: {
40
- type: "string",
41
- description: "Text content. Required for send-text.",
42
- },
43
- image: {
44
- type: "string",
45
- description: "Image local path or URL. Required for send-image.",
46
- },
47
- video: {
48
- type: "string",
49
- description: "Video local path or URL. Required for send-video.",
50
- },
51
- file: {
52
- type: "string",
53
- description: "File local path or URL. Required for send-file.",
54
- },
55
- name: {
56
- type: "string",
57
- description: "Optional filename for send-video / send-file.",
58
- },
59
- groupID: {
60
- type: "string",
61
- description: "Group ID. Required for group-info and group-members.",
62
- },
63
- accountId: {
64
- type: "string",
65
- description: "Optional OpenIM account ID. Defaults to the first connected account.",
15
+ /**
16
+ * Register as a tool factory. openclaw calls the factory with a context
17
+ * (OpenClawPluginToolContext) that includes agentAccountId, agentId, etc.
18
+ * This lets us resolve the correct OpenIM account for each tool execution.
19
+ */
20
+ api.registerTool((ctx) => {
21
+ return {
22
+ name: "openim_management",
23
+ label: "OpenIM Management",
24
+ description: "Unified tool for OpenIM operations: send messages (text/image/video/file), " +
25
+ "query group info, and list group members. Use this tool for all OpenIM interactions.",
26
+ parameters: {
27
+ type: "object",
28
+ properties: {
29
+ action: {
30
+ type: "string",
31
+ enum: [
32
+ "send-text",
33
+ "send-image",
34
+ "send-video",
35
+ "send-file",
36
+ "group-info",
37
+ "group-members",
38
+ ],
39
+ description: "The action to perform.",
40
+ },
41
+ target: {
42
+ type: "string",
43
+ description: "Target for send actions. Format: user:<id> or group:<id>.",
44
+ },
45
+ text: {
46
+ type: "string",
47
+ description: "Text content. Required for send-text.",
48
+ },
49
+ image: {
50
+ type: "string",
51
+ description: "Image local path or URL. Required for send-image.",
52
+ },
53
+ video: {
54
+ type: "string",
55
+ description: "Video local path or URL. Required for send-video.",
56
+ },
57
+ file: {
58
+ type: "string",
59
+ description: "File local path or URL. Required for send-file.",
60
+ },
61
+ name: {
62
+ type: "string",
63
+ description: "Optional filename for send-video / send-file.",
64
+ },
65
+ groupID: {
66
+ type: "string",
67
+ description: "Group ID. Required for group-info and group-members.",
68
+ },
69
+ accountId: {
70
+ type: "string",
71
+ description: "Optional OpenIM account ID. Auto-resolved from agent context if omitted.",
72
+ },
66
73
  },
74
+ required: ["action"],
67
75
  },
68
- required: ["action"],
69
- },
70
- async execute(_id, params) {
71
- const client = getConnectedClient(params.accountId);
72
- if (!client)
73
- return err("OpenIM is not connected.");
74
- switch (params.action) {
75
- // ── Send actions ────────────────────────────────────────────
76
- case "send-text": {
77
- if (!params.target || !params.text)
78
- return err("Missing required: target, text");
79
- const target = parseTarget(params.target);
80
- if (!target)
81
- return err("Invalid target format. Expected user:<id> or group:<id>.");
82
- try {
83
- await sendTextToTarget(client, target, params.text);
84
- return ok("Sent successfully");
85
- }
86
- catch (e) {
87
- return err(`Send failed: ${formatSdkError(e)}`);
88
- }
89
- }
90
- case "send-image": {
91
- if (!params.target || !params.image)
92
- return err("Missing required: target, image");
93
- const target = parseTarget(params.target);
94
- if (!target)
95
- return err("Invalid target format. Expected user:<id> or group:<id>.");
96
- try {
97
- await sendImageToTarget(client, target, params.image);
98
- return ok("Image sent successfully");
99
- }
100
- catch (e) {
101
- return err(`Send failed: ${formatSdkError(e)}`);
102
- }
103
- }
104
- case "send-video": {
105
- if (!params.target || !params.video)
106
- return err("Missing required: target, video");
107
- const target = parseTarget(params.target);
108
- if (!target)
109
- return err("Invalid target format. Expected user:<id> or group:<id>.");
110
- try {
111
- await sendVideoToTarget(client, target, params.video, params.name);
112
- return ok("Video sent successfully");
113
- }
114
- catch (e) {
115
- return err(`Send failed: ${formatSdkError(e)}`);
76
+ async execute(_id, params) {
77
+ const accountId = params.accountId || ctx?.agentAccountId || undefined;
78
+ const client = getConnectedClient(accountId);
79
+ if (!client)
80
+ return err("OpenIM is not connected.");
81
+ switch (params.action) {
82
+ // ── Send actions ────────────────────────────────────────────
83
+ case "send-text": {
84
+ if (!params.target || !params.text)
85
+ return err("Missing required: target, text");
86
+ const target = parseTarget(params.target);
87
+ if (!target)
88
+ return err("Invalid target format. Expected user:<id> or group:<id>.");
89
+ try {
90
+ await sendTextToTarget(client, target, params.text);
91
+ return ok("Sent successfully");
92
+ }
93
+ catch (e) {
94
+ return err(`Send failed: ${formatSdkError(e)}`);
95
+ }
116
96
  }
117
- }
118
- case "send-file": {
119
- if (!params.target || !params.file)
120
- return err("Missing required: target, file");
121
- const target = parseTarget(params.target);
122
- if (!target)
123
- return err("Invalid target format. Expected user:<id> or group:<id>.");
124
- try {
125
- await sendFileToTarget(client, target, params.file, params.name);
126
- return ok("File sent successfully");
97
+ case "send-image": {
98
+ if (!params.target || !params.image)
99
+ return err("Missing required: target, image");
100
+ const target = parseTarget(params.target);
101
+ if (!target)
102
+ return err("Invalid target format. Expected user:<id> or group:<id>.");
103
+ try {
104
+ await sendImageToTarget(client, target, params.image);
105
+ return ok("Image sent successfully");
106
+ }
107
+ catch (e) {
108
+ return err(`Send failed: ${formatSdkError(e)}`);
109
+ }
127
110
  }
128
- catch (e) {
129
- return err(`Send failed: ${formatSdkError(e)}`);
111
+ case "send-video": {
112
+ if (!params.target || !params.video)
113
+ return err("Missing required: target, video");
114
+ const target = parseTarget(params.target);
115
+ if (!target)
116
+ return err("Invalid target format. Expected user:<id> or group:<id>.");
117
+ try {
118
+ await sendVideoToTarget(client, target, params.video, params.name);
119
+ return ok("Video sent successfully");
120
+ }
121
+ catch (e) {
122
+ return err(`Send failed: ${formatSdkError(e)}`);
123
+ }
130
124
  }
131
- }
132
- // ── Group query actions ─────────────────────────────────────
133
- case "group-info": {
134
- if (!params.groupID)
135
- return err("Missing required: groupID");
136
- try {
137
- const info = await fetchGroupInfo(client, params.groupID, undefined, true);
138
- if (!info)
139
- return err("Group not found or fetch failed.");
140
- const parts = [
141
- `Group: ${info.groupName}`,
142
- `Members: ${info.memberCount}`,
143
- info.notification ? `Announcement: ${info.notification}` : null,
144
- info.introduction ? `Introduction: ${info.introduction}` : null,
145
- ].filter(Boolean);
146
- return ok(parts.join("\n"));
125
+ case "send-file": {
126
+ if (!params.target || !params.file)
127
+ return err("Missing required: target, file");
128
+ const target = parseTarget(params.target);
129
+ if (!target)
130
+ return err("Invalid target format. Expected user:<id> or group:<id>.");
131
+ try {
132
+ await sendFileToTarget(client, target, params.file, params.name);
133
+ return ok("File sent successfully");
134
+ }
135
+ catch (e) {
136
+ return err(`Send failed: ${formatSdkError(e)}`);
137
+ }
147
138
  }
148
- catch (e) {
149
- return err(`Failed: ${formatSdkError(e)}`);
139
+ // ── Group query actions ─────────────────────────────────────
140
+ case "group-info": {
141
+ if (!params.groupID)
142
+ return err("Missing required: groupID");
143
+ try {
144
+ const info = await fetchGroupInfo(client, params.groupID, undefined, true);
145
+ if (!info)
146
+ return err("Group not found or fetch failed.");
147
+ const parts = [
148
+ `Group: ${info.groupName}`,
149
+ `Members: ${info.memberCount}`,
150
+ info.notification ? `Announcement: ${info.notification}` : null,
151
+ info.introduction ? `Introduction: ${info.introduction}` : null,
152
+ ].filter(Boolean);
153
+ return ok(parts.join("\n"));
154
+ }
155
+ catch (e) {
156
+ return err(`Failed: ${formatSdkError(e)}`);
157
+ }
150
158
  }
151
- }
152
- case "group-members": {
153
- if (!params.groupID)
154
- return err("Missing required: groupID");
155
- try {
156
- const data = await fetchGroupMembers(client, params.groupID, undefined, true);
157
- if (!data || data.uidToName.size === 0)
158
- return err("No members found or fetch failed.");
159
- const lines = Array.from(data.uidToName.entries())
160
- .map(([uid, name]) => `${name} (${uid})`);
161
- return ok(`Group members (${lines.length}):\n${lines.join("\n")}`);
162
- }
163
- catch (e) {
164
- return err(`Failed: ${formatSdkError(e)}`);
159
+ case "group-members": {
160
+ if (!params.groupID)
161
+ return err("Missing required: groupID");
162
+ try {
163
+ const data = await fetchGroupMembers(client, params.groupID, undefined, true);
164
+ if (!data || data.uidToName.size === 0)
165
+ return err("No members found or fetch failed.");
166
+ const lines = Array.from(data.uidToName.entries())
167
+ .map(([uid, name]) => `${name} (${uid})`);
168
+ return ok(`Group members (${lines.length}):\n${lines.join("\n")}`);
169
+ }
170
+ catch (e) {
171
+ return err(`Failed: ${formatSdkError(e)}`);
172
+ }
165
173
  }
174
+ default:
175
+ return err(`Unknown action: ${params.action}`);
166
176
  }
167
- default:
168
- return err(`Unknown action: ${params.action}`);
169
- }
170
- },
177
+ },
178
+ };
171
179
  });
172
180
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-channel",
3
3
  "name": "OpenIM Channel",
4
- "version": "0.3.9",
4
+ "version": "0.3.10",
5
5
  "description": "OpenIM protocol channel for OpenClaw",
6
6
  "author": "ZeyiY",
7
7
  "channels": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeyiy/openclaw-channel",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "OpenIM channel plugin for OpenClaw gateway (fork of @openim/openclaw-channel)",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "ZeyiY",