@openclaw/line 2026.5.2-beta.2 → 2026.5.3-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.
Files changed (109) hide show
  1. package/dist/accounts-UXAvdBo_.js +105 -0
  2. package/dist/api.js +11 -0
  3. package/dist/basic-cards-7DTG6E-A.js +307 -0
  4. package/dist/card-command-UlPc6Vuc.js +240 -0
  5. package/dist/channel-Rls9s60K.js +601 -0
  6. package/dist/channel-plugin-api.js +2 -0
  7. package/dist/channel.runtime-bXoj8_Sl.js +4 -0
  8. package/dist/contract-api.js +2 -0
  9. package/dist/index.js +45 -0
  10. package/dist/markdown-to-line-CCWK6PJh.js +789 -0
  11. package/dist/monitor-B4_RxHc0.js +1523 -0
  12. package/dist/monitor.runtime-DyxYlrM9.js +2 -0
  13. package/dist/outbound-media-BfhBmWq5.js +55 -0
  14. package/dist/outbound.runtime-B-luYwEB.js +2 -0
  15. package/dist/probe-D9BPzb2n.js +30 -0
  16. package/dist/probe.runtime-N91OWW1J.js +2 -0
  17. package/dist/quick-reply-fallback-HskUUWsn.js +55 -0
  18. package/dist/reply-payload-transform-CFGTs9ln.js +713 -0
  19. package/dist/runtime-api.js +277 -0
  20. package/dist/schedule-cards-BwQChTdM.js +359 -0
  21. package/dist/secret-contract-api.js +5 -0
  22. package/dist/setup-api.js +2 -0
  23. package/dist/setup-entry.js +11 -0
  24. package/dist/setup-surface-DCH7CE-y.js +281 -0
  25. package/package.json +14 -6
  26. package/api.ts +0 -11
  27. package/channel-plugin-api.ts +0 -1
  28. package/contract-api.ts +0 -5
  29. package/index.ts +0 -54
  30. package/runtime-api.ts +0 -187
  31. package/secret-contract-api.ts +0 -4
  32. package/setup-api.ts +0 -2
  33. package/setup-entry.ts +0 -9
  34. package/src/account-helpers.ts +0 -16
  35. package/src/accounts.test.ts +0 -290
  36. package/src/accounts.ts +0 -187
  37. package/src/actions.ts +0 -61
  38. package/src/auto-reply-delivery.test.ts +0 -248
  39. package/src/auto-reply-delivery.ts +0 -200
  40. package/src/bindings.ts +0 -65
  41. package/src/bot-access.ts +0 -48
  42. package/src/bot-handlers.test.ts +0 -1089
  43. package/src/bot-handlers.ts +0 -642
  44. package/src/bot-message-context.test.ts +0 -405
  45. package/src/bot-message-context.ts +0 -581
  46. package/src/bot.ts +0 -70
  47. package/src/card-command.ts +0 -347
  48. package/src/channel-access-token.ts +0 -14
  49. package/src/channel-api.ts +0 -17
  50. package/src/channel-setup-status.contract.test.ts +0 -70
  51. package/src/channel-shared.ts +0 -48
  52. package/src/channel.logout.test.ts +0 -145
  53. package/src/channel.runtime.ts +0 -3
  54. package/src/channel.sendPayload.test.ts +0 -514
  55. package/src/channel.setup.ts +0 -11
  56. package/src/channel.status.test.ts +0 -63
  57. package/src/channel.ts +0 -154
  58. package/src/config-adapter.ts +0 -29
  59. package/src/config-schema.ts +0 -57
  60. package/src/download.test.ts +0 -133
  61. package/src/download.ts +0 -87
  62. package/src/flex-templates/basic-cards.ts +0 -395
  63. package/src/flex-templates/common.ts +0 -20
  64. package/src/flex-templates/media-control-cards.ts +0 -555
  65. package/src/flex-templates/message.ts +0 -13
  66. package/src/flex-templates/schedule-cards.ts +0 -467
  67. package/src/flex-templates/types.ts +0 -22
  68. package/src/flex-templates.ts +0 -32
  69. package/src/gateway.ts +0 -129
  70. package/src/group-keys.test.ts +0 -123
  71. package/src/group-keys.ts +0 -65
  72. package/src/group-policy.ts +0 -22
  73. package/src/markdown-to-line.test.ts +0 -348
  74. package/src/markdown-to-line.ts +0 -416
  75. package/src/message-cards.test.ts +0 -204
  76. package/src/monitor.lifecycle.test.ts +0 -421
  77. package/src/monitor.runtime.ts +0 -1
  78. package/src/monitor.ts +0 -506
  79. package/src/outbound-media.test.ts +0 -189
  80. package/src/outbound-media.ts +0 -120
  81. package/src/outbound.runtime.ts +0 -12
  82. package/src/outbound.ts +0 -356
  83. package/src/probe.contract.test.ts +0 -9
  84. package/src/probe.runtime.ts +0 -1
  85. package/src/probe.ts +0 -34
  86. package/src/quick-reply-fallback.ts +0 -10
  87. package/src/reply-chunks.test.ts +0 -179
  88. package/src/reply-chunks.ts +0 -110
  89. package/src/reply-payload-transform.test.ts +0 -387
  90. package/src/reply-payload-transform.ts +0 -317
  91. package/src/rich-menu.test.ts +0 -310
  92. package/src/rich-menu.ts +0 -326
  93. package/src/runtime.ts +0 -32
  94. package/src/send.test.ts +0 -346
  95. package/src/send.ts +0 -489
  96. package/src/setup-core.ts +0 -149
  97. package/src/setup-runtime-api.ts +0 -9
  98. package/src/setup-surface.test.ts +0 -474
  99. package/src/setup-surface.ts +0 -227
  100. package/src/signature.test.ts +0 -34
  101. package/src/signature.ts +0 -24
  102. package/src/status.ts +0 -37
  103. package/src/template-messages.ts +0 -333
  104. package/src/types.ts +0 -128
  105. package/src/webhook-node.test.ts +0 -513
  106. package/src/webhook-node.ts +0 -131
  107. package/src/webhook-utils.ts +0 -10
  108. package/src/webhook.ts +0 -111
  109. package/tsconfig.json +0 -16
