@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
package/src/probe.test.ts DELETED
@@ -1,60 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { probeZalouser } from "./probe.js";
3
- import { getZaloUserInfo } from "./zalo-js.js";
4
-
5
- vi.mock("./zalo-js.js", () => ({
6
- getZaloUserInfo: vi.fn(),
7
- }));
8
-
9
- const mockGetUserInfo = vi.mocked(getZaloUserInfo);
10
-
11
- describe("probeZalouser", () => {
12
- beforeEach(() => {
13
- mockGetUserInfo.mockReset();
14
- });
15
-
16
- afterEach(() => {
17
- vi.useRealTimers();
18
- });
19
-
20
- it("returns ok=true with user when authenticated", async () => {
21
- mockGetUserInfo.mockResolvedValueOnce({
22
- userId: "123",
23
- displayName: "Alice",
24
- });
25
-
26
- await expect(probeZalouser("default")).resolves.toEqual({
27
- ok: true,
28
- user: { userId: "123", displayName: "Alice" },
29
- });
30
- });
31
-
32
- it("returns not authenticated when no user info is returned", async () => {
33
- mockGetUserInfo.mockResolvedValueOnce(null);
34
- await expect(probeZalouser("default")).resolves.toEqual({
35
- ok: false,
36
- error: "Not authenticated",
37
- });
38
- });
39
-
40
- it("returns error when user lookup throws", async () => {
41
- mockGetUserInfo.mockRejectedValueOnce(new Error("network down"));
42
- await expect(probeZalouser("default")).resolves.toEqual({
43
- ok: false,
44
- error: "network down",
45
- });
46
- });
47
-
48
- it("times out when lookup takes too long", async () => {
49
- vi.useFakeTimers();
50
- mockGetUserInfo.mockReturnValueOnce(new Promise(() => undefined));
51
-
52
- const pending = probeZalouser("default", 10);
53
- await vi.advanceTimersByTimeAsync(1000);
54
-
55
- await expect(pending).resolves.toEqual({
56
- ok: false,
57
- error: "Not authenticated",
58
- });
59
- });
60
- });
package/src/probe.ts DELETED
@@ -1,35 +0,0 @@
1
- import type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract";
2
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
3
- import type { ZcaUserInfo } from "./types.js";
4
- import { getZaloUserInfo } from "./zalo-js.js";
5
-
6
- export type ZalouserProbeResult = BaseProbeResult<string> & {
7
- user?: ZcaUserInfo;
8
- };
9
-
10
- export async function probeZalouser(
11
- profile: string,
12
- timeoutMs?: number,
13
- ): Promise<ZalouserProbeResult> {
14
- try {
15
- const user = timeoutMs
16
- ? await Promise.race([
17
- getZaloUserInfo(profile),
18
- new Promise<null>((resolve) =>
19
- setTimeout(() => resolve(null), Math.max(timeoutMs, 1000)),
20
- ),
21
- ])
22
- : await getZaloUserInfo(profile);
23
-
24
- if (!user) {
25
- return { ok: false, error: "Not authenticated" };
26
- }
27
-
28
- return { ok: true, user };
29
- } catch (error) {
30
- return {
31
- ok: false,
32
- error: formatErrorMessage(error),
33
- };
34
- }
35
- }
@@ -1,22 +0,0 @@
1
- import fsp from "node:fs/promises";
2
- import path from "node:path";
3
- import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
4
-
5
- export async function writeQrDataUrlToTempFile(
6
- qrDataUrl: string,
7
- profile: string,
8
- ): Promise<string | null> {
9
- const trimmed = qrDataUrl.trim();
10
- const match = trimmed.match(/^data:image\/png;base64,(.+)$/i);
11
- const base64 = (match?.[1] ?? "").trim();
12
- if (!base64) {
13
- return null;
14
- }
15
- const safeProfile = profile.replace(/[^a-zA-Z0-9_-]+/g, "-") || "default";
16
- const filePath = path.join(
17
- resolvePreferredOpenClawTmpDir(),
18
- `openclaw-zalouser-qr-${safeProfile}.png`,
19
- );
20
- await fsp.writeFile(filePath, Buffer.from(base64, "base64"));
21
- return filePath;
22
- }
@@ -1,19 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { normalizeZaloReactionIcon } from "./reaction.js";
3
-
4
- describe("zalouser reaction alias normalization", () => {
5
- it("maps common aliases", () => {
6
- expect(normalizeZaloReactionIcon("like")).toBe("/-strong");
7
- expect(normalizeZaloReactionIcon("👍")).toBe("/-strong");
8
- expect(normalizeZaloReactionIcon("heart")).toBe("/-heart");
9
- expect(normalizeZaloReactionIcon("😂")).toBe(":>");
10
- });
11
-
12
- it("defaults empty icon to like", () => {
13
- expect(normalizeZaloReactionIcon("")).toBe("/-strong");
14
- });
15
-
16
- it("passes through unknown custom reactions", () => {
17
- expect(normalizeZaloReactionIcon("/custom")).toBe("/custom");
18
- });
19
- });
package/src/reaction.ts DELETED
@@ -1,32 +0,0 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
2
- import { Reactions } from "./zca-constants.js";
3
-
4
- const REACTION_ALIAS_MAP = new Map<string, string>([
5
- ["like", Reactions.LIKE],
6
- ["👍", Reactions.LIKE],
7
- [":+1:", Reactions.LIKE],
8
- ["heart", Reactions.HEART],
9
- ["❤️", Reactions.HEART],
10
- ["<3", Reactions.HEART],
11
- ["haha", Reactions.HAHA],
12
- ["laugh", Reactions.HAHA],
13
- ["😂", Reactions.HAHA],
14
- ["wow", Reactions.WOW],
15
- ["😮", Reactions.WOW],
16
- ["cry", Reactions.CRY],
17
- ["😢", Reactions.CRY],
18
- ["angry", Reactions.ANGRY],
19
- ["😡", Reactions.ANGRY],
20
- ]);
21
-
22
- export function normalizeZaloReactionIcon(raw: string): string {
23
- const trimmed = raw.trim();
24
- if (!trimmed) {
25
- return Reactions.LIKE;
26
- }
27
- return (
28
- REACTION_ALIAS_MAP.get(normalizeLowercaseStringOrEmpty(trimmed)) ??
29
- REACTION_ALIAS_MAP.get(trimmed) ??
30
- trimmed
31
- );
32
- }
package/src/runtime.ts DELETED
@@ -1,9 +0,0 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk/core";
2
- import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
3
-
4
- const { setRuntime: setZalouserRuntime, getRuntime: getZalouserRuntime } =
5
- createPluginRuntimeStore<PluginRuntime>({
6
- pluginId: "zalouser",
7
- errorMessage: "Zalouser runtime not initialized",
8
- });
9
- export { getZalouserRuntime, setZalouserRuntime };
@@ -1,80 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { collectZalouserSecurityAuditFindings } from "./security-audit.js";
3
- import type { ResolvedZalouserAccount, ZalouserAccountConfig } from "./types.js";
4
-
5
- function createAccount(config: ZalouserAccountConfig): ResolvedZalouserAccount {
6
- return {
7
- accountId: "default",
8
- enabled: true,
9
- profile: "default",
10
- authenticated: true,
11
- config,
12
- };
13
- }
14
-
15
- describe("Zalouser security audit findings", () => {
16
- const cases: Array<{
17
- name: string;
18
- config: ZalouserAccountConfig;
19
- expectedSeverity: "info" | "warn";
20
- detailIncludes: string[];
21
- detailExcludes?: string[];
22
- expectFindingMatch?: { checkId: string; severity: "info" | "warn" };
23
- }> = [
24
- {
25
- name: "warns when group routing contains mutable group entries",
26
- config: {
27
- enabled: true,
28
- groups: {
29
- "Ops Room": { enabled: true },
30
- "group:g-123": { enabled: true },
31
- },
32
- } satisfies ZalouserAccountConfig,
33
- expectedSeverity: "warn",
34
- detailIncludes: ["channels.zalouser.groups:Ops Room"],
35
- detailExcludes: ["group:g-123"],
36
- },
37
- {
38
- name: "marks mutable group routing as break-glass when dangerous matching is enabled",
39
- config: {
40
- enabled: true,
41
- dangerouslyAllowNameMatching: true,
42
- groups: {
43
- "Ops Room": { enabled: true },
44
- },
45
- } satisfies ZalouserAccountConfig,
46
- expectedSeverity: "info",
47
- detailIncludes: ["out-of-scope"],
48
- expectFindingMatch: {
49
- checkId: "channels.zalouser.groups.mutable_entries",
50
- severity: "info",
51
- },
52
- },
53
- ];
54
-
55
- it.each(cases)("$name", (testCase) => {
56
- const findings = collectZalouserSecurityAuditFindings({
57
- account: createAccount(testCase.config),
58
- accountId: "default",
59
- orderedAccountIds: ["default"],
60
- hasExplicitAccountPath: false,
61
- });
62
- const finding = findings.find(
63
- (entry) => entry.checkId === "channels.zalouser.groups.mutable_entries",
64
- );
65
-
66
- expect(finding).toBeDefined();
67
- expect(finding?.severity).toBe(testCase.expectedSeverity);
68
- for (const snippet of testCase.detailIncludes) {
69
- expect(finding?.detail).toContain(snippet);
70
- }
71
- for (const snippet of testCase.detailExcludes ?? []) {
72
- expect(finding?.detail).not.toContain(snippet);
73
- }
74
- if (testCase.expectFindingMatch) {
75
- expect(findings).toEqual(
76
- expect.arrayContaining([expect.objectContaining(testCase.expectFindingMatch)]),
77
- );
78
- }
79
- });
80
- });
@@ -1,71 +0,0 @@
1
- import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
2
- import type { ResolvedZalouserAccount } from "./accounts.js";
3
-
4
- export function isZalouserMutableGroupEntry(raw: string): boolean {
5
- const text = raw.trim();
6
- if (!text || text === "*") {
7
- return false;
8
- }
9
- const normalized = text
10
- .replace(/^(zalouser|zlu):/i, "")
11
- .replace(/^group:/i, "")
12
- .trim();
13
- if (!normalized) {
14
- return false;
15
- }
16
- if (/^\d+$/.test(normalized)) {
17
- return false;
18
- }
19
- return !/^g-\S+$/i.test(normalized);
20
- }
21
-
22
- export function collectZalouserSecurityAuditFindings(params: {
23
- accountId?: string | null;
24
- account: ResolvedZalouserAccount;
25
- orderedAccountIds: string[];
26
- hasExplicitAccountPath: boolean;
27
- }) {
28
- const zalouserCfg = params.account.config ?? {};
29
- const accountId = params.accountId?.trim() || params.account.accountId || "default";
30
- const dangerousNameMatchingEnabled = isDangerousNameMatchingEnabled(zalouserCfg);
31
- const zalouserPathPrefix =
32
- params.orderedAccountIds.length > 1 || params.hasExplicitAccountPath
33
- ? `channels.zalouser.accounts.${accountId}`
34
- : "channels.zalouser";
35
- const mutableGroupEntries = new Set<string>();
36
- const groups = zalouserCfg.groups;
37
- if (groups && typeof groups === "object" && !Array.isArray(groups)) {
38
- for (const key of Object.keys(groups as Record<string, unknown>)) {
39
- if (!isZalouserMutableGroupEntry(key)) {
40
- continue;
41
- }
42
- mutableGroupEntries.add(`${zalouserPathPrefix}.groups:${key}`);
43
- }
44
- }
45
- if (mutableGroupEntries.size === 0) {
46
- return [];
47
- }
48
- const examples = Array.from(mutableGroupEntries).slice(0, 5);
49
- const more =
50
- mutableGroupEntries.size > examples.length
51
- ? ` (+${mutableGroupEntries.size - examples.length} more)`
52
- : "";
53
- const severity: "info" | "warn" = dangerousNameMatchingEnabled ? "info" : "warn";
54
- return [
55
- {
56
- checkId: "channels.zalouser.groups.mutable_entries",
57
- severity,
58
- title: dangerousNameMatchingEnabled
59
- ? "Zalouser group routing uses break-glass name matching"
60
- : "Zalouser group routing contains mutable group entries",
61
- detail: dangerousNameMatchingEnabled
62
- ? "Zalouser group-name routing is explicitly enabled via dangerouslyAllowNameMatching. This mutable-identity mode is operator-selected break-glass behavior and out-of-scope for vulnerability reports by itself. " +
63
- `Found: ${examples.join(", ")}${more}.`
64
- : "Zalouser group auth is ID-only by default, so unresolved group-name or slug entries are ignored for auth and can drift from the intended trusted group. " +
65
- `Found: ${examples.join(", ")}${more}.`,
66
- remediation: dangerousNameMatchingEnabled
67
- ? "Prefer stable Zalo group IDs (for example group:<id> or provider-native g- ids), then disable dangerouslyAllowNameMatching."
68
- : "Prefer stable Zalo group IDs in channels.zalouser.groups, or explicitly opt in with dangerouslyAllowNameMatching=true if you accept mutable group-name matching.",
69
- },
70
- ];
71
- }