@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/CHANGELOG.md +23 -0
- package/README.md +6 -2
- package/index.ts +1 -2
- package/openclaw.plugin.json +1 -3
- package/package.json +7 -4
- package/src/accounts.ts +38 -20
- package/src/channel.test.ts +3 -2
- package/src/channel.ts +107 -62
- package/src/monitor.ts +49 -33
- package/src/onboarding.ts +63 -47
- package/src/probe.ts +1 -1
- package/src/send.ts +15 -5
- package/src/status-issues.test.ts +0 -1
- package/src/status-issues.ts +12 -4
- package/src/tool.ts +29 -21
- package/src/types.ts +8 -2
- package/src/zca.ts +3 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,28 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.2.2
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Version alignment with core OpenClaw release numbers.
|
|
8
|
+
|
|
9
|
+
## 2026.1.31
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
- Version alignment with core OpenClaw release numbers.
|
|
14
|
+
|
|
15
|
+
## 2026.1.30
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
- Version alignment with core OpenClaw release numbers.
|
|
20
|
+
|
|
3
21
|
## 2026.1.29
|
|
4
22
|
|
|
5
23
|
### Changes
|
|
24
|
+
|
|
6
25
|
- Version alignment with core OpenClaw release numbers.
|
|
7
26
|
|
|
8
27
|
## 2026.1.23
|
|
9
28
|
|
|
10
29
|
### Changes
|
|
30
|
+
|
|
11
31
|
- Version alignment with core OpenClaw release numbers.
|
|
12
32
|
|
|
13
33
|
## 2026.1.22
|
|
14
34
|
|
|
15
35
|
### Changes
|
|
36
|
+
|
|
16
37
|
- Version alignment with core OpenClaw release numbers.
|
|
17
38
|
|
|
18
39
|
## 2026.1.21
|
|
19
40
|
|
|
20
41
|
### Changes
|
|
42
|
+
|
|
21
43
|
- Version alignment with core OpenClaw release numbers.
|
|
22
44
|
|
|
23
45
|
## 2026.1.20
|
|
24
46
|
|
|
25
47
|
### Changes
|
|
48
|
+
|
|
26
49
|
- Version alignment with core OpenClaw release numbers.
|
|
27
50
|
|
|
28
51
|
## 2026.1.17-1
|
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ OpenClaw extension for Zalo Personal Account messaging via [zca-cli](https://zca
|
|
|
16
16
|
|
|
17
17
|
Install `zca` CLI and ensure it's in your PATH:
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
**macOS / Linux:**
|
|
20
|
+
|
|
21
21
|
```bash
|
|
22
22
|
curl -fsSL https://get.zca-cli.dev/install.sh | bash
|
|
23
23
|
|
|
@@ -32,6 +32,7 @@ curl -fsSL https://get.zca-cli.dev/install.sh | bash -s uninstall
|
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
**Windows (PowerShell):**
|
|
35
|
+
|
|
35
36
|
```powershell
|
|
36
37
|
irm https://get.zca-cli.dev/install.ps1 | iex
|
|
37
38
|
|
|
@@ -50,16 +51,19 @@ iex "& { $(irm https://get.zca-cli.dev/install.ps1) } -Uninstall"
|
|
|
50
51
|
Download binary directly:
|
|
51
52
|
|
|
52
53
|
**macOS / Linux:**
|
|
54
|
+
|
|
53
55
|
```bash
|
|
54
56
|
curl -fsSL https://get.zca-cli.dev/latest/zca-darwin-arm64 -o zca && chmod +x zca
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
**Windows (PowerShell):**
|
|
60
|
+
|
|
58
61
|
```powershell
|
|
59
62
|
Invoke-WebRequest -Uri https://get.zca-cli.dev/latest/zca-windows-x64.exe -OutFile zca.exe
|
|
60
63
|
```
|
|
61
64
|
|
|
62
65
|
Available binaries:
|
|
66
|
+
|
|
63
67
|
- `zca-darwin-arm64` - macOS Apple Silicon
|
|
64
68
|
- `zca-darwin-x64` - macOS Intel
|
|
65
69
|
- `zca-linux-arm64` - Linux ARM64
|
|
@@ -99,7 +103,7 @@ After onboarding, your config will include:
|
|
|
99
103
|
channels:
|
|
100
104
|
zalouser:
|
|
101
105
|
enabled: true
|
|
102
|
-
dmPolicy: pairing
|
|
106
|
+
dmPolicy: pairing # pairing | allowlist | open | disabled
|
|
103
107
|
```
|
|
104
108
|
|
|
105
109
|
For multi-account:
|
package/index.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
2
|
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
3
|
-
|
|
4
3
|
import { zalouserDock, zalouserPlugin } from "./src/channel.js";
|
|
5
|
-
import { ZalouserToolSchema, executeZalouserTool } from "./src/tool.js";
|
|
6
4
|
import { setZalouserRuntime } from "./src/runtime.js";
|
|
5
|
+
import { ZalouserToolSchema, executeZalouserTool } from "./src/tool.js";
|
|
7
6
|
|
|
8
7
|
const plugin = {
|
|
9
8
|
id: "zalouser",
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/zalouser",
|
|
3
|
-
"version": "2026.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "2026.2.2",
|
|
5
4
|
"description": "OpenClaw Zalo Personal Account plugin via zca-cli",
|
|
5
|
+
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"@sinclair/typebox": "0.34.48",
|
|
8
|
+
"openclaw": "workspace:*"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"openclaw": "workspace:*"
|
|
9
12
|
},
|
|
10
13
|
"openclaw": {
|
|
11
14
|
"extensions": [
|
package/src/accounts.ts
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
2
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk";
|
|
3
|
-
|
|
4
|
-
import { runZca, parseJsonOutput } from "./zca.js";
|
|
5
3
|
import type { ResolvedZalouserAccount, ZalouserAccountConfig, ZalouserConfig } from "./types.js";
|
|
4
|
+
import { runZca, parseJsonOutput } from "./zca.js";
|
|
6
5
|
|
|
7
6
|
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
|
|
8
7
|
const accounts = (cfg.channels?.zalouser as ZalouserConfig | undefined)?.accounts;
|
|
9
|
-
if (!accounts || typeof accounts !== "object")
|
|
8
|
+
if (!accounts || typeof accounts !== "object") {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
10
11
|
return Object.keys(accounts).filter(Boolean);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function listZalouserAccountIds(cfg: OpenClawConfig): string[] {
|
|
14
15
|
const ids = listConfiguredAccountIds(cfg);
|
|
15
|
-
if (ids.length === 0)
|
|
16
|
-
|
|
16
|
+
if (ids.length === 0) {
|
|
17
|
+
return [DEFAULT_ACCOUNT_ID];
|
|
18
|
+
}
|
|
19
|
+
return ids.toSorted((a, b) => a.localeCompare(b));
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export function resolveDefaultZalouserAccountId(cfg: OpenClawConfig): string {
|
|
20
23
|
const zalouserConfig = cfg.channels?.zalouser as ZalouserConfig | undefined;
|
|
21
|
-
if (zalouserConfig?.defaultAccount?.trim())
|
|
24
|
+
if (zalouserConfig?.defaultAccount?.trim()) {
|
|
25
|
+
return zalouserConfig.defaultAccount.trim();
|
|
26
|
+
}
|
|
22
27
|
const ids = listZalouserAccountIds(cfg);
|
|
23
|
-
if (ids.includes(DEFAULT_ACCOUNT_ID))
|
|
28
|
+
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
29
|
+
return DEFAULT_ACCOUNT_ID;
|
|
30
|
+
}
|
|
24
31
|
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -29,14 +36,13 @@ function resolveAccountConfig(
|
|
|
29
36
|
accountId: string,
|
|
30
37
|
): ZalouserAccountConfig | undefined {
|
|
31
38
|
const accounts = (cfg.channels?.zalouser as ZalouserConfig | undefined)?.accounts;
|
|
32
|
-
if (!accounts || typeof accounts !== "object")
|
|
39
|
+
if (!accounts || typeof accounts !== "object") {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
33
42
|
return accounts[accountId] as ZalouserAccountConfig | undefined;
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
function mergeZalouserAccountConfig(
|
|
37
|
-
cfg: OpenClawConfig,
|
|
38
|
-
accountId: string,
|
|
39
|
-
): ZalouserAccountConfig {
|
|
45
|
+
function mergeZalouserAccountConfig(cfg: OpenClawConfig, accountId: string): ZalouserAccountConfig {
|
|
40
46
|
const raw = (cfg.channels?.zalouser ?? {}) as ZalouserConfig;
|
|
41
47
|
const { accounts: _ignored, defaultAccount: _ignored2, ...base } = raw;
|
|
42
48
|
const account = resolveAccountConfig(cfg, accountId) ?? {};
|
|
@@ -44,9 +50,15 @@ function mergeZalouserAccountConfig(
|
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
function resolveZcaProfile(config: ZalouserAccountConfig, accountId: string): string {
|
|
47
|
-
if (config.profile?.trim())
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
if (config.profile?.trim()) {
|
|
54
|
+
return config.profile.trim();
|
|
55
|
+
}
|
|
56
|
+
if (process.env.ZCA_PROFILE?.trim()) {
|
|
57
|
+
return process.env.ZCA_PROFILE.trim();
|
|
58
|
+
}
|
|
59
|
+
if (accountId !== DEFAULT_ACCOUNT_ID) {
|
|
60
|
+
return accountId;
|
|
61
|
+
}
|
|
50
62
|
return "default";
|
|
51
63
|
}
|
|
52
64
|
|
|
@@ -60,7 +72,8 @@ export async function resolveZalouserAccount(params: {
|
|
|
60
72
|
accountId?: string | null;
|
|
61
73
|
}): Promise<ResolvedZalouserAccount> {
|
|
62
74
|
const accountId = normalizeAccountId(params.accountId);
|
|
63
|
-
const baseEnabled =
|
|
75
|
+
const baseEnabled =
|
|
76
|
+
(params.cfg.channels?.zalouser as ZalouserConfig | undefined)?.enabled !== false;
|
|
64
77
|
const merged = mergeZalouserAccountConfig(params.cfg, accountId);
|
|
65
78
|
const accountEnabled = merged.enabled !== false;
|
|
66
79
|
const enabled = baseEnabled && accountEnabled;
|
|
@@ -82,7 +95,8 @@ export function resolveZalouserAccountSync(params: {
|
|
|
82
95
|
accountId?: string | null;
|
|
83
96
|
}): ResolvedZalouserAccount {
|
|
84
97
|
const accountId = normalizeAccountId(params.accountId);
|
|
85
|
-
const baseEnabled =
|
|
98
|
+
const baseEnabled =
|
|
99
|
+
(params.cfg.channels?.zalouser as ZalouserConfig | undefined)?.enabled !== false;
|
|
86
100
|
const merged = mergeZalouserAccountConfig(params.cfg, accountId);
|
|
87
101
|
const accountEnabled = merged.enabled !== false;
|
|
88
102
|
const enabled = baseEnabled && accountEnabled;
|
|
@@ -103,14 +117,18 @@ export async function listEnabledZalouserAccounts(
|
|
|
103
117
|
): Promise<ResolvedZalouserAccount[]> {
|
|
104
118
|
const ids = listZalouserAccountIds(cfg);
|
|
105
119
|
const accounts = await Promise.all(
|
|
106
|
-
ids.map((accountId) => resolveZalouserAccount({ cfg, accountId }))
|
|
120
|
+
ids.map((accountId) => resolveZalouserAccount({ cfg, accountId })),
|
|
107
121
|
);
|
|
108
122
|
return accounts.filter((account) => account.enabled);
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
export async function getZcaUserInfo(
|
|
125
|
+
export async function getZcaUserInfo(
|
|
126
|
+
profile: string,
|
|
127
|
+
): Promise<{ userId?: string; displayName?: string } | null> {
|
|
112
128
|
const result = await runZca(["me", "info", "-j"], { profile, timeout: 10000 });
|
|
113
|
-
if (!result.ok)
|
|
129
|
+
if (!result.ok) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
114
132
|
return parseJsonOutput<{ userId?: string; displayName?: string }>(result.stdout);
|
|
115
133
|
}
|
|
116
134
|
|
package/src/channel.test.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
2
|
import { zalouserPlugin } from "./channel.js";
|
|
4
3
|
|
|
5
4
|
describe("zalouser outbound chunker", () => {
|
|
6
5
|
it("chunks without empty strings and respects limit", () => {
|
|
7
6
|
const chunker = zalouserPlugin.outbound?.chunker;
|
|
8
7
|
expect(chunker).toBeTypeOf("function");
|
|
9
|
-
if (!chunker)
|
|
8
|
+
if (!chunker) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
10
11
|
|
|
11
12
|
const limit = 10;
|
|
12
13
|
const chunks = chunker("hello world\nthis is a test", limit);
|