@alfe.ai/openclaw-mobile 0.0.25 → 0.0.27
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 +126 -101
- package/dist/plugin.d.cts +25 -15
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.ts +25 -15
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +119 -95
- package/dist/plugin.js.map +1 -1
- package/dist/plugin2.d.cts +2 -0
- package/dist/plugin2.d.ts +2 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +5 -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,8 @@
|
|
|
1
|
-
let
|
|
1
|
+
let node_module = require("node:module");
|
|
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");
|
|
5
|
+
let _sinclair_typebox = require("@sinclair/typebox");
|
|
4
6
|
//#region src/plugin.ts
|
|
5
7
|
/**
|
|
6
8
|
* @alfe/openclaw-mobile — OpenClaw native plugin
|
|
@@ -16,141 +18,161 @@ let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
|
|
|
16
18
|
* - mobile_send_sms — send an SMS message from the agent's phone number
|
|
17
19
|
* - mobile_call — initiate an outbound phone call
|
|
18
20
|
*/
|
|
19
|
-
const pkg = (0,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const pkg = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
|
|
22
|
+
const SUPPORTED_COUNTRIES = [
|
|
23
|
+
"AU",
|
|
24
|
+
"US",
|
|
25
|
+
"CA",
|
|
26
|
+
"GB"
|
|
27
|
+
];
|
|
28
|
+
const E164_PATTERN = "^\\+\\d{7,15}$";
|
|
29
|
+
const E164_RE = /^\+\d{7,15}$/u;
|
|
30
|
+
const NUMBER_SEARCH_RE = /^[A-Za-z0-9*+ -]+$/u;
|
|
31
|
+
const MAX_SEARCH_LENGTH = 32;
|
|
32
|
+
const MAX_SMS_LENGTH = 4400;
|
|
33
|
+
function requireString(params, field, options) {
|
|
34
|
+
const value = params[field];
|
|
35
|
+
if (typeof value !== "string" || value.length < 1 || value.length > options.maxLength) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${field} must be a non-empty string of at most ${String(options.maxLength)} characters`);
|
|
36
|
+
if (options.pattern && !options.pattern.test(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${field} has an invalid format`);
|
|
37
|
+
return value;
|
|
28
38
|
}
|
|
29
|
-
function
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
details: { error: message }
|
|
36
|
-
};
|
|
39
|
+
function optionalSearchQuery(params) {
|
|
40
|
+
if (params.query === void 0) return void 0;
|
|
41
|
+
return requireString(params, "query", {
|
|
42
|
+
maxLength: MAX_SEARCH_LENGTH,
|
|
43
|
+
pattern: NUMBER_SEARCH_RE
|
|
44
|
+
});
|
|
37
45
|
}
|
|
38
|
-
function
|
|
39
|
-
return
|
|
40
|
-
|
|
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
|
-
};
|
|
46
|
+
function optionalCountry(params) {
|
|
47
|
+
if (params.country === void 0) return void 0;
|
|
48
|
+
return requireCountry(params, "country");
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
50
|
+
function requireCountry(params, field) {
|
|
51
|
+
const value = params[field];
|
|
52
|
+
if (typeof value !== "string" || !SUPPORTED_COUNTRIES.includes(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${field} must be one of ${SUPPORTED_COUNTRIES.join(", ")}`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function requireE164(params, field) {
|
|
56
|
+
return requireString(params, field, {
|
|
57
|
+
maxLength: 16,
|
|
58
|
+
pattern: E164_RE
|
|
69
59
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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;
|
|
60
|
+
}
|
|
61
|
+
function requireConfirmation(params) {
|
|
62
|
+
if (params.confirm !== true) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("confirm must be true after the user explicitly approves the recurring charge");
|
|
63
|
+
}
|
|
64
|
+
const MOBILE_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("mobile");
|
|
65
|
+
let client;
|
|
66
|
+
function getClient() {
|
|
67
|
+
if (!client) throw new Error("Mobile tools unavailable — no API config resolved");
|
|
68
|
+
return client;
|
|
84
69
|
}
|
|
85
70
|
const mobileTools = [
|
|
86
|
-
defineTool({
|
|
71
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
87
72
|
name: "mobile_get_number",
|
|
88
73
|
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
|
-
parameters: _sinclair_typebox.Type.Object({}),
|
|
74
|
+
parameters: _sinclair_typebox.Type.Object({}, { additionalProperties: false }),
|
|
90
75
|
handler: async () => {
|
|
91
|
-
return
|
|
76
|
+
return getClient().getMobileNumber();
|
|
92
77
|
}
|
|
93
78
|
}),
|
|
94
|
-
defineTool({
|
|
79
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
95
80
|
name: "mobile_search_numbers",
|
|
96
81
|
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
82
|
parameters: _sinclair_typebox.Type.Object({
|
|
98
|
-
country: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.
|
|
99
|
-
query: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({
|
|
100
|
-
|
|
83
|
+
country: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.Union(SUPPORTED_COUNTRIES.map((country) => _sinclair_typebox.Type.Literal(country)), { description: "Country code: AU, US, CA, or GB (default: AU)" })),
|
|
84
|
+
query: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({
|
|
85
|
+
description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')",
|
|
86
|
+
minLength: 1,
|
|
87
|
+
maxLength: MAX_SEARCH_LENGTH,
|
|
88
|
+
pattern: NUMBER_SEARCH_RE.source
|
|
89
|
+
}))
|
|
90
|
+
}, { additionalProperties: false }),
|
|
101
91
|
handler: async (params) => {
|
|
102
|
-
const
|
|
103
|
-
|
|
92
|
+
const country = optionalCountry(params);
|
|
93
|
+
const query = optionalSearchQuery(params);
|
|
94
|
+
return getClient().searchMobileNumbers({
|
|
104
95
|
country,
|
|
105
96
|
query
|
|
106
97
|
});
|
|
107
98
|
}
|
|
108
99
|
}),
|
|
109
|
-
defineTool({
|
|
100
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
110
101
|
name: "mobile_assign_number",
|
|
111
|
-
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.",
|
|
102
|
+
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 and may replace an active number. Set confirm=true only after the user explicitly approves the charge.",
|
|
112
103
|
parameters: _sinclair_typebox.Type.Object({
|
|
113
|
-
phoneNumber: _sinclair_typebox.Type.String({
|
|
114
|
-
|
|
115
|
-
|
|
104
|
+
phoneNumber: _sinclair_typebox.Type.String({
|
|
105
|
+
description: "The phone number to purchase (from mobile_search_numbers results)",
|
|
106
|
+
minLength: 8,
|
|
107
|
+
maxLength: 16,
|
|
108
|
+
pattern: E164_PATTERN
|
|
109
|
+
}),
|
|
110
|
+
countryCode: _sinclair_typebox.Type.Union(SUPPORTED_COUNTRIES.map((country) => _sinclair_typebox.Type.Literal(country)), { description: "Country code: AU, US, CA, or GB" }),
|
|
111
|
+
confirm: _sinclair_typebox.Type.Literal(true, { description: "Must be true after the user explicitly approves the recurring charge" })
|
|
112
|
+
}, { additionalProperties: false }),
|
|
116
113
|
handler: async (params) => {
|
|
117
|
-
const
|
|
118
|
-
|
|
114
|
+
const phoneNumber = requireE164(params, "phoneNumber");
|
|
115
|
+
const countryCode = requireCountry(params, "countryCode");
|
|
116
|
+
requireConfirmation(params);
|
|
117
|
+
return getClient().assignMobileNumber({
|
|
119
118
|
phoneNumber,
|
|
120
119
|
countryCode
|
|
121
120
|
});
|
|
122
121
|
}
|
|
123
122
|
}),
|
|
124
|
-
defineTool({
|
|
123
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
125
124
|
name: "mobile_release_number",
|
|
126
|
-
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
|
-
parameters: _sinclair_typebox.Type.Object({
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
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. Copy the exact current number into confirmPhoneNumber only after the user approves.",
|
|
126
|
+
parameters: _sinclair_typebox.Type.Object({ confirmPhoneNumber: _sinclair_typebox.Type.String({
|
|
127
|
+
description: "Exact currently assigned number, copied to confirm permanent release",
|
|
128
|
+
minLength: 8,
|
|
129
|
+
maxLength: 16,
|
|
130
|
+
pattern: E164_PATTERN
|
|
131
|
+
}) }, { additionalProperties: false }),
|
|
132
|
+
handler: async (params) => {
|
|
133
|
+
const confirmPhoneNumber = requireE164(params, "confirmPhoneNumber");
|
|
134
|
+
const mobileClient = getClient();
|
|
135
|
+
if ((await mobileClient.getMobileNumber()).phoneNumber !== confirmPhoneNumber) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("confirmPhoneNumber does not match the currently assigned number");
|
|
136
|
+
return mobileClient.releaseMobileNumber();
|
|
130
137
|
}
|
|
131
138
|
}),
|
|
132
|
-
defineTool({
|
|
139
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
133
140
|
name: "mobile_send_sms",
|
|
134
141
|
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
142
|
parameters: _sinclair_typebox.Type.Object({
|
|
136
|
-
to: _sinclair_typebox.Type.String({
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
to: _sinclair_typebox.Type.String({
|
|
144
|
+
description: "Recipient phone number in E.164 format (e.g., +12025551234)",
|
|
145
|
+
minLength: 8,
|
|
146
|
+
maxLength: 16,
|
|
147
|
+
pattern: E164_PATTERN
|
|
148
|
+
}),
|
|
149
|
+
body: _sinclair_typebox.Type.String({
|
|
150
|
+
description: "The text message content to send",
|
|
151
|
+
minLength: 1,
|
|
152
|
+
maxLength: MAX_SMS_LENGTH
|
|
153
|
+
})
|
|
154
|
+
}, { additionalProperties: false }),
|
|
139
155
|
handler: async (params) => {
|
|
140
|
-
const
|
|
141
|
-
|
|
156
|
+
const to = requireE164(params, "to");
|
|
157
|
+
const body = requireString(params, "body", { maxLength: MAX_SMS_LENGTH });
|
|
158
|
+
return getClient().sendSms({
|
|
142
159
|
to,
|
|
143
160
|
body
|
|
144
161
|
});
|
|
145
162
|
}
|
|
146
163
|
}),
|
|
147
|
-
defineTool({
|
|
164
|
+
(0, _alfe_ai_openclaw_plugin_kit.defineTool)({
|
|
148
165
|
name: "mobile_call",
|
|
149
166
|
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
|
-
parameters: _sinclair_typebox.Type.Object({ to: _sinclair_typebox.Type.String({
|
|
167
|
+
parameters: _sinclair_typebox.Type.Object({ to: _sinclair_typebox.Type.String({
|
|
168
|
+
description: "Phone number to call in E.164 format (e.g., +12025551234)",
|
|
169
|
+
minLength: 8,
|
|
170
|
+
maxLength: 16,
|
|
171
|
+
pattern: E164_PATTERN
|
|
172
|
+
}) }, { additionalProperties: false }),
|
|
151
173
|
handler: async (params) => {
|
|
152
|
-
const
|
|
153
|
-
return
|
|
174
|
+
const to = requireE164(params, "to");
|
|
175
|
+
return getClient().startOutboundCall({ to });
|
|
154
176
|
}
|
|
155
177
|
})
|
|
156
178
|
];
|
|
@@ -163,24 +185,27 @@ const plugin = {
|
|
|
163
185
|
(0, _alfe_ai_agent_api_client.installToolErrorCapture)(api, { plugin: "openclaw-mobile" });
|
|
164
186
|
const log = api.logger;
|
|
165
187
|
for (const tool of mobileTools) api.registerTool(tool);
|
|
166
|
-
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((
|
|
167
|
-
|
|
168
|
-
globalThis.__mobilePluginActivated = true;
|
|
188
|
+
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((tool) => tool.name).join(", ")}`);
|
|
189
|
+
(0, _alfe_ai_openclaw_plugin_kit.guardedStart)(MOBILE_ACTIVATION_KEY, log, () => {
|
|
169
190
|
log.info("Alfe Mobile plugin activating...");
|
|
170
191
|
try {
|
|
171
192
|
const config = (0, _alfe_ai_config.resolveConfig)();
|
|
172
|
-
|
|
173
|
-
|
|
193
|
+
client = new _alfe_ai_agent_api_client.AgentApiClient({
|
|
194
|
+
apiUrl: config.apiUrl,
|
|
195
|
+
apiKey: config.apiKey
|
|
196
|
+
});
|
|
197
|
+
log.info("Mobile API client configured");
|
|
174
198
|
} catch (err) {
|
|
175
199
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
176
200
|
log.warn("Mobile tools will fail — no API config available");
|
|
201
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(MOBILE_ACTIVATION_KEY);
|
|
177
202
|
}
|
|
178
|
-
|
|
179
|
-
}
|
|
203
|
+
});
|
|
180
204
|
log.info("Alfe Mobile plugin activated");
|
|
181
205
|
},
|
|
182
206
|
deactivate(api) {
|
|
183
|
-
|
|
207
|
+
client = void 0;
|
|
208
|
+
(0, _alfe_ai_openclaw_plugin_kit.resetActivation)(MOBILE_ACTIVATION_KEY);
|
|
184
209
|
api.logger.info("Alfe Mobile plugin deactivated");
|
|
185
210
|
}
|
|
186
211
|
};
|
package/dist/plugin.d.cts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
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
|
+
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
9
|
+
interface ToolResult {
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
details: unknown;
|
|
15
|
+
isError?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface ToolDef<TParameters = unknown> {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
label: string;
|
|
21
|
+
parameters: TParameters;
|
|
22
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
23
|
+
}
|
|
24
|
+
/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
|
|
25
|
+
//#endregion
|
|
3
26
|
//#region src/plugin.d.ts
|
|
4
27
|
|
|
5
28
|
interface Logger {
|
|
@@ -11,25 +34,12 @@ interface Logger {
|
|
|
11
34
|
interface OpenClawPluginApi {
|
|
12
35
|
logger: Logger;
|
|
13
36
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
37
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
38
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
39
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
40
|
priority?: number;
|
|
18
41
|
}): void;
|
|
19
42
|
}
|
|
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
43
|
declare const plugin: {
|
|
34
44
|
id: string;
|
|
35
45
|
name: string;
|
|
@@ -39,5 +49,5 @@ declare const plugin: {
|
|
|
39
49
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
50
|
};
|
|
41
51
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
52
|
+
export { plugin as t };
|
|
43
53
|
//# 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","PublicToolError","Error","publicToolError","ok","errResult","defineTool","IpcResponse","IpcClient","IpcModule","ConnectToDaemonOptions","ConnectToDaemonDependencies","connectToDaemon","ResolveOpenClawSdkOptions","ResolveOpenClawSdkDependencies","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"],"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/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n isError?: boolean;\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/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */\ndeclare class PublicToolError extends Error {\n readonly name = \"PublicToolError\";\n}\ndeclare function publicToolError(message: string): PublicToolError;\n/** Wrap bounded JSON in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an explicitly public error in a captured OpenClaw error envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define a tool whose unexpected exceptions never expose provider, filesystem,\n * credential, or transport diagnostics to the model. Throw `PublicToolError`\n * only for a message deliberately safe for model display.\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\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\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 IpcModule {\n IPCClient: new (socketPath: string, log: KitLogger) => IpcClient;\n}\ninterface ConnectToDaemonOptions {\n pluginId: string;\n capabilities?: readonly string[];\n onMessage?: (msg: Record<string, unknown>) => void;\n standaloneNote?: string;\n}\ninterface ConnectToDaemonDependencies {\n loadIpcModule?: () => Promise<IpcModule>;\n}\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions, dependencies?: ConnectToDaemonDependencies): Promise<IpcClient | null>;\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n specifier?: string;\n exportName?: string;\n unresolvableNote?: string;\n}\ninterface ResolveOpenClawSdkDependencies {\n anchors?: readonly string[];\n globalPackageJsonPath?: string;\n requireFrom?: (anchor: string) => (specifier: string) => unknown;\n}\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions, dependencies?: ResolveOpenClawSdkDependencies): 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\n//#endregion\nexport { type ConnectToDaemonDependencies, type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, PublicToolError, type ResolveOpenClawSdkDependencies, type ResolveOpenClawSdkOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, publicToolError, resetActivation, resolveOpenClawSdk };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;ACwCgB;;;;UDrBNC,UAAAA,CC+BW;SACkD,EAAA;QAG3B,EAAA,MAAA;IAAO,IAAA,EAAA,MAAA;EAAA,CAAA,EA4O7C;EA8CL,OAAA,EAAA,OAAA;SAxCe,CAAA,EAAA,OAAA;;UD7QNC,OC+SyB,CAAA,cAAA,OAAA,CAAA,CAAA;;;;cD3SrBC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCQlE,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,CAOyC;EA4O7C,MAAA,EAlPI,MAgST;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAxCe,CAAA,IAAA,EAtPK,OAsPL,CAtPa,OAsPb,CAAA,CAAA,EAAA,IAAA;uBAkCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAvRqD,OAuRrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GApRS,OAoRT,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAxC7B;;;;;gBAMU;kBAkCE"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
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
|
+
/** Shape returned to OpenClaw from a tool `execute`. */
|
|
9
|
+
interface ToolResult {
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
details: unknown;
|
|
15
|
+
isError?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface ToolDef<TParameters = unknown> {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
label: string;
|
|
21
|
+
parameters: TParameters;
|
|
22
|
+
execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
|
|
23
|
+
}
|
|
24
|
+
/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
|
|
25
|
+
//#endregion
|
|
3
26
|
//#region src/plugin.d.ts
|
|
4
27
|
|
|
5
28
|
interface Logger {
|
|
@@ -11,25 +34,12 @@ interface Logger {
|
|
|
11
34
|
interface OpenClawPluginApi {
|
|
12
35
|
logger: Logger;
|
|
13
36
|
registrationMode?: "full" | "setup-only" | "setup-runtime" | "cli-metadata";
|
|
14
|
-
registerTool(tool: ToolDef): void;
|
|
37
|
+
registerTool(tool: ToolDef<TSchema>): void;
|
|
15
38
|
registerGatewayMethod(name: string, handler: (...args: unknown[]) => Promise<unknown>): void;
|
|
16
39
|
on(event: string, handler: (...args: unknown[]) => void | Promise<void>, options?: {
|
|
17
40
|
priority?: number;
|
|
18
41
|
}): void;
|
|
19
42
|
}
|
|
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
43
|
declare const plugin: {
|
|
34
44
|
id: string;
|
|
35
45
|
name: string;
|
|
@@ -39,5 +49,5 @@ declare const plugin: {
|
|
|
39
49
|
deactivate(api: OpenClawPluginApi): void;
|
|
40
50
|
};
|
|
41
51
|
//#endregion
|
|
42
|
-
export { plugin as
|
|
52
|
+
export { plugin as t };
|
|
43
53
|
//# 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","PublicToolError","Error","publicToolError","ok","errResult","defineTool","IpcResponse","IpcClient","IpcModule","ConnectToDaemonOptions","ConnectToDaemonDependencies","connectToDaemon","ResolveOpenClawSdkOptions","ResolveOpenClawSdkDependencies","resolveOpenClawSdk","T","getActivationKey","isActivated","resetActivation","guardedStart"],"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/** Shape returned to OpenClaw from a tool `execute`. */\ninterface ToolResult {\n content: {\n type: \"text\";\n text: string;\n }[];\n details: unknown;\n isError?: boolean;\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/** Deliberately model-safe validation/usage failure. Other exceptions stay private. */\ndeclare class PublicToolError extends Error {\n readonly name = \"PublicToolError\";\n}\ndeclare function publicToolError(message: string): PublicToolError;\n/** Wrap bounded JSON in the OpenClaw tool-result envelope. */\ndeclare function ok(data: unknown): ToolResult;\n/** Wrap an explicitly public error in a captured OpenClaw error envelope. */\ndeclare function errResult(message: string): ToolResult;\n/**\n * Define a tool whose unexpected exceptions never expose provider, filesystem,\n * credential, or transport diagnostics to the model. Throw `PublicToolError`\n * only for a message deliberately safe for model display.\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\ninterface IpcResponse {\n ok: boolean;\n error?: {\n message?: string;\n };\n [key: string]: unknown;\n}\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 IpcModule {\n IPCClient: new (socketPath: string, log: KitLogger) => IpcClient;\n}\ninterface ConnectToDaemonOptions {\n pluginId: string;\n capabilities?: readonly string[];\n onMessage?: (msg: Record<string, unknown>) => void;\n standaloneNote?: string;\n}\ninterface ConnectToDaemonDependencies {\n loadIpcModule?: () => Promise<IpcModule>;\n}\ndeclare function connectToDaemon(socketPath: string, log: KitLogger, options: ConnectToDaemonOptions, dependencies?: ConnectToDaemonDependencies): Promise<IpcClient | null>;\n//#endregion\n//#region src/sdk.d.ts\ninterface ResolveOpenClawSdkOptions {\n specifier?: string;\n exportName?: string;\n unresolvableNote?: string;\n}\ninterface ResolveOpenClawSdkDependencies {\n anchors?: readonly string[];\n globalPackageJsonPath?: string;\n requireFrom?: (anchor: string) => (specifier: string) => unknown;\n}\ndeclare function resolveOpenClawSdk<T = unknown>(log: KitLogger, options?: ResolveOpenClawSdkOptions, dependencies?: ResolveOpenClawSdkDependencies): 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\n//#endregion\nexport { type ConnectToDaemonDependencies, type ConnectToDaemonOptions, type IpcClient, type IpcResponse, type KitLogger, PublicToolError, type ResolveOpenClawSdkDependencies, type ResolveOpenClawSdkOptions, type ToolDef, type ToolResult, connectToDaemon, defineTool, errResult, getActivationKey, guardedStart, isActivated, ok, publicToolError, resetActivation, resolveOpenClawSdk };\n//# sourceMappingURL=index.d.ts.map"],"mappings":";;;;ACwCgB;;;;UDrBNC,UAAAA,CC+BW;SACkD,EAAA;QAG3B,EAAA,MAAA;IAAO,IAAA,EAAA,MAAA;EAAA,CAAA,EA4O7C;EA8CL,OAAA,EAAA,OAAA;SAxCe,CAAA,EAAA,OAAA;;UD7QNC,OC+SyB,CAAA,cAAA,OAAA,CAAA,CAAA;;;;cD3SrBC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCQlE,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,CAOyC;EA4O7C,MAAA,EAlPI,MAgST;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAxCe,CAAA,IAAA,EAtPK,OAsPL,CAtPa,OAsPb,CAAA,CAAA,EAAA,IAAA;uBAkCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAvRqD,OAuRrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GApRS,OAoRT,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAxC7B;;;;;gBAMU;kBAkCE"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import { Type } from "@sinclair/typebox";
|
|
3
2
|
import { resolveConfig } from "@alfe.ai/config";
|
|
4
|
-
import { installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
3
|
+
import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
|
|
4
|
+
import { defineTool, getActivationKey, guardedStart, publicToolError, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
|
|
5
|
+
import { Type } from "@sinclair/typebox";
|
|
5
6
|
//#region src/plugin.ts
|
|
6
7
|
/**
|
|
7
8
|
* @alfe/openclaw-mobile — OpenClaw native plugin
|
|
@@ -18,90 +19,79 @@ 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
|
-
|
|
28
|
-
|
|
22
|
+
const SUPPORTED_COUNTRIES = [
|
|
23
|
+
"AU",
|
|
24
|
+
"US",
|
|
25
|
+
"CA",
|
|
26
|
+
"GB"
|
|
27
|
+
];
|
|
28
|
+
const E164_PATTERN = "^\\+\\d{7,15}$";
|
|
29
|
+
const E164_RE = /^\+\d{7,15}$/u;
|
|
30
|
+
const NUMBER_SEARCH_RE = /^[A-Za-z0-9*+ -]+$/u;
|
|
31
|
+
const MAX_SEARCH_LENGTH = 32;
|
|
32
|
+
const MAX_SMS_LENGTH = 4400;
|
|
33
|
+
function requireString(params, field, options) {
|
|
34
|
+
const value = params[field];
|
|
35
|
+
if (typeof value !== "string" || value.length < 1 || value.length > options.maxLength) throw publicToolError(`${field} must be a non-empty string of at most ${String(options.maxLength)} characters`);
|
|
36
|
+
if (options.pattern && !options.pattern.test(value)) throw publicToolError(`${field} has an invalid format`);
|
|
37
|
+
return value;
|
|
29
38
|
}
|
|
30
|
-
function
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
details: { error: message }
|
|
37
|
-
};
|
|
39
|
+
function optionalSearchQuery(params) {
|
|
40
|
+
if (params.query === void 0) return void 0;
|
|
41
|
+
return requireString(params, "query", {
|
|
42
|
+
maxLength: MAX_SEARCH_LENGTH,
|
|
43
|
+
pattern: NUMBER_SEARCH_RE
|
|
44
|
+
});
|
|
38
45
|
}
|
|
39
|
-
function
|
|
40
|
-
return
|
|
41
|
-
|
|
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
|
-
};
|
|
46
|
+
function optionalCountry(params) {
|
|
47
|
+
if (params.country === void 0) return void 0;
|
|
48
|
+
return requireCountry(params, "country");
|
|
53
49
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
50
|
+
function requireCountry(params, field) {
|
|
51
|
+
const value = params[field];
|
|
52
|
+
if (typeof value !== "string" || !SUPPORTED_COUNTRIES.includes(value)) throw publicToolError(`${field} must be one of ${SUPPORTED_COUNTRIES.join(", ")}`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function requireE164(params, field) {
|
|
56
|
+
return requireString(params, field, {
|
|
57
|
+
maxLength: 16,
|
|
58
|
+
pattern: E164_RE
|
|
70
59
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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;
|
|
60
|
+
}
|
|
61
|
+
function requireConfirmation(params) {
|
|
62
|
+
if (params.confirm !== true) throw publicToolError("confirm must be true after the user explicitly approves the recurring charge");
|
|
63
|
+
}
|
|
64
|
+
const MOBILE_ACTIVATION_KEY = getActivationKey("mobile");
|
|
65
|
+
let client;
|
|
66
|
+
function getClient() {
|
|
67
|
+
if (!client) throw new Error("Mobile tools unavailable — no API config resolved");
|
|
68
|
+
return client;
|
|
85
69
|
}
|
|
86
70
|
const mobileTools = [
|
|
87
71
|
defineTool({
|
|
88
72
|
name: "mobile_get_number",
|
|
89
73
|
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
|
-
parameters: Type.Object({}),
|
|
74
|
+
parameters: Type.Object({}, { additionalProperties: false }),
|
|
91
75
|
handler: async () => {
|
|
92
|
-
return
|
|
76
|
+
return getClient().getMobileNumber();
|
|
93
77
|
}
|
|
94
78
|
}),
|
|
95
79
|
defineTool({
|
|
96
80
|
name: "mobile_search_numbers",
|
|
97
81
|
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).",
|
|
98
82
|
parameters: Type.Object({
|
|
99
|
-
country: Type.Optional(Type.
|
|
100
|
-
query: Type.Optional(Type.String({
|
|
101
|
-
|
|
83
|
+
country: Type.Optional(Type.Union(SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)), { description: "Country code: AU, US, CA, or GB (default: AU)" })),
|
|
84
|
+
query: Type.Optional(Type.String({
|
|
85
|
+
description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')",
|
|
86
|
+
minLength: 1,
|
|
87
|
+
maxLength: MAX_SEARCH_LENGTH,
|
|
88
|
+
pattern: NUMBER_SEARCH_RE.source
|
|
89
|
+
}))
|
|
90
|
+
}, { additionalProperties: false }),
|
|
102
91
|
handler: async (params) => {
|
|
103
|
-
const
|
|
104
|
-
|
|
92
|
+
const country = optionalCountry(params);
|
|
93
|
+
const query = optionalSearchQuery(params);
|
|
94
|
+
return getClient().searchMobileNumbers({
|
|
105
95
|
country,
|
|
106
96
|
query
|
|
107
97
|
});
|
|
@@ -109,14 +99,22 @@ const mobileTools = [
|
|
|
109
99
|
}),
|
|
110
100
|
defineTool({
|
|
111
101
|
name: "mobile_assign_number",
|
|
112
|
-
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.",
|
|
102
|
+
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 and may replace an active number. Set confirm=true only after the user explicitly approves the charge.",
|
|
113
103
|
parameters: Type.Object({
|
|
114
|
-
phoneNumber: Type.String({
|
|
115
|
-
|
|
116
|
-
|
|
104
|
+
phoneNumber: Type.String({
|
|
105
|
+
description: "The phone number to purchase (from mobile_search_numbers results)",
|
|
106
|
+
minLength: 8,
|
|
107
|
+
maxLength: 16,
|
|
108
|
+
pattern: E164_PATTERN
|
|
109
|
+
}),
|
|
110
|
+
countryCode: Type.Union(SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)), { description: "Country code: AU, US, CA, or GB" }),
|
|
111
|
+
confirm: Type.Literal(true, { description: "Must be true after the user explicitly approves the recurring charge" })
|
|
112
|
+
}, { additionalProperties: false }),
|
|
117
113
|
handler: async (params) => {
|
|
118
|
-
const
|
|
119
|
-
|
|
114
|
+
const phoneNumber = requireE164(params, "phoneNumber");
|
|
115
|
+
const countryCode = requireCountry(params, "countryCode");
|
|
116
|
+
requireConfirmation(params);
|
|
117
|
+
return getClient().assignMobileNumber({
|
|
120
118
|
phoneNumber,
|
|
121
119
|
countryCode
|
|
122
120
|
});
|
|
@@ -124,22 +122,40 @@ const mobileTools = [
|
|
|
124
122
|
}),
|
|
125
123
|
defineTool({
|
|
126
124
|
name: "mobile_release_number",
|
|
127
|
-
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
|
-
parameters: Type.Object({
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
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. Copy the exact current number into confirmPhoneNumber only after the user approves.",
|
|
126
|
+
parameters: Type.Object({ confirmPhoneNumber: Type.String({
|
|
127
|
+
description: "Exact currently assigned number, copied to confirm permanent release",
|
|
128
|
+
minLength: 8,
|
|
129
|
+
maxLength: 16,
|
|
130
|
+
pattern: E164_PATTERN
|
|
131
|
+
}) }, { additionalProperties: false }),
|
|
132
|
+
handler: async (params) => {
|
|
133
|
+
const confirmPhoneNumber = requireE164(params, "confirmPhoneNumber");
|
|
134
|
+
const mobileClient = getClient();
|
|
135
|
+
if ((await mobileClient.getMobileNumber()).phoneNumber !== confirmPhoneNumber) throw publicToolError("confirmPhoneNumber does not match the currently assigned number");
|
|
136
|
+
return mobileClient.releaseMobileNumber();
|
|
131
137
|
}
|
|
132
138
|
}),
|
|
133
139
|
defineTool({
|
|
134
140
|
name: "mobile_send_sms",
|
|
135
141
|
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).",
|
|
136
142
|
parameters: Type.Object({
|
|
137
|
-
to: Type.String({
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
to: Type.String({
|
|
144
|
+
description: "Recipient phone number in E.164 format (e.g., +12025551234)",
|
|
145
|
+
minLength: 8,
|
|
146
|
+
maxLength: 16,
|
|
147
|
+
pattern: E164_PATTERN
|
|
148
|
+
}),
|
|
149
|
+
body: Type.String({
|
|
150
|
+
description: "The text message content to send",
|
|
151
|
+
minLength: 1,
|
|
152
|
+
maxLength: MAX_SMS_LENGTH
|
|
153
|
+
})
|
|
154
|
+
}, { additionalProperties: false }),
|
|
140
155
|
handler: async (params) => {
|
|
141
|
-
const
|
|
142
|
-
|
|
156
|
+
const to = requireE164(params, "to");
|
|
157
|
+
const body = requireString(params, "body", { maxLength: MAX_SMS_LENGTH });
|
|
158
|
+
return getClient().sendSms({
|
|
143
159
|
to,
|
|
144
160
|
body
|
|
145
161
|
});
|
|
@@ -148,10 +164,15 @@ const mobileTools = [
|
|
|
148
164
|
defineTool({
|
|
149
165
|
name: "mobile_call",
|
|
150
166
|
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).",
|
|
151
|
-
parameters: Type.Object({ to: Type.String({
|
|
167
|
+
parameters: Type.Object({ to: Type.String({
|
|
168
|
+
description: "Phone number to call in E.164 format (e.g., +12025551234)",
|
|
169
|
+
minLength: 8,
|
|
170
|
+
maxLength: 16,
|
|
171
|
+
pattern: E164_PATTERN
|
|
172
|
+
}) }, { additionalProperties: false }),
|
|
152
173
|
handler: async (params) => {
|
|
153
|
-
const
|
|
154
|
-
return
|
|
174
|
+
const to = requireE164(params, "to");
|
|
175
|
+
return getClient().startOutboundCall({ to });
|
|
155
176
|
}
|
|
156
177
|
})
|
|
157
178
|
];
|
|
@@ -164,24 +185,27 @@ const plugin = {
|
|
|
164
185
|
installToolErrorCapture(api, { plugin: "openclaw-mobile" });
|
|
165
186
|
const log = api.logger;
|
|
166
187
|
for (const tool of mobileTools) api.registerTool(tool);
|
|
167
|
-
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((
|
|
168
|
-
|
|
169
|
-
globalThis.__mobilePluginActivated = true;
|
|
188
|
+
log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((tool) => tool.name).join(", ")}`);
|
|
189
|
+
guardedStart(MOBILE_ACTIVATION_KEY, log, () => {
|
|
170
190
|
log.info("Alfe Mobile plugin activating...");
|
|
171
191
|
try {
|
|
172
192
|
const config = resolveConfig();
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
client = new AgentApiClient({
|
|
194
|
+
apiUrl: config.apiUrl,
|
|
195
|
+
apiKey: config.apiKey
|
|
196
|
+
});
|
|
197
|
+
log.info("Mobile API client configured");
|
|
175
198
|
} catch (err) {
|
|
176
199
|
log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
|
|
177
200
|
log.warn("Mobile tools will fail — no API config available");
|
|
201
|
+
resetActivation(MOBILE_ACTIVATION_KEY);
|
|
178
202
|
}
|
|
179
|
-
|
|
180
|
-
}
|
|
203
|
+
});
|
|
181
204
|
log.info("Alfe Mobile plugin activated");
|
|
182
205
|
},
|
|
183
206
|
deactivate(api) {
|
|
184
|
-
|
|
207
|
+
client = void 0;
|
|
208
|
+
resetActivation(MOBILE_ACTIVATION_KEY);
|
|
185
209
|
api.logger.info("Alfe Mobile plugin deactivated");
|
|
186
210
|
}
|
|
187
211
|
};
|
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 { createRequire } from \"node:module\";\nimport { resolveConfig } from \"@alfe.ai/config\";\nimport { AgentApiClient, installToolErrorCapture } from \"@alfe.ai/agent-api-client\";\nimport {\n defineTool,\n getActivationKey,\n guardedStart,\n publicToolError,\n resetActivation,\n type ToolDef,\n} from \"@alfe.ai/openclaw-plugin-kit\";\nimport { Type, type TSchema } from \"@sinclair/typebox\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\nconst SUPPORTED_COUNTRIES = [\"AU\", \"US\", \"CA\", \"GB\"] as const;\nconst E164_PATTERN = \"^\\\\+\\\\d{7,15}$\";\nconst E164_RE = /^\\+\\d{7,15}$/u;\nconst NUMBER_SEARCH_RE = /^[A-Za-z0-9*+ -]+$/u;\nconst MAX_SEARCH_LENGTH = 32;\nconst MAX_SMS_LENGTH = 4_400;\n\ntype SupportedCountry = (typeof SUPPORTED_COUNTRIES)[number];\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(\n event: string,\n handler: (...args: unknown[]) => void | Promise<void>,\n options?: { priority?: number },\n ): void;\n}\n\nfunction requireString(\n params: Record<string, unknown>,\n field: string,\n options: { maxLength: number; pattern?: RegExp },\n): string {\n const value = params[field];\n if (typeof value !== \"string\" || value.length < 1 || value.length > options.maxLength) {\n throw publicToolError(\n `${field} must be a non-empty string of at most ${String(options.maxLength)} characters`,\n );\n }\n if (options.pattern && !options.pattern.test(value)) {\n throw publicToolError(`${field} has an invalid format`);\n }\n return value;\n}\n\nfunction optionalSearchQuery(params: Record<string, unknown>): string | undefined {\n if (params.query === undefined) return undefined;\n return requireString(params, \"query\", {\n maxLength: MAX_SEARCH_LENGTH,\n pattern: NUMBER_SEARCH_RE,\n });\n}\n\nfunction optionalCountry(params: Record<string, unknown>): SupportedCountry | undefined {\n if (params.country === undefined) return undefined;\n return requireCountry(params, \"country\");\n}\n\nfunction requireCountry(params: Record<string, unknown>, field: string): SupportedCountry {\n const value = params[field];\n if (typeof value !== \"string\" || !SUPPORTED_COUNTRIES.includes(value as SupportedCountry)) {\n throw publicToolError(`${field} must be one of ${SUPPORTED_COUNTRIES.join(\", \")}`);\n }\n return value as SupportedCountry;\n}\n\nfunction requireE164(params: Record<string, unknown>, field: string): string {\n return requireString(params, field, { maxLength: 16, pattern: E164_RE });\n}\n\nfunction requireConfirmation(params: Record<string, unknown>): void {\n if (params.confirm !== true) {\n throw publicToolError(\n \"confirm must be true after the user explicitly approves the recurring charge\",\n );\n }\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({}, { additionalProperties: false }),\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 {\n country: Type.Optional(\n Type.Union(\n SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)),\n { description: \"Country code: AU, US, CA, or GB (default: AU)\" },\n ),\n ),\n query: Type.Optional(\n Type.String({\n description: \"Optional search pattern — area code or text to match (e.g., '415' or 'COOL')\",\n minLength: 1,\n maxLength: MAX_SEARCH_LENGTH,\n pattern: NUMBER_SEARCH_RE.source,\n }),\n ),\n },\n { additionalProperties: false },\n ),\n handler: async (params) => {\n const country = optionalCountry(params);\n const query = optionalSearchQuery(params);\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 and may replace \" +\n \"an active number. Set confirm=true only after the user explicitly approves the charge.\",\n parameters: Type.Object(\n {\n phoneNumber: Type.String({\n description: \"The phone number to purchase (from mobile_search_numbers results)\",\n minLength: 8,\n maxLength: 16,\n pattern: E164_PATTERN,\n }),\n countryCode: Type.Union(\n SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)),\n { description: \"Country code: AU, US, CA, or GB\" },\n ),\n confirm: Type.Literal(true, {\n description: \"Must be true after the user explicitly approves the recurring charge\",\n }),\n },\n { additionalProperties: false },\n ),\n handler: async (params) => {\n const phoneNumber = requireE164(params, \"phoneNumber\");\n const countryCode = requireCountry(params, \"countryCode\");\n requireConfirmation(params);\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 \"Copy the exact current number into confirmPhoneNumber only after the user approves.\",\n parameters: Type.Object(\n {\n confirmPhoneNumber: Type.String({\n description: \"Exact currently assigned number, copied to confirm permanent release\",\n minLength: 8,\n maxLength: 16,\n pattern: E164_PATTERN,\n }),\n },\n { additionalProperties: false },\n ),\n handler: async (params) => {\n const confirmPhoneNumber = requireE164(params, \"confirmPhoneNumber\");\n const mobileClient = getClient();\n const current = await mobileClient.getMobileNumber();\n if (current.phoneNumber !== confirmPhoneNumber) {\n throw publicToolError(\"confirmPhoneNumber does not match the currently assigned number\");\n }\n return mobileClient.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 {\n to: Type.String({\n description: \"Recipient phone number in E.164 format (e.g., +12025551234)\",\n minLength: 8,\n maxLength: 16,\n pattern: E164_PATTERN,\n }),\n body: Type.String({\n description: \"The text message content to send\",\n minLength: 1,\n maxLength: MAX_SMS_LENGTH,\n }),\n },\n { additionalProperties: false },\n ),\n handler: async (params) => {\n const to = requireE164(params, \"to\");\n const body = requireString(params, \"body\", { maxLength: MAX_SMS_LENGTH });\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 {\n to: Type.String({\n description: \"Phone number to call in E.164 format (e.g., +12025551234)\",\n minLength: 8,\n maxLength: 16,\n pattern: E164_PATTERN,\n }),\n },\n { additionalProperties: false },\n ),\n handler: async (params) => {\n const to = requireE164(params, \"to\");\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 // 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(\n `Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((tool) => tool.name).join(\", \")}`,\n );\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 client configured\");\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":"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"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "@alfe.ai/openclaw-mobile",
|
|
3
3
|
"name": "Mobile",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Phone number management, outbound SMS, and calls via Twilio",
|
|
5
5
|
"entry": "./dist/plugin.js",
|
|
6
6
|
"activation": { "onStartup": false },
|
|
7
7
|
"contracts": {
|
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.27",
|
|
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.15.0",
|
|
32
|
+
"@alfe.ai/config": "0.4.1",
|
|
33
|
+
"@alfe.ai/openclaw-plugin-kit": "0.2.0"
|
|
33
34
|
},
|
|
34
35
|
"homepage": "https://alfe.ai",
|
|
35
36
|
"author": "Alfe (https://alfe.ai)",
|
|
@@ -45,6 +46,7 @@
|
|
|
45
46
|
"build": "tsdown",
|
|
46
47
|
"dev": "tsdown --watch",
|
|
47
48
|
"test": "vitest run --passWithNoTests",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
48
50
|
"lint": "eslint ."
|
|
49
51
|
}
|
|
50
52
|
}
|