@lotaber_wang/openclaw-dc-plugin 0.1.6 → 0.1.8

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
@@ -9,23 +9,51 @@
9
9
  - 运行时解析顺序为:插件内 bundled runtime -> 本地已安装 runtime -> 本地缓存 runtime
10
10
  - 暴露适合 agent / skill 消费的 TapTap DC 原始 JSON 工具
11
11
  - 提供高层工具 `taptap_dc_quick_brief`,可直接按游戏名 / app_id 生成 TapTap DC 快速简报
12
- - 如果当前未授权,`taptap_dc_quick_brief` 和 `taptap_dc_start_authorization` 都会直接返回可点击的授权超链接与二维码链接
12
+ - 如果当前未授权,`taptap_dc_quick_brief` 和 `taptap_dc_start_authorization` 都会优先返回“可直接点击的授权链接”,并附带 TapTap 包装链接与授权页直链
13
13
  - 覆盖授权、选游戏、商店/评价/社区概览、商店快照、论坛内容、评价列表、点赞与官方回复
14
14
  - 内置 `taptap-dc-ops-brief`,可把原始数据整理成简洁的运营简报
15
15
 
16
16
  ## 安装
17
17
 
18
18
  ```bash
19
- openclaw plugins install @lotaber_wang/openclaw-dc-plugin
19
+ openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.8
20
20
  ```
21
21
 
22
+ 安装完成后,建议立刻执行一次检查:
23
+
24
+ ```bash
25
+ openclaw plugins inspect taptap-dc-plugin
26
+ ```
27
+
28
+ 如果安装成功但当前会话里还看不到 `taptap_dc_*` 工具,请重启 Gateway:
29
+
30
+ ```bash
31
+ openclaw gateway restart
32
+ ```
33
+
34
+ 如果你的环境没有 `openclaw gateway restart`,就直接完整重启 OpenClaw / 宿主应用。
35
+
36
+ ## 推荐安装流程
37
+
38
+ ```bash
39
+ openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.8
40
+ openclaw plugins inspect taptap-dc-plugin
41
+ openclaw gateway restart
42
+ ```
43
+
44
+ 说明:
45
+
46
+ - `install` 负责把插件包装进 OpenClaw
47
+ - `inspect` 用来确认插件元信息、skills、tools 是否已被 Gateway 识别
48
+ - `gateway restart` 用来让最新插件配置真正生效;很多“装上了但没看到 tools”的问题都出在这里
49
+
22
50
  ## 典型使用流程
23
51
 
24
52
  优先推荐直接使用:
25
53
 
26
54
  1. 直接调用 `taptap_dc_quick_brief`
27
55
  2. 如果还没授权,工具会直接返回可点击授权链接
28
- 3. 手机端优先直接点击授权链接;桌面端优先打开二维码后扫码
56
+ 3. 手机端优先直接点击第一条“直接点击授权”链接;桌面端可打开授权页直链后自行扫码或转发
29
57
  4. 完成扫码后调用 `taptap_dc_complete_authorization`
30
58
  5. 再次调用 `taptap_dc_quick_brief`
31
59
 
@@ -81,17 +109,44 @@ openclaw plugins install @lotaber_wang/openclaw-dc-plugin
81
109
 
82
110
  如果 OpenClaw 能安装插件,但第一次调用工具仍然偏慢,通常是在启动内嵌 TapTap 运行时,而不是在现场重新下载依赖。
83
111
 
112
+ 如果安装后看不到 `taptap_dc_*` 工具,优先按这个顺序排查:
113
+
114
+ 1. 确认安装的是 `@lotaber_wang/openclaw-dc-plugin@0.1.8` 或更新版本
115
+ 2. 执行 `openclaw plugins inspect taptap-dc-plugin`
116
+ 3. 执行 `openclaw gateway restart`
117
+ 4. 重新开启一个新会话,再观察工具列表
118
+
119
+ 如果 `inspect` 能看到插件,但工具仍未生效,通常不是插件代码没注册,而是 Gateway 还没完成配置重载。
120
+
121
+ 说明:
122
+
123
+ - 本插件的 `taptap_dc_*` 工具由插件入口代码动态注册
124
+ - `openclaw.plugin.json` 主要承担插件元信息、默认启用和配置校验职责
125
+ - 所以“装完看不到 tool”时,优先怀疑安装状态、Gateway 重启和会话刷新,而不是去手改 manifest 里的 `tools` 字段
126
+
127
+ 如果出现配置异常,优先检查插件 ID 是否一致:
128
+
129
+ - package 名称:`@lotaber_wang/openclaw-dc-plugin`
130
+ - plugin id:`taptap-dc-plugin`
131
+ - OpenClaw 配置入口:`plugins.entries.taptap-dc-plugin`
132
+
84
133
  如果第一次调用失败,优先检查:
