@alfe.ai/openclaw-mobile 0.0.24 → 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 +28 -83
- package/dist/plugin.d.cts +42 -15
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +42 -15
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +23 -78
- 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-mobile — OpenClaw native plugin
|
|
@@ -17,81 +18,22 @@ let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
|
17
18
|
* - mobile_call — initiate an outbound phone call
|
|
18
19
|
*/
|
|
19
20
|
const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}],
|
|
26
|
-
details: data
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function errResult(message) {
|
|
30
|
-
return {
|
|
31
|
-
content: [{
|
|
32
|
-
type: "text",
|
|
33
|
-
text: JSON.stringify({ error: message })
|
|
34
|
-
}],
|
|
35
|
-
details: { error: message }
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function defineTool(def) {
|
|
39
|
-
return {
|
|
40
|
-
name: def.name,
|
|
41
|
-
description: def.description,
|
|
42
|
-
label: def.name,
|
|
43
|
-
parameters: def.parameters,
|
|
44
|
-
execute: async (_toolCallId, params) => {
|
|
45
|
-
try {
|
|
46
|
-
return ok(await def.handler(params));
|
|
47
|
-
} catch (e) {
|
|
48
|
-
return errResult(e.message);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
let apiUrl = "";
|
|
54
|
-
let apiKey = "";
|
|
55
|
-
async function mobileApi(method, path, body, query) {
|
|
56
|
-
let url = `${apiUrl}${path}`;
|
|
57
|
-
if (query) {
|
|
58
|
-
const entries = Object.entries(query).filter((entry) => entry[1] !== void 0);
|
|
59
|
-
if (entries.length > 0) url += `?${new URLSearchParams(entries).toString()}`;
|
|
60
|
-
}
|
|
61
|
-
const headers = {
|
|
62
|
-
"Content-Type": "application/json",
|
|
63
|
-
Authorization: `Bearer ${apiKey}`
|
|
64
|
-
};
|
|
65
|
-
const res = await fetch(url, {
|
|
66
|
-
method,
|
|
67
|
-
headers,
|
|
68
|
-
body: body ? JSON.stringify(body) : void 0
|
|
69
|
-
});
|
|
70
|
-
const json = await res.json();
|
|
71
|
-
if (!res.ok) {
|
|
72
|
-
const issues = json.issues;
|
|
73
|
-
if (Array.isArray(issues) && issues.length > 0) {
|
|
74
|
-
const details = issues.map((i) => {
|
|
75
|
-
return `${i.path?.join(".") ?? "input"}: ${i.message ?? "invalid"}`;
|
|
76
|
-
}).join("; ");
|
|
77
|
-
throw new Error(`Validation error — ${details}`);
|
|
78
|
-
}
|
|
79
|
-
const errorMsg = typeof json.error === "string" ? json.error : typeof json.message === "string" ? json.message : `Mobile service returned ${String(res.status)}`;
|
|
80
|
-
throw new Error(errorMsg);
|
|
81
|
-
}
|
|
82
|
-
if (json.data && typeof json.data === "object") return json.data;
|
|
83
|
-
return json;
|
|
21
|
+
const MOBILE_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("mobile");
|
|
22
|
+
let client;
|
|
23
|
+
function getClient() {
|
|
24
|
+
if (!client) throw new Error("Mobile tools unavailable — no API config resolved");
|
|
25
|
+
return client;
|
|
84
26
|
}
|
|
85
27
|
const mobileTools = [
|
|
86
|
-
defineTool({
|
|
28
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
87
29
|
name: "mobile_get_number",
|
|
88
30
|
description: "Check if you have a phone number assigned. Returns the phone number, country code, monthly price, and status. If no number is assigned, use mobile_search_numbers and mobile_assign_number to get one.",
|
|
89
31
|
parameters: _sinclair_typebox.Type.Object({}),
|
|
90
32
|
handler: async () => {
|
|
91
|
-
return
|
|
33
|
+
return getClient().getMobileNumber();
|
|
92
34
|
}
|
|
93
35
|
}),
|
|
94
|
-
defineTool({
|
|
36
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
95
37
|
name: "mobile_search_numbers",
|
|
96
38
|
description: "Search for available phone numbers that can be purchased. Returns a list of available numbers and the monthly price. Supported countries: AU (Australia), US (United States), CA (Canada), GB (United Kingdom).",
|
|
97
39
|
parameters: _sinclair_typebox.Type.Object({
|
|
@@ -100,13 +42,13 @@ const mobileTools = [
|
|
|
100
42
|
}),
|
|
101
43
|
handler: async (params) => {
|
|
102
44
|
const { country, query } = params;
|
|
103
|
-
return
|
|
45
|
+
return getClient().searchMobileNumbers({
|
|
104
46
|
country,
|
|
105
47
|
query
|
|
106
48
|
});
|
|
107
49
|
}
|
|
108
50
|
}),
|
|
109
|
-
defineTool({
|
|
51
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
110
52
|
name: "mobile_assign_number",
|
|
111
53
|
description: "Purchase and assign a phone number to yourself. You must search for available numbers first using mobile_search_numbers, then pass the chosen phoneNumber and countryCode here. Your organisation must have a payment method on file. The number will be billed monthly to the organisation's account.",
|
|
112
54
|
parameters: _sinclair_typebox.Type.Object({
|
|
@@ -115,21 +57,21 @@ const mobileTools = [
|
|
|
115
57
|
}),
|
|
116
58
|
handler: async (params) => {
|
|
117
59
|
const { phoneNumber, countryCode } = params;
|
|
118
|
-
return
|
|
60
|
+
return getClient().assignMobileNumber({
|
|
119
61
|
phoneNumber,
|
|
120
62
|
countryCode
|
|
121
63
|
});
|
|
122
64
|
}
|
|
123
65
|
}),
|
|
124
|
-
defineTool({
|
|
66
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
125
67
|
name: "mobile_release_number",
|
|
126
68
|
description: "Release your currently assigned phone number. This will cancel the monthly subscription and the number will no longer be available for calls or SMS. This action cannot be undone — the same number may not be available again.",
|
|
127
69
|
parameters: _sinclair_typebox.Type.Object({}),
|
|
128
70
|
handler: async () => {
|
|
129
|
-
return
|
|
71
|
+
return getClient().releaseMobileNumber();
|
|
130
72
|
}
|
|
131
73
|
}),
|
|
132
|
-
defineTool({
|
|
74
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
133
75
|
name: "mobile_send_sms",
|
|
134
76
|
description: "Send an SMS text message from your phone number to the specified number. The recipient number must be in E.164 format (e.g., +12025551234).",
|
|
135
77
|
parameters: _sinclair_typebox.Type.Object({
|
|
@@ -138,19 +80,19 @@ const mobileTools = [
|
|
|
138
80
|
}),
|
|
139
81
|
handler: async (params) => {
|
|
140
82
|
const { to, body } = params;
|
|
141
|
-
return
|
|
83
|
+
return getClient().sendSms({
|
|
142
84
|
to,
|
|
143
85
|
body
|
|
144
86
|
});
|
|
145
87
|
}
|
|
146
88
|
}),
|
|
147
|
-
defineTool({
|
|
89
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
148
90
|
name: "mobile_call",
|
|
149
91
|
description: "Make an outbound phone call to the specified number. When the recipient answers, they will be connected to your voice pipeline. The number must be in E.164 format (e.g., +12025551234).",
|
|
150
92
|
parameters: _sinclair_typebox.Type.Object({ to: _sinclair_typebox.Type.String({ description: "Phone number to call in E.164 format (e.g., +12025551234)" }) }),
|
|
151
93
|
handler: async (params) => {
|
|
152
94
|
const { to } = params;
|
|
153
|
-
return
|
|
95
|
+
return getClient().startOutboundCall({ to });
|
|
154
96
|
}
|
|
155
97
|
})
|
|
156
98
|
];
|
|
@@ -164,23 +106,26 @@ const plugin = {
|
|
|
164
106
|
const log = api.logger;
|
|
165
107
|
for (const tool of mobileTools) api.registerTool(tool);
|
|
166
108
|
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(", ")}`);
|
|
167
|
-
|
|
168
|
-
globalThis.__mobilePluginActivated = true;
|
|
109
|
+
(0, _alfe_ai_openclaw_plugin_kit.guardedStart)(MOBILE_ACTIVATION_KEY, log, () => {
|
|
169
110
|
log.info("Alfe Mobile plugin activating...");
|
|
170
111
|
try {
|
|
171
112
|
const config = (0, _alfe_ai_config.resolveConfig)();
|
|
172
|
-
|
|
173
|
-
|
|
113
|
+
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
114
|
+
apiUrl: config.apiUrl,
|
|
115
|
+
apiKey: config.apiKey
|
|
116
|
+
});
|
|
117
|
+
log.info(`Mobile API: ${config.apiUrl}`);
|
|
174
118
|
} catch (err) {
|
|
175
119
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
176
120
|
log.warn("Mobile tools will fail — no API config available");
|
|
121
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(MOBILE_ACTIVATION_KEY);
|
|
177
122
|
}
|
|
178
|
-
|
|
179
|
-
}
|
|
123
|
+
});
|
|
180
124
|
log.info("Alfe Mobile plugin activated");
|
|
181
125
|
},
|
|
182
126
|
deactivate(api) {
|
|
183
|
-
|
|
127
|
+
client = void 0;
|
|
128
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(MOBILE_ACTIVATION_KEY);
|
|
184
129
|
api.logger.info("Alfe Mobile plugin deactivated");
|
|
185
130
|
}
|
|
186
131
|
};
|
package/dist/plugin.d.cts
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { TSchema } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
+
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
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
|
|
3
43
|
//#region src/plugin.d.ts
|
|
4
44
|
|
|
5
45
|
interface Logger {
|
|
@@ -11,25 +51,12 @@ interface Logger {
|
|
|
11
51
|
interface OpenClawPluginApi {
|
|
12
52
|
logger: Logger;
|
|
13
53
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
54
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
55
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
56
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
57
|
priority?: number;
|
|
18
58
|
}): void;
|
|
19
59
|
}
|
|
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
60
|
declare const plugin: {
|
|
34
61
|
id: string;
|
|
35
62
|
name: string;
|
|
@@ -39,5 +66,5 @@ declare const plugin: {
|
|
|
39
66
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
67
|
};
|
|
41
68
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
69
|
+
export { plugin as t };
|
|
43
70
|
//# 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":";;;;AC6BgB;;;;;;;;AAYmD;;;;;;UDZzDC,UAAAA;;;;;;;;;;;;;;;UAeAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCpBlE,MAAA,CAOiB;MACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;MAEmB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAAR,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACkD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAJ7D,iBAAA,CAKyD;EAiH7D,MAAA,EArHI,MAkKT;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAvCe,CAAA,IAAA,EAzHK,OAyHL,CAzHa,OAyHb,CAAA,CAAA,EAAA,IAAA;uBAiCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAzJqD,OAyJrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAxJyB,OAwJzB,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAvC7B;;;;;gBAMU;kBAiCE"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { TSchema } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
+
//#region ../openclaw-plugin-kit/dist/index.d.ts
|
|
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
|
|
3
43
|
//#region src/plugin.d.ts
|
|
4
44
|
|
|
5
45
|
interface Logger {
|
|
@@ -11,25 +51,12 @@ interface Logger {
|
|
|
11
51
|
interface OpenClawPluginApi {
|
|
12
52
|
logger: Logger;
|
|
13
53
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
54
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
55
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
56
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
57
|
priority?: number;
|
|
18
58
|
}): void;
|
|
19
59
|
}
|
|
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
60
|
declare const plugin: {
|
|
34
61
|
id: string;
|
|
35
62
|
name: string;
|
|
@@ -39,5 +66,5 @@ declare const plugin: {
|
|
|
39
66
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
67
|
};
|
|
41
68
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
69
|
+
export { plugin as t };
|
|
43
70
|
//# 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":";;;;AC6BgB;;;;;;;;AAYmD;;;;;;UDZzDC,UAAAA;;;;;;;;;;;;;;;UAeAC;;;;cAIIC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCpBlE,MAAA,CAOiB;MACjB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;MAEmB,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OAAR,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;OACkD,CAAA,GAAA,EAAA,MAAA,EAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;;UAJ7D,iBAAA,CAKyD;EAiH7D,MAAA,EArHI,MAkKT;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAvCe,CAAA,IAAA,EAzHK,OAyHL,CAzHa,OAyHb,CAAA,CAAA,EAAA,IAAA;uBAiCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAzJqD,OAyJrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAxJyB,OAwJzB,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAvC7B;;;;;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-mobile — OpenClaw native plugin
|
|
@@ -18,70 +19,11 @@ import { installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
|
18
19
|
* - mobile_call — initiate an outbound phone call
|
|
19
20
|
*/
|
|
20
21
|
const pkg = createRequire(import.meta.url)("../package.json");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}],
|
|
27
|
-
details: data
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function errResult(message) {
|
|
31
|
-
return {
|
|
32
|
-
content: [{
|
|
33
|
-
type: "text",
|
|
34
|
-
text: JSON.stringify({ error: message })
|
|
35
|
-
}],
|
|
36
|
-
details: { error: message }
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function defineTool(def) {
|
|
40
|
-
return {
|
|
41
|
-
name: def.name,
|
|
42
|
-
description: def.description,
|
|
43
|
-
label: def.name,
|
|
44
|
-
parameters: def.parameters,
|
|
45
|
-
execute: async (_toolCallId, params) => {
|
|
46
|
-
try {
|
|
47
|
-
return ok(await def.handler(params));
|
|
48
|
-
} catch (e) {
|
|
49
|
-
return errResult(e.message);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
let apiUrl = "";
|
|
55
|
-
let apiKey = "";
|
|
56
|
-
async function mobileApi(method, path, body, query) {
|
|
57
|
-
let url = `${apiUrl}${path}`;
|
|
58
|
-
if (query) {
|
|
59
|
-
const entries = Object.entries(query).filter((entry) => entry[1] !== void 0);
|
|
60
|
-
if (entries.length > 0) url += `?${new URLSearchParams(entries).toString()}`;
|
|
61
|
-
}
|
|
62
|
-
const headers = {
|
|
63
|
-
"Content-Type": "application/json",
|
|
64
|
-
Authorization: `Bearer ${apiKey}`
|
|
65
|
-
};
|
|
66
|
-
const res = await fetch(url, {
|
|
67
|
-
method,
|
|
68
|
-
headers,
|
|
69
|
-
body: body ? JSON.stringify(body) : void 0
|
|
70
|
-
});
|
|
71
|
-
const json = await res.json();
|
|
72
|
-
if (!res.ok) {
|
|
73
|
-
const issues = json.issues;
|
|
74
|
-
if (Array.isArray(issues) && issues.length > 0) {
|
|
75
|
-
const details = issues.map((i) => {
|
|
76
|
-
return `${i.path?.join(".") ?? "input"}: ${i.message ?? "invalid"}`;
|
|
77
|
-
}).join("; ");
|
|
78
|
-
throw new Error(`Validation error — ${details}`);
|
|
79
|
-
}
|
|
80
|
-
const errorMsg = typeof json.error === "string" ? json.error : typeof json.message === "string" ? json.message : `Mobile service returned ${String(res.status)}`;
|
|
81
|
-
throw new Error(errorMsg);
|
|
82
|
-
}
|
|
83
|
-
if (json.data && typeof json.data === "object") return json.data;
|
|
84
|
-
return json;
|
|
22
|
+
const MOBILE_ACTIVATION_KEY = getActivationKey("mobile");
|
|
23
|
+
let client;
|
|
24
|
+
function getClient() {
|
|
25
|
+
if (!client) throw new Error("Mobile tools unavailable — no API config resolved");
|
|
26
|
+
return client;
|
|
85
27
|
}
|
|
86
28
|
const mobileTools = [
|
|
87
29
|
defineTool({
|
|
@@ -89,7 +31,7 @@ const mobileTools = [
|
|
|
89
31
|
description: "Check if you have a phone number assigned. Returns the phone number, country code, monthly price, and status. If no number is assigned, use mobile_search_numbers and mobile_assign_number to get one.",
|
|
90
32
|
parameters: Type.Object({}),
|
|
91
33
|
handler: async () => {
|
|
92
|
-
return
|
|
34
|
+
return getClient().getMobileNumber();
|
|
93
35
|
}
|
|
94
36
|
}),
|
|
95
37
|
defineTool({
|
|
@@ -101,7 +43,7 @@ const mobileTools = [
|
|
|
101
43
|
}),
|
|
102
44
|
handler: async (params) => {
|
|
103
45
|
const { country, query } = params;
|
|
104
|
-
return
|
|
46
|
+
return getClient().searchMobileNumbers({
|
|
105
47
|
country,
|
|
106
48
|
query
|
|
107
49
|
});
|
|
@@ -116,7 +58,7 @@ const mobileTools = [
|
|
|
116
58
|
}),
|
|
117
59
|
handler: async (params) => {
|
|
118
60
|
const { phoneNumber, countryCode } = params;
|
|
119
|
-
return
|
|
61
|
+
return getClient().assignMobileNumber({
|
|
120
62
|
phoneNumber,
|
|
121
63
|
countryCode
|
|
122
64
|
});
|
|
@@ -127,7 +69,7 @@ const mobileTools = [
|
|
|
127
69
|
description: "Release your currently assigned phone number. This will cancel the monthly subscription and the number will no longer be available for calls or SMS. This action cannot be undone — the same number may not be available again.",
|
|
128
70
|
parameters: Type.Object({}),
|
|
129
71
|
handler: async () => {
|
|
130
|
-
return
|
|
72
|
+
return getClient().releaseMobileNumber();
|
|
131
73
|
}
|
|
132
74
|
}),
|
|
133
75
|
defineTool({
|
|
@@ -139,7 +81,7 @@ const mobileTools = [
|
|
|
139
81
|
}),
|
|
140
82
|
handler: async (params) => {
|
|
141
83
|
const { to, body } = params;
|
|
142
|
-
return
|
|
84
|
+
return getClient().sendSms({
|
|
143
85
|
to,
|
|
144
86
|
body
|
|
145
87
|
});
|
|
@@ -151,7 +93,7 @@ const mobileTools = [
|
|
|
151
93
|
parameters: Type.Object({ to: Type.String({ description: "Phone number to call in E.164 format (e.g., +12025551234)" }) }),
|
|
152
94
|
handler: async (params) => {
|
|
153
95
|
const { to } = params;
|
|
154
|
-
return
|
|
96
|
+
return getClient().startOutboundCall({ to });
|
|
155
97
|
}
|
|
156
98
|
})
|
|
157
99
|
];
|
|
@@ -165,23 +107,26 @@ const plugin = {
|
|
|
165
107
|
const log = api.logger;
|
|
166
108
|
for (const tool of mobileTools) api.registerTool(tool);
|
|
167
109
|
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(", ")}`);
|
|
168
|
-
|
|
169
|
-
globalThis.__mobilePluginActivated = true;
|
|
110
|
+
guardedStart(MOBILE_ACTIVATION_KEY, log, () => {
|
|
170
111
|
log.info("Alfe Mobile plugin activating...");
|
|
171
112
|
try {
|
|
172
113
|
const config = resolveConfig();
|
|
173
|
-
|
|
174
|
-
|
|
114
|
+
client = new AgentApiClient({
|
|
115
|
+
apiUrl: config.apiUrl,
|
|
116
|
+
apiKey: config.apiKey
|
|
117
|
+
});
|
|
118
|
+
log.info(`Mobile API: ${config.apiUrl}`);
|
|
175
119
|
} catch (err) {
|
|
176
120
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
177
121
|
log.warn("Mobile tools will fail — no API config available");
|
|
122
|
+
resetActivation(MOBILE_ACTIVATION_KEY);
|
|
178
123
|
}
|
|
179
|
-
|
|
180
|
-
}
|
|
124
|
+
});
|
|
181
125
|
log.info("Alfe Mobile plugin activated");
|
|
182
126
|
},
|
|
183
127
|
deactivate(api) {
|
|
184
|
-
|
|
128
|
+
client = void 0;
|
|
129
|
+
resetActivation(MOBILE_ACTIVATION_KEY);
|
|
185
130
|
api.logger.info("Alfe Mobile plugin deactivated");
|
|
186
131
|
}
|
|
187
132
|
};
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * @alfe/openclaw-mobile — OpenClaw native plugin\n *\n * Registers mobile tools (SMS, calls, number management) with OpenClaw.\n * Tools call the mobile service API using the agent's API key for authentication.\n *\n * This plugin provides:\n * - mobile_get_number — check if a phone number is assigned\n * - mobile_search_numbers — search available numbers for purchase\n * - mobile_assign_number — purchase and assign a phone number\n * - mobile_release_number — release the assigned phone number\n * - mobile_send_sms — send an SMS message from the agent's phone number\n * - mobile_call — initiate an outbound phone call\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 mobileApi(\n method: string,\n path: string,\n body?: Record<string, unknown>,\n query?: Record<string, string | undefined>,\n): Promise<Record<string, unknown>> {\n let url = `${apiUrl}${path}`;\n if (query) {\n const entries = Object.entries(query).filter((entry): entry is [string, string] => entry[1] !== undefined);\n if (entries.length > 0) {\n url += `?${new URLSearchParams(entries).toString()}`;\n }\n }\n\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 // Extract Zod validation issues if present (returned as { message: \"CORE_VALIDATION_FAILED\", issues: [...] })\n const issues = json.issues as { path?: string[]; message?: string }[] | undefined;\n if (Array.isArray(issues) && issues.length > 0) {\n const details = issues\n .map((i) => {\n const field = i.path?.join(\".\") ?? \"input\";\n return `${field}: ${i.message ?? \"invalid\"}`;\n })\n .join(\"; \");\n throw new Error(`Validation error — ${details}`);\n }\n\n const errorMsg = typeof json.error === \"string\"\n ? json.error\n : typeof json.message === \"string\"\n ? json.message\n : `Mobile 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 mobileTools: ToolDef[] = [\n // ── Number management ───────────────────────────────────────\n defineTool({\n name: \"mobile_get_number\",\n description:\n \"Check if you have a phone number assigned. Returns the phone number, \" +\n \"country code, monthly price, and status. If no number is assigned, \" +\n \"use mobile_search_numbers and mobile_assign_number to get one.\",\n parameters: Type.Object({}),\n handler: async () => {\n return mobileApi(\"GET\", \"/mobile/numbers\");\n },\n }),\n\n defineTool({\n name: \"mobile_search_numbers\",\n description:\n \"Search for available phone numbers that can be purchased. \" +\n \"Returns a list of available numbers and the monthly price. \" +\n \"Supported countries: AU (Australia), US (United States), CA (Canada), GB (United Kingdom).\",\n parameters: Type.Object({\n country: Type.Optional(Type.String({ description: \"Country code: AU, US, CA, or GB (default: AU)\" })),\n query: Type.Optional(Type.String({ description: \"Optional search pattern — area code or text to match (e.g., '415' or 'COOL')\" })),\n }),\n handler: async (params) => {\n const { country, query } = params as { country?: string; query?: string };\n return mobileApi(\"GET\", \"/mobile/numbers/search\", undefined, { country, query });\n },\n }),\n\n defineTool({\n name: \"mobile_assign_number\",\n description:\n \"Purchase and assign a phone number to yourself. You must search for available \" +\n \"numbers first using mobile_search_numbers, then pass the chosen phoneNumber and \" +\n \"countryCode here. Your organisation must have a payment method on file. \" +\n \"The number will be billed monthly to the organisation's account.\",\n parameters: Type.Object({\n phoneNumber: Type.String({ description: \"The phone number to purchase (from mobile_search_numbers results)\" }),\n countryCode: Type.String({ description: \"Country code: AU, US, CA, or GB\" }),\n }),\n handler: async (params) => {\n const { phoneNumber, countryCode } = params as { phoneNumber: string; countryCode: string };\n return mobileApi(\"POST\", \"/mobile/numbers/assign\", { phoneNumber, countryCode });\n },\n }),\n\n defineTool({\n name: \"mobile_release_number\",\n description:\n \"Release your currently assigned phone number. This will cancel the monthly \" +\n \"subscription and the number will no longer be available for calls or SMS. \" +\n \"This action cannot be undone — the same number may not be available again.\",\n parameters: Type.Object({}),\n handler: async () => {\n return mobileApi(\"POST\", \"/mobile/numbers/release\", {});\n },\n }),\n\n // ── Messaging & calls ───────────────────────────────────────\n defineTool({\n name: \"mobile_send_sms\",\n description:\n \"Send an SMS text message from your phone number to the specified number. \" +\n \"The recipient number must be in E.164 format (e.g., +12025551234).\",\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 text message content to send\" }),\n }),\n handler: async (params) => {\n const { to, body } = params as { to: string; body: string };\n return mobileApi(\"POST\", \"/mobile/sms/send\", { to, body });\n },\n }),\n\n defineTool({\n name: \"mobile_call\",\n description:\n \"Make an outbound phone call to the specified number. \" +\n \"When the recipient answers, they will be connected to your voice pipeline. \" +\n \"The number must be in E.164 format (e.g., +12025551234).\",\n parameters: Type.Object({\n to: Type.String({ description: \"Phone number to call in E.164 format (e.g., +12025551234)\" }),\n }),\n handler: async (params) => {\n const { to } = params as { to: string };\n return mobileApi(\"POST\", \"/mobile/calls/outbound\", { to });\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-mobile\",\n name: \"Alfe Mobile Plugin\",\n description: \"Mobile integration — phone number management, SMS, and phone calls 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-mobile\" });\n const log = api.logger;\n\n\n // Always register tools — OpenClaw may reload the plugin registry per-session\n for (const tool of mobileTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(\", \")}`);\n\n // Only initialize config once (side effects)\n if (!(globalThis as Record<string, unknown>).__mobilePluginActivated) {\n (globalThis as Record<string, unknown>).__mobilePluginActivated = true;\n log.info(\"Alfe Mobile 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(\"Mobile tools will fail — no API config available\");\n }\n\n log.info(`Mobile API: ${apiUrl}`);\n }\n\n log.info(\"Alfe Mobile plugin activated\");\n },\n\n deactivate(api: OpenClawPluginApi) {\n (globalThis as Record<string, unknown>).__mobilePluginActivated = false;\n api.logger.info(\"Alfe Mobile 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-mobile — OpenClaw native plugin\n *\n * Registers mobile tools (SMS, calls, number management) with OpenClaw.\n * Tools call the mobile service API using the agent's API key for authentication.\n *\n * This plugin provides:\n * - mobile_get_number — check if a phone number is assigned\n * - mobile_search_numbers — search available numbers for purchase\n * - mobile_assign_number — purchase and assign a phone number\n * - mobile_release_number — release the assigned phone number\n * - mobile_send_sms — send an SMS message from the agent's phone number\n * - mobile_call — initiate an outbound phone call\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 MOBILE_ACTIVATION_KEY = getActivationKey(\"mobile\");\n\n// ── API client ───────────────────────────────────────────────\n\nlet client: AgentApiClient | undefined;\n\nfunction getClient(): AgentApiClient {\n if (!client) {\n throw new Error(\"Mobile tools unavailable — no API config resolved\");\n }\n return client;\n}\n\n// ── Tool definitions ─────────────────────────────────────────\n\nconst mobileTools: ToolDef<TSchema>[] = [\n // ── Number management ───────────────────────────────────────\n defineTool({\n name: \"mobile_get_number\",\n description:\n \"Check if you have a phone number assigned. Returns the phone number, \" +\n \"country code, monthly price, and status. If no number is assigned, \" +\n \"use mobile_search_numbers and mobile_assign_number to get one.\",\n parameters: Type.Object({}),\n handler: async () => {\n return getClient().getMobileNumber();\n },\n }),\n\n defineTool({\n name: \"mobile_search_numbers\",\n description:\n \"Search for available phone numbers that can be purchased. \" +\n \"Returns a list of available numbers and the monthly price. \" +\n \"Supported countries: AU (Australia), US (United States), CA (Canada), GB (United Kingdom).\",\n parameters: Type.Object({\n country: Type.Optional(Type.String({ description: \"Country code: AU, US, CA, or GB (default: AU)\" })),\n query: Type.Optional(Type.String({ description: \"Optional search pattern — area code or text to match (e.g., '415' or 'COOL')\" })),\n }),\n handler: async (params) => {\n const { country, query } = params as { country?: string; query?: string };\n return getClient().searchMobileNumbers({ country, query });\n },\n }),\n\n defineTool({\n name: \"mobile_assign_number\",\n description:\n \"Purchase and assign a phone number to yourself. You must search for available \" +\n \"numbers first using mobile_search_numbers, then pass the chosen phoneNumber and \" +\n \"countryCode here. Your organisation must have a payment method on file. \" +\n \"The number will be billed monthly to the organisation's account.\",\n parameters: Type.Object({\n phoneNumber: Type.String({ description: \"The phone number to purchase (from mobile_search_numbers results)\" }),\n countryCode: Type.String({ description: \"Country code: AU, US, CA, or GB\" }),\n }),\n handler: async (params) => {\n const { phoneNumber, countryCode } = params as { phoneNumber: string; countryCode: string };\n return getClient().assignMobileNumber({ phoneNumber, countryCode });\n },\n }),\n\n defineTool({\n name: \"mobile_release_number\",\n description:\n \"Release your currently assigned phone number. This will cancel the monthly \" +\n \"subscription and the number will no longer be available for calls or SMS. \" +\n \"This action cannot be undone — the same number may not be available again.\",\n parameters: Type.Object({}),\n handler: async () => {\n return getClient().releaseMobileNumber();\n },\n }),\n\n // ── Messaging & calls ───────────────────────────────────────\n defineTool({\n name: \"mobile_send_sms\",\n description:\n \"Send an SMS text message from your phone number to the specified number. \" +\n \"The recipient number must be in E.164 format (e.g., +12025551234).\",\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 text message content to send\" }),\n }),\n handler: async (params) => {\n const { to, body } = params as { to: string; body: string };\n return getClient().sendSms({ to, body });\n },\n }),\n\n defineTool({\n name: \"mobile_call\",\n description:\n \"Make an outbound phone call to the specified number. \" +\n \"When the recipient answers, they will be connected to your voice pipeline. \" +\n \"The number must be in E.164 format (e.g., +12025551234).\",\n parameters: Type.Object({\n to: Type.String({ description: \"Phone number to call in E.164 format (e.g., +12025551234)\" }),\n }),\n handler: async (params) => {\n const { to } = params as { to: string };\n return getClient().startOutboundCall({ to });\n },\n }),\n];\n\n// ── Plugin definition ────────────────────────────────────────\n\nconst plugin = {\n id: \"@alfe.ai/openclaw-mobile\",\n name: \"Alfe Mobile Plugin\",\n description: \"Mobile integration — phone number management, SMS, and phone calls 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-mobile\" });\n const log = api.logger;\n\n\n // Always register tools — OpenClaw may reload the plugin registry per-session\n for (const tool of mobileTools) {\n api.registerTool(tool);\n }\n log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(\", \")}`);\n\n // Only initialize config once (side effects)\n guardedStart(MOBILE_ACTIVATION_KEY, log, () => {\n log.info(\"Alfe Mobile plugin activating...\");\n\n try {\n const config = resolveConfig();\n client = new AgentApiClient({ apiUrl: config.apiUrl, apiKey: config.apiKey });\n log.info(`Mobile API: ${config.apiUrl}`);\n } catch (err) {\n log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);\n log.warn(\"Mobile tools will fail — no API config available\");\n // Reset so a later activate() can retry once config exists.\n resetActivation(MOBILE_ACTIVATION_KEY);\n }\n });\n\n log.info(\"Alfe Mobile plugin activated\");\n },\n\n deactivate(api: OpenClawPluginApi) {\n // Stop side effects BEFORE resetting the activation flag.\n client = undefined;\n resetActivation(MOBILE_ACTIVATION_KEY);\n api.logger.info(\"Alfe Mobile plugin deactivated\");\n },\n};\n\nexport default plugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,MADU,cAAc,OAAO,KAAK,IAAI,CAC1B,kBAAkB;AAmBtC,MAAM,wBAAwB,iBAAiB,SAAS;AAIxD,IAAI;AAEJ,SAAS,YAA4B;AACnC,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,oDAAoD;AAEtE,QAAO;;AAKT,MAAM,cAAkC;CAEtC,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO,EAAE,CAAC;EAC3B,SAAS,YAAY;AACnB,UAAO,WAAW,CAAC,iBAAiB;;EAEvC,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO;GACtB,SAAS,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,iDAAiD,CAAC,CAAC;GACrG,OAAO,KAAK,SAAS,KAAK,OAAO,EAAE,aAAa,gFAAgF,CAAC,CAAC;GACnI,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,SAAS,UAAU;AAC3B,UAAO,WAAW,CAAC,oBAAoB;IAAE;IAAS;IAAO,CAAC;;EAE7D,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAIF,YAAY,KAAK,OAAO;GACtB,aAAa,KAAK,OAAO,EAAE,aAAa,qEAAqE,CAAC;GAC9G,aAAa,KAAK,OAAO,EAAE,aAAa,mCAAmC,CAAC;GAC7E,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,aAAa,gBAAgB;AACrC,UAAO,WAAW,CAAC,mBAAmB;IAAE;IAAa;IAAa,CAAC;;EAEtE,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO,EAAE,CAAC;EAC3B,SAAS,YAAY;AACnB,UAAO,WAAW,CAAC,qBAAqB;;EAE3C,CAAC;CAGF,WAAW;EACT,MAAM;EACN,aACE;EAEF,YAAY,KAAK,OAAO;GACtB,IAAI,KAAK,OAAO,EAAE,aAAa,+DAA+D,CAAC;GAC/F,MAAM,KAAK,OAAO,EAAE,aAAa,oCAAoC,CAAC;GACvE,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,IAAI,SAAS;AACrB,UAAO,WAAW,CAAC,QAAQ;IAAE;IAAI;IAAM,CAAC;;EAE3C,CAAC;CAEF,WAAW;EACT,MAAM;EACN,aACE;EAGF,YAAY,KAAK,OAAO,EACtB,IAAI,KAAK,OAAO,EAAE,aAAa,6DAA6D,CAAC,EAC9F,CAAC;EACF,SAAS,OAAO,WAAW;GACzB,MAAM,EAAE,OAAO;AACf,UAAO,WAAW,CAAC,kBAAkB,EAAE,IAAI,CAAC;;EAE/C,CAAC;CACH;AAID,MAAM,SAAS;CACb,IAAI;CACJ,MAAM;CACN,aAAa;CACb,SAAS,IAAI;CAEb,SAAS,KAAwB;AAI/B,0BAAwB,KAAK,EAAE,QAAQ,mBAAmB,CAAC;EAC3D,MAAM,MAAM,IAAI;AAIhB,OAAK,MAAM,QAAQ,YACjB,KAAI,aAAa,KAAK;AAExB,MAAI,KAAK,cAAc,OAAO,YAAY,OAAO,CAAC,iBAAiB,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,GAAG;AAG/G,eAAa,uBAAuB,WAAW;AAC7C,OAAI,KAAK,mCAAmC;AAE5C,OAAI;IACF,MAAM,SAAS,eAAe;AAC9B,aAAS,IAAI,eAAe;KAAE,QAAQ,OAAO;KAAQ,QAAQ,OAAO;KAAQ,CAAC;AAC7E,QAAI,KAAK,eAAe,OAAO,SAAS;YACjC,KAAK;AACZ,QAAI,MAAM,6BAA6B,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC1F,QAAI,KAAK,mDAAmD;AAE5D,oBAAgB,sBAAsB;;IAExC;AAEF,MAAI,KAAK,+BAA+B;;CAG1C,WAAW,KAAwB;AAEjC,WAAS,KAAA;AACT,kBAAgB,sBAAsB;AACtC,MAAI,OAAO,KAAK,iCAAiC;;CAEpD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/openclaw-mobile",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "OpenClaw mobile plugin for Alfe — phone number management, SMS, and calls 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)",
|