openyida 2026.7.14-2 → 2026.7.14

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 (95) hide show
  1. package/README.md +22 -15
  2. package/bin/yida.js +220 -72
  3. package/lib/agent-center/api.js +2 -2
  4. package/lib/aggregate-table/aggregate-table.js +2 -2
  5. package/lib/ai/ai.js +12 -7
  6. package/lib/app/canvas-compile.js +2 -2
  7. package/lib/app/create-app.js +19 -5
  8. package/lib/app/create-form.js +76 -54
  9. package/lib/app/create-page.js +19 -4
  10. package/lib/app/er.js +8 -11
  11. package/lib/app/export-app.js +2 -2
  12. package/lib/app/externalize-form.js +10 -12
  13. package/lib/app/get-schema.js +10 -12
  14. package/lib/app/import-app.js +22 -6
  15. package/lib/app/list-forms.js +19 -3
  16. package/lib/app/nav-group.js +19 -6
  17. package/lib/app/publish.js +198 -60
  18. package/lib/app/update-app.js +18 -4
  19. package/lib/app/update-form-config.js +113 -20
  20. package/lib/app-permission/app-permission.js +2 -2
  21. package/lib/auth/auth.js +313 -0
  22. package/lib/auth/cdp-browser-login.js +390 -0
  23. package/lib/auth/codex-login.js +71 -0
  24. package/lib/auth/login.js +550 -0
  25. package/lib/auth/org.js +322 -169
  26. package/lib/auth/qr-login.js +1575 -0
  27. package/lib/basic-info/basic-info.js +12 -11
  28. package/lib/bridge/bridge.js +27 -29
  29. package/lib/connector/api.js +4 -4
  30. package/lib/core/agent-capabilities.js +8 -92
  31. package/lib/core/batch.js +8 -8
  32. package/lib/core/check-data.js +2 -2
  33. package/lib/core/command-manifest.js +3 -3
  34. package/lib/core/doctor.js +22 -19
  35. package/lib/core/env-cmd.js +3 -1
  36. package/lib/core/env-manager.js +121 -8
  37. package/lib/core/env.js +72 -30
  38. package/lib/core/locales/ar.js +5 -5
  39. package/lib/core/locales/de.js +5 -5
  40. package/lib/core/locales/en.js +26 -26
  41. package/lib/core/locales/es.js +5 -5
  42. package/lib/core/locales/fr.js +5 -5
  43. package/lib/core/locales/hi.js +5 -5
  44. package/lib/core/locales/ja.js +14 -14
  45. package/lib/core/locales/ko.js +5 -5
  46. package/lib/core/locales/pt.js +5 -5
  47. package/lib/core/locales/vi.js +5 -5
  48. package/lib/core/locales/zh-HK.js +15 -15
  49. package/lib/core/locales/zh.js +26 -26
  50. package/lib/core/query-data.js +2 -2
  51. package/lib/core/task-center.js +2 -2
  52. package/lib/core/utils.js +400 -156
  53. package/lib/core/yida-client.js +22 -57
  54. package/lib/corp-efficiency/corp-efficiency.js +3 -3
  55. package/lib/corp-manager/api.js +2 -2
  56. package/lib/db/db-seq-fix.js +25 -20
  57. package/lib/flash-note/flash-to-prd.js +2 -2
  58. package/lib/i18n-management/i18n-management.js +2 -2
  59. package/lib/integration/integration-check.js +7 -10
  60. package/lib/integration/integration-create.js +8 -11
  61. package/lib/integration/integration-list.js +8 -11
  62. package/lib/mcp/server.js +54 -5
  63. package/lib/page-config/get-page-config.js +2 -2
  64. package/lib/page-config/save-share-config.js +2 -2
  65. package/lib/page-config/verify-short-url.js +2 -2
  66. package/lib/permission/get-permission.js +2 -2
  67. package/lib/permission/save-permission.js +2 -2
  68. package/lib/process/ai-form-setting.js +2 -2
  69. package/lib/process/create-process.js +2 -2
  70. package/lib/process/preview-process.js +2 -2
  71. package/lib/report/append.js +3 -3
  72. package/lib/report/index.js +3 -3
  73. package/package.json +1 -1
  74. package/scripts/check-syntax.js +0 -6
  75. package/scripts/e2e-real/full-runner.js +10 -2
  76. package/scripts/e2e-real/runner.js +31 -0
  77. package/scripts/eval/screenshot.js +69 -27
  78. package/scripts/nightly-smoke.js +34 -16
  79. package/scripts/postinstall.js +2 -6
  80. package/yida-skills/SKILL.md +6 -10
  81. package/yida-skills/references/development-rules.md +2 -2
  82. package/yida-skills/references/setup-and-env.md +9 -10
  83. package/yida-skills/skills/sls-log-workbench/SKILL.md +2 -2
  84. package/yida-skills/skills/sls-log-workbench/sls-query.js +54 -27
  85. package/yida-skills/skills/yida-flash-note-to-prd/SKILL.md +1 -1
  86. package/yida-skills/skills/yida-integration/SKILL.md +3 -3
  87. package/yida-skills/skills/yida-login/SKILL.md +74 -56
  88. package/yida-skills/skills/yida-logout/SKILL.md +12 -13
  89. package/yida-skills/skills/yida-process-rule/SKILL.md +1 -1
  90. package/yida-skills/skills/yida-publish-page/SKILL.md +1 -1
  91. package/yida-skills/skills/yida-report/references/report-api-guide.md +20 -4
  92. package/yida-skills/skills-index.json +4 -4
  93. package/lib/auth/oauth-loopback.js +0 -156
  94. package/lib/auth/token-auth.js +0 -346
  95. package/lib/auth/token-store.js +0 -174
