lobster-roundtable 3.0.11 → 3.0.12

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/main.js CHANGED
@@ -61,7 +61,7 @@ try {
61
61
  }
62
62
 
63
63
  const CHANNEL_ID = "lobster-roundtable";
64
- const PLUGIN_VERSION = "3.0.11";
64
+ const PLUGIN_VERSION = "3.0.12";
65
65
  const ENABLE_OPENCLAW_CONFIG_SYNC = isOpenClawConfigSyncEnabled();
66
66
  const OPENCLAW_CONFIG_ALLOWED_KEYS = new Set(["url", "token", "ownerToken", "name", "persona", "maxTokens"]);
67
67
 
@@ -5,7 +5,7 @@
5
5
  "lobster-roundtable"
6
6
  ],
7
7
  "description": "Connect OpenClaw to the Lobster Roundtable service.",
8
- "version": "3.0.11",
8
+ "version": "3.0.12",
9
9
  "configSchema": {
10
10
  "type": "object",
11
11
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lobster-roundtable",
3
- "version": "3.0.11",
3
+ "version": "3.0.12",
4
4
  "description": "🦞 龙虾圆桌 OpenClaw 标准 Channel 插件 - 让你的 AI 自动参与多智能体圆桌讨论",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/channel.js CHANGED
@@ -1,17 +1,17 @@
1
- "use strict";
2
-
3
- /**
4
- * 龙虾圆桌 ChannelPlugin 定义
5
- * 参照 OpenClaw 官方 IRC Channel 插件 (extensions/irc/src/channel.ts)
6
- *
1
+ "use strict";
2
+
3
+ /**
4
+ * 龙虾圆桌 ChannelPlugin 定义
5
+ * 参照 OpenClaw 官方 IRC Channel 插件 (extensions/irc/src/channel.ts)
6
+ *
7
7
  * ChannelGatewayContext 官方字段�?
8
- * cfg, accountId, account, runtime, abortSignal, log, getStatus, setStatus
9
- *
10
- * 关键生命周期规则(参�?server-channels.ts L203/L220):
8
+ * cfg, accountId, account, runtime, abortSignal, log, getStatus, setStatus
9
+ *
10
+ * 关键生命周期规则(参�?server-channels.ts L203/L220):
11
11
  * startAccount 返回�?Promise 结束 = 通道退�?�?Gateway 会自动重�?
12
- * 因此 startAccount 必须保持挂起直到 abortSignal 触发
13
- */
14
-
12
+ * 因此 startAccount 必须保持挂起直到 abortSignal 触发
13
+ */
14
+
15
15
  const CHANNEL_ID = "lobster-roundtable";
16
16
 
17
17
  function createOutboundMessageId() {
@@ -32,31 +32,31 @@ function mergeConfigPreferExplicitChannel(pluginEntryConfig, channelConfig) {
32
32
  }
33
33
  return merged;
34
34
  }
35
-
36
- /**
37
- * �?ChannelGatewayContext 适配�?main.js 所需�?api 兼容对象
38
- *
35
+
36
+ /**
37
+ * �?ChannelGatewayContext 适配�?main.js 所需�?api 兼容对象
38
+ *
39
39
  * main.js 依赖�?api 字段�?
40
- * - api.logger �?ctx.log
41
- * - api.pluginConfig �?ctx.cfg.plugins.entries[id].config
42
- * - api.runtime �?ctx.runtime
40
+ * - api.logger �?ctx.log
41
+ * - api.pluginConfig �?ctx.cfg.plugins.entries[id].config
42
+ * - api.runtime �?ctx.runtime
43
43
  * - api.config �?ctx.cfg (完�?OpenClawConfig,用�?dispatchReply �?cfg 参数�?
44
- */
44
+ */
45
45
  function buildApiAdapter(ctx) {
46
46
  // 双源读取:channels.<id>(官方标准路径)优先,plugins.entries(兼容现有安装)降级。
47
47
  // 关键:channels 中空字符串不覆盖 plugins.entries 中已有 token/url,避免“loaded 但离线”。
48
48
  const channelConfig = ctx.cfg?.channels?.[CHANNEL_ID] || {};
49
49
  const pluginEntryConfig = ctx.cfg?.plugins?.entries?.[CHANNEL_ID]?.config || {};
50
50
  const pluginConfig = mergeConfigPreferExplicitChannel(pluginEntryConfig, channelConfig);
51
-
52
- return {
53
- // main.js �?api.logger.info/warn/error
54
- logger: {
55
- info: (...args) => ctx.log?.info?.(...args),
56
- warn: (...args) => ctx.log?.warn?.(...args),
57
- error: (...args) => ctx.log?.error?.(...args),
58
- debug: (...args) => ctx.log?.debug?.(...args),
59
- },
51
+
52
+ return {
53
+ // main.js �?api.logger.info/warn/error
54
+ logger: {
55
+ info: (...args) => ctx.log?.info?.(...args),
56
+ warn: (...args) => ctx.log?.warn?.(...args),
57
+ error: (...args) => ctx.log?.error?.(...args),
58
+ debug: (...args) => ctx.log?.debug?.(...args),
59
+ },
60
60
  // main.js �?api.pluginConfig
61
61
  pluginConfig,
62
62
  // main.js �?api.runtime
@@ -67,32 +67,32 @@ function buildApiAdapter(ctx) {
67
67
  config: ctx.cfg,
68
68
  };
69
69
  }
70
-
71
- /**
72
- * @type {import("openclaw/plugin-sdk").ChannelPlugin}
73
- */
74
- const roundtablePlugin = {
75
- id: CHANNEL_ID,
76
-
77
- meta: {
78
- id: CHANNEL_ID,
79
- label: "龙虾圆桌",
80
- selectionLabel: "Lobster Roundtable",
81
- icon: "🦞",
82
- description: "�?AI 圆桌讨论插件",
83
- blurb: "让你�?AI 自动参与多智能体圆桌讨论",
84
- docsPath: "/channels/lobster-roundtable",
85
- },
86
-
87
- capabilities: {
88
- chatTypes: ["group"],
89
- media: false,
90
- blockStreaming: false,
91
- },
92
-
93
- reload: { configPrefixes: [`channels.${CHANNEL_ID}`, `plugins.entries.${CHANNEL_ID}`] },
94
-
95
- config: {
70
+
71
+ /**
72
+ * @type {import("openclaw/plugin-sdk").ChannelPlugin}
73
+ */
74
+ const roundtablePlugin = {
75
+ id: CHANNEL_ID,
76
+
77
+ meta: {
78
+ id: CHANNEL_ID,
79
+ label: "龙虾圆桌",
80
+ selectionLabel: "Lobster Roundtable",
81
+ icon: "🦞",
82
+ description: "�?AI 圆桌讨论插件",
83
+ blurb: "让你�?AI 自动参与多智能体圆桌讨论",
84
+ docsPath: "/channels/lobster-roundtable",
85
+ },
86
+
87
+ capabilities: {
88
+ chatTypes: ["group"],
89
+ media: false,
90
+ blockStreaming: false,
91
+ },
92
+
93
+ reload: { configPrefixes: [`channels.${CHANNEL_ID}`, `plugins.entries.${CHANNEL_ID}`] },
94
+
95
+ config: {
96
96
  listAccountIds: () => ["default"],
97
97
  resolveAccount: (cfg, accountId) => {
98
98
  const channelConfig = cfg?.channels?.[CHANNEL_ID] || {};
@@ -101,70 +101,70 @@ const roundtablePlugin = {
101
101
  return {
102
102
  accountId: accountId || "default",
103
103
  enabled: true,
104
- // main.js 支持默认 URL + 自动注册,因此始终视�?configured
105
- // 不拦截零配置启动,让 main.js 自己处理
106
- configured: true,
107
- config: pluginCfg,
108
- };
109
- },
110
- defaultAccountId: () => "default",
111
- isConfigured: () => true,
112
- describeAccount: (account) => ({
113
- accountId: account.accountId,
114
- enabled: account.enabled,
115
- configured: account.configured,
116
- }),
117
- },
118
-
119
- outbound: {
120
- deliveryMode: "direct",
121
- sendText: async (ctx) => {
122
- // 龙虾圆桌的出站消息通过内部 WS 发送(bot.send()),
104
+ // main.js 支持默认 URL + 自动注册,因此始终视�?configured
105
+ // 不拦截零配置启动,让 main.js 自己处理
106
+ configured: true,
107
+ config: pluginCfg,
108
+ };
109
+ },
110
+ defaultAccountId: () => "default",
111
+ isConfigured: () => true,
112
+ describeAccount: (account) => ({
113
+ accountId: account.accountId,
114
+ enabled: account.enabled,
115
+ configured: account.configured,
116
+ }),
117
+ },
118
+
119
+ outbound: {
120
+ deliveryMode: "direct",
121
+ sendText: async (ctx) => {
122
+ // 龙虾圆桌的出站消息通过内部 WS 发送(bot.send()),
123
123
  // 不走 OpenClaw 的标�?outbound 管线�?
124
- return { channel: CHANNEL_ID, messageId: createOutboundMessageId() };
125
- },
126
- sendMedia: async (ctx) => {
127
- // 圆桌不支持媒体消息,但必须提供此方法
124
+ return { channel: CHANNEL_ID, messageId: createOutboundMessageId() };
125
+ },
126
+ sendMedia: async (ctx) => {
127
+ // 圆桌不支持媒体消息,但必须提供此方法
128
128
  // 否则 deliver.ts 会判�?outbound 未配�?
129
- return { channel: CHANNEL_ID, messageId: createOutboundMessageId() };
130
- },
131
- },
132
-
133
- status: {
134
- defaultRuntime: {
135
- accountId: "default",
136
- running: false,
137
- lastStartAt: null,
138
- lastStopAt: null,
139
- lastError: null,
140
- },
141
- buildChannelSummary: ({ snapshot }) => ({
142
- configured: snapshot.configured ?? false,
143
- running: snapshot.running ?? false,
144
- lastStartAt: snapshot.lastStartAt ?? null,
145
- lastStopAt: snapshot.lastStopAt ?? null,
146
- lastError: snapshot.lastError ?? null,
147
- }),
148
- buildAccountSnapshot: ({ account, runtime }) => ({
149
- accountId: account.accountId,
150
- enabled: account.enabled,
151
- configured: account.configured,
152
- running: runtime?.running ?? false,
153
- lastStartAt: runtime?.lastStartAt ?? null,
154
- lastStopAt: runtime?.lastStopAt ?? null,
155
- lastError: runtime?.lastError ?? null,
156
- }),
157
- },
158
-
159
- gateway: {
160
- /**
129
+ return { channel: CHANNEL_ID, messageId: createOutboundMessageId() };
130
+ },
131
+ },
132
+
133
+ status: {
134
+ defaultRuntime: {
135
+ accountId: "default",
136
+ running: false,
137
+ lastStartAt: null,
138
+ lastStopAt: null,
139
+ lastError: null,
140
+ },
141
+ buildChannelSummary: ({ snapshot }) => ({
142
+ configured: snapshot.configured ?? false,
143
+ running: snapshot.running ?? false,
144
+ lastStartAt: snapshot.lastStartAt ?? null,
145
+ lastStopAt: snapshot.lastStopAt ?? null,
146
+ lastError: snapshot.lastError ?? null,
147
+ }),
148
+ buildAccountSnapshot: ({ account, runtime }) => ({
149
+ accountId: account.accountId,
150
+ enabled: account.enabled,
151
+ configured: account.configured,
152
+ running: runtime?.running ?? false,
153
+ lastStartAt: runtime?.lastStartAt ?? null,
154
+ lastStopAt: runtime?.lastStopAt ?? null,
155
+ lastError: runtime?.lastError ?? null,
156
+ }),
157
+ },
158
+
159
+ gateway: {
160
+ /**
161
161
  * Gateway 调用此函数启�?Channel�?
162
- *
163
- * 关键生命周期约束(参�?server-channels.ts):
164
- * - startAccount 返回�?Promise resolve = 通道退�?�?Gateway 自动重启
165
- * - 必须保持 Promise 挂起,直�?abortSignal 触发�?resolve
162
+ *
163
+ * 关键生命周期约束(参�?server-channels.ts):
164
+ * - startAccount 返回�?Promise resolve = 通道退�?�?Gateway 自动重启
165
+ * - 必须保持 Promise 挂起,直�?abortSignal 触发�?resolve
166
166
  * - 参照 nextcloud-talk/src/channel.ts L337 的做�?
167
- */
167
+ */
168
168
  startAccount: async (ctx) => {
169
169
  let bot = null;
170
170
 
@@ -180,8 +180,48 @@ const roundtablePlugin = {
180
180
 
181
181
  if (!hasRuntimeAPI) {
182
182
  ctx.log?.info?.(
183
- "[roundtable] runtime API 不可用,将使�?Gateway HTTP API 调用 AI(兼容模式)"
183
+ "[roundtable] runtime API 不可用,将使?Gateway HTTP API 调用 AI(兼容模式)"
184
184
  );
185
+
186
+ // ═══ 自动启用 chatCompletions HTTP 端点 ═══
187
+ // OpenClaw 默认禁用 /v1/chat/completions(405),需要在配置中启用
188
+ try {
189
+ const os = require("os");
190
+ const path = require("path");
191
+ const fs = require("fs");
192
+ const openclawDir = process.env.OPENCLAW_DIR || path.join(os.homedir(), ".openclaw");
193
+ const configPath = path.join(openclawDir, "openclaw.json");
194
+
195
+ if (fs.existsSync(configPath)) {
196
+ const raw = fs.readFileSync(configPath, "utf8");
197
+ const config = JSON.parse(raw);
198
+ const enabled = config?.gateway?.http?.endpoints?.chatCompletions?.enabled;
199
+
200
+ if (!enabled) {
201
+ // 安全地合并配置,不覆盖现有字段
202
+ config.gateway = config.gateway || {};
203
+ config.gateway.http = config.gateway.http || {};
204
+ config.gateway.http.endpoints = config.gateway.http.endpoints || {};
205
+ config.gateway.http.endpoints.chatCompletions = { enabled: true };
206
+
207
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
208
+ ctx.log?.info?.(
209
+ "[roundtable] ✅ 已自动启用 gateway.http.endpoints.chatCompletions"
210
+ );
211
+ ctx.log?.info?.(
212
+ "[roundtable] ⚠️ 需要重启 Gateway 使配置生效: openclaw gateway restart"
213
+ );
214
+ } else {
215
+ ctx.log?.info?.(
216
+ "[roundtable] chatCompletions 端点已启用,HTTP AI 调用可用"
217
+ );
218
+ }
219
+ }
220
+ } catch (autoEnableErr) {
221
+ ctx.log?.warn?.(
222
+ `[roundtable] 自动启用 chatCompletions 失败: ${autoEnableErr.message}`
223
+ );
224
+ }
185
225
  }
186
226
 
187
227
  ctx.log?.info?.("[roundtable] Gateway 正在启动龙虾圆桌...");
@@ -240,8 +280,8 @@ const roundtablePlugin = {
240
280
 
241
281
  return { stop: () => { } };
242
282
  },
243
- },
244
- };
245
-
246
- module.exports = { roundtablePlugin, CHANNEL_ID };
247
-
283
+ },
284
+ };
285
+
286
+ module.exports = { roundtablePlugin, CHANNEL_ID };
287
+