85
134
 
86
135
  - 当前环境是否能访问 npm registry
87
136
  - 本机 Node 版本是否 >= 18.14.1
88
137
  - OpenClaw 进程是否有临时目录写权限
138
+ - OpenClaw 是否已经完成 Gateway 重启
89
139
 
90
- 从 `0.1.6` 开始,插件 bridge 会额外做这些兼容处理:
140
+ 从 `0.1.8` 开始,插件安装链路与 bridge 会额外做这些兼容处理:
91
141
 
142
+ - 在 `package.json` 中补充 `openclaw.install.npmSpec`,让安装链路更稳定
143
+ - 在 `openclaw.plugin.json` 中显式开启 `enabledByDefault`
92
144
  - `initialize` 超时后自动切换到无缓冲 / PTY 启动策略重试
93
145
  - `initialize` 默认超时提升到 45 秒,避免宿主启动稍慢时过早失败
146
+ - 向内嵌 TapTap runtime 发送 `initialize` 与后续请求时,默认改为裸 JSON + 换行(NDJSON 风格),避免只发 `Content-Length` 帧导致无响应
147
+ - `initialize` 的 `protocolVersion` 回退到 `2024-11-05`,并使用更保守的空 `capabilities`,降低 OpenClaw 宿主兼容风险
94
148
  - 兼容解析裸 JSON 输出,不再只接受 `Content-Length` 帧
95
149
  - 启动时如果 stdout 混入人类可读日志,会先自动丢弃噪音再解析协议消息
96
150
  - 如果拿到的是半截 JSON,会先继续等待后续分片,而不是立刻按失败处理
97
151
  - 过滤 PTY 场景下被回显到 stdout 的请求消息
152
+ - 授权结果会优先把“直接点击授权”链接放在第一位,方便移动端对话直接打开
package/index.js CHANGED
@@ -58,6 +58,55 @@ function buildMarkdownLink(label, url) {
58
58
  return `[${label}](${url})`;
59
59
  }
60
60
 
