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
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig, initConfigTemplate, getConfigPath } from './config.js';
3
+ import { ImclawDaemon } from './daemon.js';
4
+ const command = process.argv[2] || 'start';
5
+ async function main() {
6
+ switch (command) {
7
+ case 'init': {
8
+ initConfigTemplate();
9
+ break;
10
+ }
11
+ case 'start': {
12
+ let config;
13
+ try {
14
+ config = loadConfig();
15
+ }
16
+ catch (err) {
17
+ console.error(err.message);
18
+ console.log('\nRun "imclaw-daemon init" to create a config template.');
19
+ process.exit(1);
20
+ }
21
+ if (config.claws.length === 0) {
22
+ console.error('No claws configured. Edit ' + getConfigPath());
23
+ process.exit(1);
24
+ }
25
+ const daemon = new ImclawDaemon(config);
26
+ await daemon.start();
27
+ // Graceful shutdown
28
+ const shutdown = async () => {
29
+ await daemon.stop();
30
+ process.exit(0);
31
+ };
32
+ process.on('SIGINT', shutdown);
33
+ process.on('SIGTERM', shutdown);
34
+ break;
35
+ }
36
+ case 'status': {
37
+ try {
38
+ const res = await fetch('http://127.0.0.1:3001/api/local/health');
39
+ const data = await res.json();
40
+ console.log('IMClaw Daemon is running');
41
+ console.log(` Claws: ${data.claws?.join(', ') || 'none'}`);
42
+ }
43
+ catch {
44
+ console.log('IMClaw Daemon is not running');
45
+ }
46
+ break;
47
+ }
48
+ default:
49
+ console.log(`Usage: imclaw-daemon <command>
50
+
51
+ Commands:
52
+ init Create config template at ${getConfigPath()}
53
+ start Start the daemon (default)
54
+ status Check if daemon is running
55
+ `);
56
+ }
57
+ }
58
+ main().catch((err) => {
59
+ console.error('Fatal:', err);
60
+ process.exit(1);
61
+ });
62
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/daemon/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,kBAAkB,EAAE,CAAC;YACrB,MAAM;QACR,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,MAAM,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,GAAG,UAAU,EAAE,CAAC;YACxB,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;gBACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,KAAK,CAAC,4BAA4B,GAAG,aAAa,EAAE,CAAC,CAAC;gBAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErB,oBAAoB;YACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;gBAC1B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAChC,MAAM;QACR,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAClE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;gBACxC,OAAO,CAAC,GAAG,CAAC,YAAa,IAAY,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;YACvE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAC9C,CAAC;YACD,MAAM;QACR,CAAC;QAED;YACE,OAAO,CAAC,GAAG,CAAC;;;uCAGqB,aAAa,EAAE;;;CAGrD,CAAC,CAAC;IACD,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,27 @@
1
+ export interface DaemonConfig {
2
+ /** WebSocket URL of the IMClaw Tinode server */
3
+ serverUrl: string;
4
+ /** Tinode API key */
5
+ apiKey: string;
6
+ /** Array of Claw credentials to connect */
7
+ claws: ClawCredential[];
8
+ /** Port for local HTTP API (default 3001) */
9
+ localPort: number;
10
+ /** Path to SQLite database */
11
+ dbPath: string;
12
+ /** Human API URL for connect key exchange */
13
+ humanApiUrl?: string;
14
+ }
15
+ export interface ClawCredential {
16
+ username?: string;
17
+ password?: string;
18
+ name?: string;
19
+ clawId?: string;
20
+ /** Connect key for exchanging credentials (alternative to username/password) */
21
+ connectKey?: string;
22
+ }
23
+ export declare function getConfigPath(): string;
24
+ export declare function loadConfig(): DaemonConfig;
25
+ export declare function saveConfig(config: DaemonConfig): void;
26
+ export declare function initConfigTemplate(): void;
27
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,UAAU,IAAI,YAAY,CAkCzC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAGrD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAkBzC"}
@@ -0,0 +1,63 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.openclaw', 'imclaw');
5
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
6
+ export function getConfigPath() {
7
+ return CONFIG_FILE;
8
+ }
9
+ export function loadConfig() {
10
+ // Environment variables take precedence
11
+ if (process.env.IMCLAW_CONNECT_KEY || (process.env.IMCLAW_USERNAME && process.env.IMCLAW_PASSWORD)) {
12
+ return {
13
+ serverUrl: process.env.IMCLAW_SERVER_URL || 'ws://localhost:6060/v0/channels',
14
+ apiKey: process.env.IMCLAW_API_KEY || '',
15
+ claws: [{
16
+ username: process.env.IMCLAW_USERNAME || '',
17
+ password: process.env.IMCLAW_PASSWORD || '',
18
+ name: process.env.IMCLAW_CLAW_NAME,
19
+ connectKey: process.env.IMCLAW_CONNECT_KEY,
20
+ }],
21
+ localPort: parseInt(process.env.IMCLAW_LOCAL_PORT || '3001', 10),
22
+ dbPath: process.env.IMCLAW_DB_PATH || path.join(CONFIG_DIR, 'messages.db'),
23
+ humanApiUrl: process.env.IMCLAW_HUMAN_API_URL,
24
+ };
25
+ }
26
+ // Try config file
27
+ if (fs.existsSync(CONFIG_FILE)) {
28
+ const raw = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
29
+ return {
30
+ serverUrl: raw.serverUrl || 'ws://localhost:6060/v0/channels',
31
+ apiKey: raw.apiKey || '',
32
+ claws: raw.claws || [],
33
+ localPort: raw.localPort || 3001,
34
+ dbPath: raw.dbPath || path.join(CONFIG_DIR, 'messages.db'),
35
+ humanApiUrl: raw.humanApiUrl,
36
+ };
37
+ }
38
+ throw new Error(`No config found. Either set IMCLAW_USERNAME/IMCLAW_PASSWORD env vars, or create ${CONFIG_FILE}`);
39
+ }
40
+ export function saveConfig(config) {
41
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
42
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
43
+ }
44
+ export function initConfigTemplate() {
45
+ if (fs.existsSync(CONFIG_FILE)) {
46
+ console.log(`Config already exists: ${CONFIG_FILE}`);
47
+ return;
48
+ }
49
+ const template = {
50
+ serverUrl: 'wss://your-imclaw-server/v0/channels',
51
+ apiKey: 'your-tinode-api-key',
52
+ humanApiUrl: 'https://your-imclaw-server/api',
53
+ claws: [
54
+ { connectKey: 'imclaw_ck_your_connect_key_here', name: 'MyClaw' },
55
+ ],
56
+ localPort: 3001,
57
+ dbPath: path.join(CONFIG_DIR, 'messages.db'),
58
+ };
59
+ saveConfig(template);
60
+ console.log(`Config template created: ${CONFIG_FILE}`);
61
+ console.log('Edit it with your Claw credentials, then run: imclaw-daemon start');
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/daemon/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { DaemonConfig } from './config.js';
2
+ export declare class ImclawDaemon {
3
+ private config;
4
+ private store;
5
+ private clients;
6
+ private server;
7
+ constructor(config: DaemonConfig);
8
+ /** Exchange a connect key for Tinode credentials via Human API */
9
+ private exchangeConnectKey;
10
+ start(): Promise<void>;
11
+ private connectClaw;
12
+ stop(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/daemon/daemon.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAA8B,MAAM,aAAa,CAAC;AAGvE,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,YAAY;IAKhC,kEAAkE;YACpD,kBAAkB;IAiD1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAuBd,WAAW;IA+CnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAS5B"}
@@ -0,0 +1,121 @@
1
+ import { TinodeClient } from '../tinode-client.js';
2
+ import { MessageStore } from '../message-store.js';
3
+ import { downloadMedia } from '../media-store.js';
4
+ import { saveConfig } from './config.js';
5
+ import { createLocalApi } from './local-api.js';
6
+ export class ImclawDaemon {
7
+ config;
8
+ store;
9
+ clients = new Map();
10
+ server = null;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.store = new MessageStore(config.dbPath);
14
+ }
15
+ /** Exchange a connect key for Tinode credentials via Human API */
16
+ async exchangeConnectKey(claw) {
17
+ if (!claw.connectKey)
18
+ return claw;
19
+ if (claw.username && claw.password)
20
+ return claw; // already have creds
21
+ const apiUrl = this.config.humanApiUrl;
22
+ if (!apiUrl) {
23
+ throw new Error('humanApiUrl is required to exchange connect key');
24
+ }
25
+ console.log(`[${claw.name || 'claw'}] Exchanging connect key for credentials...`);
26
+ const res = await fetch(`${apiUrl}/claws/connect`, {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({ connectKey: claw.connectKey }),
30
+ });
31
+ if (!res.ok) {
32
+ const body = await res.json().catch(() => ({}));
33
+ throw new Error(`Connect key exchange failed: ${body.error || res.statusText}`);
34
+ }
35
+ const data = await res.json();
36
+ // Update claw with received credentials
37
+ claw.username = data.tinodeUsername;
38
+ claw.password = data.tinodePassword;
39
+ claw.clawId = data.clawId;
40
+ // Update config with resolved serverUrl and apiKey if not set
41
+ if (!this.config.serverUrl || this.config.serverUrl.includes('your-imclaw-server')) {
42
+ this.config.serverUrl = data.tinodeWsUrl;
43
+ }
44
+ if (!this.config.apiKey) {
45
+ this.config.apiKey = data.tinodeApiKey;
46
+ }
47
+ // Clear the used connect key and persist
48
+ delete claw.connectKey;
49
+ saveConfig(this.config);
50
+ console.log(`[${claw.name || claw.username}] Credentials obtained and saved.`);
51
+ return claw;
52
+ }
53
+ async start() {
54
+ console.log(`IMClaw Daemon starting → ${this.config.serverUrl}`);
55
+ // Resolve connect keys before connecting
56
+ for (let i = 0; i < this.config.claws.length; i++) {
57
+ this.config.claws[i] = await this.exchangeConnectKey(this.config.claws[i]);
58
+ }
59
+ // Connect each Claw
60
+ for (const claw of this.config.claws) {
61
+ await this.connectClaw(claw);
62
+ }
63
+ // Start local HTTP API
64
+ this.server = createLocalApi({
65
+ port: this.config.localPort,
66
+ store: this.store,
67
+ clients: this.clients,
68
+ });
69
+ console.log(`IMClaw Daemon ready. ${this.clients.size} claw(s) connected.`);
70
+ }
71
+ async connectClaw(claw) {
72
+ if (!claw.username || !claw.password) {
73
+ console.error(`[${claw.name || 'claw'}] Missing username/password, skipping.`);
74
+ return;
75
+ }
76
+ const client = new TinodeClient({
77
+ serverUrl: this.config.serverUrl,
78
+ username: claw.username,
79
+ password: claw.password,
80
+ apiKey: this.config.apiKey,
81
+ userAgent: `IMClaw-Daemon/${claw.username}`,
82
+ });
83
+ const username = claw.username;
84
+ client.on('message', async (msg) => {
85
+ let content = msg.content;
86
+ // Auto-download media files to local storage
87
+ if (content && typeof content === 'object' && content.tp && content.url) {
88
+ const localFile = await downloadMedia(content.url, content.name || 'media', msg.seqId);
89
+ if (localFile) {
90
+ content = { ...content, localFile };
91
+ }
92
+ }
93
+ this.store.saveMessage(msg.topic, msg.from, msg.seqId, content, msg.timestamp, username);
94
+ const preview = typeof msg.content === 'string'
95
+ ? msg.content.substring(0, 60)
96
+ : (msg.content?.tp ? `[${msg.content.tp}:${msg.content.name || '?'}]` : '?');
97
+ console.log(`[${username}] ← ${msg.from} ${preview}`);
98
+ });
99
+ client.on('error', (err) => {
100
+ console.error(`[${username}] error: ${err.message}`);
101
+ });
102
+ try {
103
+ await client.connect();
104
+ this.clients.set(username, client);
105
+ console.log(`[${username}] connected`);
106
+ }
107
+ catch (err) {
108
+ console.error(`[${username}] failed to connect: ${err.message}`);
109
+ }
110
+ }
111
+ async stop() {
112
+ for (const [, client] of this.clients) {
113
+ client.disconnect();
114
+ }
115
+ this.clients.clear();
116
+ this.server?.close();
117
+ this.store.close();
118
+ console.log('IMClaw Daemon stopped.');
119
+ }
120
+ }
121
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/daemon/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import http from 'http';
2
+ import { MessageStore } from '../message-store.js';
3
+ import { TinodeClient } from '../tinode-client.js';
4
+ export interface LocalApiOptions {
5
+ port: number;
6
+ store: MessageStore;
7
+ clients: Map<string, TinodeClient>;
8
+ }
9
+ export declare function createLocalApi(options: LocalApiOptions): http.Server;
10
+ //# sourceMappingURL=local-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-api.d.ts","sourceRoot":"","sources":["../../src/daemon/local-api.ts"],"names":[],"mappings":"AAEA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAenD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACpC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAC,MAAM,CA0HpE"}
@@ -0,0 +1,139 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import http from 'http';
4
+ import { getMediaDir } from '../media-store.js';
5
+ function tryParseJSON(str) {
6
+ try {
7
+ return JSON.parse(str);
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ function rewriteMediaUrl(content, baseUrl) {
14
+ const parsed = typeof content === 'string' ? tryParseJSON(content) : content;
15
+ if (parsed && parsed.localFile) {
16
+ return { ...parsed, url: `${baseUrl}/api/local/media/${parsed.localFile}` };
17
+ }
18
+ return typeof content === 'string' ? (parsed || content) : content;
19
+ }
20
+ export function createLocalApi(options) {
21
+ const { port, store, clients } = options;
22
+ const app = express();
23
+ // Only allow requests from localhost
24
+ app.use((req, res, next) => {
25
+ const ip = req.ip || req.socket.remoteAddress || '';
26
+ if (ip === '127.0.0.1' || ip === '::1' || ip === '::ffff:127.0.0.1') {
27
+ next();
28
+ }
29
+ else {
30
+ res.status(403).json({ error: 'Forbidden' });
31
+ }
32
+ });
33
+ app.use(cors({ origin: (origin, cb) => {
34
+ // Allow requests with no origin (curl, local apps) or localhost origins
35
+ if (!origin || /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) {
36
+ cb(null, true);
37
+ }
38
+ else {
39
+ cb(new Error('CORS not allowed'));
40
+ }
41
+ } }));
42
+ app.use(express.json());
43
+ // Serve downloaded media files (deny dotfiles for safety)
44
+ app.use('/api/local/media', express.static(getMediaDir(), { dotfiles: 'deny' }));
45
+ // Health check — Web UI uses this to detect local daemon
46
+ app.get('/api/local/health', (_req, res) => {
47
+ res.json({
48
+ status: 'ok',
49
+ source: 'local-daemon',
50
+ claws: Array.from(clients.keys()),
51
+ timestamp: new Date().toISOString(),
52
+ });
53
+ });
54
+ // List all conversations from local SQLite (full history)
55
+ // ?owner=<claw_username> — agent-scoped; omit for human (full view)
56
+ app.get('/api/local/conversations', (req, res) => {
57
+ try {
58
+ const baseUrl = `${req.protocol}://${req.get('host')}`;
59
+ const owner = req.query.owner;
60
+ const conversations = owner
61
+ ? store.getConversationsByOwner(owner)
62
+ : store.getConversations();
63
+ res.json(conversations.map((c) => ({
64
+ name: c.topic,
65
+ lastSeq: c.last_seq,
66
+ messageCount: owner ? undefined : store.getMessageCount(c.topic),
67
+ lastMessage: c.last_message ? {
68
+ from: c.last_message.from_user,
69
+ content: rewriteMediaUrl(c.last_message.content, baseUrl),
70
+ timestamp: c.last_message.timestamp,
71
+ } : null,
72
+ })));
73
+ }
74
+ catch (err) {
75
+ console.error('Local API error:', err);
76
+ res.status(500).json({ error: 'Internal server error' });
77
+ }
78
+ });
79
+ // Get messages for a topic from local SQLite (full history)
80
+ // ?owner=<claw_username> — agent-scoped; omit for human (full view)
81
+ app.get('/api/local/conversations/:topic/messages', (req, res) => {
82
+ try {
83
+ const baseUrl = `${req.protocol}://${req.get('host')}`;
84
+ const cursor = Math.max(0, parseInt(req.query.cursor) || 0);
85
+ const limit = Math.min(Math.max(1, parseInt(req.query.limit) || 50), 200);
86
+ const owner = req.query.owner;
87
+ const messages = owner
88
+ ? store.getMessagesByOwner(owner, req.params.topic, limit, cursor || undefined)
89
+ : store.getMessages(req.params.topic, limit, cursor || undefined);
90
+ res.json(messages.map((m) => ({
91
+ seqid: m.seq_id,
92
+ createdat: m.timestamp,
93
+ from: m.from_user,
94
+ content: rewriteMediaUrl(m.content, baseUrl),
95
+ })));
96
+ }
97
+ catch (err) {
98
+ console.error('Local API error:', err);
99
+ res.status(500).json({ error: 'Internal server error' });
100
+ }
101
+ });
102
+ // Send a message (for agent use)
103
+ app.post('/api/local/send', async (req, res) => {
104
+ try {
105
+ const { clawUsername, topic, content } = req.body;
106
+ if (!clawUsername || !topic || !content) {
107
+ res.status(400).json({ error: 'Missing clawUsername, topic, or content' });
108
+ return;
109
+ }
110
+ const client = clients.get(clawUsername);
111
+ if (!client) {
112
+ res.status(404).json({ error: `Claw ${clawUsername} not connected` });
113
+ return;
114
+ }
115
+ const seq = await client.sendMessage(topic, content);
116
+ // Save outgoing message locally too (tagged with ownerClawId)
117
+ store.saveMessage(topic, clawUsername, seq, content, new Date(), clawUsername);
118
+ res.json({ seq });
119
+ }
120
+ catch (err) {
121
+ console.error('Local API error:', err);
122
+ res.status(500).json({ error: 'Internal server error' });
123
+ }
124
+ });
125
+ // Status of connected claws
126
+ app.get('/api/local/status', (_req, res) => {
127
+ const status = Array.from(clients.entries()).map(([username]) => ({
128
+ username,
129
+ connected: true,
130
+ }));
131
+ res.json({ claws: status });
132
+ });
133
+ const server = http.createServer(app);
134
+ server.listen(port, '127.0.0.1', () => {
135
+ console.log(`Local API listening on http://127.0.0.1:${port}`);
136
+ });
137
+ return server;
138
+ }
139
+ //# sourceMappingURL=local-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-api.js","sourceRoot":"","sources":["../../src/daemon/local-api.ts"],"names":[],"mappings":"AAAA,OAAO,OAAoB,MAAM,SAAS,CAAC;AAC3C,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,OAAY,EAAE,OAAe;IACpD,MAAM,MAAM,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC7E,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,oBAAoB,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AACrE,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IAEtB,qCAAqC;IACrC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;QACpD,IAAI,EAAE,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,kBAAkB,EAAE,CAAC;YACpE,IAAI,EAAE,CAAC;QACT,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE;YACpC,wEAAwE;YACxE,IAAI,CAAC,MAAM,IAAI,8CAA8C,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3E,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,EAAE,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC;YACpC,CAAC;QACH,CAAC,EAAC,CAAC,CAAC,CAAC;IACL,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,0DAA0D;IAC1D,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAEjF,yDAAyD;IACzD,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACzC,GAAG,CAAC,IAAI,CAAC;YACP,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,cAAc;YACtB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAC1D,oEAAoE;IACpE,GAAG,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,QAAQ,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAA2B,CAAC;YACpD,MAAM,aAAa,GAAG,KAAK;gBACzB,CAAC,CAAC,KAAK,CAAC,uBAAuB,CAAC,KAAK,CAAC;gBACtC,CAAC,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;YAC7B,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACjC,IAAI,EAAE,CAAC,CAAC,KAAK;gBACb,OAAO,EAAE,CAAC,CAAC,QAAQ;gBACnB,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC;gBAChE,WAAW,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;oBAC5B,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,SAAS;oBAC9B,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC;oBACzD,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC,SAAS;iBACpC,CAAC,CAAC,CAAC,IAAI;aACT,CAAC,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;YACvC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,4DAA4D;IAC5D,oEAAoE;IACpE,GAAG,CAAC,GAAG,CAAC,0CAA0C,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC/D,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,QAAQ,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,KAAe,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;YACpF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAA2B,CAAC;YACpD,MAAM,QAAQ,GAAG,KAAK;gBACpB,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,SAAS,CAAC;gBAC/E,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;YACpE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5B,KAAK,EAAE,CAAC,CAAC,MAAM;gBACf,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,IAAI,EAAE,CAAC,CAAC,SAAS;gBACjB,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC;aAC7C,CAAC,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;YACvC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,iCAAiC;IACjC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC7C,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAClD,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;gBACxC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC,CAAC;gBAC3E,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,YAAY,gBAAgB,EAAE,CAAC,CAAC;gBACtE,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACrD,8DAA8D;YAC9D,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,EAAE,YAAY,CAAC,CAAC;YAC/E,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;YACvC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,GAAG,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;YAChE,QAAQ;YACR,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC,CAAC;QACJ,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;QACpC,OAAO,CAAC,GAAG,CAAC,2CAA2C,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { MessageStore } from './message-store.js';
2
+ import { ContactCache } from './contact-cache.js';
3
+ export interface ChannelConfig {
4
+ tinodeServerUrl: string;
5
+ tinodeUsername: string;
6
+ tinodePassword: string;
7
+ tinodeApiKey?: string;
8
+ dbPath?: string;
9
+ httpBaseUrl?: string;
10
+ clawId?: string;
11
+ }
12
+ export interface InboundMessage {
13
+ topic: string;
14
+ from: string;
15
+ content: any;
16
+ seqId: number;
17
+ timestamp: Date;
18
+ }
19
+ export type MessageHandler = (message: InboundMessage) => void | Promise<void>;
20
+ /**
21
+ * ImclawBridge - adapts Tinode messaging for OpenClaw agents.
22
+ *
23
+ * Inbound: Tinode {data} → InboundMessage → handler callback
24
+ * Outbound: Agent calls sendMessage() → TinodeClient.sendMessage()
25
+ */
26
+ export interface UploadResult {
27
+ url: string;
28
+ name: string;
29
+ size: number;
30
+ mime: string;
31
+ }
32
+ export declare class ImclawBridge {
33
+ private client;
34
+ private store;
35
+ private cache;
36
+ private config;
37
+ private messageHandler;
38
+ constructor(config: ChannelConfig);
39
+ onMessage(handler: MessageHandler): void;
40
+ start(): Promise<void>;
41
+ sendMessage(topicName: string, content: any): Promise<number>;
42
+ uploadFile(fileBuffer: Buffer, filename: string, mime?: string): Promise<UploadResult>;
43
+ sendImage(topic: string, imageBuffer: Buffer, filename: string, mime?: string): Promise<number>;
44
+ sendFile(topic: string, fileBuffer: Buffer, filename: string, mime?: string): Promise<number>;
45
+ getStore(): MessageStore;
46
+ getContactCache(): ContactCache;
47
+ stop(): Promise<void>;
48
+ }
49
+ //# sourceMappingURL=imclaw-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imclaw-bridge.d.ts","sourceRoot":"","sources":["../src/imclaw-bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,GAAG,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,cAAc,CAA+B;gBAEzC,MAAM,EAAE,aAAa;IAsDjC,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7D,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuCtF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW/F,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWnG,QAAQ,IAAI,YAAY;IAIxB,eAAe,IAAI,YAAY;IAIzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B"}