@openclaw/line 2026.7.1-beta.6 → 2026.7.2-beta.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.
package/dist/api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-C_8hLniz.js";
1
+ import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-CnPxxRbJ.js";
2
2
  import { n as lineSetupAdapter, t as lineSetupWizard } from "./setup-surface-Bc36YSS3.js";
3
3
  //#region extensions/line/src/channel.setup.ts
4
4
  const lineSetupPlugin = {
@@ -1,6 +1,7 @@
1
- import { c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, m as createImageCard, o as messageAction, s as postbackAction } from "./flex-templates-DEIXn69v.js";
1
+ import { c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, m as createImageCard, o as messageAction, s as postbackAction } from "./flex-templates-K1lS9i8L.js";
2
2
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
4
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
4
5
  //#region extensions/line/src/card-command.ts
5
6
  const CARD_USAGE = `Usage: /card <type> "title" "body" [options]
6
7
 
@@ -92,9 +93,12 @@ function parseCardArgs(argsStrInput) {
92
93
  }
93
94
  const quotedRegex = /"([^"]*?)"/g;
94
95
  let match;
95
- while ((match = quotedRegex.exec(argsStr)) !== null) result.args.push(match[1]);
96
+ while ((match = quotedRegex.exec(argsStr)) !== null) result.args.push(expectDefined(match[1], "quoted card argument capture"));
96
97
  const flagRegex = /--(\w+)\s+(?:"([^"]*?)"|(\S+))/g;
97
- while ((match = flagRegex.exec(argsStr)) !== null) result.flags[match[1]] = match[2] ?? match[3];
98
+ while ((match = flagRegex.exec(argsStr)) !== null) {
99
+ const key = expectDefined(match[1], "card flag name capture");
100
+ result.flags[key] = expectDefined(match[2] ?? match[3], "card flag value capture");
101
+ }
98
102
  return result;
99
103
  }
100
104
  function registerLineCardCommand(api) {
@@ -1,10 +1,10 @@
1
1
  import { i as resolveLineAccount, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-DJVOv1JI.js";
2
2
  import { n as lineSetupAdapter, r as hasLineCredentials, t as lineSetupWizard } from "./setup-surface-Bc36YSS3.js";
3
- import { a as resolveLineOutboundMedia, d as resolveExactLineGroupConfigKey, i as buildLineQuickReplyFallbackText, l as LineChannelConfigSchema, n as parseLineDirectives, r as createLineSendReceipt, s as getLineRuntime, t as hasLineDirectives } from "./reply-payload-transform-C7r5npxP.js";
3
+ import { a as resolveLineOutboundMedia, d as resolveExactLineGroupConfigKey, i as buildLineQuickReplyFallbackText, l as LineChannelConfigSchema, n as parseLineDirectives, r as createLineSendReceipt, s as getLineRuntime, t as hasLineDirectives } from "./reply-payload-transform-BsbwCJGH.js";
4
4
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
5
5
  import { buildChannelOutboundSessionRoute, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
6
6
  import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
7
- import { createRestrictSendersChannelSecurity, resolveChannelGroupRequireMention } from "openclaw/plugin-sdk/channel-policy";
7
+ import { buildChannelGroupsScopeTree, createRestrictSendersChannelSecurity, resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
8
8
  import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
9
9
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/account-id";
10
10
  import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
@@ -108,7 +108,7 @@ const lineChannelPluginCommon = {
108
108
  //#endregion
109
109
  //#region extensions/line/src/gateway.ts
110
110
  const loadLineProbeRuntime$1 = createLazyRuntimeModule(() => import("./probe.runtime-CRhVGqwl.js"));
111
- const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-BGIpxzfB.js"));
111
+ const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime-BiNrVz5G.js"));
112
112
  const lineGatewayAdapter = {
113
113
  startAccount: async (ctx) => {
114
114
  const account = ctx.account;
@@ -198,16 +198,14 @@ const lineGatewayAdapter = {
198
198
  //#endregion
199
199
  //#region extensions/line/src/group-policy.ts
200
200
  function resolveLineGroupRequireMention(params) {
201
- const exactGroupId = resolveExactLineGroupConfigKey({
202
- cfg: params.cfg,
203
- accountId: params.accountId,
201
+ const tree = buildChannelGroupsScopeTree(params.cfg, "line", params.accountId);
202
+ const matchedKey = resolveExactLineGroupConfigKey({
203
+ groups: tree.scopes,
204
204
  groupId: params.groupId
205
205
  });
206
- return resolveChannelGroupRequireMention({
207
- cfg: params.cfg,
208
- channel: "line",
209
- groupId: exactGroupId ?? params.groupId,
210
- accountId: params.accountId
206
+ return resolveScopeRequireMention({
207
+ tree,
208
+ path: matchedKey ? [matchedKey] : []
211
209
  });
212
210
  }
213
211
  //#endregion
@@ -225,7 +223,7 @@ function inferLineTargetChatType(target) {
225
223
  }
226
224
  //#endregion
227
225
  //#region extensions/line/src/outbound.ts
228
- const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-BwC7Y2U9.js"));
226
+ const loadLineOutboundRuntime = createLazyRuntimeModule(() => import("./outbound.runtime-CI-t7Z8P.js"));
229
227
  function isLineUserTarget(target) {
230
228
  const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
231
229
  return /^U/i.test(normalized);
@@ -367,12 +365,12 @@ const lineOutboundAdapter = {
367
365
  }
368
366
  const sendMediaAfterText = !(hasQuickReplies && chunks.length > 0);
369
367
  if (mediaUrls.length > 0 && !shouldSendQuickRepliesInline && !sendMediaAfterText) await sendMediaMessages();
370
- if (chunks.length > 0) for (let i = 0; i < chunks.length; i += 1) if (i === chunks.length - 1 && hasQuickReplies) await recordResult(sendQuickReplies(to, chunks[i], quickReplies, {
368
+ if (chunks.length > 0) for (const [i, chunk] of chunks.entries()) if (i === chunks.length - 1 && hasQuickReplies) await recordResult(sendQuickReplies(to, chunk, quickReplies, {
371
369
  verbose: false,
372
370
  cfg,
373
371
  accountId: accountId ?? void 0
374
372
  }));
375
- else await recordResult(sendText(to, chunks[i], {
373
+ else await recordResult(sendText(to, chunk, {
376
374
  verbose: false,
377
375
  cfg,
378
376
  accountId: accountId ?? void 0
@@ -562,7 +560,7 @@ const lineStatusAdapter = createComputedAccountStatusAdapter({
562
560
  });
563
561
  //#endregion
564
562
  //#region extensions/line/src/channel.ts
565
- const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-Bh_AQM0k.js"));
563
+ const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime-FPcRtjcT.js"));
566
564
  const lineSecurityAdapter = createRestrictSendersChannelSecurity({
567
565
  channelKey: "line",
568
566
  resolveDmPolicy: (account) => account.config.dmPolicy,
@@ -1,2 +1,2 @@
1
- import { t as linePlugin } from "./channel-C_8hLniz.js";
1
+ import { t as linePlugin } from "./channel-CnPxxRbJ.js";
2
2
  export { linePlugin };
@@ -0,0 +1,4 @@
1
+ import { t as monitorLineProvider } from "./monitor-q3UE2J1g.js";
2
+ import { t as probeLineBot } from "./probe-BslD77tJ.js";
3
+ import { S as pushMessageLine } from "./markdown-to-line-CixmAKiG.js";
4
+ export { monitorLineProvider, probeLineBot, pushMessageLine };
@@ -1081,8 +1081,7 @@ function createDeviceControlCard(params) {
1081
1081
  const limitedControls = controls.slice(0, 6);
1082
1082
  for (let i = 0; i < limitedControls.length; i += 2) {
1083
1083
  const rowButtons = [];
1084
- for (let j = i; j < Math.min(i + 2, limitedControls.length); j++) {
1085
- const ctrl = limitedControls[j];
1084
+ for (const [offset, ctrl] of limitedControls.slice(i, i + 2).entries()) {
1086
1085
  const buttonLabel = ctrl.icon ? `${ctrl.icon} ${ctrl.label}` : ctrl.label;
1087
1086
  rowButtons.push({
1088
1087
  type: "button",
@@ -1094,7 +1093,7 @@ function createDeviceControlCard(params) {
1094
1093
  style: ctrl.style ?? "secondary",
1095
1094
  flex: 1,
1096
1095
  height: "sm",
1097
- margin: j > i ? "md" : void 0
1096
+ margin: offset > 0 ? "md" : void 0
1098
1097
  });
1099
1098
  }
1100
1099
  if (rowButtons.length === 1) rowButtons.push({ type: "filler" });
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
4
4
  function createLineCardCommandLoader(api) {
5
5
  return createLazyRuntimeModule(async () => {
6
6
  let registered = null;
7
- const { registerLineCardCommand } = await import("./card-command--jtkAqp8.js");
7
+ const { registerLineCardCommand } = await import("./card-command-DQ-epsgt.js");
8
8
  registerLineCardCommand({
9
9
  ...api,
10
10
  registerCommand(command) {
@@ -1,7 +1,8 @@
1
1
  import { i as resolveLineAccount } from "./accounts-DJVOv1JI.js";
2
- import { o as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-C7r5npxP.js";
3
- import { c as uriAction, d as createReceiptCard, o as messageAction, s as postbackAction, t as toFlexMessage } from "./flex-templates-DEIXn69v.js";
2
+ import { o as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-BsbwCJGH.js";
3
+ import { c as uriAction, d as createReceiptCard, o as messageAction, s as postbackAction, t as toFlexMessage } from "./flex-templates-K1lS9i8L.js";
4
4
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
5
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
5
6
  import { messagingApi } from "@line/bot-sdk";
6
7
  import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
8
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
@@ -60,14 +61,15 @@ function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
60
61
  * Create a button template with title, text, and action buttons
61
62
  */
62
63
  function createButtonTemplate(title, text, actions, options) {
64
+ const normalizedTitle = title || void 0;
63
65
  const textLimit = resolveTemplateTextLimit({
64
- title,
66
+ title: normalizedTitle,
65
67
  thumbnailImageUrl: options?.thumbnailImageUrl,
66
68
  textOnlyLimit: 160
67
69
  });
68
70
  const template = {
69
71
  type: "buttons",
70
- title: truncateTemplateText(title, 40),
72
+ ...normalizedTitle ? { title: truncateTemplateText(normalizedTitle, 40) } : {},
71
73
  text: truncateTemplateText(text, textLimit),
72
74
  actions: actions.slice(0, 4),
73
75
  thumbnailImageUrl: options?.thumbnailImageUrl,
@@ -78,7 +80,7 @@ function createButtonTemplate(title, text, actions, options) {
78
80
  };
79
81
  return {
80
82
  type: "template",
81
- altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(`${title}: ${text}`, 400),
83
+ altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(normalizedTitle ? `${normalizedTitle}: ${text}` : text, 400),
82
84
  template
83
85
  };
84
86
  }
@@ -224,7 +226,7 @@ function normalizeTarget(to) {
224
226
  if (!trimmed) throw new Error("Recipient is required for LINE sends");
225
227
  const normalized = trimmed.replace(/^line:group:/i, "").replace(/^line:room:/i, "").replace(/^line:user:/i, "").replace(/^line:/i, "");
226
228
  if (!normalized) throw new Error("Recipient is required for LINE sends");
227
- if (normalized.length >= 33 && !/^[CUR]/.test(normalized)) throw new Error(`Recipient is not a valid LINE id (case-sensitive; expected leading capital C/U/R): ${normalized.slice(0, 4)}…`);
229
+ if (normalized.length >= 33 && !/^[CUR]/.test(normalized)) throw new Error(`Recipient is not a valid LINE id (case-sensitive; expected leading capital C/U/R): ${truncateUtf16Safe(normalized, 4)}…`);
228
230
  return normalized;
229
231
  }
230
232
  function isLineUserChatId(chatId) {
@@ -498,9 +500,9 @@ function extractMarkdownTables(text) {
498
500
  let match;
499
501
  const matches = [];
500
502
  while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) {
501
- const fullMatch = match[0];
502
- const headerLine = match[1];
503
- const bodyLines = match[2];
503
+ const fullMatch = expectDefined(match[0], "Markdown table match");
504
+ const headerLine = expectDefined(match[1], "Markdown table header capture");
505
+ const bodyLines = expectDefined(match[2], "Markdown table body capture");
504
506
  const headers = parseTableRow(headerLine);
505
507
  const rows = bodyLines.trim().split(/[\r\n]+/).filter((line) => line.trim()).map(parseTableRow);
506
508
  if (headers.length > 0 && rows.length > 0) matches.push({
@@ -511,8 +513,7 @@ function extractMarkdownTables(text) {
511
513
  }
512
514
  });
513
515
  }
514
- for (let i = matches.length - 1; i >= 0; i--) {
515
- const { fullMatch, table } = matches[i];
516
+ for (const { fullMatch, table } of matches.toReversed()) {
516
517
  tables.unshift(table);
517
518
  textWithoutTables = textWithoutTables.replace(fullMatch, "");
518
519
  }
@@ -627,9 +628,9 @@ function extractCodeBlocks(text) {
627
628
  let match;
628
629
  const matches = [];
629
630
  while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) {
630
- const fullMatch = match[0];
631
+ const fullMatch = expectDefined(match[0], "Markdown code block match");
631
632
  const language = match[1] || void 0;
632
- const code = match[2];
633
+ const code = expectDefined(match[2], "Markdown code body capture");
633
634
  matches.push({
634
635
  fullMatch,
635
636
  block: {
@@ -638,8 +639,7 @@ function extractCodeBlocks(text) {
638
639
  }
639
640
  });
640
641
  }
641
- for (let i = matches.length - 1; i >= 0; i--) {
642
- const { fullMatch, block } = matches[i];
642
+ for (const { fullMatch, block } of matches.toReversed()) {
643
643
  codeBlocks.unshift(block);
644
644
  textWithoutCode = textWithoutCode.replace(fullMatch, "");
645
645
  }
@@ -692,8 +692,8 @@ function extractLinks(text) {
692
692
  MARKDOWN_LINK_REGEX.lastIndex = 0;
693
693
  let match;
694
694
  while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) links.push({
695
- text: match[1],
696
- url: match[2]
695
+ text: expectDefined(match[1], "Markdown link text capture"),
696
+ url: expectDefined(match[2], "Markdown link URL capture")
697
697
  });
698
698
  return {
699
699
  links,
@@ -756,7 +756,7 @@ function processLineMessage(text) {
756
756
  }
757
757
  const { textWithLinks } = extractLinks(processedText);
758
758
  processedText = textWithLinks;
759
- processedText = stripMarkdown(processedText);
759
+ processedText = stripMarkdown(processedText, { assistantTranscriptRoleHeaders: true });
760
760
  return {
761
761
  text: processedText,
762
762
  flexMessages
@@ -1,11 +1,12 @@
1
1
  import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-DJVOv1JI.js";
2
- import { f as resolveLineGroupConfigEntry, i as buildLineQuickReplyFallbackText, s as getLineRuntime } from "./reply-payload-transform-C7r5npxP.js";
3
- import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, E as replyMessageLine, O as showLoadingAnimation, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, c as processLineMessage, d as createFlexMessage, f as createImageMessage, h as createTextMessageWithQuickReplies, m as createQuickReplyItems, p as createLocationMessage } from "./markdown-to-line-DmpZY78n.js";
2
+ import { f as resolveLineGroupConfigEntry, i as buildLineQuickReplyFallbackText, s as getLineRuntime } from "./reply-payload-transform-BsbwCJGH.js";
3
+ import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, E as replyMessageLine, O as showLoadingAnimation, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, c as processLineMessage, d as createFlexMessage, f as createImageMessage, h as createTextMessageWithQuickReplies, m as createQuickReplyItems, p as createLocationMessage } from "./markdown-to-line-CixmAKiG.js";
4
4
  import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
5
5
  import { normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-outbound";
7
7
  import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
8
8
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
9
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
9
10
  import { firstDefined } from "openclaw/plugin-sdk/allow-from";
10
11
  import { messagingApi } from "@line/bot-sdk";
11
12
  import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
@@ -25,6 +26,7 @@ import { resolveAgentRoute, resolveInboundLastRouteSessionKey } from "openclaw/p
25
26
  import { resolveAllowlistProviderRuntimeGroupPolicy, resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce } from "openclaw/plugin-sdk/runtime-group-policy";
26
27
  import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
27
28
  import crypto from "node:crypto";
29
+ import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
28
30
  //#region extensions/line/src/bot-access.ts
29
31
  function normalizeLineAllowEntry(value) {
30
32
  const trimmed = String(value).trim();
@@ -403,7 +405,7 @@ async function finalizeLineInboundContext(params) {
403
405
  sessionKey: params.route.sessionKey
404
406
  });
405
407
  if (shouldLogVerbose()) {
406
- const preview = body.slice(0, 200).replace(/\n/g, "\\n");
408
+ const preview = truncateUtf16Safe(body, 200).replace(/\n/g, "\\n");
407
409
  const mediaInfo = params.verboseLog.kind === "inbound" && (params.verboseLog.mediaCount ?? 0) > 1 ? ` mediaCount=${params.verboseLog.mediaCount}` : "";
408
410
  logVerbose(`${params.verboseLog.kind === "inbound" ? "line inbound" : "line postback"}: from=${ctxPayload.From} len=${body.length}${mediaInfo} preview="${preview}"`);
409
411
  }
@@ -584,12 +586,6 @@ const LINE_WEBHOOK_REPLAY_MAX_ENTRIES = 4096;
584
586
  function normalizeLineIngressEntry(value) {
585
587
  return normalizeLineAllowEntry(value) || null;
586
588
  }
587
- var LineRetryableWebhookError = class extends Error {
588
- constructor(message, options) {
589
- super(message, options);
590
- this.name = "LineRetryableWebhookError";
591
- }
592
- };
593
589
  function createLineWebhookReplayCache() {
594
590
  return createClaimableDedupe({
595
591
  ttlMs: LINE_WEBHOOK_REPLAY_WINDOW_MS,
@@ -710,7 +706,7 @@ async function shouldProcessLineEvent(event, context) {
710
706
  defaultGroupPolicy: resolveDefaultGroupPolicy(cfg)
711
707
  });
712
708
  const groupPolicy = runtimeGroupPolicy === "disabled" ? "disabled" : groupConfig?.allowFrom !== void 0 ? "allowlist" : runtimeGroupPolicy;
713
- const groupAllowFrom = normalizeStringEntries(firstDefined(groupConfig?.allowFrom, account.config.groupAllowFrom, account.config.allowFrom?.length ? account.config.allowFrom : void 0));
709
+ const groupAllowFrom = normalizeStringEntries(firstDefined(groupConfig?.allowFrom, account.config.groupAllowFrom));
714
710
  const mentionFacts = (() => {
715
711
  if (!isGroup || event.type !== "message") return {
716
712
  canDetectMention: false,
@@ -979,8 +975,7 @@ async function handleLineWebhookEvents(events, context) {
979
975
  }
980
976
  if (replayCandidate) await replayCandidate.cache.commit(replayCandidate.key);
981
977
  } catch (err) {
982
- if (replayCandidate) if (err instanceof LineRetryableWebhookError) replayCandidate.cache.release(replayCandidate.key, { error: err });
983
- else await replayCandidate.cache.commit(replayCandidate.key);
978
+ if (replayCandidate) await replayCandidate.cache.commit(replayCandidate.key);
984
979
  context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
985
980
  firstError ??= err;
986
981
  }
@@ -1044,7 +1039,7 @@ function resolveLineDurableReplyOptions(params) {
1044
1039
  //#endregion
1045
1040
  //#region extensions/line/src/reply-chunks.ts
1046
1041
  async function sendLineReplyChunks(params) {
1047
- const hasQuickReplies = Boolean(params.quickReplies?.length);
1042
+ const quickReplies = params.quickReplies?.length ? params.quickReplies : void 0;
1048
1043
  let replyTokenUsed = Boolean(params.replyTokenUsed);
1049
1044
  if (params.chunks.length === 0) return { replyTokenUsed };
1050
1045
  if (params.replyToken && !replyTokenUsed) try {
@@ -1054,20 +1049,20 @@ async function sendLineReplyChunks(params) {
1054
1049
  type: "text",
1055
1050
  text: chunk
1056
1051
  }));
1057
- if (hasQuickReplies && remaining.length === 0 && replyMessages.length > 0) {
1052
+ if (quickReplies && remaining.length === 0 && replyMessages.length > 0) {
1058
1053
  const lastIndex = replyMessages.length - 1;
1059
- replyMessages[lastIndex] = params.createTextMessageWithQuickReplies(replyBatch[lastIndex], params.quickReplies);
1054
+ replyMessages[lastIndex] = params.createTextMessageWithQuickReplies(expectDefined(replyBatch[lastIndex], "last non-empty LINE reply batch chunk"), quickReplies);
1060
1055
  }
1061
1056
  await params.replyMessageLine(params.replyToken, replyMessages, {
1062
1057
  cfg: params.cfg,
1063
1058
  accountId: params.accountId
1064
1059
  });
1065
1060
  replyTokenUsed = true;
1066
- for (let i = 0; i < remaining.length; i += 1) if (i === remaining.length - 1 && hasQuickReplies) await params.pushTextMessageWithQuickReplies(params.to, remaining[i], params.quickReplies, {
1061
+ for (const [i, chunk] of remaining.entries()) if (i === remaining.length - 1 && quickReplies) await params.pushTextMessageWithQuickReplies(params.to, chunk, quickReplies, {
1067
1062
  cfg: params.cfg,
1068
1063
  accountId: params.accountId
1069
1064
  });
1070
- else await params.pushMessageLine(params.to, remaining[i], {
1065
+ else await params.pushMessageLine(params.to, chunk, {
1071
1066
  cfg: params.cfg,
1072
1067
  accountId: params.accountId
1073
1068
  });
@@ -1076,11 +1071,11 @@ async function sendLineReplyChunks(params) {
1076
1071
  params.onReplyError?.(err);
1077
1072
  replyTokenUsed = true;
1078
1073
  }
1079
- for (let i = 0; i < params.chunks.length; i += 1) if (i === params.chunks.length - 1 && hasQuickReplies) await params.pushTextMessageWithQuickReplies(params.to, params.chunks[i], params.quickReplies, {
1074
+ for (const [i, chunk] of params.chunks.entries()) if (i === params.chunks.length - 1 && quickReplies) await params.pushTextMessageWithQuickReplies(params.to, chunk, quickReplies, {
1080
1075
  cfg: params.cfg,
1081
1076
  accountId: params.accountId
1082
1077
  });
1083
- else await params.pushMessageLine(params.to, params.chunks[i], {
1078
+ else await params.pushMessageLine(params.to, chunk, {
1084
1079
  cfg: params.cfg,
1085
1080
  accountId: params.accountId
1086
1081
  });
@@ -1089,16 +1084,7 @@ async function sendLineReplyChunks(params) {
1089
1084
  //#endregion
1090
1085
  //#region extensions/line/src/signature.ts
1091
1086
  function validateLineSignature(body, signature, channelSecret) {
1092
- const hash = crypto.createHmac("SHA256", channelSecret).update(body).digest("base64");
1093
- const hashBuffer = Buffer.from(hash);
1094
- const signatureBuffer = Buffer.from(signature);
1095
- const maxLen = Math.max(hashBuffer.length, signatureBuffer.length);
1096
- const paddedHash = Buffer.alloc(maxLen);
1097
- const paddedSig = Buffer.alloc(maxLen);
1098
- hashBuffer.copy(paddedHash);
1099
- signatureBuffer.copy(paddedSig);
1100
- const timingResult = crypto.timingSafeEqual(paddedHash, paddedSig);
1101
- return hashBuffer.length === signatureBuffer.length && timingResult;
1087
+ return safeEqualSecret(signature, crypto.createHmac("SHA256", channelSecret).update(body).digest("base64"));
1102
1088
  }
1103
1089
  //#endregion
1104
1090
  //#region extensions/line/src/webhook-utils.ts
@@ -1213,24 +1199,10 @@ function createLineNodeWebhookHandler(params) {
1213
1199
  }
1214
1200
  //#endregion
1215
1201
  //#region extensions/line/src/monitor.ts
1216
- const runtimeState = /* @__PURE__ */ new Map();
1217
1202
  const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
1218
1203
  const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
1219
1204
  const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5e3;
1220
1205
  const lineWebhookTargets = /* @__PURE__ */ new Map();
1221
- function recordChannelRuntimeState(params) {
1222
- const key = `${params.channel}:${params.accountId}`;
1223
- const existing = runtimeState.get(key) ?? {
1224
- running: false,
1225
- lastStartAt: null,
1226
- lastStopAt: null,
1227
- lastError: null
1228
- };
1229
- runtimeState.set(key, {
1230
- ...existing,
1231
- ...params.state
1232
- });
1233
- }
1234
1206
  function startLineLoadingKeepalive(params) {
1235
1207
  const intervalMs = params.intervalMs ?? 18e3;
1236
1208
  const loadingSeconds = params.loadingSeconds ?? 20;
@@ -1267,11 +1239,6 @@ async function monitorLineProvider(opts) {
1267
1239
  onMessage: async (ctx) => {
1268
1240
  if (!ctx) return;
1269
1241
  const { ctxPayload, replyToken, route } = ctx;
1270
- recordChannelRuntimeState({
1271
- channel: "line",
1272
- accountId: resolvedAccountId,
1273
- state: { lastInboundAt: Date.now() }
1274
- });
1275
1242
  const shouldShowLoading = Boolean(ctx.userId && !ctx.isGroup);
1276
1243
  const displayNamePromise = ctx.userId ? getUserDisplayName(ctx.userId, {
1277
1244
  cfg: config,
@@ -1352,11 +1319,6 @@ async function monitorLineProvider(opts) {
1352
1319
  });
1353
1320
  replyTokenUsed = deliveryResult.replyTokenUsed;
1354
1321
  if (deliveryResult.status === "partial") throw deliveryResult.error;
1355
- recordChannelRuntimeState({
1356
- channel: "line",
1357
- accountId: resolvedAccountId,
1358
- state: { lastOutboundAt: Date.now() }
1359
- });
1360
1322
  },
1361
1323
  onError: (err, info) => {
1362
1324
  runtime.error?.(danger(`line ${info.kind} reply failed: ${String(err)}`));
@@ -1491,14 +1453,6 @@ async function monitorLineProvider(opts) {
1491
1453
  }
1492
1454
  }
1493
1455
  });
1494
- recordChannelRuntimeState({
1495
- channel: "line",
1496
- accountId: resolvedAccountId,
1497
- state: {
1498
- running: true,
1499
- lastStartAt: Date.now()
1500
- }
1501
- });
1502
1456
  logVerbose(`line: registered webhook handler at ${normalizedPath}`);
1503
1457
  let stopped = false;
1504
1458
  const stopHandler = () => {
@@ -1506,14 +1460,6 @@ async function monitorLineProvider(opts) {
1506
1460
  stopped = true;
1507
1461
  logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
1508
1462
  unregisterHttp();
1509
- recordChannelRuntimeState({
1510
- channel: "line",
1511
- accountId: resolvedAccountId,
1512
- state: {
1513
- running: false,
1514
- lastStopAt: Date.now()
1515
- }
1516
- });
1517
1463
  };
1518
1464
  if (abortSignal?.aborted) stopHandler();
1519
1465
  else if (abortSignal) {
@@ -0,0 +1,2 @@
1
+ import { t as monitorLineProvider } from "./monitor-q3UE2J1g.js";
2
+ export { monitorLineProvider };
@@ -1,2 +1,2 @@
1
- import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, D as sendMessageLine, S as pushMessageLine, T as pushTextMessageWithQuickReplies, c as processLineMessage, m as createQuickReplyItems, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage } from "./markdown-to-line-DmpZY78n.js";
1
+ import { A as buildTemplateMessageFromPayload, C as pushMessagesLine, D as sendMessageLine, S as pushMessageLine, T as pushTextMessageWithQuickReplies, c as processLineMessage, m as createQuickReplyItems, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage } from "./markdown-to-line-CixmAKiG.js";
2
2
  export { buildTemplateMessageFromPayload, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
@@ -1,4 +1,4 @@
1
- import { i as createMediaPlayerCard, l as createAgendaCard, n as createAppleTvRemoteCard, r as createDeviceControlCard, u as createEventCard } from "./flex-templates-DEIXn69v.js";
1
+ import { i as createMediaPlayerCard, l as createAgendaCard, n as createAppleTvRemoteCard, r as createDeviceControlCard, u as createEventCard } from "./flex-templates-K1lS9i8L.js";
2
2
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
3
3
  import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
4
4
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -8,6 +8,7 @@ import { z } from "zod";
8
8
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
9
9
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
10
10
  import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
11
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
11
12
  import { parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
12
13
  //#region extensions/line/src/group-keys.ts
13
14
  function resolveLineGroupLookupIds(groupId) {
@@ -42,7 +43,7 @@ function resolveLineGroupsConfig(cfg, accountId) {
42
43
  return resolveAccountEntry(lineConfig.accounts, normalizedAccountId)?.groups ?? lineConfig.groups;
43
44
  }
44
45
  function resolveExactLineGroupConfigKey(params) {
45
- const groups = resolveLineGroupsConfig(params.cfg, params.accountId);
46
+ const { groups } = params;
46
47
  if (!groups) return;
47
48
  return resolveLineGroupLookupIds(params.groupId).find((candidate) => Object.hasOwn(groups, candidate));
48
49
  }
@@ -116,7 +117,7 @@ const LineConfigSchema = LineCommonConfigSchemaBase.extend({
116
117
  const LineChannelConfigSchema = buildChannelConfigSchema(LineConfigSchema);
117
118
  //#endregion
118
119
  //#region extensions/line/src/runtime.ts
119
- const { setRuntime: setLineRuntime, clearRuntime: clearLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
120
+ const { setRuntime: setLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
120
121
  pluginId: "line",
121
122
  errorMessage: "LINE runtime not initialized - plugin not registered"
122
123
  });
@@ -223,17 +224,31 @@ function parseLineDirectives(payload) {
223
224
  if (extras) for (const [key, value] of Object.entries(extras)) base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
224
225
  return base.join("&");
225
226
  };
227
+ const parseConfirmAction = (part) => {
228
+ const colonIndex = part.indexOf(":");
229
+ if (colonIndex === -1) return {
230
+ label: part,
231
+ data: normalizeLowercaseStringOrEmpty(part)
232
+ };
233
+ return {
234
+ label: part.slice(0, colonIndex).trim(),
235
+ data: part.slice(colonIndex + 1).trim()
236
+ };
237
+ };
226
238
  const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i);
227
239
  if (quickRepliesMatch) {
228
- const options = normalizeStringEntries(quickRepliesMatch[1].split(","));
240
+ const options = normalizeStringEntries(expectDefined(quickRepliesMatch[1], "quick replies directive body").split(","));
229
241
  if (options.length > 0) lineData.quickReplies = [...lineData.quickReplies || [], ...options];
230
242
  text = text.replace(quickRepliesMatch[0], "").trim();
231
243
  }
232
244
  const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i);
233
245
  if (locationMatch && !lineData.location) {
234
- const parts = locationMatch[1].split("|").map((s) => s.trim());
246
+ const parts = expectDefined(locationMatch[1], "location directive body").split("|").map((s) => s.trim());
235
247
  if (parts.length >= 4) {
236
- const [title, address, latStr, lonStr] = parts;
248
+ const title = expectDefined(parts[0], "location title field");
249
+ const address = expectDefined(parts[1], "location address field");
250
+ const latStr = expectDefined(parts[2], "location latitude field");
251
+ const lonStr = expectDefined(parts[3], "location longitude field");
237
252
  const latitude = parseStrictFiniteNumber(latStr);
238
253
  const longitude = parseStrictFiniteNumber(lonStr);
239
254
  if (latitude !== void 0 && longitude !== void 0) lineData.location = {
@@ -247,18 +262,20 @@ function parseLineDirectives(payload) {
247
262
  }
248
263
  const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i);
249
264
  if (confirmMatch && !lineData.templateMessage) {
250
- const parts = confirmMatch[1].split("|").map((s) => s.trim());
265
+ const parts = expectDefined(confirmMatch[1], "confirm directive body").split("|").map((s) => s.trim());
251
266
  if (parts.length >= 3) {
252
- const [question, yesPart, noPart] = parts;
253
- const [yesLabel, yesData] = yesPart.includes(":") ? yesPart.split(":").map((s) => s.trim()) : [yesPart, normalizeLowercaseStringOrEmpty(yesPart)];
254
- const [noLabel, noData] = noPart.includes(":") ? noPart.split(":").map((s) => s.trim()) : [noPart, normalizeLowercaseStringOrEmpty(noPart)];
255
- lineData.templateMessage = {
267
+ const question = expectDefined(parts[0], "confirm question field");
268
+ const yesPart = expectDefined(parts[1], "confirm yes field");
269
+ const noPart = expectDefined(parts[2], "confirm no field");
270
+ const yesAction = parseConfirmAction(yesPart);
271
+ const noAction = parseConfirmAction(noPart);
272
+ if (question && yesAction.label && noAction.label) lineData.templateMessage = {
256
273
  type: "confirm",
257
274
  text: question,
258
- confirmLabel: yesLabel,
259
- confirmData: yesData,
260
- cancelLabel: noLabel,
261
- cancelData: noData,
275
+ confirmLabel: yesAction.label,
276
+ confirmData: yesAction.data,
277
+ cancelLabel: noAction.label,
278
+ cancelData: noAction.data,
262
279
  altText: question
263
280
  };
264
281
  }
@@ -266,10 +283,11 @@ function parseLineDirectives(payload) {
266
283
  }
267
284
  const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i);
268
285
  if (buttonsMatch && !lineData.templateMessage) {
269
- const parts = buttonsMatch[1].split("|").map((s) => s.trim());
286
+ const parts = expectDefined(buttonsMatch[1], "buttons directive body").split("|").map((s) => s.trim());
270
287
  if (parts.length >= 3) {
271
- const [title, bodyText, actionsStr] = parts;
272
- const actions = actionsStr.split(",").map((actionStr) => {
288
+ const title = expectDefined(parts[0], "buttons title field");
289
+ const bodyText = expectDefined(parts[1], "buttons text field");
290
+ const actions = expectDefined(parts[2], "buttons actions field").split(",").map((actionStr) => {
273
291
  const trimmed = actionStr.trim();
274
292
  const colonIndex = (() => {
275
293
  const index = trimmed.indexOf(":");
@@ -302,22 +320,23 @@ function parseLineDirectives(payload) {
302
320
  label,
303
321
  data: data || label
304
322
  };
305
- });
306
- if (actions.length > 0) lineData.templateMessage = {
323
+ }).filter((action) => action.label);
324
+ if (actions.length > 0 && bodyText) lineData.templateMessage = {
307
325
  type: "buttons",
308
- title,
326
+ ...title ? { title } : {},
309
327
  text: bodyText,
310
328
  actions: actions.slice(0, 4),
311
- altText: `${title}: ${bodyText}`
329
+ altText: title ? `${title}: ${bodyText}` : bodyText
312
330
  };
313
331
  }
314
332
  text = text.replace(buttonsMatch[0], "").trim();
315
333
  }
316
334
  const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i);
317
335
  if (mediaPlayerMatch && !lineData.flexMessage) {
318
- const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim());
336
+ const parts = expectDefined(mediaPlayerMatch[1], "media player directive body").split("|").map((s) => s.trim());
319
337
  if (parts.length >= 1) {
320
- const [title, artist, source, imageUrl, statusStr] = parts;
338
+ const title = expectDefined(parts[0], "media player title field");
339
+ const [, artist, source, imageUrl, statusStr] = parts;
321
340
  const isPlaying = normalizeLowercaseStringOrEmpty(statusStr) === "playing";
322
341
  const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : void 0;
323
342
  const deviceKey = toSlug(source || title || "media");
@@ -343,9 +362,13 @@ function parseLineDirectives(payload) {
343
362
  }
344
363
  const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i);
345
364
  if (eventMatch && !lineData.flexMessage) {
346
- const parts = eventMatch[1].split("|").map((s) => s.trim());
365
+ const parts = expectDefined(eventMatch[1], "event directive body").split("|").map((s) => s.trim());
347
366
  if (parts.length >= 2) {
348
- const [title, date, time, location, description] = parts;
367
+ const title = expectDefined(parts[0], "event title field");
368
+ const date = expectDefined(parts[1], "event date field");
369
+ const time = parts[2];
370
+ const location = parts[3];
371
+ const description = parts[4];
349
372
  const card = createEventCard({
350
373
  title: title || "Event",
351
374
  date: date || "TBD",
@@ -362,9 +385,10 @@ function parseLineDirectives(payload) {
362
385
  }
363
386
  const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i);
364
387
  if (appleTvMatch && !lineData.flexMessage) {
365
- const parts = appleTvMatch[1].split("|").map((s) => s.trim());
388
+ const parts = expectDefined(appleTvMatch[1], "Apple TV directive body").split("|").map((s) => s.trim());
366
389
  if (parts.length >= 1) {
367
- const [deviceName, status] = parts;
390
+ const deviceName = expectDefined(parts[0], "Apple TV device name field");
391
+ const [, status] = parts;
368
392
  const deviceKey = toSlug(deviceName || "apple_tv");
369
393
  const card = createAppleTvRemoteCard({
370
394
  deviceName: deviceName || "Apple TV",
@@ -393,10 +417,10 @@ function parseLineDirectives(payload) {
393
417
  }
394
418
  const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i);
395
419
  if (agendaMatch && !lineData.flexMessage) {
396
- const parts = agendaMatch[1].split("|").map((s) => s.trim());
420
+ const parts = expectDefined(agendaMatch[1], "agenda directive body").split("|").map((s) => s.trim());
397
421
  if (parts.length >= 2) {
398
- const [title, eventsStr] = parts;
399
- const events = eventsStr.split(",").map((eventStr) => {
422
+ const title = expectDefined(parts[0], "agenda title field");
423
+ const events = expectDefined(parts[1], "agenda events field").split(",").map((eventStr) => {
400
424
  const trimmed = eventStr.trim();
401
425
  const colonIdx = trimmed.lastIndexOf(":");
402
426
  if (colonIdx > 0) return {
@@ -418,13 +442,15 @@ function parseLineDirectives(payload) {
418
442
  }
419
443
  const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i);
420
444
  if (deviceMatch && !lineData.flexMessage) {
421
- const parts = deviceMatch[1].split("|").map((s) => s.trim());
445
+ const parts = expectDefined(deviceMatch[1], "device directive body").split("|").map((s) => s.trim());
422
446
  if (parts.length >= 1) {
423
- const [deviceName, deviceType, status, controlsStr] = parts;
447
+ const deviceName = expectDefined(parts[0], "device name field");
448
+ const [, deviceType, status, controlsStr] = parts;
424
449
  const deviceKey = toSlug(deviceName || "device");
425
450
  const controls = controlsStr ? controlsStr.split(",").map((ctrlStr) => {
426
- const [label, data] = ctrlStr.split(":").map((s) => s.trim());
427
- const action = data || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
451
+ const controlParts = ctrlStr.split(":").map((s) => s.trim());
452
+ const label = expectDefined(controlParts[0], "device control label");
453
+ const action = controlParts[1] || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
428
454
  return {
429
455
  label,
430
456
  data: lineActionData(action, { "line.device": deviceKey })
@@ -1,9 +1,9 @@
1
1
  import { i as resolveLineAccount, n as normalizeAccountId, r as resolveDefaultLineAccountId, t as listLineAccountIds } from "./accounts-DJVOv1JI.js";
2
- import { c as setLineRuntime, d as resolveExactLineGroupConfigKey, f as resolveLineGroupConfigEntry, l as LineChannelConfigSchema, m as resolveLineGroupsConfig, n as parseLineDirectives, p as resolveLineGroupLookupIds, t as hasLineDirectives, u as LineConfigSchema } from "./reply-payload-transform-C7r5npxP.js";
3
- import { _ as createNotificationBubble, a as datetimePickerAction, c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, i as createMediaPlayerCard, l as createAgendaCard, m as createImageCard, n as createAppleTvRemoteCard, o as messageAction, p as createCarousel, r as createDeviceControlCard, s as postbackAction, t as toFlexMessage, u as createEventCard } from "./flex-templates-DEIXn69v.js";
4
- import { a as validateLineSignature, c as normalizeAllowFrom, i as parseLineWebhookBody, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider } from "./monitor-0eXlx_Pe.js";
2
+ import { c as setLineRuntime, d as resolveExactLineGroupConfigKey, f as resolveLineGroupConfigEntry, l as LineChannelConfigSchema, m as resolveLineGroupsConfig, n as parseLineDirectives, p as resolveLineGroupLookupIds, t as hasLineDirectives, u as LineConfigSchema } from "./reply-payload-transform-BsbwCJGH.js";
3
+ import { _ as createNotificationBubble, a as datetimePickerAction, c as uriAction, d as createReceiptCard, f as createActionCard, g as createListCard, h as createInfoCard, i as createMediaPlayerCard, l as createAgendaCard, m as createImageCard, n as createAppleTvRemoteCard, o as messageAction, p as createCarousel, r as createDeviceControlCard, s as postbackAction, t as toFlexMessage, u as createEventCard } from "./flex-templates-K1lS9i8L.js";
4
+ import { a as validateLineSignature, c as normalizeAllowFrom, i as parseLineWebhookBody, n as createLineNodeWebhookHandler, o as downloadLineMedia, r as readLineWebhookRequestBody, s as firstDefined, t as monitorLineProvider } from "./monitor-q3UE2J1g.js";
5
5
  import { t as probeLineBot } from "./probe-BslD77tJ.js";
6
- import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, I as createImageCarouselColumn, L as createLinkMenu, M as createButtonTemplate, N as createCarouselColumn, O as showLoadingAnimation, P as createConfirmTemplate, R as createProductCarousel, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, a as extractLinks, b as pushImageMessage, c as processLineMessage, d as createFlexMessage, f as createImageMessage, g as createVideoMessage, h as createTextMessageWithQuickReplies, i as extractCodeBlocks, j as createButtonMenu, k as resolveLineChannelAccessToken, l as stripMarkdown, m as createQuickReplyItems, n as convertLinksToFlexBubble, o as extractMarkdownTables, p as createLocationMessage, r as convertTableToFlexBubble, s as hasMarkdownToConvert, t as convertCodeBlockToFlexBubble, u as createAudioMessage, v as getUserProfile, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage, z as createTemplateCarousel } from "./markdown-to-line-DmpZY78n.js";
6
+ import { A as buildTemplateMessageFromPayload, B as createYesNoConfirm, C as pushMessagesLine, D as sendMessageLine, E as replyMessageLine, F as createImageCarousel, I as createImageCarouselColumn, L as createLinkMenu, M as createButtonTemplate, N as createCarouselColumn, O as showLoadingAnimation, P as createConfirmTemplate, R as createProductCarousel, S as pushMessageLine, T as pushTextMessageWithQuickReplies, _ as getUserDisplayName, a as extractLinks, b as pushImageMessage, c as processLineMessage, d as createFlexMessage, f as createImageMessage, g as createVideoMessage, h as createTextMessageWithQuickReplies, i as extractCodeBlocks, j as createButtonMenu, k as resolveLineChannelAccessToken, l as stripMarkdown, m as createQuickReplyItems, n as convertLinksToFlexBubble, o as extractMarkdownTables, p as createLocationMessage, r as convertTableToFlexBubble, s as hasMarkdownToConvert, t as convertCodeBlockToFlexBubble, u as createAudioMessage, v as getUserProfile, w as pushTemplateMessage, x as pushLocationMessage, y as pushFlexMessage, z as createTemplateCarousel } from "./markdown-to-line-CixmAKiG.js";
7
7
  import { clearAccountEntryFields } from "openclaw/plugin-sdk/core";
8
8
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
9
9
  import { createMessageReceiveContext } from "openclaw/plugin-sdk/channel-outbound";
@@ -92,7 +92,6 @@ function startLineWebhook(options) {
92
92
  }
93
93
  //#endregion
94
94
  //#region extensions/line/src/rich-menu.ts
95
- const USER_BATCH_SIZE = 500;
96
95
  const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
97
96
  function getClient(opts) {
98
97
  const account = resolveLineAccount({
@@ -110,11 +109,6 @@ function getBlobClient(opts) {
110
109
  const token = resolveLineChannelAccessToken(opts.channelAccessToken, account);
111
110
  return new messagingApi.MessagingApiBlobClient({ channelAccessToken: token });
112
111
  }
113
- function chunkUserIds(userIds) {
114
- const batches = [];
115
- for (let i = 0; i < userIds.length; i += USER_BATCH_SIZE) batches.push(userIds.slice(i, i + USER_BATCH_SIZE));
116
- return batches;
117
- }
118
112
  function truncateGraphemes(input, maxLength) {
119
113
  let result = "";
120
114
  let count = 0;
@@ -163,27 +157,6 @@ async function getDefaultRichMenuId(opts) {
163
157
  return null;
164
158
  }
165
159
  }
166
- async function linkRichMenuToUser(userId, richMenuId, opts) {
167
- await getClient(opts).linkRichMenuIdToUser(userId, richMenuId);
168
- if (opts.verbose) logVerbose(`line: linked rich menu ${richMenuId} to user ${userId}`);
169
- }
170
- async function linkRichMenuToUsers(userIds, richMenuId, opts) {
171
- const client = getClient(opts);
172
- for (const batch of chunkUserIds(userIds)) await client.linkRichMenuIdToUsers({
173
- richMenuId,
174
- userIds: batch
175
- });
176
- if (opts.verbose) logVerbose(`line: linked rich menu ${richMenuId} to ${userIds.length} users`);
177
- }
178
- async function unlinkRichMenuFromUser(userId, opts) {
179
- await getClient(opts).unlinkRichMenuIdFromUser(userId);
180
- if (opts.verbose) logVerbose(`line: unlinked rich menu from user ${userId}`);
181
- }
182
- async function unlinkRichMenuFromUsers(userIds, opts) {
183
- const client = getClient(opts);
184
- for (const batch of chunkUserIds(userIds)) await client.unlinkRichMenuIdFromUsers({ userIds: batch });
185
- if (opts.verbose) logVerbose(`line: unlinked rich menu from ${userIds.length} users`);
186
- }
187
160
  async function getRichMenuIdOfUser(userId, opts) {
188
161
  const client = getClient(opts);
189
162
  try {
@@ -298,4 +271,4 @@ function createDefaultMenuConfig() {
298
271
  };
299
272
  }
300
273
  //#endregion
301
- export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertLinksToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage, createButtonMenu, createButtonTemplate, createCarousel, createCarouselColumn, createConfirmTemplate, createDefaultMenuConfig, createDeviceControlCard, createEventCard, createFlexMessage, createGridLayout, createImageCard, createImageCarousel, createImageCarouselColumn, createImageMessage, createInfoCard, createLineNodeWebhookHandler, createLineWebhookMiddleware, createLinkMenu, createListCard, createLocationMessage, createMediaPlayerCard, createNotificationBubble, createProductCarousel, createQuickReplyItems, createReceiptCard, createRichMenu, createRichMenuAlias, createTemplateCarousel, createTextMessageWithQuickReplies, createVideoMessage, createYesNoConfirm, datetimePickerAction, deleteRichMenu, deleteRichMenuAlias, downloadLineMedia, extractCodeBlocks, extractLinks, extractMarkdownTables, firstDefined, formatDocsLink, getDefaultRichMenuId, getRichMenu, getRichMenuIdOfUser, getRichMenuList, getUserDisplayName, getUserProfile, hasLineDirectives, hasMarkdownToConvert, linkRichMenuToUser, linkRichMenuToUsers, listLineAccountIds, messageAction, monitorLineProvider, normalizeAccountId, normalizeAllowFrom, parseLineDirectives, parseLineWebhookBody, postbackAction, probeLineBot, processLineMessage, pushFlexMessage, pushImageMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, readLineWebhookRequestBody, replyMessageLine, resolveDefaultLineAccountId, resolveExactLineGroupConfigKey, resolveLineAccount, resolveLineChannelAccessToken, resolveLineGroupConfigEntry, resolveLineGroupLookupIds, resolveLineGroupsConfig, sendMessageLine, setDefaultRichMenu, setLineRuntime, setSetupChannelEnabled, showLoadingAnimation, splitSetupEntries, startLineWebhook, stripMarkdown, toFlexMessage, unlinkRichMenuFromUser, unlinkRichMenuFromUsers, uploadRichMenuImage, uriAction, validateLineSignature };
274
+ export { DEFAULT_ACCOUNT_ID, LineChannelConfigSchema, LineConfigSchema, buildChannelConfigSchema, buildComputedAccountStatusSnapshot, buildTemplateMessageFromPayload, buildTokenChannelStatusSummary, cancelDefaultRichMenu, clearAccountEntryFields, convertCodeBlockToFlexBubble, convertLinksToFlexBubble, convertTableToFlexBubble, createActionCard, createAgendaCard, createAppleTvRemoteCard, createAudioMessage, createButtonMenu, createButtonTemplate, createCarousel, createCarouselColumn, createConfirmTemplate, createDefaultMenuConfig, createDeviceControlCard, createEventCard, createFlexMessage, createGridLayout, createImageCard, createImageCarousel, createImageCarouselColumn, createImageMessage, createInfoCard, createLineNodeWebhookHandler, createLineWebhookMiddleware, createLinkMenu, createListCard, createLocationMessage, createMediaPlayerCard, createNotificationBubble, createProductCarousel, createQuickReplyItems, createReceiptCard, createRichMenu, createRichMenuAlias, createTemplateCarousel, createTextMessageWithQuickReplies, createVideoMessage, createYesNoConfirm, datetimePickerAction, deleteRichMenu, deleteRichMenuAlias, downloadLineMedia, extractCodeBlocks, extractLinks, extractMarkdownTables, firstDefined, formatDocsLink, getDefaultRichMenuId, getRichMenu, getRichMenuIdOfUser, getRichMenuList, getUserDisplayName, getUserProfile, hasLineDirectives, hasMarkdownToConvert, listLineAccountIds, messageAction, monitorLineProvider, normalizeAccountId, normalizeAllowFrom, parseLineDirectives, parseLineWebhookBody, postbackAction, probeLineBot, processLineMessage, pushFlexMessage, pushImageMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, readLineWebhookRequestBody, replyMessageLine, resolveDefaultLineAccountId, resolveExactLineGroupConfigKey, resolveLineAccount, resolveLineChannelAccessToken, resolveLineGroupConfigEntry, resolveLineGroupLookupIds, resolveLineGroupsConfig, sendMessageLine, setDefaultRichMenu, setLineRuntime, setSetupChannelEnabled, showLoadingAnimation, splitSetupEntries, startLineWebhook, stripMarkdown, toFlexMessage, uploadRichMenuImage, uriAction, validateLineSignature };
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@openclaw/line",
3
- "version": "2026.7.1-beta.6",
3
+ "version": "2026.7.2-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/line",
9
- "version": "2026.7.1-beta.6",
9
+ "version": "2026.7.2-beta.1",
10
10
  "dependencies": {
11
11
  "@line/bot-sdk": "11.1.0",
12
12
  "zod": "4.4.3"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.7.1-beta.6"
15
+ "openclaw": ">=2026.7.2-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/line",
3
- "version": "2026.7.1-beta.6",
3
+ "version": "2026.7.2-beta.1",
4
4
  "description": "OpenClaw LINE channel plugin for LINE Bot API chats.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  "zod": "4.4.3"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.7.1-beta.6"
15
+ "openclaw": ">=2026.7.2-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
@@ -42,10 +42,10 @@
42
42
  "minHostVersion": ">=2026.4.10"
43
43
  },
44
44
  "compat": {
45
- "pluginApi": ">=2026.7.1-beta.6"
45
+ "pluginApi": ">=2026.7.2-beta.1"
46
46
  },
47
47
  "build": {
48
- "openclawVersion": "2026.7.1-beta.6"
48
+ "openclawVersion": "2026.7.2-beta.1"
49
49
  },
50
50
  "release": {
51
51
  "publishToClawHub": true,
@@ -1,4 +0,0 @@
1
- import { t as monitorLineProvider } from "./monitor-0eXlx_Pe.js";
2
- import { t as probeLineBot } from "./probe-BslD77tJ.js";
3
- import { S as pushMessageLine } from "./markdown-to-line-DmpZY78n.js";
4
- export { monitorLineProvider, probeLineBot, pushMessageLine };
@@ -1,2 +0,0 @@
1
- import { t as monitorLineProvider } from "./monitor-0eXlx_Pe.js";
2
- export { monitorLineProvider };