openyida 2026.9.24-beta.0 → 2026.9.24
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 -9
- package/bin/yida.js +1 -15
- package/lib/app/application-entry-urls.js +27 -1
- package/lib/app/create-page.js +5 -9
- package/lib/app/get-form-config.js +6 -0
- package/lib/app/publish.js +20 -1
- package/lib/auth/token-auth.js +22 -75
- package/lib/core/agent-capabilities.js +1 -0
- package/lib/core/command-manifest.js +32 -4
- package/lib/core/locales/en.js +2 -14
- package/lib/core/locales/zh.js +2 -14
- package/lib/design-plan/entry-navigation.js +10 -1
- package/lib/design-plan/materialize.js +6 -4
- package/package.json +1 -1
- package/yida-skills/skills/yida-app/references/entry-navigation.md +2 -0
- package/yida-skills/skills/yida-app/workflow/step-6-main-page.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +3 -3
- package/yida-skills/skills/yida-create-form-page/SKILL.md +3 -1
- package/yida-skills/skills/yida-create-form-page/references/field-definition-guide.md +16 -0
- package/yida-skills/skills/yida-create-form-page/references/form-field-properties.md +7 -5
- package/yida-skills/skills/yida-create-page/SKILL.md +1 -1
- package/yida-skills/skills/yida-design/SKILL.md +3 -1
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +4 -2
- package/yida-skills/skills/yida-design/references/native-form-styles.md +58 -0
- package/yida-skills/skills/yida-design/references/navigation-decision.md +2 -0
- package/yida-skills/skills/yida-design/references/theme/app-custom-theme-template.css +2 -0
- package/yida-skills/skills/yida-design/references/theme/theme-token-presets.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/README.md +4 -2
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-amber/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-editorial/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-executive/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-finance/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-glass/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-graphite/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-line/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-neon/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-nordic/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-paper/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-platinum/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-plum/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-pop/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-sage/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-teal/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-terminal/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-ticket/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/app-wire/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/basic-tokens.json +1 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/dark-inset-hairline/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-signal-panels/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/free-creative/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/graphite-bevel-grid/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-shared-bands/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/hairline-soft-blocks/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/ink-glow-soft-panels/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/inset-frame-pixel-rhythm/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/app_theme.css +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/outlined-texture-duotone/design.md +3 -1
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-inset-surfaces/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-outline-rhythm/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-rail-bold-band/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/app_theme.css +4 -2
- package/yida-skills/skills/yida-design/templates/design-themes/soft-spectral-panels/design.md +4 -2
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-canvas-contrast-panels/design.md +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/app_theme.css +2 -0
- package/yida-skills/skills/yida-design/templates/design-themes/warm-rail-muted-panels/design.md +2 -0
- package/yida-skills/skills/yida-design/workflow/step-2-theme-system.md +3 -1
- package/yida-skills/skills/yida-page-config/SKILL.md +2 -2
- package/yida-skills/skills/yida-publish-page/SKILL.md +8 -1
- package/lib/auth/oauth-device.js +0 -311
package/README.md
CHANGED
|
@@ -74,17 +74,10 @@ openyida login
|
|
|
74
74
|
|
|
75
75
|
OpenYida login defaults to OAuth token mode. It opens the DingTalk OAuth authorization page, receives the local loopback callback, exchanges `code` / `authCode` with the Yida server, and stores `access_token` / `refresh_token` in the user auth store when available. If the user auth store is not writable, OpenYida explicitly falls back to the current project cache and reports that narrower persistence scope.
|
|
76
76
|
|
|
77
|
-
In a sandbox, headless shell, or CI dashboard where the loopback callback cannot complete, use the device authorization flow. Open the displayed verification URL on any device and enter the user code. `--env-hint` optionally pins the intended environment profile at authorization time:
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
openyida login --device
|
|
81
|
-
openyida login --device --env-hint pre
|
|
82
|
-
```
|
|
83
|
-
|
|
84
77
|
When the user names a target Yida entry URL, pass it to the login command so OpenYida can select the matching environment and auth profile. For example:
|
|
85
78
|
|
|
86
79
|
```bash
|
|
87
|
-
openyida login https
|
|
80
|
+
openyida login https://yida-group.alibaba-inc.com/
|
|
88
81
|
openyida login --alibaba
|
|
89
82
|
```
|
|
90
83
|
|
|
@@ -390,7 +383,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
390
383
|
|
|
391
384
|
| Command | Description |
|
|
392
385
|
|---------|-------------|
|
|
393
|
-
| `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]
|
|
386
|
+
| `openyida login [target-url] [--env <name>\|--intl\|--overseas\|--global\|--yidaapps\|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]` | Login with OAuth token mode |
|
|
394
387
|
| `openyida logout` | Logout / unbind current project auth |
|
|
395
388
|
| `openyida auth <status\|login\|refresh\|logout\|profiles\|profile switch>` | Token login state and profile management |
|
|
396
389
|
| `openyida org <list\|switch> [--json] [--corp-id <corpId>]` | Organization management (list / switch existing profiles first) |
|
package/bin/yida.js
CHANGED
|
@@ -193,7 +193,6 @@ function printLoginResult(result) {
|
|
|
193
193
|
|
|
194
194
|
if (!result) {
|
|
195
195
|
console.log(JSON.stringify({
|
|
196
|
-
type: 'login_result',
|
|
197
196
|
ok: false,
|
|
198
197
|
status: 'login_failed',
|
|
199
198
|
can_auto_use: false,
|
|
@@ -206,7 +205,6 @@ function printLoginResult(result) {
|
|
|
206
205
|
? !!result.ok
|
|
207
206
|
: (result.can_auto_use !== false && !!result.access_token);
|
|
208
207
|
const summary = {
|
|
209
|
-
type: 'login_result',
|
|
210
208
|
ok: tokenUsable,
|
|
211
209
|
status: result.status || (tokenUsable ? 'ok' : 'token_not_issued'),
|
|
212
210
|
auth_mode: 'token',
|
|
@@ -281,7 +279,6 @@ function getFirstPositionalArg(cliArgs, startIndex = 0) {
|
|
|
281
279
|
'--login-url',
|
|
282
280
|
'--profile',
|
|
283
281
|
'--user-id',
|
|
284
|
-
'--env-hint',
|
|
285
282
|
]);
|
|
286
283
|
for (let index = startIndex; index < cliArgs.length; index++) {
|
|
287
284
|
const arg = cliArgs[index];
|
|
@@ -389,7 +386,6 @@ function applyLoginEnvironmentFlags(cliArgs, options = {}) {
|
|
|
389
386
|
'--client-id',
|
|
390
387
|
'--profile',
|
|
391
388
|
'--user-id',
|
|
392
|
-
'--env-hint',
|
|
393
389
|
]);
|
|
394
390
|
const targetUrlFlags = new Set([
|
|
395
391
|
'--endpoint',
|
|
@@ -541,23 +537,13 @@ function printLoginHelp() {
|
|
|
541
537
|
}
|
|
542
538
|
|
|
543
539
|
function buildTokenLoginOptions(loginArgs) {
|
|
544
|
-
const device = loginArgs.includes('--device');
|
|
545
540
|
return {
|
|
546
541
|
clientId: getArgValue(loginArgs, '--client-id'),
|
|
547
542
|
corpId: getArgValue(loginArgs, '--corp-id'),
|
|
548
543
|
userId: getArgValue(loginArgs, '--user-id'),
|
|
549
544
|
authProfile: getArgValue(loginArgs, '--profile'),
|
|
550
545
|
quiet: process.env.YIDA_QUIET === '1' || loginArgs.includes('--quiet'),
|
|
551
|
-
noBrowser:
|
|
552
|
-
device,
|
|
553
|
-
envHint: getArgValue(loginArgs, '--env-hint'),
|
|
554
|
-
// Machine-readable progress events for agent runtimes (sandbox browsers,
|
|
555
|
-
// CI dashboards). Emits one JSON line per state transition.
|
|
556
|
-
onDeviceState: (state) => {
|
|
557
|
-
if (device) {
|
|
558
|
-
process.stdout.write(JSON.stringify({ type: 'device_login_state', ...state }) + '\n');
|
|
559
|
-
}
|
|
560
|
-
},
|
|
546
|
+
noBrowser: loginArgs.includes('--no-browser'),
|
|
561
547
|
};
|
|
562
548
|
}
|
|
563
549
|
|
|
@@ -28,4 +28,30 @@ function buildApplicationEntryUrls({ appType, baseUrl, systemLink }) {
|
|
|
28
28
|
return { appUrl: workbenchUrl, workbenchUrl, adminUrl: `${origin}/${appType}/admin${suffix}` };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
function readRenderNav(config) {
|
|
32
|
+
const value = config && Object.prototype.hasOwnProperty.call(config, 'renderNav')
|
|
33
|
+
? config.renderNav : config?.isRenderNav;
|
|
34
|
+
if (value === false || value === 'false') {return false;}
|
|
35
|
+
if (value === true || value === 'true') {return true;}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildDisplayPageEntryUrls({ appType, formUuid, baseUrl, config, formType = config?.formType }) {
|
|
40
|
+
if (formType !== 'display' || !/^FORM[-_][A-Za-z0-9_-]+$/.test(formUuid || '')) {return {};}
|
|
41
|
+
if (config?.formType && config.formType !== 'display') {return {};}
|
|
42
|
+
if ((config?.appType && config.appType !== appType) || (config?.formUuid && config.formUuid !== formUuid)) {return {};}
|
|
43
|
+
const appUrls = buildApplicationEntryUrls({ appType, baseUrl });
|
|
44
|
+
if (!appUrls.workbenchUrl) {return {};}
|
|
45
|
+
const origin = new URL(appUrls.workbenchUrl).origin;
|
|
46
|
+
const renderNav = readRenderNav(config);
|
|
47
|
+
const workbenchUrl = `${origin}/${appType}/workbench/${formUuid}`;
|
|
48
|
+
const standaloneUrl = renderNav === false ? `${origin}/${appType}/custom/${formUuid}` : null;
|
|
49
|
+
return {
|
|
50
|
+
url: standaloneUrl || workbenchUrl,
|
|
51
|
+
workbenchUrl,
|
|
52
|
+
standaloneUrl,
|
|
53
|
+
navigationVerification: { renderNav, verified: renderNav !== null },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { buildApplicationEntryUrls, buildDisplayPageEntryUrls, readRenderNav };
|
package/lib/app/create-page.js
CHANGED
|
@@ -20,6 +20,7 @@ const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff
|
|
|
20
20
|
const { throwCommandError } = require('../core/command-errors');
|
|
21
21
|
const { buildApiPath } = require('./create-form/api-path');
|
|
22
22
|
const { fetchFormSchemaInfo } = require('./get-form-config');
|
|
23
|
+
const { readRenderNav, buildDisplayPageEntryUrls } = require('./application-entry-urls');
|
|
23
24
|
|
|
24
25
|
function parseArgs(args) {
|
|
25
26
|
const openOption = parseOpenOption(args);
|
|
@@ -138,14 +139,6 @@ function buildPageInfoPostData(formUuid, pageName, isRenderNav, csrfToken, curre
|
|
|
138
139
|
return querystring.stringify(payload);
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
function readRenderNav(config) {
|
|
142
|
-
const value = config && Object.prototype.hasOwnProperty.call(config, 'renderNav')
|
|
143
|
-
? config.renderNav : config?.isRenderNav;
|
|
144
|
-
if (value === false || value === 'false') {return false;}
|
|
145
|
-
if (value === true || value === 'true') {return true;}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
142
|
async function configureNavigationVisibility(authRef, appType, pageId, pageName, isRenderNav) {
|
|
150
143
|
const verification = { requestedRenderNav: isRenderNav, renderNav: null, verified: false, attempts: 0 };
|
|
151
144
|
let currentConfig = null;
|
|
@@ -268,7 +261,10 @@ async function run(args) {
|
|
|
268
261
|
navigationVerification = await configureNavigationVisibility(authRef, appType, pageId, pageName, false);
|
|
269
262
|
if (navigationVerification.verified) {
|
|
270
263
|
chalkSuccess(t('create_page.dashboard_config_ok'));
|
|
271
|
-
pageUrl =
|
|
264
|
+
pageUrl = buildDisplayPageEntryUrls({
|
|
265
|
+
appType, formUuid: pageId, baseUrl: authRef.baseUrl,
|
|
266
|
+
config: { formType: 'display', renderNav: navigationVerification.renderNav },
|
|
267
|
+
}).standaloneUrl;
|
|
272
268
|
} else {
|
|
273
269
|
dashboardConfigWarning = navigationVerification.error;
|
|
274
270
|
warn(t('create_page.dashboard_config_failed', dashboardConfigWarning));
|
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
const { t } = require('../core/i18n');
|
|
22
22
|
const { banner, step, label, info, success, error, result } = require('../core/chalk');
|
|
23
23
|
const { throwCommandError } = require('../core/command-errors');
|
|
24
|
+
const { buildDisplayPageEntryUrls } = require('./application-entry-urls');
|
|
24
25
|
|
|
25
26
|
// 常用配置字段白名单(人读摘要),--json 会输出完整对象
|
|
26
27
|
const SUMMARY_FIELDS = [
|
|
@@ -121,6 +122,9 @@ async function run(args) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
const cfg = resp.content || resp.result || resp.data || {};
|
|
125
|
+
const entryUrls = buildDisplayPageEntryUrls({
|
|
126
|
+
appType: parsed.appType, formUuid: parsed.formUuid, baseUrl: authRef.baseUrl, config: cfg,
|
|
127
|
+
});
|
|
124
128
|
success(t('get_form_config.read_ok'));
|
|
125
129
|
|
|
126
130
|
if (parsed.json) {
|
|
@@ -129,6 +133,7 @@ async function run(args) {
|
|
|
129
133
|
appType: parsed.appType,
|
|
130
134
|
formUuid: parsed.formUuid,
|
|
131
135
|
config: cfg,
|
|
136
|
+
...entryUrls,
|
|
132
137
|
}, null, 2));
|
|
133
138
|
return;
|
|
134
139
|
}
|
|
@@ -138,6 +143,7 @@ async function run(args) {
|
|
|
138
143
|
appType: parsed.appType,
|
|
139
144
|
formUuid: parsed.formUuid,
|
|
140
145
|
title: extractTitle(cfg.title),
|
|
146
|
+
...entryUrls,
|
|
141
147
|
};
|
|
142
148
|
SUMMARY_FIELDS.forEach((key) => {
|
|
143
149
|
if (cfg[key] !== undefined) {
|
package/lib/app/publish.js
CHANGED
|
@@ -37,6 +37,8 @@ const { buildPageFile, shouldBuildPageSource } = require('./page-compat');
|
|
|
37
37
|
const { warnLargePageSource } = require('./page-size-guard');
|
|
38
38
|
const { assertPageTemplateCustomized } = require('./page-template-guard');
|
|
39
39
|
const { fetchFormPageList } = require('./form-navigation');
|
|
40
|
+
const { fetchFormSchemaInfo } = require('./get-form-config');
|
|
41
|
+
const { buildDisplayPageEntryUrls } = require('./application-entry-urls');
|
|
40
42
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
41
43
|
const { assertNoEmojiInValue } = require('../core/no-emoji-guard');
|
|
42
44
|
const { loadPageSource } = require('./services/page-source-loader');
|
|
@@ -834,7 +836,22 @@ async function runMain(argv) {
|
|
|
834
836
|
|
|
835
837
|
baseUrl = authRef.baseUrl || baseUrl;
|
|
836
838
|
|
|
837
|
-
|
|
839
|
+
let pageConfig = null;
|
|
840
|
+
let navigationWarning = null;
|
|
841
|
+
try {
|
|
842
|
+
const configResponse = await fetchFormSchemaInfo(authRef, appType, savedFormUuid);
|
|
843
|
+
if (!configResponse || configResponse.success === false || configResponse.__needLogin || configResponse.__csrfExpired) {
|
|
844
|
+
throw new Error(configResponse?.errorMsg || t('common.request_failed'));
|
|
845
|
+
}
|
|
846
|
+
pageConfig = configResponse.content || configResponse.result || configResponse.data || {};
|
|
847
|
+
} catch (configError) {
|
|
848
|
+
navigationWarning = configError.message || t('common.request_failed');
|
|
849
|
+
}
|
|
850
|
+
const entryUrls = buildDisplayPageEntryUrls({
|
|
851
|
+
appType, formUuid: savedFormUuid, baseUrl: authRef.baseUrl || baseUrl,
|
|
852
|
+
config: pageConfig, formType: 'display',
|
|
853
|
+
});
|
|
854
|
+
const pageUrl = entryUrls.url || `${baseUrl}/${appType}/workbench/${savedFormUuid}`;
|
|
838
855
|
const publishMode = isCanvas ? 'canvas' : 'native';
|
|
839
856
|
let healthCheckResult = null;
|
|
840
857
|
let navOrderResult = null;
|
|
@@ -911,6 +928,8 @@ async function runMain(argv) {
|
|
|
911
928
|
formUuid: savedFormUuid,
|
|
912
929
|
version,
|
|
913
930
|
url: pageUrl,
|
|
931
|
+
...entryUrls,
|
|
932
|
+
navigationWarning,
|
|
914
933
|
publishMode,
|
|
915
934
|
storageMode,
|
|
916
935
|
bundleId,
|
package/lib/auth/token-auth.js
CHANGED
|
@@ -83,27 +83,11 @@ function resolveDingtalkLoginOrigin(options = {}) {
|
|
|
83
83
|
return DINGTALK_LOGIN_ORIGIN;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function requestJson(method, url, body, headers = {}
|
|
86
|
+
function requestJson(method, url, body, headers = {}) {
|
|
87
87
|
return new Promise((resolve, reject) => {
|
|
88
88
|
const parsedUrl = new URL(url);
|
|
89
89
|
const data = body === undefined || body === null ? null : Buffer.from(JSON.stringify(body));
|
|
90
90
|
const transport = parsedUrl.protocol === 'https:' ? https : http;
|
|
91
|
-
const socketTimeoutMs = positiveNumber(options.timeoutMs, 0);
|
|
92
|
-
let timeoutId = null;
|
|
93
|
-
let settled = false;
|
|
94
|
-
const finish = (callback, value) => {
|
|
95
|
-
if (settled) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
settled = true;
|
|
99
|
-
if (timeoutId) {
|
|
100
|
-
clearTimeout(timeoutId);
|
|
101
|
-
}
|
|
102
|
-
callback(value);
|
|
103
|
-
};
|
|
104
|
-
const resolveOnce = value => finish(resolve, value);
|
|
105
|
-
const rejectOnce = error => finish(reject, error);
|
|
106
|
-
|
|
107
91
|
const request = transport.request({
|
|
108
92
|
method,
|
|
109
93
|
protocol: parsedUrl.protocol,
|
|
@@ -121,7 +105,6 @@ function requestJson(method, url, body, headers = {}, options = {}) {
|
|
|
121
105
|
}, (response) => {
|
|
122
106
|
const chunks = [];
|
|
123
107
|
response.on('data', (chunk) => chunks.push(chunk));
|
|
124
|
-
response.on('error', rejectOnce);
|
|
125
108
|
response.on('end', () => {
|
|
126
109
|
const text = Buffer.concat(chunks).toString('utf8');
|
|
127
110
|
let payload = null;
|
|
@@ -142,26 +125,14 @@ function requestJson(method, url, body, headers = {}, options = {}) {
|
|
|
142
125
|
error.payload = payload;
|
|
143
126
|
error.headers = response.headers;
|
|
144
127
|
error.location = location;
|
|
145
|
-
|
|
128
|
+
reject(error);
|
|
146
129
|
return;
|
|
147
130
|
}
|
|
148
|
-
|
|
131
|
+
resolve(payload || {});
|
|
149
132
|
});
|
|
150
133
|
});
|
|
151
134
|
|
|
152
|
-
|
|
153
|
-
// Node's socket timeout is reset by network activity, so it cannot enforce
|
|
154
|
-
// the caller's wall-clock budget. Use an independent timer that also
|
|
155
|
-
// covers response bodies which trickle data without ever completing.
|
|
156
|
-
timeoutId = setTimeout(() => {
|
|
157
|
-
const error = new Error('request timed out');
|
|
158
|
-
error.code = 'request_timeout';
|
|
159
|
-
rejectOnce(error);
|
|
160
|
-
request.destroy(error);
|
|
161
|
-
}, socketTimeoutMs);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
request.on('error', rejectOnce);
|
|
135
|
+
request.on('error', reject);
|
|
165
136
|
if (data) {
|
|
166
137
|
request.write(data);
|
|
167
138
|
}
|
|
@@ -169,11 +140,6 @@ function requestJson(method, url, body, headers = {}, options = {}) {
|
|
|
169
140
|
});
|
|
170
141
|
}
|
|
171
142
|
|
|
172
|
-
function positiveNumber(value, fallback) {
|
|
173
|
-
const parsed = Number(value);
|
|
174
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
143
|
function sanitizeRawTokenResponse(response) {
|
|
178
144
|
if (!response || typeof response !== 'object') {
|
|
179
145
|
return response;
|
|
@@ -336,43 +302,25 @@ async function tokenLogin(options = {}) {
|
|
|
336
302
|
const baseUrl = resolveTokenBaseUrl(options);
|
|
337
303
|
const authBaseUrl = appendPath(baseUrl, DEFAULT_AUTH_PATH_PREFIX);
|
|
338
304
|
const clientId = options.clientId || process.env.OPENYIDA_DINGTALK_CLIENT_ID || DINGTALK_OAUTH_CLIENT_ID;
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
envHint: options.envHint,
|
|
350
|
-
quiet: options.quiet,
|
|
351
|
-
onState: options.onDeviceState,
|
|
352
|
-
timeoutMs: options.timeoutMs || env.OPENYIDA_OAUTH_TIMEOUT_MS,
|
|
353
|
-
});
|
|
354
|
-
requireOkResponse(response);
|
|
355
|
-
} else {
|
|
356
|
-
const callback = await runDingtalkLoopback({
|
|
357
|
-
clientId,
|
|
358
|
-
loginOrigin: resolveDingtalkLoginOrigin(options),
|
|
359
|
-
scope,
|
|
360
|
-
prompt: options.prompt,
|
|
361
|
-
port: options.port,
|
|
362
|
-
quiet: options.quiet,
|
|
363
|
-
noBrowser: options.noBrowser,
|
|
364
|
-
timeoutMs: options.timeoutMs,
|
|
365
|
-
});
|
|
305
|
+
const callback = await runDingtalkLoopback({
|
|
306
|
+
clientId,
|
|
307
|
+
loginOrigin: resolveDingtalkLoginOrigin(options),
|
|
308
|
+
scope: options.scope || process.env.OPENYIDA_DINGTALK_SCOPE || 'openid corpid',
|
|
309
|
+
prompt: options.prompt,
|
|
310
|
+
port: options.port,
|
|
311
|
+
quiet: options.quiet,
|
|
312
|
+
noBrowser: options.noBrowser,
|
|
313
|
+
timeoutMs: options.timeoutMs,
|
|
314
|
+
});
|
|
366
315
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
316
|
+
const response = await requestJson('POST', appendPath(authBaseUrl, '/dingtalk/token'), {
|
|
317
|
+
code: callback.code,
|
|
318
|
+
authCode: callback.authCode,
|
|
319
|
+
redirectUri: callback.redirectUri,
|
|
320
|
+
state: callback.state,
|
|
321
|
+
clientId,
|
|
322
|
+
});
|
|
323
|
+
requireOkResponse(response);
|
|
376
324
|
|
|
377
325
|
const normalized = normalizeTokenResponse(response, baseUrl, clientId);
|
|
378
326
|
if (!normalized.access_token) {
|
|
@@ -676,5 +624,4 @@ module.exports = {
|
|
|
676
624
|
resolveTokenBaseUrl,
|
|
677
625
|
isRefreshAuthRequired,
|
|
678
626
|
createTokenRefreshFailedError,
|
|
679
|
-
getTokenPayload,
|
|
680
627
|
};
|
|
@@ -499,6 +499,7 @@ function buildApplicationEntryPolicy(builderPath, env = process.env) {
|
|
|
499
499
|
delivery_unit: 'single_application_entry_group',
|
|
500
500
|
resource_delivery: 'summary_only',
|
|
501
501
|
internal_artifact_delivery: 'never',
|
|
502
|
+
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.',
|
|
502
503
|
entries: {
|
|
503
504
|
workbench: 'when_workspace_in_scope',
|
|
504
505
|
custom: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
|
|
@@ -1035,7 +1035,7 @@ const COMMAND_GROUPS = [
|
|
|
1035
1035
|
id: 'auth',
|
|
1036
1036
|
titleKey: 'help.group_auth',
|
|
1037
1037
|
commands: [
|
|
1038
|
-
command('login', ['login'], 'login [target-url] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]
|
|
1038
|
+
command('login', ['login'], 'login [target-url] [--env <name>|--intl|--overseas|--global|--yidaapps|--alibaba] [--client-id <clientId>] [--endpoint <url>] [--no-browser]', 'help.cmd_login', {
|
|
1039
1039
|
requiresLogin: false,
|
|
1040
1040
|
output: 'json',
|
|
1041
1041
|
}),
|
|
@@ -1311,7 +1311,10 @@ const COMMAND_GROUPS = [
|
|
|
1311
1311
|
'openyida create-form create APP_XXX "访客登记" .cache/openyida/visitor/fields.json --layout section --theme comfortable --label-align left --icon name-card --locale zh_CN --no-open',
|
|
1312
1312
|
'openyida commands build create-form.create --app-type APP_XXX --form-title "访客登记" --fields-json-file .cache/openyida/visitor/fields.json --layout section --theme comfortable --label-align left --locale zh_CN --no-open --json',
|
|
1313
1313
|
],
|
|
1314
|
-
notes: [
|
|
1314
|
+
notes: [
|
|
1315
|
+
'Divider accepts the 23 visible platform styles (none is excluded) through field JSON dividerType, not a CLI flag. Omission falls back to bold-with-thin; invalid supplied styles fail. Choose per business page and keep same-level sections consistent; see summary.core_workflows.full_app_build.divider_style_contract.',
|
|
1316
|
+
'Divider colorType defaults to theme. If default bindings are unsuitable for the current theme, use custom with backgroundColor, secondaryColor and titleColor designed together; explicit user color values are not required. These are field JSON properties, not CLI flags. Dark-content and composite-title guidance is advisory, not a new validation or upload/save gate.',
|
|
1317
|
+
],
|
|
1315
1318
|
}),
|
|
1316
1319
|
command('create-form.icons', ['create-form', 'icons'], 'create-form icons [--json]', 'help.cmd_list_form_icons', {
|
|
1317
1320
|
requiresLogin: false,
|
|
@@ -1437,7 +1440,7 @@ const COMMAND_GROUPS = [
|
|
|
1437
1440
|
{ name: 'noOpen', type: 'boolean', source: 'option', builder_options: ['--no-open'] },
|
|
1438
1441
|
{ name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
|
|
1439
1442
|
],
|
|
1440
|
-
notes: ['Canvas publishing runs the same theme and form-open-container structure checks as compile before login or remote writes. --force does not bypass Canvas compilation checks. --health-check verifies saved content; inspect the actual page for runtime behavior. Fixed brand overrides are preserved with structured warnings by default. --strict-theme blocks them; --allow-fixed-brand conflicts with --strict-theme and --fix-theme. --fix-theme requires the migrated controls to be inside a mounted CanvasThemeProvider and compiles before writing. Unproven dynamic render paths require manual migration. No stdin prompts.'],
|
|
1443
|
+
notes: ['Canvas publishing runs the same theme and form-open-container structure checks as compile before login or remote writes. --force does not bypass Canvas compilation checks. --health-check verifies saved content; inspect the actual page for runtime behavior. Fixed brand overrides are preserved with structured warnings by default. --strict-theme blocks them; --allow-fixed-brand conflicts with --strict-theme and --fix-theme. --fix-theme requires the migrated controls to be inside a mounted CanvasThemeProvider and compiles before writing. Unproven dynamic render paths require manual migration. No stdin prompts.', 'Publishing reads persisted page navigation by default without a new flag. With renderNav=false, url and standaloneUrl use the clean /custom/<formUuid> route, with no navigation query parameters; workbenchUrl remains available. Unknown or visible navigation returns standaloneUrl=null; inspect navigationVerification and navigationWarning. A navigation read failure does not undo publication or prove standalone readiness. Configure scoped service frontend pages persistently before publishing; get-form-config can recover the verified URL without republishing.'],
|
|
1441
1444
|
}),
|
|
1442
1445
|
command('update-form-config', ['update-form-config'], 'update-form-config <appType> <formUuid> <true|false|keep> "<title>" [--locale zh_CN|en_US|ja_JP]', 'help.cmd_update_form_config', {
|
|
1443
1446
|
args: [
|
|
@@ -1456,7 +1459,7 @@ const COMMAND_GROUPS = [
|
|
|
1456
1459
|
{ name: 'formUuid', type: 'string', source: 'positional', position: 1, required: true },
|
|
1457
1460
|
{ name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
|
|
1458
1461
|
],
|
|
1459
|
-
notes: ['Navigation readback uses renderNav first, falling back to isRenderNav only when renderNav is absent. true/false booleans and strings are recognized; missing or invalid values remain unknown. --json includes the raw configuration; the default summary preserves both navigation fields when present.'],
|
|
1462
|
+
notes: ['Navigation readback uses renderNav first, falling back to isRenderNav only when renderNav is absent. true/false booleans and strings are recognized; missing or invalid values remain unknown. --json includes the raw configuration; the default summary preserves both navigation fields when present. For a confirmed display page, both outputs include url, workbenchUrl, standaloneUrl and navigationVerification. Only persisted renderNav=false returns a clean custom standaloneUrl; visible or unknown navigation returns standaloneUrl=null. Route construction does not verify publication or viewer permissions.'],
|
|
1460
1463
|
}),
|
|
1461
1464
|
],
|
|
1462
1465
|
},
|
|
@@ -1829,6 +1832,29 @@ function summarizeLocalizedCommands(commands) {
|
|
|
1829
1832
|
fallback: DEFAULT_DIVIDER_TYPE,
|
|
1830
1833
|
selection: 'Choose by each page business task and visual weight; keep same-level sections consistent within a page. Prefer suitable variation across different business pages. If the scene is unclear or suitable candidates tie, shuffle compatible candidates and rotate across pages, preferring unused styles until exhausted. Record one choice per page in design.md and dividerType before compilation; retries reuse it. Preserve existing supported styles for local edits.',
|
|
1831
1834
|
invalid_input: 'INVALID_DIVIDER_TYPE diagnostics; create rejects with CREATE_FORM_INVALID_DIVIDER_TYPE. Never silently replace a supplied unsupported style.',
|
|
1835
|
+
color_guidance: {
|
|
1836
|
+
reference: 'yida-skills/skills/yida-design/references/native-form-styles.md#分割线弱背景',
|
|
1837
|
+
mode: 'agent_design_guidance_only',
|
|
1838
|
+
default_mode: 'theme',
|
|
1839
|
+
custom_mode: 'custom',
|
|
1840
|
+
inputs: ['colorType', 'backgroundColor', 'secondaryColor', 'titleColor'],
|
|
1841
|
+
input_location: 'Field JSON for create/batch; changes for update; props in field-props operations for patch. Not CLI flags.',
|
|
1842
|
+
schema_paths: ['Divider.props.colorType', 'Divider.props.backgroundColor', 'Divider.props.secondaryColor', 'Divider.props.titleColor'],
|
|
1843
|
+
default_bindings: {
|
|
1844
|
+
primary: 'var(--color-brand1-6)',
|
|
1845
|
+
secondary: 'var(--color-brand1-2)',
|
|
1846
|
+
text: 'var(--color-text1-4, #171a1d)',
|
|
1847
|
+
},
|
|
1848
|
+
custom_when: 'The user requests colors, or default bindings do not suit the current theme, visual weight or readability. Design all three colors together; do not require user-specified values for each color. Reuse colors across peer sections.',
|
|
1849
|
+
dark_content: 'Distinguish dark content from dark navigation with light content. Treat brand1-2 as a weak brand surface, not permanently near-white. Root secondary overrides may lose to inline component bindings. For local repairs, prefer component colors when global changes would affect other consumers.',
|
|
1850
|
+
composite_titles: 'Consider outer decoration, the actual title surface and the title separately. In inner-ellipse-title the inner ellipse carries the text. Preserve existing supported shapes; do not dim the whole component to fix its background.',
|
|
1851
|
+
solid_title_surfaces: 'For dark-bar and light-left-title/light-center-title/light-house-title/light-hexagon-title, distinguish the primary-colored title block from the secondary strip. Prefer white or near-white on sufficiently dark primary surfaces; if a bright saturated fill weakens white text, deepen the component backgroundColor while preserving its hue. Do not force white onto pale surfaces. Set titleColor explicitly in custom mode instead of retaining its dark default; do not change global body text to fix a local title.',
|
|
1852
|
+
label_coordination: 'Design the divider title, first field label and input as a group. Share font family; use modest title weight/size emphasis, readable neutral labels, coordinated alignment and spacing. Preserve centered decorative headings without centering field labels. Keep the section gap larger than the title-to-first-field gap, and label-to-input spacing compact. Do not dim labels to create hierarchy or repeat excessive saturated decoration. The label role pod-form-label-color is separate from divider titleColor; record choices in existing design.md without new flags or blocking checks.',
|
|
1853
|
+
picker_icons: '--color-fill1-6 is a weak foreground for picker icons; choose a readable auxiliary foreground against the actual input surface, for example the current theme color-text1-3.',
|
|
1854
|
+
lifecycle: 'Record the choice in existing design.md. Revisit custom colors when changing themes; they do not automatically follow theme switching. Do not claim visual verification from a successful command or when preview is unavailable.',
|
|
1855
|
+
new_cli_flags: false,
|
|
1856
|
+
adds_validation_or_blocking: false,
|
|
1857
|
+
},
|
|
1832
1858
|
},
|
|
1833
1859
|
page_skill_policy: 'Use YidaCodeCanvas by default for new primary custom pages. Choose yida-custom-page only when the current target has been identified as existing .oyd.jsx/.oyb.jsx/renderJsx/platform Jsx component maintenance; do not switch to yida-custom-page merely because a page needs data, forms, uploads, this.utils.yida.*, or dataSourceMap-like capabilities.',
|
|
1834
1860
|
form_entry_policy: {
|
|
@@ -1904,6 +1930,8 @@ function summarizeLocalizedCommands(commands) {
|
|
|
1904
1930
|
custom: {
|
|
1905
1931
|
include: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
|
|
1906
1932
|
url: '{base_url}/{appType}/custom/{formUuid}',
|
|
1933
|
+
default_policy: 'A scoped service frontend is a standalone display page even when entryMode was omitted. Plan derives it from role=service and sceneKey; Fast follows the same business intent. Persist page navigation hiding and read it back, without a new flag or navigation URL query. Preserve explicit user choices and backend navigation. Missing verification is incomplete frontend delivery, not permission to silently omit it.',
|
|
1934
|
+
url_source: 'publish.standaloneUrl or post-publish get-form-config.standaloneUrl; require navigationVerification.renderNav=false and verified=true',
|
|
1907
1935
|
},
|
|
1908
1936
|
admin: {
|
|
1909
1937
|
include: 'default_for_complete_application',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -382,8 +382,8 @@ Examples:
|
|
|
382
382
|
save_permission_usage: 'Usage: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>]',
|
|
383
383
|
save_permission_example: "Example: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
384
384
|
exec_failed: '\n❌ Execution failed: {0}',
|
|
385
|
-
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--
|
|
386
|
-
login_example: 'Examples:\n openyida login # Automatically open the browser via OAuth loopback login\n openyida login --
|
|
385
|
+
login_usage: 'Usage: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
386
|
+
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',
|
|
387
387
|
login_unsupported_option: 'Removed legacy login option is no longer supported: {0}. Use token/OAuth login; env token mode reads only OPENYIDA_* tokens.',
|
|
388
388
|
auth_usage: 'Usage: openyida auth <status|login|refresh|logout|profiles|profile switch>',
|
|
389
389
|
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',
|
|
@@ -655,18 +655,6 @@ Examples:
|
|
|
655
655
|
oauth_success_message: 'Sign-in is complete. Return to the previous window to continue.',
|
|
656
656
|
oauth_closing: 'Closing this window automatically…',
|
|
657
657
|
oauth_manual_close: 'This window could not close automatically. Close it manually, then return to the previous window to continue.',
|
|
658
|
-
device_title: 'Device code login:',
|
|
659
|
-
device_open: 'Open',
|
|
660
|
-
device_enter_code: 'Enter code:',
|
|
661
|
-
device_or_direct: 'Or open directly:',
|
|
662
|
-
device_completed: 'Device authorization completed.',
|
|
663
|
-
device_timeout: 'device authorization timed out; device code may still be pending',
|
|
664
|
-
device_missing_params: 'device code flow requires authBaseUrl and clientId',
|
|
665
|
-
device_poll_missing_params: 'device token polling requires authBaseUrl, clientId and deviceCode',
|
|
666
|
-
device_invalid_code_response: 'auth service did not return a usable device code',
|
|
667
|
-
device_code_expired: 'device code expired; run login --device again',
|
|
668
|
-
device_access_denied: 'authorization was denied on the verification page',
|
|
669
|
-
device_2xx_no_token: 'auth service returned 2xx without accessToken',
|
|
670
658
|
oauth_failure_title: 'Sign-in not completed',
|
|
671
659
|
oauth_invalid_state: 'The sign-in request could not be verified. Return to the terminal and try again.',
|
|
672
660
|
oauth_missing_code: 'No authorization information was received. Return to the terminal and try again.',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -382,8 +382,8 @@ openyida - 宜搭命令行工具
|
|
|
382
382
|
save_permission_usage: '用法: openyida save-permission <appType> <formUuid> [--package-uuid <packageUuid>] [--data-permission <json>] [--action-permission <json>]',
|
|
383
383
|
save_permission_example: "示例: openyida save-permission APP_XXX FORM-XXX --data-permission '{\"role\":\"DEFAULT\",\"dataRange\":\"SELF\"}'",
|
|
384
384
|
exec_failed: '\n❌ 执行失败: {0}',
|
|
385
|
-
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--
|
|
386
|
-
login_example: '示例:\n openyida login # 通过 OAuth loopback 自动打开浏览器登录\n openyida login --
|
|
385
|
+
login_usage: '用法: openyida login [entryUrl|--public|--alibaba|--intl] [--no-browser] [--check-only] [--json] [--client-id <clientId>]',
|
|
386
|
+
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 # 登录入口别名',
|
|
387
387
|
login_unsupported_option: '已删除的旧登录参数不再支持: {0}。请使用 token/OAuth 登录;env token 模式下只读取 OPENYIDA_* token。',
|
|
388
388
|
first_run_title: ' 🤖 OpenYida - AI 问答模式已开启! ',
|
|
389
389
|
first_run_welcome: ' {0}欢迎首次使用 OpenYida!{1} 以下是快速上手指南:',
|
|
@@ -626,18 +626,6 @@ openyida - 宜搭命令行工具
|
|
|
626
626
|
oauth_success_message: '登录已完成,请返回刚才的窗口继续操作。',
|
|
627
627
|
oauth_closing: '正在自动关闭此窗口…',
|
|
628
628
|
oauth_manual_close: '此窗口未能自动关闭,请手动关闭后返回刚才的窗口继续操作。',
|
|
629
|
-
device_title: '设备码登录:',
|
|
630
|
-
device_open: '打开',
|
|
631
|
-
device_enter_code: '输入验证码:',
|
|
632
|
-
device_or_direct: '或直接打开:',
|
|
633
|
-
device_completed: '设备授权完成。',
|
|
634
|
-
device_timeout: '设备授权超时;设备码可能仍在等待验证',
|
|
635
|
-
device_missing_params: '设备码流程需要 authBaseUrl 和 clientId',
|
|
636
|
-
device_poll_missing_params: '设备令牌轮询需要 authBaseUrl、clientId 和 deviceCode',
|
|
637
|
-
device_invalid_code_response: '认证服务未返回可用的设备码',
|
|
638
|
-
device_code_expired: '设备码已过期;请重新执行 login --device',
|
|
639
|
-
device_access_denied: '授权已在验证页面被拒绝',
|
|
640
|
-
device_2xx_no_token: '认证服务返回 2xx 但未包含 accessToken',
|
|
641
629
|
oauth_failure_title: '登录未完成',
|
|
642
630
|
oauth_invalid_state: '登录验证信息无效,请返回终端重新发起登录。',
|
|
643
631
|
oauth_missing_code: '未收到登录授权信息,请返回终端重新发起登录。',
|
|
@@ -16,6 +16,15 @@ const assert = (condition, message, details) => {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
// Resource names are planning references. Runtime IDs and permissions are resolved after creation.
|
|
19
|
+
function resolvePageEntryMode(page, plan, { sceneKey, customNavigation }) {
|
|
20
|
+
const supplied = page.pageSpecHandoff || {};
|
|
21
|
+
const entries = plan.execution?.entryRecommendation?.entries;
|
|
22
|
+
const servicePage = Array.isArray(entries) && entries.some(entry =>
|
|
23
|
+
entry?.role === 'service' && filled(entry.sceneKey) && entry.sceneKey === sceneKey);
|
|
24
|
+
return supplied.entryMode || page.entryMode ||
|
|
25
|
+
(servicePage || customNavigation || supplied.navigation ? 'standalone' : 'platform-shell');
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
function buildEntryNavigation(plan, resources, pages) {
|
|
20
29
|
const recommendation = plan.execution?.entryRecommendation;
|
|
21
30
|
if (recommendation === undefined) {return null;}
|
|
@@ -94,4 +103,4 @@ function buildEntryNavigation(plan, resources, pages) {
|
|
|
94
103
|
return { recommendation: JSON.parse(JSON.stringify(recommendation)), platformOrder: [...new Set(platformOrder)] };
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
module.exports = { buildEntryNavigation, ENTRY_MODES, ENTRY_MODE_SUMMARIES };
|
|
106
|
+
module.exports = { buildEntryNavigation, resolvePageEntryMode, ENTRY_MODES, ENTRY_MODE_SUMMARIES };
|
|
@@ -9,7 +9,7 @@ const { normalizePlan, assertThemeProfile } = require('./normalize');
|
|
|
9
9
|
const { readJson, writeFiles } = require('./files');
|
|
10
10
|
const { prepareUpdates } = require('./artifact-updates');
|
|
11
11
|
const { validateAuthoring } = require('./validate');
|
|
12
|
-
const { buildEntryNavigation, ENTRY_MODE_SUMMARIES } = require('./entry-navigation');
|
|
12
|
+
const { buildEntryNavigation, resolvePageEntryMode, ENTRY_MODE_SUMMARIES } = require('./entry-navigation');
|
|
13
13
|
const { buildPageNavigationPolicy } = require('./navigation-policy');
|
|
14
14
|
const { buildAssetTasks } = require('../asset/asset-plan');
|
|
15
15
|
const { mergeParts } = require('./parallel');
|
|
@@ -216,7 +216,7 @@ function pageHandoff(page, plan) {
|
|
|
216
216
|
detail: 'detail-profile', landing: 'official-homepage', screen: 'data-screen',
|
|
217
217
|
};
|
|
218
218
|
const customNavigation = navigationConfig(plan).navigationType === 'custom';
|
|
219
|
-
const entryMode =
|
|
219
|
+
const entryMode = resolvePageEntryMode(page, plan, { sceneKey: page.sceneKey || scene, customNavigation });
|
|
220
220
|
requireFact(!customNavigation || entryMode === 'standalone', `自定义导航的页面 ${page.name} 必须使用 standalone 入口`);
|
|
221
221
|
if (supplied.navigation !== undefined) {
|
|
222
222
|
const nav = supplied.navigation;
|
|
@@ -277,7 +277,9 @@ function pageNavigationRows(page, plan) {
|
|
|
277
277
|
const handoff = page.pageSpecHandoff || {};
|
|
278
278
|
const applicationCustom = navigationConfig(plan).navigationType === 'custom';
|
|
279
279
|
const menu = handoff.navigation;
|
|
280
|
-
const entryMode =
|
|
280
|
+
const entryMode = resolvePageEntryMode(page, plan, {
|
|
281
|
+
sceneKey: page.sceneKey || sceneForPage(page), customNavigation: applicationCustom,
|
|
282
|
+
});
|
|
281
283
|
const variants = { top: '顶部', side: '侧边', mixed: '顶部与侧边', dock: '底部' };
|
|
282
284
|
return [
|
|
283
285
|
['访问方式', entryMode === 'standalone' ? '独立页面入口' : '应用工作区入口'],
|
|
@@ -613,7 +615,7 @@ function renderProjectApplications(plan, tokens, color, chapter) {
|
|
|
613
615
|
'--color-brand1-6': '主色与主要品牌操作', '--color-brand1-1': '品牌悬停',
|
|
614
616
|
'--color-brand1-9': '品牌按下', '--color-brand1-10': '品牌禁用',
|
|
615
617
|
'--pod-page-bg-color': '页面画布', '--pod-card-bg-color': '内容容器',
|
|
616
|
-
'--color-fill1-5': '独立浮层', '--color-fill1-10': 'Tooltip背景', '--color-text1-5': 'Tooltip前景',
|
|
618
|
+
'--color-fill1-5': '独立浮层', '--color-fill1-6': '弱图标与辅助操作', '--color-fill1-10': 'Tooltip背景', '--color-text1-5': 'Tooltip前景',
|
|
617
619
|
};
|
|
618
620
|
return [
|
|
619
621
|
'## 5. 项目应用与调整规则', '',
|
package/package.json
CHANGED
|
@@ -123,6 +123,8 @@ Fast(直接搭建)与 Plan(先确认方案)使用同一份入口规划
|
|
|
123
123
|
|
|
124
124
|
## 规划字段速查(Fast / Plan)
|
|
125
125
|
|
|
126
|
+
前台用途已经确定时,自定义承载页面默认采用独立入口。`role=service` 与页面 `sceneKey` 对应后,Plan 自动推导遗漏的 `entryMode=standalone`,Fast 执行同样规则;不靠页面名称猜测,不新增 CLI 参数。显式的导航要求优先,冲突需核对。取得页面 ID 后持久化隐藏页面导航并回读,发布后交付 `publish` 或 `get-form-config` 返回的 `standaloneUrl`,不追加导航 URL 参数。前台入口属于交付范围时不能因字段漏写或只拿到工作台地址而静默省略;未确认成功须明确列为未完成。
|
|
127
|
+
|
|
126
128
|
需求文件中的 `entryRecommendation` 在 Plan 初始化后保存在 `execution.entryRecommendation`;Fast 在 PRD 中使用同一结构。旧计划没有此字段时仍可使用;首次完整生成时应明确入口规划。保留已有 `source/reason/taskRefs` 等需求依据。
|
|
127
129
|
|
|
128
130
|
| 字段 | 含义和要求 |
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
3. 创建页面前核对 PRD/resource context 与 auth snapshot 的 `corpId`。
|
|
17
17
|
4. `corpId` 不一致 → 先确认重新登录到目标组织,或确认在当前组织继续。
|
|
18
18
|
5. 创建 display 页面后记录真实主页面 `formUuid`。
|
|
19
|
-
6. PRD 的应用工作区采用自定义导航,或该页已确定 `entryMode=standalone` / 页面隐藏导航时,取得真实 `formUuid` 后立即执行 `openyida update-form-config <appType> <formUuid> false "<页面标题>"` 并 `get-form-config --json` 回读 `renderNav=false
|
|
19
|
+
6. 已确定面向前台用户的自定义页面(如 `entryRecommendation.entries[].role=service`),默认作为独立页面交付,即使遗漏 `entryMode` 也按业务用途补齐为 `standalone`,不要求用户再选择开关。PRD 的应用工作区采用自定义导航,或该页已确定 `entryMode=standalone` / 页面隐藏导航时,取得真实 `formUuid` 后立即执行 `openyida update-form-config <appType> <formUuid> false "<页面标题>"` 并 `get-form-config --json` 回读 `renderNav=false`;已有页面按同样规则处理,配置已正确则不重复写入。该任务与 Step 7 的源码开发并行,不等待代码或发布完成。使用持久化配置,不在自定义页 URL 中追加 `isRenderNav=false`。用户明确要求保留平台导航、普通后台内容页和无关页面局部修改沿用其已有边界。
|
|
20
20
|
7. 将页面导航配置结果交给 Step 8 做发布后核对。其余自定义页面同样在各自创建/复用成功后配置,不只处理主页面。
|
|
21
21
|
|
|
22
22
|
同一 sceneKey 的前台业务视图共用一个自定义页面;将全部视图交给 Step 7 实现。
|