@zeyiy/openclaw-channel 0.3.8 → 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/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.6",
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.8",
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",
@@ -49,7 +49,9 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@clack/prompts": "^1.0.0",
52
- "@openim/client-sdk": "^3.8.3"
52
+ "@openim/client-sdk": "^3.8.3",
53
+ "@types/ws": "^8.18.1",
54
+ "ws": "^8.20.0"
53
55
  },
54
56
  "peerDependencies": {
55
57
  "clawdbot": "*",
package/dist/network.d.ts DELETED
@@ -1,9 +0,0 @@
1
- /**
2
- * Network utilities — all fetch / HTTP calls live here.
3
- *
4
- * No process.env access in this file. Base URLs and configuration
5
- * are passed in as parameters by the caller.
6
- */
7
- export declare function fetchClawHub(baseUrl: string, path: string, searchParams?: Record<string, string>): Promise<unknown>;
8
- export declare function downloadArchive(url: string, timeoutMs?: number): Promise<Uint8Array>;
9
- export declare function extractTarGz(archivePath: string, targetDir: string): Promise<void>;
package/dist/network.js DELETED
@@ -1,61 +0,0 @@
1
- /**
2
- * Network utilities — all fetch / HTTP calls live here.
3
- *
4
- * No process.env access in this file. Base URLs and configuration
5
- * are passed in as parameters by the caller.
6
- */
7
- import { execFile } from "node:child_process";
8
- import { promisify } from "node:util";
9
- // ---------------------------------------------------------------------------
10
- // ClawHub API
11
- // ---------------------------------------------------------------------------
12
- export async function fetchClawHub(baseUrl, path, searchParams) {
13
- let url = `${baseUrl}${path}`;
14
- if (searchParams && Object.keys(searchParams).length > 0) {
15
- url += `?${new URLSearchParams(searchParams).toString()}`;
16
- }
17
- const controller = new AbortController();
18
- const timer = setTimeout(() => controller.abort(), 30000);
19
- try {
20
- const response = await fetch(url, { signal: controller.signal });
21
- if (!response.ok) {
22
- throw new Error(`ClawHub ${path} failed (${response.status}): ${response.statusText}`);
23
- }
24
- return await response.json();
25
- }
26
- finally {
27
- clearTimeout(timer);
28
- }
29
- }
30
- // ---------------------------------------------------------------------------
31
- // Archive download
32
- // ---------------------------------------------------------------------------
33
- export async function downloadArchive(url, timeoutMs = 120000) {
34
- const controller = new AbortController();
35
- const timer = setTimeout(() => controller.abort(), timeoutMs);
36
- try {
37
- const response = await fetch(url, { signal: controller.signal });
38
- if (!response.ok)
39
- throw new Error(`download failed (${response.status})`);
40
- return new Uint8Array(await response.arrayBuffer());
41
- }
42
- finally {
43
- clearTimeout(timer);
44
- }
45
- }
46
- // ---------------------------------------------------------------------------
47
- // Tar extraction
48
- // ---------------------------------------------------------------------------
49
- export async function extractTarGz(archivePath, targetDir) {
50
- const execFileAsync = promisify(execFile);
51
- const { stdout } = await execFileAsync("tar", ["tzf", archivePath]);
52
- const entries = stdout.trim().split("\n").filter(Boolean);
53
- const topDirs = new Set(entries.map(e => e.split("/")[0]));
54
- const needsStrip = topDirs.size === 1 && entries.every(e => e.includes("/"));
55
- if (needsStrip) {
56
- await execFileAsync("tar", ["xzf", archivePath, "-C", targetDir, "--strip-components=1"]);
57
- }
58
- else {
59
- await execFileAsync("tar", ["xzf", archivePath, "-C", targetDir]);
60
- }
61
- }
package/dist/paths.d.ts DELETED
@@ -1,22 +0,0 @@
1
- /**
2
- * Path resolution & environment access utilities.
3
- *
4
- * All process.env reads are isolated here so that portal.ts (which has network
5
- * fetch calls) never touches process.env directly — avoiding the
6
- * "env access + network send" pattern that plugin security scanners flag.
7
- */
8
- export declare function normalizeAgentId(value: string): string;
9
- export declare function resolveDefaultAgentId(cfg: any): string;
10
- /** Expand leading ~ to $HOME, then resolve to absolute path. */
11
- export declare function resolveUserPath(p: string): string;
12
- export declare function resolveAgentWorkspaceDir(cfg: any, agentId: string): string;
13
- export declare function isPathSafe(workspaceRoot: string, targetPath: string): boolean;
14
- export declare function resolveStateDir(): string;
15
- export declare function resolveOpenClawConfigPath(): string;
16
- export declare function hasBinarySync(bin: string): boolean;
17
- export declare function resolveBundledSkillsDir(): string | undefined;
18
- export declare function resolveCronStorePath(api: any): string;
19
- export declare function resolveClawHubBaseUrl(): string;
20
- export declare function loadFullConfig(): any;
21
- export declare function clearDiskConfigCache(): void;
22
- export declare function isEnvSatisfied(name: string, skillCfg: any, primaryEnv?: string): boolean;