clawdy 0.0.1
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/index.d.ts +51 -0
- package/dist/index.js +211 -0
- package/openclaw.plugin.json +6 -0
- package/package.json +33 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @clawdy/openclaw-channel
|
|
3
|
+
*
|
|
4
|
+
* OpenClaw channel plugin that enables outbound push messaging from OpenClaw
|
|
5
|
+
* instances to the Clawdy Gateway. When OpenClaw wants to proactively send a
|
|
6
|
+
* message (scheduled, cron, heartbeat, agent-initiated), this plugin POSTs to
|
|
7
|
+
* the Clawdy Gateway's callback endpoint for delivery via web dashboard, Slack,
|
|
8
|
+
* Telegram, or other configured channels.
|
|
9
|
+
*
|
|
10
|
+
* ## Configuration (openclaw.json)
|
|
11
|
+
*
|
|
12
|
+
* ```json
|
|
13
|
+
* {
|
|
14
|
+
* "channels": {
|
|
15
|
+
* "clawdy": {
|
|
16
|
+
* "enabled": true,
|
|
17
|
+
* "callbackUrl": "https://gateway.clawdy.app/api/openclaw/callback",
|
|
18
|
+
* "callbackSecret": "<per-machine-secret>"
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Targets
|
|
25
|
+
*
|
|
26
|
+
* The plugin supports the following target formats in the `to` field:
|
|
27
|
+
*
|
|
28
|
+
* - `web:default` — Deliver to the machine owner's web dashboard
|
|
29
|
+
* - `slack:default` — Deliver via the owner's linked Slack workspace
|
|
30
|
+
* - `telegram:default` — Deliver via the owner's linked Telegram bot
|
|
31
|
+
* - `web:<userId>` — Deliver to a specific user's web dashboard
|
|
32
|
+
* - `slack:<teamId>` — Deliver to a specific Slack workspace
|
|
33
|
+
* - `telegram:<chatId>` — Deliver to a specific Telegram chat
|
|
34
|
+
*
|
|
35
|
+
* Using `:default` targets is recommended — the Clawdy Gateway resolves the
|
|
36
|
+
* actual destination from the user's gateway link configuration in the database.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* OpenClaw Plugin API — passed to the register function.
|
|
40
|
+
*/
|
|
41
|
+
interface OpenClawPluginApi {
|
|
42
|
+
registerChannel(opts: {
|
|
43
|
+
plugin: unknown;
|
|
44
|
+
}): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Entry point called by OpenClaw's plugin loader.
|
|
48
|
+
* Registers the Clawdy channel plugin.
|
|
49
|
+
*/
|
|
50
|
+
export default function register(api: OpenClawPluginApi): void;
|
|
51
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @clawdy/openclaw-channel
|
|
4
|
+
*
|
|
5
|
+
* OpenClaw channel plugin that enables outbound push messaging from OpenClaw
|
|
6
|
+
* instances to the Clawdy Gateway. When OpenClaw wants to proactively send a
|
|
7
|
+
* message (scheduled, cron, heartbeat, agent-initiated), this plugin POSTs to
|
|
8
|
+
* the Clawdy Gateway's callback endpoint for delivery via web dashboard, Slack,
|
|
9
|
+
* Telegram, or other configured channels.
|
|
10
|
+
*
|
|
11
|
+
* ## Configuration (openclaw.json)
|
|
12
|
+
*
|
|
13
|
+
* ```json
|
|
14
|
+
* {
|
|
15
|
+
* "channels": {
|
|
16
|
+
* "clawdy": {
|
|
17
|
+
* "enabled": true,
|
|
18
|
+
* "callbackUrl": "https://gateway.clawdy.app/api/openclaw/callback",
|
|
19
|
+
* "callbackSecret": "<per-machine-secret>"
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* ## Targets
|
|
26
|
+
*
|
|
27
|
+
* The plugin supports the following target formats in the `to` field:
|
|
28
|
+
*
|
|
29
|
+
* - `web:default` — Deliver to the machine owner's web dashboard
|
|
30
|
+
* - `slack:default` — Deliver via the owner's linked Slack workspace
|
|
31
|
+
* - `telegram:default` — Deliver via the owner's linked Telegram bot
|
|
32
|
+
* - `web:<userId>` — Deliver to a specific user's web dashboard
|
|
33
|
+
* - `slack:<teamId>` — Deliver to a specific Slack workspace
|
|
34
|
+
* - `telegram:<chatId>` — Deliver to a specific Telegram chat
|
|
35
|
+
*
|
|
36
|
+
* Using `:default` targets is recommended — the Clawdy Gateway resolves the
|
|
37
|
+
* actual destination from the user's gateway link configuration in the database.
|
|
38
|
+
*/
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.default = register;
|
|
41
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the Clawdy channel account from the OpenClaw config.
|
|
44
|
+
*/
|
|
45
|
+
function resolveAccount(cfg, accountId) {
|
|
46
|
+
const channels = cfg.channels;
|
|
47
|
+
const clawdyCfg = channels?.clawdy;
|
|
48
|
+
if (!clawdyCfg) {
|
|
49
|
+
return {
|
|
50
|
+
accountId: accountId ?? "default",
|
|
51
|
+
callbackUrl: "",
|
|
52
|
+
callbackSecret: "",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
accountId: accountId ?? "default",
|
|
57
|
+
callbackUrl: clawdyCfg.callbackUrl ?? "",
|
|
58
|
+
callbackSecret: clawdyCfg.callbackSecret ?? "",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get the machine's public IP for identification.
|
|
63
|
+
* Falls back to empty string if not detectable.
|
|
64
|
+
*/
|
|
65
|
+
async function getMachineIp() {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch("https://api.ipify.org?format=text", {
|
|
68
|
+
signal: AbortSignal.timeout(5_000),
|
|
69
|
+
});
|
|
70
|
+
if (res.ok) {
|
|
71
|
+
return (await res.text()).trim();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Ignore — non-critical
|
|
76
|
+
}
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
// Cache the machine IP to avoid repeated lookups
|
|
80
|
+
let cachedMachineIp = null;
|
|
81
|
+
async function getCachedMachineIp() {
|
|
82
|
+
if (cachedMachineIp !== null)
|
|
83
|
+
return cachedMachineIp;
|
|
84
|
+
cachedMachineIp = await getMachineIp();
|
|
85
|
+
return cachedMachineIp;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Send a callback to the Clawdy Gateway.
|
|
89
|
+
*/
|
|
90
|
+
async function sendCallback(account, payload) {
|
|
91
|
+
if (!account.callbackUrl) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
channel: "clawdy",
|
|
95
|
+
error: "No callbackUrl configured in channels.clawdy",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!account.callbackSecret) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
channel: "clawdy",
|
|
102
|
+
error: "No callbackSecret configured in channels.clawdy",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const machineIp = await getCachedMachineIp();
|
|
106
|
+
const callbackUrl = account.callbackUrl.replace(/\/+$/, "") + "/api/openclaw/callback";
|
|
107
|
+
const res = await fetch(callbackUrl, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"X-Proxy-Secret": account.callbackSecret,
|
|
112
|
+
"X-Machine-IP": machineIp,
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
to: payload.to,
|
|
116
|
+
text: payload.text,
|
|
117
|
+
mediaUrl: payload.mediaUrl,
|
|
118
|
+
messageId: payload.messageId ?? crypto.randomUUID(),
|
|
119
|
+
}),
|
|
120
|
+
signal: AbortSignal.timeout(30_000),
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok) {
|
|
123
|
+
const errorBody = await res.text().catch(() => "");
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
channel: "clawdy",
|
|
127
|
+
error: `Callback failed: ${res.status} — ${errorBody.slice(0, 200)}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const data = await res.json().catch(() => ({}));
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
channel: "clawdy",
|
|
134
|
+
messageId: data.messageId ?? payload.messageId,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ─── Channel Plugin ────────────────────────────────────────────────────────
|
|
138
|
+
const clawdyPlugin = {
|
|
139
|
+
id: "clawdy",
|
|
140
|
+
meta: {
|
|
141
|
+
id: "clawdy",
|
|
142
|
+
label: "Clawdy",
|
|
143
|
+
selectionLabel: "Clawdy (Push Gateway)",
|
|
144
|
+
docsPath: "/channels/clawdy",
|
|
145
|
+
blurb: "Send messages to the Clawdy Gateway for delivery via web dashboard, Slack, Telegram, and more.",
|
|
146
|
+
aliases: ["clawdy-gateway"],
|
|
147
|
+
},
|
|
148
|
+
capabilities: {
|
|
149
|
+
chatTypes: ["direct"],
|
|
150
|
+
},
|
|
151
|
+
config: {
|
|
152
|
+
listAccountIds: (cfg) => {
|
|
153
|
+
const channels = cfg.channels;
|
|
154
|
+
if (channels?.clawdy)
|
|
155
|
+
return ["default"];
|
|
156
|
+
return [];
|
|
157
|
+
},
|
|
158
|
+
resolveAccount: (cfg, accountId) => {
|
|
159
|
+
return resolveAccount(cfg, accountId);
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
outbound: {
|
|
163
|
+
deliveryMode: "direct",
|
|
164
|
+
/**
|
|
165
|
+
* Send a text message to a target via the Clawdy Gateway.
|
|
166
|
+
*/
|
|
167
|
+
sendText: async (ctx) => {
|
|
168
|
+
const account = resolveAccount(ctx.cfg, ctx.accountId);
|
|
169
|
+
return sendCallback(account, {
|
|
170
|
+
to: ctx.to,
|
|
171
|
+
text: ctx.text,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* Send a media message to a target via the Clawdy Gateway.
|
|
176
|
+
*/
|
|
177
|
+
sendMedia: async (ctx) => {
|
|
178
|
+
const account = resolveAccount(ctx.cfg, ctx.accountId);
|
|
179
|
+
return sendCallback(account, {
|
|
180
|
+
to: ctx.to,
|
|
181
|
+
text: ctx.text,
|
|
182
|
+
mediaUrl: ctx.mediaUrl,
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
gateway: {
|
|
187
|
+
/**
|
|
188
|
+
* Called when the Clawdy channel starts.
|
|
189
|
+
* Logs activation and caches the machine IP.
|
|
190
|
+
*/
|
|
191
|
+
startAccount: async (ctx) => {
|
|
192
|
+
const ip = await getCachedMachineIp();
|
|
193
|
+
console.log(`[clawdy-channel] Started. Callback URL: ${ctx.account.callbackUrl || "(not set)"}. Machine IP: ${ip || "(unknown)"}`);
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* Called when the Clawdy channel stops.
|
|
197
|
+
*/
|
|
198
|
+
stopAccount: async () => {
|
|
199
|
+
console.log("[clawdy-channel] Stopped.");
|
|
200
|
+
cachedMachineIp = null;
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
// ─── Plugin Registration ───────────────────────────────────────────────────
|
|
205
|
+
/**
|
|
206
|
+
* Entry point called by OpenClaw's plugin loader.
|
|
207
|
+
* Registers the Clawdy channel plugin.
|
|
208
|
+
*/
|
|
209
|
+
function register(api) {
|
|
210
|
+
api.registerChannel({ plugin: clawdyPlugin });
|
|
211
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "clawdy",
|
|
3
|
+
"name": "Clawdy Channel",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Clawdy push messaging channel — enables OpenClaw to send outbound messages (scheduled, cron, heartbeat) to the Clawdy Gateway for delivery via web dashboard, Slack, Telegram, and more."
|
|
6
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawdy",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "OpenClaw channel plugin for Clawdy — enables outbound push messaging from OpenClaw instances to the Clawdy Gateway",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"openclaw.plugin.json"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"clean": "rm -rf dist",
|
|
14
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
15
|
+
"pack": "npm run clean && npm run build && npm pack",
|
|
16
|
+
"publish:npm": "npm run clean && npm run build && npm publish --access public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"openclaw",
|
|
20
|
+
"clawdy",
|
|
21
|
+
"channel",
|
|
22
|
+
"plugin",
|
|
23
|
+
"gateway"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/heyclawdy/clawdy"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.7.0"
|
|
32
|
+
}
|
|
33
|
+
}
|