adp-openclaw 0.0.2 → 0.0.4

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/e2e_test.cjs ADDED
@@ -0,0 +1,98 @@
1
+ const WebSocket = require('ws');
2
+
3
+ // 测试配置
4
+ const token = '334b209ed0833464202cc4438e7ce801583fc74c13384cbac60d02949f33e09c';
5
+ const host = '101.32.33.231';
6
+ const port = 9876;
7
+
8
+ console.log('🧪 OpenClaw 发送消息测试');
9
+ console.log(`📍 目标服务器: ${host}:${port}`);
10
+ console.log(`🔑 Token: ${token.slice(0, 16)}...\n`);
11
+
12
+ // Step 1: 测试 WebSocket 连接
13
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
14
+ console.log('1️⃣ 连接 WebSocket...');
15
+ const ws = new WebSocket(`ws://${host}:${port}/ws`);
16
+
17
+ ws.on('open', () => {
18
+ console.log(' ✅ WebSocket 连接成功');
19
+ console.log(' 📤 发送认证请求...');
20
+ ws.send(JSON.stringify({ type: 'auth', payload: { token, name: 'e2e-test-client' } }));
21
+ });
22
+
23
+ ws.on('message', (data) => {
24
+ const msg = JSON.parse(data.toString());
25
+ console.log(' 📥 收到消息:', msg.type);
26
+
27
+ if (msg.type === 'auth_result') {
28
+ console.log(' 完整响应:', JSON.stringify(msg.payload, null, 2));
29
+ if (msg.payload.success) {
30
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
31
+ console.log('2️⃣ 认证成功');
32
+ console.log(' ClientID:', msg.payload.clientId);
33
+
34
+ // Step 3: 发送 outbound 消息 (Bot 回复)
35
+ setTimeout(() => {
36
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
37
+ console.log('3️⃣ 发送 outbound 消息...');
38
+
39
+ const requestId = 'req-' + Date.now() + '-test';
40
+ const testMessage = {
41
+ type: 'outbound',
42
+ requestId: requestId,
43
+ payload: {
44
+ to: 'test-user',
45
+ text: '🎉 这是来自 E2E 测试的消息!时间: ' + new Date().toLocaleString('zh-CN'),
46
+ conversationId: 'conv-test-' + Date.now()
47
+ },
48
+ timestamp: Date.now()
49
+ };
50
+
51
+ console.log(' 发送内容:', JSON.stringify(testMessage, null, 2));
52
+ ws.send(JSON.stringify(testMessage));
53
+
54
+ console.log(' 📤 消息已发送,等待服务器确认 (ack)...');
55
+ }, 500);
56
+ } else {
57
+ console.log(' ❌ 认证失败:', msg.payload.message || msg.payload.error);
58
+ ws.close();
59
+ }
60
+ }
61
+
62
+ // 处理服务器的响应
63
+ if (msg.type === 'ack') {
64
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
65
+ console.log('4️⃣ 收到服务器确认 (ack)');
66
+ console.log(' 确认内容:', JSON.stringify(msg.payload, null, 2));
67
+ if (msg.payload?.ok) {
68
+ console.log(' ✅ 消息发送成功!');
69
+ console.log(' 消息ID:', msg.payload.message?.id);
70
+ } else {
71
+ console.log(' ❌ 消息发送失败');
72
+ }
73
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
74
+ console.log('✅ 测试完成!');
75
+ ws.close();
76
+ }
77
+
78
+ if (msg.type === 'error') {
79
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
80
+ console.log('❌ 服务器错误:', JSON.stringify(msg.payload, null, 2));
81
+ ws.close();
82
+ }
83
+ });
84
+
85
+ ws.on('error', (err) => {
86
+ console.log(' ❌ WebSocket 错误:', err.message);
87
+ });
88
+
89
+ ws.on('close', () => {
90
+ console.log('\n📴 连接已关闭');
91
+ process.exit(0);
92
+ });
93
+
94
+ // 超时处理
95
+ setTimeout(() => {
96
+ console.log('\n⏱️ 测试超时 (10秒)');
97
+ ws.close();
98
+ }, 10000);
package/index.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
2
  import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
