cardbuddy 0.22.0 → 0.23.0

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.
Files changed (69) hide show
  1. package/README.md +11 -11
  2. package/dist/cli.js +2 -2
  3. package/dist/config.js +3 -3
  4. package/dist/init.js +13 -13
  5. package/dist/mcp.js +1 -1
  6. package/dist/preview-editor/assets/{index-CU8zVA2P.css → index-7RzNd7tf.css} +1 -1
  7. package/dist/preview-editor/assets/{index-CPpLZZyi.js → index-C7N5n_qW.js} +80 -80
  8. package/dist/preview-editor/index.html +2 -2
  9. package/dist/skills-upgrade.js +1 -1
  10. package/dist/standalone.js +2 -2
  11. package/dist/vendored/card-editor/runtime/adapter.js +10 -11
  12. package/dist/vendored/mcp/client.mjs +50 -13
  13. package/dist/vendored/mcp/tools.mjs +13 -7
  14. package/dist/vendored/server/core/ai-chat.js +540 -183
  15. package/dist/vendored/server/core/assetProxy.js +1 -1
  16. package/dist/vendored/server/core/browser.js +1 -1
  17. package/dist/vendored/server/core/cardDigest.js +324 -0
  18. package/dist/vendored/server/core/cardExport.js +5 -5
  19. package/dist/vendored/server/core/cardFiles.js +1 -1
  20. package/dist/vendored/server/core/cardHistoryRemote.js +1 -1
  21. package/dist/vendored/server/core/cardImportMeta.js +1 -1
  22. package/dist/vendored/server/core/cardListCache.js +92 -10
  23. package/dist/vendored/server/core/cardPublish.js +11 -6
  24. package/dist/vendored/server/core/cardPublishVersions.js +8 -8
  25. package/dist/vendored/server/core/cardSave.js +14 -10
  26. package/dist/vendored/server/core/cardSidecar.js +3 -3
  27. package/dist/vendored/server/core/cardSync.js +35 -23
  28. package/dist/vendored/server/core/cardVersion.js +1 -1
  29. package/dist/vendored/server/core/index.js +6 -6
  30. package/dist/vendored/server/core/logger.js +1 -1
  31. package/dist/vendored/server/core/remoteCard.js +5 -5
  32. package/dist/vendored/server/core/skillPaths.js +1 -1
  33. package/dist/vendored/server/core/sliceUpload.js +4 -4
  34. package/dist/vendored/skills/README.md +13 -13
  35. package/dist/vendored/skills/card-doctor/SKILL.md +2 -2
  36. package/dist/vendored/skills/dsl-card-ai/SKILL.md +4 -4
  37. package/dist/vendored/skills/dsl-card-generator/SKILL.md +2 -2
  38. package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +1 -1
  39. package/dist/vendored/skills/dsl-card-generator/references/build_protocol.md +54 -0
  40. package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +2 -2
  41. package/dist/vendored/skills/dsl-card-generator/references/converge_protocol.md +36 -0
  42. package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +1 -1
  43. package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +1 -1
  44. package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +1 -1
  45. package/dist/vendored/skills/dsl-card-generator/references/quickref.md +1 -1
  46. package/dist/vendored/skills/dsl-card-generator/references/recipes.md +4 -4
  47. package/dist/vendored/skills/dsl-card-generator/references/rules.md +1 -1
  48. package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +6 -6
  49. package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +1 -1
  50. package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +1 -1
  51. package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +2 -2
  52. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-CeVX_y-M.js +137 -0
  53. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-DiEiY59H.css +1 -0
  54. package/dist/vendored/skills/dsl-card-generator/vendor/preview/standalone.html +2 -2
  55. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/SKILL.md +26 -26
  56. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/scripts/api_batch_sync.mjs +184 -162
  57. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/scripts/slice_upload.mjs +48 -29
  58. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/SKILL.md +13 -13
  59. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/package-lock.json +2 -2
  60. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/package.json +2 -2
  61. package/dist/vendored/skills/{finmall-session-navigator/scripts/finmall_login.mjs → platform-session-navigator/scripts/platform_login.mjs} +41 -31
  62. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/session-manager.mjs +17 -17
  63. package/dist/vendored/skills/shared/README.md +21 -21
  64. package/dist/vendored/skills/shared/auth-model.md +8 -10
  65. package/dist/vendored/skills/shared/{finmall-config.mjs → platform-config.mjs} +10 -10
  66. package/dist/vendored/tools/{finmall-env.mjs → platform-env.mjs} +13 -13
  67. package/package.json +1 -1
  68. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-BCLshIzC.css +0 -1
  69. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-pP5kTKiI.js +0 -88
@@ -2,14 +2,35 @@ import { mkdir, stat, writeFile, readFile, readdir } from 'node:fs/promises';
2
2
  import { createHash } from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
- import { finmallConfig } from '../../shared/finmall-config.mjs';
5
+ import { platformConfig } from '../../shared/platform-config.mjs';
6
+
7
+ /**
8
+ * 对端站点 host(从配置派生,不写死域名)。
9
+ * 用于在多个标签页里认出「已登录的那个」——控制台里常同时开着本地 dev 页与对端页。
10
+ * 配置缺失时返回空串,调用方按「找不到就新建页」处理。
11
+ */
12
+ const PLATFORM_HOST = (() => {
13
+ try {
14
+ return new URL(platformConfig.baseUrl).host;
15
+ } catch {
16
+ return '';
17
+ }
18
+ })();
19
+ /** 该标签页是否属于对端站点 */
20
+ function isPlatformPage(page) {
21
+ try {
22
+ return Boolean(PLATFORM_HOST) && page.url().includes(PLATFORM_HOST);
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
6
27
 
7
28
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
8
29
  const skillDir = path.resolve(scriptDir, '..');
9
30
  // skill now lives under <repo-root>/skills/<name>: repo root is two levels up from skillDir
10
31
  const workspace = path.resolve(skillDir, '..', '..');
11
- // 与 finmall_login.mjs 保持一致的解析方式:用裸标识符让 Node 从项目根 node_modules 解析
12
- // playwright。原硬编码路径指向 .workbuddy/skills/finmall-session-navigator/scripts/node_modules
32
+ // 与 platform_login.mjs 保持一致的解析方式:用裸标识符让 Node 从项目根 node_modules 解析
33
+ // playwright。原硬编码路径指向 .workbuddy/skills/platform-session-navigator/scripts/node_modules
13
34
  // (该 node_modules 是已失效的断链符号链接),导致「Cannot find module」报错。
14
35
  const { chromium } = await import('playwright');
15
36
 
@@ -31,18 +52,18 @@ try {
31
52
  }
32
53
 
33
54
  // 后台会话管理:点击预览「同步远程数据」时自动拉起/复用无界面 Chrome 并登录,
34
- // 无需手动运行 finmall-session-navigator。
35
- import { ensureFinMallSession } from "../../finmall-session-navigator/scripts/session-manager.mjs";
55
+ // 无需手动运行 platform-session-navigator。
56
+ import { ensurePlatformSession } from "../../platform-session-navigator/scripts/session-manager.mjs";
36
57
  import {
37
58
  performLogin,
38
59
  clearTimeoutPrompts,
39
60
  waitForAuthenticatedPage,
40
61
  loadCredentials,
41
- } from "../../finmall-session-navigator/scripts/finmall_login.mjs";
62
+ } from "../../platform-session-navigator/scripts/platform_login.mjs";
42
63
 
43
64
  // 代理处理:本机设了 HTTP_PROXY,但 Chrome 远程调试端口在 127.0.0.1,需把
44
65
  // localhost 排除在代理外,否则 connectOverCDP 会经代理拿到 502 / ECONNREFUSED。
45
- // 仅影响 Node 侧 CDP 连接;浏览器内访问 finmall.com 仍走代理。
66
+ // 仅影响 Node 侧 CDP 连接;浏览器内访问对端站点仍走代理。
46
67
  for (const key of ["NO_PROXY", "no_proxy"]) {
47
68
  const cur = process.env[key];
48
69
  const extra = "127.0.0.1,localhost";
@@ -77,9 +98,9 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
77
98
  // 404,而是直接挂起不响应;page.evaluate 本身没有默认超时,裸 fetch 会把整次同步永久拖死。
78
99
  // 内部用 AbortController 掐断请求,外部再包一层 Promise.race 兜住 evaluate 自身不返回的情况。
79
100
  const COVER_FETCH_TIMEOUT_MS = Number(process.env.CARDBUDDY_COVER_TIMEOUT_MS || 15000);
80
- // FinMall 交易请求超时。IDC10001/IDC10012 偶发挂起(后端不响应),同样没有任何默认超时,
101
+ // 远端交易请求超时。IDC10001/IDC10012 偶发挂起(后端不响应),同样没有任何默认超时,
81
102
  // 一张卡挂住就会拖垮整批同步——所以和预览图一样,内层 abort + 外层 race 双重设防。
82
- const FINMALL_POST_TIMEOUT_MS = Number(process.env.CARDBUDDY_POST_TIMEOUT_MS || 30000);
103
+ const PLATFORM_POST_TIMEOUT_MS = Number(process.env.CARDBUDDY_POST_TIMEOUT_MS || 30000);
83
104
  // 卡片预览图上传(IDC10014.do)超时。与切图上传同款设防:内层 AbortController +
84
105
  // 外层 Promise.race 兜住 page.evaluate 自身不返回(evaluate 无默认超时)。
85
106
  const PREVIEW_UPLOAD_TIMEOUT_MS = Number(process.env.CARDBUDDY_PREVIEW_UPLOAD_TIMEOUT_MS || 30000);
@@ -94,37 +115,37 @@ function withTimeout(promise, ms, label) {
94
115
  ]).finally(() => clearTimeout(timer));
95
116
  }
