@zbruceli/openclaw-dchat 0.3.0 → 0.4.1

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/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
2
+ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
3
3
  import { dchatPlugin } from "./src/channel.js";
4
4
  import { setDchatRuntime } from "./src/runtime.js";
5
5
 
package/package.json CHANGED
@@ -1,18 +1,15 @@
1
1
  {
2
2
  "name": "@zbruceli/openclaw-dchat",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "OpenClaw D-Chat/nMobile channel plugin — decentralized E2E encrypted messaging over the NKN relay network",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "dependencies": {
8
8
  "nkn-sdk": "^1.3.6",
9
+ "openclaw": ">=2026.3.22",
9
10
  "zod": "^4.0.0"
10
11
  },
11
- "peerDependencies": {
12
- "openclaw": "*"
13
- },
14
12
  "devDependencies": {
15
- "openclaw": "*",
16
13
  "vitest": "^3.0.0",
17
14
  "typescript": "^5.8.0"
18
15
  },
package/src/channel.ts CHANGED
@@ -1,19 +1,21 @@
1
1
  import {
2
2
  applyAccountNameToChannelSection,
3
- buildBaseAccountStatusSnapshot,
4
- buildBaseChannelStatusSummary,
5
3
  buildChannelConfigSchema,
6
- collectStatusIssuesFromLastError,
7
- createDefaultChannelRuntimeState,
8
4
  DEFAULT_ACCOUNT_ID,
9
5
  deleteAccountFromConfigSection,
10
6
  normalizeAccountId,
11
- resolveOutboundMediaUrls,
12
- resolveSenderCommandAuthorization,
13
7
  setAccountEnabledInConfigSection,
14
8
  type ChannelPlugin,
15
- } from "openclaw/plugin-sdk";
16
- import type { PluginRuntime } from "openclaw/plugin-sdk";
9
+ type PluginRuntime,
10
+ } from "openclaw/plugin-sdk/core";
11
+ import {
12
+ buildBaseAccountStatusSnapshot,
13
+ buildBaseChannelStatusSummary,
14
+ collectStatusIssuesFromLastError,
15
+ createDefaultChannelRuntimeState,
16
+ } from "openclaw/plugin-sdk/status-helpers";
17
+ import { resolveOutboundMediaUrls } from "openclaw/plugin-sdk/reply-payload";
18
+ import { resolveSenderCommandAuthorization } from "openclaw/plugin-sdk/command-auth";
17
19
 
18
20
  /* ── Inline helpers that may not exist in older OpenClaw versions ── */
19
21
 
@@ -30,7 +32,7 @@ function createScopedPairingAccess(params: {
30
32
  channel: params.channel,
31
33
  accountId: resolvedAccountId,
32
34
  }),