3
- import { simpleGoPlugin } from "./src/channel.js";
4
- import { setSimpleGoRuntime } from "./src/runtime.js";
3
+ import { adpOpenclawPlugin } from "./src/channel.js";
4
+ import { setAdpOpenclawRuntime } from "./src/runtime.js";
5
5
 
6
6
  const plugin = {
7
- id: "simplego",
8
- name: "Simple Go",
9
- description: "Demo channel plugin backed by a Go HTTP server",
7
+ id: "adp-openclaw",
8
+ name: "ADP OpenClaw",
9
+ description: "ADP channel plugin backed by a Go WebSocket server",
10
10
  configSchema: emptyPluginConfigSchema(),
11
11
  register(api: OpenClawPluginApi) {
12
- setSimpleGoRuntime(api.runtime);
13
- api.registerChannel({ plugin: simpleGoPlugin });
12
+ console.log("[adp-openclaw] register() called");
13
+ setAdpOpenclawRuntime(api.runtime);
14
+ api.registerChannel({ plugin: adpOpenclawPlugin });
14
15
  },
15
16
  };
16
17
 
@@ -1,22 +1,9 @@
1
1
  {
2
- "id": "simplego",
3
- "name": "@openclaw/simplego",
4
- "version": "0.0.1",
5
- "description": "OpenClaw demo channel plugin (Go HTTP backend)",
2
+ "id": "adp-openclaw",
3
+ "channels": ["adp-openclaw"],
6
4
  "configSchema": {
7
5
  "type": "object",
8
- "properties": {},
9
- "additionalProperties": false
10
- },
11
- "extensions": [
12
- "./index.ts"
13
- ],
14
- "channel": {
15
- "id": "simplego",
16
- "label": "Simple Go",
17
- "selectionLabel": "Simple Go (demo)",
18
- "docsPath": "/channels/simplego",
19
- "blurb": "demo channel backed by a Go HTTP server.",
20
- "order": 999
6
+ "additionalProperties": false,
7
+ "properties": {}
21
8
  }
22
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adp-openclaw",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "OpenClaw demo channel plugin (Go WebSocket backend)",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -15,11 +15,11 @@
15
15
  "./index.ts"
16
16
  ],
17
17
  "channel": {
18
- "id": "adp",
19
- "label": "Simple Go",
20
- "selectionLabel": "Simple Go (demo)",
21
- "docsPath": "/channels/adp",
22
- "blurb": "demo channel backed by a Go HTTP server.",
18
+ "id": "adp-openclaw",
19
+ "label": "ADP OpenClaw",
20
+ "selectionLabel": "ADP OpenClaw",
21
+ "docsPath": "/channels/adp-openclaw",
22
+ "blurb": "ADP channel backed by a Go WebSocket server.",
23
23
  "order": 999
24
24
  }
25
25
  }
package/src/channel.ts CHANGED
@@ -1,25 +1,23 @@
1
- // Simple Go channel plugin for OpenClaw
1
+ // ADP OpenClaw channel plugin for OpenClaw
2
2
  // Supports: API Token auth, multiple clients, multi-turn conversations
3
3
 
4
4
  import {
5
5
  type ChannelPlugin,
6
+ type ClawdbotConfig,
6
7
  DEFAULT_ACCOUNT_ID,
7
- setAccountEnabledInConfigSection,
8
- deleteAccountFromConfigSection,
9
8
  } from "openclaw/plugin-sdk";
10
9
 
