cueclaw 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-UJ4M3TPW.js +448 -0
- package/dist/chunk-D77G7ABJ.js +1051 -0
- package/dist/chunk-E7BP6DMO.js +10 -0
- package/dist/chunk-GMHDL4CG.js +250 -0
- package/dist/chunk-JRHM3Z4C.js +158 -0
- package/dist/chunk-K4PGB2DU.js +140 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +225 -0
- package/dist/config-HMHM7UAZ.js +12 -0
- package/dist/daemon-TWVEMRCU.js +308 -0
- package/dist/router-36O66FDW.js +10 -0
- package/dist/service-BHFOM6E2.js +153 -0
- package/dist/setup-QZUEJUIN.js +154 -0
- package/dist/telegram-BTTWEETO.js +120 -0
- package/dist/whatsapp-36XWDSJ5.js +92 -0
- package/package.json +49 -11
- package/dist/index.d.ts +0 -0
- package/dist/index.js +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cueclawHome
|
|
3
|
+
} from "./chunk-JRHM3Z4C.js";
|
|
4
|
+
import {
|
|
5
|
+
logger
|
|
6
|
+
} from "./chunk-E7BP6DMO.js";
|
|
7
|
+
|
|
8
|
+
// src/service.ts
|
|
9
|
+
import { writeFileSync, existsSync, unlinkSync, mkdirSync } from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
var SERVICE_LABEL = "com.cueclaw";
|
|
13
|
+
function installService() {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
try {
|
|
16
|
+
if (platform === "darwin") {
|
|
17
|
+
return installLaunchd();
|
|
18
|
+
} else if (platform === "linux") {
|
|
19
|
+
return installSystemd();
|
|
20
|
+
} else {
|
|
21
|
+
return { success: false, error: `Unsupported platform: ${platform}` };
|
|
22
|
+
}
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function uninstallService() {
|
|
28
|
+
const platform = process.platform;
|
|
29
|
+
try {
|
|
30
|
+
if (platform === "darwin") {
|
|
31
|
+
return uninstallLaunchd();
|
|
32
|
+
} else if (platform === "linux") {
|
|
33
|
+
return uninstallSystemd();
|
|
34
|
+
} else {
|
|
35
|
+
return { success: false, error: `Unsupported platform: ${platform}` };
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function getServiceStatus() {
|
|
42
|
+
const platform = process.platform;
|
|
43
|
+
try {
|
|
44
|
+
if (platform === "darwin") {
|
|
45
|
+
const output = execFileSync("launchctl", ["list", SERVICE_LABEL], { encoding: "utf-8" });
|
|
46
|
+
return output.includes(SERVICE_LABEL) ? "running" : "stopped";
|
|
47
|
+
} else if (platform === "linux") {
|
|
48
|
+
const output = execFileSync("systemctl", ["--user", "is-active", "cueclaw"], { encoding: "utf-8" }).trim();
|
|
49
|
+
return output === "active" ? "running" : "stopped";
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
return "stopped";
|
|
53
|
+
}
|
|
54
|
+
return "unknown";
|
|
55
|
+
}
|
|
56
|
+
function installLaunchd() {
|
|
57
|
+
const home = process.env["HOME"];
|
|
58
|
+
if (!home) return { success: false, error: "HOME not set" };
|
|
59
|
+
const plistDir = join(home, "Library", "LaunchAgents");
|
|
60
|
+
mkdirSync(plistDir, { recursive: true });
|
|
61
|
+
const plistPath = join(plistDir, `${SERVICE_LABEL}.plist`);
|
|
62
|
+
const logDir = join(cueclawHome(), "logs");
|
|
63
|
+
mkdirSync(logDir, { recursive: true });
|
|
64
|
+
const nodePath = process.execPath;
|
|
65
|
+
const cliPath = join(process.cwd(), "dist", "cli.js");
|
|
66
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
67
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
68
|
+
<plist version="1.0">
|
|
69
|
+
<dict>
|
|
70
|
+
<key>Label</key>
|
|
71
|
+
<string>${SERVICE_LABEL}</string>
|
|
72
|
+
<key>ProgramArguments</key>
|
|
73
|
+
<array>
|
|
74
|
+
<string>${nodePath}</string>
|
|
75
|
+
<string>${cliPath}</string>
|
|
76
|
+
<string>daemon</string>
|
|
77
|
+
<string>start</string>
|
|
78
|
+
</array>
|
|
79
|
+
<key>KeepAlive</key>
|
|
80
|
+
<true/>
|
|
81
|
+
<key>StandardOutPath</key>
|
|
82
|
+
<string>${join(logDir, "daemon.log")}</string>
|
|
83
|
+
<key>StandardErrorPath</key>
|
|
84
|
+
<string>${join(logDir, "daemon.log")}</string>
|
|
85
|
+
<key>EnvironmentVariables</key>
|
|
86
|
+
<dict>
|
|
87
|
+
<key>PATH</key>
|
|
88
|
+
<string>/usr/local/bin:/usr/bin:/bin</string>
|
|
89
|
+
</dict>
|
|
90
|
+
</dict>
|
|
91
|
+
</plist>`;
|
|
92
|
+
writeFileSync(plistPath, plist);
|
|
93
|
+
execFileSync("launchctl", ["load", plistPath]);
|
|
94
|
+
logger.info({ plistPath }, "Installed launchd service");
|
|
95
|
+
return { success: true };
|
|
96
|
+
}
|
|
97
|
+
function uninstallLaunchd() {
|
|
98
|
+
const home = process.env["HOME"];
|
|
99
|
+
if (!home) return { success: false, error: "HOME not set" };
|
|
100
|
+
const plistPath = join(home, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
|
|
101
|
+
if (!existsSync(plistPath)) return { success: false, error: "Service not installed" };
|
|
102
|
+
try {
|
|
103
|
+
execFileSync("launchctl", ["unload", plistPath]);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
unlinkSync(plistPath);
|
|
107
|
+
logger.info("Uninstalled launchd service");
|
|
108
|
+
return { success: true };
|
|
109
|
+
}
|
|
110
|
+
function installSystemd() {
|
|
111
|
+
const home = process.env["HOME"];
|
|
112
|
+
if (!home) return { success: false, error: "HOME not set" };
|
|
113
|
+
const serviceDir = join(home, ".config", "systemd", "user");
|
|
114
|
+
mkdirSync(serviceDir, { recursive: true });
|
|
115
|
+
const servicePath = join(serviceDir, "cueclaw.service");
|
|
116
|
+
const nodePath = process.execPath;
|
|
117
|
+
const cliPath = join(process.cwd(), "dist", "cli.js");
|
|
118
|
+
const service = `[Unit]
|
|
119
|
+
Description=CueClaw Daemon
|
|
120
|
+
After=network.target
|
|
121
|
+
|
|
122
|
+
[Service]
|
|
123
|
+
ExecStart=${nodePath} ${cliPath} daemon start
|
|
124
|
+
Restart=always
|
|
125
|
+
RestartSec=5
|
|
126
|
+
|
|
127
|
+
[Install]
|
|
128
|
+
WantedBy=default.target`;
|
|
129
|
+
writeFileSync(servicePath, service);
|
|
130
|
+
execFileSync("systemctl", ["--user", "daemon-reload"]);
|
|
131
|
+
execFileSync("systemctl", ["--user", "enable", "--now", "cueclaw"]);
|
|
132
|
+
logger.info({ servicePath }, "Installed systemd service");
|
|
133
|
+
return { success: true };
|
|
134
|
+
}
|
|
135
|
+
function uninstallSystemd() {
|
|
136
|
+
const home = process.env["HOME"];
|
|
137
|
+
if (!home) return { success: false, error: "HOME not set" };
|
|
138
|
+
const servicePath = join(home, ".config", "systemd", "user", "cueclaw.service");
|
|
139
|
+
if (!existsSync(servicePath)) return { success: false, error: "Service not installed" };
|
|
140
|
+
try {
|
|
141
|
+
execFileSync("systemctl", ["--user", "disable", "--now", "cueclaw"]);
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
unlinkSync(servicePath);
|
|
145
|
+
execFileSync("systemctl", ["--user", "daemon-reload"]);
|
|
146
|
+
logger.info("Uninstalled systemd service");
|
|
147
|
+
return { success: true };
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
getServiceStatus,
|
|
151
|
+
installService,
|
|
152
|
+
uninstallService
|
|
153
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-E7BP6DMO.js";
|
|
4
|
+
|
|
5
|
+
// src/setup-environment.ts
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
function checkEnvironment() {
|
|
8
|
+
const nodeVersion = process.version;
|
|
9
|
+
let docker = false;
|
|
10
|
+
let dockerVersion;
|
|
11
|
+
let dockerRunning = false;
|
|
12
|
+
try {
|
|
13
|
+
const version = execFileSync("docker", ["--version"], { encoding: "utf-8" }).trim();
|
|
14
|
+
docker = true;
|
|
15
|
+
dockerVersion = version;
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
if (docker) {
|
|
19
|
+
try {
|
|
20
|
+
execFileSync("docker", ["info"], { encoding: "utf-8", stdio: "pipe" });
|
|
21
|
+
dockerRunning = true;
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { docker, dockerVersion, dockerRunning, nodeVersion };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/setup-auth.ts
|
|
29
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
30
|
+
async function validateAuth(config) {
|
|
31
|
+
try {
|
|
32
|
+
const client = new Anthropic({ apiKey: config.claude.api_key });
|
|
33
|
+
await client.messages.create({
|
|
34
|
+
model: "claude-haiku-4-5-20251001",
|
|
35
|
+
max_tokens: 10,
|
|
36
|
+
messages: [{ role: "user", content: "ping" }]
|
|
37
|
+
});
|
|
38
|
+
return { valid: true };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return { valid: false, error: err instanceof Error ? err.message : String(err) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/setup-container.ts
|
|
45
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
46
|
+
import { join } from "path";
|
|
47
|
+
function buildContainer(projectRoot) {
|
|
48
|
+
const buildScript = join(projectRoot, "container", "build.sh");
|
|
49
|
+
try {
|
|
50
|
+
execFileSync2("bash", [buildScript], {
|
|
51
|
+
encoding: "utf-8",
|
|
52
|
+
stdio: "inherit",
|
|
53
|
+
cwd: join(projectRoot, "container")
|
|
54
|
+
});
|
|
55
|
+
return { success: true };
|
|
56
|
+
} catch (err) {
|
|
57
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function checkContainerImage(imageName) {
|
|
61
|
+
try {
|
|
62
|
+
const result = execFileSync2("docker", ["image", "inspect", imageName], {
|
|
63
|
+
encoding: "utf-8",
|
|
64
|
+
stdio: "pipe"
|
|
65
|
+
});
|
|
66
|
+
return result.length > 0;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/setup-verify.ts
|
|
73
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
74
|
+
function runSmokeTest(imageName) {
|
|
75
|
+
try {
|
|
76
|
+
const result = execFileSync3("docker", [
|
|
77
|
+
"run",
|
|
78
|
+
"--rm",
|
|
79
|
+
"--network",
|
|
80
|
+
"none",
|
|
81
|
+
"--user",
|
|
82
|
+
"1000:1000",
|
|
83
|
+
imageName,
|
|
84
|
+
"node",
|
|
85
|
+
"-e",
|
|
86
|
+
'console.log("cueclaw-smoke-ok")'
|
|
87
|
+
], {
|
|
88
|
+
encoding: "utf-8",
|
|
89
|
+
timeout: 3e4,
|
|
90
|
+
stdio: "pipe"
|
|
91
|
+
});
|
|
92
|
+
if (result.includes("cueclaw-smoke-ok")) {
|
|
93
|
+
return { success: true };
|
|
94
|
+
}
|
|
95
|
+
return { success: false, error: "Smoke test output mismatch" };
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/setup.ts
|
|
102
|
+
async function runSetup(config, projectRoot) {
|
|
103
|
+
console.log("CueClaw Setup\n");
|
|
104
|
+
console.log("Checking environment...");
|
|
105
|
+
const env = checkEnvironment();
|
|
106
|
+
console.log(` Node.js: ${env.nodeVersion}`);
|
|
107
|
+
if (!env.docker) {
|
|
108
|
+
console.log(" Docker: NOT INSTALLED");
|
|
109
|
+
console.log("\n Docker is required for container isolation.");
|
|
110
|
+
console.log(" Install Docker: https://docs.docker.com/get-docker/");
|
|
111
|
+
console.log(" You can still use CueClaw in local mode (container.enabled: false)");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(` Docker: ${env.dockerVersion}`);
|
|
115
|
+
if (!env.dockerRunning) {
|
|
116
|
+
console.log(" Docker daemon: NOT RUNNING");
|
|
117
|
+
console.log(" Please start Docker and try again.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
console.log(" Docker daemon: running");
|
|
121
|
+
console.log("\nValidating API key...");
|
|
122
|
+
const auth = await validateAuth(config);
|
|
123
|
+
if (!auth.valid) {
|
|
124
|
+
console.log(` API key validation failed: ${auth.error}`);
|
|
125
|
+
console.log(" Check your ANTHROPIC_API_KEY in ~/.cueclaw/config.yaml or .env");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(" API key: valid");
|
|
129
|
+
const imageName = config.container?.image ?? "cueclaw-agent:latest";
|
|
130
|
+
if (!checkContainerImage(imageName)) {
|
|
131
|
+
console.log("\nBuilding container image...");
|
|
132
|
+
const build = buildContainer(projectRoot);
|
|
133
|
+
if (!build.success) {
|
|
134
|
+
console.log(` Build failed: ${build.error}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
console.log(` Image built: ${imageName}`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`
|
|
140
|
+
Container image: ${imageName} (exists)`);
|
|
141
|
+
}
|
|
142
|
+
console.log("\nRunning smoke test...");
|
|
143
|
+
const smoke = runSmokeTest(imageName);
|
|
144
|
+
if (!smoke.success) {
|
|
145
|
+
console.log(` Smoke test failed: ${smoke.error}`);
|
|
146
|
+
logger.warn({ error: smoke.error }, "Setup smoke test failed");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
console.log(" Smoke test: passed");
|
|
150
|
+
console.log("\nSetup complete. Run `cueclaw daemon install` to start the background service.");
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
runSetup
|
|
154
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-E7BP6DMO.js";
|
|
4
|
+
|
|
5
|
+
// src/channels/telegram.ts
|
|
6
|
+
import { Bot, InlineKeyboard } from "grammy";
|
|
7
|
+
var TELEGRAM_MAX_MESSAGE_LENGTH = 4096;
|
|
8
|
+
var TelegramChannel = class {
|
|
9
|
+
constructor(token, allowedUsers = [], onInbound) {
|
|
10
|
+
this.allowedUsers = allowedUsers;
|
|
11
|
+
this.bot = new Bot(token);
|
|
12
|
+
this.onInbound = onInbound ?? null;
|
|
13
|
+
}
|
|
14
|
+
name = "telegram";
|
|
15
|
+
bot;
|
|
16
|
+
connected = false;
|
|
17
|
+
onInbound = null;
|
|
18
|
+
callbackHandler = null;
|
|
19
|
+
/** Set a handler for inline keyboard callback actions (confirm/modify/cancel) */
|
|
20
|
+
onCallback(handler) {
|
|
21
|
+
this.callbackHandler = handler;
|
|
22
|
+
}
|
|
23
|
+
async connect() {
|
|
24
|
+
this.bot.on("message:text", (ctx) => {
|
|
25
|
+
const jid = String(ctx.chat.id);
|
|
26
|
+
if (!this.isAllowed(jid)) return;
|
|
27
|
+
const newMsg = {
|
|
28
|
+
text: ctx.message.text,
|
|
29
|
+
sender: String(ctx.from?.id ?? "unknown")
|
|
30
|
+
};
|
|
31
|
+
this.onInbound?.(jid, newMsg);
|
|
32
|
+
});
|
|
33
|
+
this.bot.on("callback_query:data", async (ctx) => {
|
|
34
|
+
await ctx.answerCallbackQuery();
|
|
35
|
+
const data = ctx.callbackQuery.data;
|
|
36
|
+
const chatId = String(ctx.callbackQuery.message?.chat.id ?? "");
|
|
37
|
+
const [action, workflowId] = data.split(":");
|
|
38
|
+
if (action && workflowId && chatId) {
|
|
39
|
+
this.callbackHandler?.(workflowId, action, chatId);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
this.bot.catch((err) => {
|
|
43
|
+
logger.error({ err: err.error }, "Telegram bot error");
|
|
44
|
+
});
|
|
45
|
+
await this.bot.start();
|
|
46
|
+
this.connected = true;
|
|
47
|
+
logger.info("Telegram bot connected");
|
|
48
|
+
}
|
|
49
|
+
async disconnect() {
|
|
50
|
+
await this.bot.stop();
|
|
51
|
+
this.connected = false;
|
|
52
|
+
}
|
|
53
|
+
async sendMessage(jid, text) {
|
|
54
|
+
const chunks = chunkMessage(text);
|
|
55
|
+
for (const chunk of chunks) {
|
|
56
|
+
await this.bot.api.sendMessage(Number(jid), chunk);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async sendConfirmation(jid, workflow) {
|
|
60
|
+
const steps = workflow.steps.map((s, i) => `${i + 1}. ${s.description}`).join("\n");
|
|
61
|
+
const trigger = workflow.trigger.type === "manual" ? "manual" : workflow.trigger.type === "cron" ? `cron (${workflow.trigger.expression})` : `poll (${workflow.trigger.interval_seconds}s)`;
|
|
62
|
+
const text = [
|
|
63
|
+
`Workflow: ${escapeMdV2(workflow.name)}`,
|
|
64
|
+
`Trigger: ${escapeMdV2(trigger)}`,
|
|
65
|
+
"",
|
|
66
|
+
"*Steps:*",
|
|
67
|
+
escapeMdV2(steps)
|
|
68
|
+
].join("\n");
|
|
69
|
+
const keyboard = new InlineKeyboard().text("Confirm", `confirm:${workflow.id}`).text("Modify", `modify:${workflow.id}`).text("Cancel", `cancel:${workflow.id}`);
|
|
70
|
+
await this.bot.api.sendMessage(Number(jid), text, {
|
|
71
|
+
reply_markup: keyboard,
|
|
72
|
+
parse_mode: "MarkdownV2"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
|
+
return this.connected;
|
|
77
|
+
}
|
|
78
|
+
ownsJid(jid) {
|
|
79
|
+
return /^-?\d+$/.test(jid);
|
|
80
|
+
}
|
|
81
|
+
async setTyping(jid, _isTyping) {
|
|
82
|
+
try {
|
|
83
|
+
await this.bot.api.sendChatAction(Number(jid), "typing");
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
isAllowed(jid) {
|
|
88
|
+
if (this.allowedUsers.length === 0) return true;
|
|
89
|
+
return this.allowedUsers.includes(jid);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
function escapeMdV2(text) {
|
|
93
|
+
return text.replace(/[_*[\]()~`>#+\-=|{}.!\\]/g, "\\$&");
|
|
94
|
+
}
|
|
95
|
+
function chunkMessage(text) {
|
|
96
|
+
if (text.length <= TELEGRAM_MAX_MESSAGE_LENGTH) return [text];
|
|
97
|
+
const chunks = [];
|
|
98
|
+
let remaining = text;
|
|
99
|
+
while (remaining.length > 0) {
|
|
100
|
+
if (remaining.length <= TELEGRAM_MAX_MESSAGE_LENGTH) {
|
|
101
|
+
chunks.push(remaining);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
let breakAt = remaining.lastIndexOf("\n", TELEGRAM_MAX_MESSAGE_LENGTH);
|
|
105
|
+
if (breakAt === -1 || breakAt < TELEGRAM_MAX_MESSAGE_LENGTH / 2) {
|
|
106
|
+
breakAt = remaining.lastIndexOf(" ", TELEGRAM_MAX_MESSAGE_LENGTH);
|
|
107
|
+
}
|
|
108
|
+
if (breakAt === -1) {
|
|
109
|
+
breakAt = TELEGRAM_MAX_MESSAGE_LENGTH;
|
|
110
|
+
}
|
|
111
|
+
chunks.push(remaining.slice(0, breakAt));
|
|
112
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
113
|
+
}
|
|
114
|
+
return chunks;
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
TelegramChannel,
|
|
118
|
+
chunkMessage,
|
|
119
|
+
escapeMdV2
|
|
120
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-E7BP6DMO.js";
|
|
4
|
+
|
|
5
|
+
// src/channels/whatsapp.ts
|
|
6
|
+
var WhatsAppChannel = class {
|
|
7
|
+
constructor(authDir, allowedJids = [], onInbound) {
|
|
8
|
+
this.authDir = authDir;
|
|
9
|
+
this.allowedJids = allowedJids;
|
|
10
|
+
this.onInbound = onInbound ?? null;
|
|
11
|
+
}
|
|
12
|
+
name = "whatsapp";
|
|
13
|
+
sock = null;
|
|
14
|
+
connected = false;
|
|
15
|
+
onInbound = null;
|
|
16
|
+
async connect() {
|
|
17
|
+
const baileys = await import("@whiskeysockets/baileys");
|
|
18
|
+
const makeWASocket = baileys.default;
|
|
19
|
+
const { useMultiFileAuthState } = baileys;
|
|
20
|
+
const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
|
|
21
|
+
this.sock = makeWASocket({
|
|
22
|
+
auth: state,
|
|
23
|
+
printQRInTerminal: true
|
|
24
|
+
});
|
|
25
|
+
this.sock.ev.on("creds.update", saveCreds);
|
|
26
|
+
this.sock.ev.on("connection.update", (update) => {
|
|
27
|
+
if (update.connection === "open") {
|
|
28
|
+
this.connected = true;
|
|
29
|
+
logger.info("WhatsApp connected");
|
|
30
|
+
} else if (update.connection === "close") {
|
|
31
|
+
this.connected = false;
|
|
32
|
+
logger.warn("WhatsApp disconnected");
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
this.sock.ev.on("messages.upsert", ({ messages }) => {
|
|
36
|
+
for (const msg of messages) {
|
|
37
|
+
if (msg.key.fromMe || !msg.message) continue;
|
|
38
|
+
const jid = msg.key.remoteJid;
|
|
39
|
+
if (!jid) continue;
|
|
40
|
+
if (this.allowedJids.length > 0 && !this.allowedJids.includes(jid)) continue;
|
|
41
|
+
const text = msg.message.conversation || msg.message.extendedTextMessage?.text || "";
|
|
42
|
+
if (!text) continue;
|
|
43
|
+
const newMsg = {
|
|
44
|
+
text,
|
|
45
|
+
sender: msg.key.participant || jid
|
|
46
|
+
};
|
|
47
|
+
this.onInbound?.(jid, newMsg);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async disconnect() {
|
|
52
|
+
if (this.sock) {
|
|
53
|
+
this.sock.end();
|
|
54
|
+
this.sock = null;
|
|
55
|
+
this.connected = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async sendMessage(jid, text) {
|
|
59
|
+
if (!this.sock) throw new Error("WhatsApp not connected");
|
|
60
|
+
await this.sock.sendMessage(jid, { text });
|
|
61
|
+
}
|
|
62
|
+
async sendConfirmation(jid, workflow) {
|
|
63
|
+
const steps = workflow.steps.map((s, i) => `${i + 1}. ${s.description}`).join("\n");
|
|
64
|
+
const trigger = workflow.trigger.type === "manual" ? "manual" : workflow.trigger.type === "cron" ? `cron (${workflow.trigger.expression})` : `poll (${workflow.trigger.interval_seconds}s)`;
|
|
65
|
+
const text = [
|
|
66
|
+
`Workflow: ${workflow.name}`,
|
|
67
|
+
`Trigger: ${trigger}`,
|
|
68
|
+
"",
|
|
69
|
+
"Steps:",
|
|
70
|
+
steps,
|
|
71
|
+
"",
|
|
72
|
+
"Reply: 1=Confirm, 2=Modify, 3=Cancel"
|
|
73
|
+
].join("\n");
|
|
74
|
+
await this.sendMessage(jid, text);
|
|
75
|
+
}
|
|
76
|
+
isConnected() {
|
|
77
|
+
return this.connected;
|
|
78
|
+
}
|
|
79
|
+
ownsJid(jid) {
|
|
80
|
+
return jid.endsWith("@s.whatsapp.net") || jid.endsWith("@g.us");
|
|
81
|
+
}
|
|
82
|
+
async setTyping(jid, isTyping) {
|
|
83
|
+
if (!this.sock) return;
|
|
84
|
+
try {
|
|
85
|
+
await this.sock.sendPresenceUpdate(isTyping ? "composing" : "paused", jid);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
export {
|
|
91
|
+
WhatsAppChannel
|
|
92
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cueclaw",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Orchestrate agent workflows with natural language. Natural language in, executable DAG out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,14 +17,6 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "tsc",
|
|
22
|
-
"test": "vitest run",
|
|
23
|
-
"test:watch": "vitest",
|
|
24
|
-
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
25
|
-
"typecheck": "tsc --noEmit",
|
|
26
|
-
"prepublishOnly": "pnpm build"
|
|
27
|
-
},
|
|
28
20
|
"keywords": [
|
|
29
21
|
"ai",
|
|
30
22
|
"agent",
|
|
@@ -48,5 +40,51 @@
|
|
|
48
40
|
"engines": {
|
|
49
41
|
"node": ">=22.0.0"
|
|
50
42
|
},
|
|
51
|
-
"
|
|
52
|
-
|
|
43
|
+
"simple-git-hooks": {
|
|
44
|
+
"commit-msg": "pnpm commitlint --edit $1"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@anthropic-ai/sdk": "^0.78.0",
|
|
48
|
+
"@inkjs/ui": "^2.0.0",
|
|
49
|
+
"@whiskeysockets/baileys": "7.0.0-rc.9",
|
|
50
|
+
"better-sqlite3": "^12.6.2",
|
|
51
|
+
"commander": "^14.0.3",
|
|
52
|
+
"cron-parser": "^5.5.0",
|
|
53
|
+
"dotenv": "^17.3.1",
|
|
54
|
+
"grammy": "^1.40.0",
|
|
55
|
+
"ink": "5",
|
|
56
|
+
"ink-spinner": "^5.0.0",
|
|
57
|
+
"ink-text-input": "^6.0.0",
|
|
58
|
+
"nanoid": "^5.1.6",
|
|
59
|
+
"pino": "^10.3.1",
|
|
60
|
+
"pino-pretty": "^13.1.3",
|
|
61
|
+
"react": "18",
|
|
62
|
+
"yaml": "^2.8.2",
|
|
63
|
+
"zod": "^4.3.6"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@changesets/cli": "^2.29.8",
|
|
67
|
+
"@commitlint/cli": "^20.4.2",
|
|
68
|
+
"@commitlint/config-conventional": "^20.4.2",
|
|
69
|
+
"@eslint/js": "^10.0.1",
|
|
70
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
71
|
+
"@types/node": "^25.3.0",
|
|
72
|
+
"@types/react": "^19.2.14",
|
|
73
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
74
|
+
"eslint": "^10.0.2",
|
|
75
|
+
"simple-git-hooks": "^2.13.1",
|
|
76
|
+
"tsup": "^8.5.1",
|
|
77
|
+
"tsx": "^4.21.0",
|
|
78
|
+
"typescript": "^5.9.3",
|
|
79
|
+
"typescript-eslint": "^8.56.1",
|
|
80
|
+
"vitest": "^4.0.18"
|
|
81
|
+
},
|
|
82
|
+
"scripts": {
|
|
83
|
+
"build": "tsup src/cli.ts --format esm --target node22 --dts --clean",
|
|
84
|
+
"dev": "tsx src/cli.ts",
|
|
85
|
+
"test": "vitest run",
|
|
86
|
+
"test:watch": "vitest",
|
|
87
|
+
"typecheck": "tsc --noEmit",
|
|
88
|
+
"lint": "eslint src/"
|
|
89
|
+
}
|
|
90
|
+
}
|
package/dist/index.d.ts
DELETED
|
File without changes
|
package/dist/index.js
DELETED
|
File without changes
|