@openclaw/zalouser 2026.5.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 (95) hide show
  1. package/dist/accounts-C00IMUgd.js +63 -0
  2. package/dist/accounts.runtime-uG7S8cXT.js +2 -0
  3. package/dist/api-BRwdUWuS.js +139 -0
  4. package/dist/api.js +7 -0
  5. package/dist/channel-ou_w_2j-.js +484 -0
  6. package/dist/channel-plugin-api.js +2 -0
  7. package/dist/channel.runtime-C9WxiAiR.js +25 -0
  8. package/dist/channel.setup-CiDeBFrn.js +10 -0
  9. package/dist/contract-api.js +3 -0
  10. package/dist/doctor-contract-DgqHp8E2.js +128 -0
  11. package/dist/doctor-contract-api.js +2 -0
  12. package/dist/index.js +27 -0
  13. package/dist/monitor-Cg7K_s_s.js +705 -0
  14. package/dist/runtime-QNU7vLgI.js +106 -0
  15. package/dist/runtime-api.js +22 -0
  16. package/dist/secret-contract-api.js +5 -0
  17. package/dist/security-audit-BZLhil-V.js +34 -0
  18. package/dist/send-BsmySxe3.js +534 -0
  19. package/dist/session-route-C0-Xr8bt.js +92 -0
  20. package/dist/setup-core-CqipqY98.js +40 -0
  21. package/dist/setup-entry.js +11 -0
  22. package/dist/setup-plugin-api.js +2 -0
  23. package/dist/setup-surface-NCOuKu-l.js +359 -0
  24. package/dist/shared-DSy8aIUx.js +120 -0
  25. package/dist/test-api.js +5 -0
  26. package/dist/zalo-js-CHCUlY3c.js +1279 -0
  27. package/package.json +15 -6
  28. package/api.ts +0 -9
  29. package/channel-plugin-api.ts +0 -3
  30. package/contract-api.ts +0 -2
  31. package/doctor-contract-api.ts +0 -1
  32. package/index.ts +0 -34
  33. package/runtime-api.ts +0 -67
  34. package/secret-contract-api.ts +0 -4
  35. package/setup-entry.ts +0 -9
  36. package/setup-plugin-api.ts +0 -2
  37. package/src/accounts.runtime.ts +0 -1
  38. package/src/accounts.test-mocks.ts +0 -14
  39. package/src/accounts.test.ts +0 -266
  40. package/src/accounts.ts +0 -131
  41. package/src/channel-api.ts +0 -20
  42. package/src/channel.adapters.ts +0 -391
  43. package/src/channel.directory.test.ts +0 -59
  44. package/src/channel.runtime.ts +0 -12
  45. package/src/channel.sendpayload.test.ts +0 -172
  46. package/src/channel.setup.test.ts +0 -33
  47. package/src/channel.setup.ts +0 -12
  48. package/src/channel.test.ts +0 -377
  49. package/src/channel.ts +0 -219
  50. package/src/config-schema.ts +0 -33
  51. package/src/directory.ts +0 -54
  52. package/src/doctor-contract.ts +0 -156
  53. package/src/doctor.test.ts +0 -77
  54. package/src/doctor.ts +0 -37
  55. package/src/group-policy.test.ts +0 -61
  56. package/src/group-policy.ts +0 -83
  57. package/src/message-sid.test.ts +0 -66
  58. package/src/message-sid.ts +0 -80
  59. package/src/monitor.account-scope.test.ts +0 -107
  60. package/src/monitor.group-gating.test.ts +0 -816
  61. package/src/monitor.send-mocks.ts +0 -20
  62. package/src/monitor.ts +0 -1044
  63. package/src/probe.test.ts +0 -60
  64. package/src/probe.ts +0 -35
  65. package/src/qr-temp-file.ts +0 -22
  66. package/src/reaction.test.ts +0 -19
  67. package/src/reaction.ts +0 -32
  68. package/src/runtime.ts +0 -9
  69. package/src/security-audit.test.ts +0 -80
  70. package/src/security-audit.ts +0 -71
  71. package/src/send.test.ts +0 -395
  72. package/src/send.ts +0 -272
  73. package/src/session-route.ts +0 -121
  74. package/src/setup-core.ts +0 -33
  75. package/src/setup-surface.test.ts +0 -363
  76. package/src/setup-surface.ts +0 -470
  77. package/src/setup-test-helpers.ts +0 -42
  78. package/src/shared.ts +0 -92
  79. package/src/status-issues.test.ts +0 -31
  80. package/src/status-issues.ts +0 -58
  81. package/src/test-helpers.ts +0 -26
  82. package/src/text-styles.test.ts +0 -203
  83. package/src/text-styles.ts +0 -540
  84. package/src/tool.test.ts +0 -212
  85. package/src/tool.ts +0 -210
  86. package/src/types.ts +0 -125
  87. package/src/zalo-js.credentials.test.ts +0 -465
  88. package/src/zalo-js.test-mocks.ts +0 -89
  89. package/src/zalo-js.ts +0 -1911
  90. package/src/zca-client.test.ts +0 -24
  91. package/src/zca-client.ts +0 -259
  92. package/src/zca-constants.ts +0 -55
  93. package/src/zca-js-exports.d.ts +0 -22
  94. package/test-api.ts +0 -21
  95. package/tsconfig.json +0 -16