11
- export type SimpleGoConfig = {
12
- channels?: {
13
- simplego?: {
14
- enabled?: boolean;
15
- serverUrl?: string;
16
- apiToken?: string; // Client API token for authentication
17
- pollIntervalMs?: number;
18
- };
19
- };
10
+ // Channel-level config type (from channels["adp-openclaw"])
11
+ export type AdpOpenclawChannelConfig = {
12
+ enabled?: boolean;
13
+ serverUrl?: string;
14
+ wsUrl?: string;
15
+ apiToken?: string;
16
+ clientToken?: string;
17
+ pollIntervalMs?: number;
20
18
  };
21
19
 
22
- export type ResolvedSimpleGoAccount = {
20
+ export type ResolvedAdpOpenclawAccount = {
23
21
  accountId: string;
24
22
  name: string;
25
23
  enabled: boolean;
@@ -29,35 +27,67 @@ export type ResolvedSimpleGoAccount = {
29
27
  pollIntervalMs: number;
30
28
  };
31
29
 
32
- function resolveAccount(cfg: SimpleGoConfig, accountId?: string): ResolvedSimpleGoAccount {
33
- const section = cfg.channels?.simplego ?? {};
34
- // Use environment variables as fallback
35
- const serverUrl = section.serverUrl?.trim() ||
36
- process.env.SIMPLEGO_SERVER_URL ||
37
- "http://localhost:9876";
38
- const apiToken = section.apiToken?.trim() ||
39
- process.env.SIMPLEGO_API_TOKEN ||
40
- "92527c1a861bb50bd1acc47180ef317369e97529c7a5491b2bc67839992cfbb8";
30
+ function resolveAdpOpenclawCredentials(channelCfg?: AdpOpenclawChannelConfig): {
31
+ serverUrl: string;
32
+ apiToken: string;
33
+ pollIntervalMs: number;
34
+ } | null {
35
+ // Get serverUrl: support both serverUrl and wsUrl
36
+ let serverUrl = channelCfg?.serverUrl?.trim();
37
+ if (!serverUrl && channelCfg?.wsUrl) {
38
+ // Convert ws://host:port/ws to http://host:port
39
+ serverUrl = channelCfg.wsUrl
40
+ .replace(/^wss:\/\//, "https://")
41
+ .replace(/^ws:\/\//, "http://")
42
+ .replace(/\/ws\/?$/, "");
43
+ }
44
+ if (!serverUrl) {
45
+ serverUrl = process.env.ADP_OPENCLAW_SERVER_URL || "";
46
+ }
47
+
48
+ // Get apiToken: support both apiToken and clientToken
49
+ let apiToken = channelCfg?.apiToken?.trim();
50
+ if (!apiToken && channelCfg?.clientToken) {
51
+ apiToken = channelCfg.clientToken.trim();
52
+ }
53
+ if (!apiToken) {
54
+ apiToken = process.env.ADP_OPENCLAW_API_TOKEN || "";
55
+ }
56
+
57
+ // Both are required for configured status
58
+ if (!serverUrl || !apiToken) {
59
+ return null;
60
+ }
61
+
62
+ const pollIntervalMs = channelCfg?.pollIntervalMs ?? 1000;
63
+
64
+ return { serverUrl, apiToken, pollIntervalMs };
65
+ }
66
+
67
+ function resolveAccount(cfg: ClawdbotConfig, accountId?: string): ResolvedAdpOpenclawAccount {
68
+ const channelCfg = cfg.channels?.["adp-openclaw"] as AdpOpenclawChannelConfig | undefined;
69
+ const enabled = channelCfg?.enabled !== false;
70
+ const creds = resolveAdpOpenclawCredentials(channelCfg);
41
71
 
42
72
  return {
43
- accountId: accountId || DEFAULT_ACCOUNT_ID,
44
- name: "Simple Go",
45
- enabled: section.enabled !== false,
46
- configured: Boolean(serverUrl && apiToken), // Always configured if we have values
47
- serverUrl,
48
- apiToken,
49
- pollIntervalMs: section.pollIntervalMs ?? 1000,
73
+ accountId: accountId?.trim() || DEFAULT_ACCOUNT_ID,
74
+ name: "ADP OpenClaw",
75
+ enabled,
76
+ configured: Boolean(creds),
77
+ serverUrl: creds?.serverUrl || "http://localhost:9876",
78
+ apiToken: creds?.apiToken || "",
79
+ pollIntervalMs: creds?.pollIntervalMs || 1000,
50
80
  };
51
81
  }
52
82
 
53
- export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
54
- id: "simplego",
83
+ export const adpOpenclawPlugin: ChannelPlugin<ResolvedAdpOpenclawAccount> = {
84
+ id: "adp-openclaw",
55
85
  meta: {
56
- id: "simplego",
57
- label: "Simple Go",
58
- selectionLabel: "Simple Go (demo)",
59
- docsPath: "/channels/simplego",
60
- blurb: "Demo channel backed by a Go HTTP server.",
86
+ id: "adp-openclaw",
87
+ label: "ADP OpenClaw",
88
+ selectionLabel: "ADP OpenClaw",
89
+ docsPath: "/channels/adp-openclaw",
90
+ blurb: "ADP channel backed by a Go WebSocket server.",
61
91
  order: 999,
62
92
  },
63
93
  capabilities: {
@@ -67,27 +97,48 @@ export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
67
97
  threads: false,
68
98
  media: false,
69
99
  },
70
- reload: { configPrefixes: ["channels.simplego"] },
100
+ reload: { configPrefixes: ["channels.adp-openclaw"] },
101
+ configSchema: {
102
+ schema: {
103
+ type: "object",
104
+ additionalProperties: false,
105
+ properties: {
106
+ enabled: { type: "boolean" },
107
+ serverUrl: { type: "string" },
108
+ wsUrl: { type: "string" },
109
+ apiToken: { type: "string" },
110
+ clientToken: { type: "string" },
111
+ pollIntervalMs: { type: "integer", minimum: 100 },
112
+ },
113
+ },
114
+ },
71
115
  config: {
72
116
  listAccountIds: () => [DEFAULT_ACCOUNT_ID],
73
- resolveAccount: (cfg, accountId) => resolveAccount(cfg as SimpleGoConfig, accountId),
117
+ resolveAccount: (cfg) => resolveAccount(cfg),
74
118
  defaultAccountId: () => DEFAULT_ACCOUNT_ID,
75
- setAccountEnabled: ({ cfg, accountId, enabled }) =>
76
- setAccountEnabledInConfigSection({
77
- cfg: cfg as SimpleGoConfig,
78
- sectionKey: "simplego",
79
- accountId,
80
- enabled,
81
- allowTopLevel: true,
82
- }),
83
- deleteAccount: ({ cfg, accountId }) =>
84
- deleteAccountFromConfigSection({
85
- cfg: cfg as SimpleGoConfig,
86
- sectionKey: "simplego",
87
- accountId,
88
- clearBaseFields: ["serverUrl", "apiToken", "pollIntervalMs"],
89
- }),
90
- isConfigured: (account) => account.configured,
119
+ setAccountEnabled: ({ cfg, enabled }) => ({
120
+ ...cfg,
121
+ channels: {
122
+ ...cfg.channels,
123
+ "adp-openclaw": {
124
+ ...cfg.channels?.["adp-openclaw"],
125
+ enabled,
126
+ },
127
+ },
128
+ }),
129
+ deleteAccount: ({ cfg }) => {
130
+ const next = { ...cfg } as ClawdbotConfig;
131
+ const nextChannels = { ...cfg.channels };
132
+ delete (nextChannels as Record<string, unknown>)["adp-openclaw"];
133
+ if (Object.keys(nextChannels).length > 0) {
134
+ next.channels = nextChannels;
135
+ } else {
136
+ delete next.channels;
137
+ }
138
+ return next;
139
+ },
140
+ isConfigured: (_account, cfg) =>
141
+ Boolean(resolveAdpOpenclawCredentials(cfg.channels?.["adp-openclaw"] as AdpOpenclawChannelConfig | undefined)),
91
142
  describeAccount: (account) => ({
92
143
  accountId: account.accountId,
93
144
  name: account.name,
@@ -100,38 +151,16 @@ export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
100
151
  },
101
152
  setup: {
102
153
  resolveAccountId: () => DEFAULT_ACCOUNT_ID,
103
- validateInput: ({ input }: { input: Record<string, string | undefined> }) => {
104
- // Accept both standard names (url, token) and custom names (serverUrl, apiToken)
105
- const serverUrl = input.url || input.serverUrl;
106
- const apiToken = input.token || input.apiToken;
107
-
108
- if (!serverUrl?.trim()) {
109
- return "Simple Go requires --url (server URL)";
110
- }
111
- if (!apiToken?.trim()) {
112
- return "Simple Go requires --token (API token)";
113
- }
114
- return null;
115
- },
116
- applyAccountConfig: ({ cfg, input }) => {
117
- const existing = (cfg as SimpleGoConfig).channels?.simplego ?? {};
118
- // Accept both standard names and custom names
119
- const serverUrl = input.url || input.serverUrl;
120
- const apiToken = input.token || input.apiToken;
121
-
122
- return {
123
- ...cfg,
124
- channels: {
125
- ...(cfg as SimpleGoConfig).channels,
126
- simplego: {
127
- ...existing,
128
- enabled: true,
129
- serverUrl: serverUrl?.trim() || existing.serverUrl || "http://localhost:9876",
130
- apiToken: apiToken?.trim() || existing.apiToken,
131
- },
154
+ applyAccountConfig: ({ cfg }) => ({
155
+ ...cfg,
156
+ channels: {
157
+ ...cfg.channels,
158
+ "adp-openclaw": {
159
+ ...cfg.channels?.["adp-openclaw"],
160
+ enabled: true,
132
161
  },
133
- };
134
- },
162
+ },
163
+ }),
135
164
  },
136
165
  status: {
137
166
  defaultRuntime: {
@@ -151,7 +180,7 @@ export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
151
180
  lastError: snapshot.lastError ?? null,
152
181
  }),
153
182
  probeAccount: async ({ cfg, timeoutMs }) => {
154
- const account = resolveAccount(cfg as SimpleGoConfig);
183
+ const account = resolveAccount(cfg);
155
184
  const start = Date.now();
156
185
  try {
157
186
  const controller = new AbortController();
@@ -188,15 +217,16 @@ export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
188
217
  startAccount: async (ctx) => {
189
218
  const account = ctx.account;
190
219
  ctx.setStatus({ accountId: account.accountId, baseUrl: account.serverUrl });
191
- ctx.log?.info(`[simplego] starting poll loop → ${account.serverUrl}`);
220
+ ctx.log?.info(`[adp-openclaw] starting poll loop → ${account.serverUrl}`);
192
221
 
193
- const { monitorSimpleGo } = await import("./monitor.js");
194
- return monitorSimpleGo({
222
+ const { monitorAdpOpenclaw } = await import("./monitor.js");
223
+ return monitorAdpOpenclaw({
195
224
  serverUrl: account.serverUrl,
196
225
  apiToken: account.apiToken,
197
226
  pollIntervalMs: account.pollIntervalMs,
198
227
  abortSignal: ctx.abortSignal,
199
228
  log: ctx.log,
229
+ cfg: ctx.cfg,
200
230
  });
201
231
  },
202
232
  },
@@ -212,7 +242,7 @@ export const simpleGoPlugin: ChannelPlugin<ResolvedSimpleGoAccount> = {
212
242
  tenantId?: string;
213
243
  };
214
244
  }) => {
215
- const account = resolveAccount(cfg as SimpleGoConfig);
245
+ const account = resolveAccount(cfg as ClawdbotConfig);
216
246
  const conversationId = context?.conversationId || "";
217
247
 
218
248
  // Extract user info from context for response tracing
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
 
3
- export const SimpleGoConfigSchema = z.object({
3
+ export const AdpOpenclawConfigSchema = z.object({
4
4
  enabled: z.boolean().optional(),
5
5
  serverUrl: z.string().optional(),
6
6
  apiToken: z.string().optional(),