@openclaw/line 2026.8.2 → 2026.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +1 -1
- package/dist/{card-command-DS6A5V6o.js → card-command-pwvE48Wb.js} +2 -2
- package/dist/{channel-CLCy4jAa.js → channel-CF2AeULJ.js} +27 -19
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.runtime-Ih7sq1GN.js → channel.runtime-BF4Wk2dM.js} +3 -3
- package/dist/index.js +1 -1
- package/dist/{markdown-to-line--jytmFan.js → markdown-to-line-B-sj-7II.js} +3 -16
- package/dist/{monitor-CSpwQCBI.js → monitor-D0fHFXCi.js} +51 -51
- package/dist/monitor.runtime-Mgecav_D.js +2 -0
- package/dist/{outbound.runtime-DLqcz_BQ.js → outbound.runtime-CGqyJ-Ug.js} +2 -2
- package/dist/probe.runtime-CbtMgU4s.js +2 -0
- package/dist/{rich-messages-CIP7xn5E.js → rich-messages-DY1AGKAJ.js} +158 -177
- package/dist/runtime-api.js +8 -9
- package/dist/{send-wVUV00WS.js → send-CQQxpxPp.js} +6 -13
- package/dist/{send-retry-CUvrarfE.js → send-retry-ChpfJ-LM.js} +195 -49
- package/package.json +4 -4
- package/dist/monitor.runtime-CV8EjkIT.js +0 -2
- package/dist/probe-4gc_YQ2L.js +0 -28
- package/dist/probe.runtime-DTxBWvKz.js +0 -2
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
import { i as resolveLineAccount } from "./accounts-BBEFMYbY.js";
|
|
2
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
1
3
|
import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
|
|
2
|
-
import { collectErrorGraphCandidates, extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
|
|
4
|
+
import { collectErrorGraphCandidates, extractErrorCode, formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
3
5
|
import { getFileExtension, mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
4
6
|
import { resolvePinnedHostnameWithPolicy } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
5
|
-
import {
|
|
6
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
7
|
+
import { runChannelProbe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
7
8
|
import { HTTPFetchError } from "@line/bot-sdk";
|
|
8
9
|
import { classifyTransientNetworkErrorCode, createChannelApiRetryRunner } from "openclaw/plugin-sdk/retry-runtime";
|
|
10
|
+
import { assertOkOrThrowHttpError, createProviderOperationDeadline, createProviderOperationTimeoutResolver, fetchWithTimeout, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
|
11
|
+
import { fetchWithRuntimeDispatcherOrMockedGlobal } from "openclaw/plugin-sdk/runtime-fetch";
|
|
12
|
+
//#region extensions/line/src/media-url.ts
|
|
13
|
+
function isHttpsUrl(value) {
|
|
14
|
+
if (typeof value !== "string") return false;
|
|
15
|
+
try {
|
|
16
|
+
return new URL(value).protocol === "https:";
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
9
22
|
//#region extensions/line/src/outbound-media.ts
|
|
10
23
|
const LINE_OUTBOUND_MEDIA_SSRF_POLICY = { allowPrivateNetwork: false };
|
|
11
24
|
async function validateLineMediaUrl(url) {
|
|
@@ -19,13 +32,6 @@ async function validateLineMediaUrl(url) {
|
|
|
19
32
|
if (url.length > 2e3) throw new Error(`LINE outbound media URL must be 2000 chars or less (got ${url.length})`);
|
|
20
33
|
await resolvePinnedHostnameWithPolicy(parsed.hostname, { policy: LINE_OUTBOUND_MEDIA_SSRF_POLICY });
|
|
21
34
|
}
|
|
22
|
-
function isHttpsUrl(url) {
|
|
23
|
-
try {
|
|
24
|
-
return new URL(url).protocol === "https:";
|
|
25
|
-
} catch {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
35
|
const LINE_MEDIA_KIND_BY_MIME = {
|
|
30
36
|
"image/jpeg": "image",
|
|
31
37
|
"image/png": "image",
|
|
@@ -130,6 +136,20 @@ async function buildLineMediaMessage(mediaUrl, opts, target) {
|
|
|
130
136
|
return buildLineMediaMessageObject(await resolveLineOutboundMedia(mediaUrl, opts), { allowTrackingId: isLineUserTarget(target) });
|
|
131
137
|
}
|
|
132
138
|
//#endregion
|
|
139
|
+
//#region extensions/line/src/flex-templates/message.ts
|
|
140
|
+
const LINE_FLEX_BUBBLE_MAX_BYTES = 3e4;
|
|
141
|
+
const LINE_FLEX_CAROUSEL_MAX_BYTES = 5e4;
|
|
142
|
+
function fitsLineFlexBubble(bubble) {
|
|
143
|
+
return Buffer.byteLength(JSON.stringify(bubble), "utf8") <= LINE_FLEX_BUBBLE_MAX_BYTES;
|
|
144
|
+
}
|
|
145
|
+
function toFlexMessage(altText, contents) {
|
|
146
|
+
return {
|
|
147
|
+
type: "flex",
|
|
148
|
+
altText,
|
|
149
|
+
contents
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
133
153
|
//#region extensions/line/src/actions.ts
|
|
134
154
|
const LINE_ACTION_LABEL_LIMIT = 20;
|
|
135
155
|
const LINE_ACTION_DATA_LIMIT = 300;
|
|
@@ -158,6 +178,17 @@ function truncateLineActionLabel(label, limit = LINE_ACTION_LABEL_LIMIT) {
|
|
|
158
178
|
function truncateLineActionData(data) {
|
|
159
179
|
return truncateUtf16Safe(data, LINE_ACTION_DATA_LIMIT);
|
|
160
180
|
}
|
|
181
|
+
const UNDELIVERABLE_IMAGE_WARNING = "Image unavailable: URL must use HTTPS.";
|
|
182
|
+
function flexWarning(text) {
|
|
183
|
+
return {
|
|
184
|
+
type: "text",
|
|
185
|
+
text,
|
|
186
|
+
wrap: true,
|
|
187
|
+
size: "sm",
|
|
188
|
+
color: "#B45309",
|
|
189
|
+
margin: "md"
|
|
190
|
+
};
|
|
191
|
+
}
|
|
161
192
|
const unavailableActionMarker = Symbol("lineUnavailableAction");
|
|
162
193
|
function unavailableAction(kind, reason) {
|
|
163
194
|
const action = {
|
|
@@ -185,13 +216,13 @@ function isLineAction(value) {
|
|
|
185
216
|
function isUnavailableAction(action) {
|
|
186
217
|
return action[unavailableActionMarker] === true;
|
|
187
218
|
}
|
|
188
|
-
function
|
|
189
|
-
if (Array.isArray(value))
|
|
190
|
-
const normalized = [];
|
|
191
|
-
for (const item of value) normalized.push(normalizeNestedActions(item, labelLimit, warnings));
|
|
192
|
-
return normalized;
|
|
193
|
-
}
|
|
219
|
+
function normalizeNestedContent(value, labelLimit, warnings) {
|
|
220
|
+
if (Array.isArray(value)) return value.map((item) => normalizeNestedContent(item, labelLimit, warnings)).filter((item) => item !== void 0);
|
|
194
221
|
if (!isRecord(value)) return value;
|
|
222
|
+
if (warnings && (value.type === "image" || value.type === "icon") && !isHttpsUrl(value.url)) {
|
|
223
|
+
warnings.push(UNDELIVERABLE_IMAGE_WARNING);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
195
226
|
const normalized = { ...value };
|
|
196
227
|
for (const [key, nested] of Object.entries(value)) if ((key === "action" || key === "defaultAction") && isLineAction(nested)) {
|
|
197
228
|
const action = normalizeLineAction(nested, labelLimit);
|
|
@@ -200,42 +231,61 @@ function normalizeNestedActions(value, labelLimit, warnings) {
|
|
|
200
231
|
warnings.push(isUnavailableAction(action) ? action.text ?? "Action unavailable." : "Action unavailable in this video.");
|
|
201
232
|
} else normalized[key] = action;
|
|
202
233
|
} else if (key === "actions" && Array.isArray(nested)) normalized[key] = nested.map((action) => isLineAction(action) ? normalizeLineAction(action, labelLimit) : action);
|
|
203
|
-
else
|
|
234
|
+
else {
|
|
235
|
+
const content = normalizeNestedContent(nested, labelLimit, warnings);
|
|
236
|
+
if (content === void 0) delete normalized[key];
|
|
237
|
+
else normalized[key] = content;
|
|
238
|
+
}
|
|
239
|
+
if (warnings && value.type === "video") {
|
|
240
|
+
const altContent = normalized.altContent ?? {
|
|
241
|
+
type: "box",
|
|
242
|
+
layout: "vertical",
|
|
243
|
+
contents: [flexWarning(UNDELIVERABLE_IMAGE_WARNING)]
|
|
244
|
+
};
|
|
245
|
+
if (!isHttpsUrl(value.url) || !isHttpsUrl(value.previewUrl)) {
|
|
246
|
+
warnings.push("Video unavailable: video and preview URLs must use HTTPS.");
|
|
247
|
+
return altContent;
|
|
248
|
+
}
|
|
249
|
+
normalized.altContent = altContent;
|
|
250
|
+
}
|
|
204
251
|
return normalized;
|
|
205
252
|
}
|
|
206
|
-
function
|
|
207
|
-
if (!isRecord(value) || value.type !== "bubble") return
|
|
253
|
+
function normalizeFlexBubble(value, maxBytes = LINE_FLEX_BUBBLE_MAX_BYTES) {
|
|
254
|
+
if (!isRecord(value) || value.type !== "bubble") return normalizeNestedContent(value, 40);
|
|
208
255
|
const warnings = [];
|
|
209
|
-
const normalized =
|
|
256
|
+
const normalized = normalizeNestedContent(value, 40, warnings);
|
|
210
257
|
if (!isRecord(normalized) || warnings.length === 0) return normalized;
|
|
211
|
-
const warning =
|
|
212
|
-
type: "text",
|
|
213
|
-
text: [...new Set(warnings)].join("\n"),
|
|
214
|
-
wrap: true,
|
|
215
|
-
size: "sm",
|
|
216
|
-
color: "#B45309",
|
|
217
|
-
margin: "md"
|
|
218
|
-
};
|
|
258
|
+
const warning = flexWarning([...new Set(warnings)].join("\n"));
|
|
219
259
|
const body = normalized.body;
|
|
220
|
-
|
|
221
|
-
...
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
260
|
+
const withWarning = {
|
|
261
|
+
...normalized,
|
|
262
|
+
body: isRecord(body) && Array.isArray(body.contents) ? {
|
|
263
|
+
...body,
|
|
264
|
+
contents: [...body.contents, warning]
|
|
265
|
+
} : {
|
|
266
|
+
type: "box",
|
|
267
|
+
layout: "vertical",
|
|
268
|
+
contents: [warning]
|
|
269
|
+
}
|
|
228
270
|
};
|
|
229
|
-
return normalized;
|
|
271
|
+
return Buffer.byteLength(JSON.stringify(withWarning), "utf8") <= Math.min(maxBytes, 3e4) ? withWarning : normalized;
|
|
230
272
|
}
|
|
231
|
-
function
|
|
273
|
+
function normalizeFlexContainer(value) {
|
|
232
274
|
if (!isRecord(value)) return value;
|
|
233
|
-
if (value.type === "bubble") return
|
|
234
|
-
if (value.type === "carousel" && Array.isArray(value.contents))
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
275
|
+
if (value.type === "bubble") return normalizeFlexBubble(value);
|
|
276
|
+
if (value.type === "carousel" && Array.isArray(value.contents)) {
|
|
277
|
+
let remainingBytes = LINE_FLEX_CAROUSEL_MAX_BYTES - Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
278
|
+
return {
|
|
279
|
+
...value,
|
|
280
|
+
contents: value.contents.map((bubble) => {
|
|
281
|
+
const originalBytes = Buffer.byteLength(JSON.stringify(bubble), "utf8");
|
|
282
|
+
const normalized = normalizeFlexBubble(bubble, originalBytes + remainingBytes);
|
|
283
|
+
remainingBytes += originalBytes - Buffer.byteLength(JSON.stringify(normalized), "utf8");
|
|
284
|
+
return normalized;
|
|
285
|
+
})
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return normalizeNestedContent(value, 40);
|
|
239
289
|
}
|
|
240
290
|
function unavailableImagemapAction(kind, reason, area) {
|
|
241
291
|
return {
|
|
@@ -289,17 +339,20 @@ function normalizeImagemapVideo(video) {
|
|
|
289
339
|
}
|
|
290
340
|
} };
|
|
291
341
|
}
|
|
292
|
-
function
|
|
342
|
+
function normalizeLineMessage(message) {
|
|
293
343
|
let normalized;
|
|
294
344
|
if (message.type === "flex") normalized = {
|
|
295
345
|
...message,
|
|
296
|
-
contents:
|
|
346
|
+
contents: normalizeFlexContainer(message.contents)
|
|
297
347
|
};
|
|
298
348
|
else if (message.type === "template") {
|
|
299
349
|
const labelLimit = message.template.type === "image_carousel" ? 12 : 20;
|
|
350
|
+
const template = normalizeNestedContent(message.template, labelLimit);
|
|
351
|
+
const columns = template.type === "carousel" ? template.columns : template.type === "buttons" ? [template] : [];
|
|
352
|
+
if (columns.some((column) => !isHttpsUrl(column.thumbnailImageUrl))) for (const column of columns) delete column.thumbnailImageUrl;
|
|
300
353
|
normalized = {
|
|
301
354
|
...message,
|
|
302
|
-
template
|
|
355
|
+
template
|
|
303
356
|
};
|
|
304
357
|
} else if (message.type === "imagemap") {
|
|
305
358
|
const actions = message.actions.map(normalizeImagemapAction);
|
|
@@ -315,7 +368,7 @@ function normalizeLineMessageActions(message) {
|
|
|
315
368
|
} else normalized = { ...message };
|
|
316
369
|
if (message.quickReply) normalized = {
|
|
317
370
|
...normalized,
|
|
318
|
-
quickReply:
|
|
371
|
+
quickReply: normalizeNestedContent(message.quickReply, 20)
|
|
319
372
|
};
|
|
320
373
|
return normalized;
|
|
321
374
|
}
|
|
@@ -1034,6 +1087,90 @@ function createLineSendReceipt(params) {
|
|
|
1034
1087
|
});
|
|
1035
1088
|
}
|
|
1036
1089
|
//#endregion
|
|
1090
|
+
//#region extensions/line/src/channel-access-token.ts
|
|
1091
|
+
function resolveLineChannelAccessToken(explicit, params) {
|
|
1092
|
+
if (explicit?.trim()) return explicit.trim();
|
|
1093
|
+
if (!params.channelAccessToken) throw new Error(`LINE channel access token missing for account "${params.accountId}" (set channels.line.channelAccessToken or LINE_CHANNEL_ACCESS_TOKEN).`);
|
|
1094
|
+
return params.channelAccessToken.trim();
|
|
1095
|
+
}
|
|
1096
|
+
//#endregion
|
|
1097
|
+
//#region extensions/line/src/probe.ts
|
|
1098
|
+
const LINE_QUOTA_TIMEOUT_MS = 2e3;
|
|
1099
|
+
const LINE_JSON_MAX_BYTES = 16384;
|
|
1100
|
+
function createLineApiReader(channelAccessToken, timeoutMs) {
|
|
1101
|
+
const remaining = createProviderOperationTimeoutResolver({
|
|
1102
|
+
deadline: createProviderOperationDeadline({
|
|
1103
|
+
timeoutMs,
|
|
1104
|
+
label: "LINE API"
|
|
1105
|
+
}),
|
|
1106
|
+
defaultTimeoutMs: timeoutMs
|
|
1107
|
+
});
|
|
1108
|
+
const headers = { Authorization: `Bearer ${channelAccessToken}` };
|
|
1109
|
+
return async (endpoint) => {
|
|
1110
|
+
const response = await fetchWithTimeout(`https://api.line.me/v2/bot/${endpoint}`, { headers }, remaining(), fetchWithRuntimeDispatcherOrMockedGlobal);
|
|
1111
|
+
await assertOkOrThrowHttpError(response, "LINE API", {
|
|
1112
|
+
bodyTimeoutMs: remaining,
|
|
1113
|
+
requestHeaders: headers
|
|
1114
|
+
});
|
|
1115
|
+
return await readProviderJsonResponse(response, "LINE API", {
|
|
1116
|
+
maxBytes: LINE_JSON_MAX_BYTES,
|
|
1117
|
+
timeoutMs: remaining,
|
|
1118
|
+
requestHeaders: headers
|
|
1119
|
+
});
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
async function readLineMessageQuota(channelAccessToken, budgetMs) {
|
|
1123
|
+
if (!Number.isFinite(budgetMs) || budgetMs <= 0) return;
|
|
1124
|
+
try {
|
|
1125
|
+
const read = createLineApiReader(channelAccessToken, budgetMs);
|
|
1126
|
+
const quota = await read("message/quota");
|
|
1127
|
+
if (quota.type === "none") return { kind: "unlimited" };
|
|
1128
|
+
if (quota.type !== "limited" || typeof quota.value !== "number" || !Number.isSafeInteger(quota.value) || quota.value < 0) return;
|
|
1129
|
+
const { totalUsage } = await read("message/quota/consumption");
|
|
1130
|
+
return typeof totalUsage === "number" && Number.isSafeInteger(totalUsage) && totalUsage >= 0 ? {
|
|
1131
|
+
kind: "limited",
|
|
1132
|
+
limit: quota.value,
|
|
1133
|
+
used: totalUsage
|
|
1134
|
+
} : void 0;
|
|
1135
|
+
} catch {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function readLineAccountMessageQuota(params) {
|
|
1140
|
+
try {
|
|
1141
|
+
return await readLineMessageQuota(resolveLineChannelAccessToken(void 0, resolveLineAccount({
|
|
1142
|
+
cfg: params.cfg,
|
|
1143
|
+
accountId: params.accountId ?? void 0
|
|
1144
|
+
})), LINE_QUOTA_TIMEOUT_MS);
|
|
1145
|
+
} catch {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
async function probeLineBot(channelAccessToken, timeoutMs = 5e3) {
|
|
1150
|
+
if (!channelAccessToken?.trim()) return {
|
|
1151
|
+
ok: false,
|
|
1152
|
+
error: "Channel access token not configured"
|
|
1153
|
+
};
|
|
1154
|
+
const token = channelAccessToken.trim();
|
|
1155
|
+
return await runChannelProbe(timeoutMs, async ({ elapsedMs }) => {
|
|
1156
|
+
const profile = await createLineApiReader(token, timeoutMs)("info");
|
|
1157
|
+
const quota = await readLineMessageQuota(token, Math.floor(Math.max(timeoutMs - elapsedMs(), 0) / 2));
|
|
1158
|
+
return {
|
|
1159
|
+
ok: true,
|
|
1160
|
+
bot: {
|
|
1161
|
+
displayName: profile.displayName,
|
|
1162
|
+
userId: profile.userId,
|
|
1163
|
+
basicId: profile.basicId,
|
|
1164
|
+
pictureUrl: profile.pictureUrl
|
|
1165
|
+
},
|
|
1166
|
+
...quota ? { quota } : {}
|
|
1167
|
+
};
|
|
1168
|
+
}, (error) => ({
|
|
1169
|
+
ok: false,
|
|
1170
|
+
error: formatErrorMessage(error)
|
|
1171
|
+
}));
|
|
1172
|
+
}
|
|
1173
|
+
//#endregion
|
|
1037
1174
|
//#region extensions/line/src/send-retry.ts
|
|
1038
1175
|
/** The LINE HTTP response carried by an error graph, when the request reached LINE. */
|
|
1039
1176
|
function findLineHttpError(error) {
|
|
@@ -1086,5 +1223,14 @@ const runLinePushWithRetries = (fn, label) => {
|
|
|
1086
1223
|
throw error;
|
|
1087
1224
|
});
|
|
1088
1225
|
};
|
|
1226
|
+
async function explainLineRefusal(params) {
|
|
1227
|
+
const retryable = resolveLineNonDispatchRetryable(params.error);
|
|
1228
|
+
const quota = retryable === true && findLineHttpError(params.error)?.status === 429 ? await readLineAccountMessageQuota(params) : void 0;
|
|
1229
|
+
const exhausted = quota?.kind === "limited" && quota.used >= quota.limit;
|
|
1230
|
+
return {
|
|
1231
|
+
retryable: exhausted ? false : retryable,
|
|
1232
|
+
reason: exhausted ? `LINE refused the push: ${quota.used}/${quota.limit} monthly messages used. Check the account allowance or plan before retrying.` : params.error instanceof Error ? params.error.message : "LINE rejected the message"
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1089
1235
|
//#endregion
|
|
1090
|
-
export { createVideoMessage as S,
|
|
1236
|
+
export { toFlexMessage as C, createVideoMessage as D, createImageMessage as E, fitsLineFlexBubble as S, createAudioMessage as T, normalizeLineAction as _, probeLineBot as a, truncateLineActionLabel as b, createAgendaCard as c, createActionCard as d, createImageCard as f, messageAction as g, datetimePickerAction as h, runLinePushWithRetries as i, 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, buildLineMediaMessage as w, uriAction as x, postbackAction as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/line",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.9.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.4.3"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.
|
|
16
|
+
"openclaw": ">=2026.9.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -112,10 +112,10 @@
|
|
|
112
112
|
"minHostVersion": ">=2026.4.10"
|
|
113
113
|
},
|
|
114
114
|
"compat": {
|
|
115
|
-
"pluginApi": ">=2026.
|
|
115
|
+
"pluginApi": ">=2026.9.1"
|
|
116
116
|
},
|
|
117
117
|
"build": {
|
|
118
|
-
"openclawVersion": "2026.
|
|
118
|
+
"openclawVersion": "2026.9.1"
|
|
119
119
|
},
|
|
120
120
|
"release": {
|
|
121
121
|
"publishToClawHub": true,
|
package/dist/probe-4gc_YQ2L.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
2
|
-
import { runChannelProbe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
3
|
-
import { messagingApi } from "@line/bot-sdk";
|
|
4
|
-
//#region extensions/line/src/probe.ts
|
|
5
|
-
async function probeLineBot(channelAccessToken, timeoutMs = 5e3) {
|
|
6
|
-
if (!channelAccessToken?.trim()) return {
|
|
7
|
-
ok: false,
|
|
8
|
-
error: "Channel access token not configured"
|
|
9
|
-
};
|
|
10
|
-
const client = new messagingApi.MessagingApiClient({ channelAccessToken: channelAccessToken.trim() });
|
|
11
|
-
return await runChannelProbe(timeoutMs, async () => {
|
|
12
|
-
const profile = await client.getBotInfo();
|
|
13
|
-
return {
|
|
14
|
-
ok: true,
|
|
15
|
-
bot: {
|
|
16
|
-
displayName: profile.displayName,
|
|
17
|
-
userId: profile.userId,
|
|
18
|
-
basicId: profile.basicId,
|
|
19
|
-
pictureUrl: profile.pictureUrl
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}, (error) => ({
|
|
23
|
-
ok: false,
|
|
24
|
-
error: formatErrorMessage(error)
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
|
-
//#endregion
|
|
28
|
-
export { probeLineBot as t };
|