gewe-openclaw 2026.3.25 → 2026.3.27

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/README.md CHANGED
@@ -95,7 +95,7 @@ openclaw onboard
95
95
  - `s3PublicBaseUrl`:`public` 模式下用于拼接可访问 URL(必填)。
96
96
  - `s3PresignExpiresSec`:`presigned` 模式签名有效期(默认 3600 秒)。
97
97
  - `s3KeyPrefix`:对象 key 前缀(默认 `gewe-openclaw/outbound`)。
98
- - `allowFrom`:允许私聊触发的微信 ID(或在群里走 allowlist 规则)。
98
+ - `allowFrom`:允许私聊触发的微信 ID;不再隐式用于群聊权限。
99
99
  - `voiceAutoConvert`:自动将音频转为 silk(默认开启;设为 `false` 可关闭)。
100
100
  - `silkAutoDownload`:自动下载 `rust-silk`(默认开启;可关闭后自行配置 `voiceSilkPath` / `voiceDecodePath`)。
101
101
  - `silkVersion`:自动下载的 `rust-silk` 版本(`latest` 会自动清理旧版本)。
@@ -238,6 +238,11 @@ GeWe 现在补齐了目录、标准 allowlist 适配和状态摘要。
238
238
  - 私聊:`allowFrom`
239
239
  - 群发言人:`groupAllowFrom`
240
240
 
241
+ 注意:
242
+
243
+ - 私聊 `pairing` 只会把对方加入私聊 allowlist,不会自动给任何群开权限。
244
+ - 群聊权限只看 `groupAllowFrom` 和 `groups.<groupId>.allowFrom`。
245
+
241
246
  如果你要管理“某一个群自己的 `groups.<groupId>.allowFrom` 覆盖”,请用插件工具:
242
247
 
243
248
  - `gewe_manage_group_allowlist`
@@ -260,6 +265,75 @@ GeWe 现在补齐了目录、标准 allowlist 适配和状态摘要。
260
265
  }
