@openclaw/zalo 2026.5.2 → 2026.5.3-beta.2

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.
Files changed (87) hide show
  1. package/dist/accounts-9NLDDlZ8.js +118 -0
  2. package/dist/actions.runtime-kJ65ZxW7.js +5 -0
  3. package/dist/api.js +5 -0
  4. package/dist/channel-VPbtV3Oq.js +343 -0
  5. package/dist/channel-plugin-api.js +2 -0
  6. package/dist/channel.runtime-BnTAWQx5.js +106 -0
  7. package/dist/contract-api.js +3 -0
  8. package/dist/group-access-DZR43lOR.js +30 -0
  9. package/dist/index.js +22 -0
  10. package/dist/monitor-DMysJBWa.js +823 -0
  11. package/dist/monitor.webhook-DqnuvgjV.js +175 -0
  12. package/dist/proxy-CY8VuC6H.js +135 -0
  13. package/dist/runtime-BRFxnYQx.js +8 -0
  14. package/dist/runtime-api-MOTmRW4F.js +19 -0
  15. package/dist/runtime-api.js +3 -0
  16. package/dist/secret-contract-Dw93tGo2.js +87 -0
  17. package/dist/secret-contract-api.js +2 -0
  18. package/dist/send-Gv3l5EGI.js +101 -0
  19. package/dist/setup-api.js +30 -0
  20. package/dist/setup-core-DigRD3j1.js +166 -0
  21. package/dist/setup-entry.js +15 -0
  22. package/dist/setup-surface-2Up3yWov.js +216 -0
  23. package/dist/test-api.js +2 -0
  24. package/package.json +15 -6
  25. package/api.ts +0 -9
  26. package/channel-plugin-api.ts +0 -1
  27. package/contract-api.ts +0 -5
  28. package/index.test.ts +0 -15
  29. package/index.ts +0 -20
  30. package/runtime-api.test.ts +0 -17
  31. package/runtime-api.ts +0 -75
  32. package/secret-contract-api.ts +0 -5
  33. package/setup-api.ts +0 -34
  34. package/setup-entry.ts +0 -13
  35. package/src/accounts.test.ts +0 -70
  36. package/src/accounts.ts +0 -60
  37. package/src/actions.runtime.ts +0 -5
  38. package/src/actions.test.ts +0 -32
  39. package/src/actions.ts +0 -62
  40. package/src/api.test.ts +0 -149
  41. package/src/api.ts +0 -265
  42. package/src/approval-auth.test.ts +0 -17
  43. package/src/approval-auth.ts +0 -25
  44. package/src/channel.directory.test.ts +0 -59
  45. package/src/channel.runtime.ts +0 -93
  46. package/src/channel.startup.test.ts +0 -101
  47. package/src/channel.ts +0 -275
  48. package/src/config-schema.test.ts +0 -30
  49. package/src/config-schema.ts +0 -29
  50. package/src/group-access.ts +0 -49
  51. package/src/monitor.group-policy.test.ts +0 -94
  52. package/src/monitor.image.polling.test.ts +0 -110
  53. package/src/monitor.lifecycle.test.ts +0 -198
  54. package/src/monitor.pairing.lifecycle.test.ts +0 -141
  55. package/src/monitor.polling.media-reply.test.ts +0 -425
  56. package/src/monitor.reply-once.lifecycle.test.ts +0 -171
  57. package/src/monitor.ts +0 -1028
  58. package/src/monitor.types.ts +0 -4
  59. package/src/monitor.webhook.test.ts +0 -806
  60. package/src/monitor.webhook.ts +0 -278
  61. package/src/outbound-media.test.ts +0 -182
  62. package/src/outbound-media.ts +0 -241
  63. package/src/outbound-payload.contract.test.ts +0 -45
  64. package/src/probe.ts +0 -45
  65. package/src/proxy.ts +0 -24
  66. package/src/runtime-api.ts +0 -75
  67. package/src/runtime-support.ts +0 -91
  68. package/src/runtime.ts +0 -9
  69. package/src/secret-contract.ts +0 -109
  70. package/src/secret-input.ts +0 -5
  71. package/src/send.test.ts +0 -120
  72. package/src/send.ts +0 -153
  73. package/src/session-route.ts +0 -32
  74. package/src/setup-allow-from.ts +0 -94
  75. package/src/setup-core.ts +0 -149
  76. package/src/setup-status.test.ts +0 -33
  77. package/src/setup-surface.test.ts +0 -175
  78. package/src/setup-surface.ts +0 -291
  79. package/src/status-issues.test.ts +0 -17
  80. package/src/status-issues.ts +0 -37
  81. package/src/test-support/lifecycle-test-support.ts +0 -413
  82. package/src/test-support/monitor-mocks-test-support.ts +0 -209
  83. package/src/token.test.ts +0 -92
  84. package/src/token.ts +0 -79
  85. package/src/types.ts +0 -50
  86. package/test-api.ts +0 -1
  87. package/tsconfig.json +0 -16
