@alfe.ai/openclaw-mobile 0.0.26 → 0.0.28

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/plugin.cjs CHANGED
@@ -1,7 +1,8 @@
1
- let _sinclair_typebox = require("@sinclair/typebox");
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
4
  let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
5
+ let _sinclair_typebox = require("@sinclair/typebox");
5
6
  //#region src/plugin.ts
6
7
  /**
7
8
  * @alfe/openclaw-mobile — OpenClaw native plugin
@@ -17,7 +18,65 @@ let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
17
18
  * - mobile_send_sms — send an SMS message from the agent's phone number
18
19
  * - mobile_call — initiate an outbound phone call
19
20
  */
20
- const pkg = (0, require("node:module").createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
21
+ const pkg = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
22
+ const SUPPORTED_COUNTRIES = [
23
+ "US",
24
+ "CA",
25
+ "AU",
26
+ "GB",
27
+ "AT",
28
+ "BE",
29
+ "CH",
30
+ "CZ",
31
+ "DE",
32
+ "DK",
33
+ "ES",
34
+ "FI",
35
+ "FR",
36
+ "IE",
37
+ "IT",
38
+ "NL",
39
+ "NO",
40
+ "PL",
41
+ "PT",
42
+ "SE"
43
+ ];
44
+ const E164_PATTERN = "^\\+\\d{7,15}$";
45
+ const E164_RE = /^\+\d{7,15}$/u;
46
+ const NUMBER_SEARCH_RE = /^[A-Za-z0-9*+ -]+$/u;
47
+ const MAX_SEARCH_LENGTH = 32;
48
+ const MAX_SMS_LENGTH = 4400;
49
+ function requireString(params, field, options) {
50
+ const value = params[field];
51
+ 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`);
52
+ if (options.pattern && !options.pattern.test(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${field} has an invalid format`);
53
+ return value;
54
+ }
55
+ function optionalSearchQuery(params) {
56
+ if (params.query === void 0) return void 0;
57
+ return requireString(params, "query", {
58
+ maxLength: MAX_SEARCH_LENGTH,
59
+ pattern: NUMBER_SEARCH_RE
60
+ });
61
+ }
62
+ function optionalCountry(params) {
63
+ if (params.country === void 0) return void 0;
64
+ return requireCountry(params, "country");
65
+ }
66
+ function requireCountry(params, field) {
67
+ const value = params[field];
68
+ if (typeof value !== "string" || !SUPPORTED_COUNTRIES.includes(value)) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)(`${field} must be one of ${SUPPORTED_COUNTRIES.join(", ")}`);
69
+ return value;
70
+ }
71
+ function requireE164(params, field) {
72
+ return requireString(params, field, {
73
+ maxLength: 16,
74
+ pattern: E164_RE
75
+ });
76
+ }
77
+ function requireConfirmation(params) {
78
+ if (params.confirm !== true) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("confirm must be true after the user explicitly approves the recurring charge");
79
+ }
21
80
  const MOBILE_ACTIVATION_KEY = (0, _alfe_ai_openclaw_plugin_kit.getActivationKey)("mobile");
22
81
  let client;
23
82
  function getClient() {
@@ -28,20 +87,26 @@ const mobileTools = [
28
87
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
29
88
  name: "mobile_get_number",
30
89
  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.",
31
- parameters: _sinclair_typebox.Type.Object({}),
90
+ parameters: _sinclair_typebox.Type.Object({}, { additionalProperties: false }),
32
91
  handler: async () => {
33
92
  return getClient().getMobileNumber();
34
93
  }
35
94
  }),
