visionclaw 0.1.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 +116 -0
- package/dist/agent/context.d.ts +56 -0
- package/dist/agent/context.d.ts.map +1 -0
- package/dist/agent/context.js +142 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/loop.d.ts +18 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +323 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session.d.ts +49 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +200 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +10 -0
- package/dist/agent/system-prompt.d.ts.map +1 -0
- package/dist/agent/system-prompt.js +167 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/calendar/google-calendar.d.ts +46 -0
- package/dist/calendar/google-calendar.d.ts.map +1 -0
- package/dist/calendar/google-calendar.js +132 -0
- package/dist/calendar/google-calendar.js.map +1 -0
- package/dist/calendar/scheduler.d.ts +7 -0
- package/dist/calendar/scheduler.d.ts.map +1 -0
- package/dist/calendar/scheduler.js +33 -0
- package/dist/calendar/scheduler.js.map +1 -0
- package/dist/channels/discord.d.ts +19 -0
- package/dist/channels/discord.d.ts.map +1 -0
- package/dist/channels/discord.js +169 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/gmail.d.ts +31 -0
- package/dist/channels/gmail.d.ts.map +1 -0
- package/dist/channels/gmail.js +300 -0
- package/dist/channels/gmail.js.map +1 -0
- package/dist/channels/interface.d.ts +45 -0
- package/dist/channels/interface.d.ts.map +1 -0
- package/dist/channels/interface.js +2 -0
- package/dist/channels/interface.js.map +1 -0
- package/dist/channels/manager.d.ts +36 -0
- package/dist/channels/manager.d.ts.map +1 -0
- package/dist/channels/manager.js +108 -0
- package/dist/channels/manager.js.map +1 -0
- package/dist/channels/queue.d.ts +17 -0
- package/dist/channels/queue.d.ts.map +1 -0
- package/dist/channels/queue.js +85 -0
- package/dist/channels/queue.js.map +1 -0
- package/dist/channels/slack.d.ts +17 -0
- package/dist/channels/slack.d.ts.map +1 -0
- package/dist/channels/slack.js +142 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/sms.d.ts +19 -0
- package/dist/channels/sms.d.ts.map +1 -0
- package/dist/channels/sms.js +111 -0
- package/dist/channels/sms.js.map +1 -0
- package/dist/channels/telegram.d.ts +28 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +246 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/whatsapp.d.ts +28 -0
- package/dist/channels/whatsapp.d.ts.map +1 -0
- package/dist/channels/whatsapp.js +292 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/config/index.d.ts +24 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +104 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +227 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +45 -0
- package/dist/config/types.js.map +1 -0
- package/dist/files.d.ts +20 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +82 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +384 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory/store.d.ts +24 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +71 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/obs/server.d.ts +10 -0
- package/dist/obs/server.d.ts.map +1 -0
- package/dist/obs/server.js +406 -0
- package/dist/obs/server.js.map +1 -0
- package/dist/onboarding/google-auth.d.ts +11 -0
- package/dist/onboarding/google-auth.d.ts.map +1 -0
- package/dist/onboarding/google-auth.js +113 -0
- package/dist/onboarding/google-auth.js.map +1 -0
- package/dist/onboarding/index.d.ts +2 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +213 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/onboarding/macos-permissions.d.ts +37 -0
- package/dist/onboarding/macos-permissions.d.ts.map +1 -0
- package/dist/onboarding/macos-permissions.js +207 -0
- package/dist/onboarding/macos-permissions.js.map +1 -0
- package/dist/skills/install.d.ts +7 -0
- package/dist/skills/install.d.ts.map +1 -0
- package/dist/skills/install.js +63 -0
- package/dist/skills/install.js.map +1 -0
- package/dist/tools/browser.d.ts +7 -0
- package/dist/tools/browser.d.ts.map +1 -0
- package/dist/tools/browser.js +202 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/calendar.d.ts +12 -0
- package/dist/tools/calendar.d.ts.map +1 -0
- package/dist/tools/calendar.js +210 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/computer-use.d.ts +28 -0
- package/dist/tools/computer-use.d.ts.map +1 -0
- package/dist/tools/computer-use.js +311 -0
- package/dist/tools/computer-use.js.map +1 -0
- package/dist/tools/coordinate-resolver.d.ts +26 -0
- package/dist/tools/coordinate-resolver.d.ts.map +1 -0
- package/dist/tools/coordinate-resolver.js +157 -0
- package/dist/tools/coordinate-resolver.js.map +1 -0
- package/dist/tools/desktop-executor.d.ts +52 -0
- package/dist/tools/desktop-executor.d.ts.map +1 -0
- package/dist/tools/desktop-executor.js +202 -0
- package/dist/tools/desktop-executor.js.map +1 -0
- package/dist/tools/finish.d.ts +5 -0
- package/dist/tools/finish.d.ts.map +1 -0
- package/dist/tools/finish.js +18 -0
- package/dist/tools/finish.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory.d.ts +14 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +269 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/notify.d.ts +12 -0
- package/dist/tools/notify.d.ts.map +1 -0
- package/dist/tools/notify.js +108 -0
- package/dist/tools/notify.js.map +1 -0
- package/dist/tools/screenshot.d.ts +7 -0
- package/dist/tools/screenshot.d.ts.map +1 -0
- package/dist/tools/screenshot.js +189 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/skill.d.ts +8 -0
- package/dist/tools/skill.d.ts.map +1 -0
- package/dist/tools/skill.js +133 -0
- package/dist/tools/skill.js.map +1 -0
- package/dist/tools/upgrade.d.ts +5 -0
- package/dist/tools/upgrade.d.ts.map +1 -0
- package/dist/tools/upgrade.js +89 -0
- package/dist/tools/upgrade.js.map +1 -0
- package/dist/tools/wait.d.ts +5 -0
- package/dist/tools/wait.d.ts.map +1 -0
- package/dist/tools/wait.js +21 -0
- package/dist/tools/wait.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { EventEmitter } from "node:events";
|
|
2
|
+
/**
|
|
3
|
+
* Attachment on a command message (image, file, etc.)
|
|
4
|
+
*/
|
|
5
|
+
export interface MessageAttachment {
|
|
6
|
+
type: "image" | "file" | "audio" | "video";
|
|
7
|
+
url?: string;
|
|
8
|
+
localPath?: string;
|
|
9
|
+
base64?: string;
|
|
10
|
+
mimeType?: string;
|
|
11
|
+
filename?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A normalized incoming message from any channel.
|
|
15
|
+
*/
|
|
16
|
+
export interface CommandMessage {
|
|
17
|
+
id: string;
|
|
18
|
+
channel: string;
|
|
19
|
+
sender: string;
|
|
20
|
+
text: string;
|
|
21
|
+
attachments: MessageAttachment[];
|
|
22
|
+
timestamp: string;
|
|
23
|
+
/** Raw channel-specific metadata */
|
|
24
|
+
meta?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Interface that all channel adapters must implement.
|
|
28
|
+
*/
|
|
29
|
+
export interface ChannelAdapter extends EventEmitter {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** Start listening for messages */
|
|
32
|
+
start(): Promise<void>;
|
|
33
|
+
/** Stop listening and clean up */
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
/** Send a message to a recipient on this channel, optionally with attachments */
|
|
36
|
+
sendMessage(to: string, message: string, attachments?: MessageAttachment[]): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Events emitted by channel adapters.
|
|
40
|
+
*/
|
|
41
|
+
export interface ChannelEvents {
|
|
42
|
+
message: (msg: CommandMessage) => void;
|
|
43
|
+
error: (err: Error) => void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/channels/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,mCAAmC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,kCAAkC;IAClC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB,iFAAiF;IACjF,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/channels/interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { VisionClawConfig } from "../config/types.js";
|
|
3
|
+
import type { ChannelAdapter, MessageAttachment } from "./interface.js";
|
|
4
|
+
import type { MessageQueue } from "./queue.js";
|
|
5
|
+
/**
|
|
6
|
+
* Manages all enabled channel adapters.
|
|
7
|
+
* Routes incoming messages to the message queue and provides
|
|
8
|
+
* a unified sendMessage interface for the agent.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ChannelManager extends EventEmitter {
|
|
11
|
+
private adapters;
|
|
12
|
+
private config;
|
|
13
|
+
private queue;
|
|
14
|
+
constructor(config: VisionClawConfig, queue: MessageQueue);
|
|
15
|
+
/**
|
|
16
|
+
* Register a channel adapter.
|
|
17
|
+
*/
|
|
18
|
+
registerAdapter(adapter: ChannelAdapter): void;
|
|
19
|
+
/**
|
|
20
|
+
* Start all registered adapters.
|
|
21
|
+
*/
|
|
22
|
+
startAll(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Send a message through a specific channel.
|
|
25
|
+
*/
|
|
26
|
+
sendMessage(channel: string, to: string, message: string, attachments?: MessageAttachment[]): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Get the names of all registered (started) adapters.
|
|
29
|
+
*/
|
|
30
|
+
getEnabledChannels(): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Stop all channel adapters.
|
|
33
|
+
*/
|
|
34
|
+
stopAll(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/channels/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAkB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,KAAK,CAAe;gBAEhB,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY;IAMzD;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAa9C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA6C/B;;OAEG;IACG,WAAW,CACf,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,iBAAiB,EAAE,GAChC,OAAO,CAAC,IAAI,CAAC;IAQhB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAU/B"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
/**
|
|
4
|
+
* Manages all enabled channel adapters.
|
|
5
|
+
* Routes incoming messages to the message queue and provides
|
|
6
|
+
* a unified sendMessage interface for the agent.
|
|
7
|
+
*/
|
|
8
|
+
export class ChannelManager extends EventEmitter {
|
|
9
|
+
adapters = new Map();
|
|
10
|
+
config;
|
|
11
|
+
queue;
|
|
12
|
+
constructor(config, queue) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.queue = queue;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Register a channel adapter.
|
|
19
|
+
*/
|
|
20
|
+
registerAdapter(adapter) {
|
|
21
|
+
this.adapters.set(adapter.name, adapter);
|
|
22
|
+
adapter.on("message", (msg) => {
|
|
23
|
+
this.queue.enqueue(msg);
|
|
24
|
+
this.emit("message", msg);
|
|
25
|
+
});
|
|
26
|
+
adapter.on("error", (err) => {
|
|
27
|
+
logger.err(`[Channel:${adapter.name}] Error: ${err.message}`);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Start all registered adapters.
|
|
32
|
+
*/
|
|
33
|
+
async startAll() {
|
|
34
|
+
// Dynamically import and register adapters based on config
|
|
35
|
+
// Gmail is always enabled
|
|
36
|
+
try {
|
|
37
|
+
const { GmailAdapter } = await import("./gmail.js");
|
|
38
|
+
const gmail = new GmailAdapter(this.config);
|
|
39
|
+
this.registerAdapter(gmail);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
logger.warn(`Failed to load Gmail adapter: ${err instanceof Error ? err.message : String(err)}`);
|
|
43
|
+
}
|
|
44
|
+
// Telegram
|
|
45
|
+
if (this.config.channels.telegram?.enabled) {
|
|
46
|
+
try {
|
|
47
|
+
const { TelegramAdapter } = await import("./telegram.js");
|
|
48
|
+
const telegram = new TelegramAdapter(this.config);
|
|
49
|
+
this.registerAdapter(telegram);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
logger.warn(`Failed to load Telegram adapter: ${err instanceof Error ? err.message : String(err)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Discord
|
|
56
|
+
if (this.config.channels.discord?.enabled) {
|
|
57
|
+
try {
|
|
58
|
+
const { DiscordAdapter } = await import("./discord.js");
|
|
59
|
+
const discord = new DiscordAdapter(this.config);
|
|
60
|
+
this.registerAdapter(discord);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
logger.warn(`Failed to load Discord adapter: ${err instanceof Error ? err.message : String(err)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Start all registered adapters
|
|
67
|
+
for (const [name, adapter] of this.adapters) {
|
|
68
|
+
try {
|
|
69
|
+
await adapter.start();
|
|
70
|
+
logger.channel(name, "Started");
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
logger.err(`[Channel:${name}] Failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Send a message through a specific channel.
|
|
79
|
+
*/
|
|
80
|
+
async sendMessage(channel, to, message, attachments) {
|
|
81
|
+
const adapter = this.adapters.get(channel);
|
|
82
|
+
if (!adapter) {
|
|
83
|
+
throw new Error(`Channel "${channel}" not found or not enabled`);
|
|
84
|
+
}
|
|
85
|
+
await adapter.sendMessage(to, message, attachments);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the names of all registered (started) adapters.
|
|
89
|
+
*/
|
|
90
|
+
getEnabledChannels() {
|
|
91
|
+
return Array.from(this.adapters.keys());
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Stop all channel adapters.
|
|
95
|
+
*/
|
|
96
|
+
async stopAll() {
|
|
97
|
+
for (const [name, adapter] of this.adapters) {
|
|
98
|
+
try {
|
|
99
|
+
await adapter.stop();
|
|
100
|
+
logger.channel(name, "Stopped");
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
logger.err(`[Channel:${name}] Failed to stop: ${err instanceof Error ? err.message : String(err)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/channels/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAItC;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,YAAY;IACtC,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC7C,MAAM,CAAmB;IACzB,KAAK,CAAe;IAE5B,YAAY,MAAwB,EAAE,KAAmB;QACvD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,OAAuB;QACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEzC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAmB,EAAE,EAAE;YAC5C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YACjC,MAAM,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,IAAI,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,2DAA2D;QAE3D,0BAA0B;QAC1B,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACpD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnG,CAAC;QAEH,WAAW;QACX,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACH,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;gBAC1D,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACjC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,oCAAoC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtG,CAAC;QACH,CAAC;QAED,UAAU;QACV,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACH,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;gBACxD,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrG,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,sBAAsB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACvG,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CACf,OAAe,EACf,EAAU,EACV,OAAe,EACf,WAAiC;QAEjC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,4BAA4B,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,kBAAkB;QAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtG,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CommandMessage } from "./interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* Persistent message queue backed by SQLite.
|
|
4
|
+
* Incoming channel messages are enqueued here and dequeued by the agent loop.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MessageQueue {
|
|
7
|
+
private db;
|
|
8
|
+
constructor(customDbPath?: string);
|
|
9
|
+
enqueue(msg: CommandMessage): void;
|
|
10
|
+
dequeue(): CommandMessage | null;
|
|
11
|
+
peek(): CommandMessage | null;
|
|
12
|
+
isEmpty(): boolean;
|
|
13
|
+
size(): number;
|
|
14
|
+
clear(): void;
|
|
15
|
+
close(): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/channels/queue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAgBrD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAoB;gBAElB,YAAY,CAAC,EAAE,MAAM;IAkBjC,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAiBlC,OAAO,IAAI,cAAc,GAAG,IAAI;IAsBhC,IAAI,IAAI,cAAc,GAAG,IAAI;IAoB7B,OAAO,IAAI,OAAO;IAOlB,IAAI,IAAI,MAAM;IAOd,KAAK,IAAI,IAAI;IAIb,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getConfigDir } from "../config/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Persistent message queue backed by SQLite.
|
|
6
|
+
* Incoming channel messages are enqueued here and dequeued by the agent loop.
|
|
7
|
+
*/
|
|
8
|
+
export class MessageQueue {
|
|
9
|
+
db;
|
|
10
|
+
constructor(customDbPath) {
|
|
11
|
+
const dbPath = customDbPath ?? path.join(getConfigDir(), "queue.db");
|
|
12
|
+
this.db = new Database(dbPath);
|
|
13
|
+
this.db.pragma("journal_mode = WAL");
|
|
14
|
+
this.db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
channel TEXT NOT NULL,
|
|
18
|
+
sender TEXT NOT NULL,
|
|
19
|
+
text TEXT NOT NULL,
|
|
20
|
+
attachments TEXT NOT NULL DEFAULT '[]',
|
|
21
|
+
timestamp TEXT NOT NULL,
|
|
22
|
+
meta TEXT,
|
|
23
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
enqueue(msg) {
|
|
28
|
+
this.db
|
|
29
|
+
.prepare(`INSERT OR IGNORE INTO messages (id, channel, sender, text, attachments, timestamp, meta)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
31
|
+
.run(msg.id, msg.channel, msg.sender, msg.text, JSON.stringify(msg.attachments), msg.timestamp, msg.meta ? JSON.stringify(msg.meta) : null);
|
|
32
|
+
}
|
|
33
|
+
dequeue() {
|
|
34
|
+
const row = this.db
|
|
35
|
+
.prepare(`SELECT * FROM messages ORDER BY created_at ASC LIMIT 1`)
|
|
36
|
+
.get();
|
|
37
|
+
if (!row)
|
|
38
|
+
return null;
|
|
39
|
+
this.db.prepare(`DELETE FROM messages WHERE id = ?`).run(row.id);
|
|
40
|
+
return {
|
|
41
|
+
id: row.id,
|
|
42
|
+
channel: row.channel,
|
|
43
|
+
sender: row.sender,
|
|
44
|
+
text: row.text,
|
|
45
|
+
attachments: JSON.parse(row.attachments),
|
|
46
|
+
timestamp: row.timestamp,
|
|
47
|
+
meta: row.meta ? JSON.parse(row.meta) : undefined,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
peek() {
|
|
51
|
+
const row = this.db
|
|
52
|
+
.prepare(`SELECT * FROM messages ORDER BY created_at ASC LIMIT 1`)
|
|
53
|
+
.get();
|
|
54
|
+
if (!row)
|
|
55
|
+
return null;
|
|
56
|
+
return {
|
|
57
|
+
id: row.id,
|
|
58
|
+
channel: row.channel,
|
|
59
|
+
sender: row.sender,
|
|
60
|
+
text: row.text,
|
|
61
|
+
attachments: JSON.parse(row.attachments),
|
|
62
|
+
timestamp: row.timestamp,
|
|
63
|
+
meta: row.meta ? JSON.parse(row.meta) : undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
isEmpty() {
|
|
67
|
+
const row = this.db
|
|
68
|
+
.prepare(`SELECT COUNT(*) as count FROM messages`)
|
|
69
|
+
.get();
|
|
70
|
+
return row.count === 0;
|
|
71
|
+
}
|
|
72
|
+
size() {
|
|
73
|
+
const row = this.db
|
|
74
|
+
.prepare(`SELECT COUNT(*) as count FROM messages`)
|
|
75
|
+
.get();
|
|
76
|
+
return row.count;
|
|
77
|
+
}
|
|
78
|
+
clear() {
|
|
79
|
+
this.db.prepare(`DELETE FROM messages`).run();
|
|
80
|
+
}
|
|
81
|
+
close() {
|
|
82
|
+
this.db.close();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/channels/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { VisionClawConfig } from "../config/types.js";
|
|
3
|
+
import type { ChannelAdapter, MessageAttachment } from "./interface.js";
|
|
4
|
+
/**
|
|
5
|
+
* Slack channel adapter using Bolt framework with Socket Mode.
|
|
6
|
+
* Supports multimodal inbound (file attachments) and outbound (images, files).
|
|
7
|
+
*/
|
|
8
|
+
export declare class SlackAdapter extends EventEmitter implements ChannelAdapter {
|
|
9
|
+
readonly name = "slack";
|
|
10
|
+
private config;
|
|
11
|
+
private app;
|
|
12
|
+
constructor(config: VisionClawConfig);
|
|
13
|
+
start(): Promise<void>;
|
|
14
|
+
stop(): Promise<void>;
|
|
15
|
+
sendMessage(to: string, message: string, attachments?: MessageAttachment[]): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/channels/slack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAkB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExF;;;GAGG;AACH,qBAAa,YAAa,SAAQ,YAAa,YAAW,cAAc;IACtE,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,GAAG,CAAoB;gBAEnB,MAAM,EAAE,gBAAgB;IAK9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2FtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrB,WAAW,CACf,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,iBAAiB,EAAE,GAChC,OAAO,CAAC,IAAI,CAAC;CAuCjB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { App } from "@slack/bolt";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
import { uploadMedia } from "../files.js";
|
|
6
|
+
/**
|
|
7
|
+
* Slack channel adapter using Bolt framework with Socket Mode.
|
|
8
|
+
* Supports multimodal inbound (file attachments) and outbound (images, files).
|
|
9
|
+
*/
|
|
10
|
+
export class SlackAdapter extends EventEmitter {
|
|
11
|
+
name = "slack";
|
|
12
|
+
config;
|
|
13
|
+
app = null;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
super();
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
async start() {
|
|
19
|
+
const slackConfig = this.config.channels.slack;
|
|
20
|
+
if (!slackConfig)
|
|
21
|
+
throw new Error("Slack channel not configured");
|
|
22
|
+
if (!slackConfig.appToken || !slackConfig.botToken) {
|
|
23
|
+
throw new Error("Slack app token and bot token are required");
|
|
24
|
+
}
|
|
25
|
+
this.app = new App({
|
|
26
|
+
token: slackConfig.botToken,
|
|
27
|
+
appToken: slackConfig.appToken,
|
|
28
|
+
socketMode: true,
|
|
29
|
+
});
|
|
30
|
+
const botToken = slackConfig.botToken;
|
|
31
|
+
// Listen for messages mentioning the bot or DMs
|
|
32
|
+
this.app.message(async ({ message }) => {
|
|
33
|
+
// Type narrowing for message events with text
|
|
34
|
+
if (!("text" in message) || !message.text)
|
|
35
|
+
return;
|
|
36
|
+
if (!("user" in message) || !message.user)
|
|
37
|
+
return;
|
|
38
|
+
const commandMessage = {
|
|
39
|
+
id: `slack-${message.ts}-${message.channel}`,
|
|
40
|
+
channel: "slack",
|
|
41
|
+
sender: message.user,
|
|
42
|
+
text: message.text,
|
|
43
|
+
attachments: [],
|
|
44
|
+
timestamp: new Date(Number(message.ts) * 1000).toISOString(),
|
|
45
|
+
meta: {
|
|
46
|
+
ts: message.ts,
|
|
47
|
+
channelId: message.channel,
|
|
48
|
+
threadTs: "thread_ts" in message ? message.thread_ts : undefined,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
// Handle file attachments -- download and upload to S3
|
|
52
|
+
if ("files" in message && Array.isArray(message.files)) {
|
|
53
|
+
for (const file of message.files) {
|
|
54
|
+
const mimeType = file.mimetype;
|
|
55
|
+
const filename = file.name ?? "attachment";
|
|
56
|
+
const privateUrl = file.url_private;
|
|
57
|
+
let publicUrl;
|
|
58
|
+
if (privateUrl) {
|
|
59
|
+
try {
|
|
60
|
+
// Download from Slack (requires bot token for auth)
|
|
61
|
+
const response = await fetch(privateUrl, {
|
|
62
|
+
headers: { Authorization: `Bearer ${botToken}` },
|
|
63
|
+
});
|
|
64
|
+
if (response.ok) {
|
|
65
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
66
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
67
|
+
const s3Url = await uploadMedia(buffer, filename, mimeType);
|
|
68
|
+
publicUrl = s3Url ?? undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
logger.warn(`Slack: Failed to download file ${filename}: ${err instanceof Error ? err.message : String(err)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const type = mimeType.startsWith("image/")
|
|
76
|
+
? "image"
|
|
77
|
+
: mimeType.startsWith("audio/")
|
|
78
|
+
? "audio"
|
|
79
|
+
: mimeType.startsWith("video/")
|
|
80
|
+
? "video"
|
|
81
|
+
: "file";
|
|
82
|
+
commandMessage.attachments.push({
|
|
83
|
+
type,
|
|
84
|
+
url: publicUrl ?? privateUrl,
|
|
85
|
+
filename,
|
|
86
|
+
mimeType,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.emit("message", commandMessage);
|
|
91
|
+
});
|
|
92
|
+
this.app.error(async (error) => {
|
|
93
|
+
await Promise.resolve();
|
|
94
|
+
this.emit("error", new Error(`Slack error: ${error.message}`));
|
|
95
|
+
});
|
|
96
|
+
await this.app.start();
|
|
97
|
+
}
|
|
98
|
+
async stop() {
|
|
99
|
+
if (this.app) {
|
|
100
|
+
await this.app.stop();
|
|
101
|
+
this.app = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async sendMessage(to, message, attachments) {
|
|
105
|
+
if (!this.app) {
|
|
106
|
+
throw new Error("Slack app not started");
|
|
107
|
+
}
|
|
108
|
+
// Send text message
|
|
109
|
+
if (message.trim()) {
|
|
110
|
+
await this.app.client.chat.postMessage({
|
|
111
|
+
channel: to,
|
|
112
|
+
text: message,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Send attachments
|
|
116
|
+
if (attachments && attachments.length > 0) {
|
|
117
|
+
for (const att of attachments) {
|
|
118
|
+
try {
|
|
119
|
+
if (att.localPath && fs.existsSync(att.localPath)) {
|
|
120
|
+
// Upload local file to Slack
|
|
121
|
+
await this.app.client.files.uploadV2({
|
|
122
|
+
channel_id: to,
|
|
123
|
+
file: fs.createReadStream(att.localPath),
|
|
124
|
+
filename: att.filename ?? att.localPath.split("/").pop() ?? "file",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else if (att.url) {
|
|
128
|
+
// Share URL as a message
|
|
129
|
+
await this.app.client.chat.postMessage({
|
|
130
|
+
channel: to,
|
|
131
|
+
text: `[${att.type}: ${att.filename ?? "attachment"}] ${att.url}`,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
logger.warn(`Slack: Failed to send ${att.type}: ${err instanceof Error ? err.message : String(err)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/channels/slack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { VisionClawConfig } from "../config/types.js";
|
|
3
|
+
import type { ChannelAdapter } from "./interface.js";
|
|
4
|
+
/**
|
|
5
|
+
* SMS channel adapter using the Twilio API.
|
|
6
|
+
* Requires a Twilio account and phone number.
|
|
7
|
+
* Runs a webhook server to receive incoming messages.
|
|
8
|
+
*/
|
|
9
|
+
export declare class SmsAdapter extends EventEmitter implements ChannelAdapter {
|
|
10
|
+
readonly name = "sms";
|
|
11
|
+
private config;
|
|
12
|
+
private server;
|
|
13
|
+
private port;
|
|
14
|
+
constructor(config: VisionClawConfig);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
sendMessage(to: string, message: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=sms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sms.d.ts","sourceRoot":"","sources":["../../src/channels/sms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,gBAAgB,CAAC;AAErE;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,YAAa,YAAW,cAAc;IACpE,QAAQ,CAAC,IAAI,SAAS;IACtB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAAQ;gBAER,MAAM,EAAE,gBAAgB;IAK9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsEtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAoB9D"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { URL } from "node:url";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* SMS channel adapter using the Twilio API.
|
|
7
|
+
* Requires a Twilio account and phone number.
|
|
8
|
+
* Runs a webhook server to receive incoming messages.
|
|
9
|
+
*/
|
|
10
|
+
export class SmsAdapter extends EventEmitter {
|
|
11
|
+
name = "sms";
|
|
12
|
+
config;
|
|
13
|
+
server = null;
|
|
14
|
+
port = 3002;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
super();
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
async start() {
|
|
20
|
+
const smsConfig = this.config.channels.sms;
|
|
21
|
+
if (!smsConfig)
|
|
22
|
+
throw new Error("SMS channel not configured");
|
|
23
|
+
if (!smsConfig.twilioAccountSid ||
|
|
24
|
+
!smsConfig.twilioAuthToken ||
|
|
25
|
+
!smsConfig.twilioPhoneNumber) {
|
|
26
|
+
throw new Error("Twilio credentials are required for SMS");
|
|
27
|
+
}
|
|
28
|
+
this.server = http.createServer((req, res) => {
|
|
29
|
+
const url = new URL(req.url ?? "/", `http://localhost:${this.port}`);
|
|
30
|
+
// Twilio webhook for incoming SMS
|
|
31
|
+
if (req.method === "POST" && url.pathname === "/sms") {
|
|
32
|
+
let body = "";
|
|
33
|
+
req.on("data", (chunk) => (body += String(chunk)));
|
|
34
|
+
req.on("end", () => {
|
|
35
|
+
try {
|
|
36
|
+
const params = new URLSearchParams(body);
|
|
37
|
+
const from = params.get("From") ?? "unknown";
|
|
38
|
+
const text = params.get("Body") ?? "";
|
|
39
|
+
const messageSid = params.get("MessageSid") ?? `sms-${Date.now()}`;
|
|
40
|
+
if (text.trim()) {
|
|
41
|
+
const commandMessage = {
|
|
42
|
+
id: messageSid,
|
|
43
|
+
channel: "sms",
|
|
44
|
+
sender: from,
|
|
45
|
+
text,
|
|
46
|
+
attachments: [],
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
meta: {
|
|
49
|
+
messageSid,
|
|
50
|
+
from,
|
|
51
|
+
numMedia: params.get("NumMedia"),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
this.emit("message", commandMessage);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
logger.warn(`SMS: Error parsing webhook: ${err instanceof Error ? err.message : String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
// Respond with empty TwiML
|
|
61
|
+
res.writeHead(200, { "Content-Type": "text/xml" });
|
|
62
|
+
res.end("<Response></Response>");
|
|
63
|
+
});
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
res.writeHead(404);
|
|
67
|
+
res.end("Not found");
|
|
68
|
+
});
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const server = this.server;
|
|
71
|
+
if (server) {
|
|
72
|
+
server.listen(this.port, () => {
|
|
73
|
+
logger.channel("sms", `Webhook listening on port ${this.port}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
resolve();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async stop() {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
if (this.server) {
|
|
85
|
+
this.server.close(() => resolve());
|
|
86
|
+
this.server = null;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async sendMessage(to, message) {
|
|
94
|
+
const smsConfig = this.config.channels.sms;
|
|
95
|
+
if (!smsConfig)
|
|
96
|
+
throw new Error("Twilio not configured");
|
|
97
|
+
if (!smsConfig.twilioAccountSid ||
|
|
98
|
+
!smsConfig.twilioAuthToken ||
|
|
99
|
+
!smsConfig.twilioPhoneNumber) {
|
|
100
|
+
throw new Error("Twilio credentials are required");
|
|
101
|
+
}
|
|
102
|
+
const { default: twilio } = await import("twilio");
|
|
103
|
+
const client = twilio(smsConfig.twilioAccountSid, smsConfig.twilioAuthToken);
|
|
104
|
+
await client.messages.create({
|
|
105
|
+
body: message,
|
|
106
|
+
from: smsConfig.twilioPhoneNumber,
|
|
107
|
+
to,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=sms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sms.js","sourceRoot":"","sources":["../../src/channels/sms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAGtC;;;;GAIG;AACH,MAAM,OAAO,UAAW,SAAQ,YAAY;IACjC,IAAI,GAAG,KAAK,CAAC;IACd,MAAM,CAAmB;IACzB,MAAM,GAAuB,IAAI,CAAC;IAClC,IAAI,GAAG,IAAI,CAAC;IAEpB,YAAY,MAAwB;QAClC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC3C,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC9D,IACE,CAAC,SAAS,CAAC,gBAAgB;YAC3B,CAAC,SAAS,CAAC,eAAe;YAC1B,CAAC,SAAS,CAAC,iBAAiB,EAC5B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAErE,kCAAkC;YAClC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACrD,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACpE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;wBACzC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;wBAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;wBACtC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;wBAEnE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;4BAChB,MAAM,cAAc,GAAmB;gCACrC,EAAE,EAAE,UAAU;gCACd,OAAO,EAAE,KAAK;gCACd,MAAM,EAAE,IAAI;gCACZ,IAAI;gCACJ,WAAW,EAAE,EAAE;gCACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gCACnC,IAAI,EAAE;oCACJ,UAAU;oCACV,IAAI;oCACJ,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;iCACjC;6BACF,CAAC;4BAEF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;wBACvC,CAAC;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,MAAM,CAAC,IAAI,CAAC,+BAA+B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACjG,CAAC;oBAED,2BAA2B;oBAC3B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,CAAC;oBACnD,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;gBACnC,CAAC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;oBAC5B,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,6BAA6B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;oBAChE,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAe;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC3C,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACzD,IACE,CAAC,SAAS,CAAC,gBAAgB;YAC3B,CAAC,SAAS,CAAC,eAAe;YAC1B,CAAC,SAAS,CAAC,iBAAiB,EAC5B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,gBAAgB,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;QAE7E,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC3B,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS,CAAC,iBAAiB;YACjC,EAAE;SACH,CAAC,CAAC;IACL,CAAC;CACF"}
|