@openclaw/line 2026.9.7 → 2026.10.1-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/.setup/api-BBSLQek1.mjs +3 -0
- package/dist/.setup/{api-w4QdlCCB.mjs → api-CMU7bG-C.mjs} +2 -2
- package/dist/.setup/{card-command-DYf4gnmI.mjs → card-command-BrU6u6pm.mjs} +2 -2
- package/dist/.setup/{channel-DN6Za8Vm.mjs → channel-InKuXaRS.mjs} +32 -78
- package/dist/.setup/{channel.runtime-bd5vGeSo.mjs → channel.runtime-BEhASjr_.mjs} +3 -3
- package/dist/.setup/{monitor-C01hAm3f.mjs → monitor-Bb3HieCd.mjs} +92 -160
- package/dist/.setup/monitor.runtime-C6Kgs5BI.mjs +2 -0
- package/dist/.setup/{outbound.runtime-D_Gs99Dm.mjs → outbound.runtime-BwO_m4n5.mjs} +2 -2
- package/dist/.setup/probe.runtime-C6LYzOHu.mjs +2 -0
- package/dist/.setup/{send-DOY1WTxv.mjs → send-DAwROcyQ.mjs} +10 -16
- package/dist/.setup/{send-retry-DV6I0muq.mjs → send-retry-B9Nor0-J.mjs} +54 -86
- package/dist/.setup/{setup-surface-CbfkmkIS.mjs → setup-surface-1jkHGWg7.mjs} +7 -2
- package/dist/.setup/{template-messages-CxCnYsWN.mjs → template-messages-DdF9zv_j.mjs} +103 -120
- package/dist/.setup/{webhook-spool-contract-1rY8OJw8.mjs → webhook-spool-contract-msjTl60Q.mjs} +1 -4
- package/dist/api.js +2 -2
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +5 -4
- package/dist/index.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/package.json +4 -4
- package/dist/.setup/api-4RgpQmHW.mjs +0 -3
- package/dist/.setup/monitor.runtime-DwH2zK48.mjs +0 -2
- package/dist/.setup/probe.runtime-Cj08-BZ1.mjs +0 -2
|
@@ -82,74 +82,51 @@ function resolveLineMediaKind(url, opts) {
|
|
|
82
82
|
kindSource: "url"
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
-
async function resolveLineOutboundMedia(mediaUrl, opts = {}) {
|
|
86
|
-
const trimmedUrl = mediaUrl.trim();
|
|
87
|
-
if (isHttpsUrl(trimmedUrl)) {
|
|
88
|
-
await validateLineMediaUrl(trimmedUrl);
|
|
89
|
-
const previewImageUrl = opts.previewImageUrl?.trim();
|
|
90
|
-
if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
|
|
91
|
-
return {
|
|
92
|
-
mediaUrl: trimmedUrl,
|
|
93
|
-
...resolveLineMediaKind(trimmedUrl, opts),
|
|
94
|
-
...previewImageUrl ? { previewImageUrl } : {},
|
|
95
|
-
...typeof opts.durationMs === "number" ? { durationMs: opts.durationMs } : {},
|
|
96
|
-
...opts.trackingId ? { trackingId: opts.trackingId } : {}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
let parsed;
|
|
100
|
-
try {
|
|
101
|
-
parsed = new URL(trimmedUrl);
|
|
102
|
-
} catch {}
|
|
103
|
-
if (parsed) throw new Error("LINE outbound media URL must use HTTPS");
|
|
104
|
-
throw new Error("LINE outbound media currently requires a public HTTPS URL");
|
|
105
|
-
}
|
|
106
85
|
function isLineUserTarget(target) {
|
|
107
86
|
const normalized = target.trim().replace(/^line:(group|room|user):/i, "").replace(/^line:/i, "");
|
|
108
87
|
return /^U/i.test(normalized);
|
|
109
88
|
}
|
|
110
|
-
function createImageMessage(originalContentUrl, previewImageUrl) {
|
|
111
|
-
return {
|
|
112
|
-
type: "image",
|
|
113
|
-
originalContentUrl,
|
|
114
|
-
previewImageUrl: previewImageUrl ?? originalContentUrl
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
function createVideoMessage(originalContentUrl, previewImageUrl, trackingId) {
|
|
118
|
-
return {
|
|
119
|
-
type: "video",
|
|
120
|
-
originalContentUrl,
|
|
121
|
-
previewImageUrl,
|
|
122
|
-
...trackingId ? { trackingId } : {}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
function createAudioMessage(originalContentUrl, durationMs) {
|
|
126
|
-
return {
|
|
127
|
-
type: "audio",
|
|
128
|
-
originalContentUrl,
|
|
129
|
-
duration: durationMs
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
function lineMediaUrlFallback(mediaUrl) {
|
|
133
|
-
return {
|
|
134
|
-
type: "text",
|
|
135
|
-
text: mediaUrl
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function buildLineMediaMessageObject(resolved, opts) {
|
|
139
|
-
switch (resolved.mediaKind) {
|
|
140
|
-
case "unsupported": return lineMediaUrlFallback(resolved.mediaUrl);
|
|
141
|
-
case "video": {
|
|
142
|
-
const previewImageUrl = resolved.previewImageUrl?.trim();
|
|
143
|
-
if (previewImageUrl) return createVideoMessage(resolved.mediaUrl, previewImageUrl, opts?.allowTrackingId ? resolved.trackingId : void 0);
|
|
144
|
-
if (resolved.kindSource !== "url") throw new Error("LINE video messages require previewImageUrl to reference an image URL");
|
|
145
|
-
return lineMediaUrlFallback(resolved.mediaUrl);
|
|
146
|
-
}
|
|
147
|
-
case "audio": return createAudioMessage(resolved.mediaUrl, resolved.durationMs ?? 6e4);
|
|
148
|
-
default: return createImageMessage(resolved.mediaUrl, resolved.previewImageUrl);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
89
|
async function buildLineMediaMessage(mediaUrl, opts, target) {
|
|
152
|
-
|
|
90
|
+
const trimmedUrl = mediaUrl.trim();
|
|
91
|
+
if (!isHttpsUrl(trimmedUrl)) {
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = new URL(trimmedUrl);
|
|
95
|
+
} catch {}
|
|
96
|
+
throw new Error(parsed ? "LINE outbound media URL must use HTTPS" : "LINE outbound media currently requires a public HTTPS URL");
|
|
97
|
+
}
|
|
98
|
+
await validateLineMediaUrl(trimmedUrl);
|
|
99
|
+
const previewImageUrl = opts.previewImageUrl?.trim();
|
|
100
|
+
if (previewImageUrl) await validateLineMediaUrl(previewImageUrl);
|
|
101
|
+
const { mediaKind, kindSource } = resolveLineMediaKind(trimmedUrl, opts);
|
|
102
|
+
switch (mediaKind) {
|
|
103
|
+
case "unsupported": return {
|
|
104
|
+
type: "text",
|
|
105
|
+
text: trimmedUrl
|
|
106
|
+
};
|
|
107
|
+
case "video":
|
|
108
|
+
if (previewImageUrl) return {
|
|
109
|
+
type: "video",
|
|
110
|
+
originalContentUrl: trimmedUrl,
|
|
111
|
+
previewImageUrl,
|
|
112
|
+
...isLineUserTarget(target) && opts.trackingId ? { trackingId: opts.trackingId } : {}
|
|
113
|
+
};
|
|
114
|
+
if (kindSource !== "url") throw new Error("LINE video messages require previewImageUrl to reference an image URL");
|
|
115
|
+
return {
|
|
116
|
+
type: "text",
|
|
117
|
+
text: trimmedUrl
|
|
118
|
+
};
|
|
119
|
+
case "audio": return {
|
|
120
|
+
type: "audio",
|
|
121
|
+
originalContentUrl: trimmedUrl,
|
|
122
|
+
duration: typeof opts.durationMs === "number" ? opts.durationMs : 6e4
|
|
123
|
+
};
|
|
124
|
+
default: return {
|
|
125
|
+
type: "image",
|
|
126
|
+
originalContentUrl: trimmedUrl,
|
|
127
|
+
previewImageUrl: previewImageUrl || trimmedUrl
|
|
128
|
+
};
|
|
129
|
+
}
|
|
153
130
|
}
|
|
154
131
|
//#endregion
|
|
155
132
|
//#region extensions/line/src/quote-tokens.ts
|
|
@@ -563,6 +540,16 @@ function postbackAction(label, data, displayText) {
|
|
|
563
540
|
}
|
|
564
541
|
//#endregion
|
|
565
542
|
//#region extensions/line/src/flex-templates/common.ts
|
|
543
|
+
function createCardTitle(text) {
|
|
544
|
+
return {
|
|
545
|
+
type: "text",
|
|
546
|
+
text,
|
|
547
|
+
weight: "bold",
|
|
548
|
+
size: "xl",
|
|
549
|
+
color: "#111111",
|
|
550
|
+
wrap: true
|
|
551
|
+
};
|
|
552
|
+
}
|
|
566
553
|
function createCardBubble(bodyContents, footer) {
|
|
567
554
|
const bubble = {
|
|
568
555
|
type: "bubble",
|
|
@@ -605,12 +592,7 @@ function createInfoCard(title, body, footer) {
|
|
|
605
592
|
backgroundColor: "#06C755",
|
|
606
593
|
cornerRadius: "2px"
|
|
607
594
|
}, {
|
|
608
|
-
|
|
609
|
-
text: title,
|
|
610
|
-
weight: "bold",
|
|
611
|
-
size: "xl",
|
|
612
|
-
color: "#111111",
|
|
613
|
-
wrap: true,
|
|
595
|
+
...createCardTitle(title),
|
|
614
596
|
flex: 1,
|
|
615
597
|
margin: "lg"
|
|
616
598
|
}]
|
|
@@ -679,14 +661,7 @@ function createListCard(title, items) {
|
|
|
679
661
|
return itemBox;
|
|
680
662
|
});
|
|
681
663
|
return createCardBubble([
|
|
682
|
-
|
|
683
|
-
type: "text",
|
|
684
|
-
text: title,
|
|
685
|
-
weight: "bold",
|
|
686
|
-
size: "xl",
|
|
687
|
-
color: "#111111",
|
|
688
|
-
wrap: true
|
|
689
|
-
},
|
|
664
|
+
createCardTitle(title),
|
|
690
665
|
{
|
|
691
666
|
type: "separator",
|
|
692
667
|
margin: "lg",
|
|
@@ -766,14 +741,7 @@ function createActionCard(title, body, actions, options) {
|
|
|
766
741
|
//#region extensions/line/src/flex-templates/schedule-cards.ts
|
|
767
742
|
function buildTitleSubtitleHeader(params) {
|
|
768
743
|
const { title, subtitle } = params;
|
|
769
|
-
const headerContents = [
|
|
770
|
-
type: "text",
|
|
771
|
-
text: title,
|
|
772
|
-
weight: "bold",
|
|
773
|
-
size: "xl",
|
|
774
|
-
color: "#111111",
|
|
775
|
-
wrap: true
|
|
776
|
-
}];
|
|
744
|
+
const headerContents = [createCardTitle(title)];
|
|
777
745
|
if (subtitle) headerContents.push({
|
|
778
746
|
type: "text",
|
|
779
747
|
text: subtitle,
|
|
@@ -1230,4 +1198,4 @@ async function explainLineRefusal(params) {
|
|
|
1230
1198
|
};
|
|
1231
1199
|
}
|
|
1232
1200
|
//#endregion
|
|
1233
|
-
export { withoutLineQuoteTokens as A, toFlexMessage as C, recordLineQuoteToken as D, readLineQuoteToken as E, inferLineTargetChatType as M, normalizeLineMessagingTarget as N, reportLineQuoteCarrierMissing as O, fitsLineFlexBubble as S, canCarryLineQuoteToken as T,
|
|
1201
|
+
export { withoutLineQuoteTokens as A, toFlexMessage as C, recordLineQuoteToken as D, readLineQuoteToken as E, inferLineTargetChatType as M, normalizeLineMessagingTarget as N, reportLineQuoteCarrierMissing as O, fitsLineFlexBubble as S, canCarryLineQuoteToken as T, messageAction as _, probeLineBot as a, truncateLineActionLabel as b, createAgendaCard as c, createActionCard as d, createImageCard as f, createCardTitle as g, createCardBubble as h, runLinePushWithRetries as i, buildLineMediaMessage as j, resolveLineQuoteToken as k, createEventCard as l, createListCard as m, findLineHttpError as n, resolveLineChannelAccessToken as o, createInfoCard as p, resolveLineNonDispatchRetryable as r, createLineSendReceipt as s, explainLineRefusal as t, createReceiptCard as u, normalizeLineMessage as v, applyLineQuoteToken as w, uriAction as x, postbackAction as y };
|
|
@@ -4,13 +4,18 @@ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runti
|
|
|
4
4
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
5
5
|
import { createAllowFromSection, createPromptParsedAllowFromForAccount, createSetupInputPresenceValidator, createSetupTranslator, createStandardChannelSetupStatus, defineTokenCredential, formatDocsLink, parseSetupEntriesWithParser, patchScopedAccountConfig, setSetupChannelEnabled, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
6
6
|
import { createChannelDmPolicy } from "openclaw/plugin-sdk/channel-dm-policy";
|
|
7
|
+
//#region extensions/line/src/bot-access.ts
|
|
8
|
+
function normalizeLineAllowEntry(value) {
|
|
9
|
+
return String(value).trim().replace(/^line:(?:user:)?/i, "");
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
7
12
|
//#region extensions/line/src/account-helpers.ts
|
|
8
13
|
function hasLineCredentials(account) {
|
|
9
14
|
if (account.tokenStatus && account.signingSecretStatus) return account.tokenStatus !== "missing" && account.signingSecretStatus !== "missing";
|
|
10
15
|
return Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim());
|
|
11
16
|
}
|
|
12
17
|
function parseLineAllowFromId(raw) {
|
|
13
|
-
const trimmed = raw
|
|
18
|
+
const trimmed = normalizeLineAllowEntry(raw);
|
|
14
19
|
if (!/^U[a-f0-9]{32}$/i.test(trimmed)) return null;
|
|
15
20
|
return trimmed;
|
|
16
21
|
}
|
|
@@ -322,4 +327,4 @@ const lineSetupWizard = {
|
|
|
322
327
|
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
|
323
328
|
};
|
|
324
329
|
//#endregion
|
|
325
|
-
export { hasLineCredentials as i, lineSetupAdapter as n, lineSetupContract as r, lineSetupWizard as t };
|
|
330
|
+
export { normalizeLineAllowEntry as a, hasLineCredentials as i, lineSetupAdapter as n, lineSetupContract as r, lineSetupWizard as t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { C as toFlexMessage, S as fitsLineFlexBubble, _ as
|
|
1
|
+
import { C as toFlexMessage, S as fitsLineFlexBubble, _ as messageAction, u as createReceiptCard, x as uriAction, y as postbackAction } from "./send-retry-B9Nor0-J.mjs";
|
|
2
2
|
import { markdownToIRWithMeta, stripMarkdown } from "openclaw/plugin-sdk/text-chunking";
|
|
3
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
3
4
|
//#region extensions/line/src/markdown-to-line.ts
|
|
4
5
|
const LINE_MARKDOWN_OPTIONS = {
|
|
5
6
|
assistantTranscriptRoleHeaders: true,
|
|
@@ -18,9 +19,6 @@ function parseLineMarkdown(text, tableMode = "block") {
|
|
|
18
19
|
tableMode
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
|
-
function codeBlockSpans(ir) {
|
|
22
|
-
return ir.styles.filter((span) => span.style === "code_block");
|
|
23
|
-
}
|
|
24
22
|
function toCodeBlock(ir, span) {
|
|
25
23
|
return {
|
|
26
24
|
...span.language ? { language: span.language } : {},
|
|
@@ -305,7 +303,7 @@ function convertCodeBlockToFlexBubble(block) {
|
|
|
305
303
|
/** Parse once, route existing block surfaces to Flex, and project the remainder as plain text. */
|
|
306
304
|
function processLineMessage(text) {
|
|
307
305
|
const { ir, tables } = parseLineMarkdown(text);
|
|
308
|
-
const codeSpans =
|
|
306
|
+
const codeSpans = ir.styles.filter((span) => span.style === "code_block");
|
|
309
307
|
const plainTextInsertions = [];
|
|
310
308
|
for (const table of tables) {
|
|
311
309
|
const bubble = convertTableToFlexBubble(table);
|
|
@@ -338,17 +336,55 @@ function processLineMessage(text) {
|
|
|
338
336
|
});
|
|
339
337
|
}
|
|
340
338
|
const segments = [];
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
339
|
+
projectPlainText(ir, codeSpans, plainTextInsertions, (segment) => segments.push(segment));
|
|
340
|
+
return segments;
|
|
341
|
+
}
|
|
342
|
+
//#endregion
|
|
343
|
+
//#region packages/normalization-core/src/utf16-slice.ts
|
|
344
|
+
function isHighSurrogate(codeUnit) {
|
|
345
|
+
return codeUnit >= 55296 && codeUnit <= 56319;
|
|
346
|
+
}
|
|
347
|
+
function isLowSurrogate(codeUnit) {
|
|
348
|
+
return codeUnit >= 56320 && codeUnit <= 57343;
|
|
349
|
+
}
|
|
350
|
+
/** Moves a chunk boundary away from the middle of a UTF-16 surrogate pair. */
|
|
351
|
+
function avoidTrailingHighSurrogateBreak(text, start, end) {
|
|
352
|
+
if (end <= start || end >= text.length || !isHighSurrogate(text.charCodeAt(end - 1)) || !isLowSurrogate(text.charCodeAt(end))) return end;
|
|
353
|
+
const adjusted = end - 1;
|
|
354
|
+
return adjusted > start ? adjusted : end + 1;
|
|
355
|
+
}
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region packages/normalization-core/src/grapheme.ts
|
|
358
|
+
let graphemeSegmenter;
|
|
359
|
+
function getGraphemeSegmenter() {
|
|
360
|
+
graphemeSegmenter ??= new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
361
|
+
return graphemeSegmenter;
|
|
362
|
+
}
|
|
363
|
+
/** Uses the same source text that created segments. */
|
|
364
|
+
function containingSegment(segments, text, index) {
|
|
365
|
+
const position = Number.isNaN(index) ? 0 : Math.trunc(index);
|
|
366
|
+
const codePoint = text.codePointAt(position);
|
|
367
|
+
return segments.containing(codePoint !== void 0 && codePoint > 65535 ? position + 1 : position);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Chooses a whole-grapheme cut within the hard budget, honoring a usable preference.
|
|
371
|
+
* If no whole grapheme fits, allowPartial permits a surrogate-safe progress cut;
|
|
372
|
+
* a leading surrogate pair can exceed maxEnd by one code unit.
|
|
373
|
+
*/
|
|
374
|
+
function findGraphemeChunkEnd(text, start, maxEnd, preferredEnd = maxEnd, allowPartial = true) {
|
|
375
|
+
const hardEnd = Math.min(maxEnd, text.length);
|
|
376
|
+
if (hardEnd <= start) return start;
|
|
377
|
+
const preferred = Number.isInteger(preferredEnd) && preferredEnd > start && preferredEnd <= hardEnd ? preferredEnd : hardEnd;
|
|
378
|
+
if (preferred === text.length) return preferred;
|
|
379
|
+
const segments = getGraphemeSegmenter().segment(text);
|
|
380
|
+
let end = containingSegment(segments, text, preferred)?.index ?? preferred;
|
|
381
|
+
if (end <= start && preferred < hardEnd) end = hardEnd === text.length ? hardEnd : containingSegment(segments, text, hardEnd)?.index ?? hardEnd;
|
|
382
|
+
return end > start ? end : allowPartial ? avoidTrailingHighSurrogateBreak(text, start, hardEnd) : start;
|
|
346
383
|
}
|
|
347
384
|
//#endregion
|
|
348
385
|
//#region extensions/line/src/template-messages.ts
|
|
349
386
|
const COMPACT_TEMPLATE_TEXT_LIMIT = 60;
|
|
350
387
|
const TEMPLATE_ALT_TEXT_LIMIT = 1500;
|
|
351
|
-
const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
352
388
|
function buildTemplatePayloadAction(action) {
|
|
353
389
|
if (action.type === "uri" && action.uri) return uriAction(action.label, action.uri);
|
|
354
390
|
if (action.type === "postback" && action.data) return postbackAction(action.label, action.data, action.label);
|
|
@@ -361,40 +397,19 @@ function resolveTemplateTextLimit(params) {
|
|
|
361
397
|
return params.title !== void 0 || params.thumbnailImageUrl !== void 0 ? COMPACT_TEMPLATE_TEXT_LIMIT : params.textOnlyLimit;
|
|
362
398
|
}
|
|
363
399
|
function truncateTemplateText(text, limit) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (result.length + segment.length > limit) {
|
|
367
|
-
if (!result) for (const codePoint of segment) {
|
|
368
|
-
if (result.length + codePoint.length > limit) break;
|
|
369
|
-
result += codePoint;
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
result += segment;
|
|
374
|
-
}
|
|
375
|
-
return result;
|
|
376
|
-
}
|
|
377
|
-
function truncateOptionalTemplateText(value, limit) {
|
|
378
|
-
return value === void 0 ? void 0 : truncateTemplateText(value, limit);
|
|
400
|
+
const end = findGraphemeChunkEnd(text, 0, limit, limit, false);
|
|
401
|
+
return end > 0 ? text.slice(0, end) : truncateUtf16Safe(text, limit);
|
|
379
402
|
}
|
|
380
403
|
function resolveTemplateAltText(value, fallback) {
|
|
381
404
|
return truncateTemplateText(value ?? fallback, TEMPLATE_ALT_TEXT_LIMIT);
|
|
382
405
|
}
|
|
383
|
-
function normalizeCarouselColumn(column) {
|
|
384
|
-
return {
|
|
385
|
-
...column,
|
|
386
|
-
title: column.title || void 0,
|
|
387
|
-
actions: column.actions.map((action) => normalizeLineAction(action)).filter((action) => action.label !== void 0 && action.label !== "").slice(0, 3),
|
|
388
|
-
defaultAction: column.defaultAction === void 0 ? void 0 : normalizeLineAction(column.defaultAction)
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
406
|
function describeCarouselColumn(column) {
|
|
392
407
|
const body = column.title ? `${column.title}: ${column.text}` : column.text;
|
|
393
408
|
const labels = column.actions.map((action) => action.label).filter((label) => label !== void 0 && label !== "");
|
|
394
409
|
return labels.length > 0 ? `${body} (${labels.join(" / ")})` : body;
|
|
395
410
|
}
|
|
396
411
|
function normalizeCarousel(columns, altText) {
|
|
397
|
-
const normalized = columns.slice(0, 10)
|
|
412
|
+
const normalized = columns.slice(0, 10);
|
|
398
413
|
const first = normalized[0];
|
|
399
414
|
if (!(!first || normalized.some((column) => column.text === "" || column.actions.length === 0 || column.title === void 0 !== (first.title === void 0) || column.actions.length !== first.actions.length))) return {
|
|
400
415
|
kind: "template",
|
|
@@ -406,104 +421,72 @@ function normalizeCarousel(columns, altText) {
|
|
|
406
421
|
text
|
|
407
422
|
} : { kind: "empty" };
|
|
408
423
|
}
|
|
409
|
-
|
|
410
|
-
const template = {
|
|
411
|
-
type: "carousel",
|
|
412
|
-
columns,
|
|
413
|
-
imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
|
|
414
|
-
imageSize: options?.imageSize ?? "cover"
|
|
415
|
-
};
|
|
416
|
-
return {
|
|
417
|
-
type: "template",
|
|
418
|
-
altText: resolveTemplateAltText(options?.altText, "View carousel"),
|
|
419
|
-
template
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Create a confirm template (yes/no style dialog)
|
|
424
|
-
*/
|
|
425
|
-
function createConfirmTemplate(text, confirmAction, cancelAction, altText) {
|
|
426
|
-
const template = {
|
|
427
|
-
type: "confirm",
|
|
428
|
-
text: truncateTemplateText(text, 240),
|
|
429
|
-
actions: [normalizeLineAction(confirmAction), normalizeLineAction(cancelAction)]
|
|
430
|
-
};
|
|
431
|
-
return {
|
|
432
|
-
type: "template",
|
|
433
|
-
altText: resolveTemplateAltText(altText, text),
|
|
434
|
-
template
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Create a button template with title, text, and action buttons
|
|
439
|
-
*/
|
|
440
|
-
function createButtonTemplate(title, text, actions, options) {
|
|
441
|
-
const normalizedTitle = title || void 0;
|
|
442
|
-
const textLimit = resolveTemplateTextLimit({
|
|
443
|
-
title: normalizedTitle,
|
|
444
|
-
thumbnailImageUrl: options?.thumbnailImageUrl,
|
|
445
|
-
textOnlyLimit: 160
|
|
446
|
-
});
|
|
447
|
-
const template = {
|
|
448
|
-
type: "buttons",
|
|
449
|
-
...normalizedTitle ? { title: truncateTemplateText(normalizedTitle, 40) } : {},
|
|
450
|
-
text: truncateTemplateText(text, textLimit),
|
|
451
|
-
actions: actions.slice(0, 4).map((action) => normalizeLineAction(action)),
|
|
452
|
-
thumbnailImageUrl: options?.thumbnailImageUrl,
|
|
453
|
-
imageAspectRatio: options?.imageAspectRatio ?? "rectangle",
|
|
454
|
-
imageSize: options?.imageSize ?? "cover",
|
|
455
|
-
imageBackgroundColor: options?.imageBackgroundColor,
|
|
456
|
-
defaultAction: options?.defaultAction === void 0 ? void 0 : normalizeLineAction(options.defaultAction)
|
|
457
|
-
};
|
|
458
|
-
return {
|
|
459
|
-
type: "template",
|
|
460
|
-
altText: resolveTemplateAltText(options?.altText, normalizedTitle ? `${normalizedTitle}: ${text}` : text),
|
|
461
|
-
template
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
function createCarouselColumn(params) {
|
|
465
|
-
const normalizedTitle = params.title || void 0;
|
|
466
|
-
const textLimit = resolveTemplateTextLimit({
|
|
467
|
-
...params,
|
|
468
|
-
title: normalizedTitle,
|
|
469
|
-
textOnlyLimit: 120
|
|
470
|
-
});
|
|
471
|
-
return normalizeCarouselColumn({
|
|
472
|
-
title: truncateOptionalTemplateText(normalizedTitle, 40),
|
|
473
|
-
text: truncateTemplateText(params.text, textLimit),
|
|
474
|
-
actions: params.actions,
|
|
475
|
-
thumbnailImageUrl: params.thumbnailImageUrl,
|
|
476
|
-
imageBackgroundColor: params.imageBackgroundColor,
|
|
477
|
-
defaultAction: params.defaultAction
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Convert a TemplateMessagePayload from ReplyPayload to a LINE TemplateMessage
|
|
482
|
-
*/
|
|
424
|
+
/** Convert portable template payloads at the LINE provider boundary. */
|
|
483
425
|
function buildTemplateMessageFromPayload(payload) {
|
|
484
426
|
switch (payload.type) {
|
|
485
|
-
case "confirm": return
|
|
427
|
+
case "confirm": return {
|
|
428
|
+
type: "template",
|
|
429
|
+
altText: resolveTemplateAltText(payload.altText, payload.text),
|
|
430
|
+
template: {
|
|
431
|
+
type: "confirm",
|
|
432
|
+
text: truncateTemplateText(payload.text, 240),
|
|
433
|
+
actions: [buildInferredTemplateAction(payload.confirmLabel, payload.confirmData), buildInferredTemplateAction(payload.cancelLabel, payload.cancelData)]
|
|
434
|
+
}
|
|
435
|
+
};
|
|
486
436
|
case "buttons": {
|
|
487
|
-
const
|
|
488
|
-
|
|
437
|
+
const title = payload.title || void 0;
|
|
438
|
+
const textLimit = resolveTemplateTextLimit({
|
|
439
|
+
title,
|
|
489
440
|
thumbnailImageUrl: payload.thumbnailImageUrl,
|
|
490
|
-
|
|
441
|
+
textOnlyLimit: 160
|
|
491
442
|
});
|
|
443
|
+
return {
|
|
444
|
+
type: "template",
|
|
445
|
+
altText: resolveTemplateAltText(payload.altText, title ? `${title}: ${payload.text}` : payload.text),
|
|
446
|
+
template: {
|
|
447
|
+
type: "buttons",
|
|
448
|
+
...title ? { title: truncateTemplateText(title, 40) } : {},
|
|
449
|
+
text: truncateTemplateText(payload.text, textLimit),
|
|
450
|
+
actions: payload.actions.slice(0, 4).map(buildTemplatePayloadAction),
|
|
451
|
+
thumbnailImageUrl: payload.thumbnailImageUrl,
|
|
452
|
+
imageAspectRatio: "rectangle",
|
|
453
|
+
imageSize: "cover",
|
|
454
|
+
imageBackgroundColor: void 0,
|
|
455
|
+
defaultAction: void 0
|
|
456
|
+
}
|
|
457
|
+
};
|
|
492
458
|
}
|
|
493
459
|
case "carousel": {
|
|
494
|
-
const outcome = normalizeCarousel(payload.columns.map((
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
thumbnailImageUrl:
|
|
499
|
-
|
|
460
|
+
const outcome = normalizeCarousel(payload.columns.map((column) => {
|
|
461
|
+
const title = column.title || void 0;
|
|
462
|
+
const textLimit = resolveTemplateTextLimit({
|
|
463
|
+
title,
|
|
464
|
+
thumbnailImageUrl: column.thumbnailImageUrl,
|
|
465
|
+
textOnlyLimit: 120
|
|
500
466
|
});
|
|
467
|
+
return {
|
|
468
|
+
title: title === void 0 ? void 0 : truncateTemplateText(title, 40),
|
|
469
|
+
text: truncateTemplateText(column.text, textLimit),
|
|
470
|
+
actions: column.actions.map(buildTemplatePayloadAction).filter((action) => action.label !== void 0 && action.label !== "").slice(0, 3),
|
|
471
|
+
thumbnailImageUrl: column.thumbnailImageUrl,
|
|
472
|
+
imageBackgroundColor: void 0,
|
|
473
|
+
defaultAction: void 0
|
|
474
|
+
};
|
|
501
475
|
}), payload.altText);
|
|
502
476
|
if (outcome.kind === "empty") return null;
|
|
503
477
|
return outcome.kind === "text" ? {
|
|
504
478
|
type: "text",
|
|
505
479
|
text: outcome.text
|
|
506
|
-
} :
|
|
480
|
+
} : {
|
|
481
|
+
type: "template",
|
|
482
|
+
altText: resolveTemplateAltText(payload.altText, "View carousel"),
|
|
483
|
+
template: {
|
|
484
|
+
type: "carousel",
|
|
485
|
+
columns: outcome.columns,
|
|
486
|
+
imageAspectRatio: "rectangle",
|
|
487
|
+
imageSize: "cover"
|
|
488
|
+
}
|
|
489
|
+
};
|
|
507
490
|
}
|
|
508
491
|
default: return null;
|
|
509
492
|
}
|
package/dist/.setup/{webhook-spool-contract-1rY8OJw8.mjs → webhook-spool-contract-msjTl60Q.mjs}
RENAMED
|
@@ -52,8 +52,5 @@ function laneKeyFor(event, eventId) {
|
|
|
52
52
|
}
|
|
53
53
|
return eventId;
|
|
54
54
|
}
|
|
55
|
-
function errorText(error) {
|
|
56
|
-
return error instanceof Error ? error.message : String(error);
|
|
57
|
-
}
|
|
58
55
|
//#endregion
|
|
59
|
-
export {
|
|
56
|
+
export { laneKeyFor as a, eventIdFor as i, LINE_WEBHOOK_SPOOL_INVALID_PAYLOAD_MESSAGE as n, legacyEventIdFor as o, LineWebhookPayloadError as r, LINE_WEBHOOK_SPOOL_INVALID_EVENT_REASON as t };
|
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as linePlugin } from "./.setup/channel-
|
|
2
|
-
import { t as lineSetupPlugin } from "./.setup/api-
|
|
1
|
+
import { t as linePlugin } from "./.setup/channel-InKuXaRS.mjs";
|
|
2
|
+
import { t as lineSetupPlugin } from "./.setup/api-CMU7bG-C.mjs";
|
|
3
3
|
export { linePlugin, lineSetupPlugin };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as linePlugin } from "./.setup/channel-
|
|
1
|
+
import { t as linePlugin } from "./.setup/channel-InKuXaRS.mjs";
|
|
2
2
|
export { linePlugin };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as laneKeyFor, i as eventIdFor, o as legacyEventIdFor, r as LineWebhookPayloadError, t as LINE_WEBHOOK_SPOOL_INVALID_EVENT_REASON } from "./.setup/webhook-spool-contract-msjTl60Q.mjs";
|
|
2
2
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
3
4
|
//#region extensions/line/src/webhook-spool-migration.ts
|
|
4
5
|
/** Pre-drain (#109655) rows stored the event object under `event` instead of the
|
|
5
6
|
* canonical serialized `rawEvent`; anything else is not a migratable row. */
|
|
@@ -49,7 +50,7 @@ async function migrateLineLegacySpoolRows(queue) {
|
|
|
49
50
|
try {
|
|
50
51
|
if ((await queue.resubmit?.(row.id, { resubmittedAt: row.receivedAt }))?.kind === "resubmitted") result.recovered += 1;
|
|
51
52
|
} catch (error) {
|
|
52
|
-
result.failures.push(`row ${row.id}: ${
|
|
53
|
+
result.failures.push(`row ${row.id}: ${coerceErrorMessage(error)}`);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
const pending = await queue.listPending({
|
|
@@ -66,7 +67,7 @@ async function migrateLineLegacySpoolRows(queue) {
|
|
|
66
67
|
} catch (error) {
|
|
67
68
|
await queue.fail(record.id, {
|
|
68
69
|
reason: LINE_WEBHOOK_SPOOL_INVALID_EVENT_REASON,
|
|
69
|
-
message:
|
|
70
|
+
message: coerceErrorMessage(error)
|
|
70
71
|
});
|
|
71
72
|
result.deadLettered += 1;
|
|
72
73
|
continue;
|
|
@@ -84,7 +85,7 @@ async function migrateLineLegacySpoolRows(queue) {
|
|
|
84
85
|
if (admitted.kind === "completed" || admitted.kind === "failed") result.reconciled += 1;
|
|
85
86
|
else result.migrated += 1;
|
|
86
87
|
} catch (error) {
|
|
87
|
-
result.failures.push(`row ${record.id}: ${
|
|
88
|
+
result.failures.push(`row ${record.id}: ${coerceErrorMessage(error)}`);
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
return result;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
2
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
3
3
|
//#region extensions/line/index.ts
|
|
4
|
-
const loadLineCardCommand = createLazyRuntimeModule(() => import("./.setup/card-command-
|
|
4
|
+
const loadLineCardCommand = createLazyRuntimeModule(() => import("./.setup/card-command-BrU6u6pm.mjs"));
|
|
5
5
|
var line_default = defineBundledChannelEntry({
|
|
6
6
|
id: "line",
|
|
7
7
|
name: "LINE",
|
package/dist/setup-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as lineSetupAdapter, t as lineSetupWizard } from "./.setup/setup-surface-
|
|
1
|
+
import { n as lineSetupAdapter, t as lineSetupWizard } from "./.setup/setup-surface-1jkHGWg7.mjs";
|
|
2
2
|
export { lineSetupAdapter, lineSetupWizard };
|
package/dist/setup-entry.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
5
5
|
importMetaUrl: import.meta.url,
|
|
6
6
|
plugin: {
|
|
7
|
-
specifier: fileURLToPath(new URL(".setup/api-
|
|
7
|
+
specifier: fileURLToPath(new URL(".setup/api-BBSLQek1.mjs", import.meta.url).href),
|
|
8
8
|
exportName: "lineSetupPlugin"
|
|
9
9
|
}
|
|
10
10
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1-beta.1",
|
|
4
4
|
"description": "OpenClaw LINE channel plugin for LINE Bot API chats.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"zod": "4.6.5"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.
|
|
16
|
+
"openclaw": ">=2026.10.1-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -122,10 +122,10 @@
|
|
|
122
122
|
"minHostVersion": ">=2026.4.10"
|
|
123
123
|
},
|
|
124
124
|
"compat": {
|
|
125
|
-
"pluginApi": ">=2026.
|
|
125
|
+
"pluginApi": ">=2026.10.1-beta.1"
|
|
126
126
|
},
|
|
127
127
|
"build": {
|
|
128
|
-
"openclawVersion": "2026.
|
|
128
|
+
"openclawVersion": "2026.10.1-beta.1"
|
|
129
129
|
},
|
|
130
130
|
"release": {
|
|
131
131
|
"publishToClawHub": true,
|