package/README.md CHANGED
@@ -55,10 +55,10 @@ If Codex is already installed, OpenYida also imports a local Codex plugin during
55
55
  Run this from the AI coding workspace where you want OpenYida to operate:
56
56
 
57
57
  ```bash
58
- openyida agent-capabilities --summary-json
58
+ openyida agent-capabilities --json
59
59
  ```
60
60
 
61
- OpenYida returns a compact machine-readable summary with the version, login state, workspace/cache paths, and command manifest digest. `openyida agent-capabilities --json` is the full diagnostic snapshot, where compact `workdir` maps to `active.projectRoot` and `workdir_exists` maps to `active.projectRootExists`. `openyida commands --json` remains available when an agent only needs the command manifest.
61
+ OpenYida detects the active agent environment, workspace path, login state, organization context, command manifest, and side-effect hints in one machine-readable snapshot. `openyida commands --json` remains available when an agent only needs the command manifest.
62
62
 
63
63
  ### 3. Log In
64
64
 
@@ -66,20 +66,27 @@ OpenYida returns a compact machine-readable summary with the version, login stat
66
66
  openyida login
67
67
  ```
68
68
 
69
- OpenYida login defaults to OAuth token mode. It opens the DingTalk OAuth authorization page, receives the local loopback callback, exchanges `code` / `authCode` with the Yida server, and stores `access_token` / `refresh_token` in the current project cache.
69
+ In Codex, QoderWork, Qoder, Wukong, Claude Code, MuleRun, OpenCode, Cursor, and other detected AI tools, OpenYida first tries local Chrome/Edge/Chromium CDP when no valid cached login exists. If local CDP is unavailable, it falls back to an AI-dialog QR handoff. The agent should render `qr_image_markdown` or paste `agent_response_markdown` directly in the conversation so the QR code is visible, then run `poll_command` after the user scans it with DingTalk. If image rendering is unavailable, fall back to `qr_url`. The explicit `openyida login --browser` command still prefers CDP first and uses Playwright as an optional browser fallback.
70
70
 
71
- When the user names a target Yida entry URL, pass it to the login command so OpenYida can select the matching environment and token session file. For example, Alibaba intranet Yida uses `auth-token-alibaba.json`:
71
+ When the user names a target Yida entry URL, pass it to the login command so OpenYida can select the matching environment and cookie file. For example, Alibaba intranet Yida uses `cookies-alibaba.json`:
72
72
 
73
73
  ```bash
74
74
  openyida login https://yida-group.alibaba-inc.com/
75
75
  openyida login --alibaba
76
76
  ```
77
77
 
78
- For token status checks, use:
78
+ The explicit QR polling command remains available:
79
79
 
80
80
  ```bash
81
+ openyida login --agent-qr
82
+ ```
83
+
84
+ For terminal QR login, use:
85
+
86
+ ```bash
87
+ openyida login --qr
88
+ openyida login --qr --corp-id dingxxxxxxxx
81
89
  openyida login --check-only --json
82
- openyida auth status
83
90
  ```
84
91
 
85
92
  OpenYida does not install Playwright by default.
@@ -232,7 +239,7 @@ OPENYIDA_E2E=1 OPENYIDA_E2E_FULL_STAGES=auth,app,form,process npm run test:e2e:r
232
239
  npm run test:e2e:real:skills
233
240
  ```
234
241
 
235
- The runner creates a disposable app, form, and custom page with an `OY_E2E_*` prefix, then verifies token login, app listing, schema fetch, data query, and page publish. It writes a registry to `project/.cache/e2e-real/` so created resources can be audited later. Run `openyida login` for the target environment before starting the real E2E runner.
242
+ The runner creates a disposable app, form, and custom page with an `OY_E2E_*` prefix, then verifies login, app listing, schema fetch, data query, and page publish. It writes a registry to `project/.cache/e2e-real/` so created resources can be audited later. To inject CI cookies without relying on a local login cache, pass `OPENYIDA_E2E_COOKIES_BASE64` as a base64 encoded cookie array or `{ "cookies": [...] }` object.
236
243
 
