@openclaw/line 2026.7.2-beta.5 → 2026.8.1-beta.2
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/{accounts-CSH5sk8U.js → accounts-BqEVsDoB.js} +24 -88
- package/dist/api.js +2 -3
- package/dist/{card-command-BEMIm8D8.js → card-command-DALdUSnc.js} +14 -22
- package/dist/{channel-CTaWrzfl.js → channel-CAdQJRw7.js} +75 -77
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-CuKoy9xw.js +4 -0
- package/dist/contract-api.js +1 -1
- package/dist/{flex-templates-DVy_wog4.js → flex-templates-BBBlNqpV.js} +108 -83
- package/dist/index.js +1 -1
- package/dist/markdown-to-line-D4I-6lm6.js +499 -0
- package/dist/{monitor-DzFU5UPV.js → monitor-QEh_ZqyR.js} +114 -94
- package/dist/monitor.runtime-72isajCp.js +2 -0
- package/dist/outbound.runtime-CWQMIE7N.js +3 -0
- package/dist/{reply-payload-transform-BRM2fRvK.js → reply-payload-transform-BOPGHVPo.js} +4 -3
- package/dist/runtime-api.js +9 -7
- package/dist/send-CFFr0-rZ.js +366 -0
- package/dist/setup-api.js +1 -1
- package/dist/{setup-surface-BHQOSD8R.js → setup-surface-W0Dbgu58.js} +51 -68
- package/openclaw.plugin.json +7 -0
- package/package.json +9 -5
- package/dist/channel.runtime-D4Paci2y.js +0 -4
- package/dist/markdown-to-line-Dn00k1bq.js +0 -799
- package/dist/monitor.runtime-B8xjVqev.js +0 -2
- package/dist/outbound.runtime-eHl7NNHu.js +0 -2
- package/dist/send-receipt-CY2ey8XH.js +0 -107
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/LICENSE +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/README.md +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/assert/strict.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/assert.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/async_hooks.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/buffer.buffer.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/buffer.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/child_process.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/cluster.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/compatibility/iterators.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/console.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/constants.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/crypto.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dgram.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/diagnostics_channel.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dns/promises.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/dns.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/domain.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/events.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/fs/promises.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/fs.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/globals.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/globals.typedarray.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/http.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/http2.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/https.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/index.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/inspector.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/inspector.generated.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/module.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/net.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/os.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/package.json +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/path.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/perf_hooks.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/process.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/punycode.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/querystring.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/readline/promises.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/readline.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/repl.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/sea.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/sqlite.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/consumers.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/promises.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream/web.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/stream.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/string_decoder.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/test.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/timers/promises.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/timers.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/tls.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/trace_events.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/buffer.buffer.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/compatibility/float16array.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/globals.typedarray.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.6/index.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.7/compatibility/float16array.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/ts5.7/index.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/tty.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/url.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/util.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/v8.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/vm.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/wasi.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/abortcontroller.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/crypto.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/domexception.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/events.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/fetch.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/navigator.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/storage.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/web-globals/streams.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/worker_threads.d.ts +0 -0
- /package/node_modules/{@types → @line/bot-sdk/node_modules/@types}/node/zlib.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/LICENSE +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/README.md +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/api.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/balanced-pool.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cache-interceptor.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cache.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/client-stats.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/client.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/connector.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/content-type.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/cookies.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/diagnostics-channel.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/dispatcher.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/env-http-proxy-agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/errors.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/eventsource.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/fetch.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/formdata.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/global-dispatcher.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/global-origin.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/h2c-client.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/handlers.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/header.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/index.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/interceptors.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-call-history.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-client.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-errors.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-interceptor.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/mock-pool.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/package.json +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/patch.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/pool-stats.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/pool.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/proxy-agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/readable.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/retry-agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/retry-handler.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/round-robin-pool.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/snapshot-agent.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/util.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/utility.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/webidl.d.ts +0 -0
- /package/node_modules/{undici-types → @line/bot-sdk/node_modules/undici-types}/websocket.d.ts +0 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-
|
|
1
|
+
import { i as resolveLineAccount, r as resolveDefaultLineAccountId } from "./accounts-BqEVsDoB.js";
|
|
2
2
|
import { a as resolveLineGroupConfigEntry, n as getLineRuntime, t as buildLineQuickReplyFallbackText } from "./quick-reply-fallback-D86FwAfr.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { v as buildLineMediaMessage, y as hasLineSpecificMediaOptions } from "./flex-templates-BBBlNqpV.js";
|
|
4
|
+
import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
|
|
5
|
+
import { _ as replyMessageLine, a as createQuickReplyItems, c as getUserDisplayName, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, y as showLoadingAnimation } from "./send-CFFr0-rZ.js";
|
|
5
6
|
import { createChannelPairingChallengeIssuer } from "openclaw/plugin-sdk/channel-pairing";
|
|
6
|
-
import { normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
7
|
-
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
|
|
7
|
+
import { normalizeNullableString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
|
+
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor } from "openclaw/plugin-sdk/channel-outbound";
|
|
9
|
+
import { buildChannelInboundEventContext, buildMentionRegexes, formatInboundEnvelope, formatInboundMediaUnavailableText, formatLocationText, hasFinalInboundReplyDispatch, isChannelPartialDeliveryError, matchesMentionPatterns, resolveInboundSessionEnvelopeContext, toInboundMediaFactsWithMetadata, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
|
|
8
10
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
9
11
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
10
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
11
12
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
13
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
14
|
import { firstDefined } from "openclaw/plugin-sdk/allow-from";
|
|
13
15
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
14
16
|
import { MediaFetchError } from "openclaw/plugin-sdk/media-runtime";
|
|
@@ -16,11 +18,13 @@ import { saveMediaStream } from "openclaw/plugin-sdk/media-store";
|
|
|
16
18
|
import { createNonExitingRuntime, danger, logVerbose, shouldLogVerbose, waitForAbortSignal, warn } from "openclaw/plugin-sdk/runtime-env";
|
|
17
19
|
import { fetchWithRuntimeDispatcherOrMockedGlobal } from "openclaw/plugin-sdk/runtime-fetch";
|
|
18
20
|
import { HTTPFetchError } from "@line/bot-sdk";
|
|
21
|
+
import { collectErrorGraphCandidates, extractErrorCode, formatErrorMessage, readErrorName, toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
19
22
|
import { recordChannelActivity } from "openclaw/plugin-sdk/channel-activity-runtime";
|
|
20
|
-
import {
|
|
23
|
+
import { channelReadyPatch, channelStoppedPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
21
24
|
import { chunkMarkdownText } from "openclaw/plugin-sdk/reply-runtime";
|
|
22
|
-
import { isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
25
|
+
import { canonicalizeWebhookRouteKey, isRequestBodyLimitError, normalizePluginHttpPath, normalizeWebhookPath, registerWebhookTargetWithPluginRoute, requestBodyErrorToText, resolveSingleWebhookTarget } from "openclaw/plugin-sdk/webhook-ingress";
|
|
23
26
|
import { beginWebhookRequestPipelineOrReject, createWebhookInFlightLimiter, isRequestBodyLimitError as isRequestBodyLimitError$1, readRequestBodyWithLimit, requestBodyErrorToText as requestBodyErrorToText$1, runDetachedWebhookWork } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
27
|
+
import { classifyTransientNetworkErrorCode } from "openclaw/plugin-sdk/retry-runtime";
|
|
24
28
|
import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
|
25
29
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
26
30
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
@@ -146,8 +150,15 @@ function toLineDeliveryError(error) {
|
|
|
146
150
|
return error instanceof Error ? error : new Error("LINE message send failed", { cause: error });
|
|
147
151
|
}
|
|
148
152
|
function getLineHttpError(error) {
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
return collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]).find((candidate) => candidate instanceof HTTPFetchError);
|
|
154
|
+
}
|
|
155
|
+
function canFallbackAfterLineReplyFailure(error) {
|
|
156
|
+
const httpError = getLineHttpError(error);
|
|
157
|
+
if (httpError) return httpError.status >= 400 && httpError.status < 500 && httpError.status !== 408;
|
|
158
|
+
const candidates = collectErrorGraphCandidates(error, (candidate) => [candidate.cause, candidate.error]);
|
|
159
|
+
if (candidates.some((candidate) => readErrorName(candidate) === "AbortError" || classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "ambiguous")) return false;
|
|
160
|
+
if (candidates.some((candidate) => classifyTransientNetworkErrorCode(extractErrorCode(candidate)) === "pre-connect")) return true;
|
|
161
|
+
return !candidates.some((candidate) => candidate instanceof TypeError && candidate.message === "fetch failed");
|
|
151
162
|
}
|
|
152
163
|
function markLineVisibleDeliveryError(error) {
|
|
153
164
|
const deliveryError = toLineDeliveryError(error);
|
|
@@ -175,6 +186,7 @@ async function deliverLineAutoReply(params) {
|
|
|
175
186
|
visibleReplySent = true;
|
|
176
187
|
return result;
|
|
177
188
|
} catch (error) {
|
|
189
|
+
if (isChannelPartialDeliveryError(error)) visibleReplySent = true;
|
|
178
190
|
if (visibleReplySent) throw markLineVisibleDeliveryError(error);
|
|
179
191
|
throw error;
|
|
180
192
|
}
|
|
@@ -191,7 +203,7 @@ async function deliverLineAutoReply(params) {
|
|
|
191
203
|
accountId
|
|
192
204
|
}));
|
|
193
205
|
} catch (error) {
|
|
194
|
-
if (typeof error === "object" && error !== null) failedPushSegments.set(error, {
|
|
206
|
+
if (!isChannelPartialDeliveryError(error) && typeof error === "object" && error !== null) failedPushSegments.set(error, {
|
|
195
207
|
allowFailedBatchTextRecovery,
|
|
196
208
|
failedBatch: batch,
|
|
197
209
|
unattemptedTail: [...messages.slice(i + batch.length), ...externalTail]
|
|
@@ -211,6 +223,7 @@ async function deliverLineAutoReply(params) {
|
|
|
211
223
|
accountId
|
|
212
224
|
});
|
|
213
225
|
} catch (err) {
|
|
226
|
+
if (isChannelPartialDeliveryError(err) || !canFallbackAfterLineReplyFailure(err)) throw err;
|
|
214
227
|
deps.onReplyError?.(err);
|
|
215
228
|
await pushLineMessages(replyBatch, getLineHttpError(err)?.status === 400, remaining.slice(replyBatch.length));
|
|
216
229
|
} finally {
|
|
@@ -222,19 +235,26 @@ async function deliverLineAutoReply(params) {
|
|
|
222
235
|
};
|
|
223
236
|
const richMessages = [];
|
|
224
237
|
const hasQuickReplies = Boolean(lineData.quickReplies?.length);
|
|
225
|
-
if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(
|
|
238
|
+
if (lineData.flexMessage) richMessages.push(deps.createFlexMessage(lineData.flexMessage.altText, lineData.flexMessage.contents));
|
|
226
239
|
if (lineData.templateMessage) {
|
|
227
240
|
const templateMsg = deps.buildTemplateMessageFromPayload(lineData.templateMessage);
|
|
228
241
|
if (templateMsg) richMessages.push(templateMsg);
|
|
229
242
|
}
|
|
230
|
-
if (lineData.location)
|
|
243
|
+
if (lineData.location) {
|
|
244
|
+
const locationMessage = deps.createLocationMessage(lineData.location);
|
|
245
|
+
if (locationMessage) richMessages.push(locationMessage);
|
|
246
|
+
}
|
|
231
247
|
const visibleText = payload.text ? sanitizeAssistantVisibleText(payload.text) : "";
|
|
232
248
|
const processed = visibleText ? deps.processLineMessage(visibleText) : {
|
|
233
249
|
text: "",
|
|
234
250
|
flexMessages: []
|
|
235
251
|
};
|
|
236
|
-
for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(
|
|
237
|
-
const
|
|
252
|
+
if (!processed.segments) for (const flexMsg of processed.flexMessages) richMessages.push(deps.createFlexMessage(flexMsg.altText, flexMsg.contents));
|
|
253
|
+
const orderedMessages = processed.segments?.flatMap((segment) => segment.type === "flex" ? [deps.createFlexMessage(segment.message.altText, segment.message.contents)] : deps.chunkMarkdownText(segment.text, textLimit).map((text) => ({
|
|
254
|
+
type: "text",
|
|
255
|
+
text
|
|
256
|
+
})));
|
|
257
|
+
const chunks = orderedMessages ? orderedMessages.flatMap((message) => message.type === "text" ? [message.text] : []) : processed.text ? deps.chunkMarkdownText(processed.text, textLimit) : [];
|
|
238
258
|
const mediaUrls = resolveSendableOutboundReplyParts(payload).mediaUrls;
|
|
239
259
|
const mediaOpts = {
|
|
240
260
|
mediaKind: hasLineSpecificMediaOptions(lineData) ? lineData.mediaKind : "image",
|
|
@@ -263,14 +283,14 @@ async function deliverLineAutoReply(params) {
|
|
|
263
283
|
text: buildLineQuickReplyFallbackText(lineData.quickReplies)
|
|
264
284
|
});
|
|
265
285
|
if (hasQuickReplies) {
|
|
266
|
-
const targetMessages = textMessages.length > 0 ? textMessages : richMediaMessages;
|
|
286
|
+
const targetMessages = orderedMessages?.length ? orderedMessages : textMessages.length > 0 ? textMessages : richMediaMessages;
|
|
267
287
|
const lastIndex = targetMessages.length - 1;
|
|
268
288
|
targetMessages[lastIndex] = {
|
|
269
289
|
...expectDefined(targetMessages[lastIndex], "last LINE auto-reply message"),
|
|
270
290
|
quickReply: deps.createQuickReplyItems(lineData.quickReplies)
|
|
271
291
|
};
|
|
272
292
|
}
|
|
273
|
-
const messages = hasQuickReplies ? [...richMediaMessages, ...textMessages] : [...textMessages, ...richMediaMessages];
|
|
293
|
+
const messages = hasQuickReplies ? [...richMediaMessages, ...orderedMessages ?? textMessages] : [...orderedMessages ?? textMessages, ...richMediaMessages];
|
|
274
294
|
try {
|
|
275
295
|
await sendLineMessages(messages, true);
|
|
276
296
|
} catch (err) {
|
|
@@ -451,7 +471,7 @@ async function finalizeLineInboundContext(params) {
|
|
|
451
471
|
sessionKey: params.route.sessionKey
|
|
452
472
|
});
|
|
453
473
|
const agentBody = params.agentBody ?? params.rawBody;
|
|
454
|
-
const media =
|
|
474
|
+
const media = await toInboundMediaFactsWithMetadata(params.media);
|
|
455
475
|
const body = formatInboundEnvelope({
|
|
456
476
|
channel: "LINE",
|
|
457
477
|
from: conversationLabel,
|
|
@@ -462,7 +482,8 @@ async function finalizeLineInboundContext(params) {
|
|
|
462
482
|
previousTimestamp,
|
|
463
483
|
envelope: envelopeOptions
|
|
464
484
|
});
|
|
465
|
-
const ctxPayload = buildChannelInboundEventContext({
|
|
485
|
+
const ctxPayload = (params.buildContext ?? buildChannelInboundEventContext)({
|
|
486
|
+
channelIngress: params.channelIngress,
|
|
466
487
|
channel: "line",
|
|
467
488
|
accountId: params.route.accountId,
|
|
468
489
|
messageId: params.messageSid,
|
|
@@ -592,6 +613,13 @@ async function buildLineMessageContext(params) {
|
|
|
592
613
|
timestamp,
|
|
593
614
|
messageSid: messageId,
|
|
594
615
|
commandAuthorized,
|
|
616
|
+
channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
|
|
617
|
+
agentId: route.agentId,
|
|
618
|
+
sessionKey: route.sessionKey,
|
|
619
|
+
messageId,
|
|
620
|
+
inboundEventKind: "user_request"
|
|
621
|
+
}) : params.channelIngress,
|
|
622
|
+
buildContext: params.buildContext,
|
|
595
623
|
media: mediaFacts,
|
|
596
624
|
locationContext,
|
|
597
625
|
verboseLog: {
|
|
@@ -648,6 +676,13 @@ async function buildLinePostbackContext(params) {
|
|
|
648
676
|
timestamp,
|
|
649
677
|
messageSid,
|
|
650
678
|
commandAuthorized,
|
|
679
|
+
channelIngress: params.resolveChannelIngress ? await params.resolveChannelIngress({
|
|
680
|
+
agentId: route.agentId,
|
|
681
|
+
sessionKey: route.sessionKey,
|
|
682
|
+
messageId: messageSid,
|
|
683
|
+
inboundEventKind: "user_request"
|
|
684
|
+
}) : params.channelIngress,
|
|
685
|
+
buildContext: params.buildContext,
|
|
651
686
|
media: [],
|
|
652
687
|
verboseLog: { kind: "postback" }
|
|
653
688
|
});
|
|
@@ -756,6 +791,7 @@ async function sendLinePairingReply(params) {
|
|
|
756
791
|
return;
|
|
757
792
|
} catch (err) {
|
|
758
793
|
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
|
794
|
+
if (isChannelPartialDeliveryError(err)) return;
|
|
759
795
|
}
|
|
760
796
|
try {
|
|
761
797
|
await pushMessageLine(`line:${senderId}`, text, {
|
|
@@ -813,7 +849,7 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
813
849
|
hasAnyMention: hasAnyLineMention(event.message)
|
|
814
850
|
};
|
|
815
851
|
})();
|
|
816
|
-
const
|
|
852
|
+
const resolveAccess = async (contextBinding) => await resolveStableChannelMessageIngress({
|
|
817
853
|
channelId: "line",
|
|
818
854
|
accountId: account.accountId,
|
|
819
855
|
identity: {
|
|
@@ -829,6 +865,7 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
829
865
|
kind: isGroup ? "group" : "direct",
|
|
830
866
|
id: (groupId ?? roomId ?? senderId) || "unknown"
|
|
831
867
|
},
|
|
868
|
+
...contextBinding ? { contextBinding } : {},
|
|
832
869
|
...isGroup && groupConfig?.enabled === false ? { route: {
|
|
833
870
|
id: "line:group-config",
|
|
834
871
|
enabled: false
|
|
@@ -856,13 +893,17 @@ async function shouldProcessLineEvent(event, context) {
|
|
|
856
893
|
groupOwnerAllowFrom: "none"
|
|
857
894
|
}
|
|
858
895
|
});
|
|
896
|
+
const access = await resolveAccess();
|
|
859
897
|
warnMissingProviderGroupPolicyFallbackOnce({
|
|
860
898
|
providerMissingFallbackApplied,
|
|
861
899
|
providerKey: "line",
|
|
862
900
|
accountId: account.accountId,
|
|
863
901
|
log: (message) => logVerbose(message)
|
|
864
902
|
});
|
|
865
|
-
if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return
|
|
903
|
+
if (access.senderAccess.decision === "allow" && (access.ingress.admission === "dispatch" || access.ingress.admission === "observe" || access.ingress.admission === "skip")) return {
|
|
904
|
+
access,
|
|
905
|
+
resolveBoundAccess: resolveAccess
|
|
906
|
+
};
|
|
866
907
|
if (access.senderAccess.decision === "allow") {
|
|
867
908
|
logVerbose(`Blocked line event (${access.ingress.reasonCode})`);
|
|
868
909
|
return null;
|
|
@@ -933,7 +974,7 @@ async function handleMessageEvent(event, context) {
|
|
|
933
974
|
const decision = await shouldProcessLineEvent(event, context);
|
|
934
975
|
if (!decision) return;
|
|
935
976
|
const { isGroup, groupId, roomId } = getLineSourceInfo(event.source);
|
|
936
|
-
if (isGroup && decision.activationAccess.shouldSkip) {
|
|
977
|
+
if (isGroup && decision.access.activationAccess.shouldSkip) {
|
|
937
978
|
const rawText = message.type === "text" ? message.text : "";
|
|
938
979
|
const sourceInfo = getLineSourceInfo(event.source);
|
|
939
980
|
logVerbose(`line: skipping group message (requireMention, not mentioned)`);
|
|
@@ -975,8 +1016,10 @@ async function handleMessageEvent(event, context) {
|
|
|
975
1016
|
mediaUnavailable,
|
|
976
1017
|
cfg,
|
|
977
1018
|
account,
|
|
978
|
-
commandAuthorized: decision.commandAccess.authorized,
|
|
979
|
-
|
|
1019
|
+
commandAuthorized: decision.access.commandAccess.authorized,
|
|
1020
|
+
resolveChannelIngress: decision.resolveBoundAccess,
|
|
1021
|
+
inboundHistory: historyReservation.inboundHistory,
|
|
1022
|
+
buildContext: context.buildContext
|
|
980
1023
|
});
|
|
981
1024
|
if (!messageContext) {
|
|
982
1025
|
logVerbose("line: skipping empty message");
|
|
@@ -1013,7 +1056,9 @@ async function handlePostbackEvent(event, context) {
|
|
|
1013
1056
|
event,
|
|
1014
1057
|
cfg: context.cfg,
|
|
1015
1058
|
account: context.account,
|
|
1016
|
-
commandAuthorized: decision.commandAccess.authorized
|
|
1059
|
+
commandAuthorized: decision.access.commandAccess.authorized,
|
|
1060
|
+
resolveChannelIngress: decision.resolveBoundAccess,
|
|
1061
|
+
buildContext: context.buildContext
|
|
1017
1062
|
});
|
|
1018
1063
|
if (!postbackContext) return;
|
|
1019
1064
|
await context.processMessage(postbackContext, context.turnAdoptionLifecycle ? { turnAdoptionLifecycle: context.turnAdoptionLifecycle } : {});
|
|
@@ -1026,7 +1071,7 @@ async function handleLineWebhookEvents(events, context) {
|
|
|
1026
1071
|
context.runtime.error?.(danger(`line: event handler failed: ${String(err)}`));
|
|
1027
1072
|
firstError ??= err;
|
|
1028
1073
|
}
|
|
1029
|
-
if (firstError) throw
|
|
1074
|
+
if (firstError) throw toErrorObject(firstError, "Non-Error thrown");
|
|
1030
1075
|
}
|
|
1031
1076
|
async function handleLineWebhookEvent(event, context) {
|
|
1032
1077
|
switch (event.type) {
|
|
@@ -1051,13 +1096,6 @@ async function handleLineWebhookEvent(event, context) {
|
|
|
1051
1096
|
default: logVerbose(`line: unhandled event type: ${event.type}`);
|
|
1052
1097
|
}
|
|
1053
1098
|
}
|
|
1054
|
-
function toLintErrorObject(value, fallbackMessage) {
|
|
1055
|
-
if (value instanceof Error) return value;
|
|
1056
|
-
if (typeof value === "string") return new Error(value);
|
|
1057
|
-
const error = new Error(fallbackMessage, { cause: value });
|
|
1058
|
-
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
1059
|
-
return error;
|
|
1060
|
-
}
|
|
1061
1099
|
//#endregion
|
|
1062
1100
|
//#region extensions/line/src/webhook-spool.ts
|
|
1063
1101
|
const LINE_WEBHOOK_SPOOL_VERSION = 1;
|
|
@@ -1065,14 +1103,7 @@ const LINE_WEBHOOK_DRAIN_INTERVAL_MS = 500;
|
|
|
1065
1103
|
const LINE_WEBHOOK_MAX_CONCURRENT_DELIVERIES = 8;
|
|
1066
1104
|
const LINE_WEBHOOK_DRAIN_SCAN_LIMIT = 100;
|
|
1067
1105
|
const LINE_WEBHOOK_ACTIVE_DELIVERY_STOP_GRACE_MS = 5e3;
|
|
1068
|
-
const
|
|
1069
|
-
const LINE_WEBHOOK_TOMBSTONE_MAX_ENTRIES = 4096;
|
|
1070
|
-
var LineWebhookPayloadError = class extends Error {
|
|
1071
|
-
constructor(message, options) {
|
|
1072
|
-
super(message, options);
|
|
1073
|
-
this.name = "LineWebhookPayloadError";
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1106
|
+
const LineWebhookPayloadError = createChannelIngressError("LineWebhookPayloadError");
|
|
1076
1107
|
var LineWebhookTerminalDeliveryError = class extends Error {
|
|
1077
1108
|
constructor(message, options) {
|
|
1078
1109
|
super(message, options);
|
|
@@ -1080,18 +1111,15 @@ var LineWebhookTerminalDeliveryError = class extends Error {
|
|
|
1080
1111
|
this.name = "LineWebhookTerminalDeliveryError";
|
|
1081
1112
|
}
|
|
1082
1113
|
};
|
|
1083
|
-
function nonEmptyString(value) {
|
|
1084
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1085
|
-
}
|
|
1086
1114
|
/** Message ids preserve the shipped replay-guard keyspace; other events use LINE's delivery id. */
|
|
1087
1115
|
function eventIdFor(event) {
|
|
1088
1116
|
if (!event || typeof event !== "object") throw new LineWebhookPayloadError("LINE webhook event must be an object.");
|
|
1089
1117
|
const candidate = event;
|
|
1090
1118
|
if (candidate.type === "message") {
|
|
1091
|
-
const messageId =
|
|
1119
|
+
const messageId = normalizeNullableString(candidate.message?.id);
|
|
1092
1120
|
if (messageId) return `message:${messageId}`;
|
|
1093
1121
|
}
|
|
1094
|
-
const webhookEventId =
|
|
1122
|
+
const webhookEventId = normalizeNullableString(candidate.webhookEventId);
|
|
1095
1123
|
if (webhookEventId) return `event:${webhookEventId}`;
|
|
1096
1124
|
throw new LineWebhookPayloadError("LINE webhook event is missing a stable delivery id.");
|
|
1097
1125
|
}
|
|
@@ -1099,15 +1127,15 @@ function laneKeyFor(event, eventId) {
|
|
|
1099
1127
|
if (!event || typeof event !== "object") return eventId;
|
|
1100
1128
|
const source = event.source;
|
|
1101
1129
|
if (source?.type === "group") {
|
|
1102
|
-
const groupId =
|
|
1130
|
+
const groupId = normalizeNullableString(source.groupId);
|
|
1103
1131
|
if (groupId) return `group:${groupId}`;
|
|
1104
1132
|
}
|
|
1105
1133
|
if (source?.type === "room") {
|
|
1106
|
-
const roomId =
|
|
1134
|
+
const roomId = normalizeNullableString(source.roomId);
|
|
1107
1135
|
if (roomId) return `room:${roomId}`;
|
|
1108
1136
|
}
|
|
1109
1137
|
if (source?.type === "user") {
|
|
1110
|
-
const userId =
|
|
1138
|
+
const userId = normalizeNullableString(source.userId);
|
|
1111
1139
|
if (userId) return `user:${userId}`;
|
|
1112
1140
|
}
|
|
1113
1141
|
return eventId;
|
|
@@ -1121,9 +1149,6 @@ function parseStoredEvent(rawEvent) {
|
|
|
1121
1149
|
}
|
|
1122
1150
|
return event;
|
|
1123
1151
|
}
|
|
1124
|
-
function errorText(error) {
|
|
1125
|
-
return error instanceof Error ? error.message : String(error);
|
|
1126
|
-
}
|
|
1127
1152
|
function isLineAuthenticationFailure(error) {
|
|
1128
1153
|
if (!error || typeof error !== "object") return false;
|
|
1129
1154
|
const status = error.status;
|
|
@@ -1143,7 +1168,6 @@ async function waitForActiveDeliveriesBeforeDispose(activeDeliveries) {
|
|
|
1143
1168
|
function createLineWebhookSpool(options) {
|
|
1144
1169
|
const queue = options.queue ?? getLineRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
|
|
1145
1170
|
const activeDeliveries = /* @__PURE__ */ new Set();
|
|
1146
|
-
const deferredClaims = /* @__PURE__ */ new Map();
|
|
1147
1171
|
let acceptsDeferredClaims = true;
|
|
1148
1172
|
const monitor = createChannelIngressMonitor({
|
|
1149
1173
|
queue,
|
|
@@ -1181,49 +1205,28 @@ function createLineWebhookSpool(options) {
|
|
|
1181
1205
|
},
|
|
1182
1206
|
createClaimError: (kind) => new LineWebhookPayloadError(kind === "invalid-version" ? "LINE webhook spool payload is invalid." : "LINE webhook event identity changed after durable admission.")
|
|
1183
1207
|
},
|
|
1184
|
-
deliver: async ({ event, destination }, lifecycle
|
|
1208
|
+
deliver: async ({ event, destination }, lifecycle) => {
|
|
1185
1209
|
const boundLifecycle = bindIngressLifecycleToReplyOptions(lifecycle).turnAdoptionLifecycle;
|
|
1186
1210
|
let handedOff = false;
|
|
1187
|
-
let resolveDeferredClaim;
|
|
1188
|
-
const deferredClaim = new Promise((resolve) => {
|
|
1189
|
-
resolveDeferredClaim = resolve;
|
|
1190
|
-
});
|
|
1191
|
-
let deferredClaimSettled = false;
|
|
1192
|
-
const settleDeferredClaim = () => {
|
|
1193
|
-
if (deferredClaimSettled) return;
|
|
1194
|
-
deferredClaimSettled = true;
|
|
1195
|
-
if (deferredClaims.get(claim.id) === deferredClaim) deferredClaims.delete(claim.id);
|
|
1196
|
-
resolveDeferredClaim();
|
|
1197
|
-
};
|
|
1198
1211
|
const delivery = options.deliver(event, destination, { turnAdoptionLifecycle: {
|
|
1199
1212
|
...boundLifecycle,
|
|
1200
1213
|
onAdopted: async () => {
|
|
1201
1214
|
handedOff = true;
|
|
1202
|
-
|
|
1203
|
-
await boundLifecycle.onAdopted();
|
|
1204
|
-
} finally {
|
|
1205
|
-
settleDeferredClaim();
|
|
1206
|
-
}
|
|
1215
|
+
await boundLifecycle.onAdopted();
|
|
1207
1216
|
},
|
|
1208
1217
|
onDeferred: () => {
|
|
1209
1218
|
handedOff = true;
|
|
1210
1219
|
if (!acceptsDeferredClaims) {
|
|
1211
|
-
settleDeferredClaim();
|
|
1212
1220
|
Promise.resolve().then(() => boundLifecycle.onAbandoned()).catch((error) => {
|
|
1213
|
-
options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${
|
|
1221
|
+
options.runtime.error?.(danger(`line: failed to abandon a late webhook delivery: ${formatErrorMessage(error)}`));
|
|
1214
1222
|
});
|
|
1215
1223
|
return;
|
|
1216
1224
|
}
|
|
1217
|
-
if (!deferredClaimSettled) deferredClaims.set(claim.id, deferredClaim);
|
|
1218
1225
|
boundLifecycle.onDeferred();
|
|
1219
1226
|
},
|
|
1220
1227
|
onAbandoned: async () => {
|
|
1221
1228
|
handedOff = true;
|
|
1222
|
-
|
|
1223
|
-
await boundLifecycle.onAbandoned();
|
|
1224
|
-
} finally {
|
|
1225
|
-
settleDeferredClaim();
|
|
1226
|
-
}
|
|
1229
|
+
await boundLifecycle.onAbandoned();
|
|
1227
1230
|
}
|
|
1228
1231
|
} });
|
|
1229
1232
|
activeDeliveries.add(delivery);
|
|
@@ -1240,14 +1243,13 @@ function createLineWebhookSpool(options) {
|
|
|
1240
1243
|
pollIntervalMs: LINE_WEBHOOK_DRAIN_INTERVAL_MS,
|
|
1241
1244
|
retention: {
|
|
1242
1245
|
pruneIntervalMs: 0,
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
failedTtlMs: LINE_WEBHOOK_TOMBSTONE_TTL_MS,
|
|
1246
|
-
failedMaxEntries: LINE_WEBHOOK_TOMBSTONE_MAX_ENTRIES
|
|
1246
|
+
completedMaxEntries: 4096,
|
|
1247
|
+
failedMaxEntries: 4096
|
|
1247
1248
|
},
|
|
1248
1249
|
appendRetryDelaysMs: [0],
|
|
1249
1250
|
waitForDeliveryIdleBeforeRepump: false,
|
|
1250
1251
|
waitForDeliveryIdleOnStop: false,
|
|
1252
|
+
deferredClaims: "manual",
|
|
1251
1253
|
runPumpTask: runDetachedWebhookWork,
|
|
1252
1254
|
admissionMode: "durable-after-stop",
|
|
1253
1255
|
drain: {
|
|
@@ -1270,14 +1272,14 @@ function createLineWebhookSpool(options) {
|
|
|
1270
1272
|
};
|
|
1271
1273
|
if (isLineAuthenticationFailure(error)) return {
|
|
1272
1274
|
reason: "authentication-failed",
|
|
1273
|
-
message:
|
|
1275
|
+
message: formatErrorMessage(error)
|
|
1274
1276
|
};
|
|
1275
1277
|
return null;
|
|
1276
1278
|
},
|
|
1277
1279
|
onLog: (message) => options.runtime.error?.(danger(`line: ${message}`))
|
|
1278
1280
|
},
|
|
1279
1281
|
createStoppedError: () => /* @__PURE__ */ new Error("LINE webhook spool is stopped."),
|
|
1280
|
-
onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${
|
|
1282
|
+
onError: (error) => options.runtime.error?.(danger(`line: webhook spool drain failed: ${formatErrorMessage(error)}`))
|
|
1281
1283
|
});
|
|
1282
1284
|
let stopTask;
|
|
1283
1285
|
return {
|
|
@@ -1298,7 +1300,7 @@ function createLineWebhookSpool(options) {
|
|
|
1298
1300
|
try {
|
|
1299
1301
|
const deliveriesSettled = await waitForActiveDeliveriesBeforeDispose(activeDeliveries);
|
|
1300
1302
|
if (!deliveriesSettled) options.runtime.log(warn(`line: timed out after ${LINE_WEBHOOK_ACTIVE_DELIVERY_STOP_GRACE_MS}ms waiting for active webhook deliveries; releasing drain ownership`));
|
|
1301
|
-
|
|
1303
|
+
await monitor.waitForDeferredClaims();
|
|
1302
1304
|
acceptsDeferredClaims = false;
|
|
1303
1305
|
if (deliveriesSettled) await monitor.waitForIdle();
|
|
1304
1306
|
} finally {
|
|
@@ -1311,6 +1313,7 @@ function createLineWebhookSpool(options) {
|
|
|
1311
1313
|
}
|
|
1312
1314
|
//#endregion
|
|
1313
1315
|
//#region extensions/line/src/bot.ts
|
|
1316
|
+
const DEFAULT_MEDIA_MAX_MB = 10;
|
|
1314
1317
|
function createLineBot(opts) {
|
|
1315
1318
|
const runtime = opts.runtime ?? createNonExitingRuntime();
|
|
1316
1319
|
const cfg = opts.config ?? getRuntimeConfig();
|
|
@@ -1318,7 +1321,7 @@ function createLineBot(opts) {
|
|
|
1318
1321
|
cfg,
|
|
1319
1322
|
accountId: opts.accountId
|
|
1320
1323
|
});
|
|
1321
|
-
const mediaMaxBytes = (opts.mediaMaxMb
|
|
1324
|
+
const mediaMaxBytes = ([opts.mediaMaxMb, account.config.mediaMaxMb].find((value) => typeof value === "number" && value > 0) ?? DEFAULT_MEDIA_MAX_MB) * 1024 * 1024;
|
|
1322
1325
|
const processMessage = opts.onMessage ?? (async () => {
|
|
1323
1326
|
logVerbose("line: no message handler configured");
|
|
1324
1327
|
});
|
|
@@ -1330,6 +1333,7 @@ function createLineBot(opts) {
|
|
|
1330
1333
|
cfg,
|
|
1331
1334
|
account,
|
|
1332
1335
|
runtime,
|
|
1336
|
+
buildContext: opts.buildContext,
|
|
1333
1337
|
mediaMaxBytes,
|
|
1334
1338
|
processMessage,
|
|
1335
1339
|
...control.turnAdoptionLifecycle ? { turnAdoptionLifecycle: control.turnAdoptionLifecycle } : {},
|
|
@@ -1451,7 +1455,7 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1451
1455
|
res.end(JSON.stringify({ error: requestBodyErrorToText$1("REQUEST_BODY_TIMEOUT") }));
|
|
1452
1456
|
return;
|
|
1453
1457
|
}
|
|
1454
|
-
params.runtime.error?.(danger(`line webhook error: ${
|
|
1458
|
+
params.runtime.error?.(danger(`line webhook error: ${formatErrorMessage(err)}`));
|
|
1455
1459
|
if (!res.headersSent) {
|
|
1456
1460
|
res.statusCode = 500;
|
|
1457
1461
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -1465,6 +1469,14 @@ function createLineNodeWebhookHandler(params) {
|
|
|
1465
1469
|
const lineWebhookInFlightLimiter = createWebhookInFlightLimiter();
|
|
1466
1470
|
const LINE_WEBHOOK_PREAUTH_MAX_BODY_BYTES = 64 * 1024;
|
|
1467
1471
|
const LINE_WEBHOOK_PREAUTH_BODY_TIMEOUT_MS = 5e3;
|
|
1472
|
+
async function registerLineWebhookTarget(params, bot) {
|
|
1473
|
+
try {
|
|
1474
|
+
return registerWebhookTargetWithPluginRoute(params);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
await Promise.allSettled([bot.stop()]);
|
|
1477
|
+
throw error;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1468
1480
|
const lineWebhookTargets = /* @__PURE__ */ new Map();
|
|
1469
1481
|
function startLineLoadingKeepalive(params) {
|
|
1470
1482
|
const intervalMs = params.intervalMs ?? 18e3;
|
|
@@ -1487,7 +1499,7 @@ function startLineLoadingKeepalive(params) {
|
|
|
1487
1499
|
};
|
|
1488
1500
|
}
|
|
1489
1501
|
async function monitorLineProvider(opts) {
|
|
1490
|
-
const { channelAccessToken, channelSecret, accountId, config, runtime, abortSignal, webhookPath } = opts;
|
|
1502
|
+
const { channelAccessToken, channelSecret, accountId, config, runtime, buildContext, abortSignal, webhookPath, statusSink } = opts;
|
|
1491
1503
|
const resolvedAccountId = accountId ?? resolveDefaultLineAccountId(config);
|
|
1492
1504
|
const token = channelAccessToken.trim();
|
|
1493
1505
|
const secret = channelSecret.trim();
|
|
@@ -1498,6 +1510,7 @@ async function monitorLineProvider(opts) {
|
|
|
1498
1510
|
channelSecret: secret,
|
|
1499
1511
|
accountId,
|
|
1500
1512
|
runtime,
|
|
1513
|
+
buildContext,
|
|
1501
1514
|
config,
|
|
1502
1515
|
onMessage: async (ctx, deliveryControl) => {
|
|
1503
1516
|
if (!ctx) return;
|
|
@@ -1607,12 +1620,13 @@ async function monitorLineProvider(opts) {
|
|
|
1607
1620
|
}
|
|
1608
1621
|
});
|
|
1609
1622
|
const normalizedPath = normalizeWebhookPath(normalizePluginHttpPath(webhookPath, "/line/webhook") ?? "/line/webhook");
|
|
1623
|
+
const webhookRouteKey = canonicalizeWebhookRouteKey(normalizedPath);
|
|
1610
1624
|
const createScopedLineWebhookHandler = (target) => createLineNodeWebhookHandler({
|
|
1611
1625
|
channelSecret: target.channelSecret,
|
|
1612
1626
|
bot: target.bot,
|
|
1613
1627
|
runtime: target.runtime
|
|
1614
1628
|
});
|
|
1615
|
-
const { unregister: unregisterHttp } =
|
|
1629
|
+
const { unregister: unregisterHttp } = await registerLineWebhookTarget({
|
|
1616
1630
|
targetsByPath: lineWebhookTargets,
|
|
1617
1631
|
target: {
|
|
1618
1632
|
accountId: resolvedAccountId,
|
|
@@ -1624,10 +1638,12 @@ async function monitorLineProvider(opts) {
|
|
|
1624
1638
|
route: {
|
|
1625
1639
|
auth: "plugin",
|
|
1626
1640
|
pluginId: "line",
|
|
1641
|
+
source: "line-webhook",
|
|
1627
1642
|
accountId: resolvedAccountId,
|
|
1628
1643
|
log: (msg) => logVerbose(msg),
|
|
1644
|
+
throwOnFailure: true,
|
|
1629
1645
|
handler: async (req, res) => {
|
|
1630
|
-
const targets = lineWebhookTargets.get(
|
|
1646
|
+
const targets = lineWebhookTargets.get(webhookRouteKey) ?? [];
|
|
1631
1647
|
const firstTarget = targets[0];
|
|
1632
1648
|
if (req.method !== "POST") {
|
|
1633
1649
|
if (!firstTarget) {
|
|
@@ -1642,7 +1658,7 @@ async function monitorLineProvider(opts) {
|
|
|
1642
1658
|
req,
|
|
1643
1659
|
res,
|
|
1644
1660
|
inFlightLimiter: lineWebhookInFlightLimiter,
|
|
1645
|
-
inFlightKey: `line:${
|
|
1661
|
+
inFlightKey: `line:${webhookRouteKey}`
|
|
1646
1662
|
});
|
|
1647
1663
|
if (!requestLifecycle.ok) return;
|
|
1648
1664
|
try {
|
|
@@ -1681,6 +1697,7 @@ async function monitorLineProvider(opts) {
|
|
|
1681
1697
|
if (body.events && body.events.length > 0) {
|
|
1682
1698
|
logVerbose(`line: received ${body.events.length} webhook events`);
|
|
1683
1699
|
await match.target.bot.handleWebhook(body);
|
|
1700
|
+
res.setHeader("x-openclaw-delivery-accepted", "durable");
|
|
1684
1701
|
}
|
|
1685
1702
|
res.statusCode = 200;
|
|
1686
1703
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -1698,7 +1715,7 @@ async function monitorLineProvider(opts) {
|
|
|
1698
1715
|
res.end(JSON.stringify({ error: requestBodyErrorToText("REQUEST_BODY_TIMEOUT") }));
|
|
1699
1716
|
return;
|
|
1700
1717
|
}
|
|
1701
|
-
runtime.error?.(danger(`line webhook error: ${
|
|
1718
|
+
runtime.error?.(danger(`line webhook error: ${formatErrorMessage(err)}`));
|
|
1702
1719
|
if (!res.headersSent) {
|
|
1703
1720
|
res.statusCode = 500;
|
|
1704
1721
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -1709,8 +1726,9 @@ async function monitorLineProvider(opts) {
|
|
|
1709
1726
|
}
|
|
1710
1727
|
}
|
|
1711
1728
|
}
|
|
1712
|
-
});
|
|
1729
|
+
}, bot);
|
|
1713
1730
|
logVerbose(`line: registered webhook handler at ${normalizedPath}`);
|
|
1731
|
+
statusSink?.(channelReadyPatch());
|
|
1714
1732
|
let stopped = false;
|
|
1715
1733
|
let stopPromise;
|
|
1716
1734
|
const stopHandler = () => {
|
|
@@ -1719,7 +1737,9 @@ async function monitorLineProvider(opts) {
|
|
|
1719
1737
|
stopped = true;
|
|
1720
1738
|
logVerbose(`line: stopping provider for account ${resolvedAccountId}`);
|
|
1721
1739
|
unregisterHttp();
|
|
1722
|
-
stopPromise = bot.stop()
|
|
1740
|
+
stopPromise = bot.stop().finally(() => {
|
|
1741
|
+
statusSink?.(channelStoppedPatch());
|
|
1742
|
+
});
|
|
1723
1743
|
return stopPromise;
|
|
1724
1744
|
};
|
|
1725
1745
|
const stopOnAbort = () => void stopHandler();
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { i as processLineMessage, o as buildTemplateMessageFromPayload } from "./markdown-to-line-D4I-6lm6.js";
|
|
2
|
+
import { a as createQuickReplyItems, f as pushLocationMessage, g as pushTextMessageWithQuickReplies, h as pushTemplateMessage, i as createLocationMessage, m as pushMessagesLine, n as createFlexMessage, p as pushMessageLine, u as pushFlexMessage, v as sendMessageLine } from "./send-CFFr0-rZ.js";
|
|
3
|
+
export { buildTemplateMessageFromPayload, createFlexMessage, createLocationMessage, createQuickReplyItems, processLineMessage, pushFlexMessage, pushLocationMessage, pushMessageLine, pushMessagesLine, pushTemplateMessage, pushTextMessageWithQuickReplies, sendMessageLine };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-
|
|
1
|
+
import { a as createEventCard, i as createAgendaCard, n as createDeviceControlCard, r as createMediaPlayerCard, t as createAppleTvRemoteCard } from "./flex-templates-BBBlNqpV.js";
|
|
2
2
|
import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
3
|
import { DmPolicySchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema, requireOpenAllowFrom } from "openclaw/plugin-sdk/channel-config-schema";
|
|
4
4
|
import { requireChannelOpenAllowFrom } from "openclaw/plugin-sdk/extension-shared";
|
|
@@ -15,6 +15,7 @@ const ThreadBindingsSchema = z.object({
|
|
|
15
15
|
}).strict();
|
|
16
16
|
const LineCommonConfigSchemaBase = z.object({
|
|
17
17
|
enabled: z.boolean().optional(),
|
|
18
|
+
configWrites: z.boolean().optional(),
|
|
18
19
|
channelAccessToken: z.string().optional(),
|
|
19
20
|
channelSecret: z.string().optional(),
|
|
20
21
|
tokenFile: z.string().optional(),
|
|
@@ -100,9 +101,9 @@ function parseLineDirectives(payload) {
|
|
|
100
101
|
const lonStr = expectDefined(parts[3], "location longitude field");
|
|
101
102
|
const latitude = parseStrictFiniteNumber(latStr);
|
|
102
103
|
const longitude = parseStrictFiniteNumber(lonStr);
|
|
103
|
-
if (latitude !== void 0 && longitude !== void 0) lineData.location = {
|
|
104
|
+
if (address && latitude !== void 0 && longitude !== void 0) lineData.location = {
|
|
104
105
|
title: title || "Location",
|
|
105
|
-
address
|
|
106
|
+
address,
|
|
106
107
|
latitude,
|
|
107
108
|
longitude
|
|
108
109
|
};
|