96
117
 
97
- // 服务端判定会话/权限失效并应「重新进入」的特征集合。命中后由 finmallPostWithHeal
118
+ // 服务端判定会话/权限失效并应「重新进入」的特征集合。命中后由 platformPostWithHeal
98
119
  // 自动清 session 重登再试一次——覆盖会话超时(BLEC0006)、token 失效、
99
120
  // 「您当前角色菜单权限发生变化,请重新进入」这类权限态异常,以及前端页面会话键丢失
100
121
  // (localStorage._sessionId 为空,如后台 Chrome 被清理/重启后):本地凭证仍在即可自愈。
101
122
  const SESSION_INVALID_RE = /BLEC0006|会话超时|会话过期|登录超时|未登录|登录失效|登录已失效|请重新登录|invalid token|token|角色菜单权限发生变化|权限发生变化|重新进入|请重新进入|缺少会话凭据/i;
102
123
 
103
124
  /**
104
- * 带自愈的 FinMall 接口调用:正常请求,若命中会话/权限失效特征则清 session 重登后重试一次。
125
+ * 带自愈的远端接口调用:正常请求,若命中会话/权限失效特征则清 session 重登后重试一次。
105
126
  * 列表(IDC10001)与 schema(IDC10012)共用,确保任意一步出现权限态异常都能自动恢复。
106
127
  */