261
266
  ```
262
267
 
268
+ ### 认证方式
269
+
270
+ 如果你是第一次用,可以把它理解成两步:
271
+
272
+ 1. 先在私聊里完成配对
273
+ 2. 再在要使用的群里完成认领
274
+
275
+ 为什么要分成两步:
276
+
277
+ - 私聊配对成功,只表示“你可以在私聊里跟机器人说话了”
278
+ - 这一步不会让机器人自动在所有群里生效
279
+ - 群认领是第二次确认,表示“这个群也允许你使用机器人”
280
+ - 这样做是为了避免机器人一进公开群就被任何人直接触发
281
+
282
+ 你实际要做的事很简单:
283
+
284
+ 1. 先和机器人私聊,完成配对
285
+ 2. 再在私聊里让机器人给你一个 8 位认领码
286
+ 3. 把机器人拉进目标群
287
+ 4. 在目标群里直接发这 8 位认领码
288
+ 5. 收到成功提示后,这个群就接入完成了
289
+
290
+ 举个例子:
291
+
292
+ - 机器人在私聊里给你:`MJSQ2G9H`
293
+ - 你把机器人拉进目标群
294
+ - 你在群里直接发:`MJSQ2G9H`
295
+
296
+ 几个容易搞混的点:
297
+
298
+ - 群里不需要 `@机器人`
299
+ - 最推荐的发法就是只发这 8 位码
300
+ - `认领码: MJSQ2G9H` 这种写法也能识别,但默认不推荐
301
+ - 认领码有时效,而且只能用一次
302
+ - 这个码是谁拿到的,就只能由谁来完成这次认领
303
+
304
+ 认领成功后会发生什么:
305
+
306
+ - 机器人会记住“你可以在这个群里使用它了”
307
+ - 只影响当前这个群
308
+ - 不会顺手把别的群也打开
309
+ - 不会自动帮你创建额外的绑定配置
310
+
311
+ 如果你会自己改配置文件:
312
+
313
+ - 认领成功后,插件会把当前发码者写入 `groups.<groupId>.allowFrom`
314
+
315
+ ### 群认领
316
+
317
+ 如果是一个还没放行的新群,推荐流程是:
318
+
319
+ 1. 先在已配对的私聊里调用 `gewe_issue_group_claim_code`
320
+ 2. 把机器人拉进目标群
321
+ 3. 在目标群里只发送这 8 位认领码:`XXXXXXXX`
322
+
323
+ 推荐默认话术:
324
+
325
+ - 私聊里直接把 8 位码发给用户,不要包装成 `认领码: XXXXXXXX`
326
+ - 群里也只发这 8 位码,不要加 `认领码:` 前缀
327
+
328
+ 认领成功后,插件会把“当前发码者”写入该群的 `groups.<groupId>.allowFrom`。
329
+
330
+ 默认特性:
331
+
332
+ - 认领码短时有效、单次使用
333
+ - 只能由签发它的那个发码者在群里使用
334
+ - 只给当前群开权限,不会改顶层 `groupAllowFrom`
335
+ - 不会自动创建 `bindings[]`
336
+
263
337
  如果你就在目标群里调用,`groupId` 可以省略;工具会自动用当前群。
264
338
 
265
339
  ### 状态
package/index.ts CHANGED
@@ -1,11 +1,17 @@
1
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
1
+ import type { OpenClawPluginApi } from "./src/openclaw-compat.js";
2
2
 
3
3
  import { createGeweApiTools } from "./src/api-tools.js";
4
4
  import { gewePlugin } from "./src/channel.js";
5
5
  import { createGeweManageGroupAllowlistTool } from "./src/group-allowlist-tool.js";
6
6
  import { createGeweSyncGroupBindingTool } from "./src/group-binding-tool.js";
7
+ import { createGeweIssueGroupClaimCodeTool } from "./src/group-claim-tool.js";
7
8
  import { setGeweRuntime } from "./src/runtime.js";
8
9
 
10
+ type CompatRegistrationMode = "full" | "setup-only" | "setup-runtime";
11
+ type CompatOpenClawPluginApi = OpenClawPluginApi & {
12
+ registrationMode?: CompatRegistrationMode;
13
+ };
14
+
9
15
  function emptyPluginConfigSchema() {
10
16
  return {
11
17
  safeParse(value: unknown) {
@@ -40,10 +46,18 @@ const plugin = {
40
46
  description: "OpenClaw GeWe channel plugin",
41
47
  configSchema: emptyPluginConfigSchema(),
42
48
  register(api: OpenClawPluginApi) {
49
+ const compatApi = api as CompatOpenClawPluginApi;
50
+
43
51
  setGeweRuntime(api.runtime);
44
52
  api.registerChannel({ plugin: gewePlugin });
53
+
54
+ if (compatApi.registrationMode && compatApi.registrationMode !== "full") {
55
+ return;
56
+ }
57
+
45
58
  api.registerTool((ctx) => createGeweApiTools(ctx));
46
59
  api.registerTool((ctx) => createGeweSyncGroupBindingTool(ctx));
60
+ api.registerTool((ctx) => createGeweIssueGroupClaimCodeTool(ctx));
47
61
  api.registerTool((ctx) =>
48
62
  createGeweManageGroupAllowlistTool(ctx, {
49
63
  readConfig: () => api.runtime.config.loadConfig() as never,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gewe-openclaw",
3
- "version": "2026.3.25",
3
+ "version": "2026.3.27",
4
4
  "type": "module",
5
5
  "description": "OpenClaw GeWe channel plugin",
6
6
  "license": "MIT",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "index.ts",
13
+ "setup-entry.ts",
13
14
  "openclaw.plugin.json",
14
15
  "assets/gewe-rs_logo.jpeg",
15
16
  "skills/**",
@@ -20,6 +21,7 @@
20
21
  "extensions": [
21
22
  "./index.ts"
22
23
  ],
24
+ "setupEntry": "./setup-entry.ts",
23
25
  "channel": {
24
26
  "id": "gewe-openclaw",
25
27
  "label": "GeWe",
package/setup-entry.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { geweSetupPlugin } from "./src/channel.setup.js";
2
+
3
+ export { geweSetupPlugin } from "./src/channel.setup.js";
4
+
5
+ // Keep the setup entry as a plain `{ plugin }` export so newer OpenClaw can
6
+ // load the channel surface without requiring newer runtime helpers at import time.
7
+ export default {
8
+ plugin: geweSetupPlugin,
9
+ };
@@ -14,12 +14,13 @@ metadata:
14
14
 
15
15
  # GeWe Agent Tools
16
16
 
17
- 优先把这四个工具当成 GeWe 的正式操作面:
17
+ 优先把这五个工具当成 GeWe 的正式操作面:
18
18
 
19
19
  - `gewe_contacts`
20
20
  - `gewe_groups`
21
21
  - `gewe_moments`
22
22
  - `gewe_personal`
23
+ - `gewe_issue_group_claim_code`
23
24
 
24
25
  ## 什么时候用哪个
25
26
 
@@ -27,10 +28,48 @@ metadata:
27
28
  - 处理群资料、群成员、群公告、群管理:用 `gewe_groups`
28
29
  - 处理朋友圈浏览、点赞、评论、发布、转发:用 `gewe_moments`
29
30
  - 处理当前登录微信自己的资料、二维码、安全信息、隐私设置:用 `gewe_personal`
31
+ - 处理新群接入、给当前已配对私聊签发群认领码:用 `gewe_issue_group_claim_code`
30
32
 
31
33
  如果只是想知道“现在这个私聊对象是谁”,优先用 `gewe_contacts`。
32
34
  如果只是想知道“现在这个群是谁、有哪些成员”,优先用 `gewe_groups`。
33
35
 
36
+ ## 新群接入
37
+
38
+ 当用户在 GeWe 当前私聊会话里表达这些意思时,优先进入“群认领”流程,而不是旧的手工绑群流程:
39
+
40
+ - “我要新绑定一个群”
41
+ - “给这个新群开权限”
42
+ - “把机器人接入一个新群”
43
+
44
+ 这时优先调用 `gewe_issue_group_claim_code`。
45
+
46
+ 关键原则:
47
+
48
+ - 不要先让用户手填群 id
49
+ - 不要先让用户自己改配置
50
+ - 不要把“私聊已经 pairing 成功”理解成“群已经自动放行”
51
+
52
+ 标准动作:
53
+
54
+ 1. 在当前私聊会话里调用 `gewe_issue_group_claim_code`
55
+ 2. 把返回的 8 位认领码原样发给用户
56
+ 3. 明确告诉用户:先把机器人拉进目标新群
57
+ 4. 再让用户在目标群里只发送这 8 位认领码,例如:`XXXXXXXX`
58
+
59
+ 重要话术约束:
60
+
61
+ - 默认只把 8 位码发给用户,不要包装成 `认领码: XXXXXXXX`
62
+ - 默认明确提醒:群里也只发这 8 位码,不要加 `认领码:` 前缀
63
+
64
+ 推荐话术要点:
65
+
66
+ - 这是短时有效、单次使用的认领码
67
+ - 认领成功后,只会授权当前发码者在这个新群里触发机器人
68
+ - 如果只是想查新群资料,再在当前群会话里用 `gewe_groups`
69
+
70
+ 只有在用户已经明确处在目标群里,且需求是“查当前群信息 / 查成员 / 查群 id”时,才优先使用 `gewe_groups`。
71
+ 如果需求是“接入一个还没放行的新群”,先发认领码,再谈群信息。
72
+
34
73
  ## 当前会话推断
35
74
 
36
75
  有些 action 可以少填参数,优先利用当前会话:
package/src/api-tools.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { AnyAgentTool, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
2
1
  import { z } from "zod";
3
2
 
4
3
  import { resolveGeweAccount } from "./accounts.js";
@@ -65,7 +64,13 @@ import {
65
64
  uploadSnsVideoGewe,
66
65
  } from "./moments-api.js";
67
66
  import { normalizeGeweMessagingTarget } from "./normalize.js";
68
- import { normalizeAccountId, type OpenClawConfig } from "./openclaw-compat.js";
67
+ import {
68
+ buildJsonSchema,
69
+ normalizeAccountId,
70
+ type AnyAgentTool,
71
+ type OpenClawConfig,
72
+ type OpenClawPluginToolContext,
73
+ } from "./openclaw-compat.js";
69
74
  import {
70
75
  getProfileGewe,
71
76
  getQrCodeGewe,
@@ -74,6 +79,7 @@ import {
74
79
  updateHeadImgGewe,
75
80
  updateProfileGewe,
76
81
  } from "./personal-api.js";
82
+ import { shouldExposeGeweAgentTool } from "./tool-visibility.js";
77
83
  import type { CoreConfig, ResolvedGeweAccount } from "./types.js";
78
84
 
79
85
  const ContactsActionSchema = z.enum([
@@ -243,6 +249,11 @@ const PersonalToolSchema = z
243
249
  })
244
250
  .strict();
245
251
 
252
+ const ContactsToolParameters = buildJsonSchema(ContactsToolSchema) ?? { type: "object" };
253
+ const GroupsToolParameters = buildJsonSchema(GroupsToolSchema) ?? { type: "object" };
254
+ const MomentsToolParameters = buildJsonSchema(MomentsToolSchema) ?? { type: "object" };
255
+ const PersonalToolParameters = buildJsonSchema(PersonalToolSchema) ?? { type: "object" };
256
+
246
257
  type ContactsToolParams = z.infer<typeof ContactsToolSchema>;
247
258
  type GroupsToolParams = z.infer<typeof GroupsToolSchema>;
248
259
  type MomentsToolParams = z.infer<typeof MomentsToolSchema>;
@@ -1150,15 +1161,17 @@ async function executePersonalTool(
1150
1161
  }
1151
1162
  }
1152
1163
 
1153
- export function createGeweApiTools(ctx: OpenClawPluginToolContext): AnyAgentTool[] {
1164
+ export function createGeweApiTools(ctx: OpenClawPluginToolContext): AnyAgentTool[] | null {
1165
+ if (!shouldExposeGeweAgentTool(ctx)) {
1166
+ return null;
1167
+ }
1154
1168
  return [
1155
1169
  {
1156
1170
  name: "gewe_contacts",
1157
1171
  label: "GeWe Contacts",
1158
1172
  description:
1159
1173
  "GeWe contacts operations. Actions: list, list_cache, brief, detail, search, search_im, im_detail, check_relation, set_remark, set_only_chat, delete, add, add_im, sync_im, phones_get, phones_upload.",
1160
- ownerOnly: true,
1161
- parameters: ContactsToolSchema,
1174
+ parameters: ContactsToolParameters,
1162
1175
  execute: async (_toolCallId, rawParams) => {
1163
1176
  const params = ContactsToolSchema.parse(rawParams ?? {});
1164
1177
  try {
@@ -1184,8 +1197,7 @@ export function createGeweApiTools(ctx: OpenClawPluginToolContext): AnyAgentTool
1184
1197
  label: "GeWe Groups",
1185
1198
  description:
1186
1199
  "GeWe group operations. Actions: info, announcement, members, member_detail, qr_code, set_self_nickname, rename, set_remark, create, remove_members, agree_join, join_via_qr, add_member_as_friend, approve_join_request, admin_operate, save_to_contacts, pin, disband, set_silence, set_announcement, quit, invite.",
1187
- ownerOnly: true,
1188
- parameters: GroupsToolSchema,
1200
+ parameters: GroupsToolParameters,
1189
1201
  execute: async (_toolCallId, rawParams) => {
1190
1202
  const params = GroupsToolSchema.parse(rawParams ?? {});
1191
1203
  try {
@@ -1211,8 +1223,7 @@ export function createGeweApiTools(ctx: OpenClawPluginToolContext): AnyAgentTool
1211
1223
  label: "GeWe Moments",
1212
1224
  description:
1213
1225
  "GeWe Moments operations. Actions: list_self, list_contact, detail, download_video, upload_image, upload_video, delete, post_text, post_image, post_video, post_link, set_stranger_visibility, set_visible_scope, set_privacy, like, comment, forward.",
1214
- ownerOnly: true,
1215
- parameters: MomentsToolSchema,
1226
+ parameters: MomentsToolParameters,
1216
1227
  execute: async (_toolCallId, rawParams) => {
1217
1228
  const params = MomentsToolSchema.parse(rawParams ?? {});
1218
1229
  try {
@@ -1238,8 +1249,7 @@ export function createGeweApiTools(ctx: OpenClawPluginToolContext): AnyAgentTool
1238
1249
  label: "GeWe Personal",
1239
1250
  description:
1240
1251
  "GeWe personal-account operations. Actions: profile, qrcode, safety_info, update_profile, update_avatar, privacy.",
1241
- ownerOnly: true,
1242
- parameters: PersonalToolSchema,
1252
+ parameters: PersonalToolParameters,
1243
1253
  execute: async (_toolCallId, rawParams) => {
1244
1254
  const params = PersonalToolSchema.parse(rawParams ?? {});
1245
1255
  try {
@@ -3,7 +3,7 @@ import type {
3
3
  ChannelMessageActionAdapter,
4
4
  ChannelMessageActionName,
5
5
  ChannelMessageToolSchemaContribution,
6
- } from "openclaw/plugin-sdk/channel-runtime";
6
+ } from "./openclaw-compat.js";
7
7
 
8
8
  import { listEnabledGeweAccounts, resolveGeweAccount } from "./accounts.js";
9
9
  import { deliverGewePayload } from "./delivery.js";
@@ -1,11 +1,13 @@
1
- import type { ChannelAllowlistAdapter } from "openclaw/plugin-sdk/channel-runtime";
2
-
3
1
  import { resolveGeweAccount } from "./accounts.js";
4
2
  import { collectKnownGeweGroupEntries } from "./channel-directory.js";
5
3
  import { cleanupEmptyObject, ensureGeweWriteSectionInPlace } from "./config-edit.js";
6
4
  import { CHANNEL_CONFIG_KEY, stripChannelPrefix } from "./constants.js";
7
5
  import { normalizeGeweMessagingTarget } from "./normalize.js";
8
- import { normalizeAccountId, type OpenClawConfig } from "./openclaw-compat.js";
6
+ import {
7
+ normalizeAccountId,
8
+ type ChannelAllowlistAdapter,
9
+ type OpenClawConfig,
10
+ } from "./openclaw-compat.js";
9
11
  import type { CoreConfig } from "./types.js";
10
12
  import { resolveCachedGeweName } from "./directory-cache.js";
11
13
 
@@ -0,0 +1,184 @@
1
+ import {
2
+ applyAccountNameToChannelSection,
3
+ buildChannelConfigSchema,
4
+ DEFAULT_ACCOUNT_ID,
5
+ deleteAccountFromConfigSection,
6
+ normalizeAccountId,
7
+ setAccountEnabledInConfigSection,
8
+ type ChannelPlugin,
9
+ type ChannelSetupInput,
10
+ type OpenClawConfig,
11
+ } from "./openclaw-compat.js";
12
+
13
+ import { resolveDefaultGeweAccountId, listGeweAccountIds, resolveGeweAccount } from "./accounts.js";
14
+ import { GeweConfigSchema } from "./config-schema.js";
15
+ import {
16
+ CHANNEL_ALIASES,
17
+ CHANNEL_CONFIG_KEY,
18
+ CHANNEL_DOCS_LABEL,
19
+ CHANNEL_DOCS_PATH,
20
+ CHANNEL_ID,
21
+ stripChannelPrefix,
22
+ } from "./constants.js";
23
+ import { geweSetupWizard } from "./setup-wizard.js";
24
+ import type { CoreConfig, ResolvedGeweAccount } from "./types.js";
25
+
26
+ type GeweSetupInput = ChannelSetupInput & {
27
+ token?: string;
28
+ tokenFile?: string;
29
+ appId?: string;
30
+ appIdFile?: string;
31
+ apiBaseUrl?: string;
32
+ };
33
+
34
+ export const geweChannelMeta = {
35
+ id: CHANNEL_ID,
36
+ label: "GeWe",
37
+ selectionLabel: "WeChat (GeWe)",
38
+ detailLabel: "WeChat (GeWe)",
39
+ docsPath: CHANNEL_DOCS_PATH,
40
+ docsLabel: CHANNEL_DOCS_LABEL,
41
+ blurb: "WeChat channel via GeWe API and webhook callbacks.",
42
+ aliases: [...CHANNEL_ALIASES],
43
+ order: 72,
44
+ quickstartAllowFrom: true,
45
+ } as const;
46
+
47
+ export const geweSetupAdapter: NonNullable<ChannelPlugin<ResolvedGeweAccount>["setup"]> = {
48
+ resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
49
+ applyAccountName: ({ cfg, accountId, name }) =>
50
+ applyAccountNameToChannelSection({
51
+ cfg: cfg as OpenClawConfig,
52
+ channelKey: CHANNEL_CONFIG_KEY,
53
+ accountId,
54
+ name,
55
+ }),
56
+ validateInput: ({ accountId, input }) => {
57
+ const setupInput = input as GeweSetupInput;
58
+ if (setupInput.useEnv && accountId !== DEFAULT_ACCOUNT_ID) {
59
+ return "GEWE_TOKEN/GEWE_APP_ID can only be used for the default account.";
60
+ }
61
+ if (!setupInput.useEnv && !setupInput.token && !setupInput.tokenFile) {
62
+ return "GeWe requires --token or --token-file (or --use-env).";
63
+ }
64
+ if (!setupInput.useEnv && !setupInput.appId && !setupInput.appIdFile) {
65
+ return "GeWe requires --app-id or --app-id-file (or --use-env).";
66
+ }
67
+ return null;
68
+ },
69
+ applyAccountConfig: ({ cfg, accountId, input }) => {
70
+ const setupInput = input as GeweSetupInput;
71
+ const namedConfig = applyAccountNameToChannelSection({
72
+ cfg: cfg as OpenClawConfig,
73
+ channelKey: CHANNEL_CONFIG_KEY,
74
+ accountId,
75
+ name: setupInput.name,
76
+ });
77
+ const section = (namedConfig.channels?.[CHANNEL_CONFIG_KEY] ?? {}) as Record<
78
+ string,
79
+ unknown
80
+ > & {
81
+ accounts?: Record<string, Record<string, unknown>>;
82
+ };
83
+ const useAccountPath = accountId !== DEFAULT_ACCOUNT_ID;
84
+ const base = useAccountPath ? section.accounts?.[accountId] ?? {} : section;
85
+ const nextEntry = {
86
+ ...base,
87
+ ...(setupInput.apiBaseUrl ? { apiBaseUrl: setupInput.apiBaseUrl } : {}),
88
+ ...(setupInput.useEnv
89
+ ? {}
90
+ : setupInput.token
91
+ ? { token: setupInput.token }
92
+ : setupInput.tokenFile
93
+ ? { tokenFile: setupInput.tokenFile }
94
+ : {}),
95
+ ...(setupInput.useEnv
96
+ ? {}
97
+ : setupInput.appId
98
+ ? { appId: setupInput.appId }
99
+ : setupInput.appIdFile
100
+ ? { appIdFile: setupInput.appIdFile }
101
+ : {}),
102
+ };
103
+ if (!useAccountPath) {
104
+ return {
105
+ ...namedConfig,
106
+ channels: {
107
+ ...namedConfig.channels,
108
+ [CHANNEL_CONFIG_KEY]: nextEntry,
109
+ },
110
+ };
111
+ }
112
+ return {
113
+ ...namedConfig,
114
+ channels: {
115
+ ...namedConfig.channels,
116
+ [CHANNEL_CONFIG_KEY]: {
117
+ ...section,
118
+ accounts: {
119
+ ...(section.accounts as Record<string, unknown> | undefined),
120
+ [accountId]: nextEntry,
121
+ },
122
+ },
123
+ },
124
+ };
125
+ },
126
+ };
127
+
128
+ export const geweChannelPluginCommon = {
129
+ meta: geweChannelMeta,
130
+ setupWizard: geweSetupWizard,
131
+ capabilities: {
132
+ chatTypes: ["direct", "group"],
133
+ reactions: false,
134
+ threads: false,
135
+ media: true,
136
+ nativeCommands: false,
137
+ blockStreaming: true,
138
+ },
139
+ reload: { configPrefixes: [`channels.${CHANNEL_CONFIG_KEY}`] },
140
+ configSchema: buildChannelConfigSchema(GeweConfigSchema),
141
+ config: {
142
+ listAccountIds: (cfg: OpenClawConfig) => listGeweAccountIds(cfg as CoreConfig),
143
+ resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) =>
144
+ resolveGeweAccount({ cfg: cfg as CoreConfig, accountId }),
145
+ defaultAccountId: (cfg: OpenClawConfig) => resolveDefaultGeweAccountId(cfg as CoreConfig),
146
+ setAccountEnabled: ({ cfg, accountId, enabled }: { cfg: OpenClawConfig; accountId: string; enabled: boolean }) =>
147
+ setAccountEnabledInConfigSection({
148
+ cfg,
149
+ sectionKey: CHANNEL_CONFIG_KEY,
150
+ accountId,
151
+ enabled,
152
+ allowTopLevel: true,
153
+ }),
154
+ deleteAccount: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId: string }) =>
155
+ deleteAccountFromConfigSection({
156
+ cfg,
157
+ sectionKey: CHANNEL_CONFIG_KEY,
158
+ accountId,
159
+ clearBaseFields: ["token", "tokenFile", "appId", "appIdFile", "name"],
160
+ }),
161
+ isConfigured: (account: ResolvedGeweAccount) => Boolean(account.token?.trim() && account.appId?.trim()),
162
+ describeAccount: (account: ResolvedGeweAccount) => ({
163
+ accountId: account.accountId,
164
+ name: account.name,
165
+ enabled: account.enabled,
166
+ configured: Boolean(account.token?.trim() && account.appId?.trim()),
167
+ tokenSource: account.tokenSource,
168
+ baseUrl: account.config.apiBaseUrl ? "[set]" : "[missing]",
169
+ }),
170
+ resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string | null }) =>
171
+ (resolveGeweAccount({ cfg: cfg as CoreConfig, accountId }).config.allowFrom ?? []).map(
172
+ (entry) => String(entry),
173
+ ),
174
+ formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
175
+ allowFrom
176
+ .map((entry) => String(entry).trim())
177
+ .filter(Boolean)
178
+ .map((entry) => stripChannelPrefix(entry)),
179
+ },
180
+ setup: geweSetupAdapter,
181
+ } satisfies Pick<
182
+ ChannelPlugin<ResolvedGeweAccount>,
183
+ "meta" | "setupWizard" | "capabilities" | "reload" | "configSchema" | "config" | "setup"
184
+ >;
@@ -1,5 +1,3 @@
1
- import type { ChannelDirectoryAdapter, ChannelDirectoryEntry } from "openclaw/plugin-sdk/channel-runtime";
2
-
3
1
  import { resolveGeweAccount } from "./accounts.js";
4
2
  import {
5
3
  fetchContactsListCacheGewe,
@@ -14,7 +12,12 @@ import {
14
12
  normalizeGeweBindingConversationId,
15
13
  } from "./group-binding.js";
16
14
  import { normalizeGeweMessagingTarget } from "./normalize.js";
17
- import { normalizeAccountId, type OpenClawConfig } from "./openclaw-compat.js";
15
+ import {
16
+ normalizeAccountId,
17
+ type ChannelDirectoryAdapter,
18
+ type ChannelDirectoryEntry,
19
+ type OpenClawConfig,
20
+ } from "./openclaw-compat.js";
18
21
  import type { CoreConfig } from "./types.js";
19
22
  import {
20
23
  listCachedGeweGroups,
@@ -1,12 +1,15 @@
1
- import type { ChannelStatusAdapter, ChannelStatusIssue } from "openclaw/plugin-sdk/channel-runtime";
2
-
3
1
  import type { ResolvedGeweAccount } from "./accounts.js";
4
2
  import { collectKnownGeweGroupEntries, collectKnownGewePeerEntries } from "./channel-directory.js";
5
3
  import { getGeweDirectoryCacheCounts } from "./directory-cache.js";
6
4
  import { CHANNEL_CONFIG_KEY } from "./constants.js";
7
5
  import { getGeweProfile } from "./group-binding.js";
8
6
  import { normalizeGeweBindingConversationId } from "./group-binding.js";
9
- import { normalizeAccountId, type OpenClawConfig } from "./openclaw-compat.js";
7
+ import {
8
+ normalizeAccountId,
9
+ type ChannelStatusAdapter,
10
+ type ChannelStatusIssue,
11
+ type OpenClawConfig,
12
+ } from "./openclaw-compat.js";
10
13
  import { readGeweAllowFromStore } from "./pairing-store.js";
11
14
 
12
15
  type GeweStatusProbe = {
@@ -0,0 +1,9 @@
1
+ import { CHANNEL_ID } from "./constants.js";
2
+ import { geweChannelPluginCommon } from "./channel-common.js";
3
+ import type { GeweChannelPlugin } from "./setup-wizard-types.js";
4
+ import type { ResolvedGeweAccount } from "./types.js";
5
+
6
+ export const geweSetupPlugin: GeweChannelPlugin<ResolvedGeweAccount> = {
7
+ id: CHANNEL_ID,
8
+ ...geweChannelPluginCommon,
9
+ };