61
+ function tryParseUrl(value) {
62
+ if (!value || typeof value !== 'string') {
63
+ return null;
64
+ }
65
+
66
+ try {
67
+ return new URL(value);
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ function decodeWrappedAuthUrl(value) {
74
+ const parsed = tryParseUrl(value);
75
+ const wrappedCode = parsed?.searchParams?.get('code');
76
+ if (!wrappedCode) {
77
+ return null;
78
+ }
79
+
80
+ try {
81
+ return decodeURIComponent(wrappedCode);
82
+ } catch {
83
+ return wrappedCode;
84
+ }
85
+ }
86
+
87
+ function enrichAuthPayload(authPayload) {
88
+ if (!authPayload || typeof authPayload !== 'object') {
89
+ return authPayload;
90
+ }
91
+
92
+ const directAuthUrl =
93
+ authPayload.direct_auth_url ||
94
+ authPayload.verification_uri_complete ||
95
+ authPayload.qrcode_url ||
96
+ decodeWrappedAuthUrl(authPayload.auth_url) ||
97
+ authPayload.verification_uri ||
98
+ null;
99
+
100
+ const wrappedAuthUrl = authPayload.wrapped_auth_url || authPayload.auth_url || null;
101
+
102
+ return {
103
+ ...authPayload,
104
+ direct_auth_url: directAuthUrl,
105
+ wrapped_auth_url: wrappedAuthUrl,
106
+ preferred_auth_url: directAuthUrl || wrappedAuthUrl || null,
107
+ };
108
+ }
109
+
61
110
  function normalizeText(value) {
62
111
  return String(value || '')
63
112
  .trim()
@@ -195,30 +244,39 @@ function buildBriefText(appTitle, sections, meta = {}) {
195
244
  }
196
245
 
197
246
  function buildAuthGuideText(authPayload) {
198
- const authUrl = authPayload?.auth_url;
199
- const qrcodeUrl = authPayload?.qrcode_url;
247
+ const enriched = enrichAuthPayload(authPayload);
248
+ const directAuthUrl = enriched?.direct_auth_url;
249
+ const wrappedAuthUrl = enriched?.wrapped_auth_url;
250
+ const qrcodeUrl = enriched?.qrcode_url;
200
251
  const lines = [
201
- '当前还没有完成 TapTap 授权,请先扫码授权。',
252
+ '当前还没有完成 TapTap 授权,请先完成一次授权。',
253
+ '',
254
+ '如果你现在是在手机上或当前客户端支持超链接,请优先点击下面这条直接授权链接,不用扫码:',
255
+ buildMarkdownLink('直接点击授权', directAuthUrl),
202
256
  '',
203
- '如果你现在是在手机上对话,优先直接点击下面这个授权链接:',
204
- buildMarkdownLink('立即打开授权页面', authUrl),
257
+ '如果上面的链接打不开,再尝试这个 TapTap 包装授权页:',
258
+ buildMarkdownLink('打开 TapTap 包装授权页', wrappedAuthUrl),
205
259
  '',
206
- '如果你现在是在桌面端对话,可以打开二维码后用 TapTap 扫码授权:',
207
- buildMarkdownLink('打开二维码图片', qrcodeUrl),
260
+ '如果你需要在桌面端继续,或者想把授权页转给另一台设备,也可以使用这个直链自行打开或生成二维码:',
261
+ buildMarkdownLink('打开授权页直链', qrcodeUrl || directAuthUrl),
208
262
  '',
209
263
  '完成扫码后,请继续调用 `taptap_dc_complete_authorization`,然后再次调用 `taptap_dc_quick_brief`。',
210
264
  ];
211
265
 
212
- if (authPayload?.device_code) {
213
- lines.push('', `device_code:${authPayload.device_code}`);
266
+ if (enriched?.device_code) {
267
+ lines.push('', `device_code:${enriched.device_code}`);
214
268
  }
215
269
 
216
- if (authUrl) {
217
- lines.push('', `授权直链:${authUrl}`);
270
+ if (directAuthUrl) {
271
+ lines.push('', `直接授权链接:${directAuthUrl}`);
218
272
  }
219
273
 
220
- if (qrcodeUrl) {
221
- lines.push(`二维码直链:${qrcodeUrl}`);
274
+ if (wrappedAuthUrl && wrappedAuthUrl !== directAuthUrl) {
275
+ lines.push(`TapTap 包装授权链接:${wrappedAuthUrl}`);
276
+ }
277
+
278
+ if (qrcodeUrl && qrcodeUrl !== directAuthUrl) {
279
+ lines.push(`授权页直链:${qrcodeUrl}`);
222
280
  }
223
281
 
224
282
  return lines.join('\n');
@@ -298,12 +356,15 @@ function registerProxyTool(api, bridge, definition) {
298
356
  const text = await bridge.callTool(definition.mcpToolName, params || {});
299
357
  const normalized = normalizeJsonText(text);
300
358
  const parsed = tryParseJson(normalized);
359
+ const normalizedParsed = definition.normalizeResult
360
+ ? definition.normalizeResult(parsed)
361
+ : parsed;
301
362
  const renderedText = definition.resultFormatter
302
- ? definition.resultFormatter(parsed, normalized)
363
+ ? definition.resultFormatter(normalizedParsed, normalized)
303
364
  : normalized;
304
365
  return toolResult(renderedText, {
305
366
  mcpToolName: definition.mcpToolName,
306
- parsed,
367
+ parsed: normalizedParsed,
307
368
  });
308
369
  } catch (error) {
309
370
  return toolResult(
@@ -357,7 +418,9 @@ function registerQuickBriefTool(api, bridge) {
357
418
  try {
358
419
  const env = await callJsonTool(bridge, 'check_environment_raw', {});
359
420
  if (!env?.auth?.has_mac_token) {
360
- const authPayload = await callJsonTool(bridge, 'start_oauth_authorization_raw', {});
421
+ const authPayload = enrichAuthPayload(
422
+ await callJsonTool(bridge, 'start_oauth_authorization_raw', {})
423
+ );
361
424
  return toolResult(buildAuthGuideText(authPayload), authPayload);
362
425
  }
363
426
 
@@ -425,6 +488,9 @@ const toolDefinitions = [
425
488
  'Start TapTap OAuth device flow and return a mobile-friendly clickable auth link plus qrcode link.',
426
489
  mcpToolName: 'start_oauth_authorization_raw',
427
490
  parameters: createSchema(),
491
+ normalizeResult(parsed) {
492
+ return enrichAuthPayload(parsed);
493
+ },
428
494
  resultFormatter(parsed, normalized) {
429
495
  if (!parsed || typeof parsed !== 'object') {
430
496
  return normalized;
package/lib/mcp-bridge.js CHANGED
@@ -10,7 +10,10 @@ const require = createRequire(import.meta.url);
10
10
  const HEADER_SEPARATOR = '\r\n\r\n';
11
11
  const RUNTIME_PACKAGE_NAME = '@mikoto_zero/minigame-open-mcp';
12
12
  const DEFAULT_INIT_TIMEOUT_MS = 45000;
13
+ const DEFAULT_PROTOCOL_VERSION = '2024-11-05';
13
14
  const ANSI_ESCAPE_REGEX = /\x1B\[[0-?]*[ -/]*[@-~]/g;
15
+ const pluginPackageJson = readPluginPackageJson();
16
+ const PLUGIN_VERSION = pluginPackageJson?.version || '0.1.0';
14
17
 
15
18
  function readPluginPackageJson() {
16
19
  try {
@@ -563,8 +566,7 @@ export class TapTapMcpBridge {
563
566
  }
564
567
 
565
568
  const body = JSON.stringify(message);
566
- const frame = `Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`;
567
- this.child.stdin.write(frame, 'utf8');
569
+ this.child.stdin.write(`${body}\n`, 'utf8');
568
570
  }
569
571
 
570
572
  request(method, params) {
@@ -694,15 +696,11 @@ export class TapTapMcpBridge {
694
696
  async initializeServer() {
695
697
  await Promise.race([
696
698
  this.request('initialize', {
697
- protocolVersion: '2025-11-25',
698
- capabilities: {
699
- tools: {},
700
- resources: {},
701
- logging: {},
702
- },
699
+ protocolVersion: DEFAULT_PROTOCOL_VERSION,
700
+ capabilities: {},
703
701
  clientInfo: {
704
702
  name: 'taptap-openclaw-dc-plugin',
705
- version: '0.1.0',
703
+ version: PLUGIN_VERSION,
706
704
  },
707
705
  }),
708
706
  new Promise((_, reject) => {
@@ -2,7 +2,8 @@
2
2
  "id": "taptap-dc-plugin",
3
3
  "name": "TapTap DC",
4
4
  "description": "面向 OpenClaw 的 TapTap DC 插件,内置原始数据工具与运营简报 skill。",
5
- "version": "0.1.6",
5
+ "version": "0.1.8",
6
+ "enabledByDefault": true,
6
7
  "skills": ["./skills"],
7
8
  "configSchema": {
8
9
  "type": "object",
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "@lotaber_wang/openclaw-dc-plugin",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "TapTap DC 的 OpenClaw 插件,内置原始数据工具与运营简报 skill。",
6
6
  "main": "index.js",
7
7
  "openclaw": {
8
8
  "extensions": [
9
9
  "./index.js"
10
- ]
10
+ ],
11
+ "install": {
12
+ "npmSpec": "@lotaber_wang/openclaw-dc-plugin"
13
+ }
11
14
  },
12
15
  "exports": {
13
16
  ".": "./index.js",
@@ -19,9 +19,9 @@ description: 生成 TapTap 当前游戏 DC 运营简报与结论解读(商店/
19
19
  - 如果用户给了游戏名,直接把 `app_name` 传进去
20
20
  - 如果用户给了 `app_id`,直接把 `app_id` 传进去
21
21
  2. 如果未授权
22
- - `taptap_dc_quick_brief` 会直接返回 markdown 授权链接和二维码链接
23
- - 如果用户当前在手机上对话,优先引导用户直接点击授权链接
24
- - 如果用户当前在桌面端对话,再引导用户打开二维码并扫码
22
+ - `taptap_dc_quick_brief` 会直接返回 markdown 授权链接,其中第一条通常就是可直接点击完成授权的链接
23
+ - 如果用户当前在手机上对话,优先引导用户直接点击第一条“直接点击授权”链接,不要先强调扫码
24
+ - 如果用户当前在桌面端对话,再引导用户打开授权页直链并扫码或转发到手机
25
25
  - 用户确认后调用 `taptap_dc_complete_authorization`
26
26
  - 然后再次调用 `taptap_dc_quick_brief`
27
27
  3. 只有在用户明确要求更细的内容时,才退回到底层工具链
@@ -45,7 +45,7 @@ description: 生成 TapTap 当前游戏 DC 运营简报与结论解读(商店/
45
45
  ## 关键规则
46
46
 
47
47
  - 这些 plugin tools 返回的是 **raw JSON**,你要自己完成解读,不要把 JSON 原样长篇贴回给用户
48
- - 当授权工具已经返回可点击 markdown 链接时,优先直接复用它,不要再把链接改写成纯文本说明
48
+ - 当授权工具已经返回可点击 markdown 链接时,优先直接复用第一条直链,不要再把它改写成“去扫二维码”
49
49
  - `page_view_count` 应写成“详情页访问量(PV)”,不要偷换成别的口径
50
50
  - `taptap_dc_like_review` / `taptap_dc_reply_review` 只能在用户明确确认后调用
51
51
  - 如果回复结果里出现 `need_confirmation=true`,必须先把草稿给用户确认,再决定是否带 `confirm_high_risk=true` 重试