36
95
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
37
96
  name: "mobile_search_numbers",
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
+ description: "Search for available phone numbers that can be purchased. Returns a list of available numbers and the monthly price. Supported countries: US, CA, AU, GB, and most of Europe (AT, BE, CH, CZ, DE, DK, ES, FI, FR, IE, IT, NL, NO, PL, PT, SE). European countries require an approved Twilio regulatory bundle before purchase.",
39
98
  parameters: _sinclair_typebox.Type.Object({
40
- country: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({ description: "Country code: AU, US, CA, or GB (default: AU)" })),
41
- query: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({ description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')" }))
42
- }),
99
+ country: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.Union(SUPPORTED_COUNTRIES.map((country) => _sinclair_typebox.Type.Literal(country)), { description: "ISO country code, e.g. US, GB, SE, DE (default: US)" })),
100
+ query: _sinclair_typebox.Type.Optional(_sinclair_typebox.Type.String({
101
+ description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')",
102
+ minLength: 1,
103
+ maxLength: MAX_SEARCH_LENGTH,
104
+ pattern: NUMBER_SEARCH_RE.source
105
+ }))
106
+ }, { additionalProperties: false }),
43
107
  handler: async (params) => {
44
- const { country, query } = params;
108
+ const country = optionalCountry(params);
109
+ const query = optionalSearchQuery(params);
45
110
  return getClient().searchMobileNumbers({
46
111
  country,
47
112
  query
@@ -50,13 +115,21 @@ const mobileTools = [
50
115
  }),
51
116
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
52
117
  name: "mobile_assign_number",
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.",
118
+ 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.",
54
119
  parameters: _sinclair_typebox.Type.Object({
55
- phoneNumber: _sinclair_typebox.Type.String({ description: "The phone number to purchase (from mobile_search_numbers results)" }),
56
- countryCode: _sinclair_typebox.Type.String({ description: "Country code: AU, US, CA, or GB" })
57
- }),
120
+ phoneNumber: _sinclair_typebox.Type.String({
121
+ description: "The phone number to purchase (from mobile_search_numbers results)",
122
+ minLength: 8,
123
+ maxLength: 16,
124
+ pattern: E164_PATTERN
125
+ }),
126
+ countryCode: _sinclair_typebox.Type.Union(SUPPORTED_COUNTRIES.map((country) => _sinclair_typebox.Type.Literal(country)), { description: "ISO country code matching the searched number, e.g. US, GB, SE, DE" }),
127
+ confirm: _sinclair_typebox.Type.Literal(true, { description: "Must be true after the user explicitly approves the recurring charge" })
128
+ }, { additionalProperties: false }),
58
129
  handler: async (params) => {
59
- const { phoneNumber, countryCode } = params;
130
+ const phoneNumber = requireE164(params, "phoneNumber");
131
+ const countryCode = requireCountry(params, "countryCode");
132
+ requireConfirmation(params);
60
133
  return getClient().assignMobileNumber({
61
134
  phoneNumber,
62
135
  countryCode
@@ -65,21 +138,39 @@ const mobileTools = [
65
138
  }),
66
139
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
67
140
  name: "mobile_release_number",
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.",
69
- parameters: _sinclair_typebox.Type.Object({}),
70
- handler: async () => {
71
- return getClient().releaseMobileNumber();
141
+ 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.",
142
+ parameters: _sinclair_typebox.Type.Object({ confirmPhoneNumber: _sinclair_typebox.Type.String({
143
+ description: "Exact currently assigned number, copied to confirm permanent release",
144
+ minLength: 8,
145
+ maxLength: 16,
146
+ pattern: E164_PATTERN
147
+ }) }, { additionalProperties: false }),
148
+ handler: async (params) => {
149
+ const confirmPhoneNumber = requireE164(params, "confirmPhoneNumber");
150
+ const mobileClient = getClient();
151
+ if ((await mobileClient.getMobileNumber()).phoneNumber !== confirmPhoneNumber) throw (0, _alfe_ai_openclaw_plugin_kit.publicToolError)("confirmPhoneNumber does not match the currently assigned number");
152
+ return mobileClient.releaseMobileNumber();
72
153
  }
73
154
  }),
74
155
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
75
156
  name: "mobile_send_sms",
76
157
  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).",
