@vama/openclaw 2026.5.5-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,700 @@
1
+ import { a as provisionBot, i as createBotHubClient, n as attachmentHintFromExtension } from "./client-AsD46gcK.js";
2
+ import { c as resolveVamaAccount, d as buildBaseChannelStatusSummary, f as createDefaultChannelRuntimeState, l as DEFAULT_ACCOUNT_ID$1, n as monitorVamaProvider, o as listVamaAccountIds, r as sendMessageVama, s as resolveDefaultVamaAccountId, t as probeVama, u as PAIRING_APPROVED_MESSAGE } from "./probe-B2hFOc2Y.js";
3
+ import { t as getVamaRuntime } from "./runtime-w-1oL50p.js";
4
+ import { jsonResult, readStringOrNumberParam, readStringParam } from "openclaw/plugin-sdk/channel-actions";
5
+ import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
6
+ import { DEFAULT_ACCOUNT_ID, addWildcardAllowFrom, createStandardChannelSetupStatus, formatDocsLink, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
7
+ //#region extensions/vama/src/channel-actions.ts
8
+ /**
9
+ * Channel-actions adapter for the shared `message` agent tool.
10
+ *
11
+ * Vama bots previously had exactly one outbound-media mechanism: emit a
12
+ * `MEDIA:/path/to/file` line inside the assistant reply text, which
13
+ * `reply-dispatcher.ts` parses out and uploads via `client.sendFile`. That
14
+ * path still works (see `reply-dispatcher.ts:parseInlineMediaTargets`) and
15
+ * remains the default for normal turn replies — but it is brittle:
16
+ *
17
+ * - The model has to remember the exact format (line-start `MEDIA:` + path)
18
+ * - Code-fenced or quoted variants don't match the line-anchored regex
19
+ * - Long-context decay degrades format-directive following more than
20
+ * tool-call adherence on every frontier model
21
+ * - There is no schema validation; mistakes silently become text replies
22
+ *
23
+ * Registering a `ChannelMessageActionAdapter` here mirrors the pattern used
24
+ * by every other multimedia channel in openclaw (telegram, discord,
25
+ * matrix, etc.). It exposes a structured `message({action: "send", to:
26
+ * "<channelId>", media: "/path/to/file.html", caption: "..."})` tool call.
27
+ * The platform's `message-action-discovery` layer picks this up and
28
+ * publishes the right schema fragments on the agent's `message` tool. The
29
+ * model now has two paths:
30
+ *
31
+ * 1. (Preferred) Call the `message` tool with `media`/`path`/`filePath`
32
+ * — schema-validated, returns a typed `{messageId, channelId}` tool
33
+ * result the model can read.
34
+ * 2. (Fallback) Emit `MEDIA:/path` in plain text — `reply-dispatcher`
35
+ * still parses it.
36
+ *
37
+ * Both paths converge on the same `client.sendFile` underneath and the
38
+ * same BotHub `POST /v1/bot/messages/file` endpoint. Existing dedup,
39
+ * filename preservation, and attachment-hint inference are unchanged.
40
+ */
41
+ /**
42
+ * Extension-only file-extension helper. Duplicated from `outbound.ts` and
43
+ * `reply-dispatcher.ts` rather than factored out — those copies are
44
+ * intentionally narrow (basename-of-path slice, no normalisation) and we
45
+ * want this adapter to behave identically. If a future refactor centralises
46
+ * extension parsing in the plugin-SDK, all three sites can collapse.
47
+ */
48
+ function extensionOf$1(path) {
49
+ const idx = path.lastIndexOf(".");
50
+ if (idx < 0 || idx === path.length - 1) return "";
51
+ if (idx < Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"))) return "";
52
+ return path.slice(idx + 1).toLowerCase();
53
+ }
54
+ /**
55
+ * Resolve a media argument (typically the `media`, `path`, or `filePath`
56
+ * field of the tool call) to a local filesystem path that
57
+ * `BotHubClient.sendFile` can read. Mirrors `localPathFromMediaUrl` in
58
+ * `outbound.ts` and `reply-dispatcher.ts` so all three Vama outbound
59
+ * paths agree on which inputs count as "uploadable from disk".
60
+ *
61
+ * Remote URLs (`http://` / `https://`) return `null` — the outbound side
62
+ * doesn't fetch remote bytes yet (no SSRF guard, no allowlist), so the
63
+ * caller falls back to sending the URL inline as text. That matches the
64
+ * fallback `outbound.ts:sendMedia` already implements.
65
+ */
66
+ function localPathFromMediaArg(raw) {
67
+ if (raw.startsWith("file://")) return raw.slice(7);
68
+ if (raw.startsWith("/") || raw.startsWith("./")) return raw;
69
+ return null;
70
+ }
71
+ function describeVamaMessageTool({ cfg, accountId }) {
72
+ if (!resolveVamaAccount({
73
+ cfg,
74
+ accountId: accountId ?? void 0
75
+ }).configured) return {
76
+ actions: [],
77
+ capabilities: [],
78
+ schema: null
79
+ };
80
+ return {
81
+ actions: ["send"],
82
+ capabilities: [],
83
+ schema: null
84
+ };
85
+ }
86
+ /**
87
+ * Read the channel/conversation target from the tool params. The model can
88
+ * supply any of `to` / `channelId` / `target` (the last is the routing
89
+ * schema's canonical name). Accept all three so a structured caller can
90
+ * use whichever maps best to their context.
91
+ */
92
+ function readVamaTarget(params) {
93
+ const value = readStringOrNumberParam(params, "to") ?? readStringOrNumberParam(params, "channelId") ?? readStringOrNumberParam(params, "target", { required: true });
94
+ if (!value) throw new Error("Vama send: target channel id required (use `to` or `channelId`)");
95
+ return value;
96
+ }
97
+ /**
98
+ * Read the media argument the model supplied. The shared message tool
99
+ * exposes three optional fields that can carry an attachment path: `media`
100
+ * is openclaw's canonical name, `path` and `filePath` are the
101
+ * historical aliases. Accept all three so we don't second-guess which
102
+ * name the model picks.
103
+ */
104
+ function readVamaMediaArg(params) {
105
+ return readStringParam(params, "media") ?? readStringParam(params, "path") ?? readStringParam(params, "filePath");
106
+ }
107
+ function readVamaCaption(params) {
108
+ return readStringParam(params, "caption") ?? readStringParam(params, "message");
109
+ }
110
+ function readVamaParentId(params) {
111
+ return readStringOrNumberParam(params, "replyTo") ?? readStringOrNumberParam(params, "replyToMessageId") ?? readStringOrNumberParam(params, "parentId");
112
+ }
113
+ const vamaMessageActions = {
114
+ describeMessageTool: describeVamaMessageTool,
115
+ resolveExecutionMode: () => "gateway",
116
+ extractToolSend: ({ args }) => extractToolSend(args, "send"),
117
+ handleAction: async ({ action, params, cfg, accountId }) => {
118
+ if (action !== "send") throw new Error(`Unsupported Vama action: ${action}`);
119
+ const account = resolveVamaAccount({
120
+ cfg,
121
+ accountId: accountId ?? void 0
122
+ });
123
+ if (!account.configured) throw new Error(`Vama account "${account.accountId}" not configured`);
124
+ const to = readVamaTarget(params);
125
+ const mediaArg = readVamaMediaArg(params);
126
+ const caption = readVamaCaption(params);
127
+ const parentId = readVamaParentId(params);
128
+ if (!mediaArg) {
129
+ const result = await sendMessageVama({
130
+ cfg,
131
+ to,
132
+ text: caption ?? "",
133
+ accountId: account.accountId,
134
+ parentId
135
+ });
136
+ return jsonResult({
137
+ ok: true,
138
+ messageId: result.messageId,
139
+ channelId: result.channelId
140
+ });
141
+ }
142
+ const localPath = localPathFromMediaArg(mediaArg);
143
+ if (!localPath) {
144
+ const result = await sendMessageVama({
145
+ cfg,
146
+ to,
147
+ text: caption?.trim() ? `${caption.trim()}\n${mediaArg}` : mediaArg,
148
+ accountId: account.accountId,
149
+ parentId
150
+ });
151
+ return jsonResult({
152
+ ok: true,
153
+ messageId: result.messageId,
154
+ channelId: result.channelId,
155
+ warning: "remote_media_unsupported_sent_as_link"
156
+ });
157
+ }
158
+ const result = await createBotHubClient(account).sendFile({
159
+ channelId: to,
160
+ path: localPath,
161
+ caption: caption?.trim() || void 0,
162
+ attachmentType: attachmentHintFromExtension(extensionOf$1(localPath)),
163
+ parentId
164
+ });
165
+ return jsonResult({
166
+ ok: true,
167
+ messageId: result.message_id,
168
+ channelId: result.channel_id
169
+ });
170
+ }
171
+ };
172
+ //#endregion
173
+ //#region extensions/vama/src/outbound.ts
174
+ /**
175
+ * Resolve a media URL/path to a local filesystem path. The reply-dispatcher
176
+ * normalises agent output through `splitMediaFromOutput` before reaching
177
+ * the outbound adapter, so URLs are usually already absolute paths to
178
+ * artefacts under the agent workspace (e.g.
179
+ * `/home/app/.openclaw/workspace/output/foo.pdf`). Remote URLs (`http*`)
180
+ * are not supported on the outbound side yet — we'd have to download them
181
+ * first, which raises an SSRF question we'd rather defer until there's a
182
+ * real producer. Returning `null` here causes the adapter to fall back to
183
+ * sending the URL as plain text, preserving the legacy behaviour.
184
+ */
185
+ function localPathFromMediaUrl(mediaUrl) {
186
+ if (mediaUrl.startsWith("file://")) return mediaUrl.slice(7);
187
+ if (mediaUrl.startsWith("/") || mediaUrl.startsWith("./")) return mediaUrl;
188
+ return null;
189
+ }
190
+ function extensionOf(path) {
191
+ const idx = path.lastIndexOf(".");
192
+ if (idx < 0 || idx === path.length - 1) return "";
193
+ if (idx < Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"))) return "";
194
+ return path.slice(idx + 1).toLowerCase();
195
+ }
196
+ const vamaOutbound = {
197
+ deliveryMode: "direct",
198
+ chunker: (text, limit) => getVamaRuntime().channel.text.chunkMarkdownText(text, limit),
199
+ chunkerMode: "markdown",
200
+ textChunkLimit: 1e4,
201
+ sendText: async ({ cfg, to, text, accountId }) => {
202
+ const result = await sendMessageVama({
203
+ cfg,
204
+ to,
205
+ text,
206
+ accountId: accountId ?? void 0
207
+ });
208
+ return {
209
+ channel: "vama",
210
+ messageId: result.messageId,
211
+ channelId: result.channelId
212
+ };
213
+ },
214
+ sendMedia: async ({ cfg, to, text, mediaUrl, accountId }) => {
215
+ const account = resolveVamaAccount({
216
+ cfg,
217
+ accountId: accountId ?? void 0
218
+ });
219
+ if (!account.configured) throw new Error(`Vama account "${account.accountId}" not configured`);
220
+ if (!mediaUrl) {
221
+ const result = await sendMessageVama({
222
+ cfg,
223
+ to,
224
+ text: text ?? "",
225
+ accountId: accountId ?? void 0
226
+ });
227
+ return {
228
+ channel: "vama",
229
+ messageId: result.messageId,
230
+ channelId: result.channelId
231
+ };
232
+ }
233
+ const localPath = localPathFromMediaUrl(mediaUrl);
234
+ if (!localPath) {
235
+ const result = await sendMessageVama({
236
+ cfg,
237
+ to,
238
+ text: text && text.trim().length > 0 ? `${text.trim()}\n${mediaUrl}` : mediaUrl,
239
+ accountId: accountId ?? void 0
240
+ });
241
+ return {
242
+ channel: "vama",
243
+ messageId: result.messageId,
244
+ channelId: result.channelId
245
+ };
246
+ }
247
+ const result = await createBotHubClient(account).sendFile({
248
+ channelId: to,
249
+ path: localPath,
250
+ caption: text?.trim() || void 0,
251
+ attachmentType: attachmentHintFromExtension(extensionOf(localPath))
252
+ });
253
+ return {
254
+ channel: "vama",
255
+ messageId: result.message_id,
256
+ channelId: result.channel_id
257
+ };
258
+ }
259
+ };
260
+ //#endregion
261
+ //#region extensions/vama/src/setup-surface.ts
262
+ const channel = "vama";
263
+ function setVamaDmPolicy(cfg, dmPolicy) {
264
+ const allowFrom = dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.vama?.allowFrom)?.map((entry) => entry) : void 0;
265
+ return {
266
+ ...cfg,
267
+ channels: {
268
+ ...cfg.channels,
269
+ vama: {
270
+ ...cfg.channels?.vama,
271
+ dmPolicy,
272
+ ...allowFrom ? { allowFrom } : {}
273
+ }
274
+ }
275
+ };
276
+ }
277
+ function setVamaAllowFrom(cfg, allowFrom) {
278
+ return {
279
+ ...cfg,
280
+ channels: {
281
+ ...cfg.channels,
282
+ vama: {
283
+ ...cfg.channels?.vama,
284
+ allowFrom
285
+ }
286
+ }
287
+ };
288
+ }
289
+ function parseAllowFromInput(raw) {
290
+ return raw.split(/[\n,;]+/g).map((entry) => entry.trim()).filter(Boolean);
291
+ }
292
+ async function promptVamaAllowFrom(params) {
293
+ const existing = params.cfg.channels?.vama?.allowFrom ?? [];
294
+ await params.prompter.note(["Allowlist Vama DMs by user ID.", "Enter one or more Vama user IDs, separated by commas."].join("\n"), "Vama allowlist");
295
+ while (true) {
296
+ const parts = parseAllowFromInput(await params.prompter.text({
297
+ message: "Vama allowFrom (user IDs)",
298
+ placeholder: "user_xxxxx, user_yyyyy",
299
+ initialValue: existing[0] ? String(existing[0]) : void 0,
300
+ validate: (value) => (value ?? "").trim() ? void 0 : "Required"
301
+ }));
302
+ if (parts.length === 0) {
303
+ await params.prompter.note("Enter at least one user.", "Vama allowlist");
304
+ continue;
305
+ }
306
+ const unique = [...new Set([...existing.map((v) => String(v).trim()).filter(Boolean), ...parts])];
307
+ return setVamaAllowFrom(params.cfg, unique);
308
+ }
309
+ }
310
+ const vamaDmPolicy = {
311
+ label: "Vama",
312
+ channel,
313
+ policyKey: "channels.vama.dmPolicy",
314
+ allowFromKey: "channels.vama.allowFrom",
315
+ getCurrent: (cfg) => (cfg.channels?.vama)?.dmPolicy ?? "open",
316
+ setPolicy: (cfg, policy) => setVamaDmPolicy(cfg, policy),
317
+ promptAllowFrom: promptVamaAllowFrom
318
+ };
319
+ const vamaPlugin = {
320
+ id: "vama",
321
+ meta: {
322
+ id: "vama",
323
+ label: "Vama",
324
+ selectionLabel: "Vama",
325
+ docsPath: "/channels/vama",
326
+ docsLabel: "vama",
327
+ blurb: "Vama chat integration via BotHub.",
328
+ order: 80
329
+ },
330
+ pairing: {
331
+ idLabel: "vamaUserId",
332
+ normalizeAllowEntry: (entry) => entry.trim(),
333
+ notifyApproval: async ({ cfg, id }) => {
334
+ const account = resolveVamaAccount({ cfg });
335
+ if (!account.configured) return;
336
+ try {
337
+ const { createBotHubClient } = await import("./client-AsD46gcK.js").then((n) => n.r);
338
+ await createBotHubClient(account).sendMessage({
339
+ userId: id,
340
+ text: PAIRING_APPROVED_MESSAGE
341
+ });
342
+ } catch {}
343
+ }
344
+ },
345
+ capabilities: {
346
+ chatTypes: ["direct"],
347
+ polls: false,
348
+ threads: true,
349
+ media: true,
350
+ reactions: false,
351
+ edit: false,
352
+ reply: true
353
+ },
354
+ reload: { configPrefixes: ["channels.vama"] },
355
+ configSchema: { schema: {
356
+ type: "object",
357
+ additionalProperties: false,
358
+ properties: {
359
+ enabled: { type: "boolean" },
360
+ botToken: { type: "string" },
361
+ webhookSecret: { type: "string" },
362
+ webhookSecretFile: { type: "string" },
363
+ bothubUrl: { type: "string" },
364
+ webhookPath: { type: "string" },
365
+ webhookPort: {
366
+ type: "integer",
367
+ minimum: 1
368
+ },
369
+ webhookHost: { type: "string" },
370
+ dmPolicy: {
371
+ type: "string",
372
+ enum: [
373
+ "open",
374
+ "pairing",
375
+ "allowlist"
376
+ ]
377
+ },
378
+ allowFrom: {
379
+ type: "array",
380
+ items: { oneOf: [{ type: "string" }, { type: "number" }] }
381
+ },
382
+ textChunkLimit: {
383
+ type: "integer",
384
+ minimum: 1
385
+ },
386
+ accounts: {
387
+ type: "object",
388
+ additionalProperties: {
389
+ type: "object",
390
+ properties: {
391
+ enabled: { type: "boolean" },
392
+ name: { type: "string" },
393
+ botToken: { type: "string" },
394
+ webhookSecret: { type: "string" },
395
+ webhookSecretFile: { type: "string" },
396
+ bothubUrl: { type: "string" },
397
+ webhookPath: { type: "string" },
398
+ webhookPort: {
399
+ type: "integer",
400
+ minimum: 1
401
+ },
402
+ webhookHost: { type: "string" }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ } },
408
+ config: {
409
+ listAccountIds: (cfg) => listVamaAccountIds(cfg),
410
+ resolveAccount: (cfg, accountId) => resolveVamaAccount({
411
+ cfg,
412
+ accountId
413
+ }),
414
+ defaultAccountId: (cfg) => resolveDefaultVamaAccountId(cfg),
415
+ setAccountEnabled: ({ cfg, accountId, enabled }) => {
416
+ if (accountId === DEFAULT_ACCOUNT_ID$1) return {
417
+ ...cfg,
418
+ channels: {
419
+ ...cfg.channels,
420
+ vama: {
421
+ ...cfg.channels?.vama,
422
+ enabled
423
+ }
424
+ }
425
+ };
426
+ const vamaCfg = cfg.channels?.vama;
427
+ return {
428
+ ...cfg,
429
+ channels: {
430
+ ...cfg.channels,
431
+ vama: {
432
+ ...vamaCfg,
433
+ accounts: {
434
+ ...vamaCfg?.accounts,
435
+ [accountId]: {
436
+ ...vamaCfg?.accounts?.[accountId],
437
+ enabled
438
+ }
439
+ }
440
+ }
441
+ }
442
+ };
443
+ },
444
+ deleteAccount: ({ cfg, accountId }) => {
445
+ if (accountId === DEFAULT_ACCOUNT_ID$1) {
446
+ const next = { ...cfg };
447
+ const nextChannels = { ...cfg.channels };
448
+ delete nextChannels.vama;
449
+ if (Object.keys(nextChannels).length > 0) next.channels = nextChannels;
450
+ else delete next.channels;
451
+ return next;
452
+ }
453
+ const vamaCfg = cfg.channels?.vama;
454
+ const accounts = { ...vamaCfg?.accounts };
455
+ delete accounts[accountId];
456
+ return {
457
+ ...cfg,
458
+ channels: {
459
+ ...cfg.channels,
460
+ vama: {
461
+ ...vamaCfg,
462
+ accounts: Object.keys(accounts).length > 0 ? accounts : void 0
463
+ }
464
+ }
465
+ };
466
+ },
467
+ isConfigured: (account) => account.configured,
468
+ describeAccount: (account) => ({
469
+ accountId: account.accountId,
470
+ enabled: account.enabled,
471
+ configured: account.configured,
472
+ name: account.name,
473
+ bothubUrl: account.bothubUrl
474
+ }),
475
+ resolveAllowFrom: ({ cfg, accountId }) => {
476
+ return (resolveVamaAccount({
477
+ cfg,
478
+ accountId
479
+ }).config?.allowFrom ?? []).map((entry) => String(entry));
480
+ },
481
+ formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => String(entry).trim()).filter(Boolean)
482
+ },
483
+ security: { collectWarnings: ({ cfg, accountId }) => {
484
+ const account = resolveVamaAccount({
485
+ cfg,
486
+ accountId
487
+ });
488
+ if ((account.config?.dmPolicy ?? "open") !== "open") return [];
489
+ return [`- Vama[${account.accountId}]: dmPolicy="open" allows any user to DM the bot. Set channels.vama.dmPolicy="allowlist" + channels.vama.allowFrom to restrict senders.`];
490
+ } },
491
+ setup: {
492
+ resolveAccountId: () => DEFAULT_ACCOUNT_ID$1,
493
+ applyAccountConfig: ({ cfg, accountId }) => {
494
+ if (!accountId || accountId === DEFAULT_ACCOUNT_ID$1) return {
495
+ ...cfg,
496
+ channels: {
497
+ ...cfg.channels,
498
+ vama: {
499
+ ...cfg.channels?.vama,
500
+ enabled: true
501
+ }
502
+ }
503
+ };
504
+ const vamaCfg = cfg.channels?.vama;
505
+ return {
506
+ ...cfg,
507
+ channels: {
508
+ ...cfg.channels,
509
+ vama: {
510
+ ...vamaCfg,
511
+ accounts: {
512
+ ...vamaCfg?.accounts,
513
+ [accountId]: {
514
+ ...vamaCfg?.accounts?.[accountId],
515
+ enabled: true
516
+ }
517
+ }
518
+ }
519
+ }
520
+ };
521
+ }
522
+ },
523
+ setupWizard: {
524
+ channel,
525
+ status: createStandardChannelSetupStatus({
526
+ channelLabel: "Vama",
527
+ configuredLabel: "configured",
528
+ unconfiguredLabel: "needs BotHub credentials",
529
+ configuredHint: "configured",
530
+ unconfiguredHint: "needs credentials",
531
+ configuredScore: 2,
532
+ unconfiguredScore: 0,
533
+ includeStatusLine: true,
534
+ resolveConfigured: ({ cfg }) => resolveVamaAccount({ cfg }).configured
535
+ }),
536
+ introNote: {
537
+ title: "Vama setup",
538
+ lines: [
539
+ "Vama needs BotHub credentials.",
540
+ "You will be prompted for your Vama username to auto-provision a bot.",
541
+ `Docs: ${formatDocsLink("/channels/vama", "channels/vama")}`
542
+ ],
543
+ shouldShow: ({ cfg }) => !resolveVamaAccount({ cfg }).configured
544
+ },
545
+ resolveAccountIdForConfigure: () => DEFAULT_ACCOUNT_ID,
546
+ resolveShouldPromptAccountIds: () => false,
547
+ credentials: [],
548
+ finalize: async ({ cfg, prompter }) => {
549
+ const vamaCfg = cfg.channels?.vama;
550
+ const hasCredentials = Boolean(vamaCfg?.botToken?.trim());
551
+ let next = cfg;
552
+ if (hasCredentials) {
553
+ if (await prompter.confirm({
554
+ message: "Vama credentials already configured. Keep them?",
555
+ initialValue: true
556
+ })) {
557
+ next = {
558
+ ...next,
559
+ channels: {
560
+ ...next.channels,
561
+ vama: {
562
+ ...next.channels?.vama,
563
+ enabled: true
564
+ }
565
+ }
566
+ };
567
+ return { cfg: next };
568
+ }
569
+ }
570
+ const bothubUrl = vamaCfg?.bothubUrl?.trim() || "https://bothub.vama.com";
571
+ const username = (await prompter.text({
572
+ message: "Your Vama username (for bot provisioning)",
573
+ validate: (value) => (value ?? "").trim() ? void 0 : "Required"
574
+ })).trim();
575
+ await prompter.note("Provisioning bot via BotHub...", "Vama setup");
576
+ let botToken;
577
+ let webhookSecret;
578
+ try {
579
+ const result = await provisionBot({
580
+ bothubUrl,
581
+ targetUsername: username
582
+ });
583
+ botToken = result.bot_token;
584
+ webhookSecret = result.webhook_secret;
585
+ const status = result.created ? "New bot created" : "Existing bot re-provisioned";
586
+ await prompter.note(`${status} (ID: ${result.bot_id})`, "Vama provisioning");
587
+ } catch (err) {
588
+ await prompter.note(`Provisioning failed: ${String(err)}\nYou can manually set botToken and webhookSecret in the config.`, "Vama provisioning error");
589
+ return { cfg: next };
590
+ }
591
+ const webhookPort = Number(await prompter.text({
592
+ message: "Webhook port (for receiving messages)",
593
+ initialValue: String(vamaCfg?.webhookPort ?? 3001),
594
+ validate: (value) => {
595
+ const n = Number(value);
596
+ if (Number.isNaN(n) || n < 1 || n > 65535) return "Must be a valid port (1-65535)";
597
+ }
598
+ }));
599
+ next = {
600
+ ...next,
601
+ channels: {
602
+ ...next.channels,
603
+ vama: {
604
+ ...next.channels?.vama,
605
+ enabled: true,
606
+ botToken,
607
+ webhookSecret,
608
+ bothubUrl,
609
+ webhookPort
610
+ }
611
+ }
612
+ };
613
+ const publicWebhookUrl = (await prompter.text({
614
+ message: "Public webhook URL for BotHub to send events (leave blank to skip)",
615
+ placeholder: "https://your-gateway-host.example.com/vama/events",
616
+ initialValue: "",
617
+ validate: (value) => {
618
+ const v = (value ?? "").trim();
619
+ if (!v) return;
620
+ try {
621
+ new URL(v);
622
+ return;
623
+ } catch {
624
+ return "Must be a valid URL (or leave blank to skip)";
625
+ }
626
+ }
627
+ })).trim();
628
+ if (publicWebhookUrl) try {
629
+ await createBotHubClient(resolveVamaAccount({ cfg: next })).registerWebhook({ url: publicWebhookUrl });
630
+ await prompter.note(`Webhook registered: ${publicWebhookUrl}`, "Vama webhook registration");
631
+ } catch (err) {
632
+ await prompter.note(`Webhook registration failed: ${String(err)}\nYou can register later via the BotHub API.`, "Vama webhook registration");
633
+ }
634
+ else await prompter.note("Skipped webhook registration. Run the setup wizard again once your gateway is publicly reachable to register a webhook URL.", "Vama webhook registration");
635
+ const account = resolveVamaAccount({ cfg: next });
636
+ try {
637
+ const probe = await probeVama(account);
638
+ if (probe.ok) await prompter.note(`Connected successfully (bot ${probe.botId ?? "unknown"})`, "Vama connection test");
639
+ else await prompter.note(`Connection test failed: ${probe.error ?? "unknown error"}`, "Vama connection test");
640
+ } catch (err) {
641
+ await prompter.note(`Connection test failed: ${String(err)}`, "Vama connection test");
642
+ }
643
+ return { cfg: next };
644
+ },
645
+ completionNote: {
646
+ title: "Vama next steps",
647
+ lines: [
648
+ "Next: restart gateway and verify status.",
649
+ "Command: openclaw channels status --probe",
650
+ `Docs: ${formatDocsLink("/channels/vama", "channels/vama")}`
651
+ ]
652
+ },
653
+ dmPolicy: vamaDmPolicy,
654
+ disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
655
+ },
656
+ outbound: vamaOutbound,
657
+ actions: vamaMessageActions,
658
+ status: {
659
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID$1, { port: null }),
660
+ buildChannelSummary: ({ snapshot }) => ({
661
+ ...buildBaseChannelStatusSummary(snapshot),
662
+ port: snapshot.port ?? null,
663
+ probe: snapshot.probe,
664
+ lastProbeAt: snapshot.lastProbeAt ?? null
665
+ }),
666
+ probeAccount: async ({ account }) => await probeVama(account),
667
+ buildAccountSnapshot: ({ account, runtime, probe }) => ({
668
+ accountId: account.accountId,
669
+ enabled: account.enabled,
670
+ configured: account.configured,
671
+ name: account.name,
672
+ bothubUrl: account.bothubUrl,
673
+ running: runtime?.running ?? false,
674
+ lastStartAt: runtime?.lastStartAt ?? null,
675
+ lastStopAt: runtime?.lastStopAt ?? null,
676
+ lastError: runtime?.lastError ?? null,
677
+ port: runtime?.port ?? null,
678
+ probe
679
+ })
680
+ },
681
+ gateway: { startAccount: async (ctx) => {
682
+ const port = resolveVamaAccount({
683
+ cfg: ctx.cfg,
684
+ accountId: ctx.accountId
685
+ }).config?.webhookPort ?? null;
686
+ ctx.setStatus({
687
+ accountId: ctx.accountId,
688
+ port
689
+ });
690
+ ctx.log?.info(`starting vama[${ctx.accountId}]`);
691
+ return monitorVamaProvider({
692
+ config: ctx.cfg,
693
+ runtime: ctx.runtime,
694
+ abortSignal: ctx.abortSignal,
695
+ accountId: ctx.accountId
696
+ });
697
+ } }
698
+ };
699
+ //#endregion
700
+ export { vamaPlugin as t };
@@ -0,0 +1,2 @@
1
+ import { t as vamaPlugin } from "./channel-plugin-api-CcZ_y9pT.js";
2
+ export { vamaPlugin };