openyida 2026.10.15-2 → 2026.10.16

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.
@@ -229,6 +229,26 @@ function createTokenAuthRequiredError(reason, sourceError) {
229
229
  );
230
230
  }
231
231
 
232
+ function createAuthProfileSelectionError(status) {
233
+ const reason = status.failure_reason || status.status;
234
+ if (!['profile_required', 'profile_not_found'].includes(reason)) {return null;}
235
+ const { CliError } = require('../core/cli-error');
236
+ const { t } = require('../core/i18n');
237
+ return new CliError(t(`common.auth_${reason}`), {
238
+ code: reason === 'profile_required' ? 'AUTH_PROFILE_REQUIRED' : 'AUTH_PROFILE_NOT_FOUND',
239
+ details: {
240
+ authMode: 'token',
241
+ status: reason,
242
+ failure_reason: reason,
243
+ auth_profile: status.auth_profile,
244
+ candidate_count: status.candidate_count,
245
+ candidates: status.candidates,
246
+ next_step: status.next_step,
247
+ next_step_commands: status.next_step_commands,
248
+ },
249
+ });
250
+ }
251
+
232
252
  function createTokenRefreshFailedError(reason, sourceError) {
233
253
  const { CliError } = require('../core/cli-error');
234
254
  return new CliError(
@@ -630,7 +650,12 @@ async function tokenLogout(options = {}) {
630
650
  }
631
651
 
632
652
  async function getAccessToken(options = {}) {
633
- let session = loadTokenSession(options);
653
+ const resolution = resolveTokenSession(options);
654
+ let session = resolution.session;
655
+ if (!session) {
656
+ const selectionError = createAuthProfileSelectionError(resolution);
657
+ if (selectionError) {throw selectionError;}
658
+ }
634
659
  // Do not reject a token from local expiry metadata. The business endpoint is
635
660
  // the authority: requestWithAutoLogin refreshes only after it rejects the
636
661
  // access token, then retries with the newly persisted local session.
@@ -675,4 +700,5 @@ module.exports = {
675
700
  resolveTokenBaseUrl,
676
701
  isRefreshAuthRequired,
677
702
  createTokenRefreshFailedError,
703
+ createAuthProfileSelectionError,
678
704
  };
@@ -472,12 +472,31 @@ function saveAuthProfilePointer(session, options = {}) {
472
472
  return pointer;
473
473
  }
474
474
 
475
- function loadAuthProfilePointer(options = {}) {
476
- const pointerFile = getAuthProfilePointerFilePath(options);
477
- if (!fs.existsSync(pointerFile)) {
475
+ function getWorkspaceAuthProfilePointerFilePath(options = {}) {
476
+ const root = path.resolve(resolveProjectRoot(options));
477
+ const workspace = path.dirname(root);
478
+ if (isEnvTokenAuthMode(options.env || process.env) || path.basename(root) !== 'project' ||
479
+ fs.existsSync(path.join(workspace, 'config.json'))) {
478
480
  return null;
479
481
  }
480
482
  try {
483
+ // Only bridge the conventional workspace -> workspace/project transition.
484
+ // A symlinked project belongs to its own workspace, not its lexical parent.
485
+ if (fs.realpathSync(root) !== path.join(fs.realpathSync(workspace), 'project')) {return null;}
486
+ return getAuthProfilePointerFilePath({ ...options, projectRoot: workspace, envName: resolveEnvName(options) });
487
+ } catch {return null;}
488
+ }
489
+
490
+ function loadAuthProfilePointer(options = {}) {
491
+ try {
492
+ let pointerFile = getAuthProfilePointerFilePath(options);
493
+ // An explicit child selection (including an unbound/corrupt pointer) wins.
494
+ // Reading inherited metadata never copies credentials or writes a pointer.
495
+ if (!fs.existsSync(pointerFile)) {
496
+ if (fs.existsSync(getTokenFilePath(options))) {return null;}
497
+ pointerFile = getWorkspaceAuthProfilePointerFilePath(options);
498
+ }
499
+ if (!pointerFile || !fs.existsSync(pointerFile)) {return null;}
481
500
  const pointer = JSON.parse(fs.readFileSync(pointerFile, 'utf8'));
482
501
  if (!pointer || typeof pointer !== 'object' || !pointer.auth_profile) {
483
502
  return null;
@@ -491,6 +510,13 @@ function loadAuthProfilePointer(options = {}) {
491
510
  function clearAuthProfilePointer(options = {}) {
492
511
  try {
493
512
  const pointerFile = getAuthProfilePointerFilePath(options);
513
+ const workspacePointerFile = getWorkspaceAuthProfilePointerFilePath(options);
514
+ if (workspacePointerFile && fs.existsSync(workspacePointerFile)) {
515
+ // Detaching this project must not revive its inherited workspace choice
516
+ // on the next command, or clear another project's workspace selection.
517
+ writeJsonFileAtomic(pointerFile, { version: AUTH_PROFILE_VERSION, detached: true });
518
+ return;
519
+ }
494
520
  if (fs.existsSync(pointerFile)) {
495
521
  fs.unlinkSync(pointerFile);
496
522
  }
@@ -1016,6 +1016,8 @@ Examples:
1016
1016
  data_check_failed: ' ⚠️ Data check failed, proceeding with modification',
1017
1017
  },
1018
1018
  common: {
1019
+ auth_profile_required: 'Multiple organizations have saved credentials. Run openyida auth profiles, then openyida auth profile switch <auth_profile> to select this project\'s organization.',
1020
+ auth_profile_not_found: 'The requested organization credentials were not found. Run openyida auth profiles to inspect saved organizations and select the correct profile.',
1019
1021
  auth_identity_changed: 'Authentication identity changed during preflight; no write was issued.',
1020
1022
  http_status: ' HTTP status: {0}',
1021
1023
  http_response: ' HTTP response: {0}',
@@ -987,6 +987,8 @@ openyida - 宜搭命令行工具
987
987
  data_check_failed: ' ⚠️ 数据量检查失败,继续执行修改',
988
988
  },
989
989
  common: {
990
+ auth_profile_required: '已有多个组织的登录凭证,请执行 openyida auth profiles,并用 openyida auth profile switch <auth_profile> 选择本项目的组织。',
991
+ auth_profile_not_found: '未找到指定组织的登录凭证,请执行 openyida auth profiles 核对已有组织,再选择正确的登录凭证。',
990
992
  auth_identity_changed: '校验期间登录身份发生变化,本次未发起写入。',
991
993
  http_status: ' HTTP 状态码: {0}',
992
994
  http_response: ' HTTP 响应: {0}',
package/lib/core/utils.js CHANGED
@@ -960,6 +960,11 @@ function triggerLogin(options = {}) {
960
960
  if (tokenAuthData) {
961
961
  return tokenAuthData;
962
962
  }
963
+ const { createAuthProfileSelectionError } = require('../auth/token-auth');
964
+ const selectionError = createAuthProfileSelectionError(getAuthStatus({
965
+ ...options, projectRoot: options.projectRoot || findProjectRoot(),
966
+ }));
967
+ if (selectionError) {throw selectionError;}
963
968
  const { CliError } = require('./cli-error');
964
969
  throw new CliError(
965
970
  'not_logged_in: token auth is unavailable. Run openyida login first.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.10.15-2",
3
+ "version": "2026.10.16",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -106,8 +106,8 @@
106
106
  --corner-3: 2px;
107
107
  --corner-4: 2px;
108
108
  --corner-5: 2px;
109
- --corner-circle: var(--corner-2);
110
- --corner-semicircle: 2px;
109
+ --corner-circle: 999px;
110
+ --corner-semicircle: var(--corner-2);
111
111
 
112
112
  /* 阴影 */
113
113
  --shadow-1: 0px 2px 4px 0px rgba(0, 0, 0, 0.12);
@@ -158,7 +158,7 @@
158
158
  /* 卡片 */
159
159
  --pod-card-padding: 20px;
160
160
  --pod-card-border-radius: var(--pod-xl-border-radius, 20px);
161
- --pod-card-border: none;
161
+ --pod-card-border: 1px solid var(--color-line1-1);
162
162
  --pod-card-spacing: var(--pod-md-gap-pad-margin);
163
163
 
164
164
  /* 导航:顶导与侧导布局 */
@@ -11,6 +11,7 @@ tokens:
11
11
  "--pod-app-root-bg-image": "none" # 仅作用于根节点;本主题不使用背景图
12
12
  "--pod-page-bg-color": "#FFFFFF" # neutral-gray;白色内容画布
13
13
  "--pod-card-bg-color": "#FFFFFF" # neutral-gray;一级内容表面
14
+ "--pod-card-border": "1px solid var(--color-line1-1)"
14
15
  "--pod-table-cell-color": "var(--pod-card-bg-color)" # 表格正文跟随卡片表面
15
16
  "--pod-shell-padding": "0px"
16
17
  navigation:
@@ -112,8 +113,8 @@ tokens:
112
113
  "--corner-3": 2px # 气泡、浮层、弹窗外轮廓
113
114
  "--corner-4": 2px # 小型嵌入摘要与内部组合容器
114
115
  "--corner-5": 2px # 卡片、独立面板与抽屉外轮廓
115
- "--corner-circle": "var(--corner-2)" # 等宽高头像、图例圆点
116
- "--corner-semicircle": 2px # 胶囊按钮、计数标记、进度条圆头
116
+ "--corner-circle": 999px # 等宽高头像、图例圆点
117
+ "--corner-semicircle": "var(--corner-2)" # 胶囊按钮、计数标记、进度条圆头
117
118
  shadow:
118
119
  "--shadow-1": "0px 2px 4px 0px rgba(0, 0, 0, 0.12)"
119
120
  "--shadow-2": "0px 4px 20px -1px rgba(0, 0, 0, 0.16)"