@openclaw/line 2026.7.2-beta.7 → 2026.7.34

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 (117) hide show
  1. package/README.md +1 -1
  2. package/dist/.setup/accounts-DJVOv1JI.mjs +107 -0
  3. package/dist/.setup/api-D3h9w6oz.mjs +3 -0
  4. package/dist/.setup/api-DdlGHKoX.mjs +11 -0
  5. package/dist/.setup/basic-cards-C8Qwh2X1.mjs +307 -0
  6. package/dist/{card-command-ty11OJNt.js → .setup/card-command-CWrkUiGY.mjs} +51 -23
  7. package/dist/{channel-D4i_K4fQ.js → .setup/channel-Du0PrvOH.mjs} +76 -66
  8. package/dist/.setup/channel.runtime-b3suPB-W.mjs +4 -0
  9. package/dist/.setup/flex-templates-BP6VHjzK.mjs +814 -0
  10. package/dist/{markdown-to-line-DMDQTMhx.js → .setup/markdown-to-line-o89GT1Qn.mjs} +252 -254
  11. package/dist/{monitor-DXEodYFJ.js → .setup/monitor-DpxjN6Ks.mjs} +491 -658
  12. package/dist/.setup/monitor.runtime-BS-cGD5Q.mjs +2 -0
  13. package/dist/.setup/outbound.runtime-BeNDuEIF.mjs +2 -0
  14. package/dist/{probe-CM03wo6r.js → .setup/probe-BslD77tJ.mjs} +9 -7
  15. package/dist/.setup/probe.runtime-YwWmWIlk.mjs +2 -0
  16. package/dist/{reply-payload-transform-D5-dHB8b.js → .setup/reply-payload-transform-CYQIy_x8.mjs} +204 -80
  17. package/dist/{setup-surface-DRQ7n0En.js → .setup/setup-surface-M1ivmZ-G.mjs} +143 -158
  18. package/dist/api.js +2 -11
  19. package/dist/channel-plugin-api.js +1 -1
  20. package/dist/contract-api.js +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/runtime-api.js +56 -15
  23. package/dist/setup-api.js +1 -1
  24. package/dist/setup-entry.js +2 -1
  25. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  26. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  27. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  28. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  29. package/node_modules/@line/bot-sdk/dist/cjs/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  30. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  31. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  32. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexBubble.d.ts.map +1 -1
  33. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexIconSize.d.ts.map +1 -1
  34. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  35. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  36. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  37. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/postbackAction.d.ts.map +1 -1
  38. package/node_modules/@line/bot-sdk/dist/cjs/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  39. package/node_modules/@line/bot-sdk/dist/cjs/version.d.ts +1 -1
  40. package/node_modules/@line/bot-sdk/dist/cjs/version.js +1 -1
  41. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts +1 -2
  42. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/event.d.ts.map +1 -1
  43. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts +0 -1
  44. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.d.ts.map +1 -1
  45. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js +0 -1
  46. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/models.js.map +1 -1
  47. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupCreateRoute.d.ts.map +1 -1
  48. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupFailedType.d.ts.map +1 -1
  49. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobFailedType.d.ts.map +1 -1
  50. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupJobStatus.d.ts.map +1 -1
  51. package/node_modules/@line/bot-sdk/dist/manage-audience/model/audienceGroupStatus.d.ts.map +1 -1
  52. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxBorderWidth.d.ts.map +1 -1
  53. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBoxCornerRadius.d.ts.map +1 -1
  54. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexBubble.d.ts.map +1 -1
  55. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexIconSize.d.ts.map +1 -1
  56. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexSpanSize.d.ts.map +1 -1
  57. package/node_modules/@line/bot-sdk/dist/messaging-api/model/flexTextFontSize.d.ts.map +1 -1
  58. package/node_modules/@line/bot-sdk/dist/messaging-api/model/numberOfMessagesResponse.d.ts.map +1 -1
  59. package/node_modules/@line/bot-sdk/dist/messaging-api/model/postbackAction.d.ts.map +1 -1
  60. package/node_modules/@line/bot-sdk/dist/messaging-api/model/subscriptionPeriodDemographic.d.ts.map +1 -1
  61. package/node_modules/@line/bot-sdk/dist/version.d.ts +1 -1
  62. package/node_modules/@line/bot-sdk/dist/version.js +1 -1
  63. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts +1 -2
  64. package/node_modules/@line/bot-sdk/dist/webhook/model/event.d.ts.map +1 -1
  65. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts +0 -1
  66. package/node_modules/@line/bot-sdk/dist/webhook/model/models.d.ts.map +1 -1
  67. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js +0 -1
  68. package/node_modules/@line/bot-sdk/dist/webhook/model/models.js.map +1 -1
  69. package/node_modules/@line/bot-sdk/lib/insight/model/getNumberOfFollowersResponse.ts +1 -1
  70. package/node_modules/@line/bot-sdk/lib/liff/model/liffScope.ts +1 -1
  71. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupCreateRoute.ts +4 -1
  72. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupFailedType.ts +2 -1
  73. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobFailedType.ts +2 -1
  74. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupJobStatus.ts +4 -1
  75. package/node_modules/@line/bot-sdk/lib/manage-audience/model/audienceGroupStatus.ts +6 -1
  76. package/node_modules/@line/bot-sdk/lib/messaging-api/model/botInfoResponse.ts +1 -1
  77. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxBorderWidth.ts +6 -1
  78. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBoxCornerRadius.ts +7 -1
  79. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexBubble.ts +7 -1
  80. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexIconSize.ts +10 -1
  81. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexSpanSize.ts +10 -1
  82. package/node_modules/@line/bot-sdk/lib/messaging-api/model/flexTextFontSize.ts +10 -1
  83. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getAggregationUnitNameListResponse.ts +1 -1
  84. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getFollowersResponse.ts +1 -1
  85. package/node_modules/@line/bot-sdk/lib/messaging-api/model/getJoinedMembershipUsersResponse.ts +1 -1
  86. package/node_modules/@line/bot-sdk/lib/messaging-api/model/issueLinkTokenResponse.ts +1 -1
  87. package/node_modules/@line/bot-sdk/lib/messaging-api/model/numberOfMessagesResponse.ts +5 -2
  88. package/node_modules/@line/bot-sdk/lib/messaging-api/model/postbackAction.ts +4 -1
  89. package/node_modules/@line/bot-sdk/lib/messaging-api/model/subscriptionPeriodDemographic.ts +5 -1
  90. package/node_modules/@line/bot-sdk/lib/version.ts +1 -1
  91. package/node_modules/@line/bot-sdk/lib/webhook/.openapi-generator/FILES +0 -1
  92. package/node_modules/@line/bot-sdk/lib/webhook/model/event.ts +0 -2
  93. package/node_modules/@line/bot-sdk/lib/webhook/model/models.ts +0 -1
  94. package/node_modules/@line/bot-sdk/lib/webhook/model/stickerMessageContent.ts +1 -1
  95. package/node_modules/@line/bot-sdk/node_modules/@types/node/README.md +1 -1
  96. package/node_modules/@line/bot-sdk/node_modules/@types/node/fs/promises.d.ts +5 -16
  97. package/node_modules/@line/bot-sdk/node_modules/@types/node/package.json +3 -3
  98. package/node_modules/@line/bot-sdk/package.json +3 -3
  99. package/openclaw.plugin.json +75 -57
  100. package/package.json +6 -71
  101. package/dist/accounts-BqEVsDoB.js +0 -103
  102. package/dist/channel.runtime-BdhK8Xw7.js +0 -4
  103. package/dist/flex-templates-iKJsyCq-.js +0 -1339
  104. package/dist/monitor.runtime-CZquR7jI.js +0 -2
  105. package/dist/outbound.runtime-BS0IhU_M.js +0 -2
  106. package/dist/probe.runtime-D13ZtXsd.js +0 -2
  107. package/dist/quick-reply-fallback-D86FwAfr.js +0 -56
  108. package/dist/send-receipt-CY2ey8XH.js +0 -107
  109. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts +0 -24
  110. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.d.ts.map +0 -1
  111. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js +0 -14
  112. package/node_modules/@line/bot-sdk/dist/cjs/webhook/model/messageEditedEvent.js.map +0 -1
  113. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts +0 -24
  114. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.d.ts.map +0 -1
  115. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js +0 -13
  116. package/node_modules/@line/bot-sdk/dist/webhook/model/messageEditedEvent.js.map +0 -1
  117. package/node_modules/@line/bot-sdk/lib/webhook/model/messageEditedEvent.ts +0 -32
