@lotaber_wang/openclaw-dc-plugin 0.1.9 → 0.1.11

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
@@ -16,7 +16,7 @@
16
16
  ## 安装
17
17
 
18
18
  ```bash
19
- openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.9
19
+ openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.11
20
20
  ```
21
21
 
22
22
  安装完成后,建议立刻执行一次检查:
@@ -31,7 +31,7 @@ openclaw plugins inspect openclaw-dc-plugin
31
31
 
32
32
  推荐通知文案:
33
33
 
34
- `TapTap DC 插件已安装完成。OpenClaw 接下来会重启 Gateway;重启完成后即可使用 taptap_dc_* 工具。`
34
+ `TapTap DC 插件已安装完成。OpenClaw 接下来如需重启 Gateway,应先提示用户;重启完成后即可使用 taptap_dc_* 工具。`
35
35
 
36
36
  执行顺序要求:
37
37
 
@@ -52,9 +52,9 @@ openclaw gateway restart
52
52
  ## 推荐安装流程
53
53
 
54
54
  ```bash
55
- openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.9
55
+ openclaw plugins install @lotaber_wang/openclaw-dc-plugin@0.1.11
56
56
  openclaw plugins inspect openclaw-dc-plugin
57
- echo 'TapTap DC 插件已安装完成。OpenClaw 接下来会重启 Gateway;重启完成后即可使用 taptap_dc_* 工具。'
57
+ echo 'TapTap DC 插件已安装完成。OpenClaw 接下来如需重启 Gateway,应先提示用户;重启完成后即可使用 taptap_dc_* 工具。'
58
58
  openclaw gateway restart
59
59
  ```
60
60
 
@@ -64,6 +64,7 @@ openclaw gateway restart
64
64
  - `inspect` 用来确认插件元信息、skills、tools 是否已被 Gateway 识别
65
65
  - 如果宿主判断需要重启 Gateway,必须先通知用户,再重启
66
66
  - 如果 10-30 秒后还没生效,再手动执行 `gateway restart`
67
+ - 如果宿主存在“stock 安装失败后再 fallback npm”的行为,优先使用明确版本号安装,必要时改用本地 `.tgz` 安装,避免双安装链路互相打架
67
68
 
68
69
  ## 典型使用流程
69
70
 
@@ -129,7 +130,7 @@ openclaw gateway restart
129
130
 
130
131
  如果安装后看不到 `taptap_dc_*` 工具,优先按这个顺序排查:
131
132
 
132
- 1. 确认安装的是 `@lotaber_wang/openclaw-dc-plugin@0.1.9` 或更新版本
133
+ 1. 确认安装的是 `@lotaber_wang/openclaw-dc-plugin@0.1.11` 或更新版本
133
134
  2. 执行 `openclaw plugins inspect openclaw-dc-plugin`
134
135
  3. 如果宿主要求重启 Gateway,先向用户发送安装完成提示
135
136
  4. 再执行 `openclaw gateway restart`
@@ -157,11 +158,14 @@ openclaw gateway restart
157
158
  - OpenClaw 进程是否有临时目录写权限
158
159
  - OpenClaw 是否已经完成 Gateway 重启
159
160
 
160
- 从 `0.1.9` 开始,插件安装链路与 bridge 会额外做这些兼容处理:
161
+ 从 `0.1.11` 开始,插件安装链路与 bridge 会额外做这些兼容处理:
161
162
 
162
163
  - 在 `package.json` 中补充 `openclaw.install.npmSpec`,让安装链路更稳定
164
+ - 在 `package.json` 的 `openclaw.install` 中补充 `postInstallMessage` / `instructions` / `restartCommand`
165
+ - 插件导出对象会同步暴露 `installation` / `installHints`,给宿主额外一层安装提示兜底
163
166
  - 在 `openclaw.plugin.json` 中显式开启 `enabledByDefault`
164
167
  - 统一插件 id 为 `openclaw-dc-plugin`,避免 manifest id 与 entry hint 不一致
168
+ - 默认仅保留错误级别注册/bridge 日志;详细日志改为 `verbose=true` 时才输出,减少安装后自动重载时的噪音
165
169
  - `initialize` 超时后自动切换到无缓冲 / PTY 启动策略重试
166
170
  - `initialize` 默认超时提升到 45 秒,避免宿主启动稍慢时过早失败
