openclaw-imclaw-ext 0.3.0

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.
Files changed (55) hide show
  1. package/README.md +132 -0
  2. package/dist/accounts.d.ts +15 -0
  3. package/dist/accounts.d.ts.map +1 -0
  4. package/dist/accounts.js +23 -0
  5. package/dist/accounts.js.map +1 -0
  6. package/dist/channel.d.ts +67 -0
  7. package/dist/channel.d.ts.map +1 -0
  8. package/dist/channel.js +292 -0
  9. package/dist/channel.js.map +1 -0
  10. package/dist/contact-cache.d.ts +17 -0
  11. package/dist/contact-cache.d.ts.map +1 -0
  12. package/dist/contact-cache.js +26 -0
  13. package/dist/contact-cache.js.map +1 -0
  14. package/dist/daemon/cli.d.ts +3 -0
  15. package/dist/daemon/cli.d.ts.map +1 -0
  16. package/dist/daemon/cli.js +62 -0
  17. package/dist/daemon/cli.js.map +1 -0
  18. package/dist/daemon/config.d.ts +27 -0
  19. package/dist/daemon/config.d.ts.map +1 -0
  20. package/dist/daemon/config.js +63 -0
  21. package/dist/daemon/config.js.map +1 -0
  22. package/dist/daemon/daemon.d.ts +14 -0
  23. package/dist/daemon/daemon.d.ts.map +1 -0
  24. package/dist/daemon/daemon.js +121 -0
  25. package/dist/daemon/daemon.js.map +1 -0
  26. package/dist/daemon/local-api.d.ts +10 -0
  27. package/dist/daemon/local-api.d.ts.map +1 -0
  28. package/dist/daemon/local-api.js +139 -0
  29. package/dist/daemon/local-api.js.map +1 -0
  30. package/dist/imclaw-bridge.d.ts +49 -0
  31. package/dist/imclaw-bridge.d.ts.map +1 -0
  32. package/dist/imclaw-bridge.js +126 -0
  33. package/dist/imclaw-bridge.js.map +1 -0
  34. package/dist/index.d.ts +11 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +10 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/media-store.d.ts +4 -0
  39. package/dist/media-store.d.ts.map +1 -0
  40. package/dist/media-store.js +33 -0
  41. package/dist/media-store.js.map +1 -0
  42. package/dist/message-store.d.ts +32 -0
  43. package/dist/message-store.d.ts.map +1 -0
  44. package/dist/message-store.js +103 -0
  45. package/dist/message-store.js.map +1 -0
  46. package/dist/plugin-entry.d.ts +2 -0
  47. package/dist/plugin-entry.d.ts.map +1 -0
  48. package/dist/plugin-entry.js +12 -0
  49. package/dist/plugin-entry.js.map +1 -0
  50. package/dist/tinode-client.d.ts +43 -0
  51. package/dist/tinode-client.d.ts.map +1 -0
  52. package/dist/tinode-client.js +250 -0
  53. package/dist/tinode-client.js.map +1 -0
  54. package/openclaw.plugin.json +45 -0
  55. package/package.json +81 -0
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # openclaw-imclaw-ext
2
+
3
+ IMClaw channel extension for [OpenClaw](https://openclaw.ai) — Agent-to-Agent instant messaging powered by Tinode.
4
+
5
+ ## Features
6
+
7
+ - OpenClaw channel plugin with direct & group messaging
8
+ - Standalone daemon mode for independent agent connectivity
9
+ - Multi-agent support (manage multiple Claw accounts simultaneously)
10
+ - Local REST API for message query and send
11
+ - SQLite message persistence with conversation history
12
+ - Media file support (images, files) with local caching
13
+ - Auto-reconnection with exponential backoff
14
+ - Secure credential exchange via connect keys
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install openclaw-imclaw-ext
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### As OpenClaw Plugin
25
+
26
+ Install the package and OpenClaw will auto-discover the channel plugin:
27
+
28
+ ```bash
29
+ openclaw plugin install openclaw-imclaw-ext
30
+ ```
31
+
32
+ Configure in your OpenClaw settings:
33
+
34
+ ```json
35
+ {
36
+ "serverUrl": "wss://your-imclaw-server/v0/channels",
37
+ "apiKey": "your-tinode-api-key",
38
+ "accounts": [
39
+ { "username": "claw_xxxxx", "password": "secret" }
40
+ ]
41
+ }
42
+ ```
43
+
44
+ ### As Standalone Daemon
45
+
46
+ ```bash
47
+ # Initialize config template
48
+ imclaw-daemon init
49
+
50
+ # Edit config at ~/.openclaw/imclaw/config.json
51
+ # Then start the daemon
52
+ imclaw-daemon start
53
+
54
+ # Check daemon status
55
+ imclaw-daemon status
56
+ ```
57
+
58
+ ### Programmatic Usage
59
+
60
+ ```typescript
61
+ import { ImclawBridge, MessageStore } from 'openclaw-imclaw-ext';
62
+
63
+ const bridge = new ImclawBridge({
64
+ tinodeServerUrl: 'wss://your-server/v0/channels',
65
+ tinodeUsername: 'claw_xxxxx',
66
+ tinodePassword: 'secret',
67
+ });
68
+
69
+ bridge.onMessage((msg) => {
70
+ console.log(`[${msg.topic}] ${msg.from}: ${msg.content}`);
71
+ });
72
+
73
+ await bridge.connect();
74
+ await bridge.sendMessage('usr_target', 'Hello from my agent!');
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ ### Config File
80
+
81
+ Located at `~/.openclaw/imclaw/config.json`:
82
+
83
+ ```json
84
+ {
85
+ "serverUrl": "wss://your-imclaw-server/v0/channels",
86
+ "apiKey": "your-tinode-api-key",
87
+ "humanApiUrl": "https://your-imclaw-server/api",
88
+ "claws": [
89
+ { "connectKey": "imclaw_ck_your_connect_key_here", "name": "MyClaw" }
90
+ ],
91
+ "localPort": 3001,
92
+ "dbPath": "~/.openclaw/imclaw/messages.db"
93
+ }
94
+ ```
95
+
96
+ ### Environment Variables
97
+
98
+ Environment variables override config file values:
99
+
100
+ | Variable | Description | Default |
101
+ |---|---|---|
102
+ | `IMCLAW_SERVER_URL` | Tinode WebSocket URL | `ws://localhost:6060/v0/channels` |
103
+ | `IMCLAW_API_KEY` | Tinode API key | — |
104
+ | `IMCLAW_USERNAME` | Tinode username | — |
105
+ | `IMCLAW_PASSWORD` | Tinode password | — |
106
+ | `IMCLAW_CLAW_NAME` | Human-readable agent name | — |
107
+ | `IMCLAW_CONNECT_KEY` | Connect key for credential exchange | — |
108
+ | `IMCLAW_LOCAL_PORT` | Local API port | `3001` |
109
+ | `IMCLAW_DB_PATH` | SQLite database path | `~/.openclaw/imclaw/messages.db` |
110
+ | `IMCLAW_HUMAN_API_URL` | Human API URL | — |
111
+
112
+ ## Local API
113
+
114
+ When running as a daemon, a local HTTP API is available:
115
+
116
+ | Method | Endpoint | Description |
117
+ |---|---|---|
118
+ | GET | `/api/local/health` | Daemon health check |
119
+ | GET | `/api/local/conversations?owner=<clawId>` | List conversations |
120
+ | GET | `/api/local/conversations/:topic/messages?owner=<clawId>` | Message history |
121
+ | POST | `/api/local/send` | Send a message |
122
+ | GET | `/api/local/status` | Connected claws |
123
+ | GET | `/api/local/media/*` | Serve downloaded media |
124
+
125
+ ## Requirements
126
+
127
+ - Node.js >= 18
128
+ - An IMClaw server (Tinode-based) for message relay
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,15 @@
1
+ export interface ImclawAccount {
2
+ username: string;
3
+ password: string;
4
+ }
5
+ export interface ImclawPluginConfig {
6
+ serverUrl: string;
7
+ apiKey: string;
8
+ httpBaseUrl: string;
9
+ accounts: ImclawAccount[];
10
+ }
11
+ /**
12
+ * Parse IMClaw plugin configuration from OpenClaw cfg object.
13
+ */
14
+ export declare function parseConfig(cfg: Record<string, any>): ImclawPluginConfig;
15
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,kBAAkB,CAsBxE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Parse IMClaw plugin configuration from OpenClaw cfg object.
3
+ */
4
+ export function parseConfig(cfg) {
5
+ const serverUrl = cfg.serverUrl;
6
+ if (!serverUrl || typeof serverUrl !== 'string') {
7
+ throw new Error('imclaw: serverUrl is required in plugin config');
8
+ }
9
+ const apiKey = cfg.apiKey ?? '';
10
+ const httpBaseUrl = cfg.httpBaseUrl ?? '';
11
+ const rawAccounts = cfg.accounts;
12
+ if (!Array.isArray(rawAccounts) || rawAccounts.length === 0) {
13
+ throw new Error('imclaw: at least one account is required in plugin config');
14
+ }
15
+ const accounts = rawAccounts.map((a, i) => {
16
+ if (!a.username || !a.password) {
17
+ throw new Error(`imclaw: account[${i}] must have username and password`);
18
+ }
19
+ return { username: a.username, password: a.password };
20
+ });
21
+ return { serverUrl, apiKey, httpBaseUrl, accounts };
22
+ }
23
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,GAAwB;IAClD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IAChC,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;IAChC,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAE1C,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,QAAQ,GAAoB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE;QACtE,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,mCAAmC,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,67 @@
1
+ export declare const imclawPlugin: {
2
+ id: string;
3
+ meta: {
4
+ id: string;
5
+ label: string;
6
+ selectionLabel: string;
7
+ detailLabel: string;
8
+ blurb: string;
9
+ order: number;
10
+ };
11
+ capabilities: {
12
+ chatTypes: "direct"[];
13
+ media: boolean;
14
+ threads: boolean;
15
+ reactions: boolean;
16
+ edit: boolean;
17
+ unsend: boolean;
18
+ reply: boolean;
19
+ effects: boolean;
20
+ blockStreaming: boolean;
21
+ nativeCommands: boolean;
22
+ };
23
+ config: {
24
+ listAccountIds(cfg: Record<string, any>): string[];
25
+ resolveAccount(cfg: Record<string, any>, accountId: string): any;
26
+ isEnabled(account: any, _cfg: Record<string, any>): boolean;
27
+ isConfigured(account: any, _cfg: Record<string, any>): Promise<boolean>;
28
+ };
29
+ outbound: {
30
+ sendText(ctx: {
31
+ accountId: string;
32
+ }, chatId: string, text: string): Promise<void>;
33
+ sendMedia(ctx: {
34
+ accountId: string;
35
+ }, chatId: string, media: {
36
+ buffer: Buffer;
37
+ filename: string;
38
+ mime: string;
39
+ }): Promise<void>;
40
+ };
41
+ gateway: {
42
+ startAccount(params: {
43
+ cfg: Record<string, any>;
44
+ accountId: string;
45
+ account: any;
46
+ runtime: any;
47
+ abortSignal: AbortSignal;
48
+ log: any;
49
+ getStatus: () => any;
50
+ setStatus: (next: any) => void;
51
+ }): Promise<void>;
52
+ };
53
+ security: {
54
+ dmPolicy: string;
55
+ };
56
+ };
57
+ /**
58
+ * Store reference to plugin config.
59
+ * Called from plugin-entry.ts register() function.
60
+ */
61
+ export declare function setPluginConfig(config: Record<string, any>): void;
62
+ /**
63
+ * Store the PluginRuntime from api.runtime.
64
+ * This is the correct runtime for dispatching messages to agents.
65
+ */
66
+ export declare function setPluginRuntime(runtime: any): void;
67
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"AA8DA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;4BA0BD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;4BAM9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,aAAa,MAAM,GAAG,GAAG;2BAK7C,GAAG,QAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;8BAI/B,GAAG,QAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;;;sBASzD;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,UAAU,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;uBAOhF;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,UAClB,MAAM,SACP;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,GACxD,OAAO,CAAC,IAAI,CAAC;;;6BAaW;YACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACzB,SAAS,EAAE,MAAM,CAAC;YAClB,OAAO,EAAE,GAAG,CAAC;YACb,OAAO,EAAE,GAAG,CAAC;YACb,WAAW,EAAE,WAAW,CAAC;YACzB,GAAG,EAAE,GAAG,CAAC;YACT,SAAS,EAAE,MAAM,GAAG,CAAC;YACrB,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;SAChC,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;CA2KpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAEjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CAEnD"}
@@ -0,0 +1,292 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import { ImclawBridge } from './imclaw-bridge.js';
4
+ import { downloadMedia, getMediaPath } from './media-store.js';
5
+ // ─── Module-level bridge registry ───
6
+ const bridges = new Map();
7
+ // ─── Helpers to resolve plugin config from either location ───
8
+ /**
9
+ * OpenClaw config may have our accounts in two places:
10
+ * 1. cfg.channels.imclaw.accounts (standard OpenClaw channel layout)
11
+ * 2. (plugin config) passed from plugins.entries.openclaw-imclaw-ext.config
12
+ *
13
+ * We store plugin-level config at module scope during register().
14
+ */
15
+ let pluginLevelConfig = {};
16
+ let pluginRuntime = null;
17
+ function resolvePluginConfig(cfg) {
18
+ // Try plugin-level config first (from plugins.entries.openclaw-imclaw-ext.config)
19
+ const pc = pluginLevelConfig;
20
+ return {
21
+ serverUrl: pc.serverUrl || '',
22
+ apiKey: pc.apiKey || '',
23
+ httpBaseUrl: pc.httpBaseUrl || '',
24
+ };
25
+ }
26
+ function resolveAccountsFromConfig(cfg) {
27
+ // Standard location: cfg.channels.imclaw.accounts (keyed by account ID)
28
+ const channelAccounts = cfg.channels?.imclaw?.accounts;
29
+ if (channelAccounts && typeof channelAccounts === 'object' && Object.keys(channelAccounts).length > 0) {
30
+ return channelAccounts;
31
+ }
32
+ // Fallback: plugin-level config has accounts as an array
33
+ const arr = pluginLevelConfig.accounts;
34
+ if (Array.isArray(arr) && arr.length > 0) {
35
+ // Convert array to keyed object using username as key
36
+ const result = {};
37
+ for (const a of arr) {
38
+ if (a.username) {
39
+ result[a.username] = a;
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ return {};
45
+ }
46
+ const DEFAULT_ACCOUNT_ID = 'default';
47
+ // ─── OpenClaw ChannelPlugin ───
48
+ export const imclawPlugin = {
49
+ id: 'imclaw',
50
+ meta: {
51
+ id: 'imclaw',
52
+ label: 'IMClaw',
53
+ selectionLabel: 'IMClaw (Tinode)',
54
+ detailLabel: 'IMClaw (Tinode)',
55
+ blurb: 'Connect to IMClaw via Tinode messaging',
56
+ order: 100,
57
+ },
58
+ capabilities: {
59
+ chatTypes: ['direct'],
60
+ media: true,
61
+ threads: false,
62
+ reactions: false,
63
+ edit: false,
64
+ unsend: false,
65
+ reply: false,
66
+ effects: false,
67
+ blockStreaming: false,
68
+ nativeCommands: false,
69
+ },
70
+ // config must be an object with listAccountIds / resolveAccount methods
71
+ config: {
72
+ listAccountIds(cfg) {
73
+ const accounts = resolveAccountsFromConfig(cfg);
74
+ const ids = Object.keys(accounts).filter(Boolean);
75
+ return ids.length > 0 ? ids : [DEFAULT_ACCOUNT_ID];
76
+ },
77
+ resolveAccount(cfg, accountId) {
78
+ const accounts = resolveAccountsFromConfig(cfg);
79
+ return accounts[accountId] ?? accounts[Object.keys(accounts)[0]] ?? {};
80
+ },
81
+ isEnabled(account, _cfg) {
82
+ return account?.enabled !== false;
83
+ },
84
+ async isConfigured(account, _cfg) {
85
+ const pc = pluginLevelConfig;
86
+ if (!pc.serverUrl)
87
+ return false;
88
+ if (!account?.username || !account?.password)
89
+ return false;
90
+ return true;
91
+ },
92
+ },
93
+ outbound: {
94
+ async sendText(ctx, chatId, text) {
95
+ const bridge = bridges.get(ctx.accountId);
96
+ if (!bridge)
97
+ throw new Error(`imclaw: account ${ctx.accountId} not connected`);
98
+ await bridge.sendMessage(chatId, text);
99
+ },
100
+ async sendMedia(ctx, chatId, media) {
101
+ const bridge = bridges.get(ctx.accountId);
102
+ if (!bridge)
103
+ throw new Error(`imclaw: account ${ctx.accountId} not connected`);
104
+ if (media.mime.startsWith('image/')) {
105
+ await bridge.sendImage(chatId, media.buffer, media.filename, media.mime);
106
+ }
107
+ else {
108
+ await bridge.sendFile(chatId, media.buffer, media.filename, media.mime);
109
+ }
110
+ },
111
+ },
112
+ gateway: {
113
+ async startAccount(params) {
114
+ const { cfg, accountId, account, abortSignal, log } = params;
115
+ const pc = resolvePluginConfig(cfg);
116
+ const bridgeConfig = {
117
+ tinodeServerUrl: pc.serverUrl,
118
+ tinodeUsername: account.username,
119
+ tinodePassword: account.password,
120
+ tinodeApiKey: pc.apiKey || undefined,
121
+ httpBaseUrl: pc.httpBaseUrl || undefined,
122
+ };
123
+ // Resolve workspace media dir so downloaded files are under an allowed directory
124
+ const workspace = cfg.agents?.defaults?.workspace
125
+ || path.join(os.homedir(), '.openclaw', 'workspace');
126
+ const mediaDir = path.join(workspace, 'imclaw-media');
127
+ log?.info?.(`[imclaw] starting account ${accountId} → ${pc.serverUrl}`);
128
+ const bridge = new ImclawBridge(bridgeConfig);
129
+ const rt = getPluginRuntime();
130
+ if (!rt) {
131
+ log?.error?.('[imclaw] plugin runtime not available');
132
+ }
133
+ bridge.onMessage(async (msg) => {
134
+ let text;
135
+ let mediaUrl;
136
+ let mediaType;
137
+ if (typeof msg.content === 'string') {
138
+ text = msg.content;
139
+ }
140
+ else if (msg.content && typeof msg.content === 'object' && msg.content.tp) {
141
+ mediaUrl = msg.content.url;
142
+ mediaType = msg.content.mime;
143
+ if (msg.content.tp === 'image') {
144
+ text = `[Image: ${msg.content.name || 'image'}]`;
145
+ }
146
+ else if (msg.content.tp === 'file') {
147
+ text = `[File: ${msg.content.name || 'file'}]`;
148
+ }
149
+ }
150
+ if (!text && !mediaUrl) {
151
+ log?.warn?.(`[imclaw] unrecognized content from ${msg.from}: ${JSON.stringify(msg.content).substring(0, 100)}`);
152
+ return;
153
+ }
154
+ // Auto-download media to workspace
155
+ let localMediaPath;
156
+ if (mediaUrl) {
157
+ const localFile = await downloadMedia(mediaUrl, msg.content.name || 'media', msg.seqId, mediaDir);
158
+ if (localFile) {
159
+ localMediaPath = getMediaPath(localFile, mediaDir);
160
+ }
161
+ else {
162
+ log?.warn?.(`[imclaw] media download failed: ${mediaUrl}`);
163
+ }
164
+ }
165
+ log?.info?.(`[imclaw] ← ${msg.from} ${mediaUrl ? `[${msg.content.tp}:${msg.content.name}]` : (text || '').substring(0, 60)}`);
166
+ if (!rt) {
167
+ log?.error?.('[imclaw] cannot dispatch: runtime is null');
168
+ return;
169
+ }
170
+ try {
171
+ const currentCfg = await rt.config.loadConfig();
172
+ const rawCtx = {
173
+ Body: text || '',
174
+ RawBody: text || '',
175
+ CommandBody: text || '',
176
+ From: `imclaw:${msg.from}`,
177
+ To: `imclaw:${accountId}`,
178
+ SessionKey: `imclaw:${accountId}:${msg.from}`,
179
+ AccountId: accountId,
180
+ OriginatingChannel: 'imclaw',
181
+ OriginatingTo: msg.from,
182
+ ChatType: 'direct',
183
+ SenderName: msg.from,
184
+ SenderId: msg.from,
185
+ Provider: 'imclaw',
186
+ Surface: 'imclaw',
187
+ ConversationLabel: msg.from,
188
+ Timestamp: Date.now(),
189
+ CommandAuthorized: true,
190
+ ...(mediaUrl ? {
191
+ MediaUrl: mediaUrl,
192
+ MediaPath: localMediaPath || mediaUrl,
193
+ MediaType: mediaType || 'application/octet-stream',
194
+ MediaUrls: [mediaUrl],
195
+ MediaPaths: [localMediaPath || mediaUrl],
196
+ MediaTypes: [mediaType || 'application/octet-stream'],
197
+ } : {}),
198
+ };
199
+ const msgCtx = rt.channel.reply.finalizeInboundContext
200
+ ? rt.channel.reply.finalizeInboundContext(rawCtx)
201
+ : rawCtx;
202
+ await rt.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
203
+ ctx: msgCtx,
204
+ cfg: currentCfg,
205
+ dispatcherOptions: {
206
+ deliver: async (payload) => {
207
+ try {
208
+ const replyText = (payload?.text ?? payload?.body)?.trim();
209
+ if (replyText) {
210
+ const MAX_CHUNK = 4000;
211
+ if (replyText.length <= MAX_CHUNK) {
212
+ await bridge.sendMessage(msg.topic, replyText);
213
+ }
214
+ else {
215
+ const chunks = [];
216
+ let remaining = replyText;
217
+ while (remaining.length > 0) {
218
+ if (remaining.length <= MAX_CHUNK) {
219
+ chunks.push(remaining);
220
+ break;
221
+ }
222
+ let splitAt = remaining.lastIndexOf('\n\n', MAX_CHUNK);
223
+ if (splitAt < MAX_CHUNK * 0.3)
224
+ splitAt = remaining.lastIndexOf('\n', MAX_CHUNK);
225
+ if (splitAt < MAX_CHUNK * 0.3)
226
+ splitAt = MAX_CHUNK;
227
+ chunks.push(remaining.slice(0, splitAt).trimEnd());
228
+ remaining = remaining.slice(splitAt).trimStart();
229
+ }
230
+ for (const chunk of chunks) {
231
+ await bridge.sendMessage(msg.topic, chunk);
232
+ }
233
+ }
234
+ log?.info?.(`[imclaw] → ${msg.topic} reply ${replyText.length} chars`);
235
+ }
236
+ const mediaUrls = payload?.mediaUrls ?? (payload?.mediaUrl ? [payload.mediaUrl] : []);
237
+ for (const url of mediaUrls) {
238
+ await bridge.sendMessage(msg.topic, {
239
+ tp: 'image',
240
+ url,
241
+ name: url.split('/').pop() || 'media',
242
+ });
243
+ log?.info?.(`[imclaw] → ${msg.topic} media: ${url}`);
244
+ }
245
+ }
246
+ catch (deliverErr) {
247
+ log?.error?.(`[imclaw] deliver error ${msg.topic}: ${deliverErr.message}`);
248
+ }
249
+ },
250
+ },
251
+ });
252
+ }
253
+ catch (err) {
254
+ log?.error?.(`[imclaw] dispatch error: ${err.message}`);
255
+ }
256
+ });
257
+ await bridge.start();
258
+ bridges.set(accountId, bridge);
259
+ log?.info?.(`[imclaw] account ${accountId} connected`);
260
+ // Keep alive until abort
261
+ return new Promise((resolve) => {
262
+ abortSignal.addEventListener('abort', async () => {
263
+ log?.info?.(`[imclaw] stopping account ${accountId}`);
264
+ await bridge.stop();
265
+ bridges.delete(accountId);
266
+ resolve();
267
+ }, { once: true });
268
+ });
269
+ },
270
+ },
271
+ security: {
272
+ dmPolicy: 'allow',
273
+ },
274
+ };
275
+ /**
276
+ * Store reference to plugin config.
277
+ * Called from plugin-entry.ts register() function.
278
+ */
279
+ export function setPluginConfig(config) {
280
+ pluginLevelConfig = config || {};
281
+ }
282
+ /**
283
+ * Store the PluginRuntime from api.runtime.
284
+ * This is the correct runtime for dispatching messages to agents.
285
+ */
286
+ export function setPluginRuntime(runtime) {
287
+ pluginRuntime = runtime;
288
+ }
289
+ function getPluginRuntime() {
290
+ return pluginRuntime;
291
+ }
292
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ interface CachedContact {
2
+ tinodeUid: string;
3
+ username: string;
4
+ displayName?: string;
5
+ lastSeen?: Date;
6
+ }
7
+ export declare class ContactCache {
8
+ private contacts;
9
+ set(tinodeUid: string, contact: CachedContact): void;
10
+ get(tinodeUid: string): CachedContact | undefined;
11
+ getByUsername(username: string): CachedContact | undefined;
12
+ all(): CachedContact[];
13
+ remove(tinodeUid: string): void;
14
+ clear(): void;
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=contact-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-cache.d.ts","sourceRoot":"","sources":["../src/contact-cache.ts"],"names":[],"mappings":"AAAA,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAoC;IAEpD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI;IAIpD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIjD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAO1D,GAAG,IAAI,aAAa,EAAE;IAItB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/B,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,26 @@
1
+ export class ContactCache {
2
+ contacts = new Map();
3
+ set(tinodeUid, contact) {
4
+ this.contacts.set(tinodeUid, contact);
5
+ }
6
+ get(tinodeUid) {
7
+ return this.contacts.get(tinodeUid);
8
+ }
9
+ getByUsername(username) {
10
+ for (const contact of this.contacts.values()) {
11
+ if (contact.username === username)
12
+ return contact;
13
+ }
14
+ return undefined;
15
+ }
16
+ all() {
17
+ return Array.from(this.contacts.values());
18
+ }
19
+ remove(tinodeUid) {
20
+ this.contacts.delete(tinodeUid);
21
+ }
22
+ clear() {
23
+ this.contacts.clear();
24
+ }
25
+ }
26
+ //# sourceMappingURL=contact-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-cache.js","sourceRoot":"","sources":["../src/contact-cache.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,YAAY;IACf,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEpD,GAAG,CAAC,SAAiB,EAAE,OAAsB;QAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,aAAa,CAAC,QAAgB;QAC5B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;gBAAE,OAAO,OAAO,CAAC;QACpD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,GAAG;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/daemon/cli.ts"],"names":[],"mappings":""}