@@ -1,13 +1,12 @@
1
- import { i as resolveLineAccount } from "./accounts-BqEVsDoB.js";
2
- import { a as validateLineMediaUrl, t as createLineSendReceipt } from "./send-receipt-CY2ey8XH.js";
3
- import { _ as postbackAction, g as normalizeLineMessageActions, h as normalizeLineAction, m as messageAction, o as createReceiptCard, v as uriAction } from "./flex-templates-iKJsyCq-.js";
4
- import { markdownToIRWithMeta, stripMarkdown, stripMarkdown as stripMarkdown$1 } from "openclaw/plugin-sdk/text-chunking";
1
+ import { i as resolveLineAccount } from "./accounts-DJVOv1JI.mjs";
2
+ import { o as validateLineMediaUrl, r as createLineSendReceipt } from "./reply-payload-transform-CYQIy_x8.mjs";
3
+ import { a as messageAction, o as postbackAction, s as uriAction, u as createReceiptCard } from "./flex-templates-BP6VHjzK.mjs";
5
4
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
6
- import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
7
5
  import { messagingApi } from "@line/bot-sdk";
6
+ import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
8
7
  import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
9
- import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
10
8
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
9
+ import { stripMarkdown, stripMarkdown as stripMarkdown$1 } from "openclaw/plugin-sdk/text-chunking";
11
10
  //#region extensions/line/src/flex-templates/message.ts
12
11
  /**
13
12
  * Wrap a FlexContainer in a FlexMessage
@@ -54,19 +53,6 @@ function formatProductCarouselText(description, price) {
54
53
  const descriptionText = truncateTemplateText(description, Math.max(0, COMPACT_TEMPLATE_TEXT_LIMIT - priceText.length - 1));
55
54
  return descriptionText ? `${descriptionText}\n${priceText}` : priceText;
56
55
  }
57
- function normalizeCarouselColumnActions(column) {
58
- return {
59
- ...column,
60
- actions: column.actions.map((action) => normalizeLineAction(action)),
61
- defaultAction: column.defaultAction === void 0 ? void 0 : normalizeLineAction(column.defaultAction)
62
- };
63
- }
64
- function normalizeImageCarouselColumnAction(column) {
65
- return {
66
- ...column,
67
- action: normalizeLineAction(column.action, 12)
68
- };
69
- }
70
56
  /**
71
57
  * Create a confirm template (yes/no style dialog)
72
58
  */
