openyida 2026.7.18 → 2026.7.21

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 (200) hide show
  1. package/README.md +37 -2
  2. package/bin/yida.js +113 -18
  3. package/docs/capabilities.md +202 -0
  4. package/lib/agent-center/agent-center.js +2 -3
  5. package/lib/app/app-list.js +3 -4
  6. package/lib/app/build-page.js +13 -6
  7. package/lib/app/check-page.js +16 -6
  8. package/lib/app/compile.js +17 -7
  9. package/lib/app/create-app.js +14 -2
  10. package/lib/app/create-form/api-path.js +25 -0
  11. package/lib/app/create-form/args.js +13 -0
  12. package/lib/app/create-form/commands.js +4 -0
  13. package/lib/app/create-form/definition-reader.js +23 -23
  14. package/lib/app/create-form/field-normalizers.js +26 -7
  15. package/lib/app/create-form/schema-patch.js +4 -1
  16. package/lib/app/create-form.js +155 -68
  17. package/lib/app/create-page.js +47 -10
  18. package/lib/app/generate-page.js +20 -1
  19. package/lib/app/get-form-config.js +2 -1
  20. package/lib/app/get-schema.js +67 -15
  21. package/lib/app/import-app.js +94 -42
  22. package/lib/app/list-forms.js +4 -8
  23. package/lib/app/page-ir.js +140 -0
  24. package/lib/app/publish.js +289 -556
  25. package/lib/app/schema-field-resolution.js +370 -0
  26. package/lib/app/services/app-reader.js +85 -0
  27. package/lib/app/services/app-service.js +235 -0
  28. package/lib/app/services/canvas-page-compiler.js +91 -0
  29. package/lib/app/services/canvas-page-schema-builder.js +149 -0
  30. package/lib/app/services/field-bindings.js +68 -0
  31. package/lib/app/services/form-compiler.js +1922 -0
  32. package/lib/app/services/form-mode-service.js +182 -0
  33. package/lib/app/services/form-schema-patcher.js +333 -0
  34. package/lib/app/services/form-schema-reader.js +59 -0
  35. package/lib/app/services/form-service.js +379 -0
  36. package/lib/app/services/form-validation.js +562 -0
  37. package/lib/app/services/native-page-compiler.js +92 -0
  38. package/lib/app/services/native-page-schema-builder.js +428 -0
  39. package/lib/app/services/page-resource-service.js +185 -0
  40. package/lib/app/update-app.js +22 -9
  41. package/lib/app/update-form-config.js +4 -2
  42. package/lib/asset/asset-cmd.js +19 -6
  43. package/lib/auth/oauth-loopback.js +376 -15
  44. package/lib/auth/token-auth.js +34 -3
  45. package/lib/auth/token-store.js +22 -4
  46. package/lib/basic-info/basic-info.js +2 -1
  47. package/lib/bridge/bridge.js +43 -18
  48. package/lib/core/agent-capabilities.js +5 -3
  49. package/lib/core/command-errors.js +38 -0
  50. package/lib/core/command-manifest.js +59 -1
  51. package/lib/core/copy.js +12 -0
  52. package/lib/core/doctor.js +15 -4
  53. package/lib/core/env-cmd.js +1 -1
  54. package/lib/core/env.js +7 -6
  55. package/lib/core/i18n.js +66 -8
  56. package/lib/core/legacy-schema-guard.js +408 -0
  57. package/lib/core/locales/en.js +38 -46
  58. package/lib/core/locales/zh.js +30 -46
  59. package/lib/core/sample.js +12 -2
  60. package/lib/core/utils.js +334 -66
  61. package/lib/core/yida-client.js +108 -5
  62. package/lib/corp-efficiency/corp-efficiency.js +2 -2
  63. package/lib/corp-manager/corp-manager.js +3 -3
  64. package/lib/formula/evaluate.js +11 -68
  65. package/lib/formula/field-refs.js +83 -0
  66. package/lib/i18n-management/i18n-management.js +2 -3
  67. package/lib/process/configure-process.js +44 -1992
  68. package/lib/process/create-process.js +54 -87
  69. package/lib/process/preview-process.js +20 -8
  70. package/lib/process/services/process-compiler.js +2056 -0
  71. package/lib/process/services/process-reader.js +232 -0
  72. package/lib/process/services/process-resource-service.js +571 -0
  73. package/lib/process/services/process-service.js +122 -0
  74. package/lib/process/services/process-stage-checkpoint.js +246 -0
  75. package/lib/report/append.js +7 -26
  76. package/lib/report/http.js +31 -5
  77. package/lib/report/index.js +10 -2
  78. package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
  79. package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
  80. package/lib/samples/yida-canvas-custom-page/dashboard-starter.canvas.jsx +5 -2
  81. package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
  82. package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
  83. package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
  84. package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
  85. package/lib/samples/yida-custom-page/design-tokens.js +2 -2
  86. package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
  87. package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
  88. package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
  89. package/lib/schema/adapters/app-adapter.js +210 -0
  90. package/lib/schema/adapters/form-adapter.js +1362 -0
  91. package/lib/schema/adapters/keys.js +19 -0
  92. package/lib/schema/adapters/page-adapter.js +566 -0
  93. package/lib/schema/adapters/process-adapter.js +519 -0
  94. package/lib/schema/applier.js +1878 -0
  95. package/lib/schema/apply-store.js +1083 -0
  96. package/lib/schema/command.js +645 -0
  97. package/lib/schema/dependency-graph.js +83 -0
  98. package/lib/schema/errors.js +481 -0
  99. package/lib/schema/hash.js +9 -0
  100. package/lib/schema/manifest-limits.js +177 -0
  101. package/lib/schema/manifest-loader.js +309 -0
  102. package/lib/schema/manifest-schema-v1.json +193 -0
  103. package/lib/schema/normalize-manifest.js +281 -0
  104. package/lib/schema/page-canvas-foundation.js +447 -0
  105. package/lib/schema/page-data-source-builder.js +561 -0
  106. package/lib/schema/page-foundation.js +556 -0
  107. package/lib/schema/page-source-loader.js +314 -0
  108. package/lib/schema/planner.js +691 -0
  109. package/lib/schema/remote-dispatch-boundary.js +39 -0
  110. package/lib/schema/remote-missing.js +21 -0
  111. package/lib/schema/remote-reader.js +189 -0
  112. package/lib/schema/resource-registry.js +157 -0
  113. package/lib/schema/server-revision.js +71 -0
  114. package/lib/schema/sort.js +28 -0
  115. package/lib/schema/state-store.js +589 -0
  116. package/package.json +10 -8
  117. package/scripts/check-release-risks.js +254 -0
  118. package/scripts/generate-command-docs.js +6 -1
  119. package/scripts/i18n-baseline.json +11 -11
  120. package/scripts/postinstall.js +8 -12
  121. package/scripts/validate-ci.sh +15 -11
  122. package/scripts/validate-i18n.js +30 -9
  123. package/scripts/validate-package-size.js +5 -2
  124. package/scripts/validate-skills.js +4 -0
  125. package/scripts/validate-structure.js +15 -0
  126. package/yida-skills/SKILL.md +117 -30
  127. package/yida-skills/references/schema-as-code-phase1.md +99 -0
  128. package/yida-skills/references/setup-and-env.md +59 -59
  129. package/yida-skills/references/task-retrospective.md +6 -6
  130. package/yida-skills/skills/yida-app/SKILL.md +78 -17
  131. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
  132. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
  133. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +15 -0
  134. package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
  135. package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
  136. package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
  137. package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
  138. package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
  139. package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
  140. package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
  141. package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
  142. package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
  143. package/yida-skills/skills/yida-integration/SKILL.md +7 -1
  144. package/yida-skills/skills/yida-login/SKILL.md +50 -99
  145. package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
  146. package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
  147. package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
  148. package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
  149. package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
  150. package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
  151. package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
  152. package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
  153. package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
  154. package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
  155. package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
  156. package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
  157. package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
  158. package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
  159. package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
  160. package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
  161. package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
  162. package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
  163. package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
  164. package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
  165. package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
  166. package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
  167. package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
  168. package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
  169. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
  170. package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
  171. package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
  172. package/yida-skills/skills/yida-report/SKILL.md +9 -235
  173. package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
  174. package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
  175. package/yida-skills/skills-index.json +238 -54
  176. package/lib/core/locales/ar.js +0 -834
  177. package/lib/core/locales/de.js +0 -834
  178. package/lib/core/locales/es.js +0 -834
  179. package/lib/core/locales/fr.js +0 -834
  180. package/lib/core/locales/hi.js +0 -834
  181. package/lib/core/locales/ja.js +0 -1227
  182. package/lib/core/locales/ko.js +0 -836
  183. package/lib/core/locales/pt.js +0 -834
  184. package/lib/core/locales/vi.js +0 -834
  185. package/lib/core/locales/zh-HK.js +0 -1263
  186. package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
  187. package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
  188. package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
  189. package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
  190. package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
  191. package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
  192. package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
  193. package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
  194. package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
  195. package/yida-skills/skills/large-file-write/SKILL.md +0 -93
  196. package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
  197. package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
  198. package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
  199. package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
  200. package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