77
158
  parameters: _sinclair_typebox.Type.Object({
78
- to: _sinclair_typebox.Type.String({ description: "Recipient phone number in E.164 format (e.g., +12025551234)" }),
79
- body: _sinclair_typebox.Type.String({ description: "The text message content to send" })
80
- }),
159
+ to: _sinclair_typebox.Type.String({
160
+ description: "Recipient phone number in E.164 format (e.g., +12025551234)",
161
+ minLength: 8,
162
+ maxLength: 16,
163
+ pattern: E164_PATTERN
164
+ }),
165
+ body: _sinclair_typebox.Type.String({
166
+ description: "The text message content to send",
167
+ minLength: 1,
168
+ maxLength: MAX_SMS_LENGTH
169
+ })
170
+ }, { additionalProperties: false }),
81
171
  handler: async (params) => {
82
- const { to, body } = params;
172
+ const to = requireE164(params, "to");
173
+ const body = requireString(params, "body", { maxLength: MAX_SMS_LENGTH });
83
174
  return getClient().sendSms({
84
175
  to,
85
176
  body
@@ -89,9 +180,14 @@ const mobileTools = [
89
180
  (0, _alfe_ai_openclaw_plugin_kit.defineTool)({
90
181
  name: "mobile_call",
91
182
  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).",
92
- parameters: _sinclair_typebox.Type.Object({ to: _sinclair_typebox.Type.String({ description: "Phone number to call in E.164 format (e.g., +12025551234)" }) }),
183
+ parameters: _sinclair_typebox.Type.Object({ to: _sinclair_typebox.Type.String({
184
+ description: "Phone number to call in E.164 format (e.g., +12025551234)",
185
+ minLength: 8,
186
+ maxLength: 16,
187
+ pattern: E164_PATTERN
188
+ }) }, { additionalProperties: false }),
93
189
  handler: async (params) => {
94
- const { to } = params;
190
+ const to = requireE164(params, "to");
95
191
  return getClient().startOutboundCall({ to });
96
192
  }
97
193
  })
@@ -105,7 +201,7 @@ const plugin = {
105
201
  (0, _alfe_ai_agent_api_client.installToolErrorCapture)(api, { plugin: "openclaw-mobile" });
106
202
  const log = api.logger;
107
203
  for (const tool of mobileTools) api.registerTool(tool);
108
- log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(", ")}`);
204
+ log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((tool) => tool.name).join(", ")}`);
109
205
  (0, _alfe_ai_openclaw_plugin_kit.guardedStart)(MOBILE_ACTIVATION_KEY, log, () => {
110
206
  log.info("Alfe Mobile plugin activating...");
111
207
  try {
@@ -114,7 +210,7 @@ const plugin = {
114
210
  apiUrl: config.apiUrl,
115
211
  apiKey: config.apiKey
116
212
  });
117
- log.info(`Mobile API: ${config.apiUrl}`);
213
+ log.info("Mobile API client configured");
118
214
  } catch (err) {
119
215
  log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
120
216
  log.warn("Mobile tools will fail — no API config available");
package/dist/plugin.d.cts CHANGED
@@ -5,16 +5,6 @@ import { TSchema } from "@sinclair/typebox";
5
5
  //# sourceMappingURL=types.d.ts.map
6
6
  //#endregion
7
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
8
  /** Shape returned to OpenClaw from a tool `execute`. */
19
9
  interface ToolResult {
20
10
  content: {
@@ -22,15 +12,8 @@ interface ToolResult {
22
12
  text: string;
23
13
  }[];
24
14
  details: unknown;
15
+ isError?: boolean;
25
16
  }
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
17
  interface ToolDef<TParameters = unknown> {
35
18
  name: string;
36
19
  description: string;
@@ -38,7 +21,7 @@ interface ToolDef<TParameters = unknown> {
38
21
  parameters: TParameters;
39
22
  execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
40
23
  }
41
- /** Wrap a successful handler result in the OpenClaw tool-result envelope. */
24
+ /** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
42
25
  //#endregion
43
26
  //#region src/plugin.d.ts
44
27
 
@@ -1 +1 @@
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"}
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":";;;;ACgEgB;;;;UD7CNC,UAAAA,CCuDW;SACkD,EAAA;QAG3B,EAAA,MAAA;IAAO,IAAA,EAAA,MAAA;EAAA,CAAA,EA8O7C;EA8CL,OAAA,EAAA,OAAA;SAxCe,CAAA,EAAA,OAAA;;UDvSNC,OCyUyB,CAAA,cAAA,OAAA,CAAA,CAAA;;;;cDrUrBC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCgClE,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;EA8O7C,MAAA,EApPI,MAkST;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAxCe,CAAA,IAAA,EAxPK,OAwPL,CAxPa,OAwPb,CAAA,CAAA,EAAA,IAAA;uBAkCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAzRqD,OAyRrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAtRS,OAsRT,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAxC7B;;;;;gBAMU;kBAkCE"}
package/dist/plugin.d.ts CHANGED
@@ -5,16 +5,6 @@ import { TSchema } from "@sinclair/typebox";
5
5
  //# sourceMappingURL=types.d.ts.map
6
6
  //#endregion
7
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
8
  /** Shape returned to OpenClaw from a tool `execute`. */
19
9
  interface ToolResult {
20
10
  content: {
@@ -22,15 +12,8 @@ interface ToolResult {
22
12
  text: string;
23
13
  }[];
24
14
  details: unknown;
15
+ isError?: boolean;
25
16
  }
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
17
  interface ToolDef<TParameters = unknown> {
35
18
  name: string;
36
19
  description: string;
@@ -38,7 +21,7 @@ interface ToolDef<TParameters = unknown> {
38
21
  parameters: TParameters;
39
22
  execute: (toolCallId: string, params: Record<string, unknown>) => Promise<ToolResult>;
40
23
  }
41
- /** Wrap a successful handler result in the OpenClaw tool-result envelope. */
24
+ /** Deliberately model-safe validation/usage failure. Other exceptions stay private. */
42
25
  //#endregion
43
26
  //#region src/plugin.d.ts
44
27
 
@@ -1 +1 @@
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"}
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":";;;;ACgEgB;;;;UD7CNC,UAAAA,CCuDW;SACkD,EAAA;QAG3B,EAAA,MAAA;IAAO,IAAA,EAAA,MAAA;EAAA,CAAA,EA8O7C;EA8CL,OAAA,EAAA,OAAA;SAxCe,CAAA,EAAA,OAAA;;UDvSNC,OCyUyB,CAAA,cAAA,OAAA,CAAA,CAAA;;;;cDrUrBC;wCAC0BC,4BAA4BC,QAAQJ;;;;;;UCgClE,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;EA8O7C,MAAA,EApPI,MAkST;EAAA,gBAAA,CAAA,EAAA,MAAA,GAAA,YAAA,GAAA,eAAA,GAAA,cAAA;cAxCe,CAAA,IAAA,EAxPK,OAwPL,CAxPa,OAwPb,CAAA,CAAA,EAAA,IAAA;uBAkCE,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAzRqD,OAyRrD,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;EAAiB,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,EAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,EAAA,GAAA,IAAA,GAtRS,OAsRT,CAAA,IAAA,CAAA,EAAA,QAAA,EAAA;;;;cAxC7B;;;;;gBAMU;kBAkCE"}
package/dist/plugin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createRequire } from "node:module";
2
- import { Type } from "@sinclair/typebox";
3
2
  import { resolveConfig } from "@alfe.ai/config";
4
3
  import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
5
- import { defineTool, getActivationKey, guardedStart, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
4
+ import { defineTool, getActivationKey, guardedStart, publicToolError, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
5
+ import { Type } from "@sinclair/typebox";
6
6
  //#region src/plugin.ts
7
7
  /**
8
8
  * @alfe/openclaw-mobile — OpenClaw native plugin
@@ -19,6 +19,64 @@ import { defineTool, getActivationKey, guardedStart, resetActivation } from "@al
19
19
  * - mobile_call — initiate an outbound phone call
20
20
  */
21
21
  const pkg = createRequire(import.meta.url)("../package.json");
22
+ const SUPPORTED_COUNTRIES = [
23
+ "US",
24
+ "CA",
25
+ "AU",
26
+ "GB",
27
+ "AT",
28
+ "BE",
29
+ "CH",
30
+ "CZ",
31
+ "DE",
32
+ "DK",
33
+ "ES",
34
+ "FI",
35
+ "FR",
36
+ "IE",
37
+ "IT",
38
+ "NL",
39
+ "NO",
40
+ "PL",
41
+ "PT",
42
+ "SE"
43
+ ];
44
+ const E164_PATTERN = "^\\+\\d{7,15}$";
45
+ const E164_RE = /^\+\d{7,15}$/u;
46
+ const NUMBER_SEARCH_RE = /^[A-Za-z0-9*+ -]+$/u;
47
+ const MAX_SEARCH_LENGTH = 32;
48
+ const MAX_SMS_LENGTH = 4400;
49
+ function requireString(params, field, options) {
50
+ const value = params[field];
51
+ 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`);
52
+ if (options.pattern && !options.pattern.test(value)) throw publicToolError(`${field} has an invalid format`);
53
+ return value;
54
+ }
55
+ function optionalSearchQuery(params) {
56
+ if (params.query === void 0) return void 0;
57
+ return requireString(params, "query", {
58
+ maxLength: MAX_SEARCH_LENGTH,
59
+ pattern: NUMBER_SEARCH_RE
60
+ });
61
+ }
62
+ function optionalCountry(params) {
63
+ if (params.country === void 0) return void 0;
64
+ return requireCountry(params, "country");
65
+ }
66
+ function requireCountry(params, field) {
67
+ const value = params[field];
68
+ if (typeof value !== "string" || !SUPPORTED_COUNTRIES.includes(value)) throw publicToolError(`${field} must be one of ${SUPPORTED_COUNTRIES.join(", ")}`);
69
+ return value;
70
+ }
71
+ function requireE164(params, field) {
72
+ return requireString(params, field, {
73
+ maxLength: 16,
74
+ pattern: E164_RE
75
+ });
76
+ }
77
+ function requireConfirmation(params) {
78
+ if (params.confirm !== true) throw publicToolError("confirm must be true after the user explicitly approves the recurring charge");
79
+ }
22
80
  const MOBILE_ACTIVATION_KEY = getActivationKey("mobile");
23
81
  let client;
24
82
  function getClient() {
@@ -29,20 +87,26 @@ const mobileTools = [
29
87
  defineTool({
30
88
  name: "mobile_get_number",
31
89
  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.",
32
- parameters: Type.Object({}),
90
+ parameters: Type.Object({}, { additionalProperties: false }),
33
91
  handler: async () => {
34
92
  return getClient().getMobileNumber();
35
93
  }
36
94
  }),
37
95
  defineTool({
38
96
  name: "mobile_search_numbers",
39
- 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
+ description: "Search for available phone numbers that can be purchased. Returns a list of available numbers and the monthly price. Supported countries: US, CA, AU, GB, and most of Europe (AT, BE, CH, CZ, DE, DK, ES, FI, FR, IE, IT, NL, NO, PL, PT, SE). European countries require an approved Twilio regulatory bundle before purchase.",
40
98
  parameters: Type.Object({
41
- country: Type.Optional(Type.String({ description: "Country code: AU, US, CA, or GB (default: AU)" })),
42
- query: Type.Optional(Type.String({ description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')" }))
43
- }),
99
+ country: Type.Optional(Type.Union(SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)), { description: "ISO country code, e.g. US, GB, SE, DE (default: US)" })),
100
+ query: Type.Optional(Type.String({
101
+ description: "Optional search pattern — area code or text to match (e.g., '415' or 'COOL')",
102
+ minLength: 1,
103
+ maxLength: MAX_SEARCH_LENGTH,
104
+ pattern: NUMBER_SEARCH_RE.source
105
+ }))
106
+ }, { additionalProperties: false }),
44
107
  handler: async (params) => {
45
- const { country, query } = params;
108
+ const country = optionalCountry(params);
109
+ const query = optionalSearchQuery(params);
46
110
  return getClient().searchMobileNumbers({
47
111
  country,
48
112
  query
@@ -51,13 +115,21 @@ const mobileTools = [
51
115
  }),
52
116
  defineTool({
53
117
  name: "mobile_assign_number",
54
- 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.",
118
+ 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.",
55
119
  parameters: Type.Object({
56
- phoneNumber: Type.String({ description: "The phone number to purchase (from mobile_search_numbers results)" }),
57
- countryCode: Type.String({ description: "Country code: AU, US, CA, or GB" })
58
- }),
120
+ phoneNumber: Type.String({
121
+ description: "The phone number to purchase (from mobile_search_numbers results)",
122
+ minLength: 8,
123
+ maxLength: 16,
124
+ pattern: E164_PATTERN
125
+ }),
126
+ countryCode: Type.Union(SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)), { description: "ISO country code matching the searched number, e.g. US, GB, SE, DE" }),
127
+ confirm: Type.Literal(true, { description: "Must be true after the user explicitly approves the recurring charge" })
128
+ }, { additionalProperties: false }),
59
129
  handler: async (params) => {
60
- const { phoneNumber, countryCode } = params;
130
+ const phoneNumber = requireE164(params, "phoneNumber");
131
+ const countryCode = requireCountry(params, "countryCode");
132
+ requireConfirmation(params);
61
133
  return getClient().assignMobileNumber({
62
134
  phoneNumber,
63
135
  countryCode
@@ -66,21 +138,39 @@ const mobileTools = [
66
138
  }),
67
139
  defineTool({
68
140
  name: "mobile_release_number",
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.",
70
- parameters: Type.Object({}),
71
- handler: async () => {
72
- return getClient().releaseMobileNumber();
141
+ 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.",
142
+ parameters: Type.Object({ confirmPhoneNumber: Type.String({
143
+ description: "Exact currently assigned number, copied to confirm permanent release",
144
+ minLength: 8,
145
+ maxLength: 16,
146
+ pattern: E164_PATTERN
147
+ }) }, { additionalProperties: false }),
148
+ handler: async (params) => {
149
+ const confirmPhoneNumber = requireE164(params, "confirmPhoneNumber");
150
+ const mobileClient = getClient();
151
+ if ((await mobileClient.getMobileNumber()).phoneNumber !== confirmPhoneNumber) throw publicToolError("confirmPhoneNumber does not match the currently assigned number");
152
+ return mobileClient.releaseMobileNumber();
73
153
  }
74
154
  }),
75
155
  defineTool({
76
156
  name: "mobile_send_sms",
77
157
  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).",
78
158
  parameters: Type.Object({
79
- to: Type.String({ description: "Recipient phone number in E.164 format (e.g., +12025551234)" }),
80
- body: Type.String({ description: "The text message content to send" })
81
- }),
159
+ to: Type.String({
160
+ description: "Recipient phone number in E.164 format (e.g., +12025551234)",
161
+ minLength: 8,
162
+ maxLength: 16,
163
+ pattern: E164_PATTERN
164
+ }),
165
+ body: Type.String({
166
+ description: "The text message content to send",
167
+ minLength: 1,
168
+ maxLength: MAX_SMS_LENGTH
169
+ })
170
+ }, { additionalProperties: false }),
82
171
  handler: async (params) => {
83
- const { to, body } = params;
172
+ const to = requireE164(params, "to");
173
+ const body = requireString(params, "body", { maxLength: MAX_SMS_LENGTH });
84
174
  return getClient().sendSms({
85
175
  to,
86
176
  body
@@ -90,9 +180,14 @@ const mobileTools = [
90
180
  defineTool({
91
181
  name: "mobile_call",
92
182
  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).",
93
- parameters: Type.Object({ to: Type.String({ description: "Phone number to call in E.164 format (e.g., +12025551234)" }) }),
183
+ parameters: Type.Object({ to: Type.String({
184
+ description: "Phone number to call in E.164 format (e.g., +12025551234)",
185
+ minLength: 8,
186
+ maxLength: 16,
187
+ pattern: E164_PATTERN
188
+ }) }, { additionalProperties: false }),
94
189
  handler: async (params) => {
95
- const { to } = params;
190
+ const to = requireE164(params, "to");
96
191
  return getClient().startOutboundCall({ to });
97
192
  }
98
193
  })
@@ -106,7 +201,7 @@ const plugin = {
106
201
  installToolErrorCapture(api, { plugin: "openclaw-mobile" });
107
202
  const log = api.logger;
108
203
  for (const tool of mobileTools) api.registerTool(tool);
109
- log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((t) => t.name).join(", ")}`);
204
+ log.info(`Registered ${String(mobileTools.length)} mobile tools: ${mobileTools.map((tool) => tool.name).join(", ")}`);
110
205
  guardedStart(MOBILE_ACTIVATION_KEY, log, () => {
111
206
  log.info("Alfe Mobile plugin activating...");
112
207
  try {
@@ -115,7 +210,7 @@ const plugin = {
115
210
  apiUrl: config.apiUrl,
116
211
  apiKey: config.apiKey
117
212
  });
118
- log.info(`Mobile API: ${config.apiUrl}`);
213
+ log.info("Mobile API client configured");
119
214
  } catch (err) {
120
215
  log.error(`Failed to resolve config: ${err instanceof Error ? err.message : String(err)}`);
121
216
  log.warn("Mobile tools will fail — no API config available");
@@ -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 { 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":"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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\n// Mirrors SUPPORTED_MOBILE_COUNTRY_CODES in @alfe/types (packages-internal/\n// types/src/mobile.ts) — this published package cannot depend on that private\n// workspace package, so keep the two lists in sync when adding countries.\nconst SUPPORTED_COUNTRIES = [\n \"US\",\n \"CA\",\n \"AU\",\n \"GB\",\n \"AT\",\n \"BE\",\n \"CH\",\n \"CZ\",\n \"DE\",\n \"DK\",\n \"ES\",\n \"FI\",\n \"FR\",\n \"IE\",\n \"IT\",\n \"NL\",\n \"NO\",\n \"PL\",\n \"PT\",\n \"SE\",\n] 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: US, CA, AU, GB, and most of Europe \" +\n \"(AT, BE, CH, CZ, DE, DK, ES, FI, FR, IE, IT, NL, NO, PL, PT, SE). \" +\n \"European countries require an approved Twilio regulatory bundle before purchase.\",\n parameters: Type.Object(\n {\n country: Type.Optional(\n Type.Union(\n SUPPORTED_COUNTRIES.map((country) => Type.Literal(country)),\n { description: \"ISO country code, e.g. US, GB, SE, DE (default: US)\" },\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: \"ISO country code matching the searched number, e.g. US, GB, SE, DE\" },\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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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "@alfe.ai/openclaw-mobile",
3
3
  "name": "Mobile",
4
- "description": "Outbound SMS and phone calls via Twilio",
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.26",
3
+ "version": "0.0.28",
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,9 +28,9 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@sinclair/typebox": "^0.34.48",
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"
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"
34
34
  },
35
35
  "homepage": "https://alfe.ai",
36
36
  "author": "Alfe (https://alfe.ai)",
@@ -46,6 +46,7 @@
46
46
  "build": "tsdown",
47
47
  "dev": "tsdown --watch",
48
48
  "test": "vitest run --passWithNoTests",
49
+ "typecheck": "tsc --noEmit",
49
50
  "lint": "eslint ."
50
51
  }
51
52
  }