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.
- package/README.md +132 -0
- package/dist/accounts.d.ts +15 -0
- package/dist/accounts.d.ts.map +1 -0
- package/dist/accounts.js +23 -0
- package/dist/accounts.js.map +1 -0
- package/dist/channel.d.ts +67 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +292 -0
- package/dist/channel.js.map +1 -0
- package/dist/contact-cache.d.ts +17 -0
- package/dist/contact-cache.d.ts.map +1 -0
- package/dist/contact-cache.js +26 -0
- package/dist/contact-cache.js.map +1 -0
- package/dist/daemon/cli.d.ts +3 -0
- package/dist/daemon/cli.d.ts.map +1 -0
- package/dist/daemon/cli.js +62 -0
- package/dist/daemon/cli.js.map +1 -0
- package/dist/daemon/config.d.ts +27 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +63 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/daemon.d.ts +14 -0
- package/dist/daemon/daemon.d.ts.map +1 -0
- package/dist/daemon/daemon.js +121 -0
- package/dist/daemon/daemon.js.map +1 -0
- package/dist/daemon/local-api.d.ts +10 -0
- package/dist/daemon/local-api.d.ts.map +1 -0
- package/dist/daemon/local-api.js +139 -0
- package/dist/daemon/local-api.js.map +1 -0
- package/dist/imclaw-bridge.d.ts +49 -0
- package/dist/imclaw-bridge.d.ts.map +1 -0
- package/dist/imclaw-bridge.js +126 -0
- package/dist/imclaw-bridge.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/media-store.d.ts +4 -0
- package/dist/media-store.d.ts.map +1 -0
- package/dist/media-store.js +33 -0
- package/dist/media-store.js.map +1 -0
- package/dist/message-store.d.ts +32 -0
- package/dist/message-store.d.ts.map +1 -0
- package/dist/message-store.js +103 -0
- package/dist/message-store.js.map +1 -0
- package/dist/plugin-entry.d.ts +2 -0
- package/dist/plugin-entry.d.ts.map +1 -0
- package/dist/plugin-entry.js +12 -0
- package/dist/plugin-entry.js.map +1 -0
- package/dist/tinode-client.d.ts +43 -0
- package/dist/tinode-client.d.ts.map +1 -0
- package/dist/tinode-client.js +250 -0
- package/dist/tinode-client.js.map +1 -0
- package/openclaw.plugin.json +45 -0
- 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":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AA0BpB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;AAClE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,wCAAwC;IACxC,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACnG,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,iCAAiC;YAC7E,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;YACxC,KAAK,EAAE,CAAC;oBACN,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;oBAC3C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE;oBAC3C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;oBAClC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;iBAC3C,CAAC;YACF,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,MAAM,EAAE,EAAE,CAAC;YAChE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC;YAC1E,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;SAC9C,CAAC;IACJ,CAAC;IAED,kBAAkB;IAClB,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9D,OAAO;YACL,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,iCAAiC;YAC7D,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;YACxB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,IAAI;YAChC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC;YAC1D,WAAW,EAAE,GAAG,CAAC,WAAW;SAC7B,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,KAAK,CACb,mFAAmF,WAAW,EAAE,CACjG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,0BAA0B,WAAW,EAAE,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IACD,MAAM,QAAQ,GAAiB;QAC7B,SAAS,EAAE,sCAAsC;QACjD,MAAM,EAAE,qBAAqB;QAC7B,WAAW,EAAE,gCAAgC;QAC7C,KAAK,EAAE;YACL,EAAE,UAAU,EAAE,iCAAiC,EAAE,IAAI,EAAE,QAAQ,EAAE;SAClE;QACD,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC;KAC7C,CAAC;IACF,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,4BAA4B,WAAW,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;AACnF,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -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"}
|