@@ -45,10 +45,20 @@ const SAMPLES = {
45
45
  'design-tokens': 'design-tokens.js',
46
46
  },
47
47
  'yida-canvas-custom-page': {
48
- 'data-management': 'data-management.canvas.jsx',
48
+ 'data-management': 'data-management.canvas.jsx',
49
49
  'dashboard-starter': 'dashboard-starter.canvas.jsx',
50
50
  'native-components-smoke': 'native-components-smoke.canvas.jsx',
51
51
  'portal-native-components': 'portal-native-components.canvas.jsx',
52
+ 'business-list': 'business-list.canvas.jsx',
53
+ 'dashboard-overview': 'dashboard-overview.canvas.jsx',
54
+ 'data-screen': 'data-screen.canvas.jsx',
55
+ 'detail-profile': 'detail-profile.canvas.jsx',
56
+ 'official-homepage': 'official-homepage.canvas.jsx',
57
+ 'portal-shell-home': 'portal-shell-home.canvas.jsx',
58
+ 'product-homepage': 'product-homepage.canvas.jsx',
59
+ 'split-pane-detail': 'split-pane-detail.canvas.jsx',
60
+ 'todo-mvc': 'todo-mvc.canvas.jsx',
61
+ 'workbench-home': 'workbench-home.canvas.jsx',
52
62
  },
