@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,467 +0,0 @@
1
- import { attachFooterText } from "./common.js";
2
- import type { Action, FlexBox, FlexBubble, FlexComponent, FlexText } from "./types.js";
3
-
4
- function buildTitleSubtitleHeader(params: { title: string; subtitle?: string }): FlexComponent[] {
5
- const { title, subtitle } = params;
6
- const headerContents: FlexComponent[] = [
7
- {
8
- type: "text",
9
- text: title,
10
- weight: "bold",
11
- size: "xl",
12
- color: "#111111",
13
- wrap: true,
14
- } as FlexText,
15
- ];
16
-
17
- if (subtitle) {
18
- headerContents.push({
19
- type: "text",
20
- text: subtitle,
21
- size: "sm",
22
- color: "#888888",
23
- margin: "sm",
24
- wrap: true,
25
- } as FlexText);
26
- }
27
-
28
- return headerContents;
29
- }
30
-
31
- function buildCardHeaderSections(headerContents: FlexComponent[]): FlexComponent[] {
32
- return [
33
- {
34
- type: "box",
35
- layout: "vertical",
36
- contents: headerContents,
37
- paddingBottom: "lg",
38
- } as FlexBox,
39
- {
40
- type: "separator",
41
- color: "#EEEEEE",
42
- },
43
- ];
44
- }
45
-
46
- function createMegaBubbleWithFooter(params: {
47
- bodyContents: FlexComponent[];
48
- footer?: string;
49
- }): FlexBubble {
50
- const bubble: FlexBubble = {
51
- type: "bubble",
52
- size: "mega",
53
- body: {
54
- type: "box",
55
- layout: "vertical",
56
- contents: params.bodyContents,
57
- paddingAll: "xl",
58
- backgroundColor: "#FFFFFF",
59
- },
60
- };
61
-
62
- if (params.footer) {
63
- attachFooterText(bubble, params.footer);
64
- }
65
-
66
- return bubble;
67
- }
68
-
69
- /**
70
- * Create a receipt/summary card (for orders, transactions, data tables)
71
- *
72
- * Editorial design: Clean table layout with alternating row backgrounds,
73
- * prominent total section, and clear visual hierarchy.
74
- */
75
- export function createReceiptCard(params: {
76
- title: string;
77
- subtitle?: string;
78
- items: Array<{ name: string; value: string; highlight?: boolean }>;
79
- total?: { label: string; value: string };
80
- footer?: string;
81
- }): FlexBubble {
82
- const { title, subtitle, items, total, footer } = params;
83
-
84
- const itemRows: FlexComponent[] = items.slice(0, 12).map(
85
- (item, index) =>
86
- ({
87
- type: "box",
88
- layout: "horizontal",
89
- contents: [
90
- {
91
- type: "text",
92
- text: item.name,
93
- size: "sm",
94
- color: item.highlight ? "#111111" : "#666666",
95
- weight: item.highlight ? "bold" : "regular",
96
- flex: 3,
97
- wrap: true,
98
- } as FlexText,
99
- {
100
- type: "text",
101
- text: item.value,
102
- size: "sm",
103
- color: item.highlight ? "#06C755" : "#333333",
104
- weight: item.highlight ? "bold" : "regular",
105
- flex: 2,
106
- align: "end",
107
- wrap: true,
108
- } as FlexText,
109
- ],
110
- paddingAll: "md",
111
- backgroundColor: index % 2 === 0 ? "#FFFFFF" : "#FAFAFA",
112
- }) as FlexBox,
113
- );
114
-
115
- // Header section
116
- const headerContents = buildTitleSubtitleHeader({ title, subtitle });
117
-
118
- const bodyContents: FlexComponent[] = [
119
- ...buildCardHeaderSections(headerContents),
120
- {
121
- type: "box",
122
- layout: "vertical",
123
- contents: itemRows,
124
- margin: "md",
125
- cornerRadius: "md",
126
- borderWidth: "light",
127
- borderColor: "#EEEEEE",
128
- } as FlexBox,
129
- ];
130
-
131
- // Total section with emphasis
132
- if (total) {
133
- bodyContents.push({
134
- type: "box",
135
- layout: "horizontal",
136
- contents: [
137
- {
138
- type: "text",
139
- text: total.label,
140
- size: "lg",
141
- weight: "bold",
142
- color: "#111111",
143
- flex: 2,
144
- } as FlexText,
145
- {
146
- type: "text",
147
- text: total.value,
148
- size: "xl",
149
- weight: "bold",
150
- color: "#06C755",
151
- flex: 2,
152
- align: "end",
153
- } as FlexText,
154
- ],
155
- margin: "xl",
156
- paddingAll: "lg",
157
- backgroundColor: "#F0FDF4",
158
- cornerRadius: "lg",
159
- } as FlexBox);
160
- }
161
-
162
- return createMegaBubbleWithFooter({ bodyContents, footer });
163
- }
164
-
165
- /**
166
- * Create a calendar event card (for meetings, appointments, reminders)
167
- *
168
- * Editorial design: Date as hero, strong typographic hierarchy,
169
- * color-blocked zones, full text wrapping for readability.
170
- */
171
- export function createEventCard(params: {
172
- title: string;
173
- date: string;
174
- time?: string;
175
- location?: string;
176
- description?: string;
177
- calendar?: string;
178
- isAllDay?: boolean;
179
- action?: Action;
180
- }): FlexBubble {
181
- const { title, date, time, location, description, calendar, isAllDay, action } = params;
182
-
183
- // Hero date block - the most important information
184
- const dateBlock: FlexBox = {
185
- type: "box",
186
- layout: "vertical",
187
- contents: [
188
- {
189
- type: "text",
190
- text: date.toUpperCase(),
191
- size: "sm",
192
- weight: "bold",
193
- color: "#06C755",
194
- wrap: true,
195
- } as FlexText,
196
- {
197
- type: "text",
198
- text: isAllDay ? "ALL DAY" : (time ?? ""),
199
- size: "xxl",
200
- weight: "bold",
201
- color: "#111111",
202
- wrap: true,
203
- margin: "xs",
204
- } as FlexText,
205
- ],
206
- paddingBottom: "lg",
207
- borderWidth: "none",
208
- };
209
-
210
- // If no time and not all day, hide the time display
211
- if (!time && !isAllDay) {
212
- dateBlock.contents = [
213
- {
214
- type: "text",
215
- text: date,
216
- size: "xl",
217
- weight: "bold",
218
- color: "#111111",
219
- wrap: true,
220
- } as FlexText,
221
- ];
222
- }
223
-
224
- // Event title with accent bar
225
- const titleBlock: FlexBox = {
226
- type: "box",
227
- layout: "horizontal",
228
- contents: [
229
- {
230
- type: "box",
231
- layout: "vertical",
232
- contents: [],
233
- width: "4px",
234
- backgroundColor: "#06C755",
235
- cornerRadius: "2px",
236
- } as FlexBox,
237
- {
238
- type: "box",
239
- layout: "vertical",
240
- contents: [
241
- {
242
- type: "text",
243
- text: title,
244
- size: "lg",
245
- weight: "bold",
246
- color: "#1a1a1a",
247
- wrap: true,
248
- } as FlexText,
249
- ...(calendar
250
- ? [
251
- {
252
- type: "text",
253
- text: calendar,
254
- size: "xs",
255
- color: "#888888",
256
- margin: "sm",
257
- wrap: true,
258
- } as FlexText,
259
- ]
260
- : []),
261
- ],
262
- flex: 1,
263
- paddingStart: "lg",
264
- } as FlexBox,
265
- ],
266
- paddingTop: "lg",
267
- paddingBottom: "lg",
268
- borderWidth: "light",
269
- borderColor: "#EEEEEE",
270
- };
271
-
272
- const bodyContents: FlexComponent[] = [dateBlock, titleBlock];
273
-
274
- // Details section (location + description) in subtle background
275
- const hasDetails = location || description;
276
- if (hasDetails) {
277
- const detailItems: FlexComponent[] = [];
278
-
279
- if (location) {
280
- detailItems.push({
281
- type: "box",
282
- layout: "horizontal",
283
- contents: [
284
- {
285
- type: "text",
286
- text: "📍",
287
- size: "sm",
288
- flex: 0,
289
- } as FlexText,
290
- {
291
- type: "text",
292
- text: location,
293
- size: "sm",
294
- color: "#444444",
295
- margin: "md",
296
- flex: 1,
297
- wrap: true,
298
- } as FlexText,
299
- ],
300
- alignItems: "flex-start",
301
- } as FlexBox);
302
- }
303
-
304
- if (description) {
305
- detailItems.push({
306
- type: "text",
307
- text: description,
308
- size: "sm",
309
- color: "#666666",
310
- wrap: true,
311
- margin: location ? "lg" : "none",
312
- } as FlexText);
313
- }
314
-
315
- bodyContents.push({
316
- type: "box",
317
- layout: "vertical",
318
- contents: detailItems,
319
- margin: "lg",
320
- paddingAll: "lg",
321
- backgroundColor: "#F8F9FA",
322
- cornerRadius: "lg",
323
- } as FlexBox);
324
- }
325
-
326
- return {
327
- type: "bubble",
328
- size: "mega",
329
- body: {
330
- type: "box",
331
- layout: "vertical",
332
- contents: bodyContents,
333
- paddingAll: "xl",
334
- backgroundColor: "#FFFFFF",
335
- action,
336
- },
337
- };
338
- }
339
-
340
- /**
341
- * Create a calendar agenda card showing multiple events
342
- *
343
- * Editorial timeline design: Time-focused left column with event details
344
- * on the right. Visual accent bars indicate event priority/recency.
345
- */
346
- export function createAgendaCard(params: {
347
- title: string;
348
- subtitle?: string;
349
- events: Array<{
350
- title: string;
351
- time?: string;
352
- location?: string;
353
- calendar?: string;
354
- isNow?: boolean;
355
- }>;
356
- footer?: string;
357
- }): FlexBubble {
358
- const { title, subtitle, events, footer } = params;
359
-
360
- // Header with title and optional subtitle
361
- const headerContents = buildTitleSubtitleHeader({ title, subtitle });
362
-
363
- // Event timeline items
364
- const eventItems: FlexComponent[] = events.slice(0, 6).map((event, index) => {
365
- const isActive = event.isNow || index === 0;
366
- const accentColor = isActive ? "#06C755" : "#E5E5E5";
367
-
368
- // Time column (fixed width)
369
- const timeColumn: FlexBox = {
370
- type: "box",
371
- layout: "vertical",
372
- contents: [
373
- {
374
- type: "text",
375
- text: event.time ?? "—",
376
- size: "sm",
377
- weight: isActive ? "bold" : "regular",
378
- color: isActive ? "#06C755" : "#666666",
379
- align: "end",
380
- wrap: true,
381
- } as FlexText,
382
- ],
383
- width: "65px",
384
- justifyContent: "flex-start",
385
- };
386
-
387
- // Accent dot
388
- const dotColumn: FlexBox = {
389
- type: "box",
390
- layout: "vertical",
391
- contents: [
392
- {
393
- type: "box",
394
- layout: "vertical",
395
- contents: [],
396
- width: "10px",
397
- height: "10px",
398
- backgroundColor: accentColor,
399
- cornerRadius: "5px",
400
- } as FlexBox,
401
- ],
402
- width: "24px",
403
- alignItems: "center",
404
- justifyContent: "flex-start",
405
- paddingTop: "xs",
406
- };
407
-
408
- // Event details column
409
- const detailContents: FlexComponent[] = [
410
- {
411
- type: "text",
412
- text: event.title,
413
- size: "md",
414
- weight: "bold",
415
- color: "#1a1a1a",
416
- wrap: true,
417
- } as FlexText,
418
- ];
419
-
420
- // Secondary info line
421
- const secondaryParts: string[] = [];
422
- if (event.location) {
423
- secondaryParts.push(event.location);
424
- }
425
- if (event.calendar) {
426
- secondaryParts.push(event.calendar);
427
- }
428
-
429
- if (secondaryParts.length > 0) {
430
- detailContents.push({
431
- type: "text",
432
- text: secondaryParts.join(" · "),
433
- size: "xs",
434
- color: "#888888",
435
- wrap: true,
436
- margin: "xs",
437
- } as FlexText);
438
- }
439
-
440
- const detailColumn: FlexBox = {
441
- type: "box",
442
- layout: "vertical",
443
- contents: detailContents,
444
- flex: 1,
445
- };
446
-
447
- return {
448
- type: "box",
449
- layout: "horizontal",
450
- contents: [timeColumn, dotColumn, detailColumn],
451
- margin: index > 0 ? "xl" : undefined,
452
- alignItems: "flex-start",
453
- } as FlexBox;
454
- });
455
-
456
- const bodyContents: FlexComponent[] = [
457
- ...buildCardHeaderSections(headerContents),
458
- {
459
- type: "box",
460
- layout: "vertical",
461
- contents: eventItems,
462
- paddingTop: "xl",
463
- } as FlexBox,
464
- ];
465
-
466
- return createMegaBubbleWithFooter({ bodyContents, footer });
467
- }
@@ -1,22 +0,0 @@
1
- import type { messagingApi } from "@line/bot-sdk";
2
-
3
- export type FlexContainer = messagingApi.FlexContainer;
4
- export type FlexBubble = messagingApi.FlexBubble;
5
- export type FlexCarousel = messagingApi.FlexCarousel;
6
- export type FlexBox = messagingApi.FlexBox;
7
- export type FlexText = messagingApi.FlexText;
8
- export type FlexImage = messagingApi.FlexImage;
9
- export type FlexButton = messagingApi.FlexButton;
10
- export type FlexComponent = messagingApi.FlexComponent;
11
- export type Action = messagingApi.Action;
12
-
13
- export interface ListItem {
14
- title: string;
15
- subtitle?: string;
16
- action?: Action;
17
- }
18
-
19
- export interface CardAction {
20
- label: string;
21
- action: Action;
22
- }
@@ -1,32 +0,0 @@
1
- export {
2
- createActionCard,
3
- createCarousel,
4
- createImageCard,
5
- createInfoCard,
6
- createListCard,
7
- createNotificationBubble,
8
- } from "./flex-templates/basic-cards.js";
9
- export {
10
- createAgendaCard,
11
- createEventCard,
12
- createReceiptCard,
13
- } from "./flex-templates/schedule-cards.js";
14
- export {
15
- createAppleTvRemoteCard,
16
- createDeviceControlCard,
17
- createMediaPlayerCard,
18
- } from "./flex-templates/media-control-cards.js";
19
- export { toFlexMessage } from "./flex-templates/message.js";
20
-
21
- export type {
22
- CardAction,
23
- FlexBox,
24
- FlexBubble,
25
- FlexButton,
26
- FlexCarousel,
27
- FlexComponent,
28
- FlexContainer,
29
- FlexImage,
30
- FlexText,
31
- ListItem,
32
- } from "./flex-templates/types.js";
package/src/gateway.ts DELETED
@@ -1,129 +0,0 @@
1
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
2
- import { resolveLineAccount } from "./accounts.js";
3
- import {
4
- clearAccountEntryFields,
5
- DEFAULT_ACCOUNT_ID,
6
- type ChannelPlugin,
7
- type LineConfig,
8
- type OpenClawConfig,
9
- type ResolvedLineAccount,
10
- } from "./channel-api.js";
11
- import { getLineRuntime } from "./runtime.js";
12
-
13
- const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime.js"));
14
- const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime.js"));
15
-
16
- export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>["gateway"]> = {
17
- startAccount: async (ctx) => {
18
- const account = ctx.account;
19
- const token = account.channelAccessToken.trim();
20
- const secret = account.channelSecret.trim();
21
- if (!token) {
22
- throw new Error(
23
- `LINE webhook mode requires a non-empty channel access token for account "${account.accountId}".`,
24
- );
25
- }
26
- if (!secret) {
27
- throw new Error(
28
- `LINE webhook mode requires a non-empty channel secret for account "${account.accountId}".`,
29
- );
30
- }
31
-
32
- let lineBotLabel = "";
33
- try {
34
- const probe = await (await loadLineProbeRuntime()).probeLineBot(token, 2500);
35
- const displayName = probe.ok ? probe.bot?.displayName?.trim() : null;
36
- if (displayName) {
37
- lineBotLabel = ` (${displayName})`;
38
- }
39
- } catch (err) {
40
- if (getLineRuntime().logging.shouldLogVerbose()) {
41
- ctx.log?.debug?.(`[${account.accountId}] bot probe failed: ${String(err)}`);
42
- }
43
- }
44
-
45
- ctx.log?.info(`[${account.accountId}] starting LINE provider${lineBotLabel}`);
46
-
47
- const monitorLineProvider =
48
- getLineRuntime().channel.line?.monitorLineProvider ??
49
- (await loadLineMonitorRuntime()).monitorLineProvider;
50
-
51
- return await monitorLineProvider({
52
- channelAccessToken: token,
53
- channelSecret: secret,
54
- accountId: account.accountId,
55
- config: ctx.cfg,
56
- runtime: ctx.runtime,
57
- abortSignal: ctx.abortSignal,
58
- webhookPath: account.config.webhookPath,
59
- });
60
- },
61
- logoutAccount: async ({ accountId, cfg }) => {
62
- const envToken = process.env.LINE_CHANNEL_ACCESS_TOKEN?.trim() ?? "";
63
- const nextCfg = { ...cfg } as OpenClawConfig;
64
- const lineConfig = (cfg.channels?.line ?? {}) as LineConfig;
65
- const nextLine = { ...lineConfig };
66
- let cleared = false;
67
- let changed = false;
68
-
69
- if (accountId === DEFAULT_ACCOUNT_ID) {
70
- if (
71
- nextLine.channelAccessToken ||
72
- nextLine.channelSecret ||
73
- nextLine.tokenFile ||
74
- nextLine.secretFile
75
- ) {
76
- delete nextLine.channelAccessToken;
77
- delete nextLine.channelSecret;
78
- delete nextLine.tokenFile;
79
- delete nextLine.secretFile;
80
- cleared = true;
81
- changed = true;
82
- }
83
- }
84
-
85
- const accountCleanup = clearAccountEntryFields({
86
- accounts: nextLine.accounts,
87
- accountId,
88
- fields: ["channelAccessToken", "channelSecret", "tokenFile", "secretFile"],
89
- markClearedOnFieldPresence: true,
90
- });
91
- if (accountCleanup.changed) {
92
- changed = true;
93
- if (accountCleanup.cleared) {
94
- cleared = true;
95
- }
96
- if (accountCleanup.nextAccounts) {
97
- nextLine.accounts = accountCleanup.nextAccounts;
98
- } else {
99
- delete nextLine.accounts;
100
- }
101
- }
102
-
103
- if (changed) {
104
- if (Object.keys(nextLine).length > 0) {
105
- nextCfg.channels = { ...nextCfg.channels, line: nextLine };
106
- } else {
107
- const nextChannels = { ...nextCfg.channels };
108
- delete (nextChannels as Record<string, unknown>).line;
109
- if (Object.keys(nextChannels).length > 0) {
110
- nextCfg.channels = nextChannels;
111
- } else {
112
- delete nextCfg.channels;
113
- }
114
- }
115
- await getLineRuntime().config.replaceConfigFile({
116
- nextConfig: nextCfg,
117
- afterWrite: { mode: "auto" },
118
- });
119
- }
120
-
121
- const resolved = resolveLineAccount({
122
- cfg: changed ? nextCfg : cfg,
123
- accountId,
124
- });
125
- const loggedOut = resolved.tokenSource === "none";
126
-
127
- return { cleared, envToken: Boolean(envToken), loggedOut };
128
- },
129
- };