openyida 2026.10.28-beta.2 → 2026.10.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -23
- package/bin/yida.js +0 -20
- package/lib/app/create-app.js +2 -0
- package/lib/app/import-app.js +3 -0
- package/lib/auth/token-auth.js +1 -12
- package/lib/auth/token-store.js +0 -1
- package/lib/core/agent-capabilities.js +5 -60
- package/lib/core/cli-error.js +2 -1
- package/lib/core/command-manifest.js +5 -8
- package/lib/core/locales/en.js +12 -19
- package/lib/core/locales/zh.js +11 -18
- package/lib/core/permission-guidance.js +98 -0
- package/lib/core/utils.js +28 -2
- package/lib/solution-template/solution-template.js +8 -0
- package/package.json +5 -14
- package/yida-skills/SKILL.md +0 -11
- package/yida-skills/references/setup-and-env.md +1 -10
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +1 -0
- package/yida-skills/skills/yida-create-app/SKILL.md +9 -3
- package/yida-skills/skills/yida-login/SKILL.md +1 -24
- package/bin/yida-beta.js +0 -5
- package/lib/auth/qwenwork-client.js +0 -190
- package/lib/auth/qwenwork-cloud.js +0 -93
- package/lib/auth/qwenwork-desktop.js +0 -154
- package/lib/auth/qwenwork-login.js +0 -108
- package/lib/auth/qwenwork.js +0 -37
- package/lib/core/qwenwork-login-guidance.js +0 -35
- package/scripts/install-qwenwork-pre-beta.js +0 -40
- package/yida-skills/references/qwenwork-cloud-login.md +0 -21
- package/yida-skills/references/qwenwork-desktop-login.md +0 -12
package/README.md
CHANGED
|
@@ -143,27 +143,6 @@ openyida login --check-only --json
|
|
|
143
143
|
openyida auth status
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
In QwenWork web, `openyida login --qwenwork --json` (or ordinary `login`) sends the sandbox Agent Token and gateway origin directly to the selected YiDA authentication endpoint. YiDA validates the identity through the trusted gateway’s fixed `/api/agent-gw/yida/user_info` endpoint, then issues its own short-lived session. This login flow no longer calls the legacy exchange or `/user/info` endpoints. Login preparation stays silent; failures stop without browser OAuth. Desktop and other hosts retain their existing authentication paths.
|
|
147
|
-
|
|
148
|
-
For QwenWork cloud identity diagnostics, run inside the actual cloud execution environment:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
openyida auth qwenwork user-info --json
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
This command exchanges the injected `QWENWORK_AGENT_TOKEN` (legacy: `MULE_AGENT_TOKEN` / `AGENT_TOKEN`) at `QWENWORK_GATEWAY_URL` (legacy: `MULE_GATEWAY_URL`), then queries `/user/info` on the same origin. It reports identity and scope metadata without printing or persisting either credential. It does not create an OpenYida login session, and QwenWork organization IDs are not YiDA `corpId` values. Existing OAuth/profile behavior is unchanged.
|
|
155
|
-
|
|
156
|
-
Programmatic callers can use `exchangeQwenWorkAgentToken`, `getQwenWorkUserInfo` and `getQwenWorkIdentity` from `lib/auth/qwenwork-client.js`. The exchange function returns an in-memory `access_token` credential for the identity query or a future server-side login exchange; callers must not log or persist it.
|
|
157
|
-
|
|
158
|
-
```js
|
|
159
|
-
const { exchangeQwenWorkAgentToken, getQwenWorkUserInfo } = require('openyida/lib/auth/qwenwork-client');
|
|
160
|
-
async function readCloudUser() {
|
|
161
|
-
const credential = await exchangeQwenWorkAgentToken();
|
|
162
|
-
// The result contains QwenWork identifiers, not YiDA IDs.
|
|
163
|
-
return getQwenWorkUserInfo(credential);
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
146
|
To reuse an existing shared login profile for the current project, switch first instead of starting a new OAuth login:
|
|
168
147
|
|
|
169
148
|
```bash
|
|
@@ -478,9 +457,9 @@ When preparing a PR that needs a personal Fork, or when you ask how to support t
|
|
|
478
457
|
|
|
479
458
|
| Command | Description |
|
|
480
459
|
|---------|-------------|
|
|
481
|
-
| `openyida login [
|
|
460
|
+
| `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]` | Login with OAuth token mode |
|
|
482
461
|
| `openyida logout` | Logout / unbind current project auth |
|
|
483
|
-
| `openyida auth <status\|login\|refresh\|logout\|profiles\|profile switch
|
|
462
|
+
| `openyida auth <status\|login\|refresh\|logout\|profiles\|profile switch>` | Token login state and profile management |
|
|
484
463
|
| `openyida org <list\|switch> [--json] [--corp-id <corpId>]` | Organization management (list / switch existing profiles first) |
|
|
485
464
|
| `openyida env [--json\|setup\|list\|show\|switch\|add\|remove] [options]` | Detect AI tool environment & token login state |
|
|
486
465
|
|
package/bin/yida.js
CHANGED
|
@@ -598,23 +598,7 @@ async function main() {
|
|
|
598
598
|
});
|
|
599
599
|
assertManagedInvocation(command, rawArgs);
|
|
600
600
|
applyQuietFlag();
|
|
601
|
-
// 一次性票据只在当前进程消费,不能先自更新并重启命令。
|
|
602
|
-
switch (command) {
|
|
603
|
-
case 'login-qwenwork':
|
|
604
|
-
await require('../lib/auth/qwenwork-desktop').run(args);
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
601
|
applyGlobalEnvironmentFlags();
|
|
608
|
-
// 网页态由 CLI 消费宿主凭据;桌面态继续消费宿主准备的登录态。
|
|
609
|
-
const webLogin = (command === 'login' || (command === 'auth' && args[0] === 'login')) &&
|
|
610
|
-
require('../lib/core/qwenwork-login-guidance').isQwenWorkWebRuntime(require('../lib/core/utils').detectRuntimeCapabilities());
|
|
611
|
-
if ((command === 'login' && args.includes('--qwenwork')) || webLogin) {
|
|
612
|
-
const loginArgs = applyLoginEnvironmentFlags(command === 'auth' ? args.slice(1) : args, { inferTargetUrl: true });
|
|
613
|
-
if (hasHelpFlag(loginArgs)) { printLoginHelp(); return; }
|
|
614
|
-
assertNoUnsupportedLegacyLoginFlags(rawArgs, loginArgs);
|
|
615
|
-
await require('../lib/auth/qwenwork-login').run(loginArgs, buildTokenLoginOptions(loginArgs));
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
602
|
|
|
619
603
|
const { maybeAutoUpdate } = require('../lib/core/update');
|
|
620
604
|
const updateResult = await maybeAutoUpdate({
|
|
@@ -734,10 +718,6 @@ async function main() {
|
|
|
734
718
|
|
|
735
719
|
case 'auth': {
|
|
736
720
|
const subCommand = args[0];
|
|
737
|
-
if (subCommand === 'qwenwork') {
|
|
738
|
-
await require('../lib/auth/qwenwork').run(args.slice(1));
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
721
|
const authArgs = applyLoginEnvironmentFlags(args.slice(1), { inferTargetUrl: true });
|
|
742
722
|
const { getAuthStatus } = require('../lib/core/utils');
|
|
743
723
|
const { tokenLogin, tokenLogout, tokenRefresh } = require('../lib/auth/token-auth');
|
package/lib/app/create-app.js
CHANGED
|
@@ -20,6 +20,7 @@ const { buildApplicationEntryUrls } = require('./application-entry-urls');
|
|
|
20
20
|
const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
21
21
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
22
22
|
const { throwCommandError } = require('../core/command-errors');
|
|
23
|
+
const { isPermissionGuidanceError } = require('../core/permission-guidance');
|
|
23
24
|
const { resolveBuilderAiSource } = require('./builder-ai-source');
|
|
24
25
|
|
|
25
26
|
const DEFAULT_APP_OPTIONS = {
|
|
@@ -358,6 +359,7 @@ async function run(args) {
|
|
|
358
359
|
if (corpConfig.content.forcePhysicalColumn === 'y') {openPhysicColumn = 'y';}
|
|
359
360
|
}
|
|
360
361
|
} catch (err) {
|
|
362
|
+
if (isPermissionGuidanceError(err)) { throw err; }
|
|
361
363
|
// 查询失败时使用默认值,不影响主流程
|
|
362
364
|
}
|
|
363
365
|
|
package/lib/app/import-app.js
CHANGED
|
@@ -21,6 +21,7 @@ const { t } = require('../core/i18n');
|
|
|
21
21
|
const { buildYidaI18n, resolveContentLocale } = require('../core/yida-i18n');
|
|
22
22
|
const { banner, step, label, success, fail, warn, info, error, result, usage, hint } = require('../core/chalk');
|
|
23
23
|
const { throwUsage } = require('../core/command-errors');
|
|
24
|
+
const { isPermissionGuidanceError } = require('../core/permission-guidance');
|
|
24
25
|
const { getSchemaServerRevision } = require('../core/server-revision');
|
|
25
26
|
const { resolveBuilderAiSource } = require('./builder-ai-source');
|
|
26
27
|
|
|
@@ -243,6 +244,7 @@ async function run(args) {
|
|
|
243
244
|
try {
|
|
244
245
|
newAppType = await createApp(appName, authRef);
|
|
245
246
|
} catch (err) {
|
|
247
|
+
if (isPermissionGuidanceError(err)) { throw err; }
|
|
246
248
|
error(err.message);
|
|
247
249
|
}
|
|
248
250
|
success(t('import.app_created', newAppType));
|
|
@@ -272,6 +274,7 @@ async function run(args) {
|
|
|
272
274
|
newFormUuid = await createBlankForm(newAppType, formName, authRef, formType);
|
|
273
275
|
success(t('import.blank_form_created', newFormUuid));
|
|
274
276
|
} catch (err) {
|
|
277
|
+
if (isPermissionGuidanceError(err)) { throw err; }
|
|
275
278
|
fail(t('import.create_form_failed', err.message));
|
|
276
279
|
migrationReport.forms.push({
|
|
277
280
|
oldFormUuid,
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -175,7 +175,6 @@ function normalizeTokenResponse(response, baseUrl, clientId) {
|
|
|
175
175
|
);
|
|
176
176
|
return {
|
|
177
177
|
auth_mode: 'token',
|
|
178
|
-
credential_source: data.credential_source,
|
|
179
178
|
status: data.status || 'ok',
|
|
180
179
|
can_auto_use: true,
|
|
181
180
|
message: data.message || data.errorMsg || response.errorMsg,
|
|
@@ -487,9 +486,7 @@ async function tokenRefreshUnlocked(options, session) {
|
|
|
487
486
|
// Token exchange follows the active environment endpoint, just like the
|
|
488
487
|
// legacy login bootstrap. The response may return a different corp base_url
|
|
489
488
|
// for business requests and generated application links.
|
|
490
|
-
|
|
491
|
-
const desktopSession = session.credential_source === 'qwenwork_desktop';
|
|
492
|
-
const authBaseUrlValue = desktopSession ? session.base_url : resolveTokenBaseUrl({
|
|
489
|
+
const authBaseUrlValue = resolveTokenBaseUrl({
|
|
493
490
|
projectRoot: options.projectRoot,
|
|
494
491
|
endpoint: options.endpoint,
|
|
495
492
|
});
|
|
@@ -502,13 +499,6 @@ async function tokenRefreshUnlocked(options, session) {
|
|
|
502
499
|
requireOkResponse(response);
|
|
503
500
|
const data = getTokenPayload(response);
|
|
504
501
|
const responseBusinessBaseUrl = data.base_url || data.baseUrl;
|
|
505
|
-
if (desktopSession && responseBusinessBaseUrl !== undefined) {
|
|
506
|
-
try {
|
|
507
|
-
const value = new URL(responseBusinessBaseUrl);
|
|
508
|
-
if (typeof responseBusinessBaseUrl !== 'string' || responseBusinessBaseUrl.length > 2048 || /\s/.test(responseBusinessBaseUrl) ||
|
|
509
|
-
value.protocol !== 'https:' || value.username || value.password || value.pathname !== '/' || value.search || value.hash) { throw new Error(); }
|
|
510
|
-
} catch { throw createTokenAuthRequiredError('refresh_origin_mismatch'); }
|
|
511
|
-
}
|
|
512
502
|
const businessBaseUrl = session.base_url || authBaseUrlValue;
|
|
513
503
|
const normalized = normalizeTokenResponse({
|
|
514
504
|
...data,
|
|
@@ -517,7 +507,6 @@ async function tokenRefreshUnlocked(options, session) {
|
|
|
517
507
|
user_id: data.user_id || data.userId || session.user_id,
|
|
518
508
|
user_name: data.user_name || data.userName || session.user_name,
|
|
519
509
|
refresh_token: data.refresh_token || data.refreshToken || session.refresh_token,
|
|
520
|
-
credential_source: data.credential_source || session.credential_source,
|
|
521
510
|
}, businessBaseUrl, clientId);
|
|
522
511
|
if (!normalized.access_token) {
|
|
523
512
|
return {
|
package/lib/auth/token-store.js
CHANGED
|
@@ -276,7 +276,6 @@ function normalizeTokenSession(session = {}) {
|
|
|
276
276
|
);
|
|
277
277
|
const normalized = {
|
|
278
278
|
auth_mode: 'token',
|
|
279
|
-
credential_source: session.credential_source,
|
|
280
279
|
token_type: session.token_type || session.tokenType || 'Bearer',
|
|
281
280
|
access_token: session.access_token || session.accessToken,
|
|
282
281
|
refresh_token: session.refresh_token || session.refreshToken,
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const { version
|
|
6
|
+
const { version } = require('../../package.json');
|
|
7
7
|
const { t } = require('./i18n');
|
|
8
8
|
const { buildCommandManifest } = require('./command-manifest');
|
|
9
9
|
const { buildEnvironmentSnapshot } = require('./env');
|
|
10
10
|
const coreUtils = require('./utils');
|
|
11
11
|
const { buildHostAssetCapabilities } = require('../asset/host-capabilities');
|
|
12
|
-
const { buildQwenWorkLoginInvocation, isQwenWorkWebRuntime } = require('./qwenwork-login-guidance');
|
|
13
12
|
const { findProjectRoot, getAuthStatus } = coreUtils;
|
|
14
13
|
|
|
15
14
|
function redactLogin(login) {
|
|
@@ -196,7 +195,7 @@ function forbiddenAliasBriefs(manifest) {
|
|
|
196
195
|
}));
|
|
197
196
|
}
|
|
198
197
|
|
|
199
|
-
function buildAuthPath(login, env = process.env
|
|
198
|
+
function buildAuthPath(login, env = process.env) {
|
|
200
199
|
const authSource = login.auth_source || 'token_session';
|
|
201
200
|
const envTokenSession = authSource === 'env' || login.failure_reason === 'env_token_missing';
|
|
202
201
|
const authPath = {
|
|
@@ -231,54 +230,10 @@ function buildAuthPath(login, env = process.env, runtime = null) {
|
|
|
231
230
|
? 'STOP_AND_REQUEST_ENV_TOKEN'
|
|
232
231
|
: 'RUN_OPENYIDA_LOGIN_IF_USER_APPROVES',
|
|
233
232
|
};
|
|
234
|
-
if (isQwenWorkWebRuntime(runtime) && !envTokenSession) {
|
|
235
|
-
const invocation = buildQwenWorkLoginInvocation({ subtype: 'qwenwork_web' });
|
|
236
|
-
authPath.auth_runtime = 'qwenwork_cloud_exchange';
|
|
237
|
-
authPath.interactive_login_allowed = true;
|
|
238
|
-
authPath.missing_token_action = 'RUN_OPENYIDA_LOGIN_QWENWORK';
|
|
239
|
-
for (const key of ['auth_profile', 'corp_id', 'corp_name', 'user_id', 'user_name', 'store', 'persistence_scope', 'warning']) { delete authPath[key]; }
|
|
240
|
-
authPath.next_step = authPath.can_auto_use
|
|
241
|
-
? 'Silently continue the requested business operation and report its business result.'
|
|
242
|
-
: 'Run the recommended login command silently. The CLI consumes cloud runtime credentials; do not inspect credentials, open a browser, or fall back to OAuth on failure.';
|
|
243
|
-
authPath.next_step_commands = authPath.can_auto_use ? [] : [invocation.command];
|
|
244
|
-
return authPath;
|
|
245
|
-
}
|
|
246
|
-
// 桌面换票仅由当前活跃宿主和发行配置共同启用,不影响其他宿主的认证路径。
|
|
247
|
-
if (localBeta?.desktopTicketLogin && runtime?.tool === 'qwenwork' && runtime?.subtype === 'qwenwork_desktop' && !envTokenSession) {
|
|
248
|
-
const invocation = buildQwenWorkLoginInvocation();
|
|
249
|
-
authPath.auth_runtime = 'qwenwork_desktop_exchange_ticket';
|
|
250
|
-
authPath.interactive_login_allowed = true;
|
|
251
|
-
authPath.missing_token_action = 'RUN_OPENYIDA_LOGIN_QWENWORK';
|
|
252
|
-
for (const key of ['auth_profile', 'corp_id', 'corp_name', 'user_id', 'user_name', 'store', 'persistence_scope', 'warning']) { delete authPath[key]; }
|
|
253
|
-
authPath.next_step = authPath.can_auto_use
|
|
254
|
-
? 'Silently continue the requested business operation and report its business result.'
|
|
255
|
-
: `Run the recommended login command as returned: ${invocation.command}. No extra PATH or output setup is needed. After success silently continue the requested business operation. If it fails, follow the returned invocation guidance.`;
|
|
256
|
-
authPath.next_step_commands = authPath.can_auto_use ? [] : [invocation.command];
|
|
257
|
-
}
|
|
258
233
|
return authPath;
|
|
259
234
|
}
|
|
260
235
|
|
|
261
236
|
function buildInteractiveLogin(auth, runtime) {
|
|
262
|
-
if (auth.auth_runtime === 'qwenwork_cloud_exchange' && isQwenWorkWebRuntime(runtime)) {
|
|
263
|
-
return {
|
|
264
|
-
mode: 'cloud_token', browser_default: 'not_required', browser_owner: 'none',
|
|
265
|
-
recommended_command: auth.can_auto_use ? null : buildQwenWorkLoginInvocation({ subtype: 'qwenwork_web' }).command,
|
|
266
|
-
invocation: buildQwenWorkLoginInvocation({ subtype: 'qwenwork_web' }),
|
|
267
|
-
agent_action: auth.can_auto_use ? 'continue_business_command' : 'run_login_qwenwork_and_wait',
|
|
268
|
-
user_visible: false, success_action: 'continue_business_command_without_message',
|
|
269
|
-
reason: 'qwenwork_web_cloud_credentials', completion_signal: 'process_exit_and_final_json', playwright_required: false,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
if (auth.auth_runtime === 'qwenwork_desktop_exchange_ticket' && runtime.tool === 'qwenwork' && runtime.subtype === 'qwenwork_desktop') {
|
|
273
|
-
return {
|
|
274
|
-
mode: 'desktop_ticket', browser_default: 'not_required', browser_owner: 'cli_if_auth_required',
|
|
275
|
-
recommended_command: auth.can_auto_use ? null : buildQwenWorkLoginInvocation().command,
|
|
276
|
-
invocation: buildQwenWorkLoginInvocation(),
|
|
277
|
-
agent_action: auth.can_auto_use ? 'continue_business_command' : 'run_login_qwenwork_and_wait',
|
|
278
|
-
user_visible: false, success_action: 'continue_business_command_without_message',
|
|
279
|
-
reason: 'qwenwork_desktop_pre_beta', completion_signal: 'process_exit_and_final_json', playwright_required: false,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
237
|
const base = {
|
|
283
238
|
suppress_flag: '--no-browser',
|
|
284
239
|
suppress_env: 'OPENYIDA_NO_BROWSER',
|
|
@@ -338,9 +293,9 @@ function buildInteractiveLogin(auth, runtime) {
|
|
|
338
293
|
}
|
|
339
294
|
|
|
340
295
|
function buildBuilderPath(login, projectRoot, manifest, env = process.env, runtimeSnapshot = null) {
|
|
341
|
-
const
|
|
342
|
-
const auth = buildAuthPath(login, env, runtime);
|
|
296
|
+
const auth = buildAuthPath(login, env);
|
|
343
297
|
const canTrustSummaryPreflight = auth.can_auto_use === true;
|
|
298
|
+
const runtime = runtimeSnapshot || buildRuntimeSnapshot(projectRoot);
|
|
344
299
|
|
|
345
300
|
return {
|
|
346
301
|
schema_version: 1,
|
|
@@ -356,13 +311,6 @@ function buildBuilderPath(login, projectRoot, manifest, env = process.env, runti
|
|
|
356
311
|
auth,
|
|
357
312
|
runtime,
|
|
358
313
|
interactive_login: buildInteractiveLogin(auth, runtime),
|
|
359
|
-
communication: isQwenWorkWebRuntime(runtime) || (runtime.tool === 'qwenwork' && runtime.subtype === 'qwenwork_desktop') ? {
|
|
360
|
-
preparation_visibility: 'silent',
|
|
361
|
-
completion_content: 'business_result_and_business_link_only',
|
|
362
|
-
disclose_authentication_identity_environment: false,
|
|
363
|
-
report_preparation_only_when_user_action_is_required: true,
|
|
364
|
-
bundled_skill_path: path.resolve(__dirname, '../../yida-skills'),
|
|
365
|
-
} : undefined,
|
|
366
314
|
environment_check_simplification: {
|
|
367
315
|
contract_role: 'machine_readable_builder_runtime_not_yida_agent_business_logic',
|
|
368
316
|
minimal_probe_commands: [
|
|
@@ -484,7 +432,6 @@ function compactBuilderPath(builderPath) {
|
|
|
484
432
|
auth,
|
|
485
433
|
runtime: builderPath.runtime,
|
|
486
434
|
interactive_login: builderPath.interactive_login,
|
|
487
|
-
communication: builderPath.communication,
|
|
488
435
|
environment_check_simplification: {
|
|
489
436
|
minimal_probe_commands: environment.minimal_probe_commands,
|
|
490
437
|
can_skip_default_exploration_when_summary_ok: environment.can_skip_default_exploration_when_summary_ok,
|
|
@@ -654,9 +601,7 @@ function buildAgentCapabilitiesSummary() {
|
|
|
654
601
|
schema_version: 1,
|
|
655
602
|
name: 'openyida-agent-capabilities-summary',
|
|
656
603
|
version,
|
|
657
|
-
login:
|
|
658
|
-
? { status: loginStatus.status, can_auto_use: loginStatus.can_auto_use === true }
|
|
659
|
-
: compactLogin(loginStatus),
|
|
604
|
+
login: compactLogin(loginStatus),
|
|
660
605
|
workdir: projectRoot,
|
|
661
606
|
workdir_exists: !!projectResolution.exists,
|
|
662
607
|
workdir_source: projectResolution.source,
|
package/lib/core/cli-error.js
CHANGED
|
@@ -101,7 +101,8 @@ function shouldUseStructuredErrorOutput(error, args = []) {
|
|
|
101
101
|
&& details.mutationPerformed === false
|
|
102
102
|
);
|
|
103
103
|
const navigationOrderFailure = /^NAV_ORDER_/.test(error.code || '');
|
|
104
|
-
|
|
104
|
+
const permissionRequired = !!details && details.status === 'permission_required';
|
|
105
|
+
return ownedPartial || mutationOutcomeUnknown || deterministicPreflightFailure || navigationOrderFailure || permissionRequired;
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
module.exports = {
|
|
@@ -420,7 +420,6 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
420
420
|
'export-conversation',
|
|
421
421
|
'flash-to-prd',
|
|
422
422
|
'login',
|
|
423
|
-
'login-qwenwork',
|
|
424
423
|
'logout',
|
|
425
424
|
'sample',
|
|
426
425
|
'update',
|
|
@@ -588,7 +587,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
|
|
|
588
587
|
mutates_yida: false,
|
|
589
588
|
mutates_local: true,
|
|
590
589
|
read_actions: ['status', 'profiles', 'profile list'],
|
|
591
|
-
mutating_actions: ['login', 'refresh', 'logout', 'profile switch'
|
|
590
|
+
mutating_actions: ['login', 'refresh', 'logout', 'profile switch'],
|
|
592
591
|
})],
|
|
593
592
|
['batch', sideEffect('mixed', {
|
|
594
593
|
mutates_yida: true,
|
|
@@ -813,7 +812,6 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
813
812
|
|
|
814
813
|
...permissionEntries([
|
|
815
814
|
'login',
|
|
816
|
-
'login-qwenwork',
|
|
817
815
|
'logout',
|
|
818
816
|
'update',
|
|
819
817
|
], permission('allow', 'external', {
|
|
@@ -926,7 +924,7 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
926
924
|
...permissionEntries([
|
|
927
925
|
'auth',
|
|
928
926
|
], actionDependentPermission({
|
|
929
|
-
preauthorized_actions: ['login', 'refresh', 'logout', 'profile switch'
|
|
927
|
+
preauthorized_actions: ['login', 'refresh', 'logout', 'profile switch'],
|
|
930
928
|
})),
|
|
931
929
|
|
|
932
930
|
...permissionEntries([
|
|
@@ -1161,13 +1159,12 @@ const COMMAND_GROUPS = [
|
|
|
1161
1159
|
id: 'auth',
|
|
1162
1160
|
titleKey: 'help.group_auth',
|
|
1163
1161
|
commands: [
|
|
1164
|
-
command('login', ['login'], 'login [
|
|
1162
|
+
command('login', ['login'], 'login [target-url] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]', 'help.cmd_login', { managedRun: { scope: 'local', allowedFlags: ['--check-only', '--json', '--quiet'], requiredFlags: ['--check-only'] },
|
|
1165
1163
|
requiresLogin: false,
|
|
1166
1164
|
output: 'json',
|
|
1167
1165
|
}),
|
|
1168
|
-
command('login-qwenwork', ['login-qwenwork'], 'login-qwenwork --exchange-ticket <ticket> --ticket-context <json> [--json]', 'help.cmd_login_qwenwork', { managedRun: { scope: 'denied', reason: 'identity_exchange' }, requiresLogin: false, output: 'json', hidden: true }),
|
|
1169
1166
|
command('logout', ['logout'], 'logout', 'help.cmd_logout', { managedRun: { scope: 'denied', reason: 'scope_not_audited' }, requiresLogin: false }),
|
|
1170
|
-
command('auth', ['auth'], 'auth <status|login|refresh|logout|profiles|profile switch
|
|
1167
|
+
command('auth', ['auth'], 'auth <status|login|refresh|logout|profiles|profile switch>', 'help.cmd_auth', { managedRun: { scope: 'actions', actions: { status: { scope: 'local', allowedFlags: ['--json', '--quiet'] } } }, requiresLogin: false }),
|
|
1171
1168
|
command('org', ['org'], 'org <list|switch> [--json] [--corp-id <corpId>]', 'help.cmd_org', { managedRun: { scope: 'denied', reason: 'scope_not_audited' } }),
|
|
1172
1169
|
command('env', ['env'], 'env [--json|setup|list|show|switch|add|remove] [options]', 'help.cmd_env', { managedRun: { scope: 'local', allowedFlags: ['--json', '--quiet'] },
|
|
1173
1170
|
requiresLogin: false,
|
|
@@ -1292,7 +1289,7 @@ const COMMAND_GROUPS = [
|
|
|
1292
1289
|
output: 'json',
|
|
1293
1290
|
}),
|
|
1294
1291
|
command('create-app', ['create-app'], 'create-app "<name>"|--name <name> [options] [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_create_app', { managedRun: { scope: 'denied', reason: 'scope_not_audited' },
|
|
1295
|
-
notes: ['Successful results include appUrl/workbenchUrl for the business workspace and adminUrl for developer administration. Complete application delivery retains adminUrl regardless of auth runtime; route construction does not verify recipient access. Preserve the frontend URL from its own page result.'],
|
|
1292
|
+
notes: ['Successful results include appUrl/workbenchUrl for the business workspace and adminUrl for developer administration. Complete application delivery retains adminUrl regardless of auth runtime; route construction does not verify recipient access. Preserve the frontend URL from its own page result.', 'In QwenWork, permission failures return a nonzero exit and structured status=permission_required. YIDA_PLATFORM_ACCESS_REQUIRED and YIDA_APP_CREATE_PERMISSION_REQUIRED include details.permissionLabel/permissionUrl and nextAction.label/url; YIDA_PERMISSION_REQUIRED preserves an unclassified permission reason without inventing a link. Explain the missing permission, display the semantic label as a Markdown link to its paired URL (or reuse nextStep), and pause resource writes; do not automatically open it, send an application, switch spaces, log in again or retry creation. Resume only after the user requests continuation and the host identity, organization and permissions are verified.'],
|
|
1296
1293
|
}),
|
|
1297
1294
|
command('design-plan.catalog', ['design-plan', 'catalog'], 'design-plan catalog [--json]', 'help.cmd_design_plan_catalog', { managedRun: { scope: 'local' },
|
|
1298
1295
|
requiresLogin: false, output: 'json',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -4,21 +4,6 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
|
-
qwenwork: {
|
|
8
|
-
login_check_failed: 'Unable to verify the login session. Please retry.',
|
|
9
|
-
desktop_login_failed: 'QwenWork desktop login failed. Check the desktop identity and login handoff before retrying.',
|
|
10
|
-
desktop_login_usage: 'Usage: openyida login --qwenwork [--json] [--check-only]',
|
|
11
|
-
desktop_login_standalone: 'Run the recommended login command directly: {0}. No extra PATH or output setup is needed. After success continue the business operation silently; on failure follow the returned guidance.',
|
|
12
|
-
credential_missing: 'No cloud Agent credential is available. Run this command inside the QwenWork cloud execution environment.',
|
|
13
|
-
gateway_missing: 'QWENWORK_GATEWAY_URL or MULE_GATEWAY_URL is required.',
|
|
14
|
-
invalid_url: 'The QwenWork gateway must use HTTPS (HTTP is allowed only for loopback) and contain no credentials, query or fragment.',
|
|
15
|
-
request_failed: 'The QwenWork request failed. Check the stage and HTTP status in the diagnostic details.',
|
|
16
|
-
request_timeout: 'The QwenWork request timed out.',
|
|
17
|
-
invalid_response: 'QwenWork returned an invalid authentication or identity response.',
|
|
18
|
-
identity_mismatch: 'The returned QwenWork identity does not match the cloud execution context or exchanged credential.',
|
|
19
|
-
expired_token: 'QwenWork returned an expired user credential.',
|
|
20
|
-
usage: 'Usage: openyida auth qwenwork user-info [--json]',
|
|
21
|
-
},
|
|
22
7
|
print_template: {
|
|
23
8
|
'description': 'Manage native YiDA print templates, generate document layouts, back up, restore and request PDFs',
|
|
24
9
|
'error': 'Print template operation failed: {0}',
|
|
@@ -191,7 +176,6 @@ module.exports = {
|
|
|
191
176
|
alias: 'Alias:',
|
|
192
177
|
group_auth: 'Auth & Environment',
|
|
193
178
|
cmd_login: 'Login with OAuth token mode',
|
|
194
|
-
cmd_login_qwenwork: 'Log in to YiDA with a one-time QwenWork exchange ticket',
|
|
195
179
|
cmd_logout: 'Logout / unbind current project auth',
|
|
196
180
|
cmd_auth: 'Token login state and profile management',
|
|
197
181
|
cmd_agent: 'Connect a local Agent (Go runtime, development preview)',
|
|
@@ -520,10 +504,10 @@ Examples:
|
|
|
520
504
|
save_permission_usage: 'Usage: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>] [--package-type <FORM_PACKAGE_VIEW|FORM_PACKAGE_START>]',
|
|
521
505
|
save_permission_example: "Example: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
522
506
|
exec_failed: '\n❌ Execution failed: {0}',
|
|
523
|
-
login_usage: 'Usage: openyida login [
|
|
507
|
+
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
524
508
|
login_example: 'Examples:\n openyida login # Automatically open the browser via OAuth loopback login\n openyida login --no-browser # Let the caller handle the authorization URL\n openyida login --check-only --json # Check token auth status only\n openyida login --intl # Login against the international environment\n OPENYIDA_NO_BROWSER=1 openyida login # Suppress auto-opening the browser with an environment variable\n openyida auth login # Login alias',
|
|
525
509
|
login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login; env token mode reads only OPENYIDA_* tokens.',
|
|
526
|
-
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch
|
|
510
|
+
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
527
511
|
auth_example: 'Examples:\n openyida auth status # View login status\n openyida auth profiles # List existing login profiles\n openyida auth profile switch <auth_profile> # Switch current project to an existing profile\n openyida auth login # Add a profile when the target does not exist\n openyida auth refresh # Refresh login session\n openyida auth logout # Unbind current project auth\n openyida auth logout --profile <auth_profile> # Delete a shared profile explicitly',
|
|
528
512
|
org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
|
|
529
513
|
org_example: 'Examples:\n openyida org list --json # List accessible organizations\n openyida org switch --corp-id dingXXX # Switch existing profiles first; login to add one if missing',
|
|
@@ -1178,7 +1162,7 @@ Examples:
|
|
|
1178
1162
|
|
|
1179
1163
|
import_example2: ' openyida import ./yida-export.json "Quality Traceability System (Production)"',
|
|
1180
1164
|
exec_failed: '\n❌ Execution failed: {0}',
|
|
1181
|
-
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch
|
|
1165
|
+
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
1182
1166
|
auth_example: 'Examples:\n openyida auth status # View login status\n openyida auth profiles # List existing login profiles\n openyida auth profile switch <auth_profile> # Switch current project to an existing profile\n openyida auth login # Add a profile when the target does not exist\n openyida auth refresh # Refresh login session\n openyida auth logout # Unbind current project auth\n openyida auth logout --profile <auth_profile> # Delete a shared profile explicitly',
|
|
1183
1167
|
org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
|
|
1184
1168
|
org_example: 'Examples:\n openyida org list --json # List accessible organizations\n openyida org switch --corp-id dingXXX # Switch existing profiles first; login to add one if missing',
|
|
@@ -2609,3 +2593,12 @@ module.exports.feedback_github = {
|
|
|
2609
2593
|
};
|
|
2610
2594
|
|
|
2611
2595
|
module.exports.process_dynamic_actors = { invalid: 'Invalid process actor configuration. Check details.reason, selection range and actual member fields.' };
|
|
2596
|
+
|
|
2597
|
+
module.exports.permission_guidance = {
|
|
2598
|
+
platform_access_link: 'Request YiDA access',
|
|
2599
|
+
create_app_link: 'Request permission to create YiDA applications',
|
|
2600
|
+
platform_access: 'The current account is not authorized to use YiDA in this organization.',
|
|
2601
|
+
create_app: 'The current account cannot create applications in this organization.',
|
|
2602
|
+
unknown: 'Permission denied; the permission type is unconfirmed. Contact an administrator and pause this operation.',
|
|
2603
|
+
next_step: 'Open the permission request page: [{0}]({1}). After completing it, verify the current organization and permissions before continuing.',
|
|
2604
|
+
};
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -4,21 +4,6 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
|
-
qwenwork: {
|
|
8
|
-
login_check_failed: '登录状态暂时无法确认,请重试。',
|
|
9
|
-
desktop_login_failed: '千问办公桌面免登失败,请确认桌面登录身份和免登通道后重试。',
|
|
10
|
-
desktop_login_usage: '用法:openyida login --qwenwork [--json] [--check-only]',
|
|
11
|
-
desktop_login_standalone: '请直接执行推荐的登录命令:{0}。无需额外处理 PATH 和输出;成功后静默继续业务,失败时按提示修正调用。',
|
|
12
|
-
credential_missing: '未发现云端 Agent 凭证,请在千问办公云端执行环境中运行此命令。',
|
|
13
|
-
gateway_missing: '缺少 QWENWORK_GATEWAY_URL 或 MULE_GATEWAY_URL。',
|
|
14
|
-
invalid_url: '千问网关必须使用 HTTPS(仅回环地址允许 HTTP),且不能包含账号密码、查询参数或片段。',
|
|
15
|
-
request_failed: '千问接口请求失败,请查看诊断信息中的阶段和 HTTP 状态码。',
|
|
16
|
-
request_timeout: '千问接口请求超时。',
|
|
17
|
-
invalid_response: '千问返回的认证或身份响应格式不正确。',
|
|
18
|
-
identity_mismatch: '千问返回的用户身份与云端执行上下文或兑换凭证不一致。',
|
|
19
|
-
expired_token: '千问返回的用户凭证已过期。',
|
|
20
|
-
usage: '用法:openyida auth qwenwork user-info [--json]',
|
|
21
|
-
},
|
|
22
7
|
print_template: {
|
|
23
8
|
'description': '管理宜搭原生打印模板、按字段生成多种单据、备份恢复与生成PDF',
|
|
24
9
|
'error': '打印模板操作失败:{0}',
|
|
@@ -191,7 +176,6 @@ module.exports = {
|
|
|
191
176
|
alias: '别名:',
|
|
192
177
|
group_auth: '环境 & 认证',
|
|
193
178
|
cmd_login: '登录(OAuth token 模式)',
|
|
194
|
-
cmd_login_qwenwork: '使用千问办公的一次性兑换票登录宜搭',
|
|
195
179
|
cmd_logout: '退出登录 / 解绑当前项目登录态',
|
|
196
180
|
cmd_auth: 'token 登录态与 profile 管理',
|
|
197
181
|
cmd_agent: '连接本地 Agent(Go runtime,开发预览)',
|
|
@@ -520,7 +504,7 @@ openyida - 宜搭命令行工具
|
|
|
520
504
|
save_permission_usage: '用法: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>] [--package-type <FORM_PACKAGE_VIEW|FORM_PACKAGE_START>]',
|
|
521
505
|
save_permission_example: "示例: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
522
506
|
exec_failed: '\n❌ 执行失败: {0}',
|
|
523
|
-
login_usage: '
|
|
507
|
+
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
524
508
|
login_example: '示例:\n openyida login # 通过 OAuth loopback 自动打开浏览器登录\n openyida login --no-browser # 不自动打开浏览器,由调用方接管授权链接\n openyida login --check-only --json # 只检查 token 登录态\n openyida login --intl # 使用国际站环境登录\n OPENYIDA_NO_BROWSER=1 openyida login # 通过环境变量抑制自动打开浏览器\n openyida auth login # 登录入口别名',
|
|
525
509
|
login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth 登录;env token 模式下只读取 OPENYIDA_* token。',
|
|
526
510
|
first_run_title: ' 🤖 OpenYida - AI 问答模式已开启! ',
|
|
@@ -1150,7 +1134,7 @@ openyida - 宜搭命令行工具
|
|
|
1150
1134
|
// ── lib/import-app.js ──────────────────────────────
|
|
1151
1135
|
import_example2: ' openyida import ./yida-export.json "质量追溯系统(生产环境)"',
|
|
1152
1136
|
exec_failed: '\n❌ 执行失败: {0}',
|
|
1153
|
-
auth_usage: '用法: openyida auth <status|login|refresh|logout|profiles|profile switch
|
|
1137
|
+
auth_usage: '用法: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
1154
1138
|
auth_example: '示例:\n openyida auth status # 查看登录状态\n openyida auth profiles # 列出已有登录 profile\n openyida auth profile switch <auth_profile> # 切换当前项目到已有 profile\n openyida auth login # 目标 profile 不存在时登录新增\n openyida auth refresh # 刷新登录态\n openyida auth logout # 解绑当前项目登录态\n openyida auth logout --profile <auth_profile> # 删除指定共享 profile',
|
|
1155
1139
|
org_usage: '用法: openyida org <list|switch> [--json] [--corp-id <corpId>]',
|
|
1156
1140
|
org_example: '示例:\n openyida org list --json # 列出可访问的组织\n openyida org switch --corp-id dingXXX # 优先切换已有 profile;不存在时登录新增',
|
|
@@ -2563,3 +2547,12 @@ module.exports.feedback_github = {
|
|
|
2563
2547
|
};
|
|
2564
2548
|
|
|
2565
2549
|
module.exports.process_dynamic_actors = { invalid: '流程人员来源配置无效,请按 details.reason 检查来源、范围与真实成员字段。' };
|
|
2550
|
+
|
|
2551
|
+
module.exports.permission_guidance = {
|
|
2552
|
+
platform_access_link: '申请宜搭使用授权',
|
|
2553
|
+
create_app_link: '申请宜搭应用创建权限',
|
|
2554
|
+
platform_access: '当前没有此组织的宜搭使用权限,可申请宜搭使用授权。',
|
|
2555
|
+
create_app: '当前没有此组织的宜搭应用创建权限,可申请应用创建权限。',
|
|
2556
|
+
unknown: '权限不足,具体权限类型尚未确认。请联系管理员核实,暂不重试当前操作。',
|
|
2557
|
+
next_step: '请打开申请页面:[{0}]({1})。处理完成后确认当前组织和权限,再继续搭建。',
|
|
2558
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CliError } = require('./cli-error');
|
|
4
|
+
const { t } = require('./i18n');
|
|
5
|
+
const { redactString } = require('./redact');
|
|
6
|
+
|
|
7
|
+
const TYPES = { unauth: 'platform_access', noAppCreationAuth: 'create_app' };
|
|
8
|
+
const CODES = {
|
|
9
|
+
platform_access: 'YIDA_PLATFORM_ACCESS_REQUIRED',
|
|
10
|
+
create_app: 'YIDA_APP_CREATE_PERMISSION_REQUIRED',
|
|
11
|
+
unknown: 'YIDA_PERMISSION_REQUIRED',
|
|
12
|
+
};
|
|
13
|
+
const AUTH_CODES = new Set([
|
|
14
|
+
'not_logged_in', 'invalid_access_token', 'expired_access_token',
|
|
15
|
+
'access_token_expired', 'missing_access_token',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function normalizeMessage(value) {
|
|
19
|
+
return String(value || '').replace(/[\s,,。.!!]/g, '');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 仅接受当前宜搭域名的两类申请页;重建查询参数,避免传递凭据或任意回跳地址。
|
|
23
|
+
function resolvePermissionPage(location, baseUrl) {
|
|
24
|
+
if (!location || !baseUrl) { return null; }
|
|
25
|
+
try {
|
|
26
|
+
const base = new URL(baseUrl);
|
|
27
|
+
const target = new URL(location, base);
|
|
28
|
+
if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password
|
|
29
|
+
|| target.origin !== base.origin || target.username || target.password
|
|
30
|
+
|| target.pathname !== '/defaultPage.html') { return null; }
|
|
31
|
+
const values = target.searchParams.getAll('type');
|
|
32
|
+
if (values.length !== 1 || !Object.hasOwn(TYPES, values[0])) { return null; }
|
|
33
|
+
const url = new URL('/defaultPage.html', base);
|
|
34
|
+
url.searchParams.set('type', values[0]);
|
|
35
|
+
for (const key of ['corpid', 'appType']) {
|
|
36
|
+
const value = target.searchParams.get(key);
|
|
37
|
+
if (value && /^[A-Za-z0-9_-]{1,200}$/.test(value)) { url.searchParams.set(key, value); }
|
|
38
|
+
}
|
|
39
|
+
return { permissionType: TYPES[values[0]], url: url.href };
|
|
40
|
+
} catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createPermissionError(response, options = {}) {
|
|
44
|
+
if (options.tool !== 'qwenwork') { return null; }
|
|
45
|
+
let payload = response;
|
|
46
|
+
if (typeof payload === 'string') {
|
|
47
|
+
try { payload = JSON.parse(payload); } catch { payload = {}; }
|
|
48
|
+
}
|
|
49
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { payload = {}; }
|
|
50
|
+
const content = payload.content && typeof payload.content === 'object' && !Array.isArray(payload.content)
|
|
51
|
+
? payload.content : {};
|
|
52
|
+
const code = String(payload.errorCode || payload.code || content.errorCode || content.code || '');
|
|
53
|
+
const status = String(payload.status || content.status || '').toLowerCase();
|
|
54
|
+
const message = String(payload.errorMsg || payload.message || content.errorMsg || content.message || '');
|
|
55
|
+
const httpStatus = Number(options.httpStatus || payload.__httpStatus || 0);
|
|
56
|
+
// 明确的凭据失效继续由原有认证链路处理。
|
|
57
|
+
if (httpStatus === 401 || AUTH_CODES.has(code.toLowerCase()) || AUTH_CODES.has(status)
|
|
58
|
+
|| /not_logged_in|invalid_access_token|expired_access_token|access_token_expired|access token is invalid or expired/i.test(message)) { return null; }
|
|
59
|
+
const failed = payload.success === false || payload.__needLogin || httpStatus >= 300;
|
|
60
|
+
if (!failed) { return null; }
|
|
61
|
+
let page = resolvePermissionPage(options.location || payload.__location, options.baseUrl);
|
|
62
|
+
const legacyDenied = /^(?:TIANSHU_)?000006$/.test(code);
|
|
63
|
+
if (!page && legacyDenied) {
|
|
64
|
+
const normalized = normalizeMessage(message);
|
|
65
|
+
if (normalized === '已升级宜搭你未在授权范围内因此无法使用如要使用请联系管理员授权') {
|
|
66
|
+
page = resolvePermissionPage('/defaultPage.html?type=unauth', options.baseUrl);
|
|
67
|
+
} else if (normalized === '管理员暂未为您分配权限请联系管理员授权'
|
|
68
|
+
&& /^\/query\/app\/registerApp(?:\.json)?(?:\?|$)/.test(options.requestPath || '')) {
|
|
69
|
+
// 000006 被多个权限检查共用,只有已核实的创建接口及文案组合才可归类。
|
|
70
|
+
page = resolvePermissionPage('/defaultPage.html?type=noAppCreationAuth', options.baseUrl);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!page && !legacyDenied && httpStatus !== 403 && code !== '403' && status !== '403') { return null; }
|
|
74
|
+
const permissionType = page ? page.permissionType : 'unknown';
|
|
75
|
+
const label = page ? t(`permission_guidance.${permissionType}_link`) : '';
|
|
76
|
+
const details = {
|
|
77
|
+
status: 'permission_required', permissionType,
|
|
78
|
+
platformErrorCode: code || undefined, httpStatus: httpStatus || undefined,
|
|
79
|
+
platformErrorMessage: message ? redactString(message) : undefined,
|
|
80
|
+
retryable: false,
|
|
81
|
+
nextStep: page ? t('permission_guidance.next_step', label, page.url) : t('permission_guidance.unknown'),
|
|
82
|
+
};
|
|
83
|
+
if (page) {
|
|
84
|
+
details.permissionLabel = label;
|
|
85
|
+
details.permissionUrl = page.url;
|
|
86
|
+
details.nextAction = { type: 'open_url', label, url: page.url };
|
|
87
|
+
}
|
|
88
|
+
const displayMessage = page || !message ? t(`permission_guidance.${permissionType}`) : redactString(message);
|
|
89
|
+
return new CliError(displayMessage, {
|
|
90
|
+
code: CODES[permissionType], details,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isPermissionGuidanceError(error) {
|
|
95
|
+
return !!error && error.isCliError === true && Object.values(CODES).includes(error.code);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { createPermissionError, isPermissionGuidanceError };
|