237
244
  `test:e2e:real:full` extends the smoke path into a broad deterministic feature matrix: auth/env, app update, form update and option mutation, page build/compile/generate/publish, data create/get/update/query, permission read, page config and short URL check, report create/append, dashboard skill verification, export/import, batch, task-center, formula/doctor/sample/CDN config, and local connector parsing/template generation. AI-backed commands such as `flash-to-prd` are available as the optional `ai` stage because they depend on remote model availability. Workflow mutation is available as the opt-in `process` stage; it creates and republishes a workflow on the disposable E2E form and records advanced official-node fixtures for review.
238
245
 
@@ -332,11 +339,11 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
332
339
 
333
340
  | Command | Description |
334
341
  |---------|-------------|
335
- | `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>]` | Login with OAuth token mode |
336
- | `openyida logout` | Logout / clear token |
337
- | `openyida auth <status\|login\|refresh\|logout>` | Token login state management |
338
- | `openyida org <list\|switch> [--json] [--corp-id <corpId>]` | Organization management (list / switch by OAuth re-login) |
339
- | `openyida env [--json\|setup\|list\|show\|switch\|add\|remove] [options]` | Detect AI tool environment & token login state |
342
+ | `openyida login [target-url] [--qr\|--agent-qr\|--codex\|--browser] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--corp-id <corpId>]` | Login (cache first, --browser or --agent-qr when needed) |
343
+ | `openyida logout` | Logout / switch account |
344
+ | `openyida auth <status\|login\|refresh\|logout>` | Login state management |
345
+ | `openyida org <list\|switch>` | Organization management (list / switch) |
346
+ | `openyida env [--json\|setup\|list\|show\|switch\|add\|remove] [options]` | Detect AI tool environment & login state |
340
347
 
341
348
  ### App Management
342
349
 
@@ -450,7 +457,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
450
457
  | Command | Description |
451
458
  |---------|-------------|
452
459
  | `openyida commands [--json]` | Output machine-readable command manifest |
453
- | `openyida agent-capabilities [--json] [--summary-json\|--compact]` | Output one-shot agent capability snapshot |
460
+ | `openyida agent-capabilities [--json]` | Output one-shot agent capability snapshot |
454
461
  | `openyida a2a <serve\|agent-card> [options]` | Start local read-only A2A adapter or print Agent Card |
455
462
  | `openyida bridge start [--token <pair-token>] [--port 6736] [--origin https://demo.aliwork.com] [--open\|--no-open]` | Start OpenYida local web bridge service |
456
463
  | `openyida copy [--force]` | Copy project working directory |