167
171
  - 向内嵌 TapTap runtime 发送 `initialize` 与后续请求时,默认改为裸 JSON + 换行(NDJSON 风格),避免只发 `Content-Length` 帧导致无响应
@@ -170,4 +174,4 @@ openclaw gateway restart
170
174
  - 启动时如果 stdout 混入人类可读日志,会先自动丢弃噪音再解析协议消息
171
175
  - 如果拿到的是半截 JSON,会先继续等待后续分片,而不是立刻按失败处理
172
176
  - 过滤 PTY 场景下被回显到 stdout 的请求消息
173
- - 授权结果会优先把“直接点击授权”链接放在第一位,方便移动端对话直接打开
177
+ - 授权结果会把裸授权直链放在最前面,并在 `details.preferred_auth_url` / `details.auth_links` 中重复返回,降低宿主偶发吞链路时的影响
package/index.js CHANGED
@@ -1,6 +1,16 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import process from 'node:process';
2
3
  import { TapTapMcpBridge } from './lib/mcp-bridge.js';
3
4
 
5
+ const RESTART_NOTICE =
6
+ 'TapTap DC 插件已安装完成。OpenClaw 接下来如需重启 Gateway,应先提示用户;重启完成后即可使用 taptap_dc_* 工具。';
7
+
8
+ const INSTALLATION_GUIDANCE = [
9
+ '如果安装流程判断需要重启 Gateway,先通知用户,再执行重启。',
10
+ `推荐提示文案:${RESTART_NOTICE}`,
11
+ '执行顺序:先提示用户 -> 再执行 gateway restart -> 重启完成后再开始使用 taptap_dc_* 工具。',
12
+ ];
13
+
4
14
  function toolResult(text, details = {}) {
5
15
  return {
6
16
  content: [{ type: 'text', text }],
@@ -98,15 +108,60 @@ function enrichAuthPayload(authPayload) {
98
108
  null;
99
109
 
100
110
  const wrappedAuthUrl = authPayload.wrapped_auth_url || authPayload.auth_url || null;
111
+ const preferredAuthUrl = directAuthUrl || wrappedAuthUrl || null;
112
+ const authLinks = [
113
+ preferredAuthUrl
114
+ ? {
115
+ kind: 'preferred',
116
+ label: '直接点击授权',
117
+ url: preferredAuthUrl,
118
+ }
119
+ : null,
120
+ directAuthUrl && directAuthUrl !== preferredAuthUrl
121
+ ? {
122
+ kind: 'direct',
123
+ label: '授权直链',
124
+ url: directAuthUrl,
125
+ }
126
+ : null,
127
+ wrappedAuthUrl && wrappedAuthUrl !== preferredAuthUrl
128
+ ? {
129
+ kind: 'wrapped',
130
+ label: 'TapTap 包装授权页',
131
+ url: wrappedAuthUrl,
132
+ }
133
+ : null,
134
+ authPayload.qrcode_url && authPayload.qrcode_url !== preferredAuthUrl
135
+ ? {
136
+ kind: 'qrcode',
137
+ label: '授权页直链',
138
+ url: authPayload.qrcode_url,
139
+ }
140
+ : null,
141
+ ].filter(Boolean);
101
142
 
102
143
  return {
103
144
  ...authPayload,
104
145
  direct_auth_url: directAuthUrl,
105
146
  wrapped_auth_url: wrappedAuthUrl,
106
- preferred_auth_url: directAuthUrl || wrappedAuthUrl || null,
147
+ preferred_auth_url: preferredAuthUrl,
148
+ authorization_url: preferredAuthUrl,
149
+ mobile_auth_url: preferredAuthUrl,
150
+ auth_links: authLinks,
151
+ next_action:
152
+ '优先打开 preferred_auth_url 完成授权;完成后调用 taptap_dc_complete_authorization。',
107
153
  };
108
154
  }
109
155
 
156
+ function isVerboseLoggingEnabled(config = {}) {
157
+ return (
158
+ config?.verbose === true ||
159
+ String(process.env.TAPTAP_MCP_VERBOSE || '')
160
+ .trim()
161
+ .toLowerCase() === 'true'
162
+ );
163
+ }
164
+
110
165
  function normalizeText(value) {
111
166
  return String(value || '')
112
167
  .trim()
@@ -245,37 +300,61 @@ function buildBriefText(appTitle, sections, meta = {}) {
245
300
 
246
301
  function buildAuthGuideText(authPayload) {
247
302
  const enriched = enrichAuthPayload(authPayload);
303
+ if (enriched?.authorized || enriched?.already_authorized) {
304
+ return [
305
+ 'TapTap 授权已完成,无需再次扫码或打开授权链接。',
306
+ '如果你刚完成授权,接下来可直接调用 `taptap_dc_quick_brief` 或其他 `taptap_dc_*` 工具。',
307
+ ].join('\n');
308
+ }
309
+
310
+ const preferredAuthUrl = enriched?.preferred_auth_url;
248
311
  const directAuthUrl = enriched?.direct_auth_url;
249
312
  const wrappedAuthUrl = enriched?.wrapped_auth_url;
250
313
  const qrcodeUrl = enriched?.qrcode_url;
251
- const lines = [
252
- '当前还没有完成 TapTap 授权,请先完成一次授权。',
253
- '',
254
- '如果你现在是在手机上或当前客户端支持超链接,请优先点击下面这条直接授权链接,不用扫码:',
255
- buildMarkdownLink('直接点击授权', directAuthUrl),
256
- '',
257
- '如果上面的链接打不开,再尝试这个 TapTap 包装授权页:',
258
- buildMarkdownLink('打开 TapTap 包装授权页', wrappedAuthUrl),
259
- '',
260
- '如果你需要在桌面端继续,或者想把授权页转给另一台设备,也可以使用这个直链自行打开或生成二维码:',
261
- buildMarkdownLink('打开授权页直链', qrcodeUrl || directAuthUrl),
314
+ const lines = ['当前还没有完成 TapTap 授权,请先完成一次授权。'];
315
+
316
+ if (preferredAuthUrl) {
317
+ lines.push(
318
+ '',
319
+ '优先打开下面这条授权直链。这一行会同时保留裸链接,方便手机端直接点击,或在宿主吞掉超链接时复制打开:',
320
+ preferredAuthUrl,
321
+ `<${preferredAuthUrl}>`
322
+ );
323
+ }
324
+
325
+ if (preferredAuthUrl || directAuthUrl || wrappedAuthUrl || qrcodeUrl) {
326
+ lines.push(
327
+ '',
328
+ '可点击版本:',
329
+ `- ${buildMarkdownLink('直接点击授权', preferredAuthUrl || directAuthUrl)}`,
330
+ `- ${buildMarkdownLink('打开 TapTap 包装授权页', wrappedAuthUrl)}`,
331
+ `- ${buildMarkdownLink('打开授权页直链', qrcodeUrl || directAuthUrl || preferredAuthUrl)}`
332
+ );
333
+ }
334
+
335
+ lines.push(
262
336
  '',
263
- '完成扫码后,请继续调用 `taptap_dc_complete_authorization`,然后再次调用 `taptap_dc_quick_brief`。',
264
- ];
337
+ '如果你在手机上对话,优先直接点上面的第一条裸链接;如果超链接显示异常,优先使用 details.preferred_auth_url。',
338
+ '完成授权后,请继续调用 `taptap_dc_complete_authorization`,然后再次调用 `taptap_dc_quick_brief`。'
339
+ );
265
340
 
266
341
  if (enriched?.device_code) {
267
342
  lines.push('', `device_code:${enriched.device_code}`);
268
343
  }
269
344
 
270
- if (directAuthUrl) {
345
+ if (preferredAuthUrl) {
346
+ lines.push('', `preferred_auth_url:${preferredAuthUrl}`);
347
+ }
348
+
349
+ if (directAuthUrl && directAuthUrl !== preferredAuthUrl) {
271
350
  lines.push('', `直接授权链接:${directAuthUrl}`);
272
351
  }
273
352
 
274
- if (wrappedAuthUrl && wrappedAuthUrl !== directAuthUrl) {
353
+ if (wrappedAuthUrl && wrappedAuthUrl !== preferredAuthUrl) {
275
354
  lines.push(`TapTap 包装授权链接:${wrappedAuthUrl}`);
276
355
  }
277
356
 
278
- if (qrcodeUrl && qrcodeUrl !== directAuthUrl) {
357
+ if (qrcodeUrl && qrcodeUrl !== preferredAuthUrl) {
279
358
  lines.push(`授权页直链:${qrcodeUrl}`);
280
359
  }
281
360
 
@@ -385,6 +464,23 @@ function registerProxyTool(api, bridge, definition) {
385
464
  );
386
465
  }
387
466
 
467
+ function safeRegisterTool(api, logger, verbose, registerFn, toolName) {
468
+ try {
469
+ registerFn();
470
+ if (verbose) {
471
+ logger?.info?.(`[TapTap DC] Registered OpenClaw tool: ${toolName}`);
472
+ }
473
+ return true;
474
+ } catch (error) {
475
+ logger?.error?.(
476
+ `[TapTap DC] Failed to register OpenClaw tool ${toolName}: ${
477
+ error instanceof Error ? error.stack || error.message : String(error)
478
+ }`
479
+ );
480
+ return false;
481
+ }
482
+ }
483
+
388
484
  function registerQuickBriefTool(api, bridge) {
389
485
  api.registerTool(
390
486
  () => ({
@@ -725,7 +821,18 @@ try {
725
821
  const plugin = {
726
822
  id: 'openclaw-dc-plugin',
727
823
  name: 'TapTap DC',
728
- description: '面向 OpenClaw 的 TapTap DC 插件,内置原始数据工具与运营简报 skill。',
824
+ description:
825
+ '面向 OpenClaw 的 TapTap DC 插件,内置原始数据工具与运营简报 skill;如需重启 Gateway,应先提示用户。',
826
+ installation: {
827
+ restartRequired: true,
828
+ restartCommand: 'openclaw gateway restart',
829
+ postInstallMessage: RESTART_NOTICE,
830
+ instructions: INSTALLATION_GUIDANCE,
831
+ },
832
+ installHints: {
833
+ beforeRestartMessage: RESTART_NOTICE,
834
+ instructions: INSTALLATION_GUIDANCE,
835
+ },
729
836
  configSchema: {
730
837
  type: 'object',
731
838
  properties: {
@@ -754,14 +861,70 @@ const plugin = {
754
861
  additionalProperties: false,
755
862
  },
756
863
  register(api) {
757
- const bridge = new TapTapMcpBridge({
758
- logger: api.logger,
759
- config: api.pluginConfig || {},
760
- });
864
+ const logger = api.logger;
865
+ const verbose = isVerboseLoggingEnabled(api.pluginConfig || {});
866
+ if (verbose) {
867
+ logger?.info?.('[TapTap DC] Starting OpenClaw plugin registration');
868
+ }
869
+
870
+ let bridge;
871
+ try {
872
+ bridge = new TapTapMcpBridge({
873
+ logger,
874
+ config: api.pluginConfig || {},
875
+ });
876
+ } catch (error) {
877
+ logger?.error?.(
878
+ `[TapTap DC] Failed to create TapTapMcpBridge during registration: ${
879
+ error instanceof Error ? error.stack || error.message : String(error)
880
+ }`
881
+ );
882
+ throw error;
883
+ }
884
+
885
+ let successCount = 0;
886
+
887
+ if (
888
+ safeRegisterTool(
889
+ api,
890
+ logger,
891
+ verbose,
892
+ () => registerQuickBriefTool(api, bridge),
893
+ 'taptap_dc_quick_brief'
894
+ )
895
+ ) {
896
+ successCount += 1;
897
+ }
761
898
 
762
- registerQuickBriefTool(api, bridge);
763
899
  for (const definition of toolDefinitions) {
764
- registerProxyTool(api, bridge, definition);
900
+ if (
901
+ safeRegisterTool(
902
+ api,
903
+ logger,
904
+ verbose,
905
+ () => registerProxyTool(api, bridge, definition),
906
+ definition.name
907
+ )
908
+ ) {
909
+ successCount += 1;
910
+ }
911
+ }
912
+
913
+ if (successCount !== toolDefinitions.length + 1) {
914
+ logger?.error?.(
915
+ `[TapTap DC] OpenClaw plugin registration incomplete: ${successCount}/${
916
+ toolDefinitions.length + 1
917
+ } tools registered`
918
+ );
919
+ return;
920
+ }
921
+
922
+ if (verbose) {
923
+ logger?.info?.(
924
+ `[TapTap DC] OpenClaw plugin registration completed: ${successCount}/${
925
+ toolDefinitions.length + 1
926
+ } tools registered`
927
+ );
765
928
  }
766
929
  },
767
930
  };
package/lib/mcp-bridge.js CHANGED
@@ -333,6 +333,11 @@ export class TapTapMcpBridge {
333
333
  constructor(options = {}) {
334
334
  this.logger = options.logger;
335
335
  this.config = options.config || {};
336
+ this.verbose =
337
+ this.config.verbose === true ||
338
+ String(process.env.TAPTAP_MCP_VERBOSE || '')
339
+ .trim()
340
+ .toLowerCase() === 'true';
336
341
  this.child = null;
337
342
  this.readyPromise = null;
338
343
  this.installPromise = null;
@@ -342,6 +347,12 @@ export class TapTapMcpBridge {
342
347
  this.stdoutBuffer = Buffer.alloc(0);
343
348
  }
344
349
 
350
+ logInfo(message) {
351
+ if (this.verbose) {
352
+ this.logger?.info?.(message);
353
+ }
354
+ }
355
+
345
356
  buildEnv() {
346
357
  const env = {
347
358
  ...process.env,
@@ -428,7 +439,7 @@ export class TapTapMcpBridge {
428
439
  mkdirSync(runtimeRoot, { recursive: true });
429
440
  mkdirSync(npmCacheDir, { recursive: true });
430
441
 
431
- this.logger?.info?.(
442
+ this.logInfo(
432
443
  `[TapTap DC] Local TapTap MCP runtime not found, installing ${packageSpec} into ${runtimeRoot}`
433
444
  );
434
445
 
@@ -459,7 +470,7 @@ export class TapTapMcpBridge {
459
470
  output += text;
460
471
  const trimmed = text.trim();
461
472
  if (trimmed) {
462
- this.logger?.info?.(`[TapTap DC][npm] ${trimmed}`);
473
+ this.logInfo(`[TapTap DC][npm] ${trimmed}`);
463
474
  }
464
475
  };
465
476
 
@@ -508,7 +519,7 @@ export class TapTapMcpBridge {
508
519
  const errors = [];
509
520
 
510
521
  for (const attempt of launchAttempts) {
511
- this.logger?.info?.(
522
+ this.logInfo(
512
523
  `[TapTap DC] Starting embedded TapTap MCP runtime from ${runtime.source} using ${attempt.label}: ${runtime.serverPath}`
513
524
  );
514
525
 
@@ -520,7 +531,7 @@ export class TapTapMcpBridge {
520
531
  } catch (error) {
521
532
  const message = error instanceof Error ? error.message : String(error);
522
533
  errors.push(`${attempt.label}: ${message}`);
523
- this.logger?.info?.(
534
+ this.logInfo(
524
535
  `[TapTap DC] Runtime start attempt failed via ${attempt.label}, retrying if possible: ${message}`
525
536
  );
526
537
  await this.close();
@@ -657,11 +668,11 @@ export class TapTapMcpBridge {
657
668
  this.child.stdout.on('data', (chunk) => {
658
669
  try {
659
670
  this.stdoutBuffer = Buffer.concat([this.stdoutBuffer, chunk]);
660
- this.stdoutBuffer = trimNonProtocolNoise(this.stdoutBuffer, this.logger);
671
+ this.stdoutBuffer = trimNonProtocolNoise(this.stdoutBuffer, this.verbose ? this.logger : null);
661
672
  this.stdoutBuffer = parseMessageBuffer(
662
673
  this.stdoutBuffer,
663
674
  (message) => this.handleMessage(message),
664
- this.logger
675
+ this.verbose ? this.logger : null
665
676
  );
666
677
  } catch (error) {
667
678
  this.logger?.error?.(
@@ -669,14 +680,14 @@ export class TapTapMcpBridge {
669
680
  error instanceof Error ? error.message : String(error)
670
681
  }`
671
682
  );
672
- this.stdoutBuffer = trimNonProtocolNoise(this.stdoutBuffer, this.logger);
683
+ this.stdoutBuffer = trimNonProtocolNoise(this.stdoutBuffer, this.verbose ? this.logger : null);
673
684
  }
674
685
  });
675
686
 
676
687
  this.child.stderr.on('data', (chunk) => {
677
688
  const text = chunk.toString('utf8').trim();
678
689
  if (text) {
679
- this.logger?.info?.(`[TapTap MCP] ${text}`);
690
+ this.logInfo(`[TapTap MCP] ${text}`);
680
691
  }
681
692
  });
682
693
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "openclaw-dc-plugin",
3
3
  "name": "TapTap DC",
4
- "description": "面向 OpenClaw 的 TapTap DC 插件,内置原始数据工具与运营简报 skill",
5
- "version": "0.1.9",
4
+ "description": "面向 OpenClaw 的 TapTap DC 插件,内置原始数据工具与运营简报 skill;如需重启 Gateway,应先提示用户。",
5
+ "version": "0.1.11",
6
6
  "enabledByDefault": true,
7
7
  "skills": ["./skills"],
8
8
  "configSchema": {
package/package.json CHANGED
@@ -1,15 +1,23 @@
1
1
  {
2
2
  "name": "@lotaber_wang/openclaw-dc-plugin",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
- "description": "TapTap DC 的 OpenClaw 插件,内置原始数据工具与运营简报 skill",
5
+ "description": "TapTap DC 的 OpenClaw 插件,内置原始数据工具与运营简报 skill;如需重启 Gateway,应先提示用户。",
6
6
  "main": "index.js",
7
7
  "openclaw": {
8
8
  "extensions": [
9
9
  "./index.js"
10
10
  ],
11
11
  "install": {
12
- "npmSpec": "@lotaber_wang/openclaw-dc-plugin"
12
+ "npmSpec": "@lotaber_wang/openclaw-dc-plugin",
13
+ "postInstallMessage": "TapTap DC 插件已安装完成。OpenClaw 接下来如需重启 Gateway,应先提示用户;重启完成后即可使用 taptap_dc_* 工具。",
14
+ "restartRequired": true,
15
+ "restartCommand": "openclaw gateway restart",
16
+ "instructions": [
17
+ "如果安装流程判断需要重启 Gateway,先通知用户,再执行重启。",
18
+ "推荐提示文案:TapTap DC 插件已安装完成。OpenClaw 接下来如需重启 Gateway,应先提示用户;重启完成后即可使用 taptap_dc_* 工具。",
19
+ "执行顺序:先提示用户 -> 再执行 gateway restart -> 重启完成后再开始使用 taptap_dc_* 工具。"
20
+ ]
13
21
  }
14
22
  },
15
23
  "exports": {
@@ -19,8 +19,8 @@ description: 生成 TapTap 当前游戏 DC 运营简报与结论解读(商店/
19
19
  - 如果用户给了游戏名,直接把 `app_name` 传进去
20
20
  - 如果用户给了 `app_id`,直接把 `app_id` 传进去
21
21
  2. 如果未授权
22
- - `taptap_dc_quick_brief` 会直接返回 markdown 授权链接,其中第一条通常就是可直接点击完成授权的链接
23
- - 如果用户当前在手机上对话,优先引导用户直接点击第一条“直接点击授权”链接,不要先强调扫码
22
+ - `taptap_dc_quick_brief` 会直接返回授权信息,其中最前面的裸链接和 `details.preferred_auth_url` 都应视为首选授权入口
23
+ - 如果用户当前在手机上对话,优先引导用户直接点击第一条裸链接,不要先强调扫码
24
24
  - 如果用户当前在桌面端对话,再引导用户打开授权页直链并扫码或转发到手机
25
25
  - 用户确认后调用 `taptap_dc_complete_authorization`
26
26
  - 然后再次调用 `taptap_dc_quick_brief`
@@ -45,7 +45,7 @@ description: 生成 TapTap 当前游戏 DC 运营简报与结论解读(商店/
45
45
  ## 关键规则
46
46
 
47
47
  - 这些 plugin tools 返回的是 **raw JSON**,你要自己完成解读,不要把 JSON 原样长篇贴回给用户
48
- - 当授权工具已经返回可点击 markdown 链接时,优先直接复用第一条直链,不要再把它改写成“去扫二维码”
48
+ - 当授权工具已经返回授权链接时,优先直接复用第一条裸直链;如果文本里链接缺失,就从 `details.preferred_auth_url` 取值,不要直接退化成“去扫二维码”
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` 重试