@@ -74,7 +60,7 @@ function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
74
60
  const template = {
75
61
  type: "confirm",
76
62
  text: truncateTemplateText(text, 240),
77
- actions: [normalizeLineAction(confirmAction), normalizeLineAction(cancelAction)]
63
+ actions: [confirmAction, cancelAction]
78
64
  };
79
65
  return {
80
66
  type: "template",
@@ -86,26 +72,25 @@ function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
86
72
  * Create a button template with title, text, and action buttons
87
73
  */
88
74
  function createButtonTemplate(title, text, actions, options) {
89
- const normalizedTitle = title || void 0;
90
75
  const textLimit = resolveTemplateTextLimit({
91
- title: normalizedTitle,
76
+ title,
92
77
  thumbnailImageUrl: options?.thumbnailImageUrl,
93
78
  textOnlyLimit: 160
94
79
  });
95
80
  const template = {
96
81
  type: "buttons",
97
- ...normalizedTitle ? { title: truncateTemplateText(normalizedTitle, 40) } : {},
82
+ title: truncateTemplateText(title, 40),
98
83
  text: truncateTemplateText(text, textLimit),
99
- actions: actions.slice(0, 4).map((action) => normalizeLineAction(action)),
84
+ actions: actions.slice(0, 4),
100
85
  thumbnailImageUrl: options?.thumbnailImageUrl,
101
86
  imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
102
87
  imageSize: options?.imageSize ?? "cover",
103
88
  imageBackgroundColor: options?.imageBackgroundColor,
104
- defaultAction: options?.defaultAction === void 0 ? void 0 : normalizeLineAction(options.defaultAction)
89
+ defaultAction: options?.defaultAction
105
90
  };
106
91
  return {
107
92
  type: "template",
108
- altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(normalizedTitle ? `${normalizedTitle}: ${text}` : text, 400),
93
+ altText: truncateOptionalTemplateText(options?.altText, 400) ?? truncateTemplateText(`${title}: ${text}`, 400),
109
94
  template
110
95
  };
111
96
  }
@@ -115,7 +100,7 @@ function createButtonTemplate(title, text, actions, options) {
115
100
  function createTemplateCarousel(columns, options) {
116
101
  const template = {
117
102
  type: "carousel",
118
- columns: columns.slice(0, 10).map(normalizeCarouselColumnActions),
103
+ columns: columns.slice(0, 10),
119
104
  imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
120
105
  imageSize: options?.imageSize ?? "cover"
121
106
  };
@@ -136,10 +121,10 @@ function createCarouselColumn(params) {
136
121
  return {
137
122
  title: truncateOptionalTemplateText(params.title, 40),
138
123
  text: truncateTemplateText(params.text, textLimit),
139
- actions: params.actions.slice(0, 3).map((action) => normalizeLineAction(action)),
124
+ actions: params.actions.slice(0, 3),
140
125
  thumbnailImageUrl: params.thumbnailImageUrl,
141
126
  imageBackgroundColor: params.imageBackgroundColor,
142
- defaultAction: params.defaultAction === void 0 ? void 0 : normalizeLineAction(params.defaultAction)
127
+ defaultAction: params.defaultAction
143
128
  };
144
129
  }
145
130
  /**
@@ -148,7 +133,7 @@ function createCarouselColumn(params) {
148
133
  function createImageCarousel(columns, altText) {
149
134
  const template = {
150
135
  type: "image_carousel",
151
- columns: columns.slice(0, 10).map(normalizeImageCarouselColumnAction)
136
+ columns: columns.slice(0, 10)
152
137
  };
153
138
  return {
154
139
  type: "template",
@@ -162,7 +147,7 @@ function createImageCarousel(columns, altText) {
162
147
  function createImageCarouselColumn(imageUrl, action) {
163
148
  return {
164
149
  imageUrl,
165
- action: normalizeLineAction(action, 12)
150
+ action
166
151
  };
167
152
  }
168
153
  /**
@@ -245,29 +230,22 @@ function resolveLineChannelAccessToken(explicit, params) {
245
230
  //#endregion
246
231
  //#region extensions/line/src/send.ts
247
232
  const userProfileCache = /* @__PURE__ */ new Map();
248
- const PROFILE_CACHE_TTL_MS = 300 * 1e3;
249
- const PROFILE_CACHE_MAX_ENTRIES = 1e3;
250
- function cacheUserProfile(userId, profile) {
251
- userProfileCache.delete(userId);
252
- userProfileCache.set(userId, profile);
253
- if (userProfileCache.size <= PROFILE_CACHE_MAX_ENTRIES) return;
254
- for (const [key, cached] of userProfileCache) if (profile.fetchedAt - cached.fetchedAt >= PROFILE_CACHE_TTL_MS) userProfileCache.delete(key);
255
- pruneMapToMaxSize(userProfileCache, PROFILE_CACHE_MAX_ENTRIES);
256
- }
233
+ const PROFILE_CACHE_TTL_MS = 3e5;
257
234
  function normalizeTarget(to) {
258
235
  const trimmed = to.trim();
259
236
  if (!trimmed) throw new Error("Recipient is required for LINE sends");
260
237
  const normalized = trimmed.replace(/^line:group:/i, "").replace(/^line:room:/i, "").replace(/^line:user:/i, "").replace(/^line:/i, "");
261
238
  if (!normalized) throw new Error("Recipient is required for LINE sends");
262
- 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)}…`);
239
+ 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)}…`);
263
240
  return normalized;
264
241
  }
