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.
@@ -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
+ }