@openclaw/zalouser 2026.1.29 → 2026.2.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/src/onboarding.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  promptAccountId,
12
12
  promptChannelAccessConfig,
13
13
  } from "openclaw/plugin-sdk";
14
-
14
+ import type { ZcaFriend, ZcaGroup } from "./types.js";
15
15
  import {
16
16
  listZalouserAccountIds,
17
17
  resolveDefaultZalouserAccountId,
@@ -19,7 +19,6 @@ import {
19
19
  checkZcaAuthenticated,
20
20
  } from "./accounts.js";
21
21
  import { runZca, runZcaInteractive, checkZcaInstalled, parseJsonOutput } from "./zca.js";
22
- import type { ZcaFriend, ZcaGroup } from "./types.js";
23
22
 
24
23
  const channel = "zalouser" as const;
25
24
 
@@ -28,9 +27,7 @@ function setZalouserDmPolicy(
28
27
  dmPolicy: "pairing" | "allowlist" | "open" | "disabled",
29
28
  ): OpenClawConfig {
30
29
  const allowFrom =
31
- dmPolicy === "open"
32
- ? addWildcardAllowFrom(cfg.channels?.zalouser?.allowFrom)
33
- : undefined;
30
+ dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.zalouser?.allowFrom) : undefined;
34
31
  return {
35
32
  ...cfg,
36
33
  channels: {
@@ -75,19 +72,29 @@ async function promptZalouserAllowFrom(params: {
75
72
 
76
73
  const resolveUserId = async (input: string): Promise<string | null> => {
77
74
  const trimmed = input.trim();
78
- if (!trimmed) return null;
79
- if (/^\d+$/.test(trimmed)) return trimmed;
75
+ if (!trimmed) {
76
+ return null;
77
+ }
78
+ if (/^\d+$/.test(trimmed)) {
79
+ return trimmed;
80
+ }
80
81
  const ok = await checkZcaInstalled();
81
- if (!ok) return null;
82
+ if (!ok) {
83
+ return null;
84
+ }
82
85
  const result = await runZca(["friend", "find", trimmed], {
83
86
  profile: resolved.profile,
84
87
  timeout: 15000,
85
88
  });
86
- if (!result.ok) return null;
89
+ if (!result.ok) {
90
+ return null;
91
+ }
87
92
  const parsed = parseJsonOutput<ZcaFriend[]>(result.stdout);
88
93
  const rows = Array.isArray(parsed) ? parsed : [];
89
94
  const match = rows[0];
90
- if (!match?.userId) return null;
95
+ if (!match?.userId) {
96
+ return null;
97
+ }
91
98
  if (rows.length > 1) {
92
99
  await prompter.note(
93
100
  `Multiple matches for "${trimmed}", using ${match.displayName ?? match.userId}.`,
@@ -142,9 +149,9 @@ async function promptZalouserAllowFrom(params: {
142
149
  ...cfg.channels?.zalouser,
143
150
  enabled: true,
144
151
  accounts: {
145
- ...(cfg.channels?.zalouser?.accounts ?? {}),
152
+ ...cfg.channels?.zalouser?.accounts,
146
153
  [accountId]: {
147
- ...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
154
+ ...cfg.channels?.zalouser?.accounts?.[accountId],
148
155
  enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
149
156
  dmPolicy: "allowlist",
150
157
  allowFrom: unique,
@@ -182,9 +189,9 @@ function setZalouserGroupPolicy(
182
189
  ...cfg.channels?.zalouser,
183
190
  enabled: true,
184
191
  accounts: {
185
- ...(cfg.channels?.zalouser?.accounts ?? {}),
192
+ ...cfg.channels?.zalouser?.accounts,
186
193
  [accountId]: {
187
- ...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
194
+ ...cfg.channels?.zalouser?.accounts?.[accountId],
188
195
  enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
189
196
  groupPolicy,
190
197
  },
@@ -221,9 +228,9 @@ function setZalouserGroupAllowlist(
221
228
  ...cfg.channels?.zalouser,
222
229
  enabled: true,
223
230
  accounts: {
224
- ...(cfg.channels?.zalouser?.accounts ?? {}),
231
+ ...cfg.channels?.zalouser?.accounts,
225
232
  [accountId]: {
226
- ...(cfg.channels?.zalouser?.accounts?.[accountId] ?? {}),
233
+ ...cfg.channels?.zalouser?.accounts?.[accountId],
227
234
  enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
228
235
  groups,
229
236
  },
@@ -239,15 +246,22 @@ async function resolveZalouserGroups(params: {
239
246
  entries: string[];
240
247
  }): Promise<Array<{ input: string; resolved: boolean; id?: string }>> {
241
248
  const account = resolveZalouserAccountSync({ cfg: params.cfg, accountId: params.accountId });
242
- const result = await runZca(["group", "list", "-j"], { profile: account.profile, timeout: 15000 });
243
- if (!result.ok) throw new Error(result.stderr || "Failed to list groups");
244
- const groups = (parseJsonOutput<ZcaGroup[]>(result.stdout) ?? []).filter(
245
- (group) => Boolean(group.groupId),
249
+ const result = await runZca(["group", "list", "-j"], {
250
+ profile: account.profile,
251
+ timeout: 15000,
252
+ });
253
+ if (!result.ok) {
254
+ throw new Error(result.stderr || "Failed to list groups");
255
+ }
256
+ const groups = (parseJsonOutput<ZcaGroup[]>(result.stdout) ?? []).filter((group) =>
257
+ Boolean(group.groupId),
246
258
  );
247
259
  const byName = new Map<string, ZcaGroup[]>();
248
260
  for (const group of groups) {
249
261
  const name = group.name?.trim().toLowerCase();
250
- if (!name) continue;
262
+ if (!name) {
263
+ continue;
264
+ }
251
265
  const list = byName.get(name) ?? [];
252
266
  list.push(group);
253
267
  byName.set(name, list);
@@ -255,8 +269,12 @@ async function resolveZalouserGroups(params: {
255
269
 
256
270
  return params.entries.map((input) => {
257
271
  const trimmed = input.trim();
258
- if (!trimmed) return { input, resolved: false };
259
- if (/^\d+$/.test(trimmed)) return { input, resolved: true, id: trimmed };
272
+ if (!trimmed) {
273
+ return { input, resolved: false };
274
+ }
275
+ if (/^\d+$/.test(trimmed)) {
276
+ return { input, resolved: true, id: trimmed };
277
+ }
260
278
  const matches = byName.get(trimmed.toLowerCase()) ?? [];
261
279
  const match = matches[0];
262
280
  return match?.groupId
@@ -270,15 +288,15 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
270
288
  channel,
271
289
  policyKey: "channels.zalouser.dmPolicy",
272
290
  allowFromKey: "channels.zalouser.allowFrom",
273
- getCurrent: (cfg) => ((cfg as OpenClawConfig).channels?.zalouser?.dmPolicy ?? "pairing") as "pairing",
274
- setPolicy: (cfg, policy) => setZalouserDmPolicy(cfg as OpenClawConfig, policy),
291
+ getCurrent: (cfg) => (cfg.channels?.zalouser?.dmPolicy ?? "pairing") as "pairing",
292
+ setPolicy: (cfg, policy) => setZalouserDmPolicy(cfg, policy),
275
293
  promptAllowFrom: async ({ cfg, prompter, accountId }) => {
276
294
  const id =
277
295
  accountId && normalizeAccountId(accountId)
278
- ? normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID
279
- : resolveDefaultZalouserAccountId(cfg as OpenClawConfig);
296
+ ? (normalizeAccountId(accountId) ?? DEFAULT_ACCOUNT_ID)
297
+ : resolveDefaultZalouserAccountId(cfg);
280
298
  return promptZalouserAllowFrom({
281
- cfg: cfg as OpenClawConfig,
299
+ cfg: cfg,
282
300
  prompter,
283
301
  accountId: id,
284
302
  });
@@ -289,10 +307,10 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
289
307
  channel,
290
308
  dmPolicy,
291
309
  getStatus: async ({ cfg }) => {
292
- const ids = listZalouserAccountIds(cfg as OpenClawConfig);
310
+ const ids = listZalouserAccountIds(cfg);
293
311
  let configured = false;
294
312
  for (const accountId of ids) {
295
- const account = resolveZalouserAccountSync({ cfg: cfg as OpenClawConfig, accountId });
313
+ const account = resolveZalouserAccountSync({ cfg: cfg, accountId });
296
314
  const isAuth = await checkZcaAuthenticated(account.profile);
297
315
  if (isAuth) {
298
316
  configured = true;
@@ -307,7 +325,13 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
307
325
  quickstartScore: configured ? 1 : 15,
308
326
  };
309
327
  },
310
- configure: async ({ cfg, prompter, accountOverrides, shouldPromptAccountIds, forceAllowFrom }) => {
328
+ configure: async ({
329
+ cfg,
330
+ prompter,
331
+ accountOverrides,
332
+ shouldPromptAccountIds,
333
+ forceAllowFrom,
334
+ }) => {
311
335
  // Check zca is installed
312
336
  const zcaInstalled = await checkZcaInstalled();
313
337
  if (!zcaInstalled) {
@@ -324,14 +348,12 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
324
348
  }
325
349
 
326
350
  const zalouserOverride = accountOverrides.zalouser?.trim();
327
- const defaultAccountId = resolveDefaultZalouserAccountId(cfg as OpenClawConfig);
328
- let accountId = zalouserOverride
329
- ? normalizeAccountId(zalouserOverride)
330
- : defaultAccountId;
351
+ const defaultAccountId = resolveDefaultZalouserAccountId(cfg);
352
+ let accountId = zalouserOverride ? normalizeAccountId(zalouserOverride) : defaultAccountId;
331
353
 
332
354
  if (shouldPromptAccountIds && !zalouserOverride) {
333
355
  accountId = await promptAccountId({
334
- cfg: cfg as OpenClawConfig,
356
+ cfg: cfg,
335
357
  prompter,
336
358
  label: "Zalo Personal",
337
359
  currentId: accountId,
@@ -340,7 +362,7 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
340
362
  });
341
363
  }
342
364
 
343
- let next = cfg as OpenClawConfig;
365
+ let next = cfg;
344
366
  const account = resolveZalouserAccountSync({ cfg: next, accountId });
345
367
  const alreadyAuthenticated = await checkZcaAuthenticated(account.profile);
346
368
 
@@ -364,10 +386,7 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
364
386
  });
365
387
 
366
388
  if (!result.ok) {
367
- await prompter.note(
368
- `Login failed: ${result.stderr || "Unknown error"}`,
369
- "Error",
370
- );
389
+ await prompter.note(`Login failed: ${result.stderr || "Unknown error"}`, "Error");
371
390
  } else {
372
391
  const isNowAuth = await checkZcaAuthenticated(account.profile);
373
392
  if (isNowAuth) {
@@ -408,9 +427,9 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
408
427
  ...next.channels?.zalouser,
409
428
  enabled: true,
410
429
  accounts: {
411
- ...(next.channels?.zalouser?.accounts ?? {}),
430
+ ...next.channels?.zalouser?.accounts,
412
431
  [accountId]: {
413
- ...(next.channels?.zalouser?.accounts?.[accountId] ?? {}),
432
+ ...next.channels?.zalouser?.accounts?.[accountId],
414
433
  enabled: true,
415
434
  profile: account.profile,
416
435
  },
@@ -454,10 +473,7 @@ export const zalouserOnboardingAdapter: ChannelOnboardingAdapter = {
454
473
  const unresolved = resolved
455
474
  .filter((entry) => !entry.resolved)
456
475
  .map((entry) => entry.input);
457
- keys = [
458
- ...resolvedIds,
459
- ...unresolved.map((entry) => entry.trim()).filter(Boolean),
460
- ];
476
+ keys = [...resolvedIds, ...unresolved.map((entry) => entry.trim()).filter(Boolean)];
461
477
  if (resolvedIds.length > 0 || unresolved.length > 0) {
462
478
  await prompter.note(
463
479
  [
package/src/probe.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { runZca, parseJsonOutput } from "./zca.js";
2
1
  import type { ZcaUserInfo } from "./types.js";
2
+ import { runZca, parseJsonOutput } from "./zca.js";
3
3
 
4
4
  export interface ZalouserProbeResult {
5
5
  ok: boolean;
package/src/send.ts CHANGED
@@ -34,7 +34,9 @@ export async function sendMessageZalouser(
34
34
 
35
35
  // Send text message
36
36
  const args = ["msg", "send", threadId.trim(), text.slice(0, 2000)];
37
- if (options.isGroup) args.push("-g");
37
+ if (options.isGroup) {
38
+ args.push("-g");
39
+ }
38
40
 
39
41
  try {
40
42
  const result = await runZca(args, { profile });
@@ -79,7 +81,9 @@ async function sendMediaZalouser(
79
81
  if (options.caption) {
80
82
  args.push("-m", options.caption.slice(0, 2000));
81
83
  }
82
- if (options.isGroup) args.push("-g");
84
+ if (options.isGroup) {
85
+ args.push("-g");
86
+ }
83
87
 
84
88
  try {
85
89
  const result = await runZca(args, { profile });
@@ -104,7 +108,9 @@ export async function sendImageZalouser(
104
108
  if (options.caption) {
105
109
  args.push("-m", options.caption.slice(0, 2000));
106
110
  }
107
- if (options.isGroup) args.push("-g");
111
+ if (options.isGroup) {
112
+ args.push("-g");
113
+ }
108
114
 
109
115
  try {
110
116
  const result = await runZca(args, { profile });
@@ -124,7 +130,9 @@ export async function sendLinkZalouser(
124
130
  ): Promise<ZalouserSendResult> {
125
131
  const profile = options.profile || process.env.ZCA_PROFILE || "default";
126
132
  const args = ["msg", "link", threadId.trim(), url.trim()];
127
- if (options.isGroup) args.push("-g");
133
+ if (options.isGroup) {
134
+ args.push("-g");
135
+ }
128
136
 
129
137
  try {
130
138
  const result = await runZca(args, { profile });
@@ -140,7 +148,9 @@ export async function sendLinkZalouser(
140
148
  function extractMessageId(stdout: string): string | undefined {
141
149
  // Try to extract message ID from output
142
150
  const match = stdout.match(/message[_\s]?id[:\s]+(\S+)/i);
143
- if (match) return match[1];
151
+ if (match) {
152
+ return match[1];
153
+ }
144
154
  // Return first word if it looks like an ID
145
155
  const firstWord = stdout.trim().split(/\s+/)[0];
146
156
  if (firstWord && /^[a-zA-Z0-9_-]+$/.test(firstWord)) {
@@ -1,5 +1,4 @@
1
1
  import { describe, expect, it } from "vitest";
2
-
3
2
  import { collectZalouserStatusIssues } from "./status-issues.js";
4
3
 
5
4
  describe("collectZalouserStatusIssues", () => {
@@ -15,7 +15,9 @@ const asString = (value: unknown): string | undefined =>
15
15
  typeof value === "string" ? value : typeof value === "number" ? String(value) : undefined;
16
16
 
17
17
  function readZalouserAccountStatus(value: ChannelAccountSnapshot): ZalouserAccountStatus | null {
18
- if (!isRecord(value)) return null;
18
+ if (!isRecord(value)) {
19
+ return null;
20
+ }
19
21
  return {
20
22
  accountId: value.accountId,
21
23
  enabled: value.enabled,
@@ -26,7 +28,9 @@ function readZalouserAccountStatus(value: ChannelAccountSnapshot): ZalouserAccou
26
28
  }
27
29
 
28
30
  function isMissingZca(lastError?: string): boolean {
29
- if (!lastError) return false;
31
+ if (!lastError) {
32
+ return false;
33
+ }
30
34
  const lower = lastError.toLowerCase();
31
35
  return lower.includes("zca") && (lower.includes("not found") || lower.includes("enoent"));
32
36
  }
@@ -37,10 +41,14 @@ export function collectZalouserStatusIssues(
37
41
  const issues: ChannelStatusIssue[] = [];
38
42
  for (const entry of accounts) {
39
43
  const account = readZalouserAccountStatus(entry);
40
- if (!account) continue;
44
+ if (!account) {
45
+ continue;
46
+ }
41
47
  const accountId = asString(account.accountId) ?? "default";
42
48
  const enabled = account.enabled !== false;
43
- if (!enabled) continue;
49
+ if (!enabled) {
50
+ continue;
51
+ }
44
52
 
45
53
  const configured = account.configured === true;
46
54
  const lastError = asString(account.lastError)?.trim();
package/src/tool.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Type } from "@sinclair/typebox";
2
-
3
2
  import { runZca, parseJsonOutput } from "./zca.js";
4
3
 
5
4
  const ACTIONS = ["send", "image", "link", "friends", "groups", "me", "status"] as const;
@@ -16,17 +15,18 @@ function stringEnum<T extends readonly string[]>(
16
15
  }
17
16
 
18
17
  // Tool schema - avoiding Type.Union per tool schema guardrails
19
- export const ZalouserToolSchema = Type.Object({
20
- action: stringEnum(ACTIONS, { description: `Action to perform: ${ACTIONS.join(", ")}` }),
21
- threadId: Type.Optional(
22
- Type.String({ description: "Thread ID for messaging" }),
23
- ),
24
- message: Type.Optional(Type.String({ description: "Message text" })),
25
- isGroup: Type.Optional(Type.Boolean({ description: "Is group chat" })),
26
- profile: Type.Optional(Type.String({ description: "Profile name" })),
27
- query: Type.Optional(Type.String({ description: "Search query" })),
28
- url: Type.Optional(Type.String({ description: "URL for media/link" })),
29
- }, { additionalProperties: false });
18
+ export const ZalouserToolSchema = Type.Object(
19
+ {
20
+ action: stringEnum(ACTIONS, { description: `Action to perform: ${ACTIONS.join(", ")}` }),
21
+ threadId: Type.Optional(Type.String({ description: "Thread ID for messaging" })),
22
+ message: Type.Optional(Type.String({ description: "Message text" })),
23
+ isGroup: Type.Optional(Type.Boolean({ description: "Is group chat" })),
24
+ profile: Type.Optional(Type.String({ description: "Profile name" })),
25
+ query: Type.Optional(Type.String({ description: "Search query" })),
26
+ url: Type.Optional(Type.String({ description: "URL for media/link" })),
27
+ },
28
+ { additionalProperties: false },
29
+ );
30
30
 
31
31
  type ToolParams = {
32
32
  action: (typeof ACTIONS)[number];
@@ -61,7 +61,9 @@ export async function executeZalouserTool(
61
61
  throw new Error("threadId and message required for send action");
62
62
  }
63
63
  const args = ["msg", "send", params.threadId, params.message];
64
- if (params.isGroup) args.push("-g");
64
+ if (params.isGroup) {
65
+ args.push("-g");
66
+ }
65
67
  const result = await runZca(args, { profile: params.profile });
66
68
  if (!result.ok) {
67
69
  throw new Error(result.stderr || "Failed to send message");
@@ -77,8 +79,12 @@ export async function executeZalouserTool(
77
79
  throw new Error("url required for image action");
78
80
  }
79
81
  const args = ["msg", "image", params.threadId, "-u", params.url];
80
- if (params.message) args.push("-m", params.message);
81
- if (params.isGroup) args.push("-g");
82
+ if (params.message) {
83
+ args.push("-m", params.message);
84
+ }
85
+ if (params.isGroup) {
86
+ args.push("-g");
87
+ }
82
88
  const result = await runZca(args, { profile: params.profile });
83
89
  if (!result.ok) {
84
90
  throw new Error(result.stderr || "Failed to send image");
@@ -91,7 +97,9 @@ export async function executeZalouserTool(
91
97
  throw new Error("threadId and url required for link action");
92
98
  }
93
99
  const args = ["msg", "link", params.threadId, params.url];
94
- if (params.isGroup) args.push("-g");
100
+ if (params.isGroup) {
101
+ args.push("-g");
102
+ }
95
103
  const result = await runZca(args, { profile: params.profile });
96
104
  if (!result.ok) {
97
105
  throw new Error(result.stderr || "Failed to send link");
@@ -100,9 +108,7 @@ export async function executeZalouserTool(
100
108
  }
101
109
 
102
110
  case "friends": {
103
- const args = params.query
104
- ? ["friend", "find", params.query]
105
- : ["friend", "list", "-j"];
111
+ const args = params.query ? ["friend", "find", params.query] : ["friend", "list", "-j"];
106
112
  const result = await runZca(args, { profile: params.profile });
107
113
  if (!result.ok) {
108
114
  throw new Error(result.stderr || "Failed to get friends");
@@ -143,10 +149,12 @@ export async function executeZalouserTool(
143
149
  });
144
150
  }
145
151
 
146
- default:
152
+ default: {
153
+ params.action satisfies never;
147
154
  throw new Error(
148
- `Unknown action: ${params.action}. Valid actions: send, image, link, friends, groups, me, status`,
155
+ `Unknown action: ${String(params.action)}. Valid actions: send, image, link, friends, groups, me, status`,
149
156
  );
157
+ }
150
158
  }
151
159
  } catch (err) {
152
160
  return json({
package/src/types.ts CHANGED
@@ -75,7 +75,10 @@ export type ZalouserAccountConfig = {
75
75
  dmPolicy?: "pairing" | "allowlist" | "open" | "disabled";
76
76
  allowFrom?: Array<string | number>;
77
77
  groupPolicy?: "open" | "allowlist" | "disabled";
78
- groups?: Record<string, { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }>;
78
+ groups?: Record<
79
+ string,
80
+ { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }
81
+ >;
79
82
  messagePrefix?: string;
80
83
  };
81
84
 
@@ -87,7 +90,10 @@ export type ZalouserConfig = {
87
90
  dmPolicy?: "pairing" | "allowlist" | "open" | "disabled";
88
91
  allowFrom?: Array<string | number>;
89
92
  groupPolicy?: "open" | "allowlist" | "disabled";
90
- groups?: Record<string, { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }>;
93
+ groups?: Record<
94
+ string,
95
+ { allow?: boolean; enabled?: boolean; tools?: { allow?: string[]; deny?: string[] } }
96
+ >;
91
97
  messagePrefix?: string;
92
98
  accounts?: Record<string, ZalouserAccountConfig>;
93
99
  };
package/src/zca.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { spawn, type SpawnOptions } from "node:child_process";
2
-
3
2
  import type { ZcaResult, ZcaRunOptions } from "./types.js";
4
3
 
5
4
  const ZCA_BINARY = "zca";
@@ -16,10 +15,7 @@ function buildArgs(args: string[], options?: ZcaRunOptions): string[] {
16
15
  return result;
17
16
  }
18
17
 
19
- export async function runZca(
20
- args: string[],
21
- options?: ZcaRunOptions,
22
- ): Promise<ZcaResult> {
18
+ export async function runZca(args: string[], options?: ZcaRunOptions): Promise<ZcaResult> {
23
19
  const fullArgs = buildArgs(args, options);
24
20
  const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
25
21
 
@@ -79,10 +75,7 @@ export async function runZca(
79
75
  });
80
76
  }
81
77
 
82
- export function runZcaInteractive(
83
- args: string[],
84
- options?: ZcaRunOptions,
85
- ): Promise<ZcaResult> {
78
+ export function runZcaInteractive(args: string[], options?: ZcaRunOptions): Promise<ZcaResult> {
86
79
  const fullArgs = buildArgs(args, options);
87
80
 
88
81
  return new Promise((resolve) => {
@@ -115,6 +108,7 @@ export function runZcaInteractive(
115
108
  }
116
109
 
117
110
  function stripAnsi(str: string): string {
111
+ // oxlint-disable-next-line no-control-regex
118
112
  return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
119
113
  }
120
114