107
- async function finmallPostWithHeal(page, urlPath, requestBody, transCode = urlPath) {
128
+ async function platformPostWithHeal(page, urlPath, requestBody, transCode = urlPath) {
108
129
  try {
109
- return await finmallPost(page, urlPath, requestBody, transCode);
130
+ return await platformPost(page, urlPath, requestBody, transCode);
110
131
  } catch (error) {
111
132
  if (SESSION_INVALID_RE.test(error.message)) {
112
133
  log.warn('会话/权限失效,自动重新登录后重试', { url: urlPath, reason: error.message.slice(0, 40) });
113
134
  await relogin(page);
114
- return await finmallPost(page, urlPath, requestBody, transCode);
135
+ return await platformPost(page, urlPath, requestBody, transCode);
115
136
  }
116
137
  throw error;
117
138
  }
118
139
  }
119
140
 
120
141
  /**
121
- * 从已认证的 FinMall 浏览器会话同步卡片到 src/cards。
142
+ * 从已认证的远端浏览器会话同步卡片到 src/cards。
122
143
  * 既可由 CLI 直接运行(api_batch_sync.mjs),也可被 Vite 中间件 import 后供预览「同步远程数据」按钮调用。
123
144
  *
124
- * 会话由 ensureFinMallSession 自动保证:若后台无界面 Chrome(专用端口)未启动或会话缺失,
145
+ * 会话由 ensurePlatformSession 自动保证:若后台无界面 Chrome(专用端口)未启动或会话缺失,
125
146
  * 会自动拉起并登录;完成后只 disconnect 不关闭进程,便于复用。
126
147
  * 若服务端会话/权限失效(IDC10012 返回 BLEC0006 或「角色菜单权限发生变化」等),
127
- * finmallPostWithHeal 会自动清 session 重登再重试。
148
+ * platformPostWithHeal 会自动清 session 重登再重试。
128
149
  *
129
150
  * @param {object} opts
130
151
  * @param {string} [opts.cardCode] 仅同步该英文卡 id(用列表接口 searchValue 定位)
@@ -135,7 +156,7 @@ async function finmallPostWithHeal(page, urlPath, requestBody, transCode = urlPa
135
156
  * @param {number} [opts.maxCards] 最多同步张数(不传=全部)
136
157
  * @param {string} [opts.appId]
137
158
  * @param {string} [opts.workspaceId]
138
- * @param {string} [opts.cdpUrl] 已废弃:会话现在由 ensureFinMallSession 在专用后台端口自动管理
159
+ * @param {string} [opts.cdpUrl] 已废弃:会话现在由 ensurePlatformSession 在专用后台端口自动管理
139
160
  * @param {string} [opts.outRoot] 输出根目录(默认 workspace/src/cards)
140
161
  * @param {(p: {done:number,total:number|null,currentId:string,currentTitle:string}) => void} [opts.onProgress]
141
162
  * 每张卡处理完毕后的进度回调(失败也回调),供前端进度条消费
@@ -157,8 +178,8 @@ export async function syncCards(opts = {}) {
157
178
  const startIndex = Number(opts.startIndex || 0);
158
179
  const pageSize = Number(opts.pageSize || 24);
159
180
  const maxCards = opts.maxCards != null ? Number(opts.maxCards) : Infinity;
160
- const appId = opts.appId || finmallConfig.appId;
161
- const workspaceId = opts.workspaceId || finmallConfig.workspaceId;
181
+ const appId = opts.appId || platformConfig.appId;
182
+ const workspaceId = opts.workspaceId || platformConfig.workspaceId;
162
183
  // 单卡筛选:传 cardCode 时仅同步该卡
163
184
  const cardCodeFilter = opts.cardCode || '';
164
185
  // 集合筛选:cardCodes 非空时仅同步集合内卡片(与单卡筛选互斥,单卡优先)。
@@ -175,8 +196,8 @@ export async function syncCards(opts = {}) {
175
196
  // 预期总张数:单卡=1;集合=集合大小;全量=0(未知,边拉边算)
176
197
  const expectedTotal = cardCodeFilter ? 1 : cardCodeSet ? cardCodeSet.size : 0;
177
198
 
178
- // 自动确保后台 FinMall 会话:未启动/缺失则拉起无界面 Chrome 并登录,无需手动触发。
179
- const session = await ensureFinMallSession();
199
+ // 自动确保后台远端会话:未启动/缺失则拉起无界面 Chrome 并登录,无需手动触发。
200
+ const session = await ensurePlatformSession();
180
201
  const browser = session.browser;
181
202
  const summary = { success: true, synced: [], failed: [], duplicates: [] };
182
203
  // 重复 schema 检测:记录每张卡 schema 的 MD5,若不同 code 命中相同 schema 说明抓取仍串卡。
@@ -184,8 +205,8 @@ export async function syncCards(opts = {}) {
184
205
 
185
206
  try {
186
207
  const context = browser.contexts()[0];
187
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
188
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请检查凭据或重新触发同步。');
208
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
209
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请检查凭据或重新触发同步。');
189
210
  await page.bringToFront();
190
211
 
191
212
  const firstList = await fetchCardList(page, { pageNo: startPage, pageSize, appId, workspaceId, searchValue: cardCodeFilter });
@@ -256,7 +277,8 @@ export async function syncCards(opts = {}) {
256
277
  // 全部分页,allCards 即完整列表,整体刷新同样准确。缓存未就绪则跳过(不影响同步)。
257
278
  try {
258
279
  const cache = await getCardListCache();
259
- if (cache) cache.refreshCardList(allCards, { full: !cardCodeFilter });
280
+ // 盖 appId / workspaceId 戳:环境切换后旧 app 的缓存会被闸门识别为失效而非误判
281
+ if (cache) cache.refreshCardList(allCards, { full: !cardCodeFilter, appId, workspaceId });
260
282
  } catch (e) {
261
283
  log.warn('刷新卡片列表缓存失败(不影响同步)', { error: e && e.message });
262
284
  }
@@ -269,7 +291,7 @@ export async function syncCards(opts = {}) {
269
291
 
270
292
  /**
271
293
  * 拉取远端完整卡片列表(IDC10001 分页全量),供编辑器「查看全部」网格浏览。
272
- * 只读、不落盘;与 syncCards 共用同一后台 FinMall 会话(ensureFinMallSession 自动保活)。
294
+ * 只读、不落盘;与 syncCards 共用同一后台远端会话(ensurePlatformSession 自动保活)。
273
295
  *
274
296
  * @param {object} [opts]
275
297
  * @param {number} [opts.pageSize=50] 列表分页大小
@@ -277,22 +299,22 @@ export async function syncCards(opts = {}) {
277
299
  * @param {string} [opts.appId]
278
300
  * @param {string} [opts.workspaceId]
279
301
  * @returns {Promise<Array<{id:string,title:string,cardId:string|number,coverUrl:string,picUrl:string,cardClassId:string}>>}
280
- * id=英文卡 code(本地目录名同源);coverUrl 为 FinMall 图片库绝对地址(可能为空串);
302
+ * id=英文卡 code(本地目录名同源);coverUrl 为远端图片库绝对地址(可能为空串);
281
303
  * picUrl 为原始 `@filestore/...` 站内地址、cardClassId 为卡片类型 id,二者供「编辑基本信息」原样回传。
282
304
  */
283
305
  export async function listRemoteCards(opts = {}) {
284
- const appId = opts.appId || finmallConfig.appId;
285
- const workspaceId = opts.workspaceId || finmallConfig.workspaceId;
306
+ const appId = opts.appId || platformConfig.appId;
307
+ const workspaceId = opts.workspaceId || platformConfig.workspaceId;
286
308
  const pageSize = Number(opts.pageSize || 50);
287
309
  const maxPages = Number(opts.maxPages || 400);
288
310
 
289
- // 自动确保后台 FinMall 会话:未启动/缺失则拉起无界面 Chrome 并登录,无需手动触发。
290
- const session = await ensureFinMallSession();
311
+ // 自动确保后台远端会话:未启动/缺失则拉起无界面 Chrome 并登录,无需手动触发。
312
+ const session = await ensurePlatformSession();
291
313
  const browser = session.browser;
292
314
  try {
293
315
  const context = browser.contexts()[0];
294
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
295
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请检查凭据或重新触发。');
316
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
317
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请检查凭据或重新触发。');
296
318
  await page.bringToFront();
297
319
 
298
320
  const out = [];
@@ -333,11 +355,11 @@ export async function listRemoteCards(opts = {}) {
333
355
  }
334
356
  }
335
357
 
336
- /** 从后台会话中取出 FinMall 页面(列表 / 字典 / 新建 / 编辑共用同一页面上下文) */
337
- function finmallPage(browser) {
358
+ /** 从后台会话中取出远端页面(列表 / 字典 / 新建 / 编辑共用同一页面上下文) */
359
+ function platformPage(browser) {
338
360
  const context = browser.contexts()[0];
339
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
340
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请检查凭据或重新触发。');
361
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
362
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请检查凭据或重新触发。');
341
363
  return page;
342
364
  }
343
365
 
@@ -353,14 +375,14 @@ function finmallPage(browser) {
353
375
  * @returns {Promise<Array<{id:string,name:string,count:number}>>}
354
376
  */
355
377
  export async function listRemoteCardTypes(opts = {}) {
356
- const appId = opts.appId || finmallConfig.appId;
357
- const workspaceId = opts.workspaceId || finmallConfig.workspaceId;
358
- const session = await ensureFinMallSession();
378
+ const appId = opts.appId || platformConfig.appId;
379
+ const workspaceId = opts.workspaceId || platformConfig.workspaceId;
380
+ const session = await ensurePlatformSession();
359
381
  const browser = session.browser;
360
382
  try {
361
- const page = finmallPage(browser);
383
+ const page = platformPage(browser);
362
384
  await page.bringToFront();
363
- const body = await finmallPostWithHeal(
385
+ const body = await platformPostWithHeal(
364
386
  page,
365
387
  `/ubomp-web/IDC10009.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
366
388
  {
@@ -395,20 +417,20 @@ export async function listRemoteCardTypes(opts = {}) {
395
417
  * @returns {Promise<{cardId:string, code:string}>}
396
418
  */
397
419
  export async function createRemoteCard(input = {}) {
398
- const appId = input.appId || finmallConfig.appId;
399
- const workspaceId = input.workspaceId || finmallConfig.workspaceId;
420
+ const appId = input.appId || platformConfig.appId;
421
+ const workspaceId = input.workspaceId || platformConfig.workspaceId;
400
422
  const code = String(input.code || '').trim();
401
423
  const cardName = String(input.cardName || '').trim();
402
424
  if (!/^[A-Za-z0-9_-]+$/.test(code)) throw new Error(`卡片编码非法(仅允许字母/数字/_/-):${code || '(空)'}`);
403
425
  if (!cardName) throw new Error('卡片名称不能为空');
404
426
  if (input.cardClassId == null || String(input.cardClassId) === '') throw new Error('请选择卡片类型');
405
427
 
406
- const session = await ensureFinMallSession();
428
+ const session = await ensurePlatformSession();
407
429
  const browser = session.browser;
408
430
  try {
409
- const page = finmallPage(browser);
431
+ const page = platformPage(browser);
410
432
  await page.bringToFront();
411
- const body = await finmallPostWithHeal(
433
+ const body = await platformPostWithHeal(
412
434
  page,
413
435
  `/ubomp-web/IDC10002.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
414
436
  {
@@ -439,8 +461,8 @@ export async function createRemoteCard(input = {}) {
439
461
  * @returns {Promise<{cardId:string, code:string}>}
440
462
  */
441
463
  export async function updateRemoteCard(input = {}) {
442
- const appId = input.appId || finmallConfig.appId;
443
- const workspaceId = input.workspaceId || finmallConfig.workspaceId;
464
+ const appId = input.appId || platformConfig.appId;
465
+ const workspaceId = input.workspaceId || platformConfig.workspaceId;
444
466
  const cardId = String(input.cardId || '');
445
467
  const code = String(input.code || '').trim();
446
468
  const cardName = String(input.cardName || '').trim();
@@ -449,14 +471,14 @@ export async function updateRemoteCard(input = {}) {
449
471
  if (!cardName) throw new Error('卡片名称不能为空');
450
472
  if (input.cardClassId == null || String(input.cardClassId) === '') throw new Error('请选择卡片类型');
451
473
 
452
- const session = await ensureFinMallSession();
474
+ const session = await ensurePlatformSession();
453
475
  const browser = session.browser;
454
476
  try {
455
- const page = finmallPage(browser);
477
+ const page = platformPage(browser);
456
478
  await page.bringToFront();
457
479
  // 线上实测:IDC10008 的 URL 不带 sassAppId 查询串(只有 transCode 里带),
458
480
  // 带了也能用;此处按实测的干净路径发送,减少与官方实现的差异面。
459
- const body = await finmallPostWithHeal(
481
+ const body = await platformPostWithHeal(
460
482
  page,
461
483
  '/ubomp-web/IDC10008.do',
462
484
  {
@@ -485,25 +507,25 @@ export async function updateRemoteCard(input = {}) {
485
507
  * { channel:'', cardId }
486
508
  * 即只认数字主键 cardId(列表行数据的 `id`),**不传英文 code**。
487
509
  *
488
- * 不可逆:删除后该卡在 FinMall 侧的元数据与已发布版本一并消失,无法通过重新新建
510
+ * 不可逆:删除后该卡在远端侧的元数据与已发布版本一并消失,无法通过重新新建
489
511
  * 恢复同一 cardId。调用方必须在执行前做二次确认。本地 src/cards/<code> 目录不受影响。
490
512
  *
491
513
  * @param {{cardId:string|number, channel?:string, appId?:string, workspaceId?:string}} input
492
514
  * @returns {Promise<{cardId:string}>}
493
515
  */
494
516
  export async function deleteRemoteCard(input = {}) {
495
- const appId = input.appId || finmallConfig.appId;
496
- const workspaceId = input.workspaceId || finmallConfig.workspaceId;
517
+ const appId = input.appId || platformConfig.appId;
518
+ const workspaceId = input.workspaceId || platformConfig.workspaceId;
497
519
  const cardId = String(input.cardId || '').trim();
498
520
  if (!cardId) throw new Error('缺少 cardId,无法删除远端卡片');
499
521
 
500
- const session = await ensureFinMallSession();
522
+ const session = await ensurePlatformSession();
501
523
  const browser = session.browser;
502
524
  try {
503
- const page = finmallPage(browser);
525
+ const page = platformPage(browser);
504
526
  await page.bringToFront();
505
527
  // 与 IDC10002(新建)同一写法:URL 带 sassAppId / sassWorkspaceId,transCode 取默认(同 URL)
506
- await finmallPostWithHeal(
528
+ await platformPostWithHeal(
507
529
  page,
508
530
  `/ubomp-web/IDC10003.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
509
531
  {
@@ -518,8 +540,8 @@ export async function deleteRemoteCard(input = {}) {
518
540
  }
519
541
 
520
542
  /**
521
- * 把一张卡片的最新 schema 写回 FinMall(线上编辑器「保存」接口 IDC10010.do)。
522
- * 与 syncCards 共用同一套已登录浏览器会话与带自愈的 finmallPost,
543
+ * 把一张卡片的最新 schema 写回远端(线上编辑器「保存」接口 IDC10010.do)。
544
+ * 与 syncCards 共用同一套已登录浏览器会话与带自愈的 platformPost,
523
545
  * 确保任意一步会话/权限失效都能自动重登重试。
524
546
  *
525
547
  * 入参 schema 即本地 src/cards/<id>/schema.json 的完整内容(含 pageId / dictionary / view …)。
@@ -528,7 +550,7 @@ export async function deleteRemoteCard(input = {}) {
528
550
  * - body.schemaValue = 整张 schema 的 JSON 字符串(非对象)
529
551
  * - body.skeletonScreen = 骨架屏 SVG(仅用于编辑器列表缩略图,可传空串)
530
552
  *
531
- * 保存成功后默认立刻读回(IDC10012.do)做落库校验——FinMall 网关有
553
+ * 保存成功后默认立刻读回(IDC10012.do)做落库校验——远端网关有
532
554
  * 「errorCode=0 但 body 空/未落库」的静默空返回先例,不校验无法判断真实生效。
533
555
  *
534
556
  * @param {object} schema 完整卡片 schema(含 pageId)
@@ -540,14 +562,14 @@ export async function saveCardToRemote(schema, { skeletonScreen = '', verify = t
540
562
  if (!schema || !schema.pageId) {
541
563
  throw new Error('schema 缺少 pageId,无法确定要保存的卡片(IDC10010.do 需要数字型 cardId)。')
542
564
  }
543
- const appId = finmallConfig.appId
544
- const workspaceId = finmallConfig.workspaceId
545
- const session = await ensureFinMallSession()
565
+ const appId = platformConfig.appId
566
+ const workspaceId = platformConfig.workspaceId
567
+ const session = await ensurePlatformSession()
546
568
  const browser = session.browser
547
569
  try {
548
570
  const context = browser.contexts()[0]
549
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
550
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请检查凭据或重新触发同步。')
571
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
572
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请检查凭据或重新触发同步。')
551
573
  await page.bringToFront()
552
574
 
553
575
  // 保护远端列表缩略图:本地未提供骨架屏时,先读回远端已有的 skeletonScreen 复用,
@@ -555,7 +577,7 @@ export async function saveCardToRemote(schema, { skeletonScreen = '', verify = t
555
577
  let sk = skeletonScreen && String(skeletonScreen).trim() ? skeletonScreen : ''
556
578
  if (!sk) {
557
579
  try {
558
- const pre = await finmallPostWithHeal(page, '/ubomp-web/IDC10012.do', {
580
+ const pre = await platformPostWithHeal(page, '/ubomp-web/IDC10012.do', {
559
581
  cardId: String(schema.pageId),
560
582
  sassAppId: appId,
561
583
  sassWorkspaceId: workspaceId,
@@ -565,7 +587,7 @@ export async function saveCardToRemote(schema, { skeletonScreen = '', verify = t
565
587
  }
566
588
  if (!sk) sk = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="400"></svg>'
567
589
 
568
- // finmallPost 会把 requestBody 放进 payload.body,并自己生成 payload.header;
590
+ // platformPost 会把 requestBody 放进 payload.body,并自己生成 payload.header;
569
591
  // 因此这里只传「内层 body」(与读接口 fetchCardSchema 保持一致),切勿再包一层 {header, body}。
570
592
  // 关键:IDC10010.do 需要 sassAppId/sassWorkspaceId(与读接口一致),缺失会返回 PEO000003。
571
593
  const innerBody = {
@@ -577,13 +599,13 @@ export async function saveCardToRemote(schema, { skeletonScreen = '', verify = t
577
599
  }
578
600
  const urlPath = `/ubomp-web/IDC10010.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`
579
601
  log.info('保存卡片', { pageId: schema.pageId, name: schema.name || '' })
580
- const response = await finmallPostWithHeal(page, urlPath, innerBody, 'IDC10010.do')
602
+ const response = await platformPostWithHeal(page, urlPath, innerBody, 'IDC10010.do')
581
603
 
582
604
  // 落库校验:保存后立刻读回,确认内容真的写进去了(而非静默空返回)。
583
605
  const verified = { ok: false, cardIdMatched: false, fingerprintMatched: false, diff: null, readBackError: null }
584
606
  if (verify) {
585
607
  try {
586
- const readBody = await finmallPostWithHeal(page, '/ubomp-web/IDC10012.do', {
608
+ const readBody = await platformPostWithHeal(page, '/ubomp-web/IDC10012.do', {
587
609
  cardId: String(schema.pageId),
588
610
  sassAppId: appId,
589
611
  sassWorkspaceId: workspaceId,
@@ -621,7 +643,7 @@ export async function saveCardToRemote(schema, { skeletonScreen = '', verify = t
621
643
  * - 没有 `fileName` 纯文本字段;
622
644
  * - 鉴权头只有 `token`。
623
645
  *
624
- * token 仅在页面上下文内读取并直接用于请求,不回传 Node 侧、不打印、不落盘(与 finmallPost 同纪律)。
646
+ * token 仅在页面上下文内读取并直接用于请求,不回传 Node 侧、不打印、不落盘(与 platformPost 同纪律)。
625
647
  *
626
648
  * @returns {Promise<string>} 原始响应文本(交由 parsePreviewUploadResponse 解析)
627
649
  */
@@ -651,7 +673,7 @@ async function postPreviewUpload(page, { cardId, fileName, base64, appId, worksp
651
673
  };
652
674
  const sessionToken = readSessionToken();
653
675
  if (!sessionToken) {
654
- throw new Error('页面缺少会话凭据(localStorage._sessionId 为空),请先运行 finmall-session-navigator 登录。');
676
+ throw new Error('页面缺少会话凭据(localStorage._sessionId 为空),请先运行 platform-session-navigator 登录。');
655
677
  }
656
678
  const binary = atob(base64);
657
679
  const bytes = new Uint8Array(binary.length);
@@ -683,7 +705,7 @@ async function postPreviewUpload(page, { cardId, fileName, base64, appId, worksp
683
705
  /**
684
706
  * 解析 IDC10014.do 响应(导出仅为单测可直接断言;正常路径走 uploadCardPreview)。
685
707
  *
686
- * 该接口的响应体形态没有可比对的既有文档,故按 FinMall 交易接口的通用信封
708
+ * 该接口的响应体形态没有可比对的既有文档,故按远端交易接口的通用信封
687
709
  * (`{header:{errorCode,errorMsg}, body:{...}}`)做**宽松**判定:
688
710
  * - 空响应体 → 视为成功(HTTP 2xx 已由调用方保证,接口可能不返回业务体);
689
711
  * - 非 JSON 非空 → 直接报错(多半是网关 HTML 错误页,不能当成功);
@@ -712,7 +734,7 @@ export function parsePreviewUploadResponse(text, cardId) {
712
734
  const bad = codes.find((c) => String(c) !== '0');
713
735
  if (bad != null) {
714
736
  throw new Error(
715
- `FinMall 预览图上传失败 [errorCode=${bad}] ${header.errorMsg || body.errorMsg || ''} | ${raw.slice(0, 300)}`,
737
+ `远端预览图上传失败 [errorCode=${bad}] ${header.errorMsg || body.errorMsg || ''} | ${raw.slice(0, 300)}`,
716
738
  );
717
739
  }
718
740
  // 地址字段多键兜底:接口未回地址属正常(上传即生效),所以这里只做尽力提取。
@@ -728,10 +750,10 @@ export function parsePreviewUploadResponse(text, cardId) {
728
750
 
729
751
  /**
730
752
  * 上传卡片预览图到远端(IDC10014.do)。供「推送」成功后由 server/core/cardSave.js 调用:
731
- * 本地无头渲染出透明底 PNG → 交给已登录的 FinMall 后台会话上传,卡片列表缩略图随之更新。
753
+ * 本地无头渲染出透明底 PNG → 交给已登录的远端后台会话上传,卡片列表缩略图随之更新。
732
754
  *
733
755
  * 与 saveCardToRemote 的会话纪律一致:saveCardToRemote 末尾会 disconnect,故本函数自行
734
- * ensureFinMallSession 重新取一次连接(Chrome 已常驻时这一步是廉价的 attach)。
756
+ * ensurePlatformSession 重新取一次连接(Chrome 已常驻时这一步是廉价的 attach)。
735
757
  * 会话/权限失效命中 SESSION_INVALID_RE 时自动清 session 重登并重试一次。
736
758
  *
737
759
  * @param {{ cardId: string|number, pngBase64: string, appId?: string, workspaceId?: string }} opts
@@ -744,16 +766,16 @@ export async function uploadCardPreview({ cardId, pngBase64, appId, workspaceId
744
766
  }
745
767
  const base64 = String(pngBase64 == null ? '' : pngBase64);
746
768
  if (!base64) throw new Error('上传卡片预览图缺少 PNG 内容(pngBase64 为空)。');
747
- const appIdv = appId || finmallConfig.appId;
748
- const workspaceIdv = workspaceId || finmallConfig.workspaceId;
769
+ const appIdv = appId || platformConfig.appId;
770
+ const workspaceIdv = workspaceId || platformConfig.workspaceId;
749
771
  const fileName = `${id}.png`;
750
772
 
751
- const session = await ensureFinMallSession();
773
+ const session = await ensurePlatformSession();
752
774
  const browser = session.browser;
753
775
  try {
754
776
  const context = browser.contexts()[0];
755
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
756
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请检查凭据或重新触发同步。');
777
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
778
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请检查凭据或重新触发同步。');
757
779
  await page.bringToFront();
758
780
 
759
781
  const payload = { cardId: id, fileName, base64, appId: appIdv, workspaceId: workspaceIdv };
@@ -788,7 +810,7 @@ export async function uploadCardPreview({ cardId, pngBase64, appId, workspaceId
788
810
  * 前端据返回的 cardCode 生成 scheme 二维码:
789
811
  * app://previewCard?cardId=<id>&cardCode=<code>&cardVersion=00000000&socket=<wss>
790
812
  *
791
- * 会话由 ensureFinMallSession 自动保证;与「保存到远端」共用同一套已登录后台 Chrome。
813
+ * 会话由 ensurePlatformSession 自动保证;与「保存到远端」共用同一套已登录后台 Chrome。
792
814
  * 注意:saveCardToRemote 内部会自行 ensure+disconnect,故本函数先调用它(自带会话),
793
815
  * 之后再重新 ensure 一次拿干净会话处理后续三个接口,避免复用已被 disconnect 的 page。
794
816
  *
@@ -800,8 +822,8 @@ export async function previewCardRemote(schema, { skeletonScreen = '', publish =
800
822
  if (!schema || !schema.pageId) {
801
823
  throw new Error('schema 缺少 pageId,无法确定要预览的卡片(IDC10020.do 需要 cardId)。')
802
824
  }
803
- const appId = finmallConfig.appId
804
- const workspaceId = finmallConfig.workspaceId
825
+ const appId = platformConfig.appId
826
+ const workspaceId = platformConfig.workspaceId
805
827
  const cardId = String(schema.pageId)
806
828
  const result = { cardCode: '', version: '00000000', steps: {} }
807
829
 
@@ -816,17 +838,17 @@ export async function previewCardRemote(schema, { skeletonScreen = '', publish =
816
838
  }
817
839
 
818
840
  // 后续三个接口使用一次干净的会话
819
- const session = await ensureFinMallSession()
841
+ const session = await ensurePlatformSession()
820
842
  const browser = session.browser
821
843
  try {
822
844
  const context = browser.contexts()[0]
823
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
824
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。')
845
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
846
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。')
825
847
  await page.bringToFront()
826
848
 
827
849
  // 2) 取卡片 code(对齐 ubomp queryCardDetail → IDC10015.do 的 code 字段)
828
850
  try {
829
- const detail = await finmallPostWithHeal(
851
+ const detail = await platformPostWithHeal(
830
852
  page,
831
853
  `/ubomp-web/IDC10015.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
832
854
  { cardId, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -840,7 +862,7 @@ export async function previewCardRemote(schema, { skeletonScreen = '', publish =
840
862
  // 3) 发布预览测试版(对齐 ubomp submitForm → IDC10020.do)
841
863
  if (publish) {
842
864
  try {
843
- await finmallPostWithHeal(
865
+ await platformPostWithHeal(
844
866
  page,
845
867
  `/ubomp-web/IDC10020.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
846
868
  {
@@ -862,7 +884,7 @@ export async function previewCardRemote(schema, { skeletonScreen = '', publish =
862
884
  // 4) 消息通知(对齐 ubomp doNotify → IDC10021.do)
863
885
  if (notify) {
864
886
  try {
865
- await finmallPostWithHeal(
887
+ await platformPostWithHeal(
866
888
  page,
867
889
  `/ubomp-web/IDC10021.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
868
890
  { agentId: cardId, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -882,7 +904,7 @@ export async function previewCardRemote(schema, { skeletonScreen = '', publish =
882
904
  }
883
905
 
884
906
  /**
885
- * FinMall 版本号格式:8 位「年份 + 4 位序号」,如 20260025。
907
+ * 远端版本号格式:8 位「年份 + 4 位序号」,如 20260025。
886
908
  * 该格式的最小值约 20200001,故任何 < 10000000 的纯数字都不符合格式
887
909
  * (含旧实现首发时误写出的 1 / 2 / 3…)。
888
910
  */
@@ -916,7 +938,7 @@ export function nextPublishVersion(maxVersion) {
916
938
  * 2) IDC10016.do 以 nextPublishVersion(maxVersion) 作为新版本号发布(发布的是远端「当前已保存」的卡片内容——
917
939
  * 故发布前应先用「推送当前卡」把本地内容保存到远端,否则会发布陈旧内容)。
918
940
  * 版本号格式为 8 位「年份 + 4 位序号」:远端从未发布时取 `${当前年份}0001`(不是裸 1)。
919
- * 会话由 ensureFinMallSession 自动保证;与 saveCardToRemote / syncCards / previewCardRemote
941
+ * 会话由 ensurePlatformSession 自动保证;与 saveCardToRemote / syncCards / previewCardRemote
920
942
  * 共用同一套已登录后台 Chrome,命中会话/权限失效(BLEC0006 等)时自动清 session 重登重试一次。
921
943
  *
922
944
  * @param {object} schema 当前卡片 schema(需含 pageId)
@@ -926,19 +948,19 @@ export async function publishCard(schema) {
926
948
  if (!schema || !schema.pageId) {
927
949
  throw new Error('schema 缺少 pageId,无法确定要发布的卡片(IDC10016.do 需要 cardId)。')
928
950
  }
929
- const appId = finmallConfig.appId
930
- const workspaceId = finmallConfig.workspaceId
951
+ const appId = platformConfig.appId
952
+ const workspaceId = platformConfig.workspaceId
931
953
  const cardId = String(schema.pageId)
932
- const session = await ensureFinMallSession()
954
+ const session = await ensurePlatformSession()
933
955
  const browser = session.browser
934
956
  try {
935
957
  const context = browser.contexts()[0]
936
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
937
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。')
958
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
959
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。')
938
960
  await page.bringToFront()
939
961
 
940
962
  // 1) 取当前最大版本号
941
- const maxResp = await finmallPostWithHeal(
963
+ const maxResp = await platformPostWithHeal(
942
964
  page,
943
965
  `/ubomp-web/IDC10004.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
944
966
  { cardId, pageNo: 1, pageSize: 1, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -949,7 +971,7 @@ export async function publishCard(schema) {
949
971
  log.info('发布前取最大版本', { cardId, maxVersion, nextVersion })
950
972
 
951
973
  // 2) 以 maxVersion+1 发布(发布类型/平台等参数对齐 ubomp 发布弹窗的默认值)
952
- const response = await finmallPostWithHeal(
974
+ const response = await platformPostWithHeal(
953
975
  page,
954
976
  `/ubomp-web/IDC10016.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
955
977
  {
@@ -978,7 +1000,7 @@ export async function publishCard(schema) {
978
1000
  /**
979
1001
  * 取远端「当前卡片最新版本号」:IDC10004 返回 maxVersion(该卡线上已发布的最高版本)。
980
1002
  * 供 UI「卡片来源」tab 展示「远端最新版本」,让用户一眼看到线上当前版本。
981
- * 只读、不发布;与 publishCard 共用同一后台 FinMall 会话(ensureFinMallSession + finmallPostWithHeal)。
1003
+ * 只读、不发布;与 publishCard 共用同一后台远端会话(ensurePlatformSession + platformPostWithHeal)。
982
1004
  * @param {string|number} cardId
983
1005
  * @returns {Promise<{ maxVersion: string }>}
984
1006
  */
@@ -986,17 +1008,17 @@ export async function fetchCardLatestVersion(cardId) {
986
1008
  if (cardId == null) {
987
1009
  throw new Error('缺少 cardId,无法查询远端版本(IDC10004.do 需要 cardId)。')
988
1010
  }
989
- const appId = finmallConfig.appId
990
- const workspaceId = finmallConfig.workspaceId
1011
+ const appId = platformConfig.appId
1012
+ const workspaceId = platformConfig.workspaceId
991
1013
  const id = String(cardId)
992
- const session = await ensureFinMallSession()
1014
+ const session = await ensurePlatformSession()
993
1015
  const browser = session.browser
994
1016
  try {
995
1017
  const context = browser.contexts()[0]
996
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
997
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。')
1018
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
1019
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。')
998
1020
  await page.bringToFront()
999
- const maxResp = await finmallPostWithHeal(
1021
+ const maxResp = await platformPostWithHeal(
1000
1022
  page,
1001
1023
  `/ubomp-web/IDC10004.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1002
1024
  { cardId: id, pageNo: 1, pageSize: 1, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -1044,8 +1066,8 @@ function normalizeVersionTask(raw) {
1044
1066
  * 取远端「发布版本记录」分页列表:IDC10004.do 的 versionList(新→旧,最多一页 pageSize 条)。
1045
1067
  * 供调试面板「导入信息」tab 的「发布版本记录」弹框渲染
1046
1068
  * (版本号 / 平台 / 状态 / 发布时间 / 操作:停止发布)。
1047
- * 只读;与 fetchCardLatestVersion / fetchCardHistory 共用同一后台 FinMall 会话
1048
- * (ensureFinMallSession + finmallPostWithHeal,命中会话失效自动清 session 重登重试一次)。
1069
+ * 只读;与 fetchCardLatestVersion / fetchCardHistory 共用同一后台远端会话
1070
+ * (ensurePlatformSession + platformPostWithHeal,命中会话失效自动清 session 重登重试一次)。
1049
1071
  *
1050
1072
  * 响应体(逆向 innermanage 前端 inner_flamePublish 页面):
1051
1073
  * body.versionList[] = { versionId, version, platformStatus(JSON 字符串,如 '["iOS","Android"]'),
@@ -1075,17 +1097,17 @@ export async function fetchCardPublishVersions(
1075
1097
  if (cardId == null) {
1076
1098
  throw new Error('缺少 cardId,无法查询发布版本记录(IDC10004.do 需要 cardId)。');
1077
1099
  }
1078
- const appId = finmallConfig.appId;
1079
- const workspaceId = finmallConfig.workspaceId;
1100
+ const appId = platformConfig.appId;
1101
+ const workspaceId = platformConfig.workspaceId;
1080
1102
  const id = String(cardId);
1081
- const session = await ensureFinMallSession();
1103
+ const session = await ensurePlatformSession();
1082
1104
  const browser = session.browser;
1083
1105
  try {
1084
1106
  const context = browser.contexts()[0];
1085
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
1086
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。');
1107
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
1108
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。');
1087
1109
  await page.bringToFront();
1088
- const resp = await finmallPostWithHeal(
1110
+ const resp = await platformPostWithHeal(
1089
1111
  page,
1090
1112
  `/ubomp-web/IDC10004.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1091
1113
  {
@@ -1136,7 +1158,7 @@ export async function fetchCardPublishVersions(
1136
1158
  await Promise.all(
1137
1159
  pending.slice(i, i + TASK_CONCURRENCY).map(async (row) => {
1138
1160
  try {
1139
- const taskResp = await finmallPostWithHeal(
1161
+ const taskResp = await platformPostWithHeal(
1140
1162
  page,
1141
1163
  `/ubomp-web/IDC20002.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1142
1164
  { packageInfoId: row.versionId },
@@ -1190,17 +1212,17 @@ export async function fetchCardVersionTasks(packageInfoId) {
1190
1212
  if (packageInfoId == null || packageInfoId === '') {
1191
1213
  throw new Error('缺少 packageInfoId(版本记录 versionId),无法查询发布任务(IDC20002.do)。');
1192
1214
  }
1193
- const appId = finmallConfig.appId;
1194
- const workspaceId = finmallConfig.workspaceId;
1215
+ const appId = platformConfig.appId;
1216
+ const workspaceId = platformConfig.workspaceId;
1195
1217
  const id = String(packageInfoId);
1196
- const session = await ensureFinMallSession();
1218
+ const session = await ensurePlatformSession();
1197
1219
  const browser = session.browser;
1198
1220
  try {
1199
1221
  const context = browser.contexts()[0];
1200
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
1201
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。');
1222
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
1223
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。');
1202
1224
  await page.bringToFront();
1203
- const resp = await finmallPostWithHeal(
1225
+ const resp = await platformPostWithHeal(
1204
1226
  page,
1205
1227
  `/ubomp-web/IDC20002.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1206
1228
  { packageInfoId: id },
@@ -1234,16 +1256,16 @@ export async function updateCardTaskStatus({ taskId, taskStatus = '0' } = {}) {
1234
1256
  if (taskId == null || taskId === '') {
1235
1257
  throw new Error('缺少 taskId,无法变更发布任务状态(IDC20003.do 需要 taskId)。');
1236
1258
  }
1237
- const appId = finmallConfig.appId;
1238
- const workspaceId = finmallConfig.workspaceId;
1239
- const session = await ensureFinMallSession();
1259
+ const appId = platformConfig.appId;
1260
+ const workspaceId = platformConfig.workspaceId;
1261
+ const session = await ensurePlatformSession();
1240
1262
  const browser = session.browser;
1241
1263
  try {
1242
1264
  const context = browser.contexts()[0];
1243
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0];
1244
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。');
1265
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0];
1266
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。');
1245
1267
  await page.bringToFront();
1246
- const body = await finmallPostWithHeal(
1268
+ const body = await platformPostWithHeal(
1247
1269
  page,
1248
1270
  `/ubomp-web/IDC20003.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1249
1271
  {
@@ -1287,16 +1309,16 @@ export async function createCardPublishTask({ cardId, versionId, version = "", m
1287
1309
  if (cardId == null || cardId === "" || versionId == null || versionId === "") {
1288
1310
  throw new Error("缺少 cardId / versionId,无法创建发布任务(IDC20004.do 需要两者)。");
1289
1311
  }
1290
- const appId = finmallConfig.appId;
1291
- const workspaceId = finmallConfig.workspaceId;
1292
- const session = await ensureFinMallSession();
1312
+ const appId = platformConfig.appId;
1313
+ const workspaceId = platformConfig.workspaceId;
1314
+ const session = await ensurePlatformSession();
1293
1315
  const browser = session.browser;
1294
1316
  try {
1295
1317
  const context = browser.contexts()[0];
1296
1318
  const page = context?.pages().find((p) => p.url().includes("finmall.com")) || context?.pages()[0];
1297
- if (!page) throw new Error("未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。");
1319
+ if (!page) throw new Error("未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。");
1298
1320
  await page.bringToFront();
1299
- const body = await finmallPostWithHeal(
1321
+ const body = await platformPostWithHeal(
1300
1322
  page,
1301
1323
  `/ubomp-web/IDC20004.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1302
1324
  {
@@ -1325,7 +1347,7 @@ export async function createCardPublishTask({ cardId, versionId, version = "", m
1325
1347
  /**
1326
1348
  * 取远端「卡片历史版本时间线」:IDC10013 按 cardId 分页返回 schemaValueList(新→旧)。
1327
1349
  * 供编辑器「卡片历史记录」tab 渲染 git 式时间线。只读;与 fetchCardLatestVersion 共用
1328
- * 同一后台 FinMall 会话(ensureFinMallSession + finmallPostWithHeal)。
1350
+ * 同一后台远端会话(ensurePlatformSession + platformPostWithHeal)。
1329
1351
  *
1330
1352
  * schema 全量(每条数 KB~上百 KB)不跨端透传:服务端就地解析为轻量摘要 + 相邻版本 diff
1331
1353
  * 标签,响应体积与 schema 大小解耦,UI 拿到的就是可直接渲染的字段。
@@ -1339,7 +1361,7 @@ export async function createCardPublishTask({ cardId, versionId, version = "", m
1339
1361
  // fetchCardHistory(时间线列表)本来就会解析页内全部 schema 算 diff,顺手按
1340
1362
  // (cardId, versionId) 缓存;fetchCardVersionSchema 命中缓存时零网络请求,
1341
1363
  // 把点击到加载的等待从 ~7s 降到毫秒级。未命中仍走原分页翻找兜底。
1342
- const SCHEMA_CACHE_TTL_MS = Number(process.env.FINMALL_SCHEMA_CACHE_TTL_MS || 300000)
1364
+ const SCHEMA_CACHE_TTL_MS = Number(process.env.PLATFORM_SCHEMA_CACHE_TTL_MS || 300000)
1343
1365
  const SCHEMA_CACHE_MAX = 500
1344
1366
  const _versionSchemaCache = new Map() // `${cardId}::${versionId}` → { schema, createTime, at }
1345
1367
 
@@ -1371,17 +1393,17 @@ export async function fetchCardHistory(cardId, { pageNo = 1, pageSize = 30 } = {
1371
1393
  if (cardId == null) {
1372
1394
  throw new Error('缺少 cardId,无法查询远端历史(IDC10013.do 需要 cardId)。')
1373
1395
  }
1374
- const appId = finmallConfig.appId
1375
- const workspaceId = finmallConfig.workspaceId
1396
+ const appId = platformConfig.appId
1397
+ const workspaceId = platformConfig.workspaceId
1376
1398
  const id = String(cardId)
1377
- const session = await ensureFinMallSession()
1399
+ const session = await ensurePlatformSession()
1378
1400
  const browser = session.browser
1379
1401
  try {
1380
1402
  const context = browser.contexts()[0]
1381
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
1382
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。')
1403
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
1404
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。')
1383
1405
  await page.bringToFront()
1384
- const resp = await finmallPostWithHeal(
1406
+ const resp = await platformPostWithHeal(
1385
1407
  page,
1386
1408
  `/ubomp-web/IDC10013.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`,
1387
1409
  { cardId: id, pageNo, pageSize, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -1442,20 +1464,20 @@ export async function fetchCardVersionSchema(cardId, versionId) {
1442
1464
  log.info('命中版本 schema 缓存(零网络)', { cardId: id, versionId: want })
1443
1465
  return { schema: cached.schema, record: { id: want, createTime: cached.createTime || '' } }
1444
1466
  }
1445
- const appId = finmallConfig.appId
1446
- const workspaceId = finmallConfig.workspaceId
1447
- const session = await ensureFinMallSession()
1467
+ const appId = platformConfig.appId
1468
+ const workspaceId = platformConfig.workspaceId
1469
+ const session = await ensurePlatformSession()
1448
1470
  const browser = session.browser
1449
1471
  try {
1450
1472
  const context = browser.contexts()[0]
1451
- const page = context?.pages().find((p) => p.url().includes('finmall.com')) || context?.pages()[0]
1452
- if (!page) throw new Error('未能在后台 Chrome 中找到 FinMall 页面,请先运行 finmall-session-navigator 登录。')
1473
+ const page = context?.pages().find((p) => isPlatformPage(p)) || context?.pages()[0]
1474
+ if (!page) throw new Error('未能在后台 Chrome 中找到远端页面,请先运行 platform-session-navigator 登录。')
1453
1475
  await page.bringToFront()
1454
1476
  const url = `/ubomp-web/IDC10013.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`
1455
1477
  let pageNo = 1
1456
1478
  let totalPages = 1
1457
1479
  do {
1458
- const resp = await finmallPostWithHeal(
1480
+ const resp = await platformPostWithHeal(
1459
1481
  page,
1460
1482
  url,
1461
1483
  { cardId: id, pageNo, pageSize: 100, sassAppId: appId, sassWorkspaceId: workspaceId },
@@ -1626,7 +1648,7 @@ function parseArgs(argv) {
1626
1648
  }
1627
1649
 
1628
1650
  async function fetchCardList(page, { pageNo, pageSize, appId, workspaceId, searchValue = '' }) {
1629
- const body = await finmallPostWithHeal(page, `/ubomp-web/IDC10001.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`, {
1651
+ const body = await platformPostWithHeal(page, `/ubomp-web/IDC10001.do?sassAppId=${appId}&sassWorkspaceId=${workspaceId}`, {
1630
1652
  channel: '',
1631
1653
  applyScene: '1',
1632
1654
  pageNo,
@@ -1640,7 +1662,7 @@ async function fetchCardList(page, { pageNo, pageSize, appId, workspaceId, searc
1640
1662
  }
1641
1663
 
1642
1664
  /**
1643
- * 拉取远端 schema。返回 `null` 表示「空白卡」——该卡片在 FinMall 侧从未保存过
1665
+ * 拉取远端 schema。返回 `null` 表示「空白卡」——该卡片在远端侧从未保存过
1644
1666
  * schema(新建后未编辑),IDC10012.do 会返回 errorCode=0 但 body 无 schemaValue。
1645
1667
  * 这是合法状态而非主键错误,调用方须走「空白卡落盘」分支,不要当成同步失败。
1646
1668
  */
@@ -1651,7 +1673,7 @@ async function fetchCardSchema(page, card, { appId, workspaceId }) {
1651
1673
  // dialog 变体的 cardId 本就不同(如 activityLevel=10000840 / activityLevelDialog=10000841),
1652
1674
  // 因此按 cardId 抓取不会串卡;此前的串卡来自已废弃的编辑器 DOM 抓取路径。
1653
1675
  if (!card.cardId) throw new Error(`列表未返回 cardId,无法抓取 ${card.id} 的 schema。`);
1654
- const body = await finmallPostWithHeal(page, '/ubomp-web/IDC10012.do', {
1676
+ const body = await platformPostWithHeal(page, '/ubomp-web/IDC10012.do', {
1655
1677
  cardId: card.cardId,
1656
1678
  sassAppId: appId,
1657
1679
  sassWorkspaceId: workspaceId,
@@ -1673,9 +1695,9 @@ async function fetchCardSchema(page, card, { appId, workspaceId }) {
1673
1695
  }
1674
1696
 
1675
1697
  /**
1676
- * 取 schema(已在 finmallPostWithHeal 内带会话/权限失效自愈)。
1698
+ * 取 schema(已在 platformPostWithHeal 内带会话/权限失效自愈)。
1677
1699
  * IDC10012.do 只接受数字型 `cardId` 作为主键,命中 SESSION_INVALID_RE 特征时
1678
- * finmallPostWithHeal 会自动清 session 重登再试一次,覆盖会话超时与权限态异常。
1700
+ * platformPostWithHeal 会自动清 session 重登再试一次,覆盖会话超时与权限态异常。
1679
1701
  */
1680
1702
 
1681
1703
  /** 清空过期会话并以保存的凭据重新登录(复用导航器的登录逻辑,全程在页面上下文内完成)。 */
@@ -1689,7 +1711,7 @@ async function relogin(page) {
1689
1711
  /* ignore */
1690
1712
  }
1691
1713
  }),
1692
- FINMALL_POST_TIMEOUT_MS,
1714
+ PLATFORM_POST_TIMEOUT_MS,
1693
1715
  'page.evaluate(localStorage.clear)',
1694
1716
  ).catch(() => {});
1695
1717
  await page.reload({ waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
@@ -1708,12 +1730,12 @@ async function relogin(page) {
1708
1730
  await waitForAuthenticatedPage(page, 20000);
1709
1731
  }
1710
1732
 
1711
- async function finmallPost(page, urlPath, requestBody, transCode = urlPath) {
1733
+ async function platformPost(page, urlPath, requestBody, transCode = urlPath) {
1712
1734
  return withTimeout(
1713
1735
  page.evaluate(async ({ urlPath, requestBody, transCode, timeoutMs }) => {
1714
1736
  const controller = new AbortController();
1715
1737
  setTimeout(() => controller.abort(), timeoutMs);
1716
- // FinMall 不用 Cookie 鉴权(document.cookie 恒为空),会话凭据存在 localStorage
1738
+ // 远端不用 Cookie 鉴权(document.cookie 恒为空),会话凭据存在 localStorage
1717
1739
  // 的 `_sessionId` 中,由前端以 `token` 请求头透传。缺这个头时 IDC10001.do(不鉴权)
1718
1740
  // 仍能返回,但 IDC10012.do 会返回 BLEC0006「会话超时」——这正是此前抓取失败的根因。
1719
1741
  // 该值仅在页面上下文内读取并直接用于请求,不回传 Node 侧、不打印、不落盘。
@@ -1733,7 +1755,7 @@ async function finmallPost(page, urlPath, requestBody, transCode = urlPath) {
1733
1755
  };
1734
1756
  const sessionToken = readSessionToken();
1735
1757
  if (!sessionToken) {
1736
- throw new Error('页面缺少会话凭据(localStorage._sessionId 为空),请先运行 finmall-session-navigator 登录。');
1758
+ throw new Error('页面缺少会话凭据(localStorage._sessionId 为空),请先运行 platform-session-navigator 登录。');
1737
1759
  }
1738
1760
  const url = new URL(urlPath, location.origin).href;
1739
1761
  const payload = {
@@ -1772,12 +1794,12 @@ async function finmallPost(page, urlPath, requestBody, transCode = urlPath) {
1772
1794
  if (errorCode && errorCode !== '0') {
1773
1795
  // 把 errorCode 与响应体片段一并抛出,便于排查(如 cardId 不存在 / 无权限 / 入参结构不符)。
1774
1796
  const detail = JSON.stringify(parsed).slice(0, 600);
1775
- throw new Error(`FinMall 交易失败 [errorCode=${errorCode}] ${parsed.header?.errorMsg || parsed.body?.errorMsg || ''} | ${detail}`);
1797
+ throw new Error(`远端交易失败 [errorCode=${errorCode}] ${parsed.header?.errorMsg || parsed.body?.errorMsg || ''} | ${detail}`);
1776
1798
  }
1777
1799
  return parsed.body;
1778
- }, { urlPath, requestBody, transCode, timeoutMs: FINMALL_POST_TIMEOUT_MS }),
1779
- FINMALL_POST_TIMEOUT_MS + 2000,
1780
- `FinMall 请求 ${transCode}`
1800
+ }, { urlPath, requestBody, transCode, timeoutMs: PLATFORM_POST_TIMEOUT_MS }),
1801
+ PLATFORM_POST_TIMEOUT_MS + 2000,
1802
+ `远端请求 ${transCode}`
1781
1803
  );
1782
1804
  }
1783
1805
 
@@ -1794,7 +1816,7 @@ function normalizeCard(raw) {
1794
1816
  }
1795
1817
 
1796
1818
  function filestoreToUrl(value) {
1797
- return finmallConfig.filestoreUrl(value);
1819
+ return platformConfig.filestoreUrl(value);
1798
1820
  }
1799
1821
 
1800
1822
  async function syncCard(page, card, { outRoot, appId, workspaceId, summary, schemaHashes, importMeta }) {
@@ -1813,7 +1835,7 @@ async function syncCard(page, card, { outRoot, appId, workspaceId, summary, sche
1813
1835
  }
1814
1836
  const coverPath = await saveCover(page, card, cardDir);
1815
1837
  // 注意:schema 内部的 pageId 一律以「远端拉取」的为准,原样落盘,不做覆盖。
1816
- // 远端返回的 pageId 是 FinMall 编辑器维护的权威页面/模板 ID,落盘后随 schema 一并保留,
1838
+ // 远端返回的 pageId 是远端编辑器维护的权威页面/模板 ID,落盘后随 schema 一并保留,
1817
1839
  // 供保存(IDC10010)/预览(IDC10015/10020/10021)链路按此主键打到线上。
1818
1840
  // 重复 schema 检测:若不同 code 抓到了字节完全相同的 schema,说明仍串卡,
1819
1841
  // 记录到 duplicates 并把整体结果标为失败,便于重抓时立即发现而非事后排查。
@@ -2008,7 +2030,7 @@ async function writeImportSidecar(cardDir, { appId, workspaceId, card, files, im
2008
2030
  const mod = importMeta || (await import(pathToFileURL(path.resolve(workspace, 'server', 'core', 'cardImportMeta.js')).href));
2009
2031
  const raw = card.raw || {};
2010
2032
  const meta = mod.buildImportMeta({
2011
- baseUrl: finmallConfig.baseUrl,
2033
+ baseUrl: platformConfig.baseUrl,
2012
2034
  appId,
2013
2035
  workspaceId,
2014
2036
  cardId: card.cardId,