@@ -1,290 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
5
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
6
- import {
7
- resolveLineAccount,
8
- resolveDefaultLineAccountId,
9
- normalizeAccountId,
10
- DEFAULT_ACCOUNT_ID,
11
- } from "./accounts.js";
12
-
13
- describe("LINE accounts", () => {
14
- const originalEnv = { ...process.env };
15
- const tempDirs: string[] = [];
16
-
17
- const createSecretFile = (fileName: string, contents: string) => {
18
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-line-account-"));
19
- tempDirs.push(dir);
20
- const filePath = path.join(dir, fileName);
21
- fs.writeFileSync(filePath, contents, "utf8");
22
- return filePath;
23
- };
24
-
25
- beforeEach(() => {
26
- process.env = { ...originalEnv };
27
- delete process.env.LINE_CHANNEL_ACCESS_TOKEN;
28
- delete process.env.LINE_CHANNEL_SECRET;
29
- });
30
-
31
- afterEach(() => {
32
- process.env = originalEnv;
33
- for (const dir of tempDirs.splice(0)) {
34
- fs.rmSync(dir, { recursive: true, force: true });
35
- }
36
- });
37
-
38
- describe("resolveLineAccount", () => {
39
- it("resolves account from config", () => {
40
- const cfg: OpenClawConfig = {
41
- channels: {
42
- line: {
43
- enabled: true,
44
- channelAccessToken: "test-token",
45
- channelSecret: "test-secret",
46
- name: "Test Bot",
47
- },
48
- },
49
- };
50
-
51
- const account = resolveLineAccount({ cfg });
52
-
53
- expect(account.accountId).toBe(DEFAULT_ACCOUNT_ID);
54
- expect(account.enabled).toBe(true);
55
- expect(account.channelAccessToken).toBe("test-token");
56
- expect(account.channelSecret).toBe("test-secret");
57
- expect(account.name).toBe("Test Bot");
58
- expect(account.tokenSource).toBe("config");
59
- });
60
-
61
- it("resolves account from environment variables", () => {
62
- process.env.LINE_CHANNEL_ACCESS_TOKEN = "env-token";
63
- process.env.LINE_CHANNEL_SECRET = "env-secret";
64
-
65
- const cfg: OpenClawConfig = {
66
- channels: {
67
- line: {
68
- enabled: true,
69
- },
70
- },
71
- };
72
-
73
- const account = resolveLineAccount({ cfg });
74
-
75
- expect(account.channelAccessToken).toBe("env-token");
76
- expect(account.channelSecret).toBe("env-secret");
77
- expect(account.tokenSource).toBe("env");
78
- });
79
-
80
- it("resolves named account", () => {
81
- const cfg: OpenClawConfig = {
82
- channels: {
83
- line: {
84
- enabled: true,
85
- accounts: {
86
- business: {
87
- enabled: true,
88
- channelAccessToken: "business-token",
89
- channelSecret: "business-secret",
90
- name: "Business Bot",
91
- },
92
- },
93
- },
94
- },
95
- };
96
-
97
- const account = resolveLineAccount({ cfg, accountId: "business" });
98
-
99
- expect(account.accountId).toBe("business");
100
- expect(account.enabled).toBe(true);
101
- expect(account.channelAccessToken).toBe("business-token");
102
- expect(account.channelSecret).toBe("business-secret");
103
- expect(account.name).toBe("Business Bot");
104
- });
105
-
106
- it("uses configured defaultAccount when accountId is omitted", () => {
107
- const cfg: OpenClawConfig = {
108
- channels: {
109
- line: {
110
- defaultAccount: "business",
111
- accounts: {
112
- business: {
113
- enabled: true,
114
- channelAccessToken: "business-token",
115
- channelSecret: "business-secret",
116
- name: "Business Bot",
117
- },
118
- },
119
- },
120
- },
121
- };
122
-
123
- const account = resolveLineAccount({ cfg });
124
-
125
- expect(account.accountId).toBe("business");
126
- expect(account.enabled).toBe(true);
127
- expect(account.channelAccessToken).toBe("business-token");
128
- expect(account.channelSecret).toBe("business-secret");
129
- expect(account.name).toBe("Business Bot");
130
- });
131
-
132
- it("returns empty token when not configured", () => {
133
- const cfg: OpenClawConfig = {};
134
-
135
- const account = resolveLineAccount({ cfg });
136
-
137
- expect(account.channelAccessToken).toBe("");
138
- expect(account.channelSecret).toBe("");
139
- expect(account.tokenSource).toBe("none");
140
- });
141
-
142
- it("resolves default account credentials from files", () => {
143
- const cfg: OpenClawConfig = {
144
- channels: {
145
- line: {
146
- tokenFile: createSecretFile("token.txt", "file-token\n"),
147
- secretFile: createSecretFile("secret.txt", "file-secret\n"),
148
- },
149
- },
150
- };
151
-
152
- const account = resolveLineAccount({ cfg });
153
-
154
- expect(account.channelAccessToken).toBe("file-token");
155
- expect(account.channelSecret).toBe("file-secret");
156
- expect(account.tokenSource).toBe("file");
157
- });
158
-
159
- it("resolves named account credentials from account-level files", () => {
160
- const cfg: OpenClawConfig = {
161
- channels: {
162
- line: {
163
- accounts: {
164
- business: {
165
- tokenFile: createSecretFile("business-token.txt", "business-file-token\n"),
166
- secretFile: createSecretFile("business-secret.txt", "business-file-secret\n"),
167
- },
168
- },
169
- },
170
- },
171
- };
172
-
173
- const account = resolveLineAccount({ cfg, accountId: "business" });
174
-
175
- expect(account.channelAccessToken).toBe("business-file-token");
176
- expect(account.channelSecret).toBe("business-file-secret");
177
- expect(account.tokenSource).toBe("file");
178
- });
179
-
180
- it.runIf(process.platform !== "win32")("rejects symlinked token and secret files", () => {
181
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-line-account-"));
182
- tempDirs.push(dir);
183
- const tokenFile = path.join(dir, "token.txt");
184
- const tokenLink = path.join(dir, "token-link.txt");
185
- const secretFile = path.join(dir, "secret.txt");
186
- const secretLink = path.join(dir, "secret-link.txt");
187
- fs.writeFileSync(tokenFile, "file-token\n", "utf8");
188
- fs.writeFileSync(secretFile, "file-secret\n", "utf8");
189
- fs.symlinkSync(tokenFile, tokenLink);
190
- fs.symlinkSync(secretFile, secretLink);
191
-
192
- const cfg: OpenClawConfig = {
193
- channels: {
194
- line: {
195
- tokenFile: tokenLink,
196
- secretFile: secretLink,
197
- },
198
- },
199
- };
200
-
201
- const account = resolveLineAccount({ cfg });
202
- expect(account.channelAccessToken).toBe("");
203
- expect(account.channelSecret).toBe("");
204
- expect(account.tokenSource).toBe("none");
205
- });
206
- });
207
-
208
- describe("resolveDefaultLineAccountId", () => {
209
- it.each([
210
- {
211
- name: "prefers channels.line.defaultAccount when configured",
212
- cfg: {
213
- channels: {
214
- line: {
215
- defaultAccount: "business",
216
- accounts: {
217
- business: { enabled: true },
218
- support: { enabled: true },
219
- },
220
- },
221
- },
222
- } satisfies OpenClawConfig,
223
- expected: "business",
224
- },
225
- {
226
- name: "normalizes channels.line.defaultAccount before lookup",
227
- cfg: {
228
- channels: {
229
- line: {
230
- defaultAccount: "Business Ops",
231
- accounts: {
232
- "business-ops": { enabled: true },
233
- },
234
- },
235
- },
236
- } satisfies OpenClawConfig,
237
- expected: "business-ops",
238
- },
239
- {
240
- name: "returns first named account when default not configured",
241
- cfg: {
242
- channels: {
243
- line: {
244
- accounts: {
245
- business: { enabled: true },
246
- },
247
- },
248
- },
249
- } satisfies OpenClawConfig,
250
- expected: "business",
251
- },
252
- {
253
- name: "falls back when channels.line.defaultAccount is missing",
254
- cfg: {
255
- channels: {
256
- line: {
257
- defaultAccount: "missing",
258
- accounts: {
259
- business: { enabled: true },
260
- },
261
- },
262
- },
263
- } satisfies OpenClawConfig,
264
- expected: "business",
265
- },
266
- {
267
- name: "prefers the default account when base credentials are configured",
268
- cfg: {
269
- channels: {
270
- line: {
271
- channelAccessToken: "base-token",
272
- accounts: {
273
- business: { enabled: true },
274
- },
275
- },
276
- },
277
- } satisfies OpenClawConfig,
278
- expected: DEFAULT_ACCOUNT_ID,
279
- },
280
- ])("$name", ({ cfg, expected }) => {
281
- expect(resolveDefaultLineAccountId(cfg)).toBe(expected);
282
- });
283
- });
284
-
285
- describe("normalizeAccountId", () => {
286
- it("trims and lowercases account ids", () => {
287
- expect(normalizeAccountId(" Business ")).toBe("business");
288
- });
289
- });
290
- });
package/src/accounts.ts DELETED
@@ -1,187 +0,0 @@
1
- import {
2
- DEFAULT_ACCOUNT_ID,
3
- normalizeAccountId as normalizeSharedAccountId,
4
- normalizeOptionalAccountId,
5
- } from "openclaw/plugin-sdk/account-id";
6
- import type { OpenClawConfig } from "openclaw/plugin-sdk/account-resolution";
7
- import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
8
- import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
9
- import type {
10
- LineAccountConfig,
11
- LineConfig,
12
- LineTokenSource,
13
- ResolvedLineAccount,
14
- } from "./types.js";
15
-
16
- export { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
17
-
18
- function readFileIfExists(filePath: string | undefined): string | undefined {
19
- return tryReadSecretFileSync(filePath, "LINE credential file", { rejectSymlink: true });
20
- }
21
-
22
- function resolveToken(params: {
23
- accountId: string;
24
- baseConfig?: LineConfig;
25
- accountConfig?: LineAccountConfig;
26
- }): { token: string; tokenSource: LineTokenSource } {
27
- const { accountId, baseConfig, accountConfig } = params;
28
-
29
- if (accountConfig?.channelAccessToken?.trim()) {
30
- return { token: accountConfig.channelAccessToken.trim(), tokenSource: "config" };
31
- }
32
-
33
- const accountFileToken = readFileIfExists(accountConfig?.tokenFile);
34
- if (accountFileToken) {
35
- return { token: accountFileToken, tokenSource: "file" };
36
- }
37
-
38
- if (accountId === DEFAULT_ACCOUNT_ID) {
39
- if (baseConfig?.channelAccessToken?.trim()) {
40
- return { token: baseConfig.channelAccessToken.trim(), tokenSource: "config" };
41
- }
42
-
43
- const baseFileToken = readFileIfExists(baseConfig?.tokenFile);
44
- if (baseFileToken) {
45
- return { token: baseFileToken, tokenSource: "file" };
46
- }
47
-
48
- const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim();
49
- if (envToken) {
50
- return { token: envToken, tokenSource: "env" };
51
- }
52
- }
53
-
54
- return { token: "", tokenSource: "none" };
55
- }
56
-
57
- function resolveSecret(params: {
58
- accountId: string;
59
- baseConfig?: LineConfig;
60
- accountConfig?: LineAccountConfig;
61
- }): string {
62
- const { accountId, baseConfig, accountConfig } = params;
63
-
64
- if (accountConfig?.channelSecret?.trim()) {
65
- return accountConfig.channelSecret.trim();
66
- }
67
-
68
- const accountFileSecret = readFileIfExists(accountConfig?.secretFile);
69
- if (accountFileSecret) {
70
- return accountFileSecret;
71
- }
72
-
73
- if (accountId === DEFAULT_ACCOUNT_ID) {
74
- if (baseConfig?.channelSecret?.trim()) {
75
- return baseConfig.channelSecret.trim();
76
- }
77
-
78
- const baseFileSecret = readFileIfExists(baseConfig?.secretFile);
79
- if (baseFileSecret) {
80
- return baseFileSecret;
81
- }
82
-
83
- const envSecret = process.env.LINE_CHANNEL_SECRET?.trim();
84
- if (envSecret) {
85
- return envSecret;
86
- }
87
- }
88
-
89
- return "";
90
- }
91
-
92
- export function resolveLineAccount(params: {
93
- cfg: OpenClawConfig;
94
- accountId?: string;
95
- }): ResolvedLineAccount {
96
- const cfg = params.cfg;
97
- const accountId = normalizeSharedAccountId(params.accountId ?? resolveDefaultLineAccountId(cfg));
98
- const lineConfig = cfg.channels?.line as LineConfig | undefined;
99
- const accounts = lineConfig?.accounts;
100
- const accountConfig =
101
- accountId !== DEFAULT_ACCOUNT_ID ? resolveAccountEntry(accounts, accountId) : undefined;
102
-
103
- const { token, tokenSource } = resolveToken({
104
- accountId,
105
- baseConfig: lineConfig,
106
- accountConfig,
107
- });
108
-
109
- const secret = resolveSecret({
110
- accountId,
111
- baseConfig: lineConfig,
112
- accountConfig,
113
- });
114
-
115
- const {
116
- accounts: _ignoredAccounts,
117
- defaultAccount: _ignoredDefaultAccount,
118
- ...lineBase
119
- } = (lineConfig ?? {}) as LineConfig & {
120
- accounts?: unknown;
121
- defaultAccount?: unknown;
122
- };
123
- const mergedConfig: LineConfig & LineAccountConfig = {
124
- ...lineBase,
125
- ...accountConfig,
126
- };
127
-
128
- const enabled =
129
- accountConfig?.enabled ??
130
- (accountId === DEFAULT_ACCOUNT_ID ? (lineConfig?.enabled ?? true) : false);
131
-
132
- const name =
133
- accountConfig?.name ?? (accountId === DEFAULT_ACCOUNT_ID ? lineConfig?.name : undefined);
134
-
135
- return {
136
- accountId,
137
- name,
138
- enabled,
139
- channelAccessToken: token,
140
- channelSecret: secret,
141
- tokenSource,
142
- config: mergedConfig,
143
- };
144
- }
145
-
146
- export function listLineAccountIds(cfg: OpenClawConfig): string[] {
147
- const lineConfig = cfg.channels?.line as LineConfig | undefined;
148
- const accounts = lineConfig?.accounts;
149
- const ids = new Set<string>();
150
-
151
- if (
152
- lineConfig?.channelAccessToken?.trim() ||
153
- lineConfig?.tokenFile ||
154
- process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim()
155
- ) {
156
- ids.add(DEFAULT_ACCOUNT_ID);
157
- }
158
-
159
- if (accounts) {
160
- for (const id of Object.keys(accounts)) {
161
- ids.add(id);
162
- }
163
- }
164
-
165
- return Array.from(ids);
166
- }
167
-
168
- export function resolveDefaultLineAccountId(cfg: OpenClawConfig): string {
169
- const preferred = normalizeOptionalAccountId(
170
- (cfg.channels?.line as LineConfig | undefined)?.defaultAccount,
171
- );
172
- if (
173
- preferred &&
174
- listLineAccountIds(cfg).some((accountId) => normalizeSharedAccountId(accountId) === preferred)
175
- ) {
176
- return preferred;
177
- }
178
- const ids = listLineAccountIds(cfg);
179
- if (ids.includes(DEFAULT_ACCOUNT_ID)) {
180
- return DEFAULT_ACCOUNT_ID;
181
- }
182
- return ids[0] ?? DEFAULT_ACCOUNT_ID;
183
- }
184
-
185
- export function normalizeAccountId(accountId: string | undefined): string {
186
- return normalizeSharedAccountId(accountId);
187
- }
package/src/actions.ts DELETED
@@ -1,61 +0,0 @@
1
- import type { messagingApi } from "@line/bot-sdk";
2
-
3
- export type Action = messagingApi.Action;
4
-
5
- /**
6
- * Create a message action (sends text when tapped)
7
- */
8
- export function messageAction(label: string, text?: string): Action {
9
- return {
10
- type: "message",
11
- label: label.slice(0, 20),
12
- text: text ?? label,
13
- };
14
- }
15
-
16
- /**
17
- * Create a URI action (opens a URL when tapped)
18
- */
19
- export function uriAction(label: string, uri: string): Action {
20
- return {
21
- type: "uri",
22
- label: label.slice(0, 20),
23
- uri,
24
- };
25
- }
26
-
27
- /**
28
- * Create a postback action (sends data to webhook when tapped)
29
- */
30
- export function postbackAction(label: string, data: string, displayText?: string): Action {
31
- return {
32
- type: "postback",
33
- label: label.slice(0, 20),
34
- data: data.slice(0, 300),
35
- displayText: displayText?.slice(0, 300),
36
- };
37
- }
38
-
39
- /**
40
- * Create a datetime picker action
41
- */
42
- export function datetimePickerAction(
43
- label: string,
44
- data: string,
45
- mode: "date" | "time" | "datetime",
46
- options?: {
47
- initial?: string;
48
- max?: string;
49
- min?: string;
50
- },
51
- ): Action {
52
- return {
53
- type: "datetimepicker",
54
- label: label.slice(0, 20),
55
- data: data.slice(0, 300),
56
- mode,
57
- initial: options?.initial,
58
- max: options?.max,
59
- min: options?.min,
60
- };
61
- }