@searchfe/openclaw-baiduapp 0.1.12-beta.1 → 0.1.12-beta.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/cli.js CHANGED
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import {execSync, spawnSync} from 'node:child_process';
4
+ import {createRequire} from 'node:module';
4
5
  import readline from 'node:readline';
6
+ import {Writable} from 'node:stream';
5
7
 
6
8
  import {printQRCode} from './src/cli-qrcode.mjs';
7
9
 
10
+ const _require = createRequire(import.meta.url);
11
+ const CLI_VERSION = _require('./package.json').version;
12
+
8
13
  const PLUGIN_SPEC = '@searchfe/openclaw-baiduapp';
9
14
  const CHANNEL_ID = 'openclaw-baiduapp';
10
15
  const QR_URL = 'https://www.baidu.com';
@@ -14,6 +19,8 @@ function parseArgs(argv) {
14
19
  let command;
15
20
  let ak;
16
21
  let sk;
22
+ let agent;
23
+ let yes = false;
17
24
 
18
25
  for (let index = 0; index < args.length; index++) {
19
26
  const token = args[index];
@@ -25,6 +32,11 @@ function parseArgs(argv) {
25
32
  continue;
26
33
  }
27
34
 
35
+ if (token === '--yes' || token === '-y') {
36
+ yes = true;
37
+ continue;
38
+ }
39
+
28
40
  if (token === '--ak') {
29
41
  ak = args[index + 1];
30
42
  index += 1;
@@ -47,12 +59,23 @@ function parseArgs(argv) {
47
59
  continue;
48
60
  }
49
61
 
62
+ if (token === '--agent') {
63
+ agent = args[index + 1];
64
+ index += 1;
65
+ continue;
66
+ }
67
+
68
+ if (token.startsWith('--agent=')) {
69
+ agent = token.slice('--agent='.length);
70
+ continue;
71
+ }
72
+
50
73
  if (!token.startsWith('--') && !command) {
51
74
  command = token;
52
75
  }
53
76
  }
54
77
 
55
- return {command, ak, sk};
78
+ return {command, ak, sk, agent, yes};
56
79
  }
57
80
 
58
81
  function prompt(question, {hidden = false} = {}) {
@@ -61,36 +84,30 @@ function prompt(question, {hidden = false} = {}) {
61
84
  }
62
85
 
63
86
  const askOnce = () => new Promise(resolve => {
87
+ if (hidden) {
88
+ process.stdout.write(question);
89
+ const rl = readline.createInterface({
90
+ input: process.stdin,
91
+ output: new Writable({write(_chunk, _enc, cb) { cb(); }}),
92
+ terminal: true,
93
+ });
94
+ rl.on('SIGINT', () => { rl.close(); process.stdout.write('\n'); process.exit(1); });
95
+ rl.question('', answer => {
96
+ rl.close();
97
+ process.stdout.write('\n');
98
+ resolve(String(answer || '').trim());
99
+ });
100
+ return;
101
+ }
102
+
64
103
  const rl = readline.createInterface({
65
104
  input: process.stdin,
66
105
  output: process.stdout,
67
106
  terminal: true,
68
107
  });
69
-
70
- rl.on('SIGINT', () => {
71
- rl.close();
72
- process.exit(1);
73
- });
74
-
75
- if (hidden) {
76
- rl.stdoutMuted = true;
77
- rl._writeToOutput = chunk => {
78
- if (rl.stdoutMuted) {
79
- if (chunk.startsWith(question)) {
80
- rl.output.write(chunk);
81
- }
82
- return;
83
- }
84
-
85
- rl.output.write(chunk);
86
- };
87
- }
88
-
108
+ rl.on('SIGINT', () => { rl.close(); process.exit(1); });
89
109
  rl.question(question, answer => {
90
110
  rl.close();
91
- if (hidden) {
92
- process.stdout.write('\n');
93
- }
94
111
  resolve(String(answer || '').trim());
95
112
  });
96
113
  });
@@ -111,6 +128,73 @@ function prompt(question, {hidden = false} = {}) {
111
128
  })();
112
129
  }
