openyida 2026.10.28-beta.2 → 2026.10.29

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 (43) hide show
  1. package/README.md +3 -23
  2. package/bin/yida.js +5 -20
  3. package/lib/app/create-app.js +2 -0
  4. package/lib/app/import-app.js +3 -0
  5. package/lib/auth/token-auth.js +1 -12
  6. package/lib/auth/token-store.js +0 -1
  7. package/lib/core/agent-capabilities.js +8 -63
  8. package/lib/core/cli-error.js +2 -1
  9. package/lib/core/command-manifest.js +62 -12
  10. package/lib/core/locales/en.js +13 -19
  11. package/lib/core/locales/zh.js +12 -18
  12. package/lib/core/permission-guidance.js +98 -0
  13. package/lib/core/utils.js +28 -2
  14. package/lib/plugin/plugin-lifecycle.js +379 -0
  15. package/lib/plugin/runtime-registry.js +58 -0
  16. package/lib/solution-template/solution-template.js +8 -0
  17. package/package.json +5 -14
  18. package/yida-skills/SKILL.md +5 -14
  19. package/yida-skills/references/setup-and-env.md +1 -10
  20. package/yida-skills/skills/yida-app/SKILL.md +2 -2
  21. package/yida-skills/skills/yida-app/references/common-issues.md +1 -1
  22. package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +1 -0
  23. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +19 -20
  24. package/yida-skills/skills/yida-create-app/SKILL.md +9 -3
  25. package/yida-skills/skills/yida-create-form-page/SKILL.md +2 -0
  26. package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +1 -1
  27. package/yida-skills/skills/yida-login/SKILL.md +1 -24
  28. package/yida-skills/skills/yida-plugin-frontend-deploy/SKILL.md +38 -0
  29. package/yida-skills/skills/yida-plugin-frontend-deploy/references/frontend-contract.md +72 -0
  30. package/yida-skills/skills/yida-plugin-frontend-deploy/references/frontend-hosting.md +65 -0
  31. package/yida-skills/skills/yida-plugin-lifecycle/SKILL.md +107 -0
  32. package/yida-skills/skills/yida-plugin-lifecycle/references/development-guide.md +74 -0
  33. package/yida-skills/skills-index.json +31 -1
  34. package/bin/yida-beta.js +0 -5
  35. package/lib/auth/qwenwork-client.js +0 -190
  36. package/lib/auth/qwenwork-cloud.js +0 -93
  37. package/lib/auth/qwenwork-desktop.js +0 -154
  38. package/lib/auth/qwenwork-login.js +0 -108
  39. package/lib/auth/qwenwork.js +0 -37
  40. package/lib/core/qwenwork-login-guidance.js +0 -35
  41. package/scripts/install-qwenwork-pre-beta.js +0 -40
  42. package/yida-skills/references/qwenwork-cloud-login.md +0 -21
  43. 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 [--qwenwork] [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]` | Login with OAuth token mode |
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\|qwenwork user-info> [--json]` | Token login state and profile management |
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
 
@@ -491,6 +470,7 @@ When preparing a PR that needs a personal Fork, or when you ask how to support t
491
470
  | `openyida app-settings <appType> [--section theme\|basic\|data-card\|cool-card] [--json]` | Read theme, frontend/backend, native data card and cool card management links |
492
471
  | `openyida cool-card <list\|templates\|create\|get> <appType> [cardPageCode] [--type im\|onebox] [--name <title>] [--page <n>] [--size <n>] [--search <text>] [--out <file>] [--json]` | Create and read native YiDA cool cards (interactive and pinned) |
493
472
  | `openyida data-card <list\|create\|get\|save\|publish\|probe\|release-get\|release-save\|release-check\|release-test\|refs> <appType> [cardCode] [--name <title>] [--description <text>] [--schema <file>\|--config <file>] [--expected-hash <hash>] [--backup <file>] [--out <file>] [--published\|--version <version>] [--page <n>] [--size <n>] [--search <text>] [--allow-send] [--json]` | Create, configure, publish and distribute native YiDA data cards |