33
- upsertPairingRequest: (input: { id: string; meta?: Record<string, unknown> }) =>
35
+ upsertPairingRequest: (input: { id: string; meta?: Record<string, string | null | undefined> }) =>
34
36
  params.core.channel.pairing.upsertPairingRequest({
35
37
  channel: params.channel,
36
38
  accountId: resolvedAccountId,
@@ -51,7 +53,7 @@ import {
51
53
  resolveDefaultDchatAccountId,
52
54
  } from "./config-schema.js";
53
55
  import { NknBus } from "./nkn-bus.js";
54
- import { dchatOnboardingAdapter } from "./onboarding.js";
56
+ import { dchatSetupWizard } from "./onboarding.js";
55
57
  import { getDchatRuntime } from "./runtime.js";
56
58
  import { SeenTracker } from "./seen-tracker.js";
57
59
  import type { ResolvedDchatAccount } from "./types.js";
@@ -92,7 +94,7 @@ function getBusForAccount(accountId: string): NknBus | undefined {
92
94
  export const dchatPlugin: ChannelPlugin<ResolvedDchatAccount> = {
93
95
  id: "dchat",
94
96
  meta,
95
- onboarding: dchatOnboardingAdapter,
97
+ setupWizard: dchatSetupWizard,
96
98
  capabilities: {
97
99
  chatTypes: ["direct", "group"],
98
100
  media: true,
@@ -138,7 +140,7 @@ export const dchatPlugin: ChannelPlugin<ResolvedDchatAccount> = {
138
140
  name: account.name,
139
141
  enabled: account.enabled,
140
142
  configured: account.configured,
141
- nknAddress: account.nknAddress ?? null,
143
+ publicKey: account.nknAddress ?? null,
142
144
  }),
143
145
  resolveAllowFrom: ({ cfg, accountId }) => {
144
146
  const dchatConfig = resolveDchatAccountConfig({ cfg: cfg as CoreConfig, accountId });
@@ -407,15 +409,15 @@ export const dchatPlugin: ChannelPlugin<ResolvedDchatAccount> = {
407
409
  },
408
410
  buildChannelSummary: ({ snapshot }) => ({
409
411
  ...buildBaseChannelStatusSummary(snapshot),
410
- nknAddress: snapshot.nknAddress ?? null,
412
+ publicKey: snapshot.publicKey ?? null,
411
413
  connected: snapshot.connected ?? false,
412
414
  lastConnectedAt: snapshot.lastConnectedAt ?? null,
413
415
  }),
414
416
  buildAccountSnapshot: ({ account, runtime }) => ({
415
417
  ...buildBaseAccountStatusSnapshot({ account, runtime }),
416
- nknAddress: account.nknAddress ?? null,
417
- connected: (runtime as Record<string, unknown>)?.connected ?? false,
418
- lastConnectedAt: (runtime as Record<string, unknown>)?.lastConnectedAt ?? null,
418
+ publicKey: account.nknAddress ?? null,
419
+ connected: Boolean(runtime?.connected),
420
+ lastConnectedAt: runtime?.lastConnectedAt ?? null,
419
421
  }),
420
422
  collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("dchat", accounts),
421
423
  },
@@ -1,5 +1,5 @@
1
1
  import nkn from "nkn-sdk";
2
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
3
3
  import { z } from "zod";
4
4
  import type { DchatAccountConfig, ResolvedDchatAccount } from "./types.js";
5
5
 
package/src/onboarding.ts CHANGED
@@ -1,13 +1,14 @@
1
- import type { DmPolicy } from "openclaw/plugin-sdk";
1
+ import type { DmPolicy } from "openclaw/plugin-sdk/setup";
2
2
  import {
3
3
  addWildcardAllowFrom,
4
4
  mergeAllowFromEntries,
5
5
  formatDocsLink,
6
- type ChannelOnboardingAdapter,
7
- type ChannelOnboardingDmPolicy,
6
+ type ChannelSetupDmPolicy,
8
7
  type WizardPrompter,
9
- } from "openclaw/plugin-sdk";
10
- import { listDchatAccountIds, resolveDchatAccount, type CoreConfig } from "./config-schema.js";
8
+ } from "openclaw/plugin-sdk/setup";
9
+ import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
10
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
11
+ import { listDchatAccountIds, resolveDchatAccount, resolveDchatAccountConfig, type CoreConfig } from "./config-schema.js";
11
12
 
12
13
  const channel = "dchat" as const;
13
14
 
@@ -30,154 +31,134 @@ function setDchatDmPolicy(cfg: CoreConfig, policy: DmPolicy) {
30
31
  };
31
32
  }
32
33
 
33
- async function promptDchatAllowFrom(params: {
34
- cfg: CoreConfig;
35
- prompter: WizardPrompter;
36
- }): Promise<CoreConfig> {
37
- const { cfg, prompter } = params;
38
- const existingAllowFrom = cfg.channels?.dchat?.allowFrom ?? [];
39
-
40
- const entry = await prompter.text({
41
- message: "NKN address to allow (full public key hex)",
42
- placeholder: "abc123...def456 (64-char hex NKN address)",
43
- initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : undefined,
44
- validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
45
- });
46
-
47
- const parts = String(entry)
48
- .split(/[\n,;]+/g)
49
- .map((e) => e.trim())
50
- .filter(Boolean);
51
-
52
- const unique = mergeAllowFromEntries(existingAllowFrom, parts);
53
- return {
54
- ...cfg,
55
- channels: {
56
- ...cfg.channels,
57
- dchat: {
58
- ...cfg.channels?.dchat,
59
- enabled: true,
60
- dmPolicy: "allowlist",
61
- allowFrom: unique,
62
- },
63
- },
64
- };
65
- }
66
-
67
- const dmPolicy: ChannelOnboardingDmPolicy = {
34
+ const dmPolicy: ChannelSetupDmPolicy = {
68
35
  label: "D-Chat",
69
36
  channel,
70
37
  policyKey: "channels.dchat.dmPolicy",
71
38
  allowFromKey: "channels.dchat.allowFrom",
72
39
  getCurrent: (cfg) => ((cfg as CoreConfig).channels?.dchat?.dmPolicy as DmPolicy) ?? "pairing",
73
40
  setPolicy: (cfg, policy) => setDchatDmPolicy(cfg as CoreConfig, policy),
74
- promptAllowFrom: promptDchatAllowFrom,
75
- };
41
+ promptAllowFrom: async (params) => {
42
+ const cfg = params.cfg as CoreConfig;
43
+ const { prompter } = params;
44
+ const existingAllowFrom = cfg.channels?.dchat?.allowFrom ?? [];
76
45
 
77
- export const dchatOnboardingAdapter: ChannelOnboardingAdapter = {
78
- channel,
79
- getStatus: async ({ cfg }) => {
80
- const typedCfg = cfg as CoreConfig;
81
- const accountIds = listDchatAccountIds(typedCfg);
82
- const anyConfigured = accountIds.some(
83
- (id) => resolveDchatAccount({ cfg: typedCfg, accountId: id }).configured,
84
- );
46
+ const entry = await prompter.text({
47
+ message: "NKN address to allow (full public key hex)",
48
+ placeholder: "abc123...def456 (64-char hex NKN address)",
49
+ initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : undefined,
50
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
51
+ });
52
+
53
+ const parts = String(entry)
54
+ .split(/[\n,;]+/g)
55
+ .map((e) => e.trim())
56
+ .filter(Boolean);
57
+
58
+ const unique = mergeAllowFromEntries(existingAllowFrom, parts);
85
59
  return {
86
- channel,
87
- configured: anyConfigured,
88
- statusLines: [`D-Chat: ${anyConfigured ? "configured" : "needs wallet seed"}`],
89
- selectionHint: anyConfigured ? "configured" : "needs seed",
60
+ ...cfg,
61
+ channels: {
62
+ ...cfg.channels,
63
+ dchat: {
64
+ ...cfg.channels?.dchat,
65
+ enabled: true,
66
+ dmPolicy: "allowlist",
67
+ allowFrom: unique,
68
+ },
69
+ },
90
70
  };
91
71
  },
92
- configure: async ({ cfg, prompter, forceAllowFrom }) => {
93
- let next = cfg as CoreConfig;
94
- const existing = next.channels?.dchat ?? {};
95
- const account = resolveDchatAccount({ cfg: next });
72
+ };
96
73
 
97
- if (!account.configured) {
98
- await prompter.note(
99
- [
100
- "D-Chat uses the NKN relay network for decentralized E2E encrypted messaging.",
101
- "You need a wallet seed (64-character hex string) to connect.",
102
- "Generate one with nkn-sdk or use an existing seed from D-Chat/nMobile.",
103
- `Docs: ${formatDocsLink("/channels/dchat", "channels/dchat")}`,
104
- ].join("\n"),
105
- "D-Chat setup",
74
+ export const dchatSetupWizard: ChannelSetupWizard = {
75
+ channel,
76
+ status: {
77
+ configuredLabel: "configured",
78
+ unconfiguredLabel: "needs wallet seed",
79
+ resolveConfigured: ({ cfg }) => {
80
+ const typedCfg = cfg as CoreConfig;
81
+ const accountIds = listDchatAccountIds(typedCfg);
82
+ return accountIds.some(
83
+ (id) => resolveDchatAccount({ cfg: typedCfg, accountId: id }).configured,
106
84
  );
107
- }
108
-
109
- // Check for env var (validate same 64-hex format as prompted input)
110
- const envSeed = process.env.DCHAT_SEED?.trim() || process.env.NKN_SEED?.trim();
111
- if (envSeed && /^[0-9a-f]{64}$/i.test(envSeed) && !existing.seed) {
112
- const useEnv = await prompter.confirm({
113
- message: "NKN seed env var detected. Use env value?",
114
- initialValue: true,
115
- });
116
- if (useEnv) {
117
- next = {
118
- ...next,
85
+ },
86
+ resolveStatusLines: ({ cfg, configured }) => [
87
+ `D-Chat: ${configured ? "configured" : "needs wallet seed"}`,
88
+ ],
89
+ resolveSelectionHint: ({ configured }) =>
90
+ configured ? "configured" : "needs seed",
91
+ },
92
+ introNote: {
93
+ title: "D-Chat setup",
94
+ lines: [
95
+ "D-Chat uses the NKN relay network for decentralized E2E encrypted messaging.",
96
+ "You need a wallet seed (64-character hex string) to connect.",
97
+ "Generate one with nkn-sdk or use an existing seed from D-Chat/nMobile.",
98
+ `Docs: ${formatDocsLink("/channels/dchat", "channels/dchat")}`,
99
+ ],
100
+ shouldShow: ({ cfg }) => {
101
+ const account = resolveDchatAccount({ cfg: cfg as CoreConfig });
102
+ return !account.configured;
103
+ },
104
+ },
105
+ envShortcut: {
106
+ prompt: "NKN seed env var detected. Use env value?",
107
+ preferredEnvVar: "DCHAT_SEED",
108
+ isAvailable: ({ cfg }) => {
109
+ const envSeed = process.env.DCHAT_SEED?.trim() || process.env.NKN_SEED?.trim();
110
+ const existing = (cfg as CoreConfig).channels?.dchat?.seed;
111
+ return Boolean(envSeed && /^[0-9a-f]{64}$/i.test(envSeed) && !existing);
112
+ },
113
+ apply: ({ cfg, accountId }) => {
114
+ const envSeed = (process.env.DCHAT_SEED?.trim() || process.env.NKN_SEED?.trim())!;
115
+ return {
116
+ ...cfg,
117
+ channels: {
118
+ ...(cfg as CoreConfig).channels,
119
+ dchat: {
120
+ ...(cfg as CoreConfig).channels?.dchat,
121
+ enabled: true,
122
+ seed: envSeed,
123
+ },
124
+ },
125
+ } as OpenClawConfig;
126
+ },
127
+ },
128
+ credentials: [
129
+ {
130
+ inputKey: "accessToken",
131
+ providerHint: "NKN",
132
+ credentialLabel: "wallet seed",
133
+ preferredEnvVar: "DCHAT_SEED",
134
+ envPrompt: "NKN seed env var detected. Use env value?",
135
+ keepPrompt: "Wallet seed already configured. Keep it?",
136
+ inputPrompt: "NKN wallet seed (64-char hex)",
137
+ inspect: ({ cfg, accountId }) => {
138
+ const acctCfg = resolveDchatAccountConfig({ cfg: cfg as CoreConfig, accountId });
139
+ const seed = acctCfg.seed?.trim();
140
+ return {
141
+ accountConfigured: Boolean(seed),
142
+ hasConfiguredValue: Boolean(seed),
143
+ resolvedValue: seed,
144
+ };
145
+ },
146
+ applySet: ({ cfg, accountId, resolvedValue }) => {
147
+ const seed = resolvedValue.trim();
148
+ return {
149
+ ...cfg,
119
150
  channels: {
120
- ...next.channels,
151
+ ...(cfg as CoreConfig).channels,
121
152
  dchat: {
122
- ...next.channels?.dchat,
153
+ ...(cfg as CoreConfig).channels?.dchat,
123
154
  enabled: true,
124
- seed: envSeed,
155
+ seed,
125
156
  },
126
157
  },
127
- };
128
- if (forceAllowFrom) {
129
- next = await promptDchatAllowFrom({ cfg: next, prompter });
130
- }
131
- return { cfg: next };
132
- }
133
- }
134
-
135
- // Prompt for seed
136
- let seed = existing.seed ?? "";
137
- if (seed) {
138
- const keep = await prompter.confirm({
139
- message: "Wallet seed already configured. Keep it?",
140
- initialValue: true,
141
- });
142
- if (!keep) {
143
- seed = "";
144
- }
145
- }
146
-
147
- if (!seed) {
148
- seed = String(
149
- await prompter.text({
150
- message: "NKN wallet seed (64-char hex)",
151
- validate: (value) => {
152
- const raw = String(value ?? "").trim();
153
- if (!raw) return "Required";
154
- if (!/^[0-9a-f]{64}$/i.test(raw)) {
155
- return "Must be a 64-character hex string";
156
- }
157
- return undefined;
158
- },
159
- }),
160
- ).trim();
161
- }
162
-
163
- next = {
164
- ...next,
165
- channels: {
166
- ...next.channels,
167
- dchat: {
168
- ...next.channels?.dchat,
169
- enabled: true,
170
- seed,
171
- },
158
+ } as OpenClawConfig;
172
159
  },
173
- };
174
-
175
- if (forceAllowFrom) {
176
- next = await promptDchatAllowFrom({ cfg: next, prompter });
177
- }
178
-
179
- return { cfg: next };
180
- },
160
+ },
161
+ ],
181
162
  dmPolicy,
182
163
  disable: (cfg) => ({
183
164
  ...(cfg as CoreConfig),
package/src/runtime.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
1
+ import type { PluginRuntime } from "openclaw/plugin-sdk/core";
2
2
 
3
3
  let runtime: PluginRuntime | null = null;
4
4