openyida 2026.8.30 → 2026.8.31
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 +3 -3
- package/bin/yida.js +9 -2
- package/lib/app/build-page.js +26 -4
- package/lib/app/check-page.js +27 -7
- package/lib/app/create-form/field-normalizers.js +1 -0
- package/lib/app/page-compat.js +19 -0
- package/lib/app/publish.js +22 -1
- package/lib/app/services/form-validation.js +1 -0
- package/lib/auth/token-store.js +2 -2
- package/lib/core/cli-error.js +46 -0
- package/lib/core/command-manifest.js +4 -3
- package/lib/core/copy.js +57 -0
- package/lib/core/locales/en.js +9 -0
- package/lib/core/locales/zh.js +9 -0
- package/lib/core/query-data.js +282 -5
- package/lib/report/append.js +28 -14
- package/lib/report/contract.js +35 -5
- package/lib/report/create-report.js +1 -1
- package/lib/report/index.js +33 -24
- package/lib/report/schema-template.js +0 -28
- package/package.json +1 -1
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +13 -1
- package/yida-skills/skills/yida-create-form-page/SKILL.md +2 -0
- package/yida-skills/skills/yida-data-management/SKILL.md +25 -2
- package/yida-skills/skills/yida-report/SKILL.md +26 -3
- package/yida-skills/skills/yida-report/references/examples.md +1 -1
- package/yida-skills/skills-index.json +6 -4
package/README.md
CHANGED
|
@@ -430,7 +430,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
430
430
|
|
|
431
431
|
| Command | Description |
|
|
432
432
|
|---------|-------------|
|
|
433
|
-
| `openyida data <query\|get\|create\|update\|delete> <
|
|
433
|
+
| `openyida data <query\|get\|create\|update> <resource> ... \| delete form <appType> <formUuid> --inst-id <id> --confirm [--json]` | Unified data management (form/process/task/subform) |
|
|
434
434
|
| `openyida task-center <type> [options]` | Global task center (todo/processed/cc etc.) |
|
|
435
435
|
| `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
|
|
436
436
|
| `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
|
|
@@ -462,8 +462,8 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
462
462
|
|
|
463
463
|
| Command | Description |
|
|
464
464
|
|---------|-------------|
|
|
465
|
-
| `openyida create-report <appType> "<name>" ... [--open\|--no-open]` | Create a Yida report |
|
|
466
|
-
| `openyida append-chart <appType> <reportId> ... [--open\|--no-open]` | Append chart to existing report |
|
|
465
|
+
| `openyida create-report <appType> "<name>" ... [--json] [--open\|--no-open]` | Create a Yida report |
|
|
466
|
+
| `openyida append-chart <appType> <reportId> ... [--json] [--open\|--no-open]` | Append chart to existing report |
|
|
467
467
|
| `openyida report inspect <appType> <reportId> --json` | Inspect report runtime bindings (read-only) |
|
|
468
468
|
|
|
469
469
|
### Connectors
|
package/bin/yida.js
CHANGED
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
const { version: currentVersion } = require('../package.json');
|
|
14
14
|
const { t } = require('../lib/core/i18n');
|
|
15
15
|
const { warn } = require('../lib/core/chalk');
|
|
16
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
CliError,
|
|
18
|
+
isCliError,
|
|
19
|
+
shouldUseStructuredErrorOutput,
|
|
20
|
+
toErrorPayload,
|
|
21
|
+
} = require('../lib/core/cli-error');
|
|
17
22
|
const { COMMAND_GROUPS, buildCommandManifest, findCommandSuggestion } = require('../lib/core/command-manifest');
|
|
18
23
|
|
|
19
24
|
const command = process.argv[2];
|
|
@@ -492,11 +497,13 @@ const MANIFEST_HELP_PATHS = Object.freeze({
|
|
|
492
497
|
report: ['report'],
|
|
493
498
|
'create-process': ['create-process'],
|
|
494
499
|
'create-report': ['create-report'],
|
|
500
|
+
'append-chart': ['append-chart'],
|
|
495
501
|
'save-share-config': ['save-share-config'],
|
|
496
502
|
'verify-short-url': ['verify-short-url'],
|
|
497
503
|
'integration-create': ['integration', 'create'],
|
|
498
504
|
'save-permission': ['save-permission'],
|
|
499
505
|
'get-permission': ['get-permission'],
|
|
506
|
+
copy: ['copy'],
|
|
500
507
|
});
|
|
501
508
|
|
|
502
509
|
function printManifestCommandHelp(commandName) {
|
|
@@ -1261,7 +1268,7 @@ async function main() {
|
|
|
1261
1268
|
|
|
1262
1269
|
main()
|
|
1263
1270
|
.catch((err) => {
|
|
1264
|
-
if (
|
|
1271
|
+
if (shouldUseStructuredErrorOutput(err, args)) {
|
|
1265
1272
|
console.error(JSON.stringify(toErrorPayload(err), null, 2));
|
|
1266
1273
|
} else if (isCliError(err)) {
|
|
1267
1274
|
warn(t('cli.exec_failed', err.message));
|
package/lib/app/build-page.js
CHANGED
|
@@ -65,6 +65,31 @@ async function run(args) {
|
|
|
65
65
|
modern: options.modern,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
const throwBuildFailure = () => {
|
|
69
|
+
const matchedPrimaryIssue = buildResult.errors.find(issue => issue.code === 'UNSUPPORTED_HOOK') || buildResult.errors[0];
|
|
70
|
+
const primaryIssue = matchedPrimaryIssue ? JSON.parse(JSON.stringify(matchedPrimaryIssue)) : undefined;
|
|
71
|
+
throwCommandError(t('build_page.failed'), {
|
|
72
|
+
code: 'BUILD_PAGE_FAILED',
|
|
73
|
+
details: {
|
|
74
|
+
errors: buildResult.errors,
|
|
75
|
+
primaryIssue,
|
|
76
|
+
retryable: false,
|
|
77
|
+
retrySafe: true,
|
|
78
|
+
sideEffectState: 'none',
|
|
79
|
+
sourceRepairable: buildResult.errors.length > 0,
|
|
80
|
+
nextAction: {
|
|
81
|
+
type: 'edit_source_then_recheck',
|
|
82
|
+
commandId: 'check-page',
|
|
83
|
+
args: { sourcePath },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (!buildResult.ok && options.json) {
|
|
90
|
+
throwBuildFailure();
|
|
91
|
+
}
|
|
92
|
+
|
|
68
93
|
if (options.json) {
|
|
69
94
|
console.log(JSON.stringify({
|
|
70
95
|
ok: buildResult.ok,
|
|
@@ -92,10 +117,7 @@ async function run(args) {
|
|
|
92
117
|
}
|
|
93
118
|
|
|
94
119
|
if (!buildResult.ok) {
|
|
95
|
-
|
|
96
|
-
code: 'BUILD_PAGE_FAILED',
|
|
97
|
-
details: { errors: buildResult.errors },
|
|
98
|
-
});
|
|
120
|
+
throwBuildFailure();
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
if (!options.json) {
|
package/lib/app/check-page.js
CHANGED
|
@@ -44,6 +44,32 @@ async function run(args) {
|
|
|
44
44
|
const buildErrors = buildResult && buildResult.errors ? buildResult.errors : [];
|
|
45
45
|
const ok = buildErrors.length === 0 && lintResult.errors.length === 0;
|
|
46
46
|
|
|
47
|
+
const throwCheckFailure = () => {
|
|
48
|
+
const matchedPrimaryIssue = buildErrors.find(issue => issue.code === 'UNSUPPORTED_HOOK') || buildErrors[0] || lintResult.errors[0];
|
|
49
|
+
const primaryIssue = matchedPrimaryIssue ? JSON.parse(JSON.stringify(matchedPrimaryIssue)) : undefined;
|
|
50
|
+
throwCommandError(t('check_page.failed'), {
|
|
51
|
+
code: 'CHECK_PAGE_FAILED',
|
|
52
|
+
details: {
|
|
53
|
+
errors: lintResult.errors,
|
|
54
|
+
buildErrors,
|
|
55
|
+
primaryIssue,
|
|
56
|
+
retryable: false,
|
|
57
|
+
retrySafe: true,
|
|
58
|
+
sideEffectState: 'none',
|
|
59
|
+
sourceRepairable: buildErrors.length > 0,
|
|
60
|
+
nextAction: buildErrors.length > 0 ? {
|
|
61
|
+
type: 'edit_source_then_recheck',
|
|
62
|
+
commandId: 'check-page',
|
|
63
|
+
args: { sourcePath },
|
|
64
|
+
} : undefined,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (!ok && options.json) {
|
|
70
|
+
throwCheckFailure();
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
if (options.json) {
|
|
48
74
|
console.log(JSON.stringify({
|
|
49
75
|
ok,
|
|
@@ -67,13 +93,7 @@ async function run(args) {
|
|
|
67
93
|
}
|
|
68
94
|
|
|
69
95
|
if (!ok) {
|
|
70
|
-
|
|
71
|
-
code: 'CHECK_PAGE_FAILED',
|
|
72
|
-
details: {
|
|
73
|
-
errors: lintResult.errors,
|
|
74
|
-
buildErrors,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
96
|
+
throwCheckFailure();
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
|
package/lib/app/page-compat.js
CHANGED
|
@@ -31,6 +31,12 @@ const SUPPORTED_REMOVABLE_IMPORTS = new Set([
|
|
|
31
31
|
]);
|
|
32
32
|
|
|
33
33
|
const SUPPORTED_HOOKS = new Set(['useState', 'useEffect']);
|
|
34
|
+
const UNSUPPORTED_HOOK_REPAIRS = Object.freeze({
|
|
35
|
+
useReducer: 'Replace the reducer with supported useState state updates, or move the page to .canvas.jsx when reducer semantics are required.',
|
|
36
|
+
useMemo: 'Compute the derived value with a plain local expression, or move the page to .canvas.jsx when memoization is required.',
|
|
37
|
+
useCallback: 'Use a plain local function, or move the page to .canvas.jsx when stable callback identity is required.',
|
|
38
|
+
useLayoutEffect: 'Use supported useEffect(..., []) for mount-only work, or move the page to .canvas.jsx when layout effects are required.',
|
|
39
|
+
});
|
|
34
40
|
const REQUIRED_RUNTIME_EXPORTS = {
|
|
35
41
|
getCustomState: [
|
|
36
42
|
'export function getCustomState(key) {',
|
|
@@ -783,7 +789,20 @@ function collectUnsupportedHooks(ast, errors) {
|
|
|
783
789
|
errors.push({
|
|
784
790
|
code: 'UNSUPPORTED_HOOK',
|
|
785
791
|
message: `${name} is not supported in OpenYida authoring mode.`,
|
|
792
|
+
hook: name,
|
|
786
793
|
line: pathRef.node.loc && pathRef.node.loc.start && pathRef.node.loc.start.line,
|
|
794
|
+
retryable: false,
|
|
795
|
+
retrySafe: true,
|
|
796
|
+
sideEffectState: 'none',
|
|
797
|
+
sourceRepairable: true,
|
|
798
|
+
supportedHooks: [...SUPPORTED_HOOKS],
|
|
799
|
+
recommendedAuthoringMode: 'canvas',
|
|
800
|
+
replacement: UNSUPPORTED_HOOK_REPAIRS[name]
|
|
801
|
+
|| 'Use only supported useState/useEffect(..., []) authoring, or move the page to .canvas.jsx for full React Hook support.',
|
|
802
|
+
nextAction: {
|
|
803
|
+
type: 'edit_source_then_recheck',
|
|
804
|
+
commandId: 'check-page',
|
|
805
|
+
},
|
|
787
806
|
});
|
|
788
807
|
}
|
|
789
808
|
},
|
package/lib/app/publish.js
CHANGED
|
@@ -564,7 +564,28 @@ async function runMain(argv) {
|
|
|
564
564
|
buildResult.errors.forEach((issue) => warn(`${issue.code}: ${issue.message}`));
|
|
565
565
|
const lintErrors = buildResult.lint && buildResult.lint.errors ? buildResult.lint.errors : [];
|
|
566
566
|
lintErrors.forEach((issue) => warn(`${issue.rule}: ${issue.message}`));
|
|
567
|
-
|
|
567
|
+
const matchedPrimaryIssue = buildResult.errors.find(issue => issue.code === 'UNSUPPORTED_HOOK')
|
|
568
|
+
|| buildResult.errors[0]
|
|
569
|
+
|| lintErrors[0];
|
|
570
|
+
const primaryIssue = matchedPrimaryIssue ? JSON.parse(JSON.stringify(matchedPrimaryIssue)) : undefined;
|
|
571
|
+
throw new CliError(t('build_page.failed'), {
|
|
572
|
+
code: 'BUILD_PAGE_FAILED',
|
|
573
|
+
details: {
|
|
574
|
+
sourcePath,
|
|
575
|
+
errors: buildResult.errors,
|
|
576
|
+
lintErrors,
|
|
577
|
+
primaryIssue,
|
|
578
|
+
retryable: false,
|
|
579
|
+
retrySafe: true,
|
|
580
|
+
sideEffectState: 'none',
|
|
581
|
+
sourceRepairable: buildResult.errors.length > 0,
|
|
582
|
+
nextAction: {
|
|
583
|
+
type: 'edit_source_then_recheck',
|
|
584
|
+
commandId: 'check-page',
|
|
585
|
+
args: { sourcePath },
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
});
|
|
568
589
|
}
|
|
569
590
|
sourcePath = buildResult.outputPath;
|
|
570
591
|
success(t('build_page.output', sourcePath));
|
package/lib/auth/token-store.js
CHANGED
|
@@ -368,7 +368,7 @@ function toAuthProfileCandidate(session = {}) {
|
|
|
368
368
|
function buildAuthProfileNextStep(status) {
|
|
369
369
|
if (status === 'profile_required') {
|
|
370
370
|
return {
|
|
371
|
-
next_step: 'Multiple auth profiles are available. Run openyida auth profiles, then run openyida auth profile switch <auth_profile> with the exact profile id.
|
|
371
|
+
next_step: 'Multiple auth profiles are available. Run openyida auth profiles, then run openyida auth profile switch <auth_profile> with the exact profile id. The switch updates the current project auth pointer.',
|
|
372
372
|
next_step_commands: [
|
|
373
373
|
'openyida auth profiles',
|
|
374
374
|
'openyida auth profile switch <auth_profile>',
|
|
@@ -754,7 +754,7 @@ function selectUserProfileSession(options = {}, selection = {}) {
|
|
|
754
754
|
status: 'profile_required',
|
|
755
755
|
candidate_count: candidates.length,
|
|
756
756
|
candidates: candidates.map(toAuthProfileCandidate),
|
|
757
|
-
message: 'multiple auth profiles found;
|
|
757
|
+
message: 'multiple auth profiles found; switch the current project auth pointer with openyida auth profile switch <auth_profile>',
|
|
758
758
|
...buildAuthProfileNextStep('profile_required'),
|
|
759
759
|
};
|
|
760
760
|
}
|
package/lib/core/cli-error.js
CHANGED
|
@@ -37,6 +37,26 @@ function toErrorPayload(error) {
|
|
|
37
37
|
if (details.nextStep) {
|
|
38
38
|
payload.nextStep = details.nextStep;
|
|
39
39
|
}
|
|
40
|
+
[
|
|
41
|
+
'partial',
|
|
42
|
+
'residual',
|
|
43
|
+
'retryable',
|
|
44
|
+
'retrySafe',
|
|
45
|
+
'sideEffectState',
|
|
46
|
+
'readbackAllowed',
|
|
47
|
+
'recommendedRecovery',
|
|
48
|
+
'nextAction',
|
|
49
|
+
'target',
|
|
50
|
+
'deleted',
|
|
51
|
+
'alreadyAbsent',
|
|
52
|
+
'mutationAccepted',
|
|
53
|
+
'readbackVerified',
|
|
54
|
+
'status',
|
|
55
|
+
].forEach((key) => {
|
|
56
|
+
if (Object.prototype.hasOwnProperty.call(details, key)) {
|
|
57
|
+
payload[key] = details[key];
|
|
58
|
+
}
|
|
59
|
+
});
|
|
40
60
|
}
|
|
41
61
|
payload.details = details;
|
|
42
62
|
}
|
|
@@ -44,8 +64,34 @@ function toErrorPayload(error) {
|
|
|
44
64
|
return payload;
|
|
45
65
|
}
|
|
46
66
|
|
|
67
|
+
function shouldUseStructuredErrorOutput(error, args = []) {
|
|
68
|
+
if (!isCliError(error)) {return false;}
|
|
69
|
+
if (Array.isArray(args) && args.includes('--json')) {return true;}
|
|
70
|
+
const details = error.details;
|
|
71
|
+
const ownedPartial = !!(
|
|
72
|
+
details
|
|
73
|
+
&& typeof details === 'object'
|
|
74
|
+
&& !Array.isArray(details)
|
|
75
|
+
&& details.partial === true
|
|
76
|
+
&& details.residual
|
|
77
|
+
&& typeof details.residual === 'object'
|
|
78
|
+
&& details.retrySafe === false
|
|
79
|
+
);
|
|
80
|
+
const mutationOutcomeUnknown = !!(
|
|
81
|
+
details
|
|
82
|
+
&& typeof details === 'object'
|
|
83
|
+
&& !Array.isArray(details)
|
|
84
|
+
&& details.target
|
|
85
|
+
&& typeof details.target === 'object'
|
|
86
|
+
&& details.retrySafe === false
|
|
87
|
+
&& details.sideEffectState === 'unknown'
|
|
88
|
+
);
|
|
89
|
+
return ownedPartial || mutationOutcomeUnknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
47
92
|
module.exports = {
|
|
48
93
|
CliError,
|
|
49
94
|
isCliError,
|
|
95
|
+
shouldUseStructuredErrorOutput,
|
|
50
96
|
toErrorPayload,
|
|
51
97
|
};
|
|
@@ -1168,9 +1168,10 @@ const COMMAND_GROUPS = [
|
|
|
1168
1168
|
id: 'data',
|
|
1169
1169
|
titleKey: 'help.group_data',
|
|
1170
1170
|
commands: [
|
|
1171
|
-
command('data', ['data'], 'data <query|get|create|update|delete> <
|
|
1171
|
+
command('data', ['data'], 'data <query|get|create|update> <resource> ... | delete form <appType> <formUuid> --inst-id <id> --confirm [--json]', 'help.cmd_data', {
|
|
1172
1172
|
examples: [
|
|
1173
1173
|
'openyida data create form APP_XXX FORM_XXX --data-json \'{"textField_name":"客户 A","dateField_followUp":1787932800000}\'',
|
|
1174
|
+
'openyida data delete form APP_XXX FORM_XXX --inst-id FINST_XXX --confirm --json',
|
|
1174
1175
|
],
|
|
1175
1176
|
}),
|
|
1176
1177
|
command('task-center', ['task-center'], 'task-center <type> [options]', 'help.cmd_task_center'),
|
|
@@ -1223,8 +1224,8 @@ const COMMAND_GROUPS = [
|
|
|
1223
1224
|
id: 'report',
|
|
1224
1225
|
titleKey: 'help.group_report',
|
|
1225
1226
|
commands: [
|
|
1226
|
-
command('create-report', ['create-report'], 'create-report <appType> "<name>" ... [--open|--no-open]', 'help.cmd_create_report'),
|
|
1227
|
-
command('append-chart', ['append-chart'], 'append-chart <appType> <reportId> ... [--open|--no-open]', 'help.cmd_append_chart'),
|
|
1227
|
+
command('create-report', ['create-report'], 'create-report <appType> "<name>" ... [--json] [--open|--no-open]', 'help.cmd_create_report'),
|
|
1228
|
+
command('append-chart', ['append-chart'], 'append-chart <appType> <reportId> ... [--json] [--open|--no-open]', 'help.cmd_append_chart'),
|
|
1228
1229
|
command('report.inspect', ['report', 'inspect'], 'report inspect <appType> <reportId> --json', 'help.cmd_report_inspect', { output: 'json' }),
|
|
1229
1230
|
],
|
|
1230
1231
|
},
|
package/lib/core/copy.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
const fs = require('fs');
|
|
24
24
|
const path = require('path');
|
|
25
25
|
const os = require('os');
|
|
26
|
+
const { CliError } = require('./cli-error');
|
|
26
27
|
const { detectEnvironment } = require('./env');
|
|
27
28
|
const { buildSkillsDiagnostics, resolveProjectRoot } = require('./utils');
|
|
28
29
|
const { t } = require('./i18n');
|
|
@@ -128,6 +129,55 @@ function resolveExistingPath(targetPath) {
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
function resolveCanonicalPath(targetPath) {
|
|
133
|
+
let current = path.resolve(targetPath);
|
|
134
|
+
const missingSegments = [];
|
|
135
|
+
|
|
136
|
+
while (!fs.existsSync(current)) {
|
|
137
|
+
const parent = path.dirname(current);
|
|
138
|
+
if (parent === current) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
missingSegments.unshift(path.basename(current));
|
|
142
|
+
current = parent;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const existingBase = fs.existsSync(current) ? fs.realpathSync(current) : current;
|
|
146
|
+
return path.resolve(existingBase, ...missingSegments);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isSameOrDescendantPath(candidatePath, parentPath) {
|
|
150
|
+
const relative = path.relative(parentPath, candidatePath);
|
|
151
|
+
return relative === '' || (
|
|
152
|
+
relative !== '..' &&
|
|
153
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
154
|
+
!path.isAbsolute(relative)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function assertCopyDestinationSafe(sourceDir, destDir) {
|
|
159
|
+
const sourcePath = resolveCanonicalPath(sourceDir);
|
|
160
|
+
const destinationPath = resolveCanonicalPath(destDir);
|
|
161
|
+
const destinationInsideSource = isSameOrDescendantPath(destinationPath, sourcePath);
|
|
162
|
+
const sourceInsideDestination = isSameOrDescendantPath(sourcePath, destinationPath);
|
|
163
|
+
|
|
164
|
+
if (!destinationInsideSource && !sourceInsideDestination) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
throw new CliError(t('copy.source_destination_overlap', sourcePath, destinationPath), {
|
|
169
|
+
code: 'COPY_SOURCE_DESTINATION_OVERLAP',
|
|
170
|
+
details: {
|
|
171
|
+
sourcePath,
|
|
172
|
+
destinationPath,
|
|
173
|
+
relation: destinationInsideSource ? 'destination_inside_source' : 'source_inside_destination',
|
|
174
|
+
sideEffectState: 'none',
|
|
175
|
+
retryable: false,
|
|
176
|
+
retrySafe: true,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
131
181
|
function isSameDirectory(a, b) {
|
|
132
182
|
const pathA = resolveExistingPath(a);
|
|
133
183
|
const pathB = resolveExistingPath(b);
|
|
@@ -244,6 +294,7 @@ function resolveDestBaseFromEnv(activeToolName, activeProjectRoot, envResults, o
|
|
|
244
294
|
* 执行单项复制任务,打印结果。
|
|
245
295
|
*/
|
|
246
296
|
function copyItem(label, sourceDir, destDir, isForce, options = {}) {
|
|
297
|
+
assertCopyDestinationSafe(sourceDir, destDir);
|
|
247
298
|
console.log(t('copy.copying_label', label));
|
|
248
299
|
const count = isForce
|
|
249
300
|
? forceCopyDir(sourceDir, destDir, options)
|
|
@@ -256,6 +307,9 @@ function copyItem(label, sourceDir, destDir, isForce, options = {}) {
|
|
|
256
307
|
* @param {string[]} [args=process.argv.slice(3)] 命令参数
|
|
257
308
|
*/
|
|
258
309
|
function run(args = process.argv.slice(3)) {
|
|
310
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
311
|
+
return { help: true };
|
|
312
|
+
}
|
|
259
313
|
const { c, sep, banner, info, success, label, fail: chalkFail, listItem } = require('./chalk');
|
|
260
314
|
|
|
261
315
|
banner(t('copy.title'), { stderr: false });
|
|
@@ -339,6 +393,7 @@ function run(args = process.argv.slice(3)) {
|
|
|
339
393
|
const selectedSkills = skillsDiagnostics.selected;
|
|
340
394
|
|
|
341
395
|
if (selectedSkills && selectedSkills.path) {
|
|
396
|
+
assertCopyDestinationSafe(packageYidaSkillsDir, selectedSkills.path);
|
|
342
397
|
// 清理旧版遗留在根目录的错误安装
|
|
343
398
|
if (activeResult && selectedSkills.scope === 'user') {
|
|
344
399
|
removeSkillsLink(path.join(os.homedir(), activeResult.dirName, 'yida-skills'));
|
|
@@ -359,6 +414,7 @@ function run(args = process.argv.slice(3)) {
|
|
|
359
414
|
} else {
|
|
360
415
|
// 未检测到 AI 工具,复制到当前目录下
|
|
361
416
|
const destSkillsDest = path.join(destBase, 'yida-skills');
|
|
417
|
+
assertCopyDestinationSafe(packageYidaSkillsDir, destSkillsDest);
|
|
362
418
|
removeSkillsLink(destSkillsDest);
|
|
363
419
|
const count = mergeCopyDir(packageYidaSkillsDir, destSkillsDest);
|
|
364
420
|
results.push({
|
|
@@ -397,6 +453,7 @@ function run(args = process.argv.slice(3)) {
|
|
|
397
453
|
module.exports = {
|
|
398
454
|
run,
|
|
399
455
|
_internal: {
|
|
456
|
+
assertCopyDestinationSafe,
|
|
400
457
|
forceCopyDir,
|
|
401
458
|
ensureProjectWorkspaceDirs,
|
|
402
459
|
resolveDestBaseFromEnv,
|
package/lib/core/locales/en.js
CHANGED
|
@@ -1199,6 +1199,7 @@ Examples:
|
|
|
1199
1199
|
remove_failed: ' ❌ Remove failed: {0} ({1})',
|
|
1200
1200
|
symlink_fallback_copy: ' ⚠️ Windows symlink creation failed (requires admin privileges), falling back to directory copy: {0}',
|
|
1201
1201
|
symlink_failed: ' ❌ Symlink creation failed: {0} ({1})',
|
|
1202
|
+
source_destination_overlap: 'Copy stopped because the source and destination directories overlap. Source: {0}; destination: {1}',
|
|
1202
1203
|
result_symlink: ' {0} → {1} (symlink)',
|
|
1203
1204
|
result_copy: ' {0} → {1} ({2} files)',
|
|
1204
1205
|
},
|
|
@@ -1843,6 +1844,14 @@ Options:
|
|
|
1843
1844
|
};
|
|
1844
1845
|
|
|
1845
1846
|
Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
|
|
1847
|
+
command_unsupported: 'Unsupported data command: {0} {1}. Run openyida commands --json to inspect the actual capability.',
|
|
1848
|
+
delete_confirmation_required: 'Before deleting a form instance, query it, show the target summary to the user, and add --confirm only after explicit approval. No delete was performed.',
|
|
1849
|
+
delete_preflight_failed: 'Could not read form instance {0} for delete preflight. No delete was performed.',
|
|
1850
|
+
delete_process_unsupported: 'Deleting process instances is not supported in this version. Stop and report the capability gap; do not try scripts or private APIs.',
|
|
1851
|
+
delete_readback_mismatch: 'The delete request was accepted, but form instance {0} is still present on readback. The result is unverified; do not retry the delete automatically.',
|
|
1852
|
+
delete_result_unknown: 'The delete outcome for form instance {0} is unknown. Perform read-only inspection and do not retry the delete automatically.',
|
|
1853
|
+
delete_target_mismatch: 'Form instance {0} does not belong to target form {1}. The operation stopped before delete.',
|
|
1854
|
+
delete_target_unverified: 'Could not verify the identity and ownership of form instance {0}. The operation stopped before delete.',
|
|
1846
1855
|
form_mode_unverified: 'Could not verify the type of form {0}. Creation stopped before any data write.',
|
|
1847
1856
|
resource_required: 'data query is missing the form resource type. Suggested command: {0}',
|
|
1848
1857
|
});
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -1172,6 +1172,7 @@ openyida - 宜搭命令行工具
|
|
|
1172
1172
|
remove_failed: ' ❌ 删除失败: {0} ({1})',
|
|
1173
1173
|
symlink_fallback_copy: ' ⚠️ Windows 软链创建失败(需要管理员权限),降级为目录复制: {0}',
|
|
1174
1174
|
symlink_failed: ' ❌ 软链接创建失败: {0} ({1})',
|
|
1175
|
+
source_destination_overlap: '复制已停止:源目录与目标目录重叠。源目录:{0};目标目录:{1}',
|
|
1175
1176
|
},
|
|
1176
1177
|
|
|
1177
1178
|
// ── lib/check-update.js ────────────────────────────
|
|
@@ -1798,6 +1799,14 @@ openyida - 宜搭命令行工具
|
|
|
1798
1799
|
};
|
|
1799
1800
|
|
|
1800
1801
|
Object.assign(module.exports.query_data || (module.exports.query_data = {}), {
|
|
1802
|
+
command_unsupported: '不支持的数据命令:{0} {1}。请使用 openyida commands --json 查询真实能力。',
|
|
1803
|
+
delete_confirmation_required: '删除表单实例前必须先查询并向用户展示目标摘要,获得明确确认后追加 --confirm。未执行删除。',
|
|
1804
|
+
delete_preflight_failed: '无法读取表单实例 {0} 完成删除前校验,未执行删除。',
|
|
1805
|
+
delete_process_unsupported: '当前版本不支持删除流程实例。请停止并报告能力缺口,不要尝试脚本或底层 API。',
|
|
1806
|
+
delete_readback_mismatch: '删除请求已被接受,但回读仍能找到表单实例 {0}。结果未验证,禁止自动重试删除。',
|
|
1807
|
+
delete_result_unknown: '表单实例 {0} 的删除结果未知。请只读回查目标,不要自动重试删除。',
|
|
1808
|
+
delete_target_mismatch: '表单实例 {0} 不属于目标表单 {1},已停止且未执行删除。',
|
|
1809
|
+
delete_target_unverified: '无法验证表单实例 {0} 的身份和归属,已停止且未执行删除。',
|
|
1801
1810
|
form_mode_unverified: '无法验证表单 {0} 的类型,已停止创建;未执行任何数据写入。',
|
|
1802
1811
|
resource_required: 'data query 缺少资源类型 form。建议:{0}',
|
|
1803
1812
|
});
|