@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
@@ -0,0 +1,2 @@
1
+ import { t as monitorLineProvider } from "./monitor-DpxjN6Ks.mjs";
2
+ export { monitorLineProvider };
@@ -0,0 +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-o89GT1Qn.mjs";
2
+ export { buildTemplateMessageFromPayload, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
@@ -1,4 +1,4 @@
1
- import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
1
+ import { withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
2
2
  import { messagingApi } from "@line/bot-sdk";
3
3
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4
4
  //#region extensions/line/src/probe.ts
@@ -8,8 +8,8 @@ async function probeLineBot(channelAccessToken, timeoutMs = 5e3) {
8
8
  error: "Channel access token not configured"
9
9
  };
10
10
  const client = new messagingApi.MessagingApiClient({ channelAccessToken: channelAccessToken.trim() });
11
- return await runChannelProbe(timeoutMs, async () => {
12
- const profile = await client.getBotInfo();
11
+ try {
12
+ const profile = await withTimeout(client.getBotInfo(), timeoutMs);
13
13
  return {
14
14
  ok: true,
15
15
  bot: {
@@ -19,10 +19,12 @@ async function probeLineBot(channelAccessToken, timeoutMs = 5e3) {
19
19
  pictureUrl: profile.pictureUrl
20
20
  }
21
21
  };
22
- }, (error) => ({
23
- ok: false,
24
- error: formatErrorMessage(error)
25
- }));
22
+ } catch (err) {
23
+ return {
24
+ ok: false,
25
+ error: formatErrorMessage(err)
26
+ };
27
+ }
26
28
  }
27
29
  //#endregion
28
30
  export { probeLineBot as t };
@@ -0,0 +1,2 @@
1
+ import { t as probeLineBot } from "./probe-BslD77tJ.mjs";
2
+ export { probeLineBot };
@@ -1,17 +1,72 @@
1
- import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-iKJsyCq-.js";
1
+ import { c as createAgendaCard, l as createEventCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-BP6VHjzK.mjs";
2
+ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
3
+ import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
2
4
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
3
- import { DmPolicySchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
5
+ import { buildChannelConfigSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
4
6
  import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
5
7
  import { z } from "zod";
6
- import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
8
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
9
+ import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
10
+ import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
7
11
  import { parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
12
+ //#region extensions/line/src/group-keys.ts
13
+ function resolveLineGroupLookupIds(groupId) {
14
+ const normalized = groupId?.trim();
15
+ if (!normalized) return [];
16
+ if (normalized.startsWith("group:") || normalized.startsWith("room:")) {
17
+ const rawId = normalized.split(":").slice(1).join(":");
18
+ return rawId ? [rawId, normalized] : [normalized];
19
+ }
20
+ return [
21
+ normalized,
22
+ `group:${normalized}`,
23
+ `room:${normalized}`
24
+ ];
25
+ }
26
+ function resolveLineGroupConfigEntry(groups, params) {
27
+ if (!groups) return;
28
+ for (const candidate of resolveLineGroupLookupIds(params.groupId)) {
29
+ const hit = groups[candidate];
30
+ if (hit) return hit;
31
+ }
32
+ for (const candidate of resolveLineGroupLookupIds(params.roomId)) {
33
+ const hit = groups[candidate];
34
+ if (hit) return hit;
35
+ }
36
+ return groups["*"];
37
+ }
38
+ function resolveLineGroupsConfig(cfg, accountId) {
39
+ const lineConfig = cfg.channels?.line;
40
+ if (!lineConfig) return;
41
+ const normalizedAccountId = normalizeAccountId(accountId);
42
+ return resolveAccountEntry(lineConfig.accounts, normalizedAccountId)?.groups ?? lineConfig.groups;
43
+ }
44
+ function resolveExactLineGroupConfigKey(params) {
45
+ const groups = resolveLineGroupsConfig(params.cfg, params.accountId);
46
+ if (!groups) return;
47
+ return resolveLineGroupLookupIds(params.groupId).find((candidate) => Object.hasOwn(groups, candidate));
48
+ }
49
+ //#endregion
8
50
  //#region extensions/line/src/config-schema.ts
51
+ const DmPolicySchema = z.enum([
52
+ "open",
53
+ "allowlist",
54
+ "pairing",
55
+ "disabled"
56
+ ]);
57
+ const GroupPolicySchema = z.enum([
58
+ "open",
59
+ "allowlist",
60
+ "disabled"
61
+ ]);
9
62
  const ThreadBindingsSchema = z.object({
10
63
  enabled: z.boolean().optional(),
11
64
  idleHours: z.number().optional(),
12
65
  maxAgeHours: z.number().optional(),
13
66
  spawnSessions: z.boolean().optional(),
14
- defaultSpawnContext: z.enum(["isolated", "fork"]).optional()
67
+ defaultSpawnContext: z.enum(["isolated", "fork"]).optional(),
68
+ spawnSubagentSessions: z.boolean().optional(),
69
+ spawnAcpSessions: z.boolean().optional()
15
70
  }).strict();
16
71
  const LineCommonConfigSchemaBase = z.object({
17
72
  enabled: z.boolean().optional(),
@@ -29,24 +84,119 @@ const LineCommonConfigSchemaBase = z.object({
29
84
  webhookPath: z.string().optional(),
30
85
  threadBindings: ThreadBindingsSchema.optional()
31
86
  });
32
- const LineGroupConfigSchema = buildGroupEntrySchema().omit({
33
- tools: true,
34
- toolsBySender: true
87
+ const LineGroupConfigSchema = z.object({
88
+ enabled: z.boolean().optional(),
89
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
90
+ requireMention: z.boolean().optional(),
91
+ systemPrompt: z.string().optional(),
92
+ skills: z.array(z.string()).optional()
93
+ }).strict();
94
+ const LineAccountConfigSchema = LineCommonConfigSchemaBase.extend({ groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional() }).strict().superRefine((value, ctx) => {
95
+ requireChannelOpenAllowFrom({
96
+ channel: "line",
97
+ policy: value.dmPolicy,
98
+ allowFrom: value.allowFrom,
99
+ ctx,
100
+ requireOpenAllowFrom
101
+ });
35
102
  });
36
- const LineConfigSchema = buildMultiAccountChannelSchema(LineCommonConfigSchemaBase.extend({ groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional() }).strict(), {
37
- optionalAccount: true,
38
- refine: (value, ctx) => {
39
- requireChannelOpenAllowFrom({
40
- channel: "line",
41
- policy: value.dmPolicy,
42
- allowFrom: value.allowFrom,
43
- ctx,
44
- requireOpenAllowFrom
45
- });
46
- }
103
+ const LineConfigSchema = LineCommonConfigSchemaBase.extend({
104
+ accounts: z.record(z.string(), LineAccountConfigSchema.optional()).optional(),
105
+ defaultAccount: z.string().optional(),
106
+ groups: z.record(z.string(), LineGroupConfigSchema.optional()).optional()
107
+ }).strict().superRefine((value, ctx) => {
108
+ requireChannelOpenAllowFrom({
109
+ channel: "line",
110
+ policy: value.dmPolicy,
111
+ allowFrom: value.allowFrom,
112
+ ctx,
113
+ requireOpenAllowFrom
114
+ });
47
115
  });
48
116
  const LineChannelConfigSchema = buildChannelConfigSchema(LineConfigSchema);
49
117
  //#endregion
118
+ //#region extensions/line/src/runtime.ts
119
+ const { setRuntime: setLineRuntime, clearRuntime: clearLineRuntime, getRuntime: getLineRuntime } = createPluginRuntimeStore({
120
+ pluginId: "line",
121
+ errorMessage: "LINE runtime not initialized - plugin not registered"
122
+ });
123
+ //#endregion
124
+ //#region extensions/line/src/outbound-media.ts
125
+ const LINE_OUTBOUND_MEDIA_SSRF_POLICY = { allowPrivateNetwork: false };
126
+ async function validateLineMediaUrl(url) {
127
+ let parsed;
128
+ try {
129
+ parsed = new URL(url);
130
+ } catch {
131
+ throw new Error(`LINE outbound media URL must be a valid URL: ${url}`);
132
+ }
133
+ if (parsed.protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${url}`);
134
+ if (url.length > 2e3) throw new Error(`LINE outbound media URL must be 2000 chars or less (got ${url.length})`);
135
+ await resolvePinnedHostnameWithPolicy(parsed.hostname, { policy: LINE_OUTBOUND_MEDIA_SSRF_POLICY });
136
+ }
137
+ function isHttpsUrl(url) {
138
+ try {
139
+ return new URL(url).protocol === "https:";
140
+ } catch {
141
+ return false;
142
+ }
143
+ }
144
+ function detectLineMediaKindFromUrl(url) {
145
+ try {
146
+ const pathname = normalizeLowercaseStringOrEmpty(new URL(url).pathname);
147
+ if (/\.(png|jpe?g|gif|webp|bmp|heic|heif|avif)$/i.test(pathname)) return "image";
148
+ if (/\.(mp4|mov|m4v|webm)$/i.test(pathname)) return "video";
149
+ if (/\.(mp3|m4a|aac|wav|ogg|oga)$/i.test(pathname)) return "audio";
150
+ } catch {
151
+ return;
152
+ }
153
+ }
154
+ async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
155
+ const trimmedUrl = mediaUrl.trim();
156
+ if (isHttpsUrl(trimmedUrl)) {
157
+ await validateLineMediaUrl(trimmedUrl);
158
+ const previewImageUrl = opts.previewImageUrl?.trim();
159
+ if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
160
+ return {
161
+ mediaUrl: trimmedUrl,
162
+ mediaKind: opts.mediaKind ?? (typeof opts.durationMs === "number" ? "audio" : void 0) ?? (opts.trackingId?.trim() ? "video" : void 0) ?? detectLineMediaKindFromUrl(trimmedUrl) ?? "image",
163
+ ...previewImageUrl ? { previewImageUrl } : {},
164
+ ...typeof opts.durationMs === "number" ? { durationMs: opts.durationMs } : {},
165
+ ...opts.trackingId ? { trackingId: opts.trackingId } : {}
166
+ };
167
+ }
168
+ try {
169
+ if (new URL(trimmedUrl).protocol !== "https:") throw new Error(`LINE outbound media URL must use HTTPS: ${trimmedUrl}`);
170
+ } catch (e) {
171
+ if (e instanceof Error && e.message.startsWith("LINE outbound")) throw e;
172
+ }
173
+ throw new Error("LINE outbound media currently requires a public HTTPS URL");
174
+ }
175
+ //#endregion
176
+ //#region extensions/line/src/quick-reply-fallback.ts
177
+ function buildLineQuickReplyFallbackText(labels) {
178
+ const normalized = normalizeStringEntries(labels ?? []).slice(0, 13);
179
+ if (normalized.length === 0) return "Choose an option.";
180
+ return `Options:\n${normalized.map((label) => `- ${label}`).join("\n")}`;
181
+ }
182
+ //#endregion
183
+ //#region extensions/line/src/send-receipt.ts
184
+ function createLineSendReceipt(params) {
185
+ const messageId = params.messageId.trim();
186
+ const chatId = params.chatId.trim();
187
+ return createMessageReceiptFromOutboundResults({
188
+ results: messageId ? [{
189
+ channel: "line",
190
+ messageId,
191
+ chatId,
192
+ conversationId: chatId,
193
+ meta: { messageCount: params.messageCount ?? 1 }
194
+ }] : [],
195
+ ...chatId ? { threadId: chatId } : {},
196
+ kind: params.kind ?? "unknown"
197
+ });
198
+ }
199
+ //#endregion
50
200
  //#region extensions/line/src/reply-payload-transform.ts
51
201
  /**
52
202
  * Parse LINE-specific directives from text and extract them into ReplyPayload fields.
@@ -73,31 +223,17 @@ function parseLineDirectives(payload) {
73
223
  if (extras) for (const [key, value] of Object.entries(extras)) base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
74
224
  return base.join("&");
75
225
  };
76
- const parseConfirmAction = (part) => {
77
- const colonIndex = part.indexOf(":");
78
- if (colonIndex === -1) return {
79
- label: part,
80
- data: normalizeLowercaseStringOrEmpty(part)
81
- };
82
- return {
83
- label: part.slice(0, colonIndex).trim(),
84
- data: part.slice(colonIndex + 1).trim()
85
- };
86
- };
87
226
  const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i);
88
227
  if (quickRepliesMatch) {
89
- const options = normalizeStringEntries(expectDefined(quickRepliesMatch[1], "quick replies directive body").split(","));
228
+ const options = normalizeStringEntries(quickRepliesMatch[1].split(","));
90
229
  if (options.length > 0) lineData.quickReplies = [...lineData.quickReplies || [], ...options];
91
230
  text = text.replace(quickRepliesMatch[0], "").trim();
92
231
  }
93
232
  const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i);
94
233
  if (locationMatch && !lineData.location) {
95
- const parts = expectDefined(locationMatch[1], "location directive body").split("|").map((s) => s.trim());
234
+ const parts = locationMatch[1].split("|").map((s) => s.trim());
96
235
  if (parts.length >= 4) {
97
- const title = expectDefined(parts[0], "location title field");
98
- const address = expectDefined(parts[1], "location address field");
99
- const latStr = expectDefined(parts[2], "location latitude field");
100
- const lonStr = expectDefined(parts[3], "location longitude field");
236
+ const [title, address, latStr, lonStr] = parts;
101
237
  const latitude = parseStrictFiniteNumber(latStr);
102
238
  const longitude = parseStrictFiniteNumber(lonStr);
103
239
  if (latitude !== void 0 && longitude !== void 0) lineData.location = {
@@ -111,20 +247,18 @@ function parseLineDirectives(payload) {
111
247
  }
112
248
  const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i);
113
249
  if (confirmMatch && !lineData.templateMessage) {
114
- const parts = expectDefined(confirmMatch[1], "confirm directive body").split("|").map((s) => s.trim());
250
+ const parts = confirmMatch[1].split("|").map((s) => s.trim());
115
251
  if (parts.length >= 3) {
116
- const question = expectDefined(parts[0], "confirm question field");
117
- const yesPart = expectDefined(parts[1], "confirm yes field");
118
- const noPart = expectDefined(parts[2], "confirm no field");
119
- const yesAction = parseConfirmAction(yesPart);
120
- const noAction = parseConfirmAction(noPart);
121
- if (question && yesAction.label && noAction.label) lineData.templateMessage = {
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 = {
122
256
  type: "confirm",
123
257
  text: question,
124
- confirmLabel: yesAction.label,
125
- confirmData: yesAction.data,
126
- cancelLabel: noAction.label,
127
- cancelData: noAction.data,
258
+ confirmLabel: yesLabel,
259
+ confirmData: yesData,
260
+ cancelLabel: noLabel,
261
+ cancelData: noData,
128
262
  altText: question
129
263
  };
130
264
  }
@@ -132,11 +266,10 @@ function parseLineDirectives(payload) {
132
266
  }
133
267
  const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i);
134
268
  if (buttonsMatch && !lineData.templateMessage) {
135
- const parts = expectDefined(buttonsMatch[1], "buttons directive body").split("|").map((s) => s.trim());
269
+ const parts = buttonsMatch[1].split("|").map((s) => s.trim());
136
270
  if (parts.length >= 3) {
137
- const title = expectDefined(parts[0], "buttons title field");
138
- const bodyText = expectDefined(parts[1], "buttons text field");
139
- const actions = expectDefined(parts[2], "buttons actions field").split(",").map((actionStr) => {
271
+ const [title, bodyText, actionsStr] = parts;
272
+ const actions = actionsStr.split(",").map((actionStr) => {
140
273
  const trimmed = actionStr.trim();
141
274
  const colonIndex = (() => {
142
275
  const index = trimmed.indexOf(":");
@@ -169,23 +302,22 @@ function parseLineDirectives(payload) {
169
302
  label,
170
303
  data: data || label
171
304
  };
172
- }).filter((action) => action.label);
173
- if (actions.length > 0 && bodyText) lineData.templateMessage = {
305
+ });
306
+ if (actions.length > 0) lineData.templateMessage = {
174
307
  type: "buttons",
175
- ...title ? { title } : {},
308
+ title,
176
309
  text: bodyText,
177
310
  actions: actions.slice(0, 4),
178
- altText: title ? `${title}: ${bodyText}` : bodyText
311
+ altText: `${title}: ${bodyText}`
179
312
  };
180
313
  }
181
314
  text = text.replace(buttonsMatch[0], "").trim();
182
315
  }
183
316
  const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i);
184
317
  if (mediaPlayerMatch && !lineData.flexMessage) {
185
- const parts = expectDefined(mediaPlayerMatch[1], "media player directive body").split("|").map((s) => s.trim());
318
+ const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim());
186
319
  if (parts.length >= 1) {
187
- const title = expectDefined(parts[0], "media player title field");
188
- const [, artist, source, imageUrl, statusStr] = parts;
320
+ const [title, artist, source, imageUrl, statusStr] = parts;
189
321
  const isPlaying = normalizeLowercaseStringOrEmpty(statusStr) === "playing";
190
322
  const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : void 0;
191
323
  const deviceKey = toSlug(source || title || "media");
@@ -211,13 +343,9 @@ function parseLineDirectives(payload) {
211
343
  }
212
344
  const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i);
213
345
  if (eventMatch && !lineData.flexMessage) {
214
- const parts = expectDefined(eventMatch[1], "event directive body").split("|").map((s) => s.trim());
346
+ const parts = eventMatch[1].split("|").map((s) => s.trim());
215
347
  if (parts.length >= 2) {
216
- const title = expectDefined(parts[0], "event title field");
217
- const date = expectDefined(parts[1], "event date field");
218
- const time = parts[2];
219
- const location = parts[3];
220
- const description = parts[4];
348
+ const [title, date, time, location, description] = parts;
221
349
  const card = createEventCard({
222
350
  title: title || "Event",
223
351
  date: date || "TBD",
@@ -234,10 +362,9 @@ function parseLineDirectives(payload) {
234
362
  }
235
363
  const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i);
236
364
  if (appleTvMatch && !lineData.flexMessage) {
237
- const parts = expectDefined(appleTvMatch[1], "Apple TV directive body").split("|").map((s) => s.trim());
365
+ const parts = appleTvMatch[1].split("|").map((s) => s.trim());
238
366
  if (parts.length >= 1) {
239
- const deviceName = expectDefined(parts[0], "Apple TV device name field");
240
- const [, status] = parts;
367
+ const [deviceName, status] = parts;
241
368
  const deviceKey = toSlug(deviceName || "apple_tv");
242
369
  const card = createAppleTvRemoteCard({
243
370
  deviceName: deviceName || "Apple TV",
@@ -266,10 +393,10 @@ function parseLineDirectives(payload) {
266
393
  }
267
394
  const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i);
268
395
  if (agendaMatch && !lineData.flexMessage) {
269
- const parts = expectDefined(agendaMatch[1], "agenda directive body").split("|").map((s) => s.trim());
396
+ const parts = agendaMatch[1].split("|").map((s) => s.trim());
270
397
  if (parts.length >= 2) {
271
- const title = expectDefined(parts[0], "agenda title field");
272
- const events = normalizeStringEntries(expectDefined(parts[1], "agenda events field").split(",")).map((eventStr) => {
398
+ const [title, eventsStr] = parts;
399
+ const events = eventsStr.split(",").map((eventStr) => {
273
400
  const trimmed = eventStr.trim();
274
401
  const colonIdx = trimmed.lastIndexOf(":");
275
402
  if (colonIdx > 0) return {
@@ -291,20 +418,17 @@ function parseLineDirectives(payload) {
291
418
  }
292
419
  const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i);
293
420
  if (deviceMatch && !lineData.flexMessage) {
294
- const parts = expectDefined(deviceMatch[1], "device directive body").split("|").map((s) => s.trim());
421
+ const parts = deviceMatch[1].split("|").map((s) => s.trim());
295
422
  if (parts.length >= 1) {
296
- const deviceName = expectDefined(parts[0], "device name field");
297
- const [, deviceType, status, controlsStr] = parts;
423
+ const [deviceName, deviceType, status, controlsStr] = parts;
298
424
  const deviceKey = toSlug(deviceName || "device");
299
- const controls = controlsStr ? normalizeStringEntries(controlsStr.split(",")).flatMap((ctrlStr) => {
300
- const controlParts = ctrlStr.split(":").map((s) => s.trim());
301
- const label = expectDefined(controlParts[0], "device control label");
302
- if (!label) return [];
303
- const action = controlParts[1] || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
304
- return [{
425
+ 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, "_");
428
+ return {
305
429
  label,
306
430
  data: lineActionData(action, { "line.device": deviceKey })
307
- }];
431
+ };
308
432
  }) : [];
309
433
  const card = createDeviceControlCard({
310
434
  deviceName: deviceName || "Device",
@@ -331,4 +455,4 @@ function hasLineDirectives(text) {
331
455
  return /\[\[(quick_replies|location|confirm|buttons|media_player|event|agenda|device|appletv_remote):/i.test(text);
332
456
  }
333
457
  //#endregion
334
- export { LineConfigSchema as i, parseLineDirectives as n, LineChannelConfigSchema as r, hasLineDirectives as t };
458
+ export { resolveLineOutboundMedia as a, setLineRuntime as c, resolveExactLineGroupConfigKey as d, resolveLineGroupConfigEntry as f, buildLineQuickReplyFallbackText as i, LineChannelConfigSchema as l, resolveLineGroupsConfig as m, parseLineDirectives as n, validateLineMediaUrl as o, resolveLineGroupLookupIds as p, createLineSendReceipt as r, getLineRuntime as s, hasLineDirectives as t, LineConfigSchema as u };