@@ -0,0 +1,175 @@
1
+ import { $ as withResolvedWebhookRequestPipeline, B as resolveClientIp, F as readJsonWebhookBodyOrReject, R as registerWebhookTarget, i as WEBHOOK_RATE_LIMIT_DEFAULTS, q as resolveWebhookTargetWithAuthOrRejectSync, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, v as createFixedWindowRateLimiter, y as createWebhookAnomalyTracker, z as registerWebhookTargetWithPluginRoute } from "./runtime-api-MOTmRW4F.js";
2
+ import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
3
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
4
+ //#region extensions/zalo/src/monitor.webhook.ts
5
+ const ZALO_WEBHOOK_REPLAY_WINDOW_MS = 5 * 6e4;
6
+ const webhookTargets = /* @__PURE__ */ new Map();
7
+ const webhookRateLimiter = createFixedWindowRateLimiter({
8
+ windowMs: WEBHOOK_RATE_LIMIT_DEFAULTS.windowMs,
9
+ maxRequests: WEBHOOK_RATE_LIMIT_DEFAULTS.maxRequests,
10
+ maxTrackedKeys: WEBHOOK_RATE_LIMIT_DEFAULTS.maxTrackedKeys
11
+ });
12
+ const recentWebhookEvents = createClaimableDedupe({
13
+ ttlMs: ZALO_WEBHOOK_REPLAY_WINDOW_MS,
14
+ memoryMaxSize: 5e3
15
+ });
16
+ const webhookAnomalyTracker = createWebhookAnomalyTracker({
17
+ maxTrackedKeys: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.maxTrackedKeys,
18
+ ttlMs: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.ttlMs,
19
+ logEvery: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.logEvery
20
+ });
21
+ function clearZaloWebhookSecurityStateForTest() {
22
+ webhookRateLimiter.clear();
23
+ recentWebhookEvents.clearMemory();
24
+ webhookAnomalyTracker.clear();
25
+ }
26
+ function getZaloWebhookRateLimitStateSizeForTest() {
27
+ return webhookRateLimiter.size();
28
+ }
29
+ function getZaloWebhookStatusCounterSizeForTest() {
30
+ return webhookAnomalyTracker.size();
31
+ }
32
+ function timingSafeEquals(left, right) {
33
+ return safeEqualSecret(left, right);
34
+ }
35
+ function buildReplayEventCacheKey(target, update) {
36
+ const messageId = update.message?.message_id;
37
+ if (!messageId) return null;
38
+ const chatId = update.message?.chat?.id ?? "";
39
+ const senderId = update.message?.from?.id ?? "";
40
+ return JSON.stringify([
41
+ target.path,
42
+ target.account.accountId,
43
+ update.event_name,
44
+ chatId,
45
+ senderId,
46
+ messageId
47
+ ]);
48
+ }
49
+ var ZaloRetryableWebhookError = class extends Error {
50
+ constructor(message, options) {
51
+ super(message, options);
52
+ this.name = "ZaloRetryableWebhookError";
53
+ }
54
+ };
55
+ async function processZaloReplayGuardedUpdate(params) {
56
+ const replayEventKey = buildReplayEventCacheKey(params.target, params.update);
57
+ if (replayEventKey) {
58
+ if ((await recentWebhookEvents.claim(replayEventKey, { now: params.nowMs })).kind !== "claimed") return "duplicate";
59
+ }
60
+ params.target.statusSink?.({ lastInboundAt: Date.now() });
61
+ try {
62
+ await params.processUpdate({
63
+ update: params.update,
64
+ target: params.target
65
+ });
66
+ if (replayEventKey) await recentWebhookEvents.commit(replayEventKey);
67
+ return "processed";
68
+ } catch (error) {
69
+ if (replayEventKey) if (error instanceof ZaloRetryableWebhookError) recentWebhookEvents.release(replayEventKey, { error });
70
+ else await recentWebhookEvents.commit(replayEventKey);
71
+ throw error;
72
+ }
73
+ }
74
+ function recordWebhookStatus(runtime, path, statusCode) {
75
+ webhookAnomalyTracker.record({
76
+ key: `${path}:${statusCode}`,
77
+ statusCode,
78
+ log: runtime?.log,
79
+ message: (count) => `[zalo] webhook anomaly path=${path} status=${statusCode} count=${String(count)}`
80
+ });
81
+ }
82
+ function headerValue(value) {
83
+ return Array.isArray(value) ? value[0] : value;
84
+ }
85
+ function registerZaloWebhookTarget(target, opts) {
86
+ if (opts?.route) return registerWebhookTargetWithPluginRoute({
87
+ targetsByPath: webhookTargets,
88
+ target,
89
+ route: opts.route,
90
+ onLastPathTargetRemoved: opts.onLastPathTargetRemoved
91
+ }).unregister;
92
+ return registerWebhookTarget(webhookTargets, target, opts).unregister;
93
+ }
94
+ async function handleZaloWebhookRequest(req, res, processUpdate) {
95
+ return await withResolvedWebhookRequestPipeline({
96
+ req,
97
+ res,
98
+ targetsByPath: webhookTargets,
99
+ allowMethods: ["POST"],
100
+ handle: async ({ targets, path }) => {
101
+ const trustedProxies = targets[0]?.config.gateway?.trustedProxies;
102
+ const allowRealIpFallback = targets[0]?.config.gateway?.allowRealIpFallback === true;
103
+ const rateLimitKey = `${path}:${resolveClientIp({
104
+ remoteAddr: req.socket.remoteAddress,
105
+ forwardedFor: headerValue(req.headers["x-forwarded-for"]),
106
+ realIp: headerValue(req.headers["x-real-ip"]),
107
+ trustedProxies,
108
+ allowRealIpFallback
109
+ }) ?? req.socket.remoteAddress ?? "unknown"}`;
110
+ const nowMs = Date.now();
111
+ if (!applyBasicWebhookRequestGuards({
112
+ req,
113
+ res,
114
+ rateLimiter: webhookRateLimiter,
115
+ rateLimitKey,
116
+ nowMs
117
+ })) {
118
+ recordWebhookStatus(targets[0]?.runtime, path, res.statusCode);
119
+ return true;
120
+ }
121
+ const headerToken = String(req.headers["x-bot-api-secret-token"] ?? "");
122
+ const target = resolveWebhookTargetWithAuthOrRejectSync({
123
+ targets,
124
+ res,
125
+ isMatch: (entry) => timingSafeEquals(entry.secret, headerToken)
126
+ });
127
+ if (!target) {
128
+ recordWebhookStatus(targets[0]?.runtime, path, res.statusCode);
129
+ return true;
130
+ }
131
+ if (!applyBasicWebhookRequestGuards({
132
+ req,
133
+ res,
134
+ requireJsonContentType: true
135
+ })) {
136
+ recordWebhookStatus(target.runtime, path, res.statusCode);
137
+ return true;
138
+ }
139
+ const body = await readJsonWebhookBodyOrReject({
140
+ req,
141
+ res,
142
+ maxBytes: 1024 * 1024,
143
+ timeoutMs: 3e4,
144
+ emptyObjectOnEmpty: false,
145
+ invalidJsonMessage: "Bad Request"
146
+ });
147
+ if (!body.ok) {
148
+ recordWebhookStatus(target.runtime, path, res.statusCode);
149
+ return true;
150
+ }
151
+ const raw = body.value;
152
+ const record = raw && typeof raw === "object" ? raw : null;
153
+ const update = record && record.ok === true && record.result ? record.result : record ?? void 0;
154
+ if (!update?.event_name) {
155
+ res.statusCode = 400;
156
+ res.end("Bad Request");
157
+ recordWebhookStatus(target.runtime, path, res.statusCode);
158
+ return true;
159
+ }
160
+ processZaloReplayGuardedUpdate({
161
+ target,
162
+ update,
163
+ processUpdate,
164
+ nowMs
165
+ }).catch((err) => {
166
+ target.runtime.error?.(`[${target.account.accountId}] Zalo webhook failed: ${String(err)}`);
167
+ });
168
+ res.statusCode = 200;
169
+ res.end("ok");
170
+ return true;
171
+ }
172
+ });
173
+ }
174
+ //#endregion
175
+ export { ZaloRetryableWebhookError, clearZaloWebhookSecurityStateForTest, getZaloWebhookRateLimitStateSizeForTest, getZaloWebhookStatusCounterSizeForTest, handleZaloWebhookRequest, processZaloReplayGuardedUpdate, registerZaloWebhookTarget };
@@ -0,0 +1,135 @@
1
+ import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
2
+ import { ProxyAgent, fetch as fetch$1 } from "undici";
3
+ //#region extensions/zalo/src/api.ts
4
+ /**
5
+ * Zalo Bot API client
6
+ * @see https://bot.zaloplatforms.com/docs
7
+ */
8
+ const ZALO_API_BASE = "https://bot-api.zaloplatforms.com";
9
+ const ZALO_MEDIA_SSRF_POLICY = {};
10
+ var ZaloApiError = class extends Error {
11
+ constructor(message, errorCode, description) {
12
+ super(message);
13
+ this.errorCode = errorCode;
14
+ this.description = description;
15
+ this.name = "ZaloApiError";
16
+ }
17
+ /** True if this is a long-polling timeout (no updates available) */
18
+ get isPollingTimeout() {
19
+ return this.errorCode === 408;
20
+ }
21
+ };
22
+ /**
23
+ * Call the Zalo Bot API
24
+ */
25
+ async function callZaloApi(method, token, body, options) {
26
+ const url = `${ZALO_API_BASE}/bot${token}/${method}`;
27
+ const controller = new AbortController();
28
+ const timeoutId = options?.timeoutMs ? setTimeout(() => controller.abort(), options.timeoutMs) : void 0;
29
+ const fetcher = options?.fetch ?? fetch;
30
+ try {
31
+ const data = await (await fetcher(url, {
32
+ method: "POST",
33
+ headers: { "Content-Type": "application/json" },
34
+ body: body ? JSON.stringify(body) : void 0,
35
+ signal: controller.signal
36
+ })).json();
37
+ if (!data.ok) throw new ZaloApiError(data.description ?? `Zalo API error: ${method}`, data.error_code, data.description);
38
+ return data;
39
+ } finally {
40
+ if (timeoutId) clearTimeout(timeoutId);
41
+ }
42
+ }
43
+ /**
44
+ * Validate bot token and get bot info
45
+ */
46
+ async function getMe(token, timeoutMs, fetcher) {
47
+ return callZaloApi("getMe", token, void 0, {
48
+ timeoutMs,
49
+ fetch: fetcher
50
+ });
51
+ }
52
+ /**
53
+ * Send a text message
54
+ */
55
+ async function sendMessage(token, params, fetcher) {
56
+ return callZaloApi("sendMessage", token, params, { fetch: fetcher });
57
+ }
58
+ /**
59
+ * Send a photo message
60
+ */
61
+ async function sendPhoto(token, params, fetcher) {
62
+ const photoUrl = params.photo.trim();
63
+ let parsedPhotoUrl;
64
+ try {
65
+ parsedPhotoUrl = new URL(photoUrl);
66
+ } catch {
67
+ throw new Error("Zalo photo URL must be an absolute HTTP or HTTPS URL");
68
+ }
69
+ if (parsedPhotoUrl.protocol !== "http:" && parsedPhotoUrl.protocol !== "https:") throw new Error("Zalo photo URL must use HTTP or HTTPS");
70
+ await resolvePinnedHostnameWithPolicy(parsedPhotoUrl.hostname, { policy: ZALO_MEDIA_SSRF_POLICY });
71
+ return callZaloApi("sendPhoto", token, {
72
+ ...params,
73
+ photo: parsedPhotoUrl.href
74
+ }, { fetch: fetcher });
75
+ }
76
+ /**
77
+ * Send a temporary chat action such as typing.
78
+ */
79
+ async function sendChatAction(token, params, fetcher, timeoutMs) {
80
+ return callZaloApi("sendChatAction", token, params, {
81
+ timeoutMs,
82
+ fetch: fetcher
83
+ });
84
+ }
85
+ /**
86
+ * Get updates using long polling (dev/testing only)
87
+ * Note: Zalo returns a single update per call, not an array like Telegram
88
+ */
89
+ async function getUpdates(token, params, fetcher) {
90
+ const pollTimeoutSec = params?.timeout ?? 30;
91
+ const timeoutMs = (pollTimeoutSec + 5) * 1e3;
92
+ return callZaloApi("getUpdates", token, { timeout: String(pollTimeoutSec) }, {
93
+ timeoutMs,
94
+ fetch: fetcher
95
+ });
96
+ }
97
+ /**
98
+ * Set webhook URL for receiving updates
99
+ */
100
+ async function setWebhook(token, params, fetcher) {
101
+ return callZaloApi("setWebhook", token, params, { fetch: fetcher });
102
+ }
103
+ /**
104
+ * Delete webhook configuration
105
+ */
106
+ async function deleteWebhook(token, fetcher, timeoutMs) {
107
+ return callZaloApi("deleteWebhook", token, void 0, {
108
+ timeoutMs,
109
+ fetch: fetcher
110
+ });
111
+ }
112
+ /**
113
+ * Get current webhook info
114
+ */
115
+ async function getWebhookInfo(token, fetcher) {
116
+ return callZaloApi("getWebhookInfo", token, void 0, { fetch: fetcher });
117
+ }
118
+ //#endregion
119
+ //#region extensions/zalo/src/proxy.ts
120
+ const proxyCache = /* @__PURE__ */ new Map();
121
+ function resolveZaloProxyFetch(proxyUrl) {
122
+ const trimmed = proxyUrl?.trim();
123
+ if (!trimmed) return;
124
+ const cached = proxyCache.get(trimmed);
125
+ if (cached) return cached;
126
+ const agent = new ProxyAgent(trimmed);
127
+ const fetcher = (input, init) => fetch$1(input, {
128
+ ...init,
129
+ dispatcher: agent
130
+ });
131
+ proxyCache.set(trimmed, fetcher);
132
+ return fetcher;
133
+ }
134
+ //#endregion
135
+ export { getUpdates as a, sendMessage as c, getMe as i, sendPhoto as l, ZaloApiError as n, getWebhookInfo as o, deleteWebhook as r, sendChatAction as s, resolveZaloProxyFetch as t, setWebhook as u };
@@ -0,0 +1,8 @@
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
+ //#region extensions/zalo/src/runtime.ts
3
+ const { setRuntime: setZaloRuntime, getRuntime: getZaloRuntime } = createPluginRuntimeStore({
4
+ pluginId: "zalo",
5
+ errorMessage: "Zalo runtime not initialized"
6
+ });
7
+ //#endregion
8
+ export { setZaloRuntime as n, getZaloRuntime as t };
@@ -0,0 +1,19 @@
1
+ import "./runtime-BRFxnYQx.js";
2
+ import { formatAllowFromLowercase as formatAllowFromLowercase$1, isNormalizedSenderAllowed as isNormalizedSenderAllowed$1 } from "openclaw/plugin-sdk/allow-from";
3
+ import { PAIRING_APPROVED_MESSAGE, buildTokenChannelStatusSummary as buildTokenChannelStatusSummary$1 } from "openclaw/plugin-sdk/channel-status";
4
+ import { deliverTextOrMediaReply as deliverTextOrMediaReply$1, isNumericTargetId as isNumericTargetId$1, sendPayloadWithChunkedTextAndMedia as sendPayloadWithChunkedTextAndMedia$1 } from "openclaw/plugin-sdk/reply-payload";
5
+ import { buildBaseAccountStatusSnapshot } from "openclaw/plugin-sdk/status-helpers";
6
+ import { chunkTextForOutbound as chunkTextForOutbound$1 } from "openclaw/plugin-sdk/text-chunking";
7
+ import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, buildChannelConfigSchema, createDedupeCache, formatPairingApproveHint, jsonResult, normalizeAccountId as normalizeAccountId$1, readStringParam, resolveClientIp } from "openclaw/plugin-sdk/core";
8
+ import { buildSecretInputSchema, hasConfiguredSecretInput as hasConfiguredSecretInput$1, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
9
+ import { addWildcardAllowFrom as addWildcardAllowFrom$1, applyAccountNameToChannelSection, applySetupAccountConfigPatch, buildSingleChannelSecretPromptState as buildSingleChannelSecretPromptState$1, mergeAllowFromEntries as mergeAllowFromEntries$1, migrateBaseNameToDefaultAccount, promptSingleChannelSecretInput as promptSingleChannelSecretInput$1, runSingleChannelSecretStep as runSingleChannelSecretStep$1, setTopLevelChannelDmPolicyWithAllowFrom } from "openclaw/plugin-sdk/setup";
10
+ import { evaluateSenderGroupAccess as evaluateSenderGroupAccess$1 } from "openclaw/plugin-sdk/group-access";
11
+ import { resolveDefaultGroupPolicy as resolveDefaultGroupPolicy$1, resolveOpenProviderRuntimeGroupPolicy as resolveOpenProviderRuntimeGroupPolicy$1, warnMissingProviderGroupPolicyFallbackOnce as warnMissingProviderGroupPolicyFallbackOnce$1 } from "openclaw/plugin-sdk/runtime-group-policy";
12
+ import { createChannelPairingController as createChannelPairingController$1 } from "openclaw/plugin-sdk/channel-pairing";
13
+ import { createChannelReplyPipeline as createChannelReplyPipeline$1 } from "openclaw/plugin-sdk/channel-reply-pipeline";
14
+ import { logTypingFailure as logTypingFailure$1 } from "openclaw/plugin-sdk/channel-feedback";
15
+ import { resolveDirectDmAuthorizationOutcome as resolveDirectDmAuthorizationOutcome$1, resolveSenderCommandAuthorizationWithRuntime as resolveSenderCommandAuthorizationWithRuntime$1 } from "openclaw/plugin-sdk/command-auth";
16
+ import { resolveInboundRouteEnvelopeBuilderWithRuntime as resolveInboundRouteEnvelopeBuilderWithRuntime$1 } from "openclaw/plugin-sdk/inbound-envelope";
17
+ import { waitForAbortSignal } from "openclaw/plugin-sdk/runtime";
18
+ import { WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, applyBasicWebhookRequestGuards, createFixedWindowRateLimiter, createWebhookAnomalyTracker, readJsonWebhookBodyOrReject, registerPluginHttpRoute as registerPluginHttpRoute$1, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveWebhookPath as resolveWebhookPath$1, resolveWebhookTargetWithAuthOrRejectSync, withResolvedWebhookRequestPipeline } from "openclaw/plugin-sdk/webhook-ingress";
19
+ export { withResolvedWebhookRequestPipeline as $, migrateBaseNameToDefaultAccount as A, resolveClientIp as B, formatPairingApproveHint as C, jsonResult as D, isNumericTargetId$1 as E, readJsonWebhookBodyOrReject as F, resolveSenderCommandAuthorizationWithRuntime$1 as G, resolveDirectDmAuthorizationOutcome$1 as H, readStringParam as I, runSingleChannelSecretStep$1 as J, resolveWebhookPath$1 as K, registerPluginHttpRoute$1 as L, normalizeResolvedSecretInputString as M, normalizeSecretInputString as N, logTypingFailure$1 as O, promptSingleChannelSecretInput$1 as P, warnMissingProviderGroupPolicyFallbackOnce$1 as Q, registerWebhookTarget as R, formatAllowFromLowercase$1 as S, isNormalizedSenderAllowed$1 as T, resolveInboundRouteEnvelopeBuilderWithRuntime$1 as U, resolveDefaultGroupPolicy$1 as V, resolveOpenProviderRuntimeGroupPolicy$1 as W, setTopLevelChannelDmPolicyWithAllowFrom as X, sendPayloadWithChunkedTextAndMedia$1 as Y, waitForAbortSignal as Z, createDedupeCache as _, addWildcardAllowFrom$1 as a, deliverTextOrMediaReply$1 as b, applySetupAccountConfigPatch as c, buildSecretInputSchema as d, buildSingleChannelSecretPromptState$1 as f, createChannelReplyPipeline$1 as g, createChannelPairingController$1 as h, WEBHOOK_RATE_LIMIT_DEFAULTS as i, normalizeAccountId$1 as j, mergeAllowFromEntries$1 as k, buildBaseAccountStatusSnapshot as l, chunkTextForOutbound$1 as m, PAIRING_APPROVED_MESSAGE as n, applyAccountNameToChannelSection as o, buildTokenChannelStatusSummary$1 as p, resolveWebhookTargetWithAuthOrRejectSync as q, WEBHOOK_ANOMALY_COUNTER_DEFAULTS as r, applyBasicWebhookRequestGuards as s, DEFAULT_ACCOUNT_ID$1 as t, buildChannelConfigSchema as u, createFixedWindowRateLimiter as v, hasConfiguredSecretInput$1 as w, evaluateSenderGroupAccess$1 as x, createWebhookAnomalyTracker as y, registerWebhookTargetWithPluginRoute as z };
@@ -0,0 +1,3 @@
1
+ import { $ as withResolvedWebhookRequestPipeline, A as migrateBaseNameToDefaultAccount, B as resolveClientIp, C as formatPairingApproveHint, D as jsonResult, E as isNumericTargetId, F as readJsonWebhookBodyOrReject, G as resolveSenderCommandAuthorizationWithRuntime, H as resolveDirectDmAuthorizationOutcome, I as readStringParam, J as runSingleChannelSecretStep, K as resolveWebhookPath, L as registerPluginHttpRoute, M as normalizeResolvedSecretInputString, N as normalizeSecretInputString, O as logTypingFailure, P as promptSingleChannelSecretInput, Q as warnMissingProviderGroupPolicyFallbackOnce, R as registerWebhookTarget, S as formatAllowFromLowercase, T as isNormalizedSenderAllowed, U as resolveInboundRouteEnvelopeBuilderWithRuntime, V as resolveDefaultGroupPolicy, W as resolveOpenProviderRuntimeGroupPolicy, X as setTopLevelChannelDmPolicyWithAllowFrom, Y as sendPayloadWithChunkedTextAndMedia, Z as waitForAbortSignal, _ as createDedupeCache, a as addWildcardAllowFrom, b as deliverTextOrMediaReply, c as applySetupAccountConfigPatch, d as buildSecretInputSchema, f as buildSingleChannelSecretPromptState, g as createChannelReplyPipeline, h as createChannelPairingController, i as WEBHOOK_RATE_LIMIT_DEFAULTS, j as normalizeAccountId, k as mergeAllowFromEntries, l as buildBaseAccountStatusSnapshot, m as chunkTextForOutbound, n as PAIRING_APPROVED_MESSAGE, o as applyAccountNameToChannelSection, p as buildTokenChannelStatusSummary, q as resolveWebhookTargetWithAuthOrRejectSync, r as WEBHOOK_ANOMALY_COUNTER_DEFAULTS, s as applyBasicWebhookRequestGuards, t as DEFAULT_ACCOUNT_ID, u as buildChannelConfigSchema, v as createFixedWindowRateLimiter, w as hasConfiguredSecretInput, x as evaluateSenderGroupAccess, y as createWebhookAnomalyTracker, z as registerWebhookTargetWithPluginRoute } from "./runtime-api-MOTmRW4F.js";
2
+ import { n as setZaloRuntime } from "./runtime-BRFxnYQx.js";
3
+ export { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, WEBHOOK_ANOMALY_COUNTER_DEFAULTS, WEBHOOK_RATE_LIMIT_DEFAULTS, addWildcardAllowFrom, applyAccountNameToChannelSection, applyBasicWebhookRequestGuards, applySetupAccountConfigPatch, buildBaseAccountStatusSnapshot, buildChannelConfigSchema, buildSecretInputSchema, buildSingleChannelSecretPromptState, buildTokenChannelStatusSummary, chunkTextForOutbound, createChannelPairingController, createChannelReplyPipeline, createDedupeCache, createFixedWindowRateLimiter, createWebhookAnomalyTracker, deliverTextOrMediaReply, evaluateSenderGroupAccess, formatAllowFromLowercase, formatPairingApproveHint, hasConfiguredSecretInput, isNormalizedSenderAllowed, isNumericTargetId, jsonResult, logTypingFailure, mergeAllowFromEntries, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeResolvedSecretInputString, normalizeSecretInputString, promptSingleChannelSecretInput, readJsonWebhookBodyOrReject, readStringParam, registerPluginHttpRoute, registerWebhookTarget, registerWebhookTargetWithPluginRoute, resolveClientIp, resolveDefaultGroupPolicy, resolveDirectDmAuthorizationOutcome, resolveInboundRouteEnvelopeBuilderWithRuntime, resolveOpenProviderRuntimeGroupPolicy, resolveSenderCommandAuthorizationWithRuntime, resolveWebhookPath, resolveWebhookTargetWithAuthOrRejectSync, runSingleChannelSecretStep, sendPayloadWithChunkedTextAndMedia, setTopLevelChannelDmPolicyWithAllowFrom, setZaloRuntime, waitForAbortSignal, warnMissingProviderGroupPolicyFallbackOnce, withResolvedWebhookRequestPipeline };
@@ -0,0 +1,87 @@
1
+ import { collectConditionalChannelFieldAssignments, getChannelSurface, hasOwnProperty } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
2
+ //#region extensions/zalo/src/secret-contract.ts
3
+ const secretTargetRegistryEntries = [
4
+ {
5
+ id: "channels.zalo.accounts.*.botToken",
6
+ targetType: "channels.zalo.accounts.*.botToken",
7
+ configFile: "openclaw.json",
8
+ pathPattern: "channels.zalo.accounts.*.botToken",
9
+ secretShape: "secret_input",
10
+ expectedResolvedValue: "string",
11
+ includeInPlan: true,
12
+ includeInConfigure: true,
13
+ includeInAudit: true
14
+ },
15
+ {
16
+ id: "channels.zalo.accounts.*.webhookSecret",
17
+ targetType: "channels.zalo.accounts.*.webhookSecret",
18
+ configFile: "openclaw.json",
19
+ pathPattern: "channels.zalo.accounts.*.webhookSecret",
20
+ secretShape: "secret_input",
21
+ expectedResolvedValue: "string",
22
+ includeInPlan: true,
23
+ includeInConfigure: true,
24
+ includeInAudit: true
25
+ },
26
+ {
27
+ id: "channels.zalo.botToken",
28
+ targetType: "channels.zalo.botToken",
29
+ configFile: "openclaw.json",
30
+ pathPattern: "channels.zalo.botToken",
31
+ secretShape: "secret_input",
32
+ expectedResolvedValue: "string",
33
+ includeInPlan: true,
34
+ includeInConfigure: true,
35
+ includeInAudit: true
36
+ },
37
+ {
38
+ id: "channels.zalo.webhookSecret",
39
+ targetType: "channels.zalo.webhookSecret",
40
+ configFile: "openclaw.json",
41
+ pathPattern: "channels.zalo.webhookSecret",
42
+ secretShape: "secret_input",
43
+ expectedResolvedValue: "string",
44
+ includeInPlan: true,
45
+ includeInConfigure: true,
46
+ includeInAudit: true
47
+ }
48
+ ];
49
+ function collectRuntimeConfigAssignments(params) {
50
+ const resolved = getChannelSurface(params.config, "zalo");
51
+ if (!resolved) return;
52
+ const { channel: zalo, surface } = resolved;
53
+ collectConditionalChannelFieldAssignments({
54
+ channelKey: "zalo",
55
+ field: "botToken",
56
+ channel: zalo,
57
+ surface,
58
+ defaults: params.defaults,
59
+ context: params.context,
60
+ topLevelActiveWithoutAccounts: true,
61
+ topLevelInheritedAccountActive: ({ account, enabled }) => enabled && !hasOwnProperty(account, "botToken"),
62
+ accountActive: ({ enabled }) => enabled,
63
+ topInactiveReason: "no enabled Zalo surface inherits this top-level botToken.",
64
+ accountInactiveReason: "Zalo account is disabled."
65
+ });
66
+ const baseWebhookUrl = typeof zalo.webhookUrl === "string" ? zalo.webhookUrl.trim() : "";
67
+ const accountWebhookUrl = (account) => hasOwnProperty(account, "webhookUrl") ? typeof account.webhookUrl === "string" ? account.webhookUrl.trim() : "" : baseWebhookUrl;
68
+ collectConditionalChannelFieldAssignments({
69
+ channelKey: "zalo",
70
+ field: "webhookSecret",
71
+ channel: zalo,
72
+ surface,
73
+ defaults: params.defaults,
74
+ context: params.context,
75
+ topLevelActiveWithoutAccounts: baseWebhookUrl.length > 0,
76
+ topLevelInheritedAccountActive: ({ account, enabled }) => enabled && !hasOwnProperty(account, "webhookSecret") && accountWebhookUrl(account).length > 0,
77
+ accountActive: ({ account, enabled }) => enabled && accountWebhookUrl(account).length > 0,
78
+ topInactiveReason: "no enabled Zalo webhook surface inherits this top-level webhookSecret (webhook mode is not active).",
79
+ accountInactiveReason: "Zalo account is disabled or webhook mode is not active for this account."
80
+ });
81
+ }
82
+ const channelSecrets = {
83
+ secretTargetRegistryEntries,
84
+ collectRuntimeConfigAssignments
85
+ };
86
+ //#endregion
87
+ export { collectRuntimeConfigAssignments as n, secretTargetRegistryEntries as r, channelSecrets as t };
@@ -0,0 +1,2 @@
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-Dw93tGo2.js";
2
+ export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -0,0 +1,101 @@
1
+ import { a as resolveZaloAccount, o as resolveZaloToken } from "./accounts-9NLDDlZ8.js";
2
+ import { c as sendMessage, l as sendPhoto, t as resolveZaloProxyFetch } from "./proxy-CY8VuC6H.js";
3
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
+ //#region extensions/zalo/src/send.ts
5
+ function toZaloSendResult(response) {
6
+ if (response.ok && response.result) return {
7
+ ok: true,
8
+ messageId: response.result.message_id
9
+ };
10
+ return {
11
+ ok: false,
12
+ error: "Failed to send message"
13
+ };
14
+ }
15
+ async function runZaloSend(failureMessage, send) {
16
+ try {
17
+ const result = toZaloSendResult(await send());
18
+ return result.ok ? result : {
19
+ ok: false,
20
+ error: failureMessage
21
+ };
22
+ } catch (err) {
23
+ return {
24
+ ok: false,
25
+ error: formatErrorMessage(err)
26
+ };
27
+ }
28
+ }
29
+ function resolveSendContext(options) {
30
+ if (options.cfg) {
31
+ const account = resolveZaloAccount({
32
+ cfg: options.cfg,
33
+ accountId: options.accountId
34
+ });
35
+ return {
36
+ token: options.token || account.token,
37
+ fetcher: resolveZaloProxyFetch(options.proxy ?? account.config.proxy)
38
+ };
39
+ }
40
+ const token = options.token ?? resolveZaloToken(void 0, options.accountId).token;
41
+ const proxy = options.proxy;
42
+ return {
43
+ token,
44
+ fetcher: resolveZaloProxyFetch(proxy)
45
+ };
46
+ }
47
+ function resolveValidatedSendContext(chatId, options) {
48
+ const { token, fetcher } = resolveSendContext(options);
49
+ if (!token) return {
50
+ ok: false,
51
+ error: "No Zalo bot token configured"
52
+ };
53
+ const trimmedChatId = chatId?.trim();
54
+ if (!trimmedChatId) return {
55
+ ok: false,
56
+ error: "No chat_id provided"
57
+ };
58
+ return {
59
+ ok: true,
60
+ chatId: trimmedChatId,
61
+ token,
62
+ fetcher
63
+ };
64
+ }
65
+ function resolveSendContextOrFailure(chatId, options) {
66
+ const context = resolveValidatedSendContext(chatId, options);
67
+ return context.ok ? { context } : { failure: {
68
+ ok: false,
69
+ error: context.error
70
+ } };
71
+ }
72
+ async function sendMessageZalo(chatId, text, options = {}) {
73
+ const resolved = resolveSendContextOrFailure(chatId, options);
74
+ if ("failure" in resolved) return resolved.failure;
75
+ const { context } = resolved;
76
+ if (options.mediaUrl) return sendPhotoZalo(context.chatId, options.mediaUrl, {
77
+ ...options,
78
+ token: context.token,
79
+ caption: text || options.caption
80
+ });
81
+ return await runZaloSend("Failed to send message", () => sendMessage(context.token, {
82
+ chat_id: context.chatId,
83
+ text: text.slice(0, 2e3)
84
+ }, context.fetcher));
85
+ }
86
+ async function sendPhotoZalo(chatId, photoUrl, options = {}) {
87
+ const resolved = resolveSendContextOrFailure(chatId, options);
88
+ if ("failure" in resolved) return resolved.failure;
89
+ const { context } = resolved;
90
+ if (!photoUrl?.trim()) return {
91
+ ok: false,
92
+ error: "No photo URL provided"
93
+ };
94
+ return await runZaloSend("Failed to send photo", () => (async () => sendPhoto(context.token, {
95
+ chat_id: context.chatId,
96
+ photo: photoUrl.trim(),
97
+ caption: options.caption?.slice(0, 2e3)
98
+ }, context.fetcher))());
99
+ }
100
+ //#endregion
101
+ export { sendMessageZalo as t };
@@ -0,0 +1,30 @@
1
+ import { n as zaloDmPolicy, r as zaloSetupAdapter, t as createZaloSetupWizardProxy } from "./setup-core-DigRD3j1.js";
2
+ import { r as resolveZaloRuntimeGroupPolicy, t as evaluateZaloGroupAccess } from "./group-access-DZR43lOR.js";
3
+ import { loadBundledEntryExportSync } from "openclaw/plugin-sdk/channel-entry-contract";
4
+ //#region extensions/zalo/setup-api.ts
5
+ function createLazyObjectValue(load) {
6
+ return new Proxy({}, {
7
+ get(_target, property, receiver) {
8
+ return Reflect.get(load(), property, receiver);
9
+ },
10
+ has(_target, property) {
11
+ return property in load();
12
+ },
13
+ ownKeys() {
14
+ return Reflect.ownKeys(load());
15
+ },
16
+ getOwnPropertyDescriptor(_target, property) {
17
+ const descriptor = Object.getOwnPropertyDescriptor(load(), property);
18
+ return descriptor ? {
19
+ ...descriptor,
20
+ configurable: true
21
+ } : void 0;
22
+ }
23
+ });
24
+ }
25
+ function loadSetupSurfaceModule() {
26
+ return loadBundledEntryExportSync(import.meta.url, { specifier: "./src/setup-surface.js" });
27
+ }
28
+ const zaloSetupWizard = createLazyObjectValue(() => loadSetupSurfaceModule().zaloSetupWizard);
29
+ //#endregion
30
+ export { createZaloSetupWizardProxy, evaluateZaloGroupAccess, resolveZaloRuntimeGroupPolicy, zaloDmPolicy, zaloSetupAdapter, zaloSetupWizard };