265
242
  function isLineUserChatId(chatId) {
266
243
  return /^U/i.test(chatId);
267
244
  }
268
245
  function createLineMessagingClient(opts) {
246
+ const cfg = requireRuntimeConfig(opts.cfg, "LINE send");
269
247
  const account = resolveLineAccount({
270
- cfg: requireRuntimeConfig(opts.cfg, "LINE send"),
248
+ cfg,
271
249
  accountId: opts.accountId
272
250
  });
273
251
  const token = resolveLineChannelAccessToken(opts.channelAccessToken, account);
@@ -324,7 +302,10 @@ function createLocationMessage(location) {
324
302
  function logLineHttpError(err, context) {
325
303
  if (!err || typeof err !== "object") return;
326
304
  const { status, statusText, body } = err;
327
- if (typeof body === "string") logVerbose(`line: ${context} failed (${status ? `${status} ${statusText ?? ""}`.trim() : "unknown status"}): ${body}`);
305
+ if (typeof body === "string") {
306
+ const summary = status ? `${status} ${statusText ?? ""}`.trim() : "unknown status";
307
+ logVerbose(`line: ${context} failed (${summary}): ${body}`);
308
+ }
328
309
  }
329
310
  function recordLineOutboundActivity(accountId) {
330
311
  recordChannelActivity({
@@ -344,10 +325,9 @@ function resolveLineReceiptKind(messages) {
344
325
  async function pushLineMessages(to, messages, opts, behavior = {}) {
345
326
  if (messages.length === 0) throw new Error("Message must be non-empty for LINE sends");
346
327
  const { account, client, chatId } = createLinePushContext(to, opts);
347
- const normalizedMessages = messages.map(normalizeLineMessageActions);
348
328
  const pushRequest = client.pushMessage({
349
329
  to: chatId,
350
- messages: normalizedMessages
330
+ messages
351
331
  });
352
332
  if (behavior.errorContext) await pushRequest.catch((err) => {
353
333
  logLineHttpError(err, behavior.errorContext);
@@ -355,7 +335,10 @@ async function pushLineMessages(to, messages, opts, behavior = {}) {
355
335
  });
356
336
  else await pushRequest;
357
337
  recordLineOutboundActivity(account.accountId);
358
- if (opts.verbose) logVerbose(behavior.verboseMessage?.(chatId, messages.length) ?? `line: pushed ${messages.length} messages to ${chatId}`);
338
+ if (opts.verbose) {
339
+ const logMessage = behavior.verboseMessage?.(chatId, messages.length) ?? `line: pushed ${messages.length} messages to ${chatId}`;
340
+ logVerbose(logMessage);
341
+ }
359
342
  return {
360
343
  messageId: "push",
361
344
  chatId,
@@ -369,10 +352,9 @@ async function pushLineMessages(to, messages, opts, behavior = {}) {
369
352
  }
370
353
  async function replyLineMessages(replyToken, messages, opts, behavior = {}) {
371
354
  const { account, client } = createLineMessagingClient(opts);
372
- const normalizedMessages = messages.map(normalizeLineMessageActions);
373
355
  await client.replyMessage({
374
356
  replyToken,
375
- messages: normalizedMessages
357
+ messages
376
358
  });
377
359
  recordLineOutboundActivity(account.accountId);
378
360
  if (opts.verbose) logVerbose(behavior.verboseMessage?.(messages.length) ?? `line: replied with ${messages.length} messages`);
@@ -395,13 +377,11 @@ async function sendMessageLine(to, text, opts) {
395
377
  case "audio":
396
378
  messages.push(createAudioMessage(mediaUrl, opts.durationMs ?? 6e4));
397
379
  break;
398
- default:
399
- {
400
- const previewImageUrl = opts.previewImageUrl?.trim() || mediaUrl;
401
- await validateLineMediaUrl(previewImageUrl);
402
- messages.push(createImageMessage(mediaUrl, previewImageUrl));
403
- }
404
- break;
380
+ default: {
381
+ const previewImageUrl = opts.previewImageUrl?.trim() || mediaUrl;
382
+ await validateLineMediaUrl(previewImageUrl);
383
+ messages.push(createImageMessage(mediaUrl, previewImageUrl));
384
+ }
405
385
  }
406
386
  }
407
387
  if (text?.trim()) messages.push(createTextMessage(text.trim()));
@@ -504,7 +484,7 @@ async function getUserProfile(userId, opts) {
504
484
  displayName: profile.displayName,
505
485
  pictureUrl: profile.pictureUrl
506
486
  };
507
- cacheUserProfile(userId, {
487
+ userProfileCache.set(userId, {
508
488
  ...result,
509
489
  fetchedAt: Date.now()
510
490
  });
@@ -519,184 +499,89 @@ async function getUserDisplayName(userId, opts) {
519
499
  }
520
500
  //#endregion
521
501
  //#region extensions/line/src/markdown-to-line.ts
522
- const LINE_MARKDOWN_OPTIONS = {
523
- assistantTranscriptRoleHeaders: true,
524
- autolink: false,
525
- blockquotePrefix: "",
526
- headingStyle: "none",
527
- horizontalRuleText: "",
528
- linkify: false,
529
- preserveSourceBlockSpacing: true
530
- };
531
- const TRANSCRIPT_ROLE_PREFIX = "[assistant-authored transcript] ";
532
- function parseLineMarkdown(text, tableMode = "block") {
533
- return markdownToIRWithMeta(text, {
534
- ...LINE_MARKDOWN_OPTIONS,
535
- tableMode
536
- });
537
- }
538
- function toMarkdownTable(table) {
539
- return {
540
- headers: table.headers,
541
- rows: table.rows,
542
- headerCells: table.headerCells,
543
- rowCells: table.rowCells
544
- };
545
- }
546
- function codeBlockSpans(ir) {
547
- return ir.styles.filter((span) => span.style === "code_block");
548
- }
549
- function toCodeBlock(ir, span) {
550
- return {
551
- ...span.language ? { language: span.language } : {},
552
- code: ir.text.slice(span.start, span.end).trim()
553
- };
554
- }
555
- function rangesOverlap(left, right) {
556
- return left.start < right.end && right.start < left.end;
557
- }
558
- function projectPlainText(ir, omitted = []) {
559
- const insertions = [];
560
- for (const link of ir.links) {
561
- if (omitted.some((range) => rangesOverlap(range, link))) continue;
562
- const href = link.href.trim();
563
- const label = ir.text.slice(link.start, link.end).trim();
564
- const comparableHref = href.startsWith("mailto:") ? href.slice(7) : href;
565
- if (href && label && label !== href && label !== comparableHref) insertions.push({
566
- position: link.end,
567
- text: ` (${href})`
568
- });
569
- }
570
- for (const annotation of ir.annotations ?? []) if (annotation.type === "assistant_transcript_role" && !omitted.some((range) => rangesOverlap(range, annotation))) insertions.push({
571
- position: annotation.start,
572
- text: TRANSCRIPT_ROLE_PREFIX
573
- });
574
- const inlineCodeSpans = ir.styles.filter((span) => span.style === "code" && !omitted.some((range) => rangesOverlap(range, span)));
575
- for (const span of inlineCodeSpans) if (stripMarkdown(ir.text.slice(span.start, span.end), { assistantTranscriptRoleHeaders: true }).startsWith(TRANSCRIPT_ROLE_PREFIX)) insertions.push({
576
- position: span.start,
577
- text: TRANSCRIPT_ROLE_PREFIX
578
- });
579
- insertions.sort((left, right) => left.position - right.position);
580
- const underlineTags = [...ir.text.matchAll(/<\/?u>/gi)].map((match) => ({
581
- start: match.index,
582
- end: match.index + match[0].length
583
- })).filter((tag) => !inlineCodeSpans.some((span) => rangesOverlap(tag, span)) && !omitted.some((span) => rangesOverlap(tag, span)));
584
- const removed = [...omitted, ...underlineTags].toSorted((left, right) => left.start - right.start);
585
- let output = "";
586
- let cursor = 0;
587
- let insertionIndex = 0;
588
- const appendRange = (end) => {
589
- while (insertionIndex < insertions.length) {
590
- const insertion = insertions[insertionIndex];
591
- if (!insertion || insertion.position > end) break;
592
- if (insertion.position >= cursor) {
593
- output += ir.text.slice(cursor, insertion.position) + insertion.text;
594
- cursor = insertion.position;
502
+ /**
503
+ * Regex patterns for markdown detection
504
+ */
505
+ const MARKDOWN_TABLE_REGEX = /^\|(.+)\|[\r\n]+\|[-:\s|]+\|[\r\n]+((?:\|.+\|[\r\n]*)+)/gm;
506
+ const MARKDOWN_CODE_BLOCK_REGEX = /```(\w*)\n([\s\S]*?)```/g;
507
+ const MARKDOWN_LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
508
+ /**
509
+ * Detect and extract markdown tables from text
510
+ */
511
+ function extractMarkdownTables(text) {
512
+ const tables = [];
513
+ let textWithoutTables = text;
514
+ MARKDOWN_TABLE_REGEX.lastIndex = 0;
515
+ let match;
516
+ const matches = [];
517
+ while ((match = MARKDOWN_TABLE_REGEX.exec(text)) !== null) {
518
+ const fullMatch = match[0];
519
+ const headerLine = match[1];
520
+ const bodyLines = match[2];
521
+ const headers = parseTableRow(headerLine);
522
+ const rows = bodyLines.trim().split(/[\r\n]+/).filter((line) => line.trim()).map(parseTableRow);
523
+ if (headers.length > 0 && rows.length > 0) matches.push({
524
+ fullMatch,
525
+ table: {
526
+ headers,
527
+ rows
595
528
  }
596
- insertionIndex += 1;
597
- }
598
- output += ir.text.slice(cursor, end);
599
- cursor = end;
600
- };
601
- for (const range of removed) {
602
- appendRange(range.start);
603
- cursor = Math.max(cursor, range.end);
604
- while (insertionIndex < insertions.length && (insertions[insertionIndex]?.position ?? cursor) < cursor) insertionIndex += 1;
605
- }
606
- appendRange(ir.text.length);
607
- return output.trim();
608
- }
609
- function sameSpanStyle(left, right) {
610
- return left.weight === right.weight && left.style === right.style && left.decoration === right.decoration;
611
- }
612
- function renderTableCell(cell, fallback) {
613
- if (!cell?.text.trim()) return {
614
- text: fallback,
615
- hasMarkup: false
616
- };
617
- const codeSpans = cell.styles.filter((span) => span.style === "code");
618
- const tags = [...cell.text.matchAll(/<\/?u>/gi)].map((match) => ({
619
- start: match.index,
620
- end: match.index + match[0].length,
621
- closing: match[0][1] === "/"
622
- })).filter((tag) => !codeSpans.some((span) => rangesOverlap(tag, span)));
623
- const boundaries = /* @__PURE__ */ new Set([0, cell.text.length]);
624
- for (const style of cell.styles) {
625
- boundaries.add(style.start);
626
- boundaries.add(style.end);
627
- }
628
- for (const link of cell.links) boundaries.add(link.end);
629
- for (const tag of tags) {
630
- boundaries.add(tag.start);
631
- boundaries.add(tag.end);
529
+ });
632
530
  }
633
- const sortedBoundaries = [...boundaries].toSorted((left, right) => left - right);
634
- const spans = [];
635
- let underlineDepth = 0;
636
- let hasMarkup = false;
637
- const appendSpan = (span) => {
638
- const previous = spans.at(-1);
639
- if (previous && sameSpanStyle(previous, span)) previous.text = `${previous.text ?? ""}${span.text ?? ""}`;
640
- else spans.push(span);
641
- };
642
- for (let index = 0; index < sortedBoundaries.length - 1; index += 1) {
643
- const start = sortedBoundaries[index];
644
- const end = sortedBoundaries[index + 1];
645
- if (start === void 0 || end === void 0) continue;
646
- const tag = tags.find((candidate) => candidate.start === start);
647
- if (tag) {
648
- underlineDepth += tag.closing ? -1 : 1;
649
- hasMarkup = true;
650
- continue;
651
- }
652
- const text = cell.text.slice(start, end);
653
- if (text) {
654
- const active = new Set(cell.styles.filter((style) => style.start <= start && style.end >= end).map((style) => style.style));
655
- const weight = active.has("bold") ? "bold" : void 0;
656
- const style = active.has("italic") ? "italic" : void 0;
657
- const decoration = active.has("strikethrough") ? "line-through" : underlineDepth > 0 ? "underline" : void 0;
658
- hasMarkup ||= weight !== void 0 || style !== void 0 || decoration !== void 0;
659
- appendSpan({
660
- type: "span",
661
- text,
662
- weight,
663
- style,
664
- decoration
665
- });
666
- }
667
- for (const link of cell.links.filter((candidate) => candidate.end === end)) {
668
- const href = link.href.trim();
669
- const label = cell.text.slice(link.start, link.end).trim();
670
- if (href && label && label !== href) {
671
- appendSpan({
672
- type: "span",
673
- text: ` (${href})`
674
- });
675
- hasMarkup = true;
676
- }
677
- }
531
+ for (let i = matches.length - 1; i >= 0; i--) {
532
+ const { fullMatch, table } = matches[i];
533
+ tables.unshift(table);
534
+ textWithoutTables = textWithoutTables.replace(fullMatch, "");
678
535
  }
679
536
  return {
680
- text: spans.map((span) => span.text ?? "").join("").trim() || fallback,
681
- ...hasMarkup ? { contents: spans } : {},
682
- hasMarkup
537
+ tables,
538
+ textWithoutTables
683
539
  };
684
540
  }
685
- function plainTableCell(text) {
686
- return parseLineMarkdown(text, "off").ir;
541
+ /**
542
+ * Parse a single table row (pipe-separated values)
543
+ */
544
+ function parseTableRow(row) {
545
+ return row.split("|").map((cell) => cell.trim()).filter((cell, index, arr) => {
546
+ if (index === 0 && cell === "") return false;
547
+ if (index === arr.length - 1 && cell === "") return false;
548
+ return true;
549
+ });
687
550
  }
688
- /** Convert a markdown table to a LINE Flex Message bubble. */
551
+ /**
552
+ * Convert a markdown table to a LINE Flex Message bubble
553
+ */
689
554
  function convertTableToFlexBubble(table) {
690
- const headerCells = (table.headerCells ?? table.headers.map(plainTableCell)).map((cell) => renderTableCell(cell, "-"));
691
- const rowCells = (table.rowCells ?? table.rows.map((row) => row.map(plainTableCell))).map((row) => row.map((cell) => renderTableCell(cell, "-")));
555
+ const parseCell = (value) => {
556
+ const raw = value?.trim() ?? "";
557
+ if (!raw) return {
558
+ text: "-",
559
+ bold: false,
560
+ hasMarkup: false
561
+ };
562
+ let hasMarkup = false;
563
+ return {
564
+ text: raw.replace(/\*\*(.+?)\*\*/g, (_, inner) => {
565
+ hasMarkup = true;
566
+ return String(inner);
567
+ }).trim() || "-",
568
+ bold: /^\*\*.+\*\*$/.test(raw),
569
+ hasMarkup
570
+ };
571
+ };
572
+ const headerCells = table.headers.map((header) => parseCell(header));
573
+ const rowCells = table.rows.map((row) => row.map((cell) => parseCell(cell)));
692
574
  const hasInlineMarkup = headerCells.some((cell) => cell.hasMarkup) || rowCells.some((row) => row.some((cell) => cell.hasMarkup));
693
- if (table.headers.length === 2 && !hasInlineMarkup) return createReceiptCard({
694
- title: headerCells.map((cell) => cell.text).join(" / "),
695
- items: rowCells.map((row) => ({
575
+ if (table.headers.length === 2 && !hasInlineMarkup) {
576
+ const items = rowCells.map((row) => ({
696
577
  name: row[0]?.text ?? "-",
697
578
  value: row[1]?.text ?? "-"
698
- }))
699
- });
579
+ }));
580
+ return createReceiptCard({
581
+ title: headerCells.map((cell) => cell.text).join(" / "),
582
+ items
583
+ });
584
+ }
700
585
  return {
701
586
  type: "bubble",
702
587
  body: {
@@ -709,7 +594,6 @@ function convertTableToFlexBubble(table) {
709
594
  contents: headerCells.map((cell) => ({
710
595
  type: "text",
711
596
  text: cell.text,
712
- contents: cell.contents,
713
597
  weight: "bold",
714
598
  size: "sm",
715
599
  color: "#333333",
@@ -722,32 +606,68 @@ function convertTableToFlexBubble(table) {
722
606
  type: "separator",
723
607
  margin: "sm"
724
608
  },
725
- ...rowCells.slice(0, 10).map((row, rowIndex) => ({
726
- type: "box",
727
- layout: "horizontal",
728
- contents: table.headers.map((_, colIndex) => {
729
- const cell = row[colIndex] ?? {
730
- text: "-",
731
- hasMarkup: false
732
- };
733
- return {
734
- type: "text",
735
- text: cell.text,
736
- contents: cell.contents,
737
- size: "sm",
738
- color: "#666666",
739
- flex: 1,
740
- wrap: true
741
- };
742
- }),
743
- margin: rowIndex === 0 ? "md" : "sm"
744
- }))
609
+ ...rowCells.slice(0, 10).map((row, rowIndex) => {
610
+ return {
611
+ type: "box",
612
+ layout: "horizontal",
613
+ contents: table.headers.map((_, colIndex) => {
614
+ const cell = row[colIndex] ?? {
615
+ text: "-",
616
+ bold: false,
617
+ hasMarkup: false
618
+ };
619
+ return {
620
+ type: "text",
621
+ text: cell.text,
622
+ size: "sm",
623
+ color: "#666666",
624
+ flex: 1,
625
+ wrap: true,
626
+ weight: cell.bold ? "bold" : void 0
627
+ };
628
+ }),
629
+ margin: rowIndex === 0 ? "md" : "sm"
630
+ };
631
+ })
745
632
  ],
746
633
  paddingAll: "lg"
747
634
  }
748
635
  };
749
636
  }
750
- /** Convert a code block to a LINE Flex Message bubble. */
637
+ /**
638
+ * Detect and extract code blocks from text
639
+ */
640
+ function extractCodeBlocks(text) {
641
+ const codeBlocks = [];
642
+ let textWithoutCode = text;
643
+ MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
644
+ let match;
645
+ const matches = [];
646
+ while ((match = MARKDOWN_CODE_BLOCK_REGEX.exec(text)) !== null) {
647
+ const fullMatch = match[0];
648
+ const language = match[1] || void 0;
649
+ const code = match[2];
650
+ matches.push({
651
+ fullMatch,
652
+ block: {
653
+ language,
654
+ code: code.trim()
655
+ }
656
+ });
657
+ }
658
+ for (let i = matches.length - 1; i >= 0; i--) {
659
+ const { fullMatch, block } = matches[i];
660
+ codeBlocks.unshift(block);
661
+ textWithoutCode = textWithoutCode.replace(fullMatch, "");
662
+ }
663
+ return {
664
+ codeBlocks,
665
+ textWithoutCode
666
+ };
667
+ }
668
+ /**
669
+ * Convert a code block to a LINE Flex Message bubble
670
+ */
751
671
  function convertCodeBlockToFlexBubble(block) {
752
672
  const titleText = block.language ? `Code (${block.language})` : "Code";
753
673
  const displayCode = block.code.length > 2e3 ? truncateUtf16Safe(block.code, 2e3) + "\n..." : block.code;
@@ -781,19 +701,97 @@ function convertCodeBlockToFlexBubble(block) {
781
701
  }
782
702
  };
783
703
  }
784
- /** Parse once, route existing block surfaces to Flex, and project the remainder as plain text. */
704
+ /**
705
+ * Extract markdown links from text
706
+ */
707
+ function extractLinks(text) {
708
+ const links = [];
709
+ MARKDOWN_LINK_REGEX.lastIndex = 0;
710
+ let match;
711
+ while ((match = MARKDOWN_LINK_REGEX.exec(text)) !== null) links.push({
712
+ text: match[1],
713
+ url: match[2]
714
+ });
715
+ return {
716
+ links,
717
+ textWithLinks: text.replace(MARKDOWN_LINK_REGEX, "$1")
718
+ };
719
+ }
720
+ /**
721
+ * Create a Flex Message with tappable link buttons
722
+ */
723
+ function convertLinksToFlexBubble(links) {
724
+ return {
725
+ type: "bubble",
726
+ body: {
727
+ type: "box",
728
+ layout: "vertical",
729
+ contents: [{
730
+ type: "text",
731
+ text: "Links",
732
+ weight: "bold",
733
+ size: "md",
734
+ color: "#333333"
735
+ }],
736
+ paddingAll: "lg",
737
+ paddingBottom: "sm"
738
+ },
739
+ footer: {
740
+ type: "box",
741
+ layout: "vertical",
742
+ contents: links.slice(0, 4).map((link, index) => ({
743
+ type: "button",
744
+ action: uriAction(link.text, link.url),
745
+ style: index === 0 ? "primary" : "secondary",
746
+ margin: index > 0 ? "sm" : void 0
747
+ })),
748
+ paddingAll: "md"
749
+ }
750
+ };
751
+ }
752
+ /**
753
+ * Main function: Process text for LINE output
754
+ * - Extracts tables → Flex Messages
755
+ * - Extracts code blocks → Flex Messages
756
+ * - Strips remaining markdown
757
+ * - Returns processed text + Flex Messages
758
+ */
785
759
  function processLineMessage(text) {
786
- const { ir, tables } = parseLineMarkdown(text);
787
- const codeSpans = codeBlockSpans(ir);
760
+ const flexMessages = [];
761
+ let processedText = text;
762
+ const { tables, textWithoutTables } = extractMarkdownTables(processedText);
763
+ processedText = textWithoutTables;
764
+ for (const table of tables) {
765
+ const bubble = convertTableToFlexBubble(table);
766
+ flexMessages.push(toFlexMessage("Table", bubble));
767
+ }
768
+ const { codeBlocks, textWithoutCode } = extractCodeBlocks(processedText);
769
+ processedText = textWithoutCode;
770
+ for (const block of codeBlocks) {
771
+ const bubble = convertCodeBlockToFlexBubble(block);
772
+ flexMessages.push(toFlexMessage("Code", bubble));
773
+ }
774
+ const { textWithLinks } = extractLinks(processedText);
775
+ processedText = textWithLinks;
776
+ processedText = stripMarkdown(processedText);
788
777
  return {
789
- text: projectPlainText(ir, codeSpans),
790
- flexMessages: [...tables.map((table) => toFlexMessage("Table", convertTableToFlexBubble(toMarkdownTable(table)))), ...codeSpans.map((span) => toFlexMessage("Code", convertCodeBlockToFlexBubble(toCodeBlock(ir, span))))]
778
+ text: processedText,
779
+ flexMessages
791
780
  };
792
781
  }
793
- /** Check if text contains markdown that needs conversion. */
782
+ /**
783
+ * Check if text contains markdown that needs conversion
784
+ */
794
785
  function hasMarkdownToConvert(text) {
795
- const { ir, tables } = parseLineMarkdown(text);
796
- return tables.length > 0 || ir.styles.length > 0 || ir.links.length > 0 || /<\/?u>/i.test(ir.text) || ir.text !== text.trimEnd();
786
+ MARKDOWN_TABLE_REGEX.lastIndex = 0;
787
+ if (MARKDOWN_TABLE_REGEX.test(text)) return true;
788
+ MARKDOWN_CODE_BLOCK_REGEX.lastIndex = 0;
789
+ if (MARKDOWN_CODE_BLOCK_REGEX.test(text)) return true;
790
+ if (/\*\*[^*]+\*\*/.test(text)) return true;
791
+ if (/~~[^~]+~~/.test(text)) return true;
792
+ if (/^#{1,6}\s+/m.test(text)) return true;
793
+ if (/^>\s+/m.test(text)) return true;
794
+ return false;
797
795
  }
798
796
  //#endregion
799
- export { createConfirmTemplate as A, sendMessageLine as C, createButtonMenu as D, buildTemplateMessageFromPayload as E, createTemplateCarousel as F, createYesNoConfirm as I, toFlexMessage as L, createImageCarouselColumn as M, createLinkMenu as N, createButtonTemplate as O, createProductCarousel as P, replyMessageLine as S, resolveLineChannelAccessToken as T, pushLocationMessage as _, stripMarkdown$1 as a, pushTemplateMessage as b, createImageMessage as c, createTextMessageWithQuickReplies as d, createVideoMessage as f, pushImageMessage as g, pushFlexMessage as h, processLineMessage as i, createImageCarousel as j, createCarouselColumn as k, createLocationMessage as l, getUserProfile as m, convertTableToFlexBubble as n, createAudioMessage as o, getUserDisplayName as p, hasMarkdownToConvert as r, createFlexMessage as s, convertCodeBlockToFlexBubble as t, createQuickReplyItems as u, pushMessageLine as v, showLoadingAnimation as w, pushTextMessageWithQuickReplies as x, pushMessagesLine as y };
797
+ export { buildTemplateMessageFromPayload as A, createYesNoConfirm as B, pushMessagesLine as C, sendMessageLine as D, replyMessageLine as E, createImageCarousel as F, createImageCarouselColumn as I, createLinkMenu as L, createButtonTemplate as M, createCarouselColumn as N, showLoadingAnimation as O, createConfirmTemplate as P, createProductCarousel as R, pushMessageLine as S, pushTextMessageWithQuickReplies as T, toFlexMessage as V, getUserDisplayName as _, extractLinks as a, pushImageMessage as b, processLineMessage as c, createFlexMessage as d, createImageMessage as f, createVideoMessage as g, createTextMessageWithQuickReplies as h, extractCodeBlocks as i, createButtonMenu as j, resolveLineChannelAccessToken as k, stripMarkdown$1 as l, createQuickReplyItems as m, convertLinksToFlexBubble as n, extractMarkdownTables as o, createLocationMessage as p, convertTableToFlexBubble as r, hasMarkdownToConvert as s, convertCodeBlockToFlexBubble as t, createAudioMessage as u, getUserProfile as v, pushTemplateMessage as w, pushLocationMessage as x, pushFlexMessage as y, createTemplateCarousel as z };