@@ -578,9 +585,9 @@ When adding new CLI commands, register the route in `bin/yida.js`, add it to `li
578
585
 
579
586
  ## Security and Configuration
580
587
 
581
- - OAuth token sessions are cached locally and should never be hard-coded into source files.
588
+ - Login cookies are cached locally and should never be hard-coded into source files.
582
589
  - Private deployment environments are managed through `lib/core/env-manager.js`.
583
- - Yida API requests should use the active environment base URL and Bearer token auth.
590
+ - Yida API requests should use the active environment base URL and authenticated cookies.
584
591
  - For multi-organization accounts, prefer explicit `--corp-id` values in non-interactive automation.
585
592
 
586
593
  ## Community
package/bin/yida.js CHANGED
@@ -211,38 +211,6 @@ function handleFirstRunGuide() {
211
211
  console.log('');
212
212
  }
213
213
 
214
- const TOKEN_OUTPUT_SECRET_KEYS = new Set([
215
- 'access_token',
216
- 'accessToken',
217
- 'refresh_token',
218
- 'refreshToken',
219
- 'ai_app_user_auth_token',
220
- 'aiAppUserAuthToken',
221
- 'tianshu_csrf_token',
222
- 'tianshuCsrfToken',
223
- 'csrf_token',
224
- 'csrfToken',
225
- ]);
226
-
227
- function maskSensitiveAuthOutput(value) {
228
- if (!value || typeof value !== 'object') {
229
- return value;
230
- }
231
- const { maskToken } = require('../lib/auth/token-store');
232
- if (Array.isArray(value)) {
233
- return value.map((item) => maskSensitiveAuthOutput(item));
234
- }
235
- const output = {};
236
- Object.entries(value).forEach(([key, item]) => {
237
- if (TOKEN_OUTPUT_SECRET_KEYS.has(key)) {
238
- output[key] = maskToken(item);
239
- } else {
240
- output[key] = maskSensitiveAuthOutput(item);
241
- }
242
- });
243
- return output;
244
- }
245
-
246
214
  function printLoginResult(result) {
247
215
  noteLoginCommandResult(result);
248
216
 
@@ -256,21 +224,49 @@ function printLoginResult(result) {
256
224
  return;
257
225
  }
258
226
 
259
- const tokenUsable = result.can_auto_use !== false && !!result.access_token;
227
+ if (result && (result.status === 'need_qr_scan' || result.status === 'need_corp_selection')) {
228
+ console.log(JSON.stringify(result));
229
+ return;
230
+ }
231
+
232
+ if (result && result.status === 'need_codex_browser_login') {
233
+ const handoff = {
234
+ status: result.status,
235
+ handoff_type: result.handoff_type || 'browser',
236
+ can_auto_use: false,
237
+ browser: result.browser,
238
+ login_url: result.login_url,
239
+ message: result.message,
240
+ };
241
+ [
242
+ 'agent_action',
243
+ 'browser_open_strategy',
244
+ 'browser_use_local_redirect_fallback',
245
+ 'required_agent_tool',
246
+ 'required_runtime_tool',
247
+ 'cookie_export_file',
248
+ 'cookie_file',
249
+ 'post_login_check_command',
250
+ 'fallback_command',
251
+ ].forEach((key) => {
252
+ if (result[key]) {handoff[key] = result[key];}
253
+ });
254
+ console.log(JSON.stringify(handoff));
255
+ return;
256
+ }
257
+
258
+ if (result.status && result.status !== 'ok' && !result.csrf_token) {
259
+ console.log(JSON.stringify(result));
260
+ return;
261
+ }
262
+
260
263
  const summary = {
261
- ok: tokenUsable,
262
- status: result.status || (tokenUsable ? 'ok' : 'token_not_issued'),
263
- auth_mode: 'token',
264
- can_auto_use: tokenUsable,
265
- token_type: result.token_type || 'Bearer',
266
- access_token: maskSensitiveAuthOutput({ access_token: result.access_token }).access_token,
267
- expires_at: result.expires_at ? new Date(result.expires_at).toISOString() : undefined,
268
- base_url: result.base_url,
269
- corp_id: result.corp_id,
270
- user_id: result.user_id,
271
- user_name: result.user_name,
272
- message: result.message,
273
- raw: tokenUsable ? undefined : result.raw,
264
+ ok: true,
265
+ base_url: result && result.base_url,
266
+ corp_id: result && result.corp_id,
267
+ user_id: result && result.user_id,
268
+ csrf_token: result && result.csrf_token ? `${result.csrf_token.slice(0, 16)}...` : undefined,
269
+ cookies_count: Array.isArray(result && result.cookies) ? result.cookies.length : (result.cookies_count || 0),
274
270
  };
275
271
  console.log(JSON.stringify(summary));
276
272
  }
@@ -281,11 +277,15 @@ function noteLoginCommandResult(result) {
281
277
  let status = 'failed';
282
278
  let reason = 'login_failed';
283
279
 
284
- const tokenLoginOk = result && result.auth_mode === 'token' &&
285
- (result.access_token || (result.status === 'ok' && result.can_auto_use));
286
- if (result && (tokenLoginOk || (result.status === 'ok' && result.can_auto_use))) {
280
+ if (result && (result.csrf_token || (result.status === 'ok' && result.can_auto_use))) {
287
281
  status = 'success';
288
282
  reason = 'login_ok';
283
+ } else if (result && result.status === 'need_qr_scan') {
284
+ status = 'need_qr_scan';
285
+ reason = 'need_qr_scan';
286
+ } else if (result && result.status === 'need_codex_browser_login') {
287
+ status = 'need_browser_login';
288
+ reason = 'need_browser_login';
289
289
  } else if (result && result.status === 'need_corp_selection') {
290
290
  status = 'success';
291
291
  reason = 'need_corp_selection';
@@ -306,6 +306,40 @@ function noteLoginCommandResult(result) {
306
306
  }
307
307
  }
308
308
 
309
+ function isAgentConversationEnvironment() {
310
+ const { detectActiveTool } = require('../lib/core/utils');
311
+ return !!detectActiveTool() || process.env.OPENYIDA_AGENT_MODE === '1';
312
+ }
313
+
314
+ function shouldUseBrowserHandoffLogin(cliArgs) {
315
+ if (cliArgs.includes('--qr') || cliArgs.includes('--codex-qr') || cliArgs.includes('--agent-qr')) {return false;}
316
+ if (cliArgs.includes('--browser') || cliArgs.includes('--codex') || cliArgs.includes('--qoder') || cliArgs.includes('--wukong')) {return true;}
317
+ return false;
318
+ }
319
+
320
+ function shouldUseAgentLogin(cliArgs) {
321
+ if (cliArgs.includes('--qr') || cliArgs.includes('--codex-qr') || cliArgs.includes('--agent-qr')) {return false;}
322
+ if (shouldUseBrowserHandoffLogin(cliArgs)) {return false;}
323
+ return isAgentConversationEnvironment();
324
+ }
325
+
326
+ function shouldUsePlaywrightFallbackInAgentLogin() {
327
+ const { hasDesktopEnvironment } = require('../lib/core/utils');
328
+ return hasDesktopEnvironment() || process.env.OPENYIDA_AGENT_PLAYWRIGHT_FALLBACK === '1';
329
+ }
330
+
331
+ function shouldUseLocalBrowserLogin(options = {}) {
332
+ const { hasLocalBrowserLoginCapability } = require('../lib/auth/login');
333
+ return hasLocalBrowserLoginCapability({
334
+ playwrightFallback: options.playwrightFallback,
335
+ });
336
+ }
337
+
338
+ function shouldUseCodexQrLogin(cliArgs) {
339
+ if (cliArgs.includes('--codex-qr') || cliArgs.includes('--agent-qr')) {return true;}
340
+ return false;
341
+ }
342
+
309
343
  function getArgValue(cliArgs, name) {
310
344
  const index = cliArgs.indexOf(name);
311
345
  if (index === -1 || !cliArgs[index + 1] || cliArgs[index + 1].startsWith('--')) {
@@ -380,9 +414,11 @@ function applyLoginEnvironmentFlags(cliArgs, options = {}) {
380
414
  '--intranet': 'alibaba',
381
415
  };
382
416
  const valuePassthroughFlags = new Set([
417
+ '--agent-poll',
418
+ '--codex-poll',
419
+ '--agent-select',
420
+ '--codex-select',
383
421
  '--corp-id',
384
- '--client-id',
385
- '--user-id',
386
422
  ]);
387
423
  const targetUrlFlags = new Set([
388
424
  '--endpoint',
@@ -527,42 +563,129 @@ async function main() {
527
563
  }
528
564
 
529
565
  case 'login': {
566
+ const { checkLoginOnly } = require('../lib/auth/login');
530
567
  const loginArgs = applyLoginEnvironmentFlags(args, { inferTargetUrl: true });
531
- const { tokenLogin, tokenStatus } = require('../lib/auth/token-auth');
532
- if (loginArgs.includes('--check-only')) {
533
- console.log(JSON.stringify(tokenStatus(), null, 2));
534
- } else {
535
- const result = await tokenLogin({
536
- clientId: getArgValue(loginArgs, '--client-id'),
537
- quiet: loginArgs.includes('--quiet'),
568
+ const { isEnvAuthMode } = require('../lib/core/utils');
569
+ if (loginArgs.includes('--agent-poll') || loginArgs.includes('--codex-poll')) {
570
+ const sessionFile = getArgValue(loginArgs, '--agent-poll') || getArgValue(loginArgs, '--codex-poll');
571
+ const { pollCodexQrLogin } = require('../lib/auth/qr-login');
572
+ const result = await pollCodexQrLogin(sessionFile, {
573
+ corpId: getArgValue(loginArgs, '--corp-id'),
538
574
  });
539
575
  printLoginResult(result);
576
+ } else if (loginArgs.includes('--agent-select') || loginArgs.includes('--codex-select')) {
577
+ const sessionFile = getArgValue(loginArgs, '--agent-select') || getArgValue(loginArgs, '--codex-select');
578
+ const { selectCodexQrCorp } = require('../lib/auth/qr-login');
579
+ const result = await selectCodexQrCorp(sessionFile, {
580
+ corpId: getArgValue(loginArgs, '--corp-id'),
581
+ });
582
+ printLoginResult(result);
583
+ } else if (loginArgs.includes('--check-only')) {
584
+ const result = checkLoginOnly({ includeSecrets: loginArgs.includes('--with-cookies') });
585
+ console.log(JSON.stringify(result, null, 2));
586
+ } else if (isEnvAuthMode()) {
587
+ const result = checkLoginOnly({ includeSecrets: true });
588
+ printLoginResult(result);
589
+ } else if (shouldUseCodexQrLogin(loginArgs)) {
590
+ const { startCodexQrLogin } = require('../lib/auth/qr-login');
591
+ const result = await startCodexQrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
592
+ printLoginResult(result);
593
+ } else if (loginArgs.includes('--browser')) {
594
+ const { interactiveLogin } = require('../lib/auth/login');
595
+ const result = interactiveLogin({ force: true });
596
+ printLoginResult(result);
597
+ } else if (loginArgs.includes('--qoder') || loginArgs.includes('--wukong')) {
598
+ const { codexLogin } = require('../lib/auth/codex-login');
599
+ const result = await codexLogin({ tool: loginArgs.includes('--qoder') ? 'qoder' : 'wukong' });
600
+ printLoginResult(result);
601
+ } else if (loginArgs.includes('--qr')) {
602
+ const { qrLogin } = require('../lib/auth/qr-login');
603
+ const result = await qrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
604
+ printLoginResult(result);
605
+ } else if (shouldUseAgentLogin(loginArgs)) {
606
+ const cachedResult = checkLoginOnly({ includeSecrets: true });
607
+ if (cachedResult.status === 'ok') {
608
+ printLoginResult(cachedResult);
609
+ } else {
610
+ const { interactiveLogin } = require('../lib/auth/login');
611
+ const playwrightFallback = shouldUsePlaywrightFallbackInAgentLogin();
612
+ const browserResult = shouldUseLocalBrowserLogin({ playwrightFallback })
613
+ ? interactiveLogin({ playwrightFallback })
614
+ : null;
615
+ if (browserResult) {
616
+ printLoginResult(browserResult);
617
+ } else {
618
+ const { startCodexQrLogin } = require('../lib/auth/qr-login');
619
+ const result = await startCodexQrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
620
+ printLoginResult(result);
621
+ }
622
+ }
623
+ } else if (shouldUseBrowserHandoffLogin(loginArgs)) {
624
+ const cachedResult = checkLoginOnly({ includeSecrets: true });
625
+ if (cachedResult.status === 'ok') {
626
+ printLoginResult(cachedResult);
627
+ } else {
628
+ const { codexLogin } = require('../lib/auth/codex-login');
629
+ const result = await codexLogin({ tool: loginArgs.includes('--codex') ? 'codex' : undefined });
630
+ printLoginResult(result);
631
+ }
632
+ } else {
633
+ const cachedResult = checkLoginOnly({ includeSecrets: true });
634
+ if (cachedResult.status === 'ok') {
635
+ printLoginResult(cachedResult);
636
+ break;
637
+ }
638
+ if (shouldUseLocalBrowserLogin({ playwrightFallback: true })) {
639
+ const { interactiveLogin } = require('../lib/auth/login');
640
+ const browserResult = interactiveLogin({ playwrightFallback: true });
641
+ if (browserResult) {
642
+ printLoginResult(browserResult);
643
+ break;
644
+ }
645
+ }
646
+ const { qrLogin } = require('../lib/auth/qr-login');
647
+ const result = await qrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
648
+ printLoginResult(result);
540
649
  }
541
650
  break;
542
651
  }
543
652
 
544
653
  case 'logout': {
545
- const { tokenLogout } = require('../lib/auth/token-auth');
546
- console.log(JSON.stringify(await tokenLogout(), null, 2));
654
+ const { logout } = require('../lib/auth/login');
655
+ logout();
547
656
  break;
548
657
  }
549
658
 
550
659
  case 'auth': {
551
660
  const subCommand = args[0];
552
- const authArgs = applyLoginEnvironmentFlags(args.slice(1), { inferTargetUrl: true });
553
- const { tokenLogin, tokenLogout, tokenRefresh, tokenStatus } = require('../lib/auth/token-auth');
661
+ const { authStatus, authLogin, authRefresh, authLogout } = require('../lib/auth/auth');
662
+
554
663
  if (subCommand === 'status') {
555
- console.log(JSON.stringify(tokenStatus(), null, 2));
664
+ authStatus();
556
665
  } else if (subCommand === 'login') {
557
- const result = await tokenLogin({
558
- clientId: getArgValue(authArgs, '--client-id'),
559
- quiet: authArgs.includes('--quiet'),
666
+ const authArgs = applyLoginEnvironmentFlags(args.slice(1), { inferTargetUrl: true });
667
+ let loginType = 'qrcode';
668
+ if (authArgs.includes('--codex')) {
669
+ loginType = 'codex';
670
+ } else if (authArgs.includes('--qoder')) {
671
+ loginType = 'qoder';
672
+ } else if (authArgs.includes('--wukong')) {
673
+ loginType = 'wukong';
674
+ } else if (authArgs.includes('--browser')) {
675
+ loginType = 'browser';
676
+ }
677
+ const result = await authLogin({
678
+ type: loginType,
679
+ corpId: getArgValue(authArgs, '--corp-id'),
680
+ forceTerminalQr: authArgs.includes('--qr'),
560
681
  });
561
- printLoginResult(result);
682
+ if (result) {
683
+ printLoginResult(result);
684
+ }
562
685
  } else if (subCommand === 'refresh') {
563
- console.log(JSON.stringify(maskSensitiveAuthOutput(await tokenRefresh()), null, 2));
686
+ authRefresh();
564
687
  } else if (subCommand === 'logout') {
565
- console.log(JSON.stringify(await tokenLogout(), null, 2));
688
+ authLogout();
566
689
  } else {
567
690
  throwCliUsage(t('cli.auth_usage'), t('cli.auth_example'));
568
691
  }
@@ -570,9 +693,34 @@ async function main() {
570
693
  }
571
694
 
572
695
  case 'org': {
573
- const orgArgs = applyLoginEnvironmentFlags(args, { inferTargetUrl: true });
574
- const { run } = require('../lib/auth/org');
575
- await run(orgArgs);
696
+ const subCommand = args[0];
697
+ const { listOrganizations, switchOrganization, interactiveSwitch } = require('../lib/auth/org');
698
+ const { loadCookieData } = require('../lib/core/utils');
699
+
700
+ if (subCommand === 'list') {
701
+ const cookieData = loadCookieData();
702
+ if (!cookieData || !cookieData.cookies) {
703
+ throwNeedLogin(t('org.no_login'));
704
+ }
705
+ await listOrganizations(cookieData);
706
+ } else if (subCommand === 'switch') {
707
+ const cookieData = loadCookieData();
708
+ if (!cookieData || !cookieData.cookies) {
709
+ throwNeedLogin(t('org.no_login'));
710
+ }
711
+
712
+ // 解析 --corp-id 参数
713
+ const corpIdIndex = args.indexOf('--corp-id');
714
+ if (corpIdIndex !== -1 && args[corpIdIndex + 1]) {
715
+ const targetCorpId = args[corpIdIndex + 1];
716
+ await switchOrganization(targetCorpId, cookieData);
717
+ } else {
718
+ // 交互式选择
719
+ await interactiveSwitch(cookieData);
720
+ }
721
+ } else {
722
+ throwCliUsage(t('cli.org_usage'), t('cli.org_example'));
723
+ }
576
724
  break;
577
725
  }
578
726
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { createAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
3
+ const { createAuthRef, createYidaClient } = require('../core/yida-client');
4
4
 
5
5
  const AGENT_TYPE_ALIASES = {
6
6
  normal: 'NORMAL',
@@ -70,7 +70,7 @@ const AGENT_RANGE_ALIASES = {
70
70
 
71
71
  function getAuthRef() {
72
72
  const authRef = createAuthRef();
73
- if (!isAuthRefReady(authRef)) {
73
+ if (!authRef.cookies || !authRef.cookies.length || !authRef.csrfToken) {
74
74
  throw new Error('无法获取有效登录态或 CSRF Token');
75
75
  }
76
76
  return authRef;
@@ -3,7 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const querystring = require('querystring');
5
5
 
6
- const { createAuthRef: createCoreAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
6
+ const { createAuthRef: createCoreAuthRef, createYidaClient } = require('../core/yida-client');
7
7
  const { t } = require('../core/i18n');
8
8
  const { buildYidaTitleI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
9
9
  const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
@@ -97,7 +97,7 @@ function assertSuccess(result, action) {
97
97
 
98
98
  async function createAuthRef() {
99
99
  const authRef = createCoreAuthRef();
100
- if (!isAuthRefReady(authRef)) {
100
+ if (!authRef.cookies || authRef.cookies.length === 0) {
101
101
  throw new Error(t('aggregate_table.no_login'));
102
102
  }
103
103
  return authRef;
package/lib/ai/ai.js CHANGED
@@ -15,7 +15,7 @@ const {
15
15
  findProjectRoot,
16
16
  } = require('../core/utils');
17
17
  const { CliError } = require('../core/cli-error');
18
- const { createAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
18
+ const { createAuthRef, createYidaClient } = require('../core/yida-client');
19
19
 
20
20
  const DEFAULT_MAX_TOKENS = 3000;
21
21
  const DEFAULT_IMAGE_CONNECTOR = {
@@ -46,7 +46,7 @@ function printHelp() {
46
46
  image 上传本地图片并调用识图连接器;也可直接传入 --image-url
47
47
 
48
48
  image 选项:
49
- --app-type <APP_XXX> 上传图片使用的宜搭应用 ID;未传时尝试读取当前 project/config.json
49
+ --app-type <APP_XXX> 上传图片使用的宜搭应用 ID;未传时尝试读取当前 project/config.json 或 Cookie
50
50
  --form-uuid <FORM_XXX> 可选,上传回调关联的表单 ID
51
51
  --connector-id <id> 识图 HTTP 连接器 ID,默认使用 HAR 中的植物识别连接器
52
52
  --action-id <id> 识图动作 ID,默认 flowerrecognize
@@ -146,7 +146,7 @@ function getAuthRef(options = {}) {
146
146
  if (options.baseUrl) {
147
147
  authRef.baseUrl = options.baseUrl;
148
148
  }
149
- if (!isAuthRefReady(authRef)) {
149
+ if (!authRef.cookies || authRef.cookies.length === 0 || !authRef.csrfToken) {
150
150
  throw new CliError('未获取到有效宜搭登录态,请先执行 openyida login', {
151
151
  code: 'NEED_LOGIN',
152
152
  });
@@ -154,7 +154,12 @@ function getAuthRef(options = {}) {
154
154
  return authRef;
155
155
  }
156
156
 
157
- function inferAppType(options) {
157
+ function getCookieValue(authRef, name) {
158
+ const matched = (authRef.cookies || []).filter(cookie => cookie.name === name);
159
+ return matched.length ? matched[0].value : '';
160
+ }
161
+
162
+ function inferAppType(options, authRef) {
158
163
  if (options.appType) {
159
164
  return options.appType;
160
165
  }
@@ -168,11 +173,11 @@ function inferAppType(options) {
168
173
  return config.appType;
169
174
  }
170
175
  } catch {
171
- // ignore invalid local config and continue with explicit appType validation
176
+ // ignore invalid local config and continue with cookie fallback
172
177
  }
173
178
  }
174
179
 
175
- return '';
180
+ return getCookieValue(authRef, 'tianshu_app_type');
176
181
  }
177
182
 
178
183
  function getSuccessContent(response, fallbackMessage, code = 'AI_REQUEST_FAILED') {
@@ -329,7 +334,7 @@ async function uploadImageForAI(filePath, options, authRef) {
329
334
  });
330
335
  }
331
336
 
332
- const appType = inferAppType(options);
337
+ const appType = inferAppType(options, authRef);
333
338
  if (!appType) {
334
339
  throw new CliError('上传图片需要 appType,请传入 --app-type APP_XXX', {
335
340
  code: 'AI_IMAGE_APP_TYPE_REQUIRED',
@@ -273,8 +273,8 @@ function compileCanvasLocal(source) {
273
273
  }
274
274
 
275
275
  /**
276
- * 兼容既有调用方传入 options 的异步入口。
277
- * 本地编译不需要 endpoint,options 保留但忽略。
276
+ * 兼容既有调用方(publish.js `await compileCanvas(src, { cookies })`)的异步入口。
277
+ * 本地编译不需要 cookies/endpoint,options 保留但忽略。
278
278
  * @param {string} source
279
279
  * @param {object} [options] 兼容占位,未使用
280
280
  * @returns {Promise<{ runtimeCode: string, importedModules: string }>}
@@ -10,10 +10,12 @@ const fs = require('fs');
10
10
  const path = require('path');
11
11
  const querystring = require('querystring');
12
12
  const {
13
+ loadCookieData,
14
+ triggerLogin,
15
+ resolveBaseUrl,
13
16
  httpPost,
14
17
  requestWithAutoLogin,
15
18
  } = require('../core/utils');
16
- const { createAuthRef } = require('../core/yida-client');
17
19
  const { t } = require('../core/i18n');
18
20
  const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
19
21
  const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
@@ -230,7 +232,18 @@ async function run(args) {
230
232
 
231
233
  // Step 1: 读取登录态
232
234
  step(1, t('common.step_login', 1));
233
- const authRef = createAuthRef();
235
+ let cookieData = loadCookieData();
236
+ if (!cookieData) {
237
+ info(t('common.login_no_cache'));
238
+ cookieData = triggerLogin();
239
+ }
240
+
241
+ const authRef = {
242
+ csrfToken: cookieData.csrf_token,
243
+ cookies: cookieData.cookies,
244
+ baseUrl: resolveBaseUrl(cookieData),
245
+ cookieData,
246
+ };
234
247
  chalkSuccess(t('common.login_ready', authRef.baseUrl));
235
248
 
236
249
  const contentLocale = resolveContentLocale({ locale: params.locale, baseUrl: authRef.baseUrl });
@@ -247,7 +260,8 @@ async function run(args) {
247
260
  const corpConfig = await httpPost(
248
261
  authRef.baseUrl,
249
262
  `/query/exclusive/queryCorpAppConfig.json?_api=Global.queryCorpAppConfig&_mock=false&_csrf_token=${authRef.csrfToken}&_locale_time_zone_offset=28800000&_stamp=${Date.now()}`,
250
- ''
263
+ '',
264
+ authRef.cookies
251
265
  );
252
266
  if (corpConfig && corpConfig.content) {
253
267
  if (corpConfig.content.forceExclusiveDb === 'y') {openExclusive = 'y';}
@@ -275,14 +289,14 @@ async function run(args) {
275
289
  openExclusiveUnit: 'n',
276
290
  group: 'ALL',
277
291
  });
278
- return httpPost(auth.baseUrl, '/query/app/registerApp.json', postData);
292
+ return httpPost(auth.baseUrl, '/query/app/registerApp.json', postData, auth.cookies);
279
293
  }, authRef);
280
294
 
281
295
  // 输出结果
282
296
  if (response && response.success && response.content) {
283
297
  const appType = response.content;
284
298
  const appUrl = `${authRef.baseUrl}/${appType}/admin`;
285
- const corpId = authRef.corpId || (authRef.authData && authRef.authData.corp_id) || '';
299
+ const corpId = authRef.cookieData.corp_id || '';
286
300
 
287
301
  chalkResult(true, t('create_app.success'), [
288
302
  ['appType', appType],