@openclaw/line 2026.9.1-beta.1 → 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/account-helpers-BSUyZwLm.js +12 -0
- package/dist/{accounts-BqEVsDoB.js → accounts-BBEFMYbY.js} +2 -1
- package/dist/api.js +2 -2
- package/dist/card-command-pwvE48Wb.js +252 -0
- package/dist/{channel-CsQLcrsA.js → channel-CF2AeULJ.js} +111 -380
- package/dist/channel-plugin-api.js +1 -1
- package/dist/channel.runtime-BF4Wk2dM.js +4 -0
- package/dist/contract-api.js +1 -1
- package/dist/doctor-contract-api.js +141 -0
- package/dist/index.js +5 -17
- package/dist/{markdown-to-line-BmZ4hhfC.js → markdown-to-line-B-sj-7II.js} +22 -23
- package/dist/{monitor-Bd9XDV1i.js → monitor-D0fHFXCi.js} +323 -234
- package/dist/monitor.runtime-Mgecav_D.js +2 -0
- package/dist/outbound.runtime-CGqyJ-Ug.js +3 -0
- package/dist/probe.runtime-CbtMgU4s.js +2 -0
- package/dist/rich-messages-DY1AGKAJ.js +742 -0
- package/dist/runtime-api.js +14 -15
- package/dist/{send-Dce0m539.js → send-CQQxpxPp.js} +142 -112
- package/dist/{send-receipt-CCPTalf9.js → send-retry-ChpfJ-LM.js} +326 -86
- package/dist/setup-api.js +1 -1
- package/dist/{setup-surface-W0Dbgu58.js → setup-surface-CDlgkTKi.js} +47 -39
- package/dist/webhook-spool-contract-1rY8OJw8.js +59 -0
- package/node_modules/typebox/build/format/email.mjs +1 -1
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +19 -1
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +16 -2
- package/package.json +5 -5
- package/dist/card-command-CmU47gD9.js +0 -230
- package/dist/channel.runtime-B83x7k_i.js +0 -4
- package/dist/media-control-cards-CksNB8Wk.js +0 -474
- package/dist/monitor.runtime-BCCa6qTX.js +0 -2
- package/dist/outbound.runtime-D4w0-Gkc.js +0 -3
- package/dist/probe-CM03wo6r.js +0 -28
- package/dist/probe.runtime-D13ZtXsd.js +0 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region extensions/line/src/account-helpers.ts
|
|
2
|
+
function hasLineCredentials(account) {
|
|
3
|
+
if (account.tokenStatus && account.signingSecretStatus) return account.tokenStatus !== "missing" && account.signingSecretStatus !== "missing";
|
|
4
|
+
return Boolean(account.channelAccessToken?.trim() && account.channelSecret?.trim());
|
|
5
|
+
}
|
|
6
|
+
function parseLineAllowFromId(raw) {
|
|
7
|
+
const trimmed = raw.trim().replace(/^line:(?:user:)?/i, "");
|
|
8
|
+
if (!/^U[a-f0-9]{32}$/i.test(trimmed)) return null;
|
|
9
|
+
return trimmed;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { parseLineAllowFromId as n, hasLineCredentials as t };
|
|
@@ -16,7 +16,8 @@ function resolveLineCredential(params) {
|
|
|
16
16
|
};
|
|
17
17
|
const file = config?.[fileKey];
|
|
18
18
|
if (file?.trim()) {
|
|
19
|
-
const
|
|
19
|
+
const scope = index === 0 ? `accounts.${accountId}.` : "";
|
|
20
|
+
const result = tryReadSecretFileSync(file, "LINE credential file", { rejectSymlink: true }, { configPath: `channels.line.${scope}${fileKey}` });
|
|
20
21
|
return result.status === "available" ? {
|
|
21
22
|
value: result.value,
|
|
22
23
|
source: "file",
|
package/dist/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-
|
|
2
|
-
import { r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-
|
|
1
|
+
import { n as lineChannelPluginCommon, t as linePlugin } from "./channel-CF2AeULJ.js";
|
|
2
|
+
import { r as lineSetupContract, t as lineSetupWizard } from "./setup-surface-CDlgkTKi.js";
|
|
3
3
|
//#region extensions/line/src/channel.setup.ts
|
|
4
4
|
const lineSetupPlugin = {
|
|
5
5
|
id: "line",
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { d as createActionCard, f as createImageCard, m as createListCard, p as createInfoCard, u as createReceiptCard } from "./send-retry-ChpfJ-LM.js";
|
|
2
|
+
import { t as createFlexMessage } from "./send-CQQxpxPp.js";
|
|
3
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
5
|
+
//#region extensions/line/src/card-command.ts
|
|
6
|
+
const CARD_USAGE = `Usage: /card <type> "title" "body" [options]
|
|
7
|
+
|
|
8
|
+
Types:
|
|
9
|
+
info "Title" "Body" ["Footer"]
|
|
10
|
+
image "Title" "Caption" --url <image-url>
|
|
11
|
+
action "Title" "Body" --actions "Btn1|url1,Btn2|text2"
|
|
12
|
+
list "Title" "Item1|Desc1,Item2|Desc2"
|
|
13
|
+
receipt "Title" "Item1:$10,Item2:$20" --total "$30"
|
|
14
|
+
confirm "Question?" --yes "Yes|data" --no "No|data"
|
|
15
|
+
buttons "Title" "Text" --actions "Btn1|url1,Btn2|data2"
|
|
16
|
+
|
|
17
|
+
Escape a separator that belongs to the data with a backslash: \\, \\|, or \\:
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
/card info "Welcome" "Thanks for joining!"
|
|
21
|
+
/card image "Product" "Check it out" --url https://example.com/img.jpg
|
|
22
|
+
/card action "Menu" "Choose an option" --actions "Order|/order,Help|/help"
|
|
23
|
+
/card action "Links" "Pick one" --actions "Open|https://example.com/a\\,b"`;
|
|
24
|
+
function buildLineReply(lineData) {
|
|
25
|
+
return { channelData: { line: lineData } };
|
|
26
|
+
}
|
|
27
|
+
function buildLineFlexReply(altText, contents) {
|
|
28
|
+
const message = createFlexMessage(altText, contents);
|
|
29
|
+
return buildLineReply({ flexMessage: {
|
|
30
|
+
altText: message.altText,
|
|
31
|
+
contents: message.contents
|
|
32
|
+
} });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Split a card option value on its unescaped separators.
|
|
36
|
+
*
|
|
37
|
+
* Each pass consumes escapes for its own separator and preserves every other
|
|
38
|
+
* backslash. This lets nested comma, pipe, and colon passes stay independent.
|
|
39
|
+
*/
|
|
40
|
+
function splitCardValue(value, separator) {
|
|
41
|
+
const parts = [];
|
|
42
|
+
let current = "";
|
|
43
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
44
|
+
const char = value[index];
|
|
45
|
+
if (char === "\\" && value[index + 1] === separator) {
|
|
46
|
+
current += separator;
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (char === separator) {
|
|
51
|
+
parts.push(current);
|
|
52
|
+
current = "";
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
current += char;
|
|
56
|
+
}
|
|
57
|
+
parts.push(current);
|
|
58
|
+
return parts;
|
|
59
|
+
}
|
|
60
|
+
/** Split a "Label|data" pair, keeping escaped separators inside either side. */
|
|
61
|
+
function splitCardPair(part) {
|
|
62
|
+
const [label = "", data] = splitCardValue(part, "|").map((piece) => piece.trim());
|
|
63
|
+
return [label, data];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse action string format: "Label|data,Label2|data2"
|
|
67
|
+
* Data can be a URL (uri action) or plain text (message action) or key=value (postback)
|
|
68
|
+
*/
|
|
69
|
+
function parseActions(actionsStr) {
|
|
70
|
+
if (!actionsStr) return [];
|
|
71
|
+
const results = [];
|
|
72
|
+
for (const part of splitCardValue(actionsStr, ",")) {
|
|
73
|
+
const [label, data] = splitCardPair(part);
|
|
74
|
+
if (!label) continue;
|
|
75
|
+
const actionData = data || label;
|
|
76
|
+
const action = actionData.startsWith("http://") || actionData.startsWith("https://") ? {
|
|
77
|
+
type: "uri",
|
|
78
|
+
label,
|
|
79
|
+
uri: actionData
|
|
80
|
+
} : actionData.includes("=") ? {
|
|
81
|
+
type: "postback",
|
|
82
|
+
label,
|
|
83
|
+
data: actionData,
|
|
84
|
+
displayText: label
|
|
85
|
+
} : {
|
|
86
|
+
type: "message",
|
|
87
|
+
label,
|
|
88
|
+
text: actionData
|
|
89
|
+
};
|
|
90
|
+
results.push({
|
|
91
|
+
label,
|
|
92
|
+
action
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse list items format: "Item1|Subtitle1,Item2|Subtitle2"
|
|
99
|
+
*/
|
|
100
|
+
function parseListItems(itemsStr) {
|
|
101
|
+
return splitCardValue(itemsStr, ",").map((part) => {
|
|
102
|
+
const [title, subtitle] = splitCardPair(part);
|
|
103
|
+
return {
|
|
104
|
+
title,
|
|
105
|
+
subtitle
|
|
106
|
+
};
|
|
107
|
+
}).filter((item) => item.title);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parse receipt items format: "Item1:$10,Item2:$20"
|
|
111
|
+
*/
|
|
112
|
+
function parseReceiptItems(itemsStr) {
|
|
113
|
+
return splitCardValue(itemsStr, ",").map((part) => {
|
|
114
|
+
const segments = splitCardValue(part, ":");
|
|
115
|
+
const value = segments.length > 1 ? segments.pop() : void 0;
|
|
116
|
+
return {
|
|
117
|
+
name: segments.join(":").trim(),
|
|
118
|
+
value: value?.trim() ?? ""
|
|
119
|
+
};
|
|
120
|
+
}).filter((item) => item.name);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Parse quoted arguments from command string
|
|
124
|
+
* Supports: /card type "arg1" "arg2" "arg3" --flag value
|
|
125
|
+
*/
|
|
126
|
+
function parseCardArgs(argsStrInput) {
|
|
127
|
+
let argsStr = argsStrInput;
|
|
128
|
+
const result = {
|
|
129
|
+
type: "",
|
|
130
|
+
args: [],
|
|
131
|
+
flags: {}
|
|
132
|
+
};
|
|
133
|
+
const typeMatch = argsStr.match(/^(\w+)/);
|
|
134
|
+
if (typeMatch) {
|
|
135
|
+
result.type = normalizeLowercaseStringOrEmpty(typeMatch[1]);
|
|
136
|
+
argsStr = argsStr.slice(typeMatch[0].length).trim();
|
|
137
|
+
}
|
|
138
|
+
const quotedRegex = /"([^"]*?)"/g;
|
|
139
|
+
let match;
|
|
140
|
+
while ((match = quotedRegex.exec(argsStr)) !== null) result.args.push(expectDefined(match[1], "quoted card argument capture") || void 0);
|
|
141
|
+
const flagRegex = /--(\w+)\s+(?:"([^"]*?)"|(\S+))/g;
|
|
142
|
+
while ((match = flagRegex.exec(argsStr)) !== null) {
|
|
143
|
+
const key = expectDefined(match[1], "card flag name capture");
|
|
144
|
+
result.flags[key] = expectDefined(match[2] ?? match[3], "card flag value capture");
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
async function handleLineCardCommand(argsInput) {
|
|
149
|
+
const argsStr = argsInput?.trim() ?? "";
|
|
150
|
+
if (!argsStr) return { text: CARD_USAGE };
|
|
151
|
+
const { type, args, flags } = parseCardArgs(argsStr);
|
|
152
|
+
if (!type) return { text: CARD_USAGE };
|
|
153
|
+
try {
|
|
154
|
+
switch (type) {
|
|
155
|
+
case "info": {
|
|
156
|
+
const [title = "Info", body = "", footer] = args;
|
|
157
|
+
const bubble = createInfoCard(title, body, footer);
|
|
158
|
+
return buildLineFlexReply(body ? `${title}: ${body}` : title, bubble);
|
|
159
|
+
}
|
|
160
|
+
case "image": {
|
|
161
|
+
const [title = "Image", caption = ""] = args;
|
|
162
|
+
const imageUrl = flags.url || flags.image;
|
|
163
|
+
if (!imageUrl) return { text: "Error: Image card requires --url <image-url>" };
|
|
164
|
+
const bubble = createImageCard(imageUrl, title, caption);
|
|
165
|
+
return buildLineFlexReply(`${title}: ${caption}`, bubble);
|
|
166
|
+
}
|
|
167
|
+
case "action": {
|
|
168
|
+
const [title = "Actions", body = ""] = args;
|
|
169
|
+
const actions = parseActions(flags.actions);
|
|
170
|
+
if (actions.length === 0) return { text: "Error: Action card requires --actions \"Label1|data1,Label2|data2\"" };
|
|
171
|
+
const bubble = createActionCard(title, body, actions, { imageUrl: flags.url || flags.image });
|
|
172
|
+
return buildLineFlexReply(body ? `${title}: ${body}` : title, bubble);
|
|
173
|
+
}
|
|
174
|
+
case "list": {
|
|
175
|
+
const [title = "List", itemsStr = ""] = args;
|
|
176
|
+
const items = parseListItems(itemsStr || flags.items || "");
|
|
177
|
+
if (items.length === 0) return { text: "Error: List card requires items. Usage: /card list \"Title\" \"Item1|Desc1,Item2|Desc2\"" };
|
|
178
|
+
const bubble = createListCard(title, items);
|
|
179
|
+
return buildLineFlexReply(`${title}: ${items.map((item) => item.title).join(", ")}`, bubble);
|
|
180
|
+
}
|
|
181
|
+
case "receipt": {
|
|
182
|
+
const [title = "Receipt", itemsStr = ""] = args;
|
|
183
|
+
const items = parseReceiptItems(itemsStr || flags.items || "");
|
|
184
|
+
const total = flags.total ? {
|
|
185
|
+
label: "Total",
|
|
186
|
+
value: flags.total
|
|
187
|
+
} : void 0;
|
|
188
|
+
const footer = flags.footer;
|
|
189
|
+
if (items.length === 0) return { text: "Error: Receipt card requires items. Usage: /card receipt \"Title\" \"Item1:$10,Item2:$20\" --total \"$30\"" };
|
|
190
|
+
const bubble = createReceiptCard({
|
|
191
|
+
title,
|
|
192
|
+
items,
|
|
193
|
+
total,
|
|
194
|
+
footer
|
|
195
|
+
});
|
|
196
|
+
return buildLineFlexReply(`${title}: ${items.map((item) => `${item.name} ${item.value}`).join(", ")}`, bubble);
|
|
197
|
+
}
|
|
198
|
+
case "confirm": {
|
|
199
|
+
const [question = "Confirm?"] = args;
|
|
200
|
+
const yesStr = flags.yes || "Yes|yes";
|
|
201
|
+
const noStr = flags.no || "No|no";
|
|
202
|
+
const [yesLabel, yesData] = splitCardPair(yesStr);
|
|
203
|
+
const [noLabel, noData] = splitCardPair(noStr);
|
|
204
|
+
return buildLineReply({ templateMessage: {
|
|
205
|
+
type: "confirm",
|
|
206
|
+
text: question,
|
|
207
|
+
confirmLabel: yesLabel || "Yes",
|
|
208
|
+
confirmData: yesData || "yes",
|
|
209
|
+
cancelLabel: noLabel || "No",
|
|
210
|
+
cancelData: noData || "no",
|
|
211
|
+
altText: question
|
|
212
|
+
} });
|
|
213
|
+
}
|
|
214
|
+
case "buttons": {
|
|
215
|
+
const [title = "Menu", text = "Choose an option"] = args;
|
|
216
|
+
const actionParts = parseActions(flags.actions || "");
|
|
217
|
+
if (actionParts.length === 0) return { text: "Error: Buttons card requires --actions \"Label1|data1,Label2|data2\"" };
|
|
218
|
+
const templateActions = actionParts.map((a) => {
|
|
219
|
+
const action = a.action;
|
|
220
|
+
const label = action.label ?? a.label;
|
|
221
|
+
if (action.type === "uri") return {
|
|
222
|
+
type: "uri",
|
|
223
|
+
label,
|
|
224
|
+
uri: action.uri
|
|
225
|
+
};
|
|
226
|
+
if (action.type === "postback") return {
|
|
227
|
+
type: "postback",
|
|
228
|
+
label,
|
|
229
|
+
data: action.data
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
type: "message",
|
|
233
|
+
label,
|
|
234
|
+
data: action.text
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
return buildLineReply({ templateMessage: {
|
|
238
|
+
type: "buttons",
|
|
239
|
+
title,
|
|
240
|
+
text,
|
|
241
|
+
thumbnailImageUrl: flags.url || flags.image,
|
|
242
|
+
actions: templateActions
|
|
243
|
+
} });
|
|
244
|
+
}
|
|
245
|
+
default: return { text: `Unknown card type: "${type}". Available types: info, image, action, list, receipt, confirm, buttons` };
|
|
246
|
+
}
|
|
247
|
+
} catch (err) {
|
|
248
|
+
return { text: `Error creating card: ${String(err)}` };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
252
|
+
export { handleLineCardCommand };
|