113
130
 
131
+ function promptOptional(question) {
132
+ if (!process.stdin.isTTY) {
133
+ return Promise.resolve('');
134
+ }
135
+ return new Promise(resolve => {
136
+ const rl = readline.createInterface({
137
+ input: process.stdin,
138
+ output: process.stdout,
139
+ terminal: true,
140
+ });
141
+ rl.on('SIGINT', () => {
142
+ rl.close();
143
+ process.exit(1);
144
+ });
145
+ rl.question(question, answer => {
146
+ rl.close();
147
+ resolve(String(answer || '').trim());
148
+ });
149
+ });
150
+ }
151
+
152
+ async function checkAndCreateAgent(agentId, {yes = false} = {}) {
153
+ let agentsList;
154
+ try {
155
+ const agentsJson = run('openclaw', ['agents', 'list', '--json']);
156
+ agentsList = JSON.parse(agentsJson);
157
+ } catch {
158
+ // 解析失败时假设 agent 不存在,继续后续流程
159
+ agentsList = [];
160
+ }
161
+
162
+ const exists = Array.isArray(agentsList) && agentsList.some(a => a.id === agentId);
163
+ if (exists) {
164
+ return;
165
+ }
166
+
167
+ log(`Agent "${agentId}" 不存在`);
168
+ console.log(` 你可以手动执行以下命令新建:`);
169
+ console.log(` openclaw agents add ${agentId}`);
170
+ console.log();
171
+
172
+ let confirm;
173
+ if (yes) {
174
+ confirm = 'y';
175
+ } else {
176
+ try {
177
+ confirm = await prompt(`是否立即自动新建 agent "${agentId}"?[y/N]: `);
178
+ } catch {
179
+ confirm = '';
180
+ }
181
+ }
182
+
183
+ if (confirm.toLowerCase() !== 'y') {
184
+ error('已取消,请手动新建 agent 后重试');
185
+ process.exit(1);
186
+ }
187
+
188
+ log(`正在新建 agent "${agentId}"...`);
189
+ try {
190
+ run('openclaw', ['agents', 'add', agentId], {silent: false});
191
+ } catch {
192
+ error('新建 agent 失败,请手动执行:');
193
+ console.log(` openclaw agents add ${agentId}`);
194
+ process.exit(1);
195
+ }
196
+ }
197
+
114
198
  function run(cmd, args = [], {silent = true} = {}) {
115
199
  const stdio = silent ? ['pipe', 'pipe', 'pipe'] : 'inherit';
116
200
  const result = spawnSync(cmd, args, {encoding: 'utf-8', stdio});
@@ -153,13 +237,19 @@ function error(msg) {
153
237
  }
154
238
 
155
239
  async function install() {
156
- let {ak, sk} = parseArgs(process.argv);
240
+ let {ak, sk, agent, yes} = parseArgs(process.argv);
157
241
 
158
242
  if ((!ak || !sk) && !process.stdin.isTTY) {
159
243
  error('非交互环境:请通过 --ak/--sk 参数传入凭证');
160
244
  process.exit(1);
161
245
  }
162
246
 
247
+ if (agent !== undefined && !agent.trim()) {
248
+ error('--agent 参数不能为空');
249
+ process.exit(1);
250
+ }
251
+ let agentId = agent?.trim();
252
+
163
253
  if (!which('openclaw')) {
164
254
  error('未找到 openclaw,请先安装:');
165
255
  console.log(' npm install -g openclaw');
@@ -168,36 +258,44 @@ async function install() {
168
258
  }
169
259
  log('已找到本地安装的 openclaw');
170
260
 
171
- log('正在安装插件...');
261
+ log('正在检查插件版本...');
262
+ let installedVersion = null;
172
263
  try {
173
- const out = run('openclaw', ['plugins', 'install', PLUGIN_SPEC]);
174
- if (out) {
175
- log(out);
176
- }
177
- } catch (installErr) {
178
- if (installErr.stderr && installErr.stderr.includes('already exists')) {
179
- log('检测到本地已安装,正在更新...');
180
- try {
181
- const updateOut = run('openclaw', ['plugins', 'update', CHANNEL_ID]);
182
- if (updateOut) {
183
- log(updateOut);
184
- }
185
- } catch (updateErr) {
186
- error('插件更新失败,请手动执行:');
187
- if (updateErr.stderr) {
188
- console.error(updateErr.stderr);
189
- }
190
- console.log(` openclaw plugins update "${CHANNEL_ID}"`);
191
- process.exit(1);
192
- }
193
- } else {
264
+ const infoJson = run('openclaw', ['config', 'get', `plugins.installs.${CHANNEL_ID}`, '--json']);
265
+ const info = JSON.parse(infoJson);
266
+ installedVersion = info?.version ?? null;
267
+ } catch {
268
+ // 未安装或无法读取,后续走安装流程
269
+ }
270
+
271
+ if (installedVersion === null) {
272
+ log(`插件未安装,正在安装 ${PLUGIN_SPEC}@${CLI_VERSION}...`);
273
+ try {
274
+ run('openclaw', ['plugins', 'install', `${PLUGIN_SPEC}@${CLI_VERSION}`], {silent: false});
275
+ } catch (installErr) {
194
276
  error('插件安装失败,请手动执行:');
195
- if (installErr.stderr) {
196
- console.error(installErr.stderr);
197
- }
198
- console.log(` openclaw plugins install "${PLUGIN_SPEC}"`);
277
+ console.log(` openclaw plugins install "${PLUGIN_SPEC}@${CLI_VERSION}"`);
278
+ process.exit(1);
279
+ }
280
+ } else if (installedVersion !== CLI_VERSION) {
281
+ log(`插件版本不一致(已安装: ${installedVersion},当前 CLI: ${CLI_VERSION}),正在更新...`);
282
+ try {
283
+ run('openclaw', ['config', 'set', `plugins.installs.${CHANNEL_ID}.spec`, `${PLUGIN_SPEC}@${CLI_VERSION}`]);
284
+ run('openclaw', ['config', 'unset', `plugins.installs.${CHANNEL_ID}.integrity`]);
285
+ run('openclaw', ['config', 'unset', `plugins.installs.${CHANNEL_ID}.shasum`]);
286
+ run('openclaw', ['plugins', 'update', CHANNEL_ID], {silent: false});
287
+ run('openclaw', ['gateway', 'restart'], {silent: false});
288
+ } catch (updateErr) {
289
+ error('插件更新失败,请手动执行:');
290
+ console.log(` openclaw config set plugins.installs.${CHANNEL_ID}.spec ${PLUGIN_SPEC}@${CLI_VERSION}`);
291
+ console.log(` openclaw config unset plugins.installs.${CHANNEL_ID}.integrity`);
292
+ console.log(` openclaw config unset plugins.installs.${CHANNEL_ID}.shasum`);
293
+ console.log(` openclaw plugins update ${CHANNEL_ID}`);
294
+ console.log(` openclaw gateway restart`);
199
295
  process.exit(1);
200
296
  }
297
+ } else {
298
+ log(`插件版本已是最新(${CLI_VERSION}),跳过更新`);
201
299
  }
202
300
 
203
301
  try {
@@ -217,17 +315,36 @@ async function install() {
217
315
  process.exit(1);
218
316
  }
219
317
 
318
+ // 插件已确认安装,再询问/校验 agent
319
+ if (!agentId && process.stdin.isTTY) {
320
+ agentId = await promptOptional('请输入 Agent ID(可选,用于多 agent 配置,直接回车跳过):') || undefined;
321
+ }
322
+
323
+ if (agentId) {
324
+ await checkAndCreateAgent(agentId, {yes});
325
+ }
326
+
220
327
  log('正在写入配置...');
328
+ const akPath = agentId
329
+ ? `channels.${CHANNEL_ID}.accounts.${agentId}.appKey`
330
+ : `channels.${CHANNEL_ID}.appKey`;
331
+ const skPath = agentId
332
+ ? `channels.${CHANNEL_ID}.accounts.${agentId}.appSecret`
333
+ : `channels.${CHANNEL_ID}.appSecret`;
221
334
  try {
222
- run('openclaw', ['config', 'set', `channels.${CHANNEL_ID}.appKey`, ak.trim()]);
223
- run('openclaw', ['config', 'set', `channels.${CHANNEL_ID}.appSecret`, sk.trim()]);
335
+ run('openclaw', ['config', 'set', akPath, ak.trim()]);
336
+ run('openclaw', ['config', 'set', skPath, sk.trim()]);
224
337
  } catch {
225
338
  error('配置写入失败,请手动执行:');
226
- console.log(` openclaw config set channels.${CHANNEL_ID}.appKey <your-app-key>`);
227
- console.log(` openclaw config set channels.${CHANNEL_ID}.appSecret <your-app-secret>`);
339
+ console.log(` openclaw config set ${akPath} <your-app-key>`);
340
+ console.log(` openclaw config set ${skPath} <your-app-secret>`);
228
341
  process.exit(1);
229
342
  }
230
- log('配置写入成功');
343
+ if (agentId) {
344
+ log(`配置写入成功(agent: ${agentId})`);
345
+ } else {
346
+ log('配置写入成功');
347
+ }
231
348
 
232
349
  log('正在重启 OpenClaw Gateway...');
233
350
  try {
@@ -255,10 +372,16 @@ function help() {
255
372
  选项:
256
373
  --ak <appKey> App Key(必填,缺省时交互输入)
257
374
  --sk <appSecret> App Secret(必填,缺省时交互输入,不回显)
375
+ --agent <agentId> Agent ID(可选,指定后写入 accounts.<agentId> 以支持多 agent 配置)
376
+ 交互模式下也会提示输入,直接回车跳过(写入根路径)
377
+ 指定后自动校验 agent 是否存在,不存在时提示确认新建
378
+ --yes, -y 自动确认所有交互提示(如 agent 不存在时自动新建,适合 CI/脚本环境)
258
379
  --help, -h 显示帮助信息
259
380
 
260
381
  示例:
261
382
  npx @searchfe/openclaw-baiduapp install --ak your-key --sk your-secret
383
+ npx @searchfe/openclaw-baiduapp install --agent my-agent --ak your-key --sk your-secret
384
+ npx @searchfe/openclaw-baiduapp install --agent my-agent --ak your-key --sk your-secret --yes
262
385
  npx @searchfe/openclaw-baiduapp install`);
263
386
  }
264
387
 
package/dist/index.js CHANGED
@@ -28506,6 +28506,7 @@ async function handleBaiduAppWebhookRequest(req, res) {
28506
28506
  // src/poller.ts
28507
28507
  var DEFAULT_POLL_INTERVAL_MS = 3e3;
28508
28508
  var DEFAULT_POLL_REQUEST_TIMEOUT_MS = 1e4;
28509
+ var pollerLogger = createLogger("openclaw-baiduapp:poller");
28509
28510
  var accountPollers = /* @__PURE__ */ new Map();
28510
28511
  function buildPollingTextInboundMessage(content) {
28511
28512
  return {
@@ -28541,8 +28542,10 @@ async function dispatchPendingPollingMessages(data, target) {
28541
28542
  const list = Array.isArray(item.list) ? item.list : [];
28542
28543
  const { content, files } = parseTextAndFilesFromList(list);
28543
28544
  if (typeof content !== "string" && files.length === 0) {
28545
+ pollerLogger.debug(`dispatch skip: msgId=${item.msgId ?? "?"} agentId=${item.agentId ?? "none"} reason=no-content`);
28544
28546
  continue;
28545
28547
  }
28548
+ pollerLogger.info(`dispatch msg: msgId=${item.msgId ?? "?"} agentId=${item.agentId ?? "none"} files=${files.length} hasText=${typeof content === "string"}`);
28546
28549
  await processBaiduAppInboundMessage({
28547
28550
  target,
28548
28551
  msg: {
@@ -28627,6 +28630,7 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28627
28630
  version: PLUGIN_VERSION
28628
28631
  };
28629
28632
  const body = JSON.stringify(payload);
28633
+ logger3.debug(`poll request: accountId=${account.accountId} url=${endpoint} sessionId=${payload.sessionId}`);
28630
28634
  const requestSignal = createAbortSignalController({
28631
28635
  signal: requestOptions?.signal,
28632
28636
  timeoutMs: requestOptions?.timeoutMs
@@ -28645,6 +28649,7 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28645
28649
  } catch (error) {
28646
28650
  requestSignal.cleanup();
28647
28651
  if (requestSignal.didTimeout()) {
28652
+ logger3.warn(`poll timeout: accountId=${account.accountId} timeoutMs=${requestOptions?.timeoutMs}`);
28648
28653
  return buildPollResult({
28649
28654
  ok: false,
28650
28655
  error: {
@@ -28654,6 +28659,7 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28654
28659
  });
28655
28660
  }
28656
28661
  if (isAbortError2(error)) {
28662
+ logger3.debug(`poll aborted: accountId=${account.accountId}`);
28657
28663
  return buildPollResult({
28658
28664
  ok: false,
28659
28665
  error: {
@@ -28662,17 +28668,20 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28662
28668
  }
28663
28669
  });
28664
28670
  }
28671
+ const errMsg = error instanceof Error ? error.message : String(error);
28672
+ logger3.error(`poll request failed: accountId=${account.accountId} error=${errMsg}`);
28665
28673
  return buildPollResult({
28666
28674
  ok: false,
28667
28675
  error: {
28668
28676
  kind: "request-failed",
28669
- message: error instanceof Error ? error.message : String(error)
28677
+ message: errMsg
28670
28678
  }
28671
28679
  });
28672
28680
  }
28673
28681
  const responseText = await response.text();
28674
28682
  requestSignal.cleanup();
28675
28683
  if (!responseText) {
28684
+ logger3.warn(`poll empty response: accountId=${account.accountId} status=${response.status}`);
28676
28685
  return buildPollResult({
28677
28686
  ok: false,
28678
28687
  error: {
@@ -28685,6 +28694,7 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28685
28694
  try {
28686
28695
  parsed = JSON.parse(responseText);
28687
28696
  } catch {
28697
+ logger3.warn(`poll invalid json: accountId=${account.accountId} status=${response.status}`);
28688
28698
  return buildPollResult({
28689
28699
  ok: false,
28690
28700
  error: {
@@ -28693,10 +28703,12 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
28693
28703
  }
28694
28704
  });
28695
28705
  }
28706
+ const msgList = Array.isArray(parsed.data?.msgList) ? parsed.data.msgList : [];
28696
28707
  if (parsed.code !== 0) {
28697
- logger3.debug(`Baidu poll returned non-zero code=${String(parsed.code)}, message=${parsed.message}`);
28708
+ logger3.debug(`poll non-zero code: accountId=${account.accountId} code=${String(parsed.code)} message=${parsed.message}`);
28709
+ } else {
28710
+ logger3.debug(`poll ok: accountId=${account.accountId} status=${response.status} msgCount=${msgList.length}`);
28698
28711
  }
28699
- const msgList = Array.isArray(parsed.data?.msgList) ? parsed.data.msgList : [];
28700
28712
  return buildPollResult({
28701
28713
  ok: true,
28702
28714
  data: msgList
@@ -28706,6 +28718,7 @@ function scheduleNextPoll(account, state) {
28706
28718
  if (state.stopped) {
28707
28719
  return;
28708
28720
  }
28721
+ pollerLogger.debug(`schedule next poll: accountId=${account.accountId} intervalMs=${state.intervalMs}`);
28709
28722
  state.timer = setTimeout(() => {
28710
28723
  void runPollCycle(account, state);
28711
28724
  }, state.intervalMs);
@@ -28714,6 +28727,9 @@ async function runPollCycle(account, state) {
28714
28727
  if (state.stopped || state.pending) {
28715
28728
  return;
28716
28729
  }
28730
+ state.cycleCount += 1;
28731
+ const cycle = state.cycleCount;
28732
+ pollerLogger.debug(`poll cycle #${cycle} start: accountId=${account.accountId}`);
28717
28733
  const controller = new AbortController();
28718
28734
  state.activeController = controller;
28719
28735
  const pending = (async () => {
@@ -28723,9 +28739,18 @@ async function runPollCycle(account, state) {
28723
28739
  fetchImpl: state.fetchImpl,
28724
28740
  timeoutMs: state.requestTimeoutMs
28725
28741
  });
28742
+ if (!result2.ok && result2.error) {
28743
+ if (result2.error.kind !== "aborted") {
28744
+ pollerLogger.warn(`poll cycle #${cycle} error: accountId=${account.accountId} kind=${result2.error.kind} msg=${result2.error.message}`);
28745
+ }
28746
+ } else if (result2.data.length > 0) {
28747
+ pollerLogger.info(`poll cycle #${cycle} msgs: accountId=${account.accountId} count=${result2.data.length}`);
28748
+ }
28726
28749
  await dispatchPendingPollingMessages(result2.data, state.dispatchTarget);
28727
28750
  } catch (error) {
28728
28751
  if (!(state.stopped && isAbortError2(error))) {
28752
+ const errMsg = error instanceof Error ? error.message : String(error);
28753
+ pollerLogger.error(`poll cycle #${cycle} unhandled error: accountId=${account.accountId} error=${errMsg}`);
28729
28754
  state.onError?.(error);
28730
28755
  }
28731
28756
  } finally {
@@ -28743,18 +28768,23 @@ async function runPollCycle(account, state) {
28743
28768
  }
28744
28769
  function startAccountPolling(params) {
28745
28770
  if (accountPollers.has(params.account.accountId)) {
28771
+ pollerLogger.debug(`polling already running: accountId=${params.account.accountId}, skip`);
28746
28772
  return;
28747
28773
  }
28774
+ const intervalMs = params.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
28775
+ const requestTimeoutMs = params.requestTimeoutMs ?? DEFAULT_POLL_REQUEST_TIMEOUT_MS;
28776
+ pollerLogger.info(`start polling: accountId=${params.account.accountId} intervalMs=${intervalMs} timeoutMs=${requestTimeoutMs} apiBase=${params.account.apiBase}`);
28748
28777
  const state = {
28749
28778
  stopped: false,
28750
28779
  timer: null,
28751
28780
  activeController: null,
28752
28781
  pending: null,
28753
- intervalMs: params.intervalMs ?? DEFAULT_POLL_INTERVAL_MS,
28754
- requestTimeoutMs: params.requestTimeoutMs ?? DEFAULT_POLL_REQUEST_TIMEOUT_MS,
28782
+ intervalMs,
28783
+ requestTimeoutMs,
28755
28784
  fetchImpl: params.fetchImpl,
28756
28785
  onError: params.onError,
28757
- dispatchTarget: params.dispatchTarget
28786
+ dispatchTarget: params.dispatchTarget,
28787
+ cycleCount: 0
28758
28788
  };
28759
28789
  accountPollers.set(params.account.accountId, state);
28760
28790
  void runPollCycle(params.account, state);
@@ -28764,6 +28794,7 @@ function stopAccountPolling(accountId) {
28764
28794
  if (!state) {
28765
28795
  return;
28766
28796
  }
28797
+ pollerLogger.info(`stop polling: accountId=${accountId} totalCycles=${state.cycleCount}`);
28767
28798
  state.stopped = true;
28768
28799
  if (state.timer) {
28769
28800
  clearTimeout(state.timer);