openyida 2026.10.15 → 2026.10.16-2
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 +4 -1
- package/lib/agent/cmd.js +1 -1
- package/lib/agent/frozen-bundle.js +4 -4
- package/lib/agent/private-directory.js +9 -2
- package/lib/agent/provider-preflight.js +10 -3
- package/lib/agent/runtime-catalog.json +15 -12
- package/lib/agent/stdio.js +7 -1
- package/lib/app/check-prd-completeness.js +63 -1
- package/lib/app/form-navigation.js +7 -3
- package/lib/app/update-app.js +3 -0
- package/lib/auth/token-auth.js +27 -1
- package/lib/auth/token-store.js +29 -3
- package/lib/core/command-manifest.js +8 -4
- package/lib/core/locales/en.js +17 -1
- package/lib/core/locales/zh.js +17 -1
- package/lib/core/process-approver-query.js +152 -0
- package/lib/core/query-data.js +19 -1
- package/lib/core/utils.js +5 -0
- package/lib/design-plan/design-plan.js +3 -3
- package/lib/design-plan/entry-navigation.js +29 -2
- package/lib/design-plan/materialize.js +30 -4
- package/lib/design-plan/patch.js +9 -2
- package/package.json +5 -5
- package/yida-skills/SKILL.md +5 -0
- package/yida-skills/references/local-agent-preflight.md +5 -2
- package/yida-skills/skills/yida-app/SKILL.md +4 -0
- package/yida-skills/skills/yida-app/references/entry-navigation.md +13 -0
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +10 -0
- package/yida-skills/skills/yida-app/workflow/step-3-create-or-reuse-app.md +2 -0
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +1 -1
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +4 -2
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +7 -1
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +2 -0
- package/yida-skills/skills/yida-data-management/SKILL.md +24 -2
- package/yida-skills/skills/yida-design/SKILL.md +1 -1
- package/yida-skills/skills/yida-design/references/application-theme-consistency.md +6 -0
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/app_theme.css +3 -3
- package/yida-skills/skills/yida-design/templates/design-themes/dark-rail-fine-lines/design.md +3 -2
- package/yida-skills/skills-index.json +6 -2
package/README.md
CHANGED
|
@@ -245,10 +245,13 @@ openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process.json
|
|
|
245
245
|
openyida process preview APP_XXX PROC_INST_XXX --output .cache/openyida/process/process.html
|
|
246
246
|
openyida data query form APP_XXX FORM_XXX --page 1 --size 20
|
|
247
247
|
openyida data query form APP_XXX FORM_XXX --dynamic-order '{"dateField_xxx":"-"}'
|
|
248
|
+
openyida data query process APP_XXX FORM_XXX --current-approver USER_ID --count
|
|
248
249
|
openyida data create form APP_XXX FORM_XXX --expect-form-name 客户 --expect-form-type receipt --data-file .cache/openyida/data-import/record.json
|
|
249
250
|
openyida get-permission APP_XXX FORM_XXX
|
|
250
251
|
```
|
|
251
252
|
|
|
253
|
+
`--current-approver` takes an exact userId, not a display name. It scans running instances and their current TODO records, counts each matching instance once, and respects the current login's server-authorized data scope. Omit `--count` to include matching instance data. An incomplete scan or permission failure is reported as an error; the result does not claim application-wide visibility.
|
|
254
|
+
|
|
252
255
|
Append and forward permissions are configured in the process definition through `nodes[].actions.normalActions/appendActions`. Both `create-process` and `configure-process` compile these settings into matching designer and runtime properties. See [approval action configuration](yida-skills/skills/yida-process-rule/references/approval-actions.md) for the JSON example, defaults, and verification limits.
|
|
253
256
|
|
|
254
257
|
`configure-process` 的流程 JSON 中,审批人可配置为发起人、指定成员、指定角色、部门主管或直属主管,例如:
|
|
@@ -476,7 +479,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
476
479
|
| `openyida list-forms <appType> [--keyword <text>]` | List forms/pages in an app |
|
|
477
480
|
| `openyida aggregate-table <list\|create-empty\|inspect\|preview\|save\|publish\|status> <appType> ...` | Manage aggregate tables (virtualView) |
|
|
478
481
|
| `openyida get-schema <appType> <formUuid\|--all> [--summary-json\|--field-map-json\|--analysis-json]` | Get one form Schema or all form Schemas |
|
|
479
|
-
| `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check
|
|
482
|
+
| `openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]` | Check resources, frontend menu hiding and application theme delivery |
|
|
480
483
|
| `openyida check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]` | Validate design documents, theme tokens and PRD handoff |
|
|
481
484
|
| `openyida er <appType> [--format mermaid\|json] [--output file] [--include-system] [--include-pages]` | Export app entity relationship diagram |
|
|
482
485
|
| `openyida create-page <appType> "<name>" [--mode dashboard] [--hide-nav] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page |
|
package/lib/agent/cmd.js
CHANGED
|
@@ -285,7 +285,7 @@ async function run(argv = [], options = {}) {
|
|
|
285
285
|
// Inventory includes signed-out CLIs. Runtime reports their auth status;
|
|
286
286
|
// the control plane independently enforces task readiness.
|
|
287
287
|
config.providers = providerCheck.providers.filter(p => p.registrable).map(p => ({
|
|
288
|
-
profileId: 'openyida.' + p.provider, provider: p.provider, executable: p.executable,
|
|
288
|
+
profileId: 'openyida.' + p.provider, provider: p.provider, executable: p.executable, ...(p.version ? { version: p.version } : {}),
|
|
289
289
|
}));
|
|
290
290
|
}
|
|
291
291
|
if (managedConnections && args.command === 'connect') {
|
|
@@ -285,11 +285,11 @@ function materializeBundle(runtime, config, options = {}) {
|
|
|
285
285
|
catch (error) {
|
|
286
286
|
if (!['EEXIST', 'ENOTEMPTY', 'EACCES', 'EPERM'].includes(error.code) || !fs.existsSync(root)) {throw error;}
|
|
287
287
|
}
|
|
288
|
-
} else {
|
|
289
|
-
// Never trust a version or mtime cache. Check source stability and every
|
|
290
|
-
// byte of the existing bundle, including unexpected entries, on reuse.
|
|
291
|
-
verifySources();
|
|
292
288
|
}
|
|
289
|
+
// On reuse, source bytes were hashed to compute this exact manifest ID.
|
|
290
|
+
// We execute only the existing bundle, whose complete inventory and bytes
|
|
291
|
+
// are verified below. Reopening the source snapshot cannot make that bundle
|
|
292
|
+
// more trustworthy. New publication still checks source stability above.
|
|
293
293
|
if (published) {fs.chmodSync(root, 0o500);}
|
|
294
294
|
verifyBundle(root, id);
|
|
295
295
|
const bundle = { schemaVersion: 1, id, root, manifestPath: path.join(root, 'bundle-manifest.json'), manifestSha256: id,
|
|
@@ -51,8 +51,15 @@ foreach ($sid in @($identity.Value, 'S-1-5-18', 'S-1-5-32-544')) {
|
|
|
51
51
|
foreach ($directory in ($reader.ReadToEnd() | ConvertFrom-Json)) {
|
|
52
52
|
# Concurrent launchers may create the same parent. Never rewrite a winner;
|
|
53
53
|
# accept it only when its actual owner and grants meet the same private policy.
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
# Extended-length paths avoid the .NET Framework MAX_PATH limit. Paths
|
|
55
|
+
# are already absolute and validated by the Node caller.
|
|
56
|
+
$nativePath = $directory
|
|
57
|
+
if (-not $directory.StartsWith('\\\\?\\')) {
|
|
58
|
+
if ($directory.StartsWith('\\\\')) { $nativePath = '\\\\?\\UNC\\' + $directory.Substring(2) }
|
|
59
|
+
else { $nativePath = '\\\\?\\' + $directory }
|
|
60
|
+
}
|
|
61
|
+
[System.IO.Directory]::CreateDirectory($nativePath, $acl) | Out-Null
|
|
62
|
+
$actual = (New-Object System.IO.DirectoryInfo($nativePath)).GetAccessControl()
|
|
56
63
|
if ($actual.GetOwner([System.Security.Principal.SecurityIdentifier]).Value -ne $identity.Value) {throw 'Private directory owner differs'}
|
|
57
64
|
foreach ($rule in $actual.Access) {
|
|
58
65
|
$sid = $rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
|
|
@@ -186,7 +186,14 @@ async function checkProvider(provider, executable, options) {
|
|
|
186
186
|
if (version.status !== 'passed') { return { status: version.status, cliDetected: false }; }
|
|
187
187
|
// Installation evidence is independent of authentication/protocol readiness.
|
|
188
188
|
// Apply this consistently to every enabled provider, including future adapters.
|
|
189
|
-
|
|
189
|
+
// Reuse only a bounded, recognizable version line from this exact probe.
|
|
190
|
+
// Arbitrary provider banners/stderr never become launch metadata.
|
|
191
|
+
const versionLine = String(version.output || '').split(/\r?\n/).map(line => line.trim()).find(line =>
|
|
192
|
+
line.length <= 64 && /^(?:(?:codex(?:-cli)?|qoder(?:cli| cli)?|opencode)\s+)?v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?(?:\s+\([0-9a-z ._-]+\))?$/i.test(line));
|
|
193
|
+
const detected = { cliDetected: true, ...(versionLine ? { version: versionLine } : {}) };
|
|
194
|
+
// Connect/run only establish installation inventory. The runtime discovers live
|
|
195
|
+
// protocol, login and models for EVERY registered provider; doctor probes them here.
|
|
196
|
+
if (options.inventoryOnly) { return { ...detected, status: 'cli_detected', authKnown: false, protocolProbe: 'not_run' }; }
|
|
190
197
|
if (provider === 'opencode') { return { ...detected, status: 'available', authKnown: false, protocolProbe: 'not_run' }; }
|
|
191
198
|
const protocol = await execute(executable, provider === 'qoder' ? ['--acp'] : ['app-server', '--listen', 'stdio://'], provider, options);
|
|
192
199
|
if (protocol.status === 'login_required') {
|
|
@@ -230,9 +237,9 @@ async function preflight(args, options = {}) {
|
|
|
230
237
|
};
|
|
231
238
|
for (const candidate of entry.candidates.slice(0, 4)) {
|
|
232
239
|
executable = candidate;
|
|
233
|
-
result = await checkProvider(entry.provider, executable, { ...options, probe: boundedProbe });
|
|
240
|
+
result = await checkProvider(entry.provider, executable, { ...options, inventoryOnly: ['connect', 'run'].includes(args.command), probe: boundedProbe });
|
|
234
241
|
if (result.cliDetected && !discovered) { discovered = { result, executable }; }
|
|
235
|
-
if (Date.now() >= deadline || ['ready', 'available', 'login_required', 'probe_cancelled'].includes(result.status)) { break; }
|
|
242
|
+
if (Date.now() >= deadline || ['ready', 'available', 'cli_detected', 'login_required', 'probe_cancelled'].includes(result.status)) { break; }
|
|
236
243
|
}
|
|
237
244
|
if (!result.cliDetected && result.status !== 'probe_cancelled' && discovered) {
|
|
238
245
|
({ result, executable } = discovered);
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
"keys": {
|
|
4
4
|
"openyida-runtime-public-beta-20260923-1": {
|
|
5
5
|
"publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEApl2Q+64rX997KxEQUY2yU8Xnc7sM3NRQ8Nf0bAi9Hck=\n-----END PUBLIC KEY-----\n"
|
|
6
|
+
},
|
|
7
|
+
"openyida-runtime-public-beta-20261012-1": {
|
|
8
|
+
"publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAKaA5RBJXWVhQa+9ehmR3x7WQCAlVGxbJULG9T17qmRc=\n-----END PUBLIC KEY-----\n"
|
|
6
9
|
}
|
|
7
10
|
},
|
|
8
11
|
"releases": [
|
|
@@ -10,33 +13,33 @@
|
|
|
10
13
|
"platform": "darwin",
|
|
11
14
|
"arch": "arm64",
|
|
12
15
|
"packageName": "@openyida/local-agent-runtime-darwin-arm64",
|
|
13
|
-
"packageVersion": "0.1.
|
|
14
|
-
"runtimeVersion": "0.1.
|
|
15
|
-
"manifestSha256": "
|
|
16
|
+
"packageVersion": "0.1.4",
|
|
17
|
+
"runtimeVersion": "0.1.4",
|
|
18
|
+
"manifestSha256": "48dc2a4473422c663e3330840c4f96e6646b2ff70ed61452fef8f1e68bf4a258"
|
|
16
19
|
},
|
|
17
20
|
{
|
|
18
21
|
"platform": "darwin",
|
|
19
22
|
"arch": "x64",
|
|
20
23
|
"packageName": "@openyida/local-agent-runtime-darwin-x64",
|
|
21
|
-
"packageVersion": "0.1.
|
|
22
|
-
"runtimeVersion": "0.1.
|
|
23
|
-
"manifestSha256": "
|
|
24
|
+
"packageVersion": "0.1.4",
|
|
25
|
+
"runtimeVersion": "0.1.4",
|
|
26
|
+
"manifestSha256": "1c93114b7fc9f2af7de1088d801ccbd3eaf32aabd967b5bd56917e8c1ec0661b"
|
|
24
27
|
},
|
|
25
28
|
{
|
|
26
29
|
"platform": "win32",
|
|
27
30
|
"arch": "x64",
|
|
28
31
|
"packageName": "@openyida/local-agent-runtime-win32-x64",
|
|
29
|
-
"packageVersion": "0.1.
|
|
30
|
-
"runtimeVersion": "0.1.
|
|
31
|
-
"manifestSha256": "
|
|
32
|
+
"packageVersion": "0.1.4",
|
|
33
|
+
"runtimeVersion": "0.1.4",
|
|
34
|
+
"manifestSha256": "13a7fc58b87a7f3f2592ba5239992c415463ab8b3fbf32312944db8c611fb813"
|
|
32
35
|
},
|
|
33
36
|
{
|
|
34
37
|
"platform": "win32",
|
|
35
38
|
"arch": "arm64",
|
|
36
39
|
"packageName": "@openyida/local-agent-runtime-win32-arm64",
|
|
37
|
-
"packageVersion": "0.1.
|
|
38
|
-
"runtimeVersion": "0.1.
|
|
39
|
-
"manifestSha256": "
|
|
40
|
+
"packageVersion": "0.1.4",
|
|
41
|
+
"runtimeVersion": "0.1.4",
|
|
42
|
+
"manifestSha256": "49e63f81730e1ee495863a48b9e90f687e9529e700788665196c169a630ed094"
|
|
40
43
|
}
|
|
41
44
|
]
|
|
42
45
|
}
|
package/lib/agent/stdio.js
CHANGED
|
@@ -127,7 +127,13 @@ function launchRuntime(runtime, config, options = {}) {
|
|
|
127
127
|
diagnostic('error', 'runtime_action_failed', {
|
|
128
128
|
command: config.command, component: 'node', errorCode: code, retryable: event.retryable === true,
|
|
129
129
|
});
|
|
130
|
-
|
|
130
|
+
const enrollmentMessages = {
|
|
131
|
+
ENROLLMENT_EXPIRED: 'agent.enrollment_expired',
|
|
132
|
+
ENROLLMENT_REJECTED: 'agent.enrollment_rejected',
|
|
133
|
+
ENROLLMENT_CONFLICT: 'agent.enrollment_conflict',
|
|
134
|
+
ENROLLMENT_REQUEST_FAILED: 'agent.enrollment_request_failed',
|
|
135
|
+
};
|
|
136
|
+
failure = new CliError(t(enrollmentMessages[code] || 'agent.runtime_failed'), { code, details: { retryable: event.retryable === true } });
|
|
131
137
|
stop('SIGTERM');
|
|
132
138
|
return;
|
|
133
139
|
}
|
|
@@ -268,8 +268,19 @@ function extractResources(markdown) {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
function parsePrdMarkdown(markdown) {
|
|
271
|
+
const navigationVisibility = [];
|
|
272
|
+
let applicationTheme;
|
|
273
|
+
for (const match of String(markdown || '').matchAll(/```json\s*\n([\s\S]*?)\n```/g)) {
|
|
274
|
+
try {
|
|
275
|
+
const handoff = JSON.parse(match[1]);
|
|
276
|
+
if (Array.isArray(handoff.navigationVisibility)) {navigationVisibility.push(...handoff.navigationVisibility);}
|
|
277
|
+
if (Object.prototype.hasOwnProperty.call(handoff, 'applicationTheme')) {applicationTheme = handoff.applicationTheme;}
|
|
278
|
+
} catch { /* Other fenced examples are not the structured execution handoff. */ }
|
|
279
|
+
}
|
|
271
280
|
return {
|
|
272
281
|
resources: extractResources(markdown),
|
|
282
|
+
navigationVisibility,
|
|
283
|
+
...(applicationTheme !== undefined ? { applicationTheme } : {}),
|
|
273
284
|
};
|
|
274
285
|
}
|
|
275
286
|
|
|
@@ -451,6 +462,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
|
|
|
451
462
|
const parsedManifest = parseBuildManifest(options.buildManifest);
|
|
452
463
|
const items = [];
|
|
453
464
|
const assetExecution = auditAssetExecution(options.buildManifest?.assetExecution, options.buildManifest?.assetTasks);
|
|
465
|
+
let themeVerification;
|
|
454
466
|
|
|
455
467
|
function addItem(id, source, status, severity, message, details = {}) {
|
|
456
468
|
items.push(makeRiskItem(id, source, status, severity, message, details));
|
|
@@ -465,6 +477,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
|
|
|
465
477
|
verdict,
|
|
466
478
|
mode: 'delivery_risk_radar',
|
|
467
479
|
assetExecution,
|
|
480
|
+
...(themeVerification ? { themeVerification } : {}),
|
|
468
481
|
appType,
|
|
469
482
|
prdPath: options.prdPath || '',
|
|
470
483
|
buildManifestPath: options.buildManifestPath || '',
|
|
@@ -510,7 +523,7 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
|
|
|
510
523
|
const fetchForms = deps.fetchForms || fetchFormPageList;
|
|
511
524
|
let forms = [];
|
|
512
525
|
try {
|
|
513
|
-
forms = await fetchForms(appType, authRef);
|
|
526
|
+
forms = await fetchForms(appType, authRef, { includeNavigation: true });
|
|
514
527
|
} catch (error) {
|
|
515
528
|
addItem('app_resource_list_read_failed', 'remote.resource_list', 'fail', 'blocker', '目标应用资源列表读取失败,无法执行数量完整性检查', {
|
|
516
529
|
expected: { appType, readable: true },
|
|
@@ -522,6 +535,55 @@ async function evaluatePrdCompleteness(markdown, options = {}, deps = {}) {
|
|
|
522
535
|
const actualResources = forms.map(makeActualResource);
|
|
523
536
|
const manifestResources = parsedManifest.resources.filter(resource => resource.required);
|
|
524
537
|
|
|
538
|
+
if (parsedPrd.applicationTheme !== undefined) {
|
|
539
|
+
const expected = parsedPrd.applicationTheme;
|
|
540
|
+
const { normalizeCssColorToHex } = require('./custom-theme');
|
|
541
|
+
const expectedColor = normalizeCssColorToHex(expected?.themeColor);
|
|
542
|
+
themeVerification = { configurationReadback: false, verified: false, runtimeVerified: false };
|
|
543
|
+
try {
|
|
544
|
+
if (!expectedColor || !['light', 'dark'].includes(expected?.navTheme)) {throw new Error('INVALID_THEME_EXPECTATION');}
|
|
545
|
+
const { fetchCurrentApp, getThemeSettings, getNavigationSettings } = require('./update-app');
|
|
546
|
+
const app = await (deps.fetchApp || fetchCurrentApp)(appType, authRef);
|
|
547
|
+
if (!app || typeof app !== 'object' || Array.isArray(app) || !Object.keys(app).length) {throw new Error('APP_SETTINGS_UNAVAILABLE');}
|
|
548
|
+
const settings = getThemeSettings(app);
|
|
549
|
+
const rawNav = getNavigationSettings(app, ['navTheme']).navTheme;
|
|
550
|
+
const actual = {
|
|
551
|
+
colour: settings.colour,
|
|
552
|
+
themeColor: normalizeCssColorToHex(settings.themeColor),
|
|
553
|
+
navTheme: ['white', 'gray'].includes(rawNav) ? 'light' : rawNav,
|
|
554
|
+
customThemeEnabled: settings.customThemeStyle?.enabled,
|
|
555
|
+
hasCssUrl: typeof settings.customThemeStyle?.cssUrl === 'string' && !!settings.customThemeStyle.cssUrl.trim(),
|
|
556
|
+
};
|
|
557
|
+
const matched = actual.colour === 'custom' && actual.themeColor === expectedColor
|
|
558
|
+
&& actual.navTheme === expected.navTheme && actual.customThemeEnabled === true && actual.hasCssUrl;
|
|
559
|
+
themeVerification = { ...themeVerification, configurationReadback: true, verified: matched, expected, actual };
|
|
560
|
+
if (!matched) {
|
|
561
|
+
addItem('application_theme_mismatch', 'remote.application_theme', 'fail', 'blocker',
|
|
562
|
+
t('check_prd_completeness.application_theme_mismatch'), { expected, actual });
|
|
563
|
+
}
|
|
564
|
+
} catch {
|
|
565
|
+
addItem('application_theme_not_checked', 'remote.application_theme', 'not_checked', 'warning',
|
|
566
|
+
t('check_prd_completeness.application_theme_not_checked'), { expected });
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Reuse the resource-list readback. Page renderNav and menu visibility are
|
|
571
|
+
// independent; a successful publication cannot prove the latter.
|
|
572
|
+
for (const expected of parsedPrd.navigationVisibility) {
|
|
573
|
+
const matches = parsedManifest.resources.filter(resource => resource.name === expected?.name && resource.kind === 'display-page');
|
|
574
|
+
const ids = [...new Set(matches.map(resource => resource.formUuid).filter(Boolean))];
|
|
575
|
+
const actual = ids.length === 1 ? forms.find(form => form.formUuid === ids[0]) : undefined;
|
|
576
|
+
const known = value => typeof value === 'boolean';
|
|
577
|
+
const valid = expected?.hidden === true && expected?.mobileHidden === true;
|
|
578
|
+
if (valid && actual && (actual.hidden === false || actual.mobileHidden === false)) {
|
|
579
|
+
addItem('frontend_navigation_visible', 'remote.navigation', 'fail', 'blocker',
|
|
580
|
+
t('check_prd_completeness.frontend_navigation_visible', expected.name), { expected, actual });
|
|
581
|
+
} else if (!valid || !actual || !known(actual.hidden) || !known(actual.mobileHidden)) {
|
|
582
|
+
addItem('frontend_navigation_not_checked', 'prd.navigationVisibility', 'not_checked', 'warning',
|
|
583
|
+
t('check_prd_completeness.frontend_navigation_not_checked', expected?.name || ''), { expected, actual: actual || null });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
525
587
|
if (!options.buildManifest) {
|
|
526
588
|
addItem('build_manifest_missing', 'build_manifest', 'not_checked', 'info', '未提供 build-manifest.json,只能基于 PRD 资源 hints 做风险提示', {
|
|
527
589
|
expected: '--build-manifest <file>',
|
|
@@ -8,7 +8,7 @@ function resolveLocalizedText(value, fallback = '') {
|
|
|
8
8
|
return parseYidaI18n(value, fallback, { parseJson: false });
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function normalizeFormNavigationNode(node) {
|
|
11
|
+
function normalizeFormNavigationNode(node, options = {}) {
|
|
12
12
|
if (!node || node.navType === 'SYSTEM' || !node.formUuid) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
@@ -21,10 +21,14 @@ function normalizeFormNavigationNode(node) {
|
|
|
21
21
|
),
|
|
22
22
|
formType: node.formType || '',
|
|
23
23
|
pathName: node.pathName || '',
|
|
24
|
+
...(options.includeNavigation ? {
|
|
25
|
+
hidden: ['y', true].includes(node.hidden) ? true : ['n', false].includes(node.hidden) ? false : null,
|
|
26
|
+
mobileHidden: ['y', true].includes(node.mobileHidden) ? true : ['n', false].includes(node.mobileHidden) ? false : null,
|
|
27
|
+
} : {}),
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
async function fetchFormPageList(appType, authRef) {
|
|
31
|
+
async function fetchFormPageList(appType, authRef, options = {}) {
|
|
28
32
|
const result = await createYidaClient({ authRef }).get(
|
|
29
33
|
`/dingtalk/web/${appType}/query/formnav/getFormNavigationListByOrder.json`,
|
|
30
34
|
{ _api: 'Nav.queryList', _mock: false }
|
|
@@ -39,7 +43,7 @@ async function fetchFormPageList(appType, authRef) {
|
|
|
39
43
|
|
|
40
44
|
const items = Array.isArray(result.content) ? result.content : [];
|
|
41
45
|
return items
|
|
42
|
-
.map(normalizeFormNavigationNode)
|
|
46
|
+
.map(node => normalizeFormNavigationNode(node, options))
|
|
43
47
|
.filter(Boolean);
|
|
44
48
|
}
|
|
45
49
|
|
package/lib/app/update-app.js
CHANGED
package/lib/auth/token-auth.js
CHANGED
|
@@ -229,6 +229,26 @@ function createTokenAuthRequiredError(reason, sourceError) {
|
|
|
229
229
|
);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
function createAuthProfileSelectionError(status) {
|
|
233
|
+
const reason = status.failure_reason || status.status;
|
|
234
|
+
if (!['profile_required', 'profile_not_found'].includes(reason)) {return null;}
|
|
235
|
+
const { CliError } = require('../core/cli-error');
|
|
236
|
+
const { t } = require('../core/i18n');
|
|
237
|
+
return new CliError(t(`common.auth_${reason}`), {
|
|
238
|
+
code: reason === 'profile_required' ? 'AUTH_PROFILE_REQUIRED' : 'AUTH_PROFILE_NOT_FOUND',
|
|
239
|
+
details: {
|
|
240
|
+
authMode: 'token',
|
|
241
|
+
status: reason,
|
|
242
|
+
failure_reason: reason,
|
|
243
|
+
auth_profile: status.auth_profile,
|
|
244
|
+
candidate_count: status.candidate_count,
|
|
245
|
+
candidates: status.candidates,
|
|
246
|
+
next_step: status.next_step,
|
|
247
|
+
next_step_commands: status.next_step_commands,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
232
252
|
function createTokenRefreshFailedError(reason, sourceError) {
|
|
233
253
|
const { CliError } = require('../core/cli-error');
|
|
234
254
|
return new CliError(
|
|
@@ -630,7 +650,12 @@ async function tokenLogout(options = {}) {
|
|
|
630
650
|
}
|
|
631
651
|
|
|
632
652
|
async function getAccessToken(options = {}) {
|
|
633
|
-
|
|
653
|
+
const resolution = resolveTokenSession(options);
|
|
654
|
+
let session = resolution.session;
|
|
655
|
+
if (!session) {
|
|
656
|
+
const selectionError = createAuthProfileSelectionError(resolution);
|
|
657
|
+
if (selectionError) {throw selectionError;}
|
|
658
|
+
}
|
|
634
659
|
// Do not reject a token from local expiry metadata. The business endpoint is
|
|
635
660
|
// the authority: requestWithAutoLogin refreshes only after it rejects the
|
|
636
661
|
// access token, then retries with the newly persisted local session.
|
|
@@ -675,4 +700,5 @@ module.exports = {
|
|
|
675
700
|
resolveTokenBaseUrl,
|
|
676
701
|
isRefreshAuthRequired,
|
|
677
702
|
createTokenRefreshFailedError,
|
|
703
|
+
createAuthProfileSelectionError,
|
|
678
704
|
};
|
package/lib/auth/token-store.js
CHANGED
|
@@ -472,12 +472,31 @@ function saveAuthProfilePointer(session, options = {}) {
|
|
|
472
472
|
return pointer;
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
-
function
|
|
476
|
-
const
|
|
477
|
-
|
|
475
|
+
function getWorkspaceAuthProfilePointerFilePath(options = {}) {
|
|
476
|
+
const root = path.resolve(resolveProjectRoot(options));
|
|
477
|
+
const workspace = path.dirname(root);
|
|
478
|
+
if (isEnvTokenAuthMode(options.env || process.env) || path.basename(root) !== 'project' ||
|
|
479
|
+
fs.existsSync(path.join(workspace, 'config.json'))) {
|
|
478
480
|
return null;
|
|
479
481
|
}
|
|
480
482
|
try {
|
|
483
|
+
// Only bridge the conventional workspace -> workspace/project transition.
|
|
484
|
+
// A symlinked project belongs to its own workspace, not its lexical parent.
|
|
485
|
+
if (fs.realpathSync(root) !== path.join(fs.realpathSync(workspace), 'project')) {return null;}
|
|
486
|
+
return getAuthProfilePointerFilePath({ ...options, projectRoot: workspace, envName: resolveEnvName(options) });
|
|
487
|
+
} catch {return null;}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function loadAuthProfilePointer(options = {}) {
|
|
491
|
+
try {
|
|
492
|
+
let pointerFile = getAuthProfilePointerFilePath(options);
|
|
493
|
+
// An explicit child selection (including an unbound/corrupt pointer) wins.
|
|
494
|
+
// Reading inherited metadata never copies credentials or writes a pointer.
|
|
495
|
+
if (!fs.existsSync(pointerFile)) {
|
|
496
|
+
if (fs.existsSync(getTokenFilePath(options))) {return null;}
|
|
497
|
+
pointerFile = getWorkspaceAuthProfilePointerFilePath(options);
|
|
498
|
+
}
|
|
499
|
+
if (!pointerFile || !fs.existsSync(pointerFile)) {return null;}
|
|
481
500
|
const pointer = JSON.parse(fs.readFileSync(pointerFile, 'utf8'));
|
|
482
501
|
if (!pointer || typeof pointer !== 'object' || !pointer.auth_profile) {
|
|
483
502
|
return null;
|
|
@@ -491,6 +510,13 @@ function loadAuthProfilePointer(options = {}) {
|
|
|
491
510
|
function clearAuthProfilePointer(options = {}) {
|
|
492
511
|
try {
|
|
493
512
|
const pointerFile = getAuthProfilePointerFilePath(options);
|
|
513
|
+
const workspacePointerFile = getWorkspaceAuthProfilePointerFilePath(options);
|
|
514
|
+
if (workspacePointerFile && fs.existsSync(workspacePointerFile)) {
|
|
515
|
+
// Detaching this project must not revive its inherited workspace choice
|
|
516
|
+
// on the next command, or clear another project's workspace selection.
|
|
517
|
+
writeJsonFileAtomic(pointerFile, { version: AUTH_PROFILE_VERSION, detached: true });
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
494
520
|
if (fs.existsSync(pointerFile)) {
|
|
495
521
|
fs.unlinkSync(pointerFile);
|
|
496
522
|
}
|
|
@@ -1165,7 +1165,8 @@ const COMMAND_GROUPS = [
|
|
|
1165
1165
|
output: 'text|json',
|
|
1166
1166
|
args: [
|
|
1167
1167
|
{ name: 'buildPlan', type: 'file', required: true, source: 'positional', position: 0, builder_options: ['--build-plan'] },
|
|
1168
|
-
{ name: '
|
|
1168
|
+
{ name: 'setFile', type: 'file', source: 'option', builder_options: ['--set-file'], description: 'JSON object mapping field paths to typed values; use for Windows arrays, objects and quoted text. Requires --set or --set-file.' },
|
|
1169
|
+
{ name: 'set', type: 'string', source: 'option', builder_options: ['--set'], repeatable: true, description: 'Field assignment path=value; repeat --set for multiple changes. CSS token overrides use visualStyle.tokens.<token>=<value>.' },
|
|
1169
1170
|
{ name: 'materialize', type: 'boolean', source: 'option', builder_options: ['--materialize'] },
|
|
1170
1171
|
{ name: 'outputDir', type: 'string', source: 'option', builder_options: ['--output-dir'] },
|
|
1171
1172
|
{ name: 'json', type: 'boolean', source: 'option', builder_options: ['--json'] },
|
|
@@ -1492,7 +1493,7 @@ const COMMAND_GROUPS = [
|
|
|
1492
1493
|
}),
|
|
1493
1494
|
command('check-prd-completeness', ['check-prd-completeness'], 'check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]', 'help.cmd_check_prd_completeness', { managedRun: { scope: 'app', appParser: 'prd-completeness' },
|
|
1494
1495
|
output: 'json',
|
|
1495
|
-
notes: ['Checks resource counts and optional build-manifest.assetExecution recorded intervals. Include materialize.assetTasks and taskState records plus businessWork startedAt/endedAt; missing dispatch proof, missing overlap or assets before business in synchronous fallback produces needs_review. This is record consistency, not independent verification of host events.'],
|
|
1496
|
+
notes: ['Checks resource counts and optional build-manifest.assetExecution recorded intervals. Include materialize.assetTasks and taskState records plus businessWork startedAt/endedAt; missing dispatch proof, missing overlap or assets before business in synchronous fallback produces needs_review. This is record consistency, not independent verification of host events.', 'When the PRD execution handoff contains navigationVisibility, resolves frontend display pages through build-manifest resource IDs and checks PC/mobile menu hiding using the same remote resource-list read. A visible menu fails; unknown visibility or missing ID binding needs review. Checks PRD applicationTheme against live application settings: selected brand color, custom CSS enabled with a resource URL, and navigation tone. Mismatch fails; unreadable settings or invalid expectations need review. Reuses update-app readback field handling. Does not verify page renderNav, actual CSS content/loading, default routes or browser appearance.'],
|
|
1496
1497
|
}),
|
|
1497
1498
|
command('check-design', ['check-design'], 'check-design <design.md> [--prd <prd.md>] [--base-dir <dir>] [--json]', 'help.cmd_check_design', { managedRun: { scope: 'local' },
|
|
1498
1499
|
requiresLogin: false,
|
|
@@ -1624,13 +1625,14 @@ const COMMAND_GROUPS = [
|
|
|
1624
1625
|
} },
|
|
1625
1626
|
} },
|
|
1626
1627
|
output: 'json',
|
|
1627
|
-
noteKeys: ['help.data_notes'],
|
|
1628
|
+
noteKeys: ['help.data_notes', 'query_data.approver_query_help'],
|
|
1628
1629
|
notes: [
|
|
1629
1630
|
'Each condition uses key (field ID), componentName, type, operator and value. Query types: TextField/TextareaField/TableField TEXT; NumberField/RateField/DateField/CascadeDateField DOUBLE; RadioField/SelectField/CheckboxField/MultiSelectField/DepartmentSelectField ARRAY; EmployeeField/CascadeSelectField/CitySelectField STRING; ImageField/AttachmentField TEXT.',
|
|
1630
1631
|
'Ordinary form searchFormDatas accepts AND condition arrays via --search-json/--search-file. DateField: type DOUBLE, is/is_not, value ""; zero is filled. Query range endpoints as DateField with .start/.end: both empty means unfilled, both filled means complete.',
|
|
1631
1632
|
'Use synchronized NumberField counts (eq 0/gt 0) for any filled endpoint, multi-choice options or subform rows; maintain every write entry and backfill history. --resolve-aliases supports rangeAlias.start/.end and parentId aliases. Use server pagination and dynamicOrder. Guide: yida-skills/references/query-condition-guide.md; sample: yida-data-management empty-query.',
|
|
1632
1633
|
],
|
|
1633
1634
|
examples: [
|
|
1635
|
+
'openyida data query process APP_XXX FORM_XXX --current-approver USER_ID --count',
|
|
1634
1636
|
'openyida data query form APP_XXX FORM_XXX --page 1 --size 20 --search-json \'[{"key":"dateField_due","componentName":"DateField","type":"DOUBLE","operator":"is","value":""}]\'',
|
|
1635
1637
|
'openyida data create form APP_XXX FORM_XXX --expect-form-name 客户 --expect-form-type receipt --data-json \'{"textField_name":"客户 A","dateField_followUp":1787932800000}\'',
|
|
1636
1638
|
'openyida data delete form APP_XXX FORM_XXX --inst-id FINST_XXX --expect-form-name 客户 --expect-form-type receipt --confirm --json',
|
|
@@ -2083,7 +2085,9 @@ function summarizeLocalizedCommands(commands) {
|
|
|
2083
2085
|
derived_summary: 'execution.entryModeSummary is generated for PRD and preview from the selected mode; do not author it in build-plan.json.',
|
|
2084
2086
|
roles: ['service', 'management', 'workspace'],
|
|
2085
2087
|
entry_required_fields: ['key', 'name', 'role', 'menu', 'defaultMenuKey'],
|
|
2086
|
-
service_page_binding: 'sceneKey of the frontend page; standalone by default, preserve explicit platform-shell',
|
|
2088
|
+
service_page_binding: 'sceneKey of the frontend page; standalone by default, preserve explicit platform-shell. Missing page navigation for a multi-task service entry derives a custom top menu; single-task entries and explicit none retain their choice.',
|
|
2089
|
+
application_theme_handoff: 'Derived applicationTheme in the PRD execution handoff records final design themeColor/navTheme/designFile for full builds. Fast records the same contract. check-prd-completeness reads current application settings; publish does not install application themes. Resource-only changes add no theme task. Configuration matching is separate from browser acceptance and custom-page token consumption.',
|
|
2090
|
+
menu_visibility_handoff: 'Derived navigationVisibility in the PRD execution handoff, not a build-plan input: service-management hides frontend-only standalone display pages in PC/mobile platform menus. Shared forms and explicitly management-linked pages are preserved. Resolve names to build-manifest resource IDs; nav-group hide and readback are separate from page renderNav=false. Resource-only changes schedule no menu hiding.',
|
|
2087
2091
|
page_navigation_policy: getPageNavigationContract(),
|
|
2088
2092
|
local_menu_binding: 'Every entry containing local menus, including management/workspace, requires sceneKey matching exactly one page; each local menu requires resource equal to that page name and a nonempty viewKey.',
|
|
2089
2093
|
menu_target_types: ['local', 'submission', 'page', 'custom'],
|
package/lib/core/locales/en.js
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
agent: {
|
|
8
|
+
preflight_cli_detected: 'CLI detected; runtime will discover and report login, protocol and model status.',
|
|
9
|
+
enrollment_expired: 'The connection command expired. Get a fresh command from the page; do not retry the old command.',
|
|
10
|
+
enrollment_rejected: 'Connection authorization was rejected. Get a fresh command from the target page.',
|
|
11
|
+
enrollment_conflict: 'Connection authorization conflicts with existing state. Check agent status, then get a fresh command.',
|
|
12
|
+
enrollment_request_failed: 'Connection authorization failed. Check the network and run agent diagnose; do not automatically retry.',
|
|
8
13
|
preflight_advisory: 'The following is status information, not instructions to install software. Do not install, upgrade, log in to, or reconfigure any CLI unless the user explicitly requests it. Discovered signed-out CLIs are also reported; other CLIs are optional.',
|
|
9
14
|
preflight_probe_cancelled: 'Preflight was interrupted before enrollment. Repeat the connect command when ready.',
|
|
10
15
|
preflight_continue: 'Local CLI found. Connecting and reporting status; signed-out CLIs remain visible and cannot execute tasks until logged in.',
|
|
@@ -136,7 +141,7 @@ module.exports = {
|
|
|
136
141
|
cmd_list_forms: 'List forms/pages in an app',
|
|
137
142
|
cmd_aggregate_table: 'Manage aggregate tables (virtualView)',
|
|
138
143
|
cmd_get_schema: 'Get one form Schema or all form Schemas',
|
|
139
|
-
cmd_check_prd_completeness: 'Check
|
|
144
|
+
cmd_check_prd_completeness: 'Check resources, frontend menu hiding and application theme delivery',
|
|
140
145
|
cmd_er: 'Export app entity relationship diagram',
|
|
141
146
|
cmd_create_page: 'Create a custom display page',
|
|
142
147
|
cmd_build_page: 'Build Yida-compatible page source',
|
|
@@ -1011,6 +1016,8 @@ Examples:
|
|
|
1011
1016
|
data_check_failed: ' ⚠️ Data check failed, proceeding with modification',
|
|
1012
1017
|
},
|
|
1013
1018
|
common: {
|
|
1019
|
+
auth_profile_required: 'Multiple organizations have saved credentials. Run openyida auth profiles, then openyida auth profile switch <auth_profile> to select this project\'s organization.',
|
|
1020
|
+
auth_profile_not_found: 'The requested organization credentials were not found. Run openyida auth profiles to inspect saved organizations and select the correct profile.',
|
|
1014
1021
|
auth_identity_changed: 'Authentication identity changed during preflight; no write was issued.',
|
|
1015
1022
|
http_status: ' HTTP status: {0}',
|
|
1016
1023
|
http_response: ' HTTP response: {0}',
|
|
@@ -1548,6 +1555,10 @@ Examples:
|
|
|
1548
1555
|
},
|
|
1549
1556
|
|
|
1550
1557
|
check_prd_completeness: {
|
|
1558
|
+
application_theme_mismatch: 'The application theme does not match the selected project theme.',
|
|
1559
|
+
application_theme_not_checked: 'Application theme settings could not be verified; theme delivery remains unverified.',
|
|
1560
|
+
frontend_navigation_not_checked: 'Frontend menu hiding has not been verified: {0}',
|
|
1561
|
+
frontend_navigation_visible: 'Frontend page remains visible in the desktop or mobile management menu: {0}',
|
|
1551
1562
|
usage: 'Usage: openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]',
|
|
1552
1563
|
example: 'Example: openyida check-prd-completeness prd/order-management/prd.md --app-type APP_XXX --build-manifest prd/order-management/build-manifest.json --json',
|
|
1553
1564
|
prd_not_found: 'PRD file not found: {0}',
|
|
@@ -2036,6 +2047,11 @@ Options:
|
|
|
2036
2047
|
};
|
|
2037
2048
|
|
|
2038
2049
|
Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
|
|
2050
|
+
approver_query_help: 'Current process approver: --current-approver <userId> [--count] scans all visible pages and counts distinct instances. Incomplete scans or permission failures are errors.',
|
|
2051
|
+
approver_option_invalid: 'Invalid current-approver query option: {0}. Use an exact userId and scan RUNNING instances from page 1.',
|
|
2052
|
+
approver_incomplete: 'The current-approver scan could not complete. No complete count was returned; check error details, permissions and pagination.',
|
|
2053
|
+
approver_response_invalid: 'The process API response could not be verified; a current-approver count cannot be determined.',
|
|
2054
|
+
approver_scope_note: 'Counts only instances visible to the current login under server authorization; this does not imply access to all application data.',
|
|
2039
2055
|
association_derived_field_read_only: 'Derived association-form query field {0} is read-only and cannot be saved or updated. Write the source association field without the _id suffix.',
|
|
2040
2056
|
association_value_invalid: 'The write value for association-form field {0} is invalid. Use an array of objects where every item includes appType, formUuid, formType, instanceId, and title.',
|
|
2041
2057
|
command_unsupported: 'Unsupported data command: {0} {1}. Run openyida commands --json to inspect the actual capability.',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
agent: {
|
|
8
|
+
preflight_cli_detected: 'CLI 已发现;登录、协议和模型状态由 runtime 检测并上报。',
|
|
9
|
+
enrollment_expired: '连接指令已过期,请回到页面获取新的连接指令。不要重试旧指令。',
|
|
10
|
+
enrollment_rejected: '连接授权被拒绝,请从目标页面重新获取连接指令。',
|
|
11
|
+
enrollment_conflict: '连接授权状态冲突,请先检查 agent status,再从页面获取新指令。',
|
|
12
|
+
enrollment_request_failed: '连接授权请求失败,请检查网络并运行 agent diagnose;不要自动重复连接。',
|
|
8
13
|
preflight_advisory: '以下仅为状态信息,不是安装指令。除非用户明确要求,不得安装、升级、登录或修改任何 CLI 配置。已发现但未登录的 CLI 也会展示状态,无需补齐其他 CLI。',
|
|
9
14
|
preflight_probe_cancelled: '预检已中止,尚未开始连接。准备好后重新执行连接命令。',
|
|
10
15
|
preflight_continue: '已发现本地 CLI,将继续连接并上报状态;未登录的 CLI 会显示“未登录”,登录前不能执行任务。',
|
|
@@ -136,7 +141,7 @@ module.exports = {
|
|
|
136
141
|
cmd_list_forms: '列出应用下的表单/页面',
|
|
137
142
|
cmd_aggregate_table: '管理聚合表(virtualView)',
|
|
138
143
|
cmd_get_schema: '获取单个或全部表单 Schema',
|
|
139
|
-
cmd_check_prd_completeness: '
|
|
144
|
+
cmd_check_prd_completeness: '检查资源数量、前台菜单隐藏及应用主题同步',
|
|
140
145
|
cmd_er: '导出应用实体关系图',
|
|
141
146
|
cmd_create_page: '创建自定义展示页面',
|
|
142
147
|
cmd_build_page: '构建宜搭兼容页面源码',
|
|
@@ -982,6 +987,8 @@ openyida - 宜搭命令行工具
|
|
|
982
987
|
data_check_failed: ' ⚠️ 数据量检查失败,继续执行修改',
|
|
983
988
|
},
|
|
984
989
|
common: {
|
|
990
|
+
auth_profile_required: '已有多个组织的登录凭证,请执行 openyida auth profiles,并用 openyida auth profile switch <auth_profile> 选择本项目的组织。',
|
|
991
|
+
auth_profile_not_found: '未找到指定组织的登录凭证,请执行 openyida auth profiles 核对已有组织,再选择正确的登录凭证。',
|
|
985
992
|
auth_identity_changed: '校验期间登录身份发生变化,本次未发起写入。',
|
|
986
993
|
http_status: ' HTTP 状态码: {0}',
|
|
987
994
|
http_response: ' HTTP 响应: {0}',
|
|
@@ -1545,6 +1552,10 @@ openyida - 宜搭命令行工具
|
|
|
1545
1552
|
},
|
|
1546
1553
|
|
|
1547
1554
|
check_prd_completeness: {
|
|
1555
|
+
application_theme_mismatch: '应用主题与已选项目主题不一致,尚未完成统一换肤。',
|
|
1556
|
+
application_theme_not_checked: '应用主题配置未能核验,不能确认已完成统一换肤。',
|
|
1557
|
+
frontend_navigation_not_checked: '前台页面的后台菜单隐藏尚未核验:{0}',
|
|
1558
|
+
frontend_navigation_visible: '前台页面仍出现在后台 PC 或移动端菜单:{0}',
|
|
1548
1559
|
usage: '用法: openyida check-prd-completeness <prd.md> --app-type <appType> [--build-manifest <file>] [--json]',
|
|
1549
1560
|
example: '示例: openyida check-prd-completeness prd/订单管理/prd.md --app-type APP_XXX --build-manifest prd/订单管理/build-manifest.json --json',
|
|
1550
1561
|
prd_not_found: 'PRD 文件不存在:{0}',
|
|
@@ -1990,6 +2001,11 @@ openyida - 宜搭命令行工具
|
|
|
1990
2001
|
};
|
|
1991
2002
|
|
|
1992
2003
|
Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
|
|
2004
|
+
approver_query_help: '流程当前审批人:--current-approver <userId> [--count],完整分页并按实例去重;结果仅限当前登录人的可见范围,不完整或无权限会报错。',
|
|
2005
|
+
approver_option_invalid: '当前审批人查询参数无效:{0}。请使用真实 userId,并从第1页查询运行中实例。',
|
|
2006
|
+
approver_incomplete: '无法完成当前审批人统计,未返回完整条数。请检查错误详情、权限及分页范围。',
|
|
2007
|
+
approver_response_invalid: '流程接口返回结构无法确认,不能据此统计当前审批人条数。',
|
|
2008
|
+
approver_scope_note: '仅统计当前登录人经平台服务端授权可见的实例;不代表拥有全应用数据权限。',
|
|
1993
2009
|
association_derived_field_read_only: '关联表单查询派生字段 {0} 只读,不能用于保存或更新。请写入不带 _id 后缀的原始关联表单字段。',
|
|
1994
2010
|
association_value_invalid: '关联表单字段 {0} 的写入值无效。必须使用对象数组,每项完整包含 appType、formUuid、formType、instanceId 和 title。',
|
|
1995
2011
|
command_unsupported: '不支持的数据命令:{0} {1}。请使用 openyida commands --json 查询真实能力。',
|