@alfe.ai/openclaw-whatsapp 0.0.25 → 0.0.26
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.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/plugin.cjs +23 -67
- package/dist/plugin.d.cts +42 -16
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +42 -16
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +21 -65
- package/dist/plugin.js.map +1 -1
- package/dist/plugin2.d.cts +2 -0
- package/dist/plugin2.d.ts +2 -0
- package/package.json +4 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import plugin from "./plugin.cjs";
|
|
1
|
+
import { t as plugin } from "./plugin.cjs";
|
|
2
2
|
export { plugin as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import plugin from "./plugin.js";
|
|
1
|
+
import { t as plugin } from "./plugin.js";
|
|
2
2
|
export { plugin as default };
|
package/dist/plugin.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
let _sinclair_typebox = require("@sinclair/typebox");
|
|
2
2
|
let _alfe_ai_config = require("@alfe.ai/config");
|
|
3
3
|
let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
4
|
+
let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
|
|
4
5
|
//#region src/plugin.ts
|
|
5
6
|
/**
|
|
6
7
|
* @alfe/openclaw-whatsapp — OpenClaw native plugin
|
|
@@ -14,62 +15,14 @@ let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
|
14
15
|
* - whatsapp_list_templates — list available approved templates
|
|
15
16
|
*/
|
|
16
17
|
const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}],
|
|
23
|
-
details: data
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function errResult(message) {
|
|
27
|
-
return {
|
|
28
|
-
content: [{
|
|
29
|
-
type: "text",
|
|
30
|
-
text: JSON.stringify({ error: message })
|
|
31
|
-
}],
|
|
32
|
-
details: { error: message }
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function defineTool(def) {
|
|
36
|
-
return {
|
|
37
|
-
name: def.name,
|
|
38
|
-
description: def.description,
|
|
39
|
-
label: def.name,
|
|
40
|
-
parameters: def.parameters,
|
|
41
|
-
execute: async (_toolCallId, params) => {
|
|
42
|
-
try {
|
|
43
|
-
return ok(await def.handler(params));
|
|
44
|
-
} catch (e) {
|
|
45
|
-
return errResult(e.message);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
let apiUrl = "";
|
|
51
|
-
let apiKey = "";
|
|
52
|
-
async function whatsappApi(method, path, body) {
|
|
53
|
-
const url = `${apiUrl}${path}`;
|
|
54
|
-
const headers = {
|
|
55
|
-
"Content-Type": "application/json",
|
|
56
|
-
Authorization: `Bearer ${apiKey}`
|
|
57
|
-
};
|
|
58
|
-
const res = await fetch(url, {
|
|
59
|
-
method,
|
|
60
|
-
headers,
|
|
61
|
-
body: body ? JSON.stringify(body) : void 0
|
|
62
|
-
});
|
|
63
|
-
const json = await res.json();
|
|
64
|
-
if (!res.ok) {
|
|
65
|
-
const errorMsg = typeof json.error === "string" ? json.error : typeof json.message === "string" ? json.message : `WhatsApp service returned ${String(res.status)}`;
|
|
66
|
-
throw new Error(errorMsg);
|
|
67
|
-
}
|
|
68
|
-
if (json.data && typeof json.data === "object") return json.data;
|
|
69
|
-
return json;
|
|
18
|
+
const WHATSAPP_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("whatsapp");
|
|
19
|
+
let client;
|
|
20
|
+
function getClient() {
|
|
21
|
+
if (!client) throw new Error("WhatsApp tools unavailable — no API config resolved");
|
|
22
|
+
return client;
|
|
70
23
|
}
|
|
71
24
|
const whatsappTools = [
|
|
72
|
-
defineTool({
|
|
25
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
73
26
|
name: "whatsapp_send_message",
|
|
74
27
|
description: "Send a free-form WhatsApp message from your phone number. This only works if the recipient has messaged you within the last 24 hours (active session). If there is no active session, you must use whatsapp_send_template to initiate the conversation first.",
|
|
75
28
|
parameters: _sinclair_typebox.Type.Object({
|
|
@@ -79,18 +32,18 @@ const whatsappTools = [
|
|
|
79
32
|
handler: async (params) => {
|
|
80
33
|
const { to, body } = params;
|
|
81
34
|
try {
|
|
82
|
-
if (!(await
|
|
35
|
+
if (!(await getClient().getWhatsAppSession(to)).active) throw new Error("No active WhatsApp session with this user. Use whatsapp_send_template to initiate the conversation first, then the user can reply and you can send free-form messages.");
|
|
83
36
|
} catch (e) {
|
|
84
37
|
const msg = e.message;
|
|
85
38
|
if (msg.includes("No active WhatsApp session") || msg.includes("whatsapp_send_template")) throw e;
|
|
86
39
|
}
|
|
87
|
-
return
|
|
40
|
+
return getClient().sendWhatsAppMessage({
|
|
88
41
|
to,
|
|
89
42
|
body
|
|
90
43
|
});
|
|
91
44
|
}
|
|
92
45
|
}),
|
|
93
|
-
defineTool({
|
|
46
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
94
47
|
name: "whatsapp_send_template",
|
|
95
48
|
description: "Send a pre-approved WhatsApp template message to initiate a conversation. Use this when you need to message a user who hasn't contacted you recently (no active 24-hour session). First call whatsapp_list_templates to see available templates and their variables, then call this tool with the chosen template. After the user replies to your template, you can use whatsapp_send_message for free-form messages.",
|
|
96
49
|
parameters: _sinclair_typebox.Type.Object({
|
|
@@ -104,7 +57,7 @@ const whatsappTools = [
|
|
|
104
57
|
}),
|
|
105
58
|
handler: async (params) => {
|
|
106
59
|
const { to, contentSid, contentVariables, bodyPreview } = params;
|
|
107
|
-
return
|
|
60
|
+
return getClient().sendWhatsAppTemplate({
|
|
108
61
|
to,
|
|
109
62
|
contentSid,
|
|
110
63
|
contentVariables,
|
|
@@ -112,12 +65,12 @@ const whatsappTools = [
|
|
|
112
65
|
});
|
|
113
66
|
}
|
|
114
67
|
}),
|
|
115
|
-
defineTool({
|
|
68
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
116
69
|
name: "whatsapp_list_templates",
|
|
117
70
|
description: "List all approved WhatsApp message templates available for outreach. Each template has a contentSid, name, body with {{1}}, {{2}} placeholders, and variables showing what each placeholder represents. Use this to find the right template before calling whatsapp_send_template.",
|
|
118
71
|
parameters: _sinclair_typebox.Type.Object({}),
|
|
119
72
|
handler: async () => {
|
|
120
|
-
return
|
|
73
|
+
return getClient().listWhatsAppTemplates();
|
|
121
74
|
}
|
|
122
75
|
})
|
|
123
76
|
];
|
|
@@ -131,23 +84,26 @@ const plugin = {
|
|
|
131
84
|
const log = api.logger;
|
|
132
85
|
for (const tool of whatsappTools) api.registerTool(tool);
|
|
133
86
|
log.info(`Registered ${String(whatsappTools.length)} WhatsApp tools: ${whatsappTools.map((t) => t.name).join(", ")}`);
|
|
134
|
-
|
|
135
|
-
globalThis.__whatsappPluginActivated = true;
|
|
87
|
+
(0, _alfe_ai_openclaw_plugin_kit.guardedStart)(WHATSAPP_ACTIVATION_KEY, log, () => {
|
|
136
88
|
log.info("Alfe WhatsApp plugin activating...");
|
|
137
89
|
try {
|
|
138
90
|
const config = (0, _alfe_ai_config.resolveConfig)();
|
|
139
|
-
|
|
140
|
-
|
|
91
|
+
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
92
|
+
apiUrl: config.apiUrl,
|
|
93
|
+
apiKey: config.apiKey
|
|
94
|
+
});
|
|
95
|
+
log.info(`WhatsApp API: ${config.apiUrl}`);
|
|
141
96
|
} catch (err) {
|
|
142
97
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
143
98
|
log.warn("WhatsApp tools will fail — no API config available");
|
|
99
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(WHATSAPP_ACTIVATION_KEY);
|
|
144
100
|
}
|
|
145
|
-
|
|
146
|
-
}
|
|
101
|
+
});
|
|
147
102
|
log.info("Alfe WhatsApp plugin activated");
|
|
148
103
|
},
|
|
149
104
|
deactivate(api) {
|
|
150
|
-
|
|
105
|
+
client = void 0;
|
|
106
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(WHATSAPP_ACTIVATION_KEY);
|
|
151
107
|
api.logger.info("Alfe WhatsApp plugin deactivated");
|
|
152
108
|
}
|
|
153
109
|
};
|
package/dist/plugin.d.cts
CHANGED
|
@@ -1,7 +1,46 @@
|
|
|
1
1
|
import { TSchema } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
4
4
|
|
|
5
|
+
//# sourceMappingURL=types.d.ts.map
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/tools.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that
|
|
10
|
+
* was copy-pasted across 8 plugins (identity, google, teams, mobile,
|
|
11
|
+
* whatsapp, voice, chat a2a-tools, base openclaw).
|
|
12
|
+
*
|
|
13
|
+
* Error handling is standardized on the openclaw-google variant — the only
|
|
14
|
+
* copy that survived non-`Error` throws (`e instanceof Error ? e.message :
|
|
15
|
+
* "Unknown error"`). The other copies did `(e as Error).message`, which
|
|
16
|
+
* crashes the tool executor when a handler throws a string/object.
|
|
17
|
+
*/
|
|
18
|
+
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
19
|
+
interface ToolResult {
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
details: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* An OpenClaw tool definition.
|
|
28
|
+
*
|
|
29
|
+
* `parameters` is generic because the fleet is split between TypeBox
|
|
30
|
+
* `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain
|
|
31
|
+
* JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema
|
|
32
|
+
* type the plugin uses — the kit itself has no schema dependency.
|
|
33
|
+
*/
|
|
34
|
+
interface ToolDef<TParameters = unknown> {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
label: string;
|
|
38
|
+
parameters: TParameters;
|
|
39
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
40
|
+
}
|
|
41
|
+
/** Wrap a successful handler result in the OpenClaw tool-result envelope. */
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/plugin.d.ts
|
|
5
44
|
interface Logger {
|
|
6
45
|
info(msg: string, ...args: unknown[]): void;
|
|
7
46
|
warn(msg: string, ...args: unknown[]): void;
|
|
@@ -11,25 +50,12 @@ interface Logger {
|
|
|
11
50
|
interface OpenClawPluginApi {
|
|
12
51
|
logger: Logger;
|
|
13
52
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
53
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
54
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
55
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
56
|
priority?: number;
|
|
18
57
|
}): void;
|
|
19
58
|
}
|
|
20
|
-
interface ToolDef {
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
label: string;
|
|
24
|
-
parameters: TSchema;
|
|
25
|
-
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{
|
|
26
|
-
content: {
|
|
27
|
-
type: "text";
|
|
28
|
-
text: string;
|
|
29
|
-
}[];
|
|
30
|
-
details: unknown;
|
|
31
|
-
}>;
|
|
32
|
-
}
|
|
33
59
|
declare const plugin: {
|
|
34
60
|
id: string;
|
|
35
61
|
name: string;
|
|
@@ -39,5 +65,5 @@ declare const plugin: {
|
|
|
39
65
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
66
|
};
|
|
41
67
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
68
|
+
export { plugin as t };
|
|
43
69
|
//# sourceMappingURL=plugin.d.cts.map
|
package/dist/plugin.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"plugin.d.cts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","ok","errResult","defineTool","IpcResponse","IpcClient","ConnectToDaemonOptions","connectToDaemon","ResolveOpenClawSdkOptions","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart","ScheduleRefreshOptions","RefreshHandle","scheduleRefresh"],"sources":["../../openclaw-plugin-kit/dist/index.d.ts","../src/plugin.ts"],"sourcesContent":["//#region src/types.d.ts\n/**\n * Minimal logger contract shared by every helper in the kit.\n *\n * Deliberately the *narrowest* shape in the fleet: some plugins declare\n * variadic loggers (`info(msg: string, ...args: unknown[])`), others\n * single-arg (`info(msg: string)`). Both are assignable to this. The kit\n * only ever calls with a single string.\n */\ninterface KitLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n//# sourceMappingURL=types.d.ts.map\n//#endregion\n//#region src/tools.d.ts\n/**\n * Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that\n * was copy-pasted across 8 plugins (identity, google, teams, mobile,\n * whatsapp, voice, chat a2a-tools, base openclaw).\n *\n * Error handling is standardized on the openclaw-google variant — the only\n * copy that survived non-`Error` throws (`e instanceof Error ? e.message :\n * \"Unknown error\"`). The other copies did `(e as Error).message`, which\n * crashes the tool executor when a handler throws a string/object.\n */\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n}\n/**\n * An OpenClaw tool definition.\n *\n * `parameters` is generic because the fleet is split between TypeBox\n * `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain\n * JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema\n * type the plugin uses — the kit itself has no schema dependency.\n */\ninterface ToolDef<TParameters = unknown> {\n name: string;\n description: string;\n label: string;\n parameters: TParameters;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;\n}\n/** Wrap a successful handler result in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an error message in the OpenClaw tool-result envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define an OpenClaw tool from a plain async handler. Handler throws (of\n * any type — `Error` or not) are converted to `errResult` envelopes so the\n * LLM sees a structured error instead of the tool executor crashing.\n */\ndeclare function defineTool<TParameters>(def: {\n name: string;\n description: string;\n parameters: TParameters;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef<TParameters>;\n//# sourceMappingURL=tools.d.ts.map\n//#endregion\n//#region src/daemon.d.ts\n/** Response envelope for daemon IPC requests. */\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\n/** The subset of `@alfe.ai/openclaw`'s IPCClient the kit relies on. */\ninterface IpcClient {\n on(event: string, handler: (...args: unknown[]) => void): void;\n request(method: string, params: Record<string, unknown>): Promise<IpcResponse>;\n start(): void;\n stop(): void;\n}\ninterface ConnectToDaemonOptions {\n /** Plugin package name sent with `capability.register` (e.g. `@alfe.ai/openclaw-sync`). */\n pluginId: string;\n /** Capabilities to register on every (re)connect. Constants stay per-plugin. */\n capabilities?: readonly string[];\n /**\n * Optional handler for daemon → plugin messages. Only invoked with\n * object payloads — the kit does the message-shape checking.\n */\n onMessage?: (msg: Record<string, unknown>) => void;\n /** Log line when the daemon isn't available (plugin runs standalone). */\n standaloneNote?: string;\n}\n/**\n * Attempt to connect to the Alfe daemon IPC socket. Returns `null` (after\n * an info log) when `@alfe.ai/openclaw` isn't installed — plugins degrade\n * gracefully to standalone mode.\n */\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions): Promise<IpcClient | null>;\n//# sourceMappingURL=daemon.d.ts.map\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n /** Module specifier resolved from OpenClaw's context. */\n specifier?: string;\n /** Named export to extract from the resolved module. */\n exportName?: string;\n /** Warn line emitted when the SDK can't be resolved. */\n unresolvableNote?: string;\n}\n/**\n * Resolve a named export from the running OpenClaw process's SDK.\n *\n * Defaults target `dispatchInboundDirectDmWithRuntime` from\n * `openclaw/plugin-sdk/channel-inbound` — the export both existing\n * consumers (chat, google-chat) need. Returns `null` (after a warn log)\n * when unresolvable; callers degrade gracefully.\n */\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions): T | null;\n//# sourceMappingURL=sdk.d.ts.map\n\n//#endregion\n//#region src/activation.d.ts\n/**\n * Canonical activation-flag key for a plugin: `__alfe<Name>PluginActivated`.\n *\n * Accepts a short name (`\"google\"`, `\"google-chat\"`) or a full package name\n * (`\"@alfe.ai/openclaw-teams\"`); scope + `openclaw-` prefix are stripped\n * and the remainder PascalCased:\n *\n * getActivationKey(\"google\") → \"__alfeGooglePluginActivated\"\n * getActivationKey(\"google-chat\") → \"__alfeGoogleChatPluginActivated\"\n * getActivationKey(\"@alfe.ai/openclaw-teams\") → \"__alfeTeamsPluginActivated\"\n */\ndeclare function getActivationKey(name: string): string;\n/** True when the activation flag for `key` is currently set. */\ndeclare function isActivated(key: string): boolean;\n/**\n * Clear the activation flag so a later start can run again.\n *\n * Call this LAST in stop/deactivate paths — after side effects are stopped\n * (see the ordering rule in the module doc). Also exported for soft-failure\n * paths inside a `guardedStart` fn that want to log at a custom level and\n * return normally instead of throwing.\n */\ndeclare function resetActivation(key: string): void;\n/**\n * Run a service start exactly once per activation cycle.\n *\n * - If the flag is already set, logs at debug and returns `false` (skipped).\n * - Otherwise sets the flag and runs `fn`.\n * - If `fn` throws synchronously OR returns a promise that rejects, the\n * flag is RESET (so a later activate can retry) and the error is logged.\n * Errors are not rethrown — matching fleet behavior where a failed start\n * must never crash the host's plugin loader.\n *\n * Returns `true` when the start was initiated (even if an async portion\n * later fails), `false` when skipped or when `fn` threw synchronously.\n *\n * Note: cleaning up partial side effects on failure is `fn`'s job (throw\n * only after tearing down what was started); the kit only guarantees the\n * flag reset happens after `fn` has failed — i.e. after `fn`'s own cleanup.\n */\ndeclare function guardedStart(key: string, log: KitLogger, fn: () => void | Promise<void>): boolean;\n//# sourceMappingURL=activation.d.ts.map\n//#endregion\n//#region src/refresh.d.ts\ninterface ScheduleRefreshOptions {\n /** Cadence between successful refreshes (per-provider token lifetime — stays in the consuming package). */\n intervalMs: number;\n /** Delay before retrying after a failed refresh. */\n retryMs: number;\n /** Run one refresh immediately instead of waiting a full interval first. Default false. */\n immediate?: boolean;\n}\ninterface RefreshHandle {\n /** Cancel the schedule. Safe to call multiple times. */\n stop(): void;\n}\n/**\n * Run `refreshFn` every `intervalMs`; on failure, log a warning and retry\n * after `retryMs`. Timers are unref'd so the schedule never keeps the\n * process alive.\n */\ndeclare function scheduleRefresh(refreshFn: () => Promise<void>, options: ScheduleRefreshOptions, log: KitLogger): RefreshHandle;\n//# sourceMappingURL=refresh.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, type RefreshHandle, type ResolveOpenClawSdkOptions, type ScheduleRefreshOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, resetActivation, resolveOpenClawSdk, scheduleRefresh };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;AC0BgB;;;;;;;;AAYmD;;;;;;UDTzDC,UAAAA;;;;;;;;;;;;;;;UAeAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;UCvBlE,MAAA;EAAA,IAAA,CAAA,GAAA,EAAM,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAON,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAiB,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UADA,iBAAA,CAGW;QACkD,EAH7D,MAG6D;kBACX,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAAO,YAAA,CAAA,IAAA,EAF9C,OAE8C,CAFtC,OAEsC,CAAA,CAAA,EAAA,IAAA;EAoG7D,qBA6CL,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAlJsE,OAkJtE,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAjJ2D,OAiJ3D,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;IAvCe,QAAA,CAAA,EAAA,MAAA;MAiCE,IAAA;;cAvCZ;;;;;gBAMU;kBAiCE"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,7 +1,46 @@
|
|
|
1
1
|
import { TSchema } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
4
4
|
|
|
5
|
+
//# sourceMappingURL=types.d.ts.map
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/tools.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that
|
|
10
|
+
* was copy-pasted across 8 plugins (identity, google, teams, mobile,
|
|
11
|
+
* whatsapp, voice, chat a2a-tools, base openclaw).
|
|
12
|
+
*
|
|
13
|
+
* Error handling is standardized on the openclaw-google variant — the only
|
|
14
|
+
* copy that survived non-`Error` throws (`e instanceof Error ? e.message :
|
|
15
|
+
* "Unknown error"`). The other copies did `(e as Error).message`, which
|
|
16
|
+
* crashes the tool executor when a handler throws a string/object.
|
|
17
|
+
*/
|
|
18
|
+
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
19
|
+
interface ToolResult {
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
details: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* An OpenClaw tool definition.
|
|
28
|
+
*
|
|
29
|
+
* `parameters` is generic because the fleet is split between TypeBox
|
|
30
|
+
* `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain
|
|
31
|
+
* JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema
|
|
32
|
+
* type the plugin uses — the kit itself has no schema dependency.
|
|
33
|
+
*/
|
|
34
|
+
interface ToolDef<TParameters = unknown> {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
label: string;
|
|
38
|
+
parameters: TParameters;
|
|
39
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
40
|
+
}
|
|
41
|
+
/** Wrap a successful handler result in the OpenClaw tool-result envelope. */
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/plugin.d.ts
|
|
5
44
|
interface Logger {
|
|
6
45
|
info(msg: string, ...args: unknown[]): void;
|
|
7
46
|
warn(msg: string, ...args: unknown[]): void;
|
|
@@ -11,25 +50,12 @@ interface Logger {
|
|
|
11
50
|
interface OpenClawPluginApi {
|
|
12
51
|
logger: Logger;
|
|
13
52
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
53
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
54
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
55
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
56
|
priority?: number;
|
|
18
57
|
}): void;
|
|
19
58
|
}
|
|
20
|
-
interface ToolDef {
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
label: string;
|
|
24
|
-
parameters: TSchema;
|
|
25
|
-
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{
|
|
26
|
-
content: {
|
|
27
|
-
type: "text";
|
|
28
|
-
text: string;
|
|
29
|
-
}[];
|
|
30
|
-
details: unknown;
|
|
31
|
-
}>;
|
|
32
|
-
}
|
|
33
59
|
declare const plugin: {
|
|
34
60
|
id: string;
|
|
35
61
|
name: string;
|
|
@@ -39,5 +65,5 @@ declare const plugin: {
|
|
|
39
65
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
66
|
};
|
|
41
67
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
68
|
+
export { plugin as t };
|
|
43
69
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":["KitLogger","ToolResult","ToolDef","TParameters","Record","Promise","ok","errResult","defineTool","IpcResponse","IpcClient","ConnectToDaemonOptions","connectToDaemon","ResolveOpenClawSdkOptions","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart","ScheduleRefreshOptions","RefreshHandle","scheduleRefresh"],"sources":["../../openclaw-plugin-kit/dist/index.d.ts","../src/plugin.ts"],"sourcesContent":["//#region src/types.d.ts\n/**\n * Minimal logger contract shared by every helper in the kit.\n *\n * Deliberately the *narrowest* shape in the fleet: some plugins declare\n * variadic loggers (`info(msg: string, ...args: unknown[])`), others\n * single-arg (`info(msg: string)`). Both are assignable to this. The kit\n * only ever calls with a single string.\n */\ninterface KitLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n debug(msg: string): void;\n}\n//# sourceMappingURL=types.d.ts.map\n//#endregion\n//#region src/tools.d.ts\n/**\n * Tool-definition helpers — the `defineTool` / `ok` / `errResult` trio that\n * was copy-pasted across 8 plugins (identity, google, teams, mobile,\n * whatsapp, voice, chat a2a-tools, base openclaw).\n *\n * Error handling is standardized on the openclaw-google variant — the only\n * copy that survived non-`Error` throws (`e instanceof Error ? e.message :\n * \"Unknown error\"`). The other copies did `(e as Error).message`, which\n * crashes the tool executor when a handler throws a string/object.\n */\n/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n}\n/**\n * An OpenClaw tool definition.\n *\n * `parameters` is generic because the fleet is split between TypeBox\n * `TSchema` schemas (identity/google/teams/mobile/whatsapp) and plain\n * JSON-Schema objects (chat a2a-tools). Instantiate with whichever schema\n * type the plugin uses — the kit itself has no schema dependency.\n */\ninterface ToolDef<TParameters = unknown> {\n name: string;\n description: string;\n label: string;\n parameters: TParameters;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;\n}\n/** Wrap a successful handler result in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an error message in the OpenClaw tool-result envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define an OpenClaw tool from a plain async handler. Handler throws (of\n * any type — `Error` or not) are converted to `errResult` envelopes so the\n * LLM sees a structured error instead of the tool executor crashing.\n */\ndeclare function defineTool<TParameters>(def: {\n name: string;\n description: string;\n parameters: TParameters;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef<TParameters>;\n//# sourceMappingURL=tools.d.ts.map\n//#endregion\n//#region src/daemon.d.ts\n/** Response envelope for daemon IPC requests. */\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\n/** The subset of `@alfe.ai/openclaw`'s IPCClient the kit relies on. */\ninterface IpcClient {\n on(event: string, handler: (...args: unknown[]) => void): void;\n request(method: string, params: Record<string, unknown>): Promise<IpcResponse>;\n start(): void;\n stop(): void;\n}\ninterface ConnectToDaemonOptions {\n /** Plugin package name sent with `capability.register` (e.g. `@alfe.ai/openclaw-sync`). */\n pluginId: string;\n /** Capabilities to register on every (re)connect. Constants stay per-plugin. */\n capabilities?: readonly string[];\n /**\n * Optional handler for daemon → plugin messages. Only invoked with\n * object payloads — the kit does the message-shape checking.\n */\n onMessage?: (msg: Record<string, unknown>) => void;\n /** Log line when the daemon isn't available (plugin runs standalone). */\n standaloneNote?: string;\n}\n/**\n * Attempt to connect to the Alfe daemon IPC socket. Returns `null` (after\n * an info log) when `@alfe.ai/openclaw` isn't installed — plugins degrade\n * gracefully to standalone mode.\n */\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions): Promise<IpcClient | null>;\n//# sourceMappingURL=daemon.d.ts.map\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n /** Module specifier resolved from OpenClaw's context. */\n specifier?: string;\n /** Named export to extract from the resolved module. */\n exportName?: string;\n /** Warn line emitted when the SDK can't be resolved. */\n unresolvableNote?: string;\n}\n/**\n * Resolve a named export from the running OpenClaw process's SDK.\n *\n * Defaults target `dispatchInboundDirectDmWithRuntime` from\n * `openclaw/plugin-sdk/channel-inbound` — the export both existing\n * consumers (chat, google-chat) need. Returns `null` (after a warn log)\n * when unresolvable; callers degrade gracefully.\n */\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions): T | null;\n//# sourceMappingURL=sdk.d.ts.map\n\n//#endregion\n//#region src/activation.d.ts\n/**\n * Canonical activation-flag key for a plugin: `__alfe<Name>PluginActivated`.\n *\n * Accepts a short name (`\"google\"`, `\"google-chat\"`) or a full package name\n * (`\"@alfe.ai/openclaw-teams\"`); scope + `openclaw-` prefix are stripped\n * and the remainder PascalCased:\n *\n * getActivationKey(\"google\") → \"__alfeGooglePluginActivated\"\n * getActivationKey(\"google-chat\") → \"__alfeGoogleChatPluginActivated\"\n * getActivationKey(\"@alfe.ai/openclaw-teams\") → \"__alfeTeamsPluginActivated\"\n */\ndeclare function getActivationKey(name: string): string;\n/** True when the activation flag for `key` is currently set. */\ndeclare function isActivated(key: string): boolean;\n/**\n * Clear the activation flag so a later start can run again.\n *\n * Call this LAST in stop/deactivate paths — after side effects are stopped\n * (see the ordering rule in the module doc). Also exported for soft-failure\n * paths inside a `guardedStart` fn that want to log at a custom level and\n * return normally instead of throwing.\n */\ndeclare function resetActivation(key: string): void;\n/**\n * Run a service start exactly once per activation cycle.\n *\n * - If the flag is already set, logs at debug and returns `false` (skipped).\n * - Otherwise sets the flag and runs `fn`.\n * - If `fn` throws synchronously OR returns a promise that rejects, the\n * flag is RESET (so a later activate can retry) and the error is logged.\n * Errors are not rethrown — matching fleet behavior where a failed start\n * must never crash the host's plugin loader.\n *\n * Returns `true` when the start was initiated (even if an async portion\n * later fails), `false` when skipped or when `fn` threw synchronously.\n *\n * Note: cleaning up partial side effects on failure is `fn`'s job (throw\n * only after tearing down what was started); the kit only guarantees the\n * flag reset happens after `fn` has failed — i.e. after `fn`'s own cleanup.\n */\ndeclare function guardedStart(key: string, log: KitLogger, fn: () => void | Promise<void>): boolean;\n//# sourceMappingURL=activation.d.ts.map\n//#endregion\n//#region src/refresh.d.ts\ninterface ScheduleRefreshOptions {\n /** Cadence between successful refreshes (per-provider token lifetime — stays in the consuming package). */\n intervalMs: number;\n /** Delay before retrying after a failed refresh. */\n retryMs: number;\n /** Run one refresh immediately instead of waiting a full interval first. Default false. */\n immediate?: boolean;\n}\ninterface RefreshHandle {\n /** Cancel the schedule. Safe to call multiple times. */\n stop(): void;\n}\n/**\n * Run `refreshFn` every `intervalMs`; on failure, log a warning and retry\n * after `retryMs`. Timers are unref'd so the schedule never keeps the\n * process alive.\n */\ndeclare function scheduleRefresh(refreshFn: () => Promise<void>, options: ScheduleRefreshOptions, log: KitLogger): RefreshHandle;\n//# sourceMappingURL=refresh.d.ts.map\n\n//#endregion\nexport { type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, type RefreshHandle, type ResolveOpenClawSdkOptions, type ScheduleRefreshOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, resetActivation, resolveOpenClawSdk, scheduleRefresh };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;AC0BgB;;;;;;;;AAYmD;;;;;;UDTzDC,UAAAA;;;;;;;;;;;;;;;UAeAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;UCvBlE,MAAA;EAAA,IAAA,CAAA,GAAA,EAAM,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAON,IAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAiB,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UADA,iBAAA,CAGW;QACkD,EAH7D,MAG6D;kBACX,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;EAAO,YAAA,CAAA,IAAA,EAF9C,OAE8C,CAFtC,OAEsC,CAAA,CAAA,EAAA,IAAA;EAoG7D,qBA6CL,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAlJsE,OAkJtE,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAjJ2D,OAiJ3D,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;IAvCe,QAAA,CAAA,EAAA,MAAA;MAiCE,IAAA;;cAvCZ;;;;;gBAMU;kBAiCE"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { resolveConfig } from "@alfe.ai/config";
|
|
4
|
-
import { installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
4
|
+
import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
5
|
+
import { defineTool, getActivationKey, guardedStart, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
|
|
5
6
|
//#region src/plugin.ts
|
|
6
7
|
/**
|
|
7
8
|
* @alfe/openclaw-whatsapp — OpenClaw native plugin
|
|
@@ -15,59 +16,11 @@ import { installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
|
15
16
|
* - whatsapp_list_templates — list available approved templates
|
|
16
17
|
*/
|
|
17
18
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}],
|
|
24
|
-
details: data
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function errResult(message) {
|
|
28
|
-
return {
|
|
29
|
-
content: [{
|
|
30
|
-
type: "text",
|
|
31
|
-
text: JSON.stringify({ error: message })
|
|
32
|
-
}],
|
|
33
|
-
details: { error: message }
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function defineTool(def) {
|
|
37
|
-
return {
|
|
38
|
-
name: def.name,
|
|
39
|
-
description: def.description,
|
|
40
|
-
label: def.name,
|
|
41
|
-
parameters: def.parameters,
|
|
42
|
-
execute: async (_toolCallId, params) => {
|
|
43
|
-
try {
|
|
44
|
-
return ok(await def.handler(params));
|
|
45
|
-
} catch (e) {
|
|
46
|
-
return errResult(e.message);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
let apiUrl = "";
|
|
52
|
-
let apiKey = "";
|
|
53
|
-
async function whatsappApi(method, path, body) {
|
|
54
|
-
const url = `${apiUrl}${path}`;
|
|
55
|
-
const headers = {
|
|
56
|
-
"Content-Type": "application/json",
|
|
57
|
-
Authorization: `Bearer ${apiKey}`
|
|
58
|
-
};
|
|
59
|
-
const res = await fetch(url, {
|
|
60
|
-
method,
|
|
61
|
-
headers,
|
|
62
|
-
body: body ? JSON.stringify(body) : void 0
|
|
63
|
-
});
|
|
64
|
-
const json = await res.json();
|
|
65
|
-
if (!res.ok) {
|
|
66
|
-
const errorMsg = typeof json.error === "string" ? json.error : typeof json.message === "string" ? json.message : `WhatsApp service returned ${String(res.status)}`;
|
|
67
|
-
throw new Error(errorMsg);
|
|
68
|
-
}
|
|
69
|
-
if (json.data && typeof json.data === "object") return json.data;
|
|
70
|
-
return json;
|
|
19
|
+
const WHATSAPP_ACTIVATION_KEY = getActivationKey("whatsapp");
|
|
20
|
+
let client;
|
|
21
|
+
function getClient() {
|
|
22
|
+
if (!client) throw new Error("WhatsApp tools unavailable — no API config resolved");
|
|
23
|
+
return client;
|
|
71
24
|
}
|
|
72
25
|
const whatsappTools = [
|
|
73
26
|
defineTool({
|
|
@@ -80,12 +33,12 @@ const whatsappTools = [
|
|
|
80
33
|
handler: async (params) => {
|
|
81
34
|
const { to, body } = params;
|
|
82
35
|
try {
|
|
83
|
-
if (!(await
|
|
36
|
+
if (!(await getClient().getWhatsAppSession(to)).active) throw new Error("No active WhatsApp session with this user. Use whatsapp_send_template to initiate the conversation first, then the user can reply and you can send free-form messages.");
|
|
84
37
|
} catch (e) {
|
|
85
38
|
const msg = e.message;
|
|
86
39
|
if (msg.includes("No active WhatsApp session") || msg.includes("whatsapp_send_template")) throw e;
|
|
87
40
|
}
|
|
88
|
-
return
|
|
41
|
+
return getClient().sendWhatsAppMessage({
|
|
89
42
|
to,
|
|
90
43
|
body
|
|
91
44
|
});
|
|
@@ -105,7 +58,7 @@ const whatsappTools = [
|
|
|
105
58
|
}),
|
|
106
59
|
handler: async (params) => {
|
|
107
60
|
const { to, contentSid, contentVariables, bodyPreview } = params;
|
|
108
|
-
return
|
|
61
|
+
return getClient().sendWhatsAppTemplate({
|
|
109
62
|
to,
|
|
110
63
|
contentSid,
|
|
111
64
|
contentVariables,
|
|
@@ -118,7 +71,7 @@ const whatsappTools = [
|
|
|
118
71
|
description: "List all approved WhatsApp message templates available for outreach. Each template has a contentSid, name, body with {{1}}, {{2}} placeholders, and variables showing what each placeholder represents. Use this to find the right template before calling whatsapp_send_template.",
|
|
119
72
|
parameters: Type.Object({}),
|
|
120
73
|
handler: async () => {
|
|
121
|
-
return
|
|
74
|
+
return getClient().listWhatsAppTemplates();
|
|
122
75
|
}
|
|
123
76
|
})
|
|
124
77
|
];
|
|
@@ -132,23 +85,26 @@ const plugin = {
|
|
|
132
85
|
const log = api.logger;
|
|
133
86
|
for (const tool of whatsappTools) api.registerTool(tool);
|
|
134
87
|
log.info(`Registered ${String(whatsappTools.length)} WhatsApp tools: ${whatsappTools.map((t) => t.name).join(", ")}`);
|
|
135
|
-
|
|
136
|
-
globalThis.__whatsappPluginActivated = true;
|
|
88
|
+
guardedStart(WHATSAPP_ACTIVATION_KEY, log, () => {
|
|
137
89
|
log.info("Alfe WhatsApp plugin activating...");
|
|
138
90
|
try {
|
|
139
91
|
const config = resolveConfig();
|
|
140
|
-
|
|
141
|
-
|
|
92
|
+
client = new AgentApiClient({
|
|
93
|
+
apiUrl: config.apiUrl,
|
|
94
|
+
apiKey: config.apiKey
|
|
95
|
+
});
|
|
96
|
+
log.info(`WhatsApp API: ${config.apiUrl}`);
|
|
142
97
|
} catch (err) {
|
|
143
98
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
144
99
|
log.warn("WhatsApp tools will fail — no API config available");
|
|
100
|
+
resetActivation(WHATSAPP_ACTIVATION_KEY);
|
|
145
101
|
}
|
|
146
|
-
|
|
147
|
-
}
|
|
102
|
+
});
|
|
148
103
|
log.info("Alfe WhatsApp plugin activated");
|
|
149
104
|
},
|
|
150
105
|
deactivate(api) {
|
|
151
|
-
|
|
106
|
+
client = void 0;
|
|
107
|
+
resetActivation(WHATSAPP_ACTIVATION_KEY);
|
|
152
108
|
api.logger.info("Alfe WhatsApp plugin deactivated");
|
|
153
109
|
}
|
|
154
110
|
};
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-whatsapp — OpenClaw native plugin\n *\n * Registers WhatsApp tools with OpenClaw. Tools call the Twilio service\n * API using the agent's API key for authentication.\n *\n * This plugin provides:\n * - whatsapp_send_message — send a free-form WhatsApp message (requires active session)\n * - whatsapp_send_template — send a template message to initiate conversation\n * - whatsapp_list_templates — list available approved templates\n */\n\nimport { Type, type TSchema } from \"@sinclair/typebox\";\nimport { resolveConfig } from \"@alfe.ai/config\";\nimport { installToolErrorCapture } from \"@alfe.ai/agent-api-client\";\nimport { createRequire } from 'node:module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\ninterface Logger {\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n debug(msg: string, ...args: unknown[]): void;\n}\n\ninterface OpenClawPluginApi {\n logger: Logger;\n registrationMode?: \"full\" | \"setup-only\" | \"setup-runtime\" | \"cli-metadata\";\n registerTool(tool: ToolDef): void;\n registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: { priority?: number }): void;\n}\n\n// ── Tool types ───────────────────────────────────────────────\n\ninterface ToolDef {\n name: string;\n description: string;\n label: string;\n parameters: TSchema;\n execute: (toolCallId: string, params: Record<string, unknown>) => Promise<{\n content: { type: \"text\"; text: string }[];\n details: unknown;\n }>;\n}\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(data) }], details: data };\n}\n\nfunction errResult(message: string) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify({ error: message }) }], details: { error: message } };\n}\n\nfunction defineTool(def: {\n name: string;\n description: string;\n parameters: TSchema;\n handler: (params: Record<string, unknown>) => Promise<unknown>;\n}): ToolDef {\n return {\n name: def.name,\n description: def.description,\n label: def.name,\n parameters: def.parameters,\n execute: async (_toolCallId: string, params: Record<string, unknown>) => {\n try {\n const result = await def.handler(params);\n return ok(result);\n } catch (e) {\n return errResult((e as Error).message);\n }\n },\n };\n}\n\n// ── API client ───────────────────────────────────────────────\n\nlet apiUrl = \"\";\nlet apiKey = \"\";\n\nasync function whatsappApi(\n method: string,\n path: string,\n body?: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const url = `${apiUrl}${path}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n };\n\n const res = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const json = (await res.json()) as Record<string, unknown>;\n if (!res.ok) {\n const errorMsg = typeof json.error === \"string\"\n ? json.error\n : typeof json.message === \"string\"\n ? json.message\n : `WhatsApp service returned ${String(res.status)}`;\n throw new Error(errorMsg);\n }\n\n // unwrap { data: ... } envelope if present\n if (json.data && typeof json.data === \"object\") {\n return json.data as Record<string, unknown>;\n }\n return json;\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst whatsappTools: ToolDef[] = [\n defineTool({\n name: \"whatsapp_send_message\",\n description:\n \"Send a free-form WhatsApp message from your phone number. \" +\n \"This only works if the recipient has messaged you within the last 24 hours (active session). \" +\n \"If there is no active session, you must use whatsapp_send_template to initiate the conversation first.\",\n parameters: Type.Object({\n to: Type.String({ description: \"Recipient phone number in E.164 format (e.g., +12025551234)\" }),\n body: Type.String({ description: \"The message content to send\" }),\n }),\n handler: async (params) => {\n const { to, body } = params as { to: string; body: string };\n\n // Pre-check session to give a clear error before hitting the API\n try {\n const session = await whatsappApi(\"GET\", `/mobile/whatsapp/session?to=${encodeURIComponent(to)}`);\n if (!session.active) {\n throw new Error(\n \"No active WhatsApp session with this user. Use whatsapp_send_template to initiate the conversation first, then the user can reply and you can send free-form messages.\",\n );\n }\n } catch (e) {\n const msg = (e as Error).message;\n if (msg.includes(\"No active WhatsApp session\") || msg.includes(\"whatsapp_send_template\")) {\n throw e;\n }\n // If session check fails for other reasons, try sending anyway — the API will enforce\n }\n\n return whatsappApi(\"POST\", \"/mobile/whatsapp/send\", { to, body });\n },\n }),\n\n defineTool({\n name: \"whatsapp_send_template\",\n description:\n \"Send a pre-approved WhatsApp template message to initiate a conversation. \" +\n \"Use this when you need to message a user who hasn't contacted you recently (no active 24-hour session). \" +\n \"First call whatsapp_list_templates to see available templates and their variables, \" +\n \"then call this tool with the chosen template. \" +\n \"After the user replies to your template, you can use whatsapp_send_message for free-form messages.\",\n parameters: Type.Object({\n to: Type.String({ description: \"Recipient phone number in E.164 format (e.g., +12025551234)\" }),\n contentSid: Type.String({ description: \"The template content SID (from whatsapp_list_templates)\" }),\n contentVariables: Type.Object({}, { description: \"Variables to fill in the template, e.g. {\\\"1\\\": \\\"Kevin\\\", \\\"2\\\": \\\"Agent Name\\\"}\", additionalProperties: Type.String() }),\n bodyPreview: Type.Optional(Type.String({ description: \"The filled template text for conversation context\" })),\n }),\n handler: async (params) => {\n const { to, contentSid, contentVariables, bodyPreview } = params as {\n to: string;\n contentSid: string;\n contentVariables: Record<string, string>;\n bodyPreview?: string;\n };\n return whatsappApi(\"POST\", \"/mobile/whatsapp/send-template\", {\n to,\n contentSid,\n contentVariables,\n bodyPreview,\n });\n },\n }),\n\n defineTool({\n name: \"whatsapp_list_templates\",\n description:\n \"List all approved WhatsApp message templates available for outreach. \" +\n \"Each template has a contentSid, name, body with {{1}}, {{2}} placeholders, \" +\n \"and variables showing what each placeholder represents. \" +\n \"Use this to find the right template before calling whatsapp_send_template.\",\n parameters: Type.Object({}),\n handler: async () => {\n return whatsappApi(\"GET\", \"/mobile/whatsapp/templates\");\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-whatsapp\",\n name: \"Alfe WhatsApp Plugin\",\n description: \"WhatsApp integration — messaging and templates via Twilio\",\n version: pkg.version,\n\n activate(api: OpenClawPluginApi) {\n // First thing, before any registerTool call: tool failures emit a\n // deterministic [ERROR] line the gateway's runtime-output monitor captures\n // to Sentry. See @alfe.ai/agent-api-client tool-error-capture.\n installToolErrorCapture(api, { plugin: \"openclaw-whatsapp\" });\n const log = api.logger;\n\n\n // Always register tools — OpenClaw may reload the plugin registry per-session\n for (const tool of whatsappTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${String(whatsappTools.length)} WhatsApp tools: ${whatsappTools.map((t) => t.name).join(\", \")}`);\n\n // Only initialize config once (side effects)\n if (!(globalThis as Record<string, unknown>).__whatsappPluginActivated) {\n (globalThis as Record<string, unknown>).__whatsappPluginActivated = true;\n log.info(\"Alfe WhatsApp plugin activating...\");\n\n try {\n const config = resolveConfig();\n apiUrl = config.apiUrl;\n apiKey = config.apiKey;\n } catch (err) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);\n log.warn(\"WhatsApp tools will fail — no API config available\");\n }\n\n log.info(`WhatsApp API: ${apiUrl}`);\n }\n\n log.info(\"Alfe WhatsApp plugin activated\");\n },\n\n deactivate(api: OpenClawPluginApi) {\n (globalThis as Record<string, unknown>).__whatsappPluginActivated = false;\n api.logger.info(\"Alfe WhatsApp plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-whatsapp — OpenClaw native plugin\n *\n * Registers WhatsApp tools with OpenClaw. Tools call the Twilio service\n * API using the agent's API key for authentication.\n *\n * This plugin provides:\n * - whatsapp_send_message — send a free-form WhatsApp message (requires active session)\n * - whatsapp_send_template — send a template message to initiate conversation\n * - whatsapp_list_templates — list available approved templates\n */\n\nimport { Type, type TSchema } from \"@sinclair/typebox\";\nimport { resolveConfig } from \"@alfe.ai/config\";\nimport { AgentApiClient, installToolErrorCapture } from \"@alfe.ai/agent-api-client\";\nimport {\n defineTool,\n getActivationKey,\n guardedStart,\n resetActivation,\n type ToolDef,\n} from \"@alfe.ai/openclaw-plugin-kit\";\nimport { createRequire } from 'node:module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\ninterface Logger {\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n debug(msg: string, ...args: unknown[]): void;\n}\n\ninterface OpenClawPluginApi {\n logger: Logger;\n registrationMode?: \"full\" | \"setup-only\" | \"setup-runtime\" | \"cli-metadata\";\n registerTool(tool: ToolDef<TSchema>): void;\n registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: { priority?: number }): void;\n}\n\n// ── Activation guard ─────────────────────────────────────────\n\nconst WHATSAPP_ACTIVATION_KEY = getActivationKey(\"whatsapp\");\n\n// ── API client ───────────────────────────────────────────────\n\nlet client: AgentApiClient | undefined;\n\nfunction getClient(): AgentApiClient {\n if (!client) {\n throw new Error(\"WhatsApp tools unavailable — no API config resolved\");\n }\n return client;\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst whatsappTools: ToolDef<TSchema>[] = [\n defineTool({\n name: \"whatsapp_send_message\",\n description:\n \"Send a free-form WhatsApp message from your phone number. \" +\n \"This only works if the recipient has messaged you within the last 24 hours (active session). \" +\n \"If there is no active session, you must use whatsapp_send_template to initiate the conversation first.\",\n parameters: Type.Object({\n to: Type.String({ description: \"Recipient phone number in E.164 format (e.g., +12025551234)\" }),\n body: Type.String({ description: \"The message content to send\" }),\n }),\n handler: async (params) => {\n const { to, body } = params as { to: string; body: string };\n\n // Pre-check session to give a clear error before hitting the API\n try {\n const session = await getClient().getWhatsAppSession(to);\n if (!session.active) {\n throw new Error(\n \"No active WhatsApp session with this user. Use whatsapp_send_template to initiate the conversation first, then the user can reply and you can send free-form messages.\",\n );\n }\n } catch (e) {\n const msg = (e as Error).message;\n if (msg.includes(\"No active WhatsApp session\") || msg.includes(\"whatsapp_send_template\")) {\n throw e;\n }\n // If session check fails for other reasons, try sending anyway — the API will enforce\n }\n\n return getClient().sendWhatsAppMessage({ to, body });\n },\n }),\n\n defineTool({\n name: \"whatsapp_send_template\",\n description:\n \"Send a pre-approved WhatsApp template message to initiate a conversation. \" +\n \"Use this when you need to message a user who hasn't contacted you recently (no active 24-hour session). \" +\n \"First call whatsapp_list_templates to see available templates and their variables, \" +\n \"then call this tool with the chosen template. \" +\n \"After the user replies to your template, you can use whatsapp_send_message for free-form messages.\",\n parameters: Type.Object({\n to: Type.String({ description: \"Recipient phone number in E.164 format (e.g., +12025551234)\" }),\n contentSid: Type.String({ description: \"The template content SID (from whatsapp_list_templates)\" }),\n contentVariables: Type.Object({}, { description: \"Variables to fill in the template, e.g. {\\\"1\\\": \\\"Kevin\\\", \\\"2\\\": \\\"Agent Name\\\"}\", additionalProperties: Type.String() }),\n bodyPreview: Type.Optional(Type.String({ description: \"The filled template text for conversation context\" })),\n }),\n handler: async (params) => {\n const { to, contentSid, contentVariables, bodyPreview } = params as {\n to: string;\n contentSid: string;\n contentVariables: Record<string, string>;\n bodyPreview?: string;\n };\n return getClient().sendWhatsAppTemplate({\n to,\n contentSid,\n contentVariables,\n bodyPreview,\n });\n },\n }),\n\n defineTool({\n name: \"whatsapp_list_templates\",\n description:\n \"List all approved WhatsApp message templates available for outreach. \" +\n \"Each template has a contentSid, name, body with {{1}}, {{2}} placeholders, \" +\n \"and variables showing what each placeholder represents. \" +\n \"Use this to find the right template before calling whatsapp_send_template.\",\n parameters: Type.Object({}),\n handler: async () => {\n return getClient().listWhatsAppTemplates();\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-whatsapp\",\n name: \"Alfe WhatsApp Plugin\",\n description: \"WhatsApp integration — messaging and templates via Twilio\",\n version: pkg.version,\n\n activate(api: OpenClawPluginApi) {\n // First thing, before any registerTool call: tool failures emit a\n // deterministic [ERROR] line the gateway's runtime-output monitor captures\n // to Sentry. See @alfe.ai/agent-api-client tool-error-capture.\n installToolErrorCapture(api, { plugin: \"openclaw-whatsapp\" });\n const log = api.logger;\n\n\n // Always register tools — OpenClaw may reload the plugin registry per-session\n for (const tool of whatsappTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${String(whatsappTools.length)} WhatsApp tools: ${whatsappTools.map((t) => t.name).join(\", \")}`);\n\n // Only initialize config once (side effects)\n guardedStart(WHATSAPP_ACTIVATION_KEY, log, () => {\n log.info(\"Alfe WhatsApp plugin activating...\");\n\n try {\n const config = resolveConfig();\n client = new AgentApiClient({ apiUrl: config.apiUrl, apiKey: config.apiKey });\n log.info(`WhatsApp API: ${config.apiUrl}`);\n } catch (err) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);\n log.warn(\"WhatsApp tools will fail — no API config available\");\n // Reset so a later activate() can retry once config exists.\n resetActivation(WHATSAPP_ACTIVATION_KEY);\n }\n });\n\n log.info(\"Alfe WhatsApp plugin activated\");\n },\n\n deactivate(api: OpenClawPluginApi) {\n // Stop side effects BEFORE resetting the activation flag.\n client = undefined;\n resetActivation(WHATSAPP_ACTIVATION_KEY);\n api.logger.info(\"Alfe WhatsApp plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,MAAM,MADU,cAAc,OAAO,KAAK,IAAI,CAC1B,kBAAkB;AAmBtC,MAAM,0BAA0B,iBAAiB,WAAW;AAI5D,IAAI;AAEJ,SAAS,YAA4B;AACnC,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,sDAAsD;AAExE,QAAO;;AAKT,MAAM,gBAAoC;CACxC,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO;GACtB,IAAI,KAAK,OAAO,EAAE,aAAa,+DAA+D,CAAC;GAC/F,MAAM,KAAK,OAAO,EAAE,aAAa,+BAA+B,CAAC;GAClE,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,IAAI,SAAS;AAGrB,OAAI;AAEF,QAAI,EADY,MAAM,WAAW,CAAC,mBAAmB,GAAG,EAC3C,OACX,OAAM,IAAI,MACR,yKACD;YAEI,GAAG;IACV,MAAM,MAAO,EAAY;AACzB,QAAI,IAAI,SAAS,6BAA6B,IAAI,IAAI,SAAS,yBAAyB,CACtF,OAAM;;AAKV,UAAO,WAAW,CAAC,oBAAoB;IAAE;IAAI;IAAM,CAAC;;EAEvD,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAKF,YAAY,KAAK,OAAO;GACtB,IAAI,KAAK,OAAO,EAAE,aAAa,+DAA+D,CAAC;GAC/F,YAAY,KAAK,OAAO,EAAE,aAAa,2DAA2D,CAAC;GACnG,kBAAkB,KAAK,OAAO,EAAE,EAAE;IAAE,aAAa;IAAqF,sBAAsB,KAAK,QAAQ;IAAE,CAAC;GAC5K,aAAa,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,qDAAqD,CAAC,CAAC;GAC9G,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,IAAI,YAAY,kBAAkB,gBAAgB;AAM1D,UAAO,WAAW,CAAC,qBAAqB;IACtC;IACA;IACA;IACA;IACD,CAAC;;EAEL,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAIF,YAAY,KAAK,OAAO,EAAE,CAAC;EAC3B,SAAS,YAAY;AACnB,UAAO,WAAW,CAAC,uBAAuB;;EAE7C,CAAC;CACH;AAID,MAAM,SAAS;CACb,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS,IAAI;CAEb,SAAS,KAAwB;AAI/B,0BAAwB,KAAK,EAAE,QAAQ,qBAAqB,CAAC;EAC7D,MAAM,MAAM,IAAI;AAIhB,OAAK,MAAM,QAAQ,cACjB,KAAI,aAAa,KAAK;AAExB,MAAI,KAAK,cAAc,OAAO,cAAc,OAAO,CAAC,mBAAmB,cAAc,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,GAAG;AAGrH,eAAa,yBAAyB,WAAW;AAC/C,OAAI,KAAK,qCAAqC;AAE9C,OAAI;IACF,MAAM,SAAS,eAAe;AAC9B,aAAS,IAAI,eAAe;KAAE,QAAQ,OAAO;KAAQ,QAAQ,OAAO;KAAQ,CAAC;AAC7E,QAAI,KAAK,iBAAiB,OAAO,SAAS;YACnC,KAAK;AACZ,QAAI,MAAM,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC1F,QAAI,KAAK,qDAAqD;AAE9D,oBAAgB,wBAAwB;;IAE1C;AAEF,MAAI,KAAK,iCAAiC;;CAG5C,WAAW,KAAwB;AAEjC,WAAS,KAAA;AACT,kBAAgB,wBAAwB;AACxC,MAAI,OAAO,KAAK,mCAAmC;;CAEtD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-whatsapp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "OpenClaw WhatsApp plugin for Alfe — outbound WhatsApp messaging via Twilio",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@sinclair/typebox": "^0.34.48",
|
|
31
|
-
"@alfe.ai/agent-api-client": "0.
|
|
32
|
-
"@alfe.ai/config": "0.
|
|
31
|
+
"@alfe.ai/agent-api-client": "0.14.0",
|
|
32
|
+
"@alfe.ai/config": "0.4.0",
|
|
33
|
+
"@alfe.ai/openclaw-plugin-kit": "0.1.0"
|
|
33
34
|
},
|
|
34
35
|
"homepage": "https://alfe.ai",
|
|
35
36
|
"author": "Alfe (https://alfe.ai)",
|