53
63
  'yida-create-app': {
54
64
  'ipd-app-template': 'ipd-app-template.js',
@@ -161,7 +171,7 @@ function applyTemplateVariables(content, variables) {
161
171
  */
162
172
  async function run(args) {
163
173
  // --list 模式
164
- if (args.includes('--list') || args.length === 0) {
174
+ if (args.includes('--list') || args.includes('--help') || args.includes('-h') || args.length === 0) {
165
175
  printSampleList();
166
176
  return;
167
177
  }
package/lib/core/utils.js CHANGED
@@ -3,10 +3,14 @@
3
3
  *
4
4
  * 导出函数:
5
5
  * findProjectRoot() - 查找项目根目录(兼容悟空环境)
6
- * loadAuthData() - 读取 token 登录态缓存
6
+ * extractInfoFromCookies() - Cookie 列表中提取 csrf_token / corp_id / user_id
7
+ * loadCookieData() - 已废弃:默认登录态不再读取 Cookie 缓存
8
+ * getAuthStatus() - 读取当前鉴权模式的安全状态摘要
7
9
  * triggerLogin() - 触发登录
8
- * resolveBaseUrl() - authData 中解析 base_url
10
+ * refreshCsrfToken() - 刷新 csrf_token
11
+ * resolveBaseUrl() - 从 cookieData 中解析 base_url
9
12
  * isLoginExpired() - 检测响应体是否表示登录过期
13
+ * isCsrfTokenExpired() - 检测响应体是否表示 csrf_token 过期
10
14
  */
11
15
 
12
16
  'use strict';
@@ -150,9 +154,6 @@ function detectActiveTool() {
150
154
  }
151
155
 
152
156
  function hasDesktopEnvironment(env = process.env, platform = process.platform) {
153
- if (env.OPENYIDA_FORCE_TERMINAL_QR === '1') {
154
- return false;
155
- }
156
157
  if (env.OPENYIDA_ASSUME_DESKTOP === '1') {
157
158
  return true;
158
159
  }
@@ -256,15 +257,16 @@ function getNodeExecutable() {
256
257
  return 'node';
257
258
  }
258
259
 
259
- function isInjectedAuthMode() {
260
- return false;
260
+ function isInjectedAuthMode(env = process.env) {
261
+ const authEnabled = String(env.YIDA_AUTH_ENABLED || '').trim().toLowerCase();
262
+ return ['1', 'true', 'yes', 'on'].includes(authEnabled);
261
263
  }
262
264
 
263
- function isEnvAuthMode() {
264
- return false;
265
+ function isEnvAuthMode(env = process.env) {
266
+ return isInjectedAuthMode(env);
265
267
  }
266
268
 
267
- function isTokenAuthMode() {
269
+ function isTokenAuthMode(_env = process.env) {
268
270
  return true;
269
271
  }
270
272
 
@@ -329,9 +331,6 @@ function resolveTokenBaseUrl(root, fallbackBaseUrl, session) {
329
331
  }
330
332
 
331
333
  function loadTokenAuthData(projectRoot, defaultBaseUrl) {
332
- if (!isTokenAuthMode()) {
333
- return null;
334
- }
335
334
  try {
336
335
  const { loadTokenSession } = require('../auth/token-store');
337
336
  const session = loadTokenSession({ projectRoot });
@@ -342,7 +341,7 @@ function loadTokenAuthData(projectRoot, defaultBaseUrl) {
342
341
  corp_id: session.corp_id,
343
342
  user_id: session.user_id,
344
343
  base_url: resolveTokenBaseUrl(projectRoot, defaultBaseUrl, session),
345
- auth_source: 'token',
344
+ auth_source: session.auth_source === 'env' ? 'env' : 'token',
346
345
  auth_mode: 'token',
347
346
  };
348
347
  } catch {
@@ -364,10 +363,115 @@ async function resolveBearerAuthHeaders(options = {}) {
364
363
  };
365
364
  }
366
365
 
366
+ // ── Cookie 解析 ───────────────────────────────────────
367
+
368
+ const CSRF_COOKIE_NAMES = new Set([
369
+ 'tianshu_csrf_token',
370
+ 'china_csrf_token',
371
+ 'csrf_token',
372
+ '_csrf_token',
373
+ ]);
374
+
375
+ /**
376
+ * 从 Cookie 列表中提取 csrf_token、corp_id、user_id。
377
+ *
378
+ * 国内宜搭(aliwork.com):corpId/userId 合并写在 `tianshu_corp_user` 里,
379
+ * 形如 `${corpId}_${userId}`,按最后一个下划线切分。
380
+ *
381
+ * 海外 YiDA(yidaapps.com):不写 `tianshu_corp_user`,而是单独写 `corp_id` cookie
382
+ * 存放 corpId 明文;userId 加密在 `pub_uid` 里客户端无法解密,留 null 接受。
383
+ *
384
+ * @param {Array} cookies
385
+ * @returns {{ csrfToken: string|null, corpId: string|null, userId: string|null }}
386
+ */
387
+ function extractInfoFromCookies(cookies) {
388
+ let csrfToken = null;
389
+ let corpId = null;
390
+ let userId = null;
391
+ const cookieList = Array.isArray(cookies) ? cookies : [];
392
+
393
+ for (const cookie of cookieList) {
394
+ if (cookie && CSRF_COOKIE_NAMES.has(cookie.name) && cookie.value && !csrfToken) {
395
+ csrfToken = cookie.value;
396
+ } else if (cookie && cookie.name === 'tianshu_corp_user') {
397
+ const lastUnderscore = cookie.value.lastIndexOf('_');
398
+ if (lastUnderscore > 0) {
399
+ corpId = cookie.value.slice(0, lastUnderscore);
400
+ userId = cookie.value.slice(lastUnderscore + 1);
401
+ }
402
+ }
403
+ }
404
+
405
+ if (!corpId) {
406
+ const corpCookie = cookieList.find((c) => c && ['corp_id', 'corpId'].includes(c.name) && c.value);
407
+ if (corpCookie) {
408
+ corpId = corpCookie.value;
409
+ }
410
+ }
411
+
412
+ if (!userId) {
413
+ const userCookie = cookieList.find((c) => c && ['user_id', 'userId', 'staffId'].includes(c.name) && c.value);
414
+ if (userCookie) {
415
+ userId = userCookie.value;
416
+ }
417
+ }
418
+
419
+ return { csrfToken, corpId, userId };
420
+ }
421
+
422
+ function parseCookieHeader(rawCookieHeader, options = {}) {
423
+ const raw = String(rawCookieHeader || '').trim();
424
+ if (!raw) {
425
+ return [];
426
+ }
427
+ let domain = '';
428
+ if (options.baseUrl) {
429
+ try {
430
+ domain = new URL(options.baseUrl).hostname;
431
+ } catch {
432
+ domain = '';
433
+ }
434
+ }
435
+ return raw
436
+ .split(';')
437
+ .map((part) => {
438
+ const trimmed = part.trim();
439
+ const equalsIndex = trimmed.indexOf('=');
440
+ if (equalsIndex <= 0) {
441
+ return null;
442
+ }
443
+ const name = trimmed.slice(0, equalsIndex).trim();
444
+ const value = trimmed.slice(equalsIndex + 1).trim();
445
+ if (!name) {
446
+ return null;
447
+ }
448
+ const cookie = { name, value };
449
+ if (domain) {
450
+ cookie.domain = domain;
451
+ }
452
+ return cookie;
453
+ })
454
+ .filter(Boolean);
455
+ }
456
+
367
457
  // ── 登录态缓存读取 ────────────────────────────────────
368
458
 
369
459
  /**
370
- * 读取 token 登录态缓存。
460
+ * 已废弃:默认登录态不再读取 Cookie。
461
+ * 保留函数名用于旧测试/显式 mock 兼容;实际登录态只从 token session 读取。
462
+ * @param {string} [projectRoot]
463
+ * @param {string} [defaultBaseUrl]
464
+ * @returns {object|null}
465
+ */
466
+ function loadCookieData(projectRoot, defaultBaseUrl) {
467
+ void projectRoot;
468
+ void defaultBaseUrl;
469
+ return null;
470
+ }
471
+
472
+ /**
473
+ * 读取当前默认登录态。
474
+ * 默认使用 OAuth token session;YIDA_AUTH_ENABLED=true 时仅使用宿主注入 token。
371
475
  * @param {string} [projectRoot]
372
476
  * @param {string} [defaultBaseUrl]
373
477
  * @returns {object|null}
@@ -378,15 +482,41 @@ function loadAuthData(projectRoot, defaultBaseUrl) {
378
482
  return loadTokenAuthData(root, fallbackBaseUrl);
379
483
  }
380
484
 
485
+ function getAuthStatus(options = {}) {
486
+ const { tokenStatus } = require('../auth/token-auth');
487
+ return tokenStatus(options);
488
+ }
489
+
381
490
  // ── 登录触发 ──────────────────────────────────────────
382
491
 
383
492
  /**
384
- * 触发登录。
493
+ * 校验默认登录态;host mode 下不会触发 OAuth,本地模式提示用户执行 openyida login。
385
494
  * @param {object} [options]
386
495
  * @param {boolean} [options.force=false] - 是否跳过本地缓存,强制重新登录
387
496
  * @returns {object} loginResult
388
497
  */
389
498
  function triggerLogin(options = {}) {
499
+ if (isEnvAuthMode()) {
500
+ const status = getAuthStatus({ projectRoot: options.projectRoot || findProjectRoot() });
501
+ if (status && status.ok) {
502
+ return loadTokenAuthData(options.projectRoot || findProjectRoot(), status.base_url || 'https://www.aliwork.com');
503
+ }
504
+ const reason = status && status.failure_reason
505
+ ? status.failure_reason
506
+ : 'env_token_missing';
507
+ const { CliError } = require('./cli-error');
508
+ throw new CliError(
509
+ `not_logged_in: host-injected token is unavailable (${reason}). Ask the host to inject an OpenYida token.`,
510
+ {
511
+ code: 'INJECTED_AUTH_REQUIRED',
512
+ details: {
513
+ authMode: 'token',
514
+ failure_reason: reason,
515
+ },
516
+ }
517
+ );
518
+ }
519
+
390
520
  const tokenAuthData = loadTokenAuthData(options.projectRoot || findProjectRoot(), 'https://www.aliwork.com');
391
521
  if (tokenAuthData) {
392
522
  return tokenAuthData;
@@ -403,6 +533,15 @@ function triggerLogin(options = {}) {
403
533
  );
404
534
  }
405
535
 
536
+ /**
537
+ * 已废弃:默认 token 登录态不再刷新 CSRF。
538
+ * @returns {object} loginResult
539
+ */
540
+ function refreshCsrfToken() {
541
+ warn(t('login.csrf_refresh'));
542
+ return null;
543
+ }
544
+
406
545
  // ── 响应检测 ──────────────────────────────────────────
407
546
 
408
547
  /**
@@ -412,7 +551,7 @@ function triggerLogin(options = {}) {
412
551
  */
413
552
  function isLoginExpired(responseJson) {
414
553
  if (!responseJson) {return false;}
415
- const content = responseJson && typeof responseJson.content === 'object' && !Array.isArray(responseJson.content)
554
+ const content = responseJson && responseJson.content && typeof responseJson.content === 'object' && !Array.isArray(responseJson.content)
416
555
  ? responseJson.content
417
556
  : {};
418
557
  const status = String(responseJson.status || content.status || '').toLowerCase();
@@ -446,6 +585,19 @@ function isLoginExpired(responseJson) {
446
585
  );
447
586
  }
448
587
 
588
+ /**
589
+ * 检测响应体是否表示 csrf_token 过期。
590
+ * @param {object} responseJson
591
+ * @returns {boolean}
592
+ */
593
+ function isCsrfTokenExpired(responseJson) {
594
+ return (
595
+ responseJson &&
596
+ responseJson.success === false &&
597
+ responseJson.errorCode === 'TIANSHU_000030'
598
+ );
599
+ }
600
+
449
601
  function isHttpRedirectStatus(statusCode) {
450
602
  return [301, 302, 303, 307, 308].includes(Number(statusCode));
451
603
  }
@@ -457,23 +609,23 @@ function isHttpAuthStatus(statusCode) {
457
609
  // ── base_url 解析 ─────────────────────────────────────
458
610
 
459
611
  /**
460
- * 从 authData 中解析 base_url,支持多环境配置优先级。
612
+ * 从 cookieData 中解析 base_url,支持多环境配置优先级。
461
613
  *
462
614
  * 优先级(高 → 低):
463
615
  * 1. OPENYIDA_ENDPOINT 环境变量
464
- * 2. authData.base_url(登录后实际跳转域名)
616
+ * 2. cookieData.base_url(登录后实际跳转域名)
465
617
  * 3. 当前激活的私有化环境配置
466
618
  * 4. 当前激活的环境配置(公有云默认)
467
619
  * 5. defaultBaseUrl 参数 / 公有云兜底
468
620
  *
469
- * @param {object} authData
621
+ * @param {object} cookieData
470
622
  * @param {string} [defaultBaseUrl]
471
623
  * @returns {string}
472
624
  */
473
- function resolveBaseUrl(authData, defaultBaseUrl) {
625
+ function resolveBaseUrl(cookieData, defaultBaseUrl) {
474
626
  const { resolveEndpoint } = require('./env-manager');
475
- const resolved = resolveEndpoint(authData, undefined);
476
- if (defaultBaseUrl && resolved === 'https://www.aliwork.com' && (!authData || !authData.base_url)) {
627
+ const resolved = resolveEndpoint(cookieData, undefined);
628
+ if (defaultBaseUrl && resolved === 'https://www.aliwork.com' && (!cookieData || !cookieData.base_url)) {
477
629
  return defaultBaseUrl.replace(/\/+$/, '');
478
630
  }
479
631
  return resolved;
@@ -491,19 +643,79 @@ function collectResponseText(res, onEnd) {
491
643
  });
492
644
  }
493
645
 
646
+ function splitRequestAuthArgs(cookiesOrOptions, maybeOptions) {
647
+ if (Array.isArray(cookiesOrOptions)) {
648
+ return {
649
+ cookies: cookiesOrOptions,
650
+ options: maybeOptions || {},
651
+ };
652
+ }
653
+ return {
654
+ cookies: [],
655
+ options: cookiesOrOptions || {},
656
+ };
657
+ }
658
+
659
+ function resolveCookieAuthCookies(cookies) {
660
+ if (Array.isArray(cookies) && cookies.length > 0) {
661
+ return cookies;
662
+ }
663
+ return [];
664
+ }
665
+
666
+ function buildCookieAuthHeaders(baseUrl, cookies, optionsOverride = {}) {
667
+ const parsedUrl = new URL(baseUrl);
668
+ const requestHost = parsedUrl.hostname;
669
+ const cookieList = resolveCookieAuthCookies(cookies);
670
+ const filteredCookies = cookieList.filter(c => {
671
+ const cookieDomain = (c.domain || '').replace(/^\./, '');
672
+ return !cookieDomain || requestHost === cookieDomain || requestHost.endsWith('.' + cookieDomain);
673
+ });
674
+ const effectiveCookies = filteredCookies.length > 0 ? filteredCookies : cookieList;
675
+ const cookieHeader = effectiveCookies.map((c) => `${c.name}=${c.value}`).join('; ');
676
+ const { csrfToken } = extractInfoFromCookies(effectiveCookies);
677
+ const headers = {};
678
+ if (cookieHeader) {
679
+ headers.Cookie = cookieHeader;
680
+ }
681
+ if (optionsOverride.csrfToken || csrfToken) {
682
+ headers.global_csrf_token = optionsOverride.csrfToken || csrfToken;
683
+ }
684
+ return headers;
685
+ }
686
+
687
+ async function resolveRequestAuthHeaders(baseUrl, cookies, optionsOverride = {}) {
688
+ if (Array.isArray(cookies) && cookies.length > 0) {
689
+ return buildCookieAuthHeaders(baseUrl, cookies, optionsOverride);
690
+ }
691
+ const auth = await resolveBearerAuthHeaders(optionsOverride);
692
+ return auth.headers;
693
+ }
694
+
695
+ function createNonJsonResponseResult(statusCode, data) {
696
+ const responseText = data === null || data === undefined ? '' : String(data);
697
+ return {
698
+ success: false,
699
+ errorMsg: `HTTP ${statusCode}: ` + t('common.response_not_json'),
700
+ __httpStatus: statusCode,
701
+ __nonJsonResponse: true,
702
+ __emptyBody: responseText.trim().length === 0,
703
+ };
704
+ }
705
+
494
706
  /**
495
707
  * 发送 HTTP POST 请求(application/x-www-form-urlencoded)
496
708
  * @param {string} baseUrl
497
709
  * @param {string} requestPath
498
710
  * @param {string} postData - querystring 格式
499
- * @param {object} [optionsOverride]
711
+ * @param {Array} cookies
500
712
  * @returns {Promise<object>}
501
713
  */
502
- async function httpPost(baseUrl, requestPath, postData, optionsOverride = {}) {
714
+ async function httpPost(baseUrl, requestPath, postData, cookiesOrOptions, maybeOptions) {
503
715
  const https = require('https');
504
716
  const http = require('http');
505
-
506
- const auth = await resolveBearerAuthHeaders(optionsOverride);
717
+ const { cookies, options: optionsOverride } = splitRequestAuthArgs(cookiesOrOptions, maybeOptions);
718
+ const authHeaders = await resolveRequestAuthHeaders(baseUrl, cookies, optionsOverride);
507
719
  const bodyData = postData === null || postData === undefined ? '' : String(postData);
508
720
 
509
721
  return new Promise((resolve, reject) => {
@@ -523,9 +735,9 @@ async function httpPost(baseUrl, requestPath, postData, optionsOverride = {}) {
523
735
  Origin: baseUrl,
524
736
  Referer: optionsOverride.referer || baseUrl + '/',
525
737
  'x-requested-with': 'XMLHttpRequest',
526
- ...auth.headers,
738
+ ...authHeaders,
527
739
  },
528
- timeout: 30000,
740
+ timeout: optionsOverride.timeout || 30000,
529
741
  };
530
742
 
531
743
  const req = requestModule.request(options, (res) => {
@@ -547,10 +759,16 @@ async function httpPost(baseUrl, requestPath, postData, optionsOverride = {}) {
547
759
  resolve({ __needLogin: true });
548
760
  return;
549
761
  }
762
+ if (isCsrfTokenExpired(parsed)) {
763
+ resolve({ __csrfExpired: true });
764
+ return;
765
+ }
550
766
  resolve(parsed);
551
767
  } catch {
552
- warn(t('common.http_response', data.substring(0, 500)));
553
- resolve({ success: false, errorMsg: `HTTP ${res.statusCode}: ` + t('common.response_not_json') });
768
+ if (!optionsOverride.silentStatus) {
769
+ warn(t('common.http_response', data.substring(0, 500)));
770
+ }
771
+ resolve(createNonJsonResponseResult(res.statusCode, data));
554
772
  }
555
773
  });
556
774
  });
@@ -568,11 +786,11 @@ async function httpPost(baseUrl, requestPath, postData, optionsOverride = {}) {
568
786
  });
569
787
  }
570
788
 
571
- async function httpPostJson(baseUrl, requestPath, payload, optionsOverride = {}) {
789
+ async function httpPostJson(baseUrl, requestPath, payload, cookiesOrOptions, maybeOptions) {
572
790
  const https = require('https');
573
791
  const http = require('http');
574
-
575
- const auth = await resolveBearerAuthHeaders(optionsOverride);
792
+ const { cookies, options: optionsOverride } = splitRequestAuthArgs(cookiesOrOptions, maybeOptions);
793
+ const authHeaders = await resolveRequestAuthHeaders(baseUrl, cookies, optionsOverride);
576
794
 
577
795
  return new Promise((resolve, reject) => {
578
796
  const parsedUrl = new URL(baseUrl);
@@ -592,7 +810,7 @@ async function httpPostJson(baseUrl, requestPath, payload, optionsOverride = {})
592
810
  Origin: baseUrl,
593
811
  Referer: optionsOverride.referer || baseUrl + '/',
594
812
  'x-requested-with': 'XMLHttpRequest',
595
- ...auth.headers,
813
+ ...authHeaders,
596
814
  },
597
815
  timeout: optionsOverride.timeout || 30000,
598
816
  };
@@ -616,10 +834,16 @@ async function httpPostJson(baseUrl, requestPath, payload, optionsOverride = {})
616
834
  resolve({ __needLogin: true });
617
835
  return;
618
836
  }
837
+ if (isCsrfTokenExpired(parsed)) {
838
+ resolve({ __csrfExpired: true });
839
+ return;
840
+ }
619
841
  resolve(parsed);
620
842
  } catch {
621
- warn(t('common.http_response', data.substring(0, 500)));
622
- resolve({ success: false, errorMsg: `HTTP ${res.statusCode}: ` + t('common.response_not_json') });
843
+ if (!optionsOverride.silentStatus) {
844
+ warn(t('common.http_response', data.substring(0, 500)));
845
+ }
846
+ resolve(createNonJsonResponseResult(res.statusCode, data));
623
847
  }
624
848
  });
625
849
  });
@@ -641,15 +865,15 @@ async function httpPostJson(baseUrl, requestPath, payload, optionsOverride = {})
641
865
  * @param {string} baseUrl
642
866
  * @param {string} requestPath
643
867
  * @param {object} queryParams
644
- * @param {object} [optionsOverride]
868
+ * @param {Array} cookies
645
869
  * @returns {Promise<object>}
646
870
  */
647
- async function httpGet(baseUrl, requestPath, queryParams, optionsOverride = {}) {
871
+ async function httpGet(baseUrl, requestPath, queryParams, cookiesOrOptions, maybeOptions) {
648
872
  const https = require('https');
649
873
  const http = require('http');
650
874
  const querystring = require('querystring');
651
-
652
- const auth = await resolveBearerAuthHeaders(optionsOverride);
875
+ const { cookies, options: optionsOverride } = splitRequestAuthArgs(cookiesOrOptions, maybeOptions);
876
+ const authHeaders = await resolveRequestAuthHeaders(baseUrl, cookies, optionsOverride);
653
877
 
654
878
  return new Promise((resolve, reject) => {
655
879
  const parsedUrl = new URL(baseUrl);
@@ -667,9 +891,9 @@ async function httpGet(baseUrl, requestPath, queryParams, optionsOverride = {})
667
891
  Origin: baseUrl,
668
892
  Referer: baseUrl + '/',
669
893
  'x-requested-with': 'XMLHttpRequest',
670
- ...auth.headers,
894
+ ...authHeaders,
671
895
  },
672
- timeout: 30000,
896
+ timeout: optionsOverride.timeout || 30000,
673
897
  };
674
898
 
675
899
  const req = requestModule.request(options, (res) => {
@@ -691,10 +915,16 @@ async function httpGet(baseUrl, requestPath, queryParams, optionsOverride = {})
691
915
  resolve({ __needLogin: true });
692
916
  return;
693
917
  }
918
+ if (isCsrfTokenExpired(parsed)) {
919
+ resolve({ __csrfExpired: true });
920
+ return;
921
+ }
694
922
  resolve(parsed);
695
923
  } catch {
696
- warn(t('common.http_response', data.substring(0, 500)));
697
- resolve({ success: false, errorMsg: `HTTP ${res.statusCode}: ` + t('common.response_not_json') });
924
+ if (!optionsOverride.silentStatus) {
925
+ warn(t('common.http_response', data.substring(0, 500)));
926
+ }
927
+ resolve(createNonJsonResponseResult(res.statusCode, data));
698
928
  }
699
929
  });
700
930
  });
@@ -714,31 +944,16 @@ async function httpGet(baseUrl, requestPath, queryParams, optionsOverride = {})
714
944
  /**
715
945
  * 带自动重登录的请求封装。
716
946
  * @param {Function} requestFn - 接受 authRef 返回 Promise 的工厂函数
717
- * @param {object} authRef - { baseUrl, authData }
947
+ * @param {object} authRef - { csrfToken, cookies, baseUrl, cookieData }
718
948
  * @returns {Promise<object>}
719
949
  */
720
- function tokenAuthRequiredResult() {
721
- return {
722
- success: false,
723
- __needLogin: true,
724
- errorCode: 'TOKEN_AUTH_REQUIRED',
725
- errorMsg: 'not_logged_in: token auth is unavailable. Run openyida login first.',
726
- };
727
- }
728
-
729
- function tokenRefreshFailedResult(error) {
730
- return {
731
- success: false,
732
- errorCode: 'TOKEN_REFRESH_FAILED',
733
- errorMsg: error && error.message
734
- ? error.message
735
- : 'token_refresh_failed: failed to refresh access token. Please retry later.',
736
- };
737
- }
738
-
739
950
  async function requestWithAutoLogin(requestFn, authRef) {
740
951
  const result = await requestFn(authRef);
741
952
 
953
+ if (isExplicitCookieAuthRef(authRef)) {
954
+ return result;
955
+ }
956
+
742
957
  if (result && result.__needLogin) {
743
958
  const { tokenRefresh, isRefreshAuthRequired } = require('../auth/token-auth');
744
959
  try {
@@ -755,8 +970,13 @@ async function requestWithAutoLogin(requestFn, authRef) {
755
970
  }
756
971
  return tokenRefreshFailedResult(new Error('token_refresh_failed'));
757
972
  }
758
- const refreshedTokenData = loadTokenAuthData(authRef && authRef.projectRoot, currentBaseUrl);
973
+ const refreshedTokenData = refreshResult.access_token
974
+ ? normalizeRefreshedTokenAuthData(refreshResult, currentBaseUrl)
975
+ : loadTokenAuthData(authRef && authRef.projectRoot, currentBaseUrl);
759
976
  if (refreshedTokenData) {
977
+ if (!authRef) {
978
+ authRef = {};
979
+ }
760
980
  authRef.authData = refreshedTokenData;
761
981
  authRef.baseUrl = resolveBaseUrl(refreshedTokenData);
762
982
  authRef.authMode = refreshedTokenData.auth_mode || refreshedTokenData.authMode || '';
@@ -777,14 +997,62 @@ async function requestWithAutoLogin(requestFn, authRef) {
777
997
  return result;
778
998
  }
779
999
 
1000
+ function isExplicitCookieAuthRef(authRef) {
1001
+ if (!authRef || !Array.isArray(authRef.cookies) || authRef.cookies.length === 0) {
1002
+ return false;
1003
+ }
1004
+ const authData = authRef.authData || authRef.cookieData || {};
1005
+ const authMode = authRef.authMode || authRef.auth_mode || authData.auth_mode || authData.authMode;
1006
+ const authSource = authRef.authSource || authRef.auth_source || authData.auth_source || authData.authSource;
1007
+ return authMode !== 'token' && authSource !== 'token';
1008
+ }
1009
+
1010
+ function normalizeRefreshedTokenAuthData(refreshResult, fallbackBaseUrl) {
1011
+ if (!refreshResult || !refreshResult.access_token) {
1012
+ return null;
1013
+ }
1014
+ return {
1015
+ corp_id: refreshResult.corp_id,
1016
+ user_id: refreshResult.user_id,
1017
+ base_url: resolveBaseUrl(refreshResult, fallbackBaseUrl),
1018
+ auth_source: refreshResult.auth_source === 'env' ? 'env' : 'token',
1019
+ auth_mode: 'token',
1020
+ };
1021
+ }
1022
+
1023
+ function tokenAuthRequiredResult() {
1024
+ return {
1025
+ success: false,
1026
+ __needLogin: true,
1027
+ errorCode: 'TOKEN_AUTH_REQUIRED',
1028
+ errorMsg: 'not_logged_in: token auth is unavailable. Run openyida login first.',
1029
+ };
1030
+ }
1031
+
1032
+ function tokenRefreshFailedResult(error) {
1033
+ return {
1034
+ success: false,
1035
+ errorCode: 'TOKEN_REFRESH_FAILED',
1036
+ errorMsg: error && error.message
1037
+ ? error.message
1038
+ : 'token_refresh_failed: failed to refresh access token. Please retry later.',
1039
+ };
1040
+ }
1041
+
780
1042
  module.exports = {
781
1043
  detectActiveTool,
782
1044
  hasDesktopEnvironment,
783
1045
  findProjectRoot,
1046
+ extractInfoFromCookies,
1047
+ parseCookieHeader,
1048
+ getAuthStatus,
784
1049
  loadAuthData,
1050
+ loadCookieData,
785
1051
  triggerLogin,
1052
+ refreshCsrfToken,
786
1053
  resolveBaseUrl,
787
1054
  isLoginExpired,
1055
+ isCsrfTokenExpired,
788
1056
  httpPost,
789
1057
  httpPostJson,
790
1058
  httpGet,