473
+ | `openyida plugin <dev-list\|get\|version-list\|version-get\|extensions\|installed-list\|installed-get\|runtime-list\|probe\|create\|create-version\|save-dev\|configure\|enable\|disable\|upgrade\|uninstall> [pluginId] [version] [options]` | Manage YiDA platform plugin versions, installation state, and runtime registrations |
494
474
  | `openyida app-entry get <appType> [--json]` | Read frontend and management runtime entries |
495
475
  | `openyida app-entry set <appType> [--frontend <url>\|--frontend-page <formUuid>] [--management <url>\|--management-page <formUuid>] [--clear-frontend] [--clear-management] [--json]` | Register application entries with partial update and readback |
496
476
  | `openyida app-list [--type managed\|created] [--page N] [--size N]` | Page through apps I manage or created |
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');
@@ -795,6 +775,11 @@ async function main() {
795
775
  await run(args);
796
776
  break;
797
777
  }
778
+ case 'plugin': {
779
+ const { run } = require('../lib/plugin/plugin-lifecycle');
780
+ await run(args);
781
+ break;
782
+ }
798
783
 
799
784
  case 'app-settings': {
800
785
  const { run } = require('../lib/app/app-settings');
@@ -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
 
@@ -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,
@@ -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
- // 桌面会话沿用首次换票的可信 origin;刷新不受后续全局环境切换影响。
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 {
@@ -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, openyidaLocalBeta: localBeta } = require('../../package.json');
6
+ const { version } = require('../../package.json');
7
7
  const { t } = require('./i18n');
8
- const { buildCommandManifest } = require('./command-manifest');
8
+ const { buildCommandManifest, buildEntryDeliveryPolicy } = 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, runtime = null) {
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 runtime = runtimeSnapshot || buildRuntimeSnapshot(projectRoot);
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,
@@ -550,7 +497,7 @@ function buildApplicationEntryPolicy(builderPath, env = process.env) {
550
497
  return {
551
498
  schema_version: 1,
552
499
  environment: injectedAuthUsable ? 'managed_cloud_agent' : 'non_cloud_agent',
553
- delivery_unit: 'single_application_entry_group',
500
+ ...buildEntryDeliveryPolicy(),
554
501
  resource_delivery: 'summary_only',
555
502
  internal_artifact_delivery: 'never',
556
503
  frontend_delivery: 'Infer standalone for a scoped service frontend, persist page navigation hiding and return the verified publish/get-form-config standaloneUrl. No extra flag or navigation URL parameter. Missing verification is incomplete delivery; never silently omit the frontend or replace it with workbench/admin.',
@@ -654,9 +601,7 @@ function buildAgentCapabilitiesSummary() {
654
601
  schema_version: 1,
655
602
  name: 'openyida-agent-capabilities-summary',
656
603
  version,
657
- login: ['qwenwork_desktop_exchange_ticket', 'qwenwork_cloud_exchange'].includes(builderPath.auth.auth_runtime)
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,
@@ -756,7 +701,7 @@ function buildAgentCapabilities() {
756
701
  ],
757
702
  retry_policy: 'Do not repeat the same failed command without changing login state, organization, parameters, files, or field IDs.',
758
703
  completion_contracts: {
759
- full_app: 'Default full-app build is complete after creating or reusing the app, core forms, primary page, publishing it, and returning one named application entry group. Summarize business resources in prose; do not deliver one artifact or link card per resource.',
704
+ full_app: 'Default full-app build is complete after creating or reusing the app, core forms, primary page, publishing it, and returning up to three verified application entry URLs, with runtime access and developer admin included by default. Summarize business resources in prose; do not deliver one artifact or link card per resource.',
760
705
  },
761
706
  full_app_data_contract: 'Default full-app page code must not call this.dataSourceMap.* unless the same run created and bound a designer data source; use this.utils.yida.* or an entry-only page by default.',
762
707
  },
@@ -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
- return ownedPartial || mutationOutcomeUnknown || deterministicPreflightFailure || navigationOrderFailure;
104
+ const permissionRequired = !!details && details.status === 'permission_required';
105
+ return ownedPartial || mutationOutcomeUnknown || deterministicPreflightFailure || navigationOrderFailure || permissionRequired;
105
106
  }
106
107
 
107
108
  module.exports = {
@@ -10,6 +10,29 @@ const { DIVIDER_TYPES, DEFAULT_DIVIDER_TYPE } = require('../app/form-field-valid
10
10
  const { FORMULA_COMMANDS } = require('../formula/cli-contract');
11
11
  const { printTemplateCommand, printTemplateEffects, printTemplatePermission } = require('../print-template/contract');
12
12
 
13
+ /** 应用交付只约定入口内容;展示方式由宿主负责。 */
14
+ function buildEntryDeliveryPolicy() {
15
+ return {
16
+ delivery_unit: 'application_entries',
17
+ presentation: 'host_managed',
18
+ entry_delivery: {
19
+ max_count: 3,
20
+ deduplicate_by: 'verified_url',
21
+ order: ['frontend', 'management', 'admin'],
22
+ by_scope: {
23
+ 'service-management': ['frontend', 'management', 'admin'],
24
+ 'frontend-only': ['frontend', 'admin'],
25
+ 'backend-only': ['management', 'admin'],
26
+ unified: ['management', 'admin'],
27
+ },
28
+ explicit_user_exclusions: 'take_precedence',
29
+ resource_only: 'current_resource_only',
30
+ missing_entry: 'report_incomplete_without_guessing',
31
+ },
32
+ };
33
+ }
34
+
35
+
13
36
  const SIDE_EFFECT_BASES = Object.freeze({
14
37
  local_read: Object.freeze({
15
38
  kind: 'local_read',
@@ -420,7 +443,6 @@ const COMMAND_SIDE_EFFECTS = new Map([
420
443
  'export-conversation',
421
444
  'flash-to-prd',
422
445
  'login',
423
- 'login-qwenwork',
424
446
  'logout',
425
447
  'sample',
426
448
  'update',
@@ -565,6 +587,13 @@ const COMMAND_SIDE_EFFECTS = new Map([
565
587
  note: 'Direct enable writes a durable local request receipt before the one-shot platform copy. AI build only validates the template and returns the native builder handoff.',
566
588
  })],
567
589
  ['data-card', sideEffect('mixed', { mutates_yida: true, mutates_local: true, read_actions: ['list', 'get', 'probe', 'release-check', 'refs'], mutating_actions: ['create', 'save', 'publish', 'release-get', 'release-save', 'release-test'], note: 'release-get reads broadcast settings but the native backend may initialize the application cool app; classify it conservatively as a possible mutation.' })],
590
+ ['plugin', sideEffect('mixed', {
591
+ mutates_yida: true,
592
+ mutates_local: true,
593
+ read_actions: ['dev-list', 'get', 'version-list', 'version-get', 'extensions', 'installed-list', 'installed-get', 'runtime-list', 'probe'],
594
+ mutating_actions: ['create', 'create-version', 'save-dev', 'configure', 'enable', 'disable', 'upgrade', 'uninstall'],
595
+ note: 'Organization-level YiDA plugin lifecycle. Writes require --confirm and deterministic readback; save-dev also writes a local backup.',
596
+ })],
568
597
  ['print-template', sideEffect('mixed', printTemplateEffects)],
569
598
  ['form-view', sideEffect('mixed', {
570
599
  mutates_yida: true,
@@ -588,7 +617,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
588
617
  mutates_yida: false,
589
618
  mutates_local: true,
590
619
  read_actions: ['status', 'profiles', 'profile list'],
591
- mutating_actions: ['login', 'refresh', 'logout', 'profile switch', 'qwenwork user-info'],
620
+ mutating_actions: ['login', 'refresh', 'logout', 'profile switch'],
592
621
  })],
593
622
  ['batch', sideEffect('mixed', {
594
623
  mutates_yida: true,
@@ -813,7 +842,6 @@ const COMMAND_PERMISSIONS = new Map([
813
842
 
814
843
  ...permissionEntries([
815
844
  'login',
816
- 'login-qwenwork',
817
845
  'logout',
818
846
  'update',
819
847
  ], permission('allow', 'external', {
@@ -866,6 +894,9 @@ const COMMAND_PERMISSIONS = new Map([
866
894
  ...permissionEntries(['cool-card'], actionDependentPermission({ preauthorized_actions: ['create'] })),
867
895
  ...permissionEntries(['solution-template'], actionDependentPermission({ ask_actions: ['enable'] })),
868
896
  ...permissionEntries(['data-card'], actionDependentPermission({ preauthorized_actions: ['create', 'save', 'publish', 'release-get'], ask_actions: ['release-save', 'release-test'] })),
897
+ ...permissionEntries(['plugin'], actionDependentPermission({
898
+ ask_actions: ['create', 'create-version', 'save-dev', 'configure', 'enable', 'disable', 'upgrade', 'uninstall'],
899
+ })),
869
900
  ...permissionEntries([
870
901
  'form-view',
871
902
  ], actionDependentPermission({
@@ -926,7 +957,7 @@ const COMMAND_PERMISSIONS = new Map([
926
957
  ...permissionEntries([
927
958
  'auth',
928
959
  ], actionDependentPermission({
929
- preauthorized_actions: ['login', 'refresh', 'logout', 'profile switch', 'qwenwork user-info'],
960
+ preauthorized_actions: ['login', 'refresh', 'logout', 'profile switch'],
930
961
  })),
931
962
 
932
963
  ...permissionEntries([
@@ -1161,13 +1192,12 @@ const COMMAND_GROUPS = [
1161
1192
  id: 'auth',
1162
1193
  titleKey: 'help.group_auth',
1163
1194
  commands: [
1164
- command('login', ['login'], 'login [--qwenwork] [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'] },
1195
+ 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
1196
  requiresLogin: false,
1166
1197
  output: 'json',
1167
1198
  }),
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
1199
  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|qwenwork user-info> [--json]', 'help.cmd_auth', { managedRun: { scope: 'actions', actions: { status: { scope: 'local', allowedFlags: ['--json', '--quiet'] }, qwenwork: { scope: 'denied', reason: 'identity_exchange' } } }, requiresLogin: false }),
1200
+ 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
1201
  command('org', ['org'], 'org <list|switch> [--json] [--corp-id <corpId>]', 'help.cmd_org', { managedRun: { scope: 'denied', reason: 'scope_not_audited' } }),
1172
1202
  command('env', ['env'], 'env [--json|setup|list|show|switch|add|remove] [options]', 'help.cmd_env', { managedRun: { scope: 'local', allowedFlags: ['--json', '--quiet'] },
1173
1203
  requiresLogin: false,
@@ -1240,6 +1270,25 @@ const COMMAND_GROUPS = [
1240
1270
  examples: ['openyida data-card create APP_XXX --name "Sales summary" --json', 'openyida data-card get APP_XXX CARD-XXX --out card-schema.json --json'],
1241
1271
  notes: ['Native dataCardRpc resources. Agent use requires an explicit user choice of native YiDA data cards or continuation of an already explicit native-card task; generic cards, KPIs, dashboards or broadcasts do not trigger this capability. Metric cards rendered by custom page code belong to the page; an embedded CARD reference alone does not authorize changes to its native source. get defaults to the draft; --published reads the online schema. --out exports the complete schema without overwriting a file. save accepts either a complete exported --schema or a simplified --config with charts and optional select filters (legacy indicators remains compatible), appending verified native components. Supports indicator/bar/line/pie/table/combo/gauge/funnel/calendarheatmap with real form fields, aggregates and date grain. Six-column layout, full-width tables and optional gauge min/max ranges. Five chart maximum; filters link only within the same source. probe performs read-only CARD runtime queries and returns component data; a saved schema alone is not runtime evidence. Numeric fields support COUNT/SUM/AVG/MIN/MAX; text and serial fields support COUNT. Config generation preserves the existing page and rejects unsupported keys/types. save requires its original --expected-hash; backs up and rereads before writing, then verifies readback. publish requires the current draft hash, backs up, and verifies the published schema, version and state; it does not save unsent local edits. Hash checks are not atomic server locking. New i18n bindings are synchronized before saving. Writes are not automatically replayed. Preserve platform permission and entitlement errors; never alter quotas or app tags. release-get exports broadcast configuration and releaseHash; the native GET may initialize a cool app and is conservatively classified as a possible mutation; release-save requires its original --expected-hash, backup and verified readback. Enabled schedules require explicit --allow-send; release-test also requires --allow-send and only requests a test to the current operator. release-check checks published state and broadcast entitlement; refs queries portal usage records. Group delivery still requires installation in DingTalk. Successful save or test acceptance does not prove delivery. The CLI does not mount portal components or delete cards. Mutation receipts refresh the app using the existing update_app_setting operation.'],
1242
1272
  }),
1273
+ command('plugin', ['plugin'], 'plugin <dev-list|get|version-list|version-get|extensions|installed-list|installed-get|runtime-list|probe|create|create-version|save-dev|configure|enable|disable|upgrade|uninstall> [pluginId] [version] [options]', 'plugin_lifecycle.description', {
1274
+ managedRun: { scope: 'denied', reason: 'organization_level_plugin_lifecycle' },
1275
+ output: 'json',
1276
+ args: [
1277
+ { name: 'action', type: 'string', source: 'positional', position: 0, required: true },
1278
+ { name: 'pluginId', type: 'string', source: 'positional', position: 1 },
1279
+ { name: 'version', type: 'string', source: 'positional', position: 2 },
1280
+ { name: 'config', type: 'file', source: 'option', builder_options: ['--config'] },
1281
+ { name: 'expectedHash', type: 'string', source: 'option', builder_options: ['--expected-hash'] },
1282
+ { name: 'backup', type: 'file', source: 'option', builder_options: ['--backup'] },
1283
+ { name: 'extensions', type: 'string', source: 'option', builder_options: ['--extensions'] },
1284
+ { name: 'checkResource', type: 'boolean', source: 'option', builder_options: ['--check-resource'] },
1285
+ { name: 'type', type: 'string', source: 'option', builder_options: ['--type'], description: 'create only: connector (default) or other; custom-page extensions use other.' },
1286
+ { name: 'confirm', type: 'boolean', source: 'option', builder_options: ['--confirm'] },
1287
+ { name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
1288
+ ],
1289
+ examples: ['openyida plugin dev-list --search AI --json', 'openyida plugin runtime-list --extensions yida.app.custom-page.component --json', 'openyida plugin create --type other --name Timeline --description "Custom page timeline" --logo https://example.com/logo.png --confirm --json'],
1290
+ notes: ['YiDA platform plugins, not npm packages or Codex plugins. Read commands separate persisted developer configuration, static resource reachability, runtime registry candidates, enabled state, and actual consumer execution. runtime-list includes disabled candidates and does not prove that an application consumed one. probe --check-resource sends a read-only HEAD request to the resolved HTTPS entry. save-dev and configure require the original hash and a non-overwriting backup; save-dev validates extension IDs and never publishes. Organization-wide writes require --confirm and readback. disable follows platform semantics: it blocks new selection but may not unload existing uses. The CLI intentionally does not publish/release/delete developer plugins or install from the market.'],
1291
+ }),
1243
1292
  command('app-entry.get', ['app-entry', 'get'], 'app-entry get <appType> [--json]', 'app_entry.desc_get', { managedRun: {'scope':'app','appPosition':1},
1244
1293
  args: [{ name: 'appType', type: 'string', source: 'positional', position: 0, required: true },
1245
1294
  { name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] }],
@@ -1292,7 +1341,7 @@ const COMMAND_GROUPS = [
1292
1341
  output: 'json',
1293
1342
  }),
1294
1343
  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.'],
1344
+ 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
1345
  }),
1297
1346
  command('design-plan.catalog', ['design-plan', 'catalog'], 'design-plan catalog [--json]', 'help.cmd_design_plan_catalog', { managedRun: { scope: 'local' },
1298
1347
  requiresLogin: false, output: 'json',
@@ -2367,7 +2416,7 @@ function summarizeLocalizedCommands(commands) {
2367
2416
  product_design_policy: 'yida-requirement-analysis owns shared facts and first-time intake. yida-prd owns business planning; yida-design owns visual design. Reuse the same confirmed brief and supplied details. Prepare business and base visuals concurrently, then bind visuals to settled page tasks. yida-app merges and checks the artifacts before creating resources. Plan authors scope, data and rules, page organization, business interactions and acceptance once. Ordered blocks {name,purpose} supply default priority and content layers; explicit designs remain authoritative. Project-specific acceptance criteria are merged with generated resource checks. Plan uses the compact authoring contract and selected theme context; the CLI reads full templates and renders all artifacts. Fast, Plan and single-page design share yida-design/templates/design-themes and visual_decision_policy: infer directly from supplied style intent or extracted reference DNA, and use the shared three-direction rule only when comparison is needed. Theme templates declare application-global variables from basic-tokens.json with fixed typography, spacing and Tooltip colors, plus scoped custom-page variables; component recipes consume those variables. Each selected template is the authority for navTheme, its six navigation color tokens, and its selected-item shadow token; free-creative authors its own navigation tone and tokens. Explicit project customization stays in visualStyle.tokens. The CLI declares each fixed application palette once in a shared :root and platform-mode selector block. The CLI preserves mode-independent navigation appearance tokens. Light, dark, white and gray mode classes consume the same project tokens; navTheme remains platform metadata, not a second palette. Legacy generated mode blocks are consolidated on update, while unrelated custom CSS is preserved.',
2368
2417
  ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, state styling, and page-level imageNeed. When a page is imageNeed=required, or beneficial with declared image slots, yida-image-assets prepares a traceable asset manifest alongside page layout and interaction work; wait for final images only at that page\'s image binding and acceptance. imageNeed=none skips the branch. prd.md and design.md remain the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
2369
2418
  default_nav_order_policy: 'For application-wide custom navigation, implement each entry menu and verify app/page navigation hiding. A standalone frontend preserves platform navigation for the management workspace; frontend-only delivery skips platform ordering. After all scoped resources and page publications finish, perform exactly one navigation order operation. Use management tasks and their default destination, without creating a homepage or putting a frontend page first. When execution.entryRecommendation exists, derive navigationOrder from its management/workspace menus. Call openyida nav-group order <appType> <items...> for an explicit order; otherwise call openyida nav-group auto-order <appType> once for legacy single-workspace plans. Do not use openyida publish ... --auto-nav-order for per-page application builds. Explicit and automatic ordering are mutually exclusive; never generate per-item move loops.',
2370
- final_link_policy: 'Return exactly one user-visible application entry group, never one artifact or link card per form, process, report, schema, manifest, PRD, design file, or intermediate artifact. Include one verified management entry when a management or unified workspace is in scope: the app workbench URL or a planned workbench/formUuid URL with its real viewUuid. Backend-only returns its verified business backend entry and developer admin URL without adding a frontend. Native management does not require a display homepage; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. For scoped service frontend pages, infer standalone when entryMode is missing; preserve an explicit platform-shell choice and its verified workbenchUrl. Add the clean custom page URL only for standalone pages when get-form-config readback confirms renderNav=false (fallback to isRenderNav only when renderNav is absent). Complete application delivery defaults to 2-3 distinct entries: frontend or unified workspace, business backend when separately planned, and developer admin. agent-capabilities application_entry_policy.entries.admin=include is independent of auth runtime. Read adminUrl from create-app or app-list results; route construction does not verify recipient permissions. Explicit user exclusions take precedence; single-page tasks retain their scope. Put the business summary, verification results and remaining work in the terminal delivery artifact description; when no delivery tool is available, return the same content and entry group in the final response. Summarize business resources by capability or count, but every resource count, seed-record count, and completed/verified claim must come from that resource\'s own successful result or readback. When the user or caller explicitly requests a resource manifest, resource UUIDs/IDs, publish status, or test-data summary, include one concise verified delivery manifest in that description (or the final response when no delivery tool is available).',
2419
+ final_link_policy: 'Return up to three verified application entry URLs in frontend, business management, developer admin order; default complete delivery includes runtime access and developer admin. Never emit one artifact or link card per form, process, report, schema, manifest, PRD, design file, or intermediate artifact. Include one verified management entry when a management or unified workspace is in scope: the app workbench URL or a planned workbench/formUuid URL with its real viewUuid. Backend-only returns its verified business backend entry and developer admin URL without adding a frontend. Native management does not require a display homepage; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. For scoped service frontend pages, infer standalone when entryMode is missing; preserve an explicit platform-shell choice and its verified workbenchUrl. Add the clean custom page URL only for standalone pages when get-form-config readback confirms renderNav=false (fallback to isRenderNav only when renderNav is absent). agent-capabilities application_entry_policy.entries.admin=include is independent of auth runtime. Read adminUrl from create-app or app-list results; route construction does not verify recipient permissions. Explicit user exclusions take precedence; single-page tasks retain their scope. QwenWork owns link, file and result presentation; return the verified summary and URLs without prescribing styles, card counts, display tools or update identifiers. Other hosts may use their terminal delivery artifact description; return the same concise content when no delivery tool is available. Summarize business resources by capability or count, but every resource count, seed-record count, and completed/verified claim must come from that resource\'s own successful result or readback. When the user or caller explicitly requests a resource manifest, resource UUIDs/IDs, publish status, or test-data summary, include one concise verified delivery manifest in that description (or the final response when no delivery tool is available).',
2371
2420
  entry_navigation_contract: {
2372
2421
  reference: 'yida-skills/skills/yida-app/references/entry-navigation.md',
2373
2422
  brief_path: 'entryRecommendation',
@@ -2410,10 +2459,10 @@ function summarizeLocalizedCommands(commands) {
2410
2459
  },
2411
2460
  },
2412
2461
  application_entry_policy: {
2413
- delivery_unit: 'single_application_entry_group',
2462
+ ...buildEntryDeliveryPolicy(),
2414
2463
  persistence: {
2415
2464
  command_ids: ['app-entry.get', 'app-entry.set'],
2416
- policy: 'Before final complete-application delivery, registration and readback of all verified in-scope business runtime entries is required by default: service-management writes both, frontend-only writes frontend, backend-only/unified writes management. Resolve each planned default resource/view independently; never reuse the last published page or substitute a root workbench for a task URL. Use readback urls.frontend/management for final cards. Existing identical entries can reuse the readback; set performs partial merge and readback. Missing verification or failed persistence is incomplete delivery, not a reason to emit guessed links. Respect explicit no-write constraints. Do not register admin/design URLs. Incremental resource-only work preserves existing entries. Clear only on explicit removal. Conflicts require reread and review, never blind retries.',
2465
+ policy: 'Before final complete-application delivery, registration and readback of all verified in-scope business runtime entries is required by default: service-management writes both, frontend-only writes frontend, backend-only/unified writes management. Resolve each planned default resource/view independently; never reuse the last published page or substitute a root workbench for a task URL. Use readback urls.frontend/management for final entry URLs. Existing identical entries can reuse the readback; set performs partial merge and readback. Missing verification or failed persistence is incomplete delivery, not a reason to emit guessed links. Respect explicit no-write constraints. Do not register admin/design URLs. Incremental resource-only work preserves existing entries. Clear only on explicit removal. Conflicts require reread and review, never blind retries.',
2417
2466
  },
2418
2467
  workbench: {
2419
2468
  include: 'when_workspace_in_scope',
@@ -2465,7 +2514,7 @@ function summarizeLocalizedCommands(commands) {
2465
2514
  'yida-data-source-connectors',
2466
2515
  'yida-dashboard',
2467
2516
  ],
2468
- completion_contract: 'Clarify core functions and actual usage first, then resolve the required resource context and settle entry navigation before PRD planning. Fast generates prd.md and design.md in parallel; Plan uses design-plan init/preview/materialize/patch and confirms the current revision. Validate both artifacts, create or reuse app and core forms/processes, seed and query-check core normal-form samples or record an allowed skip reason, then implement the primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order for platform navigation, or verify app and per-page hiding for application-wide custom navigation. For a standalone frontend in a platform-navigation app, verify that page independently and retain workspace navigation. Before returning one named application entry group, persist and read back the verified in-scope frontend/management access addresses through app-entry get/set; final business links come from urls.frontend/management, not the last publish result. Include workbench when a management or unified workspace is in scope; backend-only delivers its business backend and developer admin URL without adding a frontend; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. Always include developer adminUrl for complete application delivery unless explicitly excluded by the user, regardless of auth runtime; this is separate from the optional business backend. Use create-app or app-list URL fields and state any unavailable entry instead of silently omitting it. Put the verified business summary, results and remaining work in the delivery artifact description; use the final response when no delivery tool is available.',
2517
+ completion_contract: 'Clarify core functions and actual usage first, then resolve the required resource context and settle entry navigation before PRD planning. Fast generates prd.md and design.md in parallel; Plan uses design-plan init/preview/materialize/patch and confirms the current revision. Validate both artifacts, create or reuse app and core forms/processes, seed and query-check core normal-form samples or record an allowed skip reason, then implement the primary page in PRD resource order, publish it, apply PRD navigation order or lightweight fallback navigation order for platform navigation, or verify app and per-page hiding for application-wide custom navigation. For a standalone frontend in a platform-navigation app, verify that page independently and retain workspace navigation. Before returning up to three verified application entry URLs, persist and read back the verified in-scope frontend/management access addresses through app-entry get/set; final business links come from urls.frontend/management, not the last publish result. Include workbench when a management or unified workspace is in scope; backend-only delivers its business backend and developer admin URL without adding a frontend; frontend-only delivery includes its verified frontend entry and developer admin URL, without inventing a business backend. Always include developer adminUrl for complete application delivery unless explicitly excluded by the user, regardless of auth runtime; this is separate from the optional business backend. Use create-app or app-list URL fields and state any unavailable entry instead of silently omitting it. Put the verified business summary, results and remaining work in the delivery artifact description; use the final response when no delivery tool is available.',
2469
2518
  },
2470
2519
  },
2471
2520
  };
@@ -2501,6 +2550,7 @@ function buildCommandManifest(options = {}) {
2501
2550
  module.exports = {
2502
2551
  COMMAND_GROUPS,
2503
2552
  buildCommandManifest,
2553
+ buildEntryDeliveryPolicy,
2504
2554
  findCommandSuggestion,
2505
2555
  findForbiddenAliasSuggestion,
2506
2556
  flattenCommandManifest,
@@ -4,21 +4,7 @@
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
- },
7
+ plugin_lifecycle: { description: 'Manage YiDA platform plugin versions, installation state, and runtime registrations', failed: 'Plugin operation stopped: {0}', usage: 'Usage: openyida plugin <dev-list|get|version-list|version-get|extensions|installed-list|installed-get|runtime-list|probe|create|create-version|save-dev|configure|enable|disable|upgrade|uninstall> [pluginId] [version] [options]; create accepts --type connector|other; use other for custom-page extensions' },
22
8
  print_template: {
23
9
  'description': 'Manage native YiDA print templates, generate document layouts, back up, restore and request PDFs',
24
10
  'error': 'Print template operation failed: {0}',
@@ -191,7 +177,6 @@ module.exports = {
191
177
  alias: 'Alias:',
192
178
  group_auth: 'Auth & Environment',
193
179
  cmd_login: 'Login with OAuth token mode',
194
- cmd_login_qwenwork: 'Log in to YiDA with a one-time QwenWork exchange ticket',
195
180
  cmd_logout: 'Logout / unbind current project auth',
196
181
  cmd_auth: 'Token login state and profile management',
197
182
  cmd_agent: 'Connect a local Agent (Go runtime, development preview)',
@@ -520,10 +505,10 @@ Examples:
520
505
  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
506
  save_permission_example: "Example: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
522
507
  exec_failed: '\n❌ Execution failed: {0}',
523
- login_usage: 'Usage: openyida login [--qwenwork] [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
508
+ login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
524
509
  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
510
  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|qwenwork user-info> [--json]',
511
+ auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
527
512
  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
513
  org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
529
514
  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 +1163,7 @@ Examples:
1178
1163
 
1179
1164
  import_example2: ' openyida import ./yida-export.json "Quality Traceability System (Production)"',
1180
1165
  exec_failed: '\n❌ Execution failed: {0}',
1181
- auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch|qwenwork user-info> [--json]',
1166
+ auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
1182
1167
  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
1168
  org_usage: 'Usage: openyida org <list|switch> [--json] [--corp-id <corpId>]',
1184
1169
  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 +2594,12 @@ module.exports.feedback_github = {
2609
2594
  };
2610
2595
 
2611
2596
  module.exports.process_dynamic_actors = { invalid: 'Invalid process actor configuration. Check details.reason, selection range and actual member fields.' };
2597
+
2598
+ module.exports.permission_guidance = {
2599
+ platform_access_link: 'Request YiDA access',
2600
+ create_app_link: 'Request permission to create YiDA applications',
2601
+ platform_access: 'The current account is not authorized to use YiDA in this organization.',
2602
+ create_app: 'The current account cannot create applications in this organization.',
2603
+ unknown: 'Permission denied; the permission type is unconfirmed. Contact an administrator and pause this operation.',
2604
+ next_step: 'Open the permission request page: [{0}]({1}). After completing it, verify the current organization and permissions before continuing.',
2605
+ };