@openclaw/line 2026.8.1-beta.3 → 2026.8.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.
Files changed (153) hide show
  1. package/dist/account-helpers-BSUyZwLm.js +12 -0
  2. package/dist/api.js +2 -2
  3. package/dist/{card-command-CyDSm-OU.js → card-command-BywtX77U.js} +66 -29
  4. package/dist/{channel-DmoKZXmG.js → channel-DpMgRaxl.js} +52 -349
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/channel.runtime-Bty5U_1N.js +4 -0
  7. package/dist/doctor-contract-api.js +141 -0
  8. package/dist/index.js +1 -1
  9. package/dist/{markdown-to-line-rEaras_0.js → markdown-to-line-CG9Jy22s.js} +29 -25
  10. package/dist/{monitor-BYXS_B6r.js → monitor-Ch6TIatG.js} +261 -166
  11. package/dist/monitor.runtime-Czc3NxNV.js +2 -0
  12. package/dist/outbound.runtime-CyleVzFR.js +3 -0
  13. package/dist/probe.runtime-DTxBWvKz.js +2 -0
  14. package/dist/rich-messages-t_zRL24p.js +760 -0
  15. package/dist/runtime-api.js +10 -11
  16. package/dist/{send-BJCeiiOB.js → send-B-K2B6au.js} +138 -104
  17. package/dist/{send-receipt-DKZzgHMO.js → send-retry-CUvrarfE.js} +137 -413
  18. package/dist/setup-api.js +1 -1
  19. package/dist/{setup-surface-W0Dbgu58.js → setup-surface-DSJ92oJ5.js} +24 -17
  20. package/dist/webhook-spool-contract-CNe2h1I2.js +58 -0
  21. package/node_modules/typebox/build/format/date.d.mts +1 -1
  22. package/node_modules/typebox/build/format/date.mjs +1 -1
  23. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  24. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  25. package/node_modules/typebox/build/format/email.d.mts +1 -1
  26. package/node_modules/typebox/build/format/email.mjs +2 -2
  27. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  28. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  29. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  30. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  31. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  32. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  33. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  34. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  35. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  36. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  37. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  38. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  39. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  40. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  41. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  42. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  43. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  44. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  45. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  46. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  47. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  48. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  49. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  50. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  51. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  52. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  53. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  54. package/node_modules/typebox/build/format/iri.mjs +26 -8
  55. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  56. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  57. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  58. package/node_modules/typebox/build/format/regex.mjs +2 -6
  59. package/node_modules/typebox/build/format/time.d.mts +2 -2
  60. package/node_modules/typebox/build/format/time.mjs +19 -14
  61. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  62. package/node_modules/typebox/build/format/uri.mjs +3 -130
  63. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  64. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  65. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  66. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  67. package/node_modules/typebox/build/format/url.d.mts +3 -3
  68. package/node_modules/typebox/build/format/url.mjs +4 -5
  69. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  70. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  71. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  72. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  73. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  74. package/node_modules/typebox/build/guard/string.mjs +7 -7
  75. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  76. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  77. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  78. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  79. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  80. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  81. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  82. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  83. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  84. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  85. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  86. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  87. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  88. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  89. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  90. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  91. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  92. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  93. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  94. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  95. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  96. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  97. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  98. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  99. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  100. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  101. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  102. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  103. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  104. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  105. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  106. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  107. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  108. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  109. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  110. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  111. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  112. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  113. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  114. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  115. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  116. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  117. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  118. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  119. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  120. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  121. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  122. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  123. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  124. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  125. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  126. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  127. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  128. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  129. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  130. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  131. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  132. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  133. package/node_modules/typebox/package.json +27 -27
  134. package/node_modules/typebox/readme.md +79 -82
  135. package/openclaw.plugin.json +26 -2
  136. package/package.json +6 -6
  137. package/dist/channel.runtime-qrUV6wPE.js +0 -4
  138. package/dist/config-schema-Be3azjB_.js +0 -47
  139. package/dist/monitor.runtime-B2z_5ugU.js +0 -2
  140. package/dist/outbound.runtime-DDTeqaBW.js +0 -3
  141. package/dist/probe.runtime-D13ZtXsd.js +0 -2
  142. package/dist/quick-reply-fallback-D86FwAfr.js +0 -56
  143. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  144. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  145. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  146. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  147. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  148. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  149. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  150. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  151. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  152. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
  153. package/dist/{probe-CM03wo6r.js → probe-4gc_YQ2L.js} +1 -1
@@ -1,8 +1,8 @@
1
1
  import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-BqEVsDoB.js";