@@ -1,121 +0,0 @@
1
- import {
2
- buildChannelOutboundSessionRoute,
3
- type ChannelOutboundSessionRouteParams,
4
- } from "openclaw/plugin-sdk/core";
5
- import {
6
- normalizeLowercaseStringOrEmpty,
7
- normalizeOptionalLowercaseString,
8
- } from "openclaw/plugin-sdk/text-runtime";
9
-
10
- function stripZalouserTargetPrefix(raw: string): string {
11
- return raw
12
- .trim()
13
- .replace(/^(zalouser|zlu):/i, "")
14
- .trim();
15
- }
16
-
17
- export function normalizeZalouserTarget(raw: string): string | undefined {
18
- const trimmed = stripZalouserTargetPrefix(raw);
19
- if (!trimmed) {
20
- return undefined;
21
- }
22
-
23
- const lower = normalizeLowercaseStringOrEmpty(trimmed);
24
- if (lower.startsWith("group:")) {
25
- const id = trimmed.slice("group:".length).trim();
26
- return id ? `group:${id}` : undefined;
27
- }
28
- if (lower.startsWith("g:")) {
29
- const id = trimmed.slice("g:".length).trim();
30
- return id ? `group:${id}` : undefined;
31
- }
32
- if (lower.startsWith("user:")) {
33
- const id = trimmed.slice("user:".length).trim();
34
- return id ? `user:${id}` : undefined;
35
- }
36
- if (lower.startsWith("dm:")) {
37
- const id = trimmed.slice("dm:".length).trim();
38
- return id ? `user:${id}` : undefined;
39
- }
40
- if (lower.startsWith("u:")) {
41
- const id = trimmed.slice("u:".length).trim();
42
- return id ? `user:${id}` : undefined;
43
- }
44
- if (/^g-\S+$/i.test(trimmed)) {
45
- return `group:${trimmed}`;
46
- }
47
- if (/^u-\S+$/i.test(trimmed)) {
48
- return `user:${trimmed}`;
49
- }
50
-
51
- return trimmed;
52
- }
53
-
54
- export function parseZalouserOutboundTarget(raw: string): {
55
- threadId: string;
56
- isGroup: boolean;
57
- } {
58
- const normalized = normalizeZalouserTarget(raw);
59
- if (!normalized) {
60
- throw new Error("Zalouser target is required");
61
- }
62
- const lowered = normalizeLowercaseStringOrEmpty(normalized);
63
- if (lowered.startsWith("group:")) {
64
- const threadId = normalized.slice("group:".length).trim();
65
- if (!threadId) {
66
- throw new Error("Zalouser group target is missing group id");
67
- }
68
- return { threadId, isGroup: true };
69
- }
70
- if (lowered.startsWith("user:")) {
71
- const threadId = normalized.slice("user:".length).trim();
72
- if (!threadId) {
73
- throw new Error("Zalouser user target is missing user id");
74
- }
75
- return { threadId, isGroup: false };
76
- }
77
- // Backward-compatible fallback for bare IDs.
78
- // Group sends should use explicit `group:<id>` targets.
79
- return { threadId: normalized, isGroup: false };
80
- }
81
-
82
- export function parseZalouserDirectoryGroupId(raw: string): string {
83
- const normalized = normalizeZalouserTarget(raw);
84
- if (!normalized) {
85
- throw new Error("Zalouser group target is required");
86
- }
87
- const lowered = normalizeLowercaseStringOrEmpty(normalized);
88
- if (lowered.startsWith("group:")) {
89
- const groupId = normalized.slice("group:".length).trim();
90
- if (!groupId) {
91
- throw new Error("Zalouser group target is missing group id");
92
- }
93
- return groupId;
94
- }
95
- if (lowered.startsWith("user:")) {
96
- throw new Error("Zalouser group members lookup requires a group target (group:<id>)");
97
- }
98
- return normalized;
99
- }
100
-
101
- export function resolveZalouserOutboundSessionRoute(params: ChannelOutboundSessionRouteParams) {
102
- const normalized = normalizeZalouserTarget(params.target);
103
- if (!normalized) {
104
- return null;
105
- }
106
- const isGroup = (normalizeOptionalLowercaseString(normalized) ?? "").startsWith("group:");
107
- const peerId = normalized.replace(/^(group|user):/i, "").trim();
108
- return buildChannelOutboundSessionRoute({
109
- cfg: params.cfg,
110
- agentId: params.agentId,
111
- channel: "zalouser",
112
- accountId: params.accountId,
113
- peer: {
114
- kind: isGroup ? "group" : "direct",
115
- id: peerId,
116
- },
117
- chatType: isGroup ? "group" : "direct",
118
- from: isGroup ? `zalouser:group:${peerId}` : `zalouser:${peerId}`,
119
- to: `zalouser:${peerId}`,
120
- });
121
- }
package/src/setup-core.ts DELETED
@@ -1,33 +0,0 @@
1
- import {
2
- createDelegatedSetupWizardProxy,
3
- createPatchedAccountSetupAdapter,
4
- type ChannelSetupWizard,
5
- } from "openclaw/plugin-sdk/setup-runtime";
6
-
7
- const channel = "zalouser" as const;
8
-
9
- export const zalouserSetupAdapter = createPatchedAccountSetupAdapter({
10
- channelKey: channel,
11
- validateInput: () => null,
12
- buildPatch: () => ({}),
13
- });
14
-
15
- export function createZalouserSetupWizardProxy(
16
- loadWizard: () => Promise<ChannelSetupWizard>,
17
- ): ChannelSetupWizard {
18
- return createDelegatedSetupWizardProxy({
19
- channel,
20
- loadWizard,
21
- status: {
22
- configuredLabel: "logged in",
23
- unconfiguredLabel: "needs QR login",
24
- configuredHint: "recommended · logged in",
25
- unconfiguredHint: "recommended · QR login",
26
- configuredScore: 1,
27
- unconfiguredScore: 15,
28
- },
29
- credentials: [],
30
- delegatePrepare: true,
31
- delegateFinalize: true,
32
- });
33
- }
@@ -1,363 +0,0 @@
1
- import {
2
- createPluginSetupWizardConfigure,
3
- createTestWizardPrompter,
4
- runSetupWizardConfigure,
5
- } from "openclaw/plugin-sdk/plugin-test-runtime";
6
- import { describe, expect, it, vi } from "vitest";
7
- import type { OpenClawConfig } from "../runtime-api.js";
8
- import "./zalo-js.test-mocks.js";
9
- import { zalouserSetupWizard } from "./setup-surface.js";
10
- import { zalouserSetupPlugin } from "./setup-test-helpers.js";
11
-
12
- const zalouserConfigure = createPluginSetupWizardConfigure(zalouserSetupPlugin);
13
-
14
- async function runSetup(params: {
15
- cfg?: OpenClawConfig;
16
- prompter: ReturnType<typeof createTestWizardPrompter>;
17
- options?: Record<string, unknown>;
18
- forceAllowFrom?: boolean;
19
- }) {
20
- return await runSetupWizardConfigure({
21
- configure: zalouserConfigure,
22
- cfg: params.cfg,
23
- prompter: params.prompter,
24
- options: params.options,
25
- forceAllowFrom: params.forceAllowFrom,
26
- });
27
- }
28
-
29
- describe("zalouser setup wizard", () => {
30
- function expectEnabledDefaultSetup(
31
- result: Awaited<ReturnType<typeof runSetup>>,
32
- dmPolicy?: "pairing" | "allowlist",
33
- ) {
34
- expect(result.accountId).toBe("default");
35
- expect(result.cfg.channels?.zalouser?.enabled).toBe(true);
36
- expect(result.cfg.plugins?.entries?.zalouser?.enabled).toBe(true);
37
- if (dmPolicy) {
38
- expect(result.cfg.channels?.zalouser?.dmPolicy).toBe(dmPolicy);
39
- }
40
- }
41
-
42
- function createQuickstartPrompter(params?: {
43
- note?: ReturnType<typeof createTestWizardPrompter>["note"];
44
- seen?: string[];
45
- dmPolicy?: "pairing" | "allowlist";
46
- groupAccess?: boolean;
47
- groupPolicy?: "allowlist";
48
- textByMessage?: Record<string, string>;
49
- }) {
50
- const select = vi.fn(
51
- async ({ message, options }: { message: string; options: Array<{ value: string }> }) => {
52
- const first = options[0];
53
- if (!first) {
54
- throw new Error("no options");
55
- }
56
- params?.seen?.push(message);
57
- if (message === "Zalo Personal DM policy" && params?.dmPolicy) {
58
- return params.dmPolicy;
59
- }
60
- if (message === "Zalo groups access" && params?.groupPolicy) {
61
- return params.groupPolicy;
62
- }
63
- return first.value;
64
- },
65
- ) as ReturnType<typeof createTestWizardPrompter>["select"];
66
- const text = vi.fn(
67
- async ({ message }: { message: string }) => params?.textByMessage?.[message] ?? "",
68
- ) as ReturnType<typeof createTestWizardPrompter>["text"];
69
- return createTestWizardPrompter({
70
- ...(params?.note ? { note: params.note } : {}),
71
- confirm: vi.fn(async ({ message }: { message: string }) => {
72
- params?.seen?.push(message);
73
- if (message === "Login via QR code now?") {
74
- return false;
75
- }
76
- if (message === "Configure Zalo groups access?") {
77
- return params?.groupAccess ?? false;
78
- }
79
- return false;
80
- }),
81
- select,
82
- text,
83
- });
84
- }
85
-
86
- it("enables the account without forcing QR login", async () => {
87
- const prompter = createTestWizardPrompter({
88
- confirm: vi.fn(async ({ message }: { message: string }) => {
89
- if (message === "Login via QR code now?") {
90
- return false;
91
- }
92
- if (message === "Configure Zalo groups access?") {
93
- return false;
94
- }
95
- return false;
96
- }),
97
- });
98
-
99
- const result = await runSetup({ prompter });
100
-
101
- expect(result.accountId).toBe("default");
102
- expect(result.cfg.channels?.zalouser?.enabled).toBe(true);
103
- expect(result.cfg.plugins?.entries?.zalouser?.enabled).toBe(true);
104
- });
105
-
106
- it("prompts DM policy before group access in quickstart", async () => {
107
- const seen: string[] = [];
108
- const prompter = createQuickstartPrompter({ seen, dmPolicy: "pairing" });
109
-
110
- const result = await runSetup({
111
- prompter,
112
- options: { quickstartDefaults: true },
113
- });
114
-
115
- expectEnabledDefaultSetup(result, "pairing");
116
- expect(seen.indexOf("Zalo Personal DM policy")).toBeGreaterThanOrEqual(0);
117
- expect(seen.indexOf("Configure Zalo groups access?")).toBeGreaterThanOrEqual(0);
118
- expect(seen.indexOf("Zalo Personal DM policy")).toBeLessThan(
119
- seen.indexOf("Configure Zalo groups access?"),
120
- );
121
- });
122
-
123
- it("allows an empty quickstart DM allowlist with a warning", async () => {
124
- const note = vi.fn(async (_message: string, _title?: string) => {});
125
- const prompter = createQuickstartPrompter({
126
- note,
127
- dmPolicy: "allowlist",
128
- textByMessage: {
129
- "Zalouser allowFrom (name or user id)": "",
130
- },
131
- });
132
-
133
- const result = await runSetup({
134
- prompter,
135
- options: { quickstartDefaults: true },
136
- });
137
-
138
- expectEnabledDefaultSetup(result, "allowlist");
139
- expect(result.cfg.channels?.zalouser?.allowFrom).toEqual([]);
140
- expect(
141
- note.mock.calls.some(([message]) => message.includes("No DM allowlist entries added yet.")),
142
- ).toBe(true);
143
- });
144
-
145
- it("allows an empty group allowlist with a warning", async () => {
146
- const note = vi.fn(async (_message: string, _title?: string) => {});
147
- const prompter = createQuickstartPrompter({
148
- note,
149
- groupAccess: true,
150
- groupPolicy: "allowlist",
151
- textByMessage: {
152
- "Zalo groups allowlist (comma-separated)": "",
153
- },
154
- });
155
-
156
- const result = await runSetup({ prompter });
157
-
158
- expect(result.cfg.channels?.zalouser?.groupPolicy).toBe("allowlist");
159
- expect(result.cfg.channels?.zalouser?.groups).toEqual({});
160
- expect(
161
- note.mock.calls.some(([message]) =>
162
- message.includes("No group allowlist entries added yet."),
163
- ),
164
- ).toBe(true);
165
- });
166
-
167
- it("writes canonical enabled entries for configured groups", async () => {
168
- const prompter = createQuickstartPrompter({
169
- groupAccess: true,
170
- groupPolicy: "allowlist",
171
- textByMessage: {
172
- "Zalo groups allowlist (comma-separated)": "Family, Work",
173
- },
174
- });
175
-
176
- const result = await runSetup({ prompter });
177
-
178
- expect(result.cfg.channels?.zalouser?.groups).toEqual({
179
- Family: { enabled: true, requireMention: true },
180
- Work: { enabled: true, requireMention: true },
181
- });
182
- });
183
-
184
- it("preserves non-quickstart forceAllowFrom behavior", async () => {
185
- const note = vi.fn(async (_message: string, _title?: string) => {});
186
- const seen: string[] = [];
187
- const prompter = createTestWizardPrompter({
188
- note,
189
- confirm: vi.fn(async ({ message }: { message: string }) => {
190
- seen.push(message);
191
- if (message === "Login via QR code now?") {
192
- return false;
193
- }
194
- if (message === "Configure Zalo groups access?") {
195
- return false;
196
- }
197
- return false;
198
- }),
199
- text: vi.fn(async ({ message }: { message: string }) => {
200
- seen.push(message);
201
- if (message === "Zalouser allowFrom (name or user id)") {
202
- return "";
203
- }
204
- return "";
205
- }) as ReturnType<typeof createTestWizardPrompter>["text"],
206
- });
207
-
208
- const result = await runSetup({ prompter, forceAllowFrom: true });
209
-
210
- expect(result.cfg.channels?.zalouser?.dmPolicy).toBe("allowlist");
211
- expect(result.cfg.channels?.zalouser?.allowFrom).toEqual([]);
212
- expect(seen).not.toContain("Zalo Personal DM policy");
213
- expect(seen).toContain("Zalouser allowFrom (name or user id)");
214
- expect(
215
- note.mock.calls.some(([message]) => message.includes("No DM allowlist entries added yet.")),
216
- ).toBe(true);
217
- });
218
-
219
- it("allowlists the plugin when a plugin allowlist already exists", async () => {
220
- const prompter = createTestWizardPrompter({
221
- confirm: vi.fn(async ({ message }: { message: string }) => {
222
- if (message === "Login via QR code now?") {
223
- return false;
224
- }
225
- if (message === "Configure Zalo groups access?") {
226
- return false;
227
- }
228
- return false;
229
- }),
230
- });
231
-
232
- const result = await runSetup({
233
- cfg: {
234
- plugins: {
235
- allow: ["telegram"],
236
- },
237
- } as OpenClawConfig,
238
- prompter,
239
- });
240
-
241
- expect(result.cfg.plugins?.entries?.zalouser?.enabled).toBe(true);
242
- expect(result.cfg.plugins?.allow).toEqual(["telegram", "zalouser"]);
243
- });
244
-
245
- it("reads the named-account DM policy instead of the channel root", () => {
246
- expect(
247
- zalouserSetupWizard.dmPolicy?.getCurrent(
248
- {
249
- channels: {
250
- zalouser: {
251
- dmPolicy: "disabled",
252
- accounts: {
253
- work: {
254
- profile: "work",
255
- dmPolicy: "allowlist",
256
- },
257
- },
258
- },
259
- },
260
- } as OpenClawConfig,
261
- "work",
262
- ),
263
- ).toBe("allowlist");
264
- });
265
-
266
- it("reports account-scoped config keys for named accounts", () => {
267
- expect(zalouserSetupWizard.dmPolicy?.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual(
268
- {
269
- policyKey: "channels.zalouser.accounts.work.dmPolicy",
270
- allowFromKey: "channels.zalouser.accounts.work.allowFrom",
271
- },
272
- );
273
- });
274
-
275
- it("uses configured defaultAccount for omitted DM policy account context", () => {
276
- const cfg = {
277
- channels: {
278
- zalouser: {
279
- defaultAccount: "work",
280
- dmPolicy: "disabled",
281
- allowFrom: ["123456789"],
282
- accounts: {
283
- work: {
284
- dmPolicy: "allowlist",
285
- profile: "work-profile",
286
- },
287
- },
288
- },
289
- },
290
- } as OpenClawConfig;
291
-
292
- expect(zalouserSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
293
- expect(zalouserSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
294
- policyKey: "channels.zalouser.accounts.work.dmPolicy",
295
- allowFromKey: "channels.zalouser.accounts.work.allowFrom",
296
- });
297
-
298
- const next = zalouserSetupWizard.dmPolicy?.setPolicy(cfg, "open");
299
- expect(next?.channels?.zalouser?.dmPolicy).toBe("disabled");
300
- const workAccount = next?.channels?.zalouser?.accounts?.work as
301
- | { dmPolicy?: string; allowFrom?: Array<string | number> }
302
- | undefined;
303
- expect(workAccount?.dmPolicy).toBe("open");
304
- });
305
-
306
- it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
307
- const next = zalouserSetupWizard.dmPolicy?.setPolicy(
308
- {
309
- channels: {
310
- zalouser: {
311
- allowFrom: ["123456789"],
312
- accounts: {
313
- work: {
314
- profile: "work",
315
- },
316
- },
317
- },
318
- },
319
- } as OpenClawConfig,
320
- "open",
321
- "work",
322
- );
323
-
324
- expect(next?.channels?.zalouser?.dmPolicy).toBeUndefined();
325
- const workAccount = next?.channels?.zalouser?.accounts?.work as
326
- | { dmPolicy?: string; allowFrom?: Array<string | number> }
327
- | undefined;
328
- expect(workAccount?.dmPolicy).toBe("open");
329
- expect(workAccount?.allowFrom).toEqual(["123456789", "*"]);
330
- });
331
-
332
- it("shows the account-scoped current DM policy in quickstart notes", async () => {
333
- const note = vi.fn(async (_message: string, _title?: string) => {});
334
- const prompter = createQuickstartPrompter({ note, dmPolicy: "pairing" });
335
-
336
- await runSetupWizardConfigure({
337
- configure: zalouserConfigure,
338
- cfg: {
339
- channels: {
340
- zalouser: {
341
- dmPolicy: "disabled",
342
- accounts: {
343
- work: {
344
- profile: "work",
345
- dmPolicy: "allowlist",
346
- allowFrom: ["123456789"],
347
- },
348
- },
349
- },
350
- },
351
- } as OpenClawConfig,
352
- prompter,
353
- options: { quickstartDefaults: true },
354
- accountOverrides: { zalouser: "work" },
355
- });
356
-
357
- expect(
358
- note.mock.calls.some(([message]) =>
359
- message.includes("Current: dmPolicy=allowlist, allowFrom=123456789"),
360
- ),
361
- ).toBe(true);
362
- });
363
- });