2
- import { i as hasLineCredentials, r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-W0Dbgu58.js";
3
- import { i as resolveExactLineGroupConfigKey, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-D86FwAfr.js";
4
- import { t as LineChannelConfigSchema } from "./config-schema-Be3azjB_.js";
5
- import { _ as uriAction, a as createAgendaCard, b as resolveLineOutboundMedia, c as createActionCard, g as postbackAction, i as createMediaPlayerCard, n as createAppleTvRemoteCard, o as createEventCard, p as messageAction, r as createDeviceControlCard, t as createLineSendReceipt, v as buildLineMediaMessage, y as hasLineSpecificMediaOptions } from "./send-receipt-DKZzgHMO.js";
2
+ import { t as hasLineCredentials } from "./account-helpers-BSUyZwLm.js";
3
+ import { _ as LineChannelConfigSchema, a as renderLineCard, d as resolveExactLineGroupConfigKey, h as getLineRuntime, n as createLineQuickReply, o as renderLinePresentation, r as lineMessageActions, t as LINE_PRESENTATION_CAPABILITIES, u as buildLineQuickReplyFallbackText } from "./rich-messages-t_zRL24p.js";
4
+ import { i as createLineSendReceipt, n as resolveLineNonDispatchRetryable, y as buildLineMediaMessage } from "./send-retry-CUvrarfE.js";
5
+ import { r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-DSJ92oJ5.js";
6
6
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
7
7
  import { buildDmGroupAccountAllowlistAdapter, createFlatAllowlistOverrideResolver } from "openclaw/plugin-sdk/allowlist-config-edit";
8
8
  import { buildChannelOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
@@ -10,17 +10,15 @@ import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing
10
10
  import { buildChannelGroupsScopeTree, createRestrictSendersChannelSecurity, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
11
11
  import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
12
12
  import { describeWebhookAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
13
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/account-id";
14
- import { createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
15
- import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
16
- import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
13
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
14
+ import { clearAccountFieldsFromConfigSection, createScopedChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
15
+ import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
17
16
  import { createAccountStatusSink, defineChannelMessageAdapter, listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound";
18
- import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
17
+ import { createChannelPartialDeliveryError, isChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound";
19
18
  import { createAttachedChannelResultAdapter, createEmptyChannelResult } from "openclaw/plugin-sdk/channel-send-result";
19
+ import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime";
20
20
  import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
21
21
  import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
22
- import { resolveMessagePresentationButtonAction, resolveMessagePresentationOptionAction } from "openclaw/plugin-sdk/interactive-runtime";
23
- import { Type } from "typebox";
24
22
  import { buildTokenChannelStatusSummary, createComputedAccountStatusAdapter, createDefaultChannelRuntimeState, createDependentCredentialStatusIssueCollector } from "openclaw/plugin-sdk/status-helpers";
25
23
  //#region extensions/line/src/bindings.ts
26
24
  function normalizeLineConversationId(raw) {
@@ -121,8 +119,8 @@ const lineChannelPluginCommon = {
121
119
  };
122
120
  //#endregion
123
121
  //#region extensions/line/src/gateway.ts
124
- const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-D13ZtXsd.js"));
125
- const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-B2z_5ugU.js"));
122
+ const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-DTxBWvKz.js"));
123
+ const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-Czc3NxNV.js"));
126
124
  const lineGatewayAdapter = {
127
125
  startAccount: async (ctx) => {
128
126
  const account = ctx.account;
@@ -158,22 +156,9 @@ const lineGatewayAdapter = {
158
156
  },
159
157
  logoutAccount: async ({ accountId, cfg }) => {
160
158
  const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() ?? "";
161
- const nextCfg = { ...cfg };
162
- const nextLine = { ...cfg.channels?.line ?? {} };
163
- let cleared = false;
164
- let changed = false;
165
- if (accountId === DEFAULT_ACCOUNT_ID$1) {
166
- if (nextLine.channelAccessToken || nextLine.channelSecret || nextLine.tokenFile || nextLine.secretFile) {
167
- delete nextLine.channelAccessToken;
168
- delete nextLine.channelSecret;
169
- delete nextLine.tokenFile;
170
- delete nextLine.secretFile;
171
- cleared = true;
172
- changed = true;
173
- }
174
- }
175
- const accountCleanup = clearAccountEntryFields({
176
- accounts: nextLine.accounts,
159
+ const { nextConfig, changed, cleared } = clearAccountFieldsFromConfigSection({
160
+ cfg,
161
+ sectionKey: "line",
177
162
  accountId,
178
163
  fields: [
179
164
  "channelAccessToken",
@@ -183,30 +168,12 @@ const lineGatewayAdapter = {
183
168
  ],
184
169
  markClearedOnFieldPresence: true
185
170
  });
186
- if (accountCleanup.changed) {
187
- changed = true;
188
- if (accountCleanup.cleared) cleared = true;
189
- if (accountCleanup.nextAccounts) nextLine.accounts = accountCleanup.nextAccounts;
190
- else delete nextLine.accounts;
191
- }
192
- if (changed) {
193
- if (Object.keys(nextLine).length > 0) nextCfg.channels = {
194
- ...nextCfg.channels,
195
- line: nextLine
196
- };
197
- else {
198
- const nextChannels = { ...nextCfg.channels };
199
- delete nextChannels.line;
200
- if (Object.keys(nextChannels).length > 0) nextCfg.channels = nextChannels;
201
- else delete nextCfg.channels;
202
- }
203
- await getLineRuntime().config.replaceConfigFile({
204
- nextConfig: nextCfg,
205
- afterWrite: { mode: "auto" }
206
- });
207
- }
171
+ if (changed) await getLineRuntime().config.replaceConfigFile({
172
+ nextConfig,
173
+ afterWrite: { mode: "auto" }
174
+ });
208
175
  const loggedOut = resolveLineAccount({
209
- cfg: changed ? nextCfg : cfg,
176
+ cfg: nextConfig,
210
177
  accountId
211
178
  }).tokenSource === "none";
212
179
  return {
@@ -243,234 +210,8 @@ function inferLineTargetChatType(target) {
243
210
  return /^[CR][a-f0-9]{32}$/i.test(normalized) ? "group" : void 0;
244
211
  }
245
212
  //#endregion
246
- //#region extensions/line/src/rich-messages.ts
247
- const nonempty = () => Type.String({ minLength: 1 });
248
- const closed = (properties) => Type.Object(properties, { additionalProperties: false });
249
- const lineCardSchema = Type.Union([
250
- closed({
251
- type: Type.Literal("media_player"),
252
- title: nonempty(),
253
- artist: Type.Optional(nonempty()),
254
- source: Type.Optional(nonempty()),
255
- imageUrl: Type.Optional(Type.String({ pattern: "^https://" })),
256
- status: Type.Optional(Type.Union([Type.Literal("playing"), Type.Literal("paused")]))
257
- }),
258
- closed({
259
- type: Type.Literal("event"),
260
- title: nonempty(),
261
- date: nonempty(),
262
- time: Type.Optional(nonempty()),
263
- location: Type.Optional(nonempty()),
264
- description: Type.Optional(nonempty())
265
- }),
266
- closed({
267
- type: Type.Literal("agenda"),
268
- title: nonempty(),
269
- events: Type.Array(closed({
270
- title: nonempty(),
271
- time: Type.Optional(nonempty()),
272
- location: Type.Optional(nonempty())
273
- }), {
274
- minItems: 1,
275
- maxItems: 6
276
- })
277
- }),
278
- closed({
279
- type: Type.Literal("device"),
280
- name: nonempty(),
281
- deviceType: Type.Optional(nonempty()),
282
- status: Type.Optional(nonempty()),
283
- controls: Type.Optional(Type.Array(closed({
284
- label: nonempty(),
285
- action: nonempty()
286
- }), { maxItems: 6 }))
287
- }),
288
- closed({
289
- type: Type.Literal("appletv_remote"),
290
- name: Type.Optional(nonempty()),
291
- status: Type.Optional(nonempty())
292
- })
293
- ]);
294
- const lineChannelDataSchema = Type.Optional(closed({ line: closed({
295
- location: Type.Optional(closed({
296
- title: nonempty(),
297
- address: nonempty(),
298
- latitude: Type.Number({
299
- minimum: -90,
300
- maximum: 90
301
- }),
302
- longitude: Type.Number({
303
- minimum: -180,
304
- maximum: 180
305
- })
306
- })),
307
- card: Type.Optional(lineCardSchema),
308
- mediaKind: Type.Optional(Type.Union([
309
- Type.Literal("image"),
310
- Type.Literal("video"),
311
- Type.Literal("audio")
312
- ])),
313
- previewImageUrl: Type.Optional(Type.String({ pattern: "^https://" })),
314
- durationMs: Type.Optional(Type.Integer({ minimum: 1 })),
315
- trackingId: Type.Optional(nonempty())
316
- }) }));
317
- const lineMessageActions = {
318
- describeMessageTool: ({ cfg, accountId }) => {
319
- const account = resolveLineAccount({
320
- cfg,
321
- accountId: accountId ?? void 0
322
- });
323
- return account.enabled && hasLineCredentials(account) ? {
324
- actions: ["send"],
325
- capabilities: ["presentation"],
326
- schema: {
327
- actions: ["send"],
328
- properties: { channelData: lineChannelDataSchema }
329
- }
330
- } : {
331
- actions: [],
332
- capabilities: [],
333
- schema: null
334
- };
335
- },
336
- prepareSendPayload: ({ payload }) => payload
337
- };
338
- const LINE_PRESENTATION_CAPABILITIES = {
339
- supported: true,
340
- buttons: true,
341
- selects: true,
342
- context: true,
343
- limits: {
344
- actions: {
345
- maxActions: 4,
346
- maxActionsPerRow: 1,
347
- maxRows: 4,
348
- maxLabelLength: 40
349
- },
350
- selects: {
351
- maxOptions: 13,
352
- maxLabelLength: 20,
353
- maxValueBytes: 300
354
- },
355
- text: { markdownDialect: "plain" }
356
- }
357
- };
358
- function toLineAction(button) {
359
- const normalized = resolveMessagePresentationButtonAction(button);
360
- if (normalized?.type === "command") return messageAction(button.label, normalized.command);
361
- if (normalized?.type === "callback") return postbackAction(button.label, normalized.value, button.label);
362
- if (normalized?.type === "url") return uriAction(button.label, normalized.url);
363
- if (normalized?.type === "web-app" && normalized.url) return uriAction(button.label, normalized.url);
364
- }
365
- function renderLinePresentation(payload, presentation) {
366
- const buttons = presentation.blocks.flatMap((block) => block.type === "buttons" ? block.buttons : []);
367
- const buttonActions = buttons.map(toLineAction);
368
- const options = presentation.blocks.flatMap((block) => block.type === "select" ? block.options : []);
369
- const quickReplyItems = options.flatMap((option) => {
370
- const action = resolveMessagePresentationOptionAction(option);
371
- return action?.type === "command" || action?.type === "callback" ? [{
372
- label: option.label,
373
- action
374
- }] : [];
375
- });
376
- if (buttons.length > 0 && buttonActions.some((action) => !action) || quickReplyItems.length !== options.length || buttons.length === 0 && options.length === 0) return null;
377
- const lineData = isRecord(payload.channelData?.line) ? payload.channelData.line : {};
378
- const text = presentation.blocks.flatMap((block) => block.type === "text" || block.type === "context" ? [block.text] : []).join("\n");
379
- const title = presentation.title || "Choose an option";
380
- const flexMessage = buttonActions.length > 0 ? {
381
- altText: title,
382
- contents: createActionCard(title, text || "Choose an option.", buttons.map((button, index) => ({
383
- label: button.label,
384
- action: buttonActions[index]
385
- })))
386
- } : void 0;
387
- return {
388
- ...payload,
389
- channelData: {
390
- ...payload.channelData,
391
- line: {
392
- ...lineData,
393
- ...flexMessage ? { flexMessage } : {},
394
- quickReplyItems
395
- }
396
- }
397
- };
398
- }
399
- const toSlug = (value) => normalizeLowercaseStringOrEmpty(value).replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "device";
400
- const lineActionData = (action, device) => `line.action=${encodeURIComponent(action)}&line.device=${encodeURIComponent(device)}`;
401
- function renderLineCard(card) {
402
- if (card.type === "media_player") {
403
- const device = toSlug(card.source || card.title);
404
- return {
405
- altText: `🎵 ${card.title}${card.artist ? ` - ${card.artist}` : ""}`,
406
- contents: createMediaPlayerCard({
407
- title: card.title,
408
- subtitle: card.artist,
409
- source: card.source,
410
- imageUrl: card.imageUrl,
411
- isPlaying: card.status ? card.status === "playing" : void 0,
412
- controls: Object.fromEntries([
413
- "previous",
414
- "play",
415
- "pause",
416
- "next"
417
- ].map((action) => [action, { data: lineActionData(action, device) }]))
418
- })
419
- };
420
- }
421
- if (card.type === "event") return {
422
- altText: `📅 ${card.title} - ${card.date}${card.time ? ` ${card.time}` : ""}`,
423
- contents: createEventCard(card)
424
- };
425
- if (card.type === "agenda") return {
426
- altText: `📋 ${card.title} (${card.events.length} events)`,
427
- contents: createAgendaCard(card)
428
- };
429
- const device = toSlug(card.type === "device" ? card.name : card.name || "apple_tv");
430
- if (card.type === "device") return {
431
- altText: `📱 ${card.name}${card.status ? `: ${card.status}` : ""}`,
432
- contents: createDeviceControlCard({
433
- deviceName: card.name,
434
- deviceType: card.deviceType,
435
- status: card.status,
436
- controls: (card.controls ?? []).map((control) => ({
437
- label: control.label,
438
- data: lineActionData(control.action, device)
439
- }))
440
- })
441
- };
442
- const actionData = {
443
- up: lineActionData("up", device),
444
- down: lineActionData("down", device),
445
- left: lineActionData("left", device),
446
- right: lineActionData("right", device),
447
- select: lineActionData("select", device),
448
- menu: lineActionData("menu", device),
449
- home: lineActionData("home", device),
450
- play: lineActionData("play", device),
451
- pause: lineActionData("pause", device),
452
- volumeUp: lineActionData("volume_up", device),
453
- volumeDown: lineActionData("volume_down", device),
454
- mute: lineActionData("mute", device)
455
- };
456
- return {
457
- altText: `📺 ${card.name || "Apple TV"} Remote`,
458
- contents: createAppleTvRemoteCard({
459
- deviceName: card.name || "Apple TV",
460
- status: card.status,
461
- actionData
462
- })
463
- };
464
- }
465
- function createLineQuickReply(items) {
466
- return { items: items.slice(0, 13).map((item) => ({
467
- type: "action",
468
- action: item.action.type === "command" ? messageAction(item.label, item.action.command) : postbackAction(item.label, item.action.value, item.label)
469
- })) };
470
- }
471
- //#endregion
472
213
  //#region extensions/line/src/outbound.ts
473
- const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-DDTeqaBW.js"));
214
+ const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-CyleVzFR.js"));
474
215
  const lineOutboundAdapter = {
475
216
  deliveryMode: "direct",
476
217
  chunker: (text, limit) => getLineRuntime().channel.text.chunkMarkdownText(text, limit),
@@ -496,9 +237,23 @@ const lineOutboundAdapter = {
496
237
  const sendLocation = lineRuntime?.pushLocationMessage ?? outboundRuntime.pushLocationMessage;
497
238
  const sendQuickReplies = lineRuntime?.pushTextMessageWithQuickReplies ?? outboundRuntime.pushTextMessageWithQuickReplies;
498
239
  const buildTemplate = lineRuntime?.buildTemplateMessageFromPayload ?? outboundRuntime.buildTemplateMessageFromPayload;
240
+ const sendOptions = {
241
+ verbose: false,
242
+ cfg,
243
+ accountId: accountId ?? void 0
244
+ };
499
245
  let lastResult = null;
500
246
  const recordResult = async (resultPromise) => {
501
- const result = await resultPromise;
247
+ let result;
248
+ try {
249
+ result = await resultPromise;
250
+ } catch (error) {
251
+ const retryable = isChannelPartialDeliveryError(error) ? void 0 : resolveLineNonDispatchRetryable(error);
252
+ throw lastResult === null && retryable !== void 0 ? new PlatformMessageNotDispatchedError(error instanceof Error ? error.message : "LINE rejected the message", {
253
+ cause: error,
254
+ retryable
255
+ }) : error;
256
+ }
502
257
  lastResult = result;
503
258
  try {
504
259
  await onDeliveryResult?.(createEmptyChannelResult("line", { ...result }));
@@ -520,11 +275,7 @@ const lineOutboundAdapter = {
520
275
  if (messages.length === 0) return;
521
276
  for (let i = 0; i < messages.length; i += 5) {
522
277
  const batch = messages.slice(i, i + 5);
523
- await recordResult(sendBatch(to, batch, {
524
- verbose: false,
525
- cfg,
526
- accountId: accountId ?? void 0
527
- }));
278
+ await recordResult(sendBatch(to, batch, sendOptions));
528
279
  }
529
280
  };
530
281
  const sendTextWithQuickReply = async (text) => {
@@ -536,11 +287,7 @@ const lineOutboundAdapter = {
536
287
  }]);
537
288
  return;
538
289
  }
539
- await recordResult(sendQuickReplies(to, text, quickReplies, {
540
- verbose: false,
541
- cfg,
542
- accountId: accountId ?? void 0
543
- }));
290
+ await recordResult(sendQuickReplies(to, text, quickReplies, sendOptions));
544
291
  };
545
292
  const processed = payload.text ? outboundRuntime.processLineMessage(payload.text) : {
546
293
  text: "",
@@ -553,9 +300,8 @@ const lineOutboundAdapter = {
553
300
  })));
554
301
  const chunks = orderedMessages ? orderedMessages.flatMap((message) => message.type === "text" ? [message.text] : []) : processed.text ? runtime.channel.text.chunkMarkdownText(processed.text, chunkLimit) : [];
555
302
  const mediaUrls = resolveOutboundMediaUrls(payload);
556
- const useLineSpecificMedia = hasLineSpecificMediaOptions(lineData);
557
303
  const mediaOptions = {
558
- mediaKind: useLineSpecificMedia ? lineData.mediaKind : "image",
304
+ mediaKind: lineData.mediaKind,
559
305
  previewImageUrl: lineData.previewImageUrl,
560
306
  durationMs: lineData.durationMs,
561
307
  trackingId: lineData.trackingId
@@ -565,82 +311,39 @@ const lineOutboundAdapter = {
565
311
  for (const url of mediaUrls) {
566
312
  const trimmed = url?.trim();
567
313
  if (!trimmed) continue;
568
- if (!useLineSpecificMedia) {
569
- await recordResult((lineRuntime?.sendMessageLine ?? outboundRuntime.sendMessageLine)(to, "", {
570
- verbose: false,
571
- mediaUrl: trimmed,
572
- cfg,
573
- accountId: accountId ?? void 0
574
- }));
575
- continue;
576
- }
577
- const resolved = await resolveLineOutboundMedia(trimmed, mediaOptions);
578
314
  await recordResult((lineRuntime?.sendMessageLine ?? outboundRuntime.sendMessageLine)(to, "", {
579
- verbose: false,
580
- mediaUrl: resolved.mediaUrl,
581
- mediaKind: resolved.mediaKind,
582
- previewImageUrl: resolved.previewImageUrl,
583
- durationMs: resolved.durationMs,
584
- trackingId: resolved.trackingId,
585
- cfg,
586
- accountId: accountId ?? void 0
315
+ ...sendOptions,
316
+ ...mediaOptions,
317
+ mediaUrl: trimmed
587
318
  }));
588
319
  }
589
320
  };
590
321
  if (!shouldSendQuickRepliesInline) {
591
322
  if (lineData.flexMessage) {
592
323
  const flexContents = lineData.flexMessage.contents;
593
- await recordResult(sendFlex(to, lineData.flexMessage.altText, flexContents, {
594
- verbose: false,
595
- cfg,
596
- accountId: accountId ?? void 0
597
- }));
324
+ await recordResult(sendFlex(to, lineData.flexMessage.altText, flexContents, sendOptions));
598
325
  }
599
326
  if (lineData.templateMessage) {
600
327
  const template = buildTemplate(lineData.templateMessage);
601
- if (template) await recordResult(sendTemplate(to, template, {
602
- verbose: false,
603
- cfg,
604
- accountId: accountId ?? void 0
605
- }));
328
+ if (template) await recordResult(sendTemplate(to, template, sendOptions));
606
329
  }
607
- if (location) await recordResult(sendLocation(to, location, {
608
- verbose: false,
609
- cfg,
610
- accountId: accountId ?? void 0
611
- }));
612
- if (!orderedMessages) for (const flexMsg of processed.flexMessages) await recordResult(sendFlex(to, flexMsg.altText, flexMsg.contents, {
613
- verbose: false,
614
- cfg,
615
- accountId: accountId ?? void 0
616
- }));
330
+ if (location) await recordResult(sendLocation(to, location, sendOptions));
331
+ if (!orderedMessages) for (const flexMsg of processed.flexMessages) await recordResult(sendFlex(to, flexMsg.altText, flexMsg.contents, sendOptions));
617
332
  }
618
333
  const sendMediaAfterText = !(hasQuickReplies && chunks.length > 0);
619
334
  if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && !sendMediaAfterText) await sendMediaMessages();
620
- if (orderedMessages) for (const [index, message] of orderedMessages.entries()) {
335
+ if (orderedMessages && !shouldSendQuickRepliesInline) for (const [index, message] of orderedMessages.entries()) {
621
336
  const isLast = index === orderedMessages.length - 1;
622
337
  if (message.type === "flex") if (isLast && quickReply) await sendMessageBatch([{
623
338
  ...message,
624
339
  quickReply
625
340
  }]);
626
- else await recordResult(sendFlex(to, message.altText, message.contents, {
627
- verbose: false,
628
- cfg,
629
- accountId: accountId ?? void 0
630
- }));
341
+ else await recordResult(sendFlex(to, message.altText, message.contents, sendOptions));
631
342
  else if (isLast && hasQuickReplies) await sendTextWithQuickReply(message.text);
632
- else await recordResult(sendText(to, message.text, {
633
- verbose: false,
634
- cfg,
635
- accountId: accountId ?? void 0
636
- }));
343
+ else await recordResult(sendText(to, message.text, sendOptions));
637
344
  }
638
345
  else if (chunks.length > 0) for (const [i, chunk] of chunks.entries()) if (i === chunks.length - 1 && hasQuickReplies) await sendTextWithQuickReply(chunk);
639
- else await recordResult(sendText(to, chunk, {
640
- verbose: false,
641
- cfg,
642
- accountId: accountId ?? void 0
643
- }));
346
+ else await recordResult(sendText(to, chunk, sendOptions));
644
347
  else if (shouldSendQuickRepliesInline) {
645
348
  const quickReplyMessages = [];
646
349
  if (lineData.flexMessage) quickReplyMessages.push(outboundRuntime.createFlexMessage(lineData.flexMessage.altText, lineData.flexMessage.contents));
@@ -740,7 +443,7 @@ const lineMessageAdapter = defineChannelMessageAdapter({
740
443
  });
741
444
  //#endregion
742
445
  //#region extensions/line/src/status.ts
743
- const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-D13ZtXsd.js"));
446
+ const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime-DTxBWvKz.js"));
744
447
  const collectLineStatusIssues = createDependentCredentialStatusIssueCollector({
745
448
  channel: "line",
746
449
  dependencySourceKey: "tokenSource",
@@ -748,7 +451,7 @@ const collectLineStatusIssues = createDependentCredentialStatusIssueCollector({
748
451
  missingDependentMessage: "LINE channel secret not configured"
749
452
  });
750
453
  const lineStatusAdapter = createComputedAccountStatusAdapter({
751
- defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID$1),
454
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
752
455
  collectStatusIssues: collectLineStatusIssues,
753
456
  buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot),
754
457
  probeAccount: async ({ account, timeoutMs }) => await (await loadLineProbeRuntime()).probeLineBot(account.channelAccessToken, timeoutMs),
@@ -768,7 +471,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
768
471
  });
769
472
  //#endregion
770
473
  //#region extensions/line/src/channel.ts
771
- const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-qrUV6wPE.js"));
474
+ const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-Bty5U_1N.js"));
772
475
  const lineSecurityAdapter = createRestrictSendersChannelSecurity({
773
476
  channelKey: "line",
774
477
  resolveDmPolicy: (account) => account.config.dmPolicy,
@@ -1,2 +1,2 @@
1
- import { t as linePlugin } from "./channel-DmoKZXmG.js";
1
+ import { t as linePlugin } from "./channel-DpMgRaxl.js";
2
2
  export { linePlugin };
@@ -0,0 +1,4 @@
1
+ import { t as monitorLineProvider } from "./monitor-Ch6TIatG.js";
2
+ import { t as probeLineBot } from "./probe-4gc_YQ2L.js";
3
+ import { d as pushMessageLine } from "./send-B-K2B6au.js";
4
+ export { monitorLineProvider, probeLineBot, pushMessageLine };
@@ -0,0 +1,141 @@
1
+ import { a as eventIdFor, i as errorText, o as laneKeyFor, r as LineWebhookPayloadError, s as legacyEventIdFor, t as LINE_WEBHOOK_SPOOL_INVALID_EVENT_REASON } from "./webhook-spool-contract-CNe2h1I2.js";
2
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ //#region extensions/line/src/webhook-spool-migration.ts
4
+ /** Pre-drain (#109655) rows stored the event object under `event` instead of the
5
+ * canonical serialized `rawEvent`; anything else is not a migratable row. */
6
+ function parseLegacySpoolPayload(payload) {
7
+ if (!isRecord(payload) || "rawEvent" in payload) return null;
8
+ if (typeof payload.destination !== "string") return null;
9
+ if (!isRecord(payload.event)) return null;
10
+ return {
11
+ destination: payload.destination,
12
+ event: payload.event
13
+ };
14
+ }
15
+ /** A dead-letter carries the pre-fix decoder's signature when the canonical spool
16
+ * rejected a pre-drain row before this migration existed. The identity fence and
17
+ * ordinary delivery failures write different reasons or messages, so neither is
18
+ * ever treated as recoverable. */
19
+ function isLegacyDecodeDeadLetter(row) {
20
+ return row.reason === "invalid-event" && row.message === "LINE webhook spool payload is invalid." && parseLegacySpoolPayload(row.payload) !== null;
21
+ }
22
+ /** Counts pre-drain rows (pending, still claimed, or decoder-dead-lettered)
23
+ * without mutating anything. */
24
+ async function countLegacySpoolRows(queue) {
25
+ const pending = await queue.listPending({ limit: "all" });
26
+ const claims = await queue.listClaims();
27
+ const failed = await queue.listFailed?.({ limit: "all" }) ?? [];
28
+ return pending.filter((record) => parseLegacySpoolPayload(record.payload) !== null).length + claims.filter((claim) => parseLegacySpoolPayload(claim.payload) !== null).length + failed.filter((row) => isLegacyDecodeDeadLetter(row)).length;
29
+ }
30
+ /** One-time upgrade migration: rewrite pre-drain (#109655) rows into the canonical
31
+ * payload and message:/event: keyspace before the spool drains, so the runtime
32
+ * reader keeps a single row contract. Idempotent — enqueue deduplicates by id, so
33
+ * a rerun after a crash only re-completes the leftover legacy rows. */
34
+ async function migrateLineLegacySpoolRows(queue) {
35
+ const result = {
36
+ migrated: 0,
37
+ reconciled: 0,
38
+ deadLettered: 0,
39
+ recovered: 0,
40
+ failures: []
41
+ };
42
+ await queue.recoverStaleClaims({
43
+ staleMs: 0,
44
+ shouldRecover: (claim) => parseLegacySpoolPayload(claim.payload) !== null
45
+ });
46
+ const failed = await queue.listFailed?.({ limit: "all" }) ?? [];
47
+ for (const row of failed) {
48
+ if (!isLegacyDecodeDeadLetter(row)) continue;
49
+ try {
50
+ if ((await queue.resubmit?.(row.id, { resubmittedAt: row.receivedAt }))?.kind === "resubmitted") result.recovered += 1;
51
+ } catch (error) {
52
+ result.failures.push(`row ${row.id}: ${errorText(error)}`);
53
+ }
54
+ }
55
+ const pending = await queue.listPending({
56
+ limit: "all",
57
+ orderBy: "received"
58
+ });
59
+ for (const record of pending) {
60
+ const legacy = parseLegacySpoolPayload(record.payload);
61
+ if (!legacy) continue;
62
+ let eventId;
63
+ try {
64
+ if (record.id !== legacyEventIdFor(legacy.event)) throw new LineWebhookPayloadError("LINE webhook event identity changed after durable admission.");
65
+ eventId = eventIdFor(legacy.event);
66
+ } catch (error) {
67
+ await queue.fail(record.id, {
68
+ reason: LINE_WEBHOOK_SPOOL_INVALID_EVENT_REASON,
69
+ message: errorText(error)
70
+ });
71
+ result.deadLettered += 1;
72
+ continue;
73
+ }
74
+ try {
75
+ const admitted = await queue.enqueue(eventId, {
76
+ version: 1,
77
+ rawEvent: JSON.stringify(legacy.event),
78
+ destination: legacy.destination
79
+ }, {
80
+ receivedAt: record.receivedAt,
81
+ laneKey: laneKeyFor(legacy.event, eventId)
82
+ });
83
+ await queue.complete(record.id);
84
+ if (admitted.kind === "completed" || admitted.kind === "failed") result.reconciled += 1;
85
+ else result.migrated += 1;
86
+ } catch (error) {
87
+ result.failures.push(`row ${record.id}: ${errorText(error)}`);
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ //#endregion
93
+ //#region extensions/line/doctor-contract-api.ts
94
+ const LINE_CHANNEL_ID = "line";
95
+ /** Pre-drain rows can outlive the account config that admitted them, so the sweep
96
+ * enumerates accounts from the host's queue lane instead of the config; a host
97
+ * without the lane fails visibly rather than silently skipping the migration. */
98
+ function lineSpoolQueueAccess(context) {
99
+ const access = context.channelIngressQueues?.find((entry) => entry.channelId === LINE_CHANNEL_ID);
100
+ if (!access) throw new Error("LINE pre-drain spool migration requires the doctor host's channel ingress queue access.");
101
+ return access;
102
+ }
103
+ /** Doctor-owned upgrade migration for pre-drain (#109655) webhook spool rows. */
104
+ const stateMigrations = [{
105
+ id: "line-pre-drain-spool-rows",
106
+ label: "LINE pre-drain webhook spool rows",
107
+ async detectLegacyState(params) {
108
+ const spool = lineSpoolQueueAccess(params.context);
109
+ const preview = [];
110
+ for (const accountId of await spool.listChannelIngressQueueAccountIds()) {
111
+ const count = await countLegacySpoolRows(spool.openChannelIngressQueueForInspection({ accountId }));
112
+ if (count > 0) preview.push(`- LINE pre-drain spool rows (account "${accountId}"): ${count} row(s) -> canonical ingress contract`);
113
+ }
114
+ return preview.length > 0 ? { preview } : null;
115
+ },
116
+ async migrateLegacyState(params) {
117
+ const spool = lineSpoolQueueAccess(params.context);
118
+ const lineConfig = params.config.channels?.line;
119
+ const configuredAccountIds = /* @__PURE__ */ new Set([...Object.keys(lineConfig?.accounts ?? {}), ...lineConfig ? ["default"] : []]);
120
+ const openForMigration = spool.openChannelIngressQueue;
121
+ if (!openForMigration) throw new Error("LINE pre-drain spool migration requires mutable ingress access from the doctor host's exclusive repair section.");
122
+ const changes = [];
123
+ const warnings = [];
124
+ for (const accountId of await spool.listChannelIngressQueueAccountIds()) {
125
+ const result = await migrateLineLegacySpoolRows(openForMigration({ accountId }));
126
+ if (result.migrated > 0 || result.reconciled > 0 || result.deadLettered > 0 || result.recovered > 0) {
127
+ const recovered = result.recovered > 0 ? ` (${result.recovered} recovered from the dead-letter table)` : "";
128
+ const reconciled = result.reconciled > 0 ? `, ${result.reconciled} already settled under the canonical id` : "";
129
+ const dispatchNote = configuredAccountIds.has(accountId) ? "" : ` (account not currently configured, so these stay queued until it is restored)`;
130
+ changes.push(`Migrated LINE pre-drain spool rows (account "${accountId}"): ${result.migrated} queued under the canonical contract, ${result.deadLettered} dead-lettered at the identity fence${reconciled}${recovered}${dispatchNote}`);
131
+ }
132
+ for (const failure of result.failures) warnings.push(`Failed migrating a LINE pre-drain spool row (account "${accountId}", ${failure}); the row stays pending and the migration retries on the next run`);
133
+ }
134
+ return {
135
+ changes,
136
+ warnings
137
+ };
138
+ }
139
+ }];
140
+ //#endregion
141
+ export { stateMigrations };