openyida 2026.9.8-1 → 2026.9.8

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 CHANGED
@@ -174,8 +174,6 @@ openyida update-app APP_XXX --theme-file .cache/openyida/crm/app-theme.css --nav
174
174
  openyida corp-efficiency
175
175
  openyida create-form create APP_XXX "Customer" .cache/openyida/forms/customer-fields.json
176
176
  openyida create-form update APP_XXX FORM_XXX .cache/openyida/forms/customer-changes.json
177
- openyida create-form update APP_XXX FORM_XXX --data-file .cache/openyida/forms/customer-changes.json
178
- openyida create-form resume APP_XXX FORM_XXX .cache/openyida/forms/customer-fields.json --json
179
177
  openyida sample openyida-page-template form-fields --output .cache/openyida/forms/customer-fields.json
180
178
  openyida sample openyida-page-template canvas-form-drawer --output project/pages/src/customer-entry.canvas.jsx --var APP_TYPE=APP_XXX --var FORM_UUID=FORM_XXX
181
179
  openyida get-schema APP_XXX FORM_XXX
@@ -415,8 +413,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
415
413
  | `openyida create-form create <appType> "<formTitle>" <fieldsJsonFile> [--icon auto\|<iconName>] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
416
414
  | `openyida create-form icons [--json]` | List available form navigation icons |
417
415
  | `openyida create-form validate-fields <fieldsJsonOrFile> [--json]` | Validate form field JSON locally |
418
- | `openyida create-form update <appType> <formUuid> (<changesJsonOrFile> \| --data-file <changesJsonOrFile>) [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
419
- | `openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]` | Update a form page |
416
+ | `openyida create-form update <appType> ... [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
420
417
  | `openyida create-form patch <appType> <formUuid> <patchJsonOrFile> [--open\|--no-open]` | Update a form page |
421
418
  | `openyida create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open\|--no-open]` | Update a form page |
422
419
  | `openyida create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open\|--no-open]` | Update a form page |
@@ -89,7 +89,6 @@ function createParseArgs(dependencies) {
89
89
  contentLocale: null,
90
90
  browserOpenMode: openOption.mode,
91
91
  };
92
- let dataFileInput;
93
92
 
94
93
  const args = [...rawArgs];
95
94
 
@@ -112,14 +111,6 @@ function createParseArgs(dependencies) {
112
111
  i--;
113
112
  } else if (args[i] === '--icon') {
114
113
  throwCreateFormError('--icon requires a value', 'CREATE_FORM_INVALID_ARGUMENTS');
115
- } else if (args[i] === '--data-file') {
116
- const next = args[i + 1];
117
- if (dataFileInput !== undefined || !next || next.startsWith('--')) {
118
- throwCreateFormError('--data-file requires exactly one value', 'CREATE_FORM_INVALID_ARGUMENTS');
119
- }
120
- dataFileInput = next;
121
- args.splice(i, 2);
122
- i--;
123
114
  } else if ((args[i] === '--locale' || args[i] === '--content-locale' || args[i] === '--lang') && i + 1 < args.length) {
124
115
  options.contentLocale = args[i + 1];
125
116
  if (!normalizeYidaLocale(options.contentLocale)) {
@@ -154,10 +145,6 @@ function createParseArgs(dependencies) {
154
145
 
155
146
  const mode = args[0];
156
147
 
157
- if (dataFileInput !== undefined && mode !== 'update') {
158
- throwCreateFormError('--data-file is only supported by create-form update', 'CREATE_FORM_INVALID_ARGUMENTS');
159
- }
160
-
161
148
  if (mode === 'icons' || mode === 'list-icons') {
162
149
  return {
163
150
  mode: 'icons',
@@ -195,12 +182,7 @@ function createParseArgs(dependencies) {
195
182
  }
196
183
 
197
184
  if (mode === 'update') {
198
- const positionalInput = args[3];
199
- if (args.length > 4 || (positionalInput && dataFileInput !== undefined)) {
200
- throwCreateFormError('Use either <changesJsonOrFile> or --data-file, not both', 'CREATE_FORM_INVALID_ARGUMENTS');
201
- }
202
- const changesJsonOrFile = dataFileInput !== undefined ? dataFileInput : positionalInput;
203
- if (args.length < 3 || !changesJsonOrFile) {
185
+ if (args.length < 4) {
204
186
  usage(t('create_form.usage_update'), t('create_form.example_update'));
205
187
  throwCreateFormError(t('create_form.usage_update'), 'CREATE_FORM_INVALID_ARGUMENTS');
206
188
  }
@@ -208,23 +190,7 @@ function createParseArgs(dependencies) {
208
190
  mode: 'update',
209
191
  appType: args[1],
210
192
  formUuid: args[2],
211
- changesJsonOrFile,
212
- ...options
213
- };
214
- }
215
-
216
- if (mode === 'resume') {
217
- if (args.length !== 4) {
218
- throwCreateFormError(
219
- 'openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> --json',
220
- 'CREATE_FORM_INVALID_ARGUMENTS'
221
- );
222
- }
223
- return {
224
- mode: 'resume',
225
- appType: args[1],
226
- formUuid: args[2],
227
- fieldsJsonOrFile: args[3],
193
+ changesJsonOrFile: args[3],
228
194
  ...options
229
195
  };
230
196
  }
@@ -101,10 +101,7 @@ function execute(args) {
101
101
  }
102
102
 
103
103
  async function schedule(forms, concurrency, results, worker, save) {
104
- const pending = new Set(forms.filter(form => {
105
- const result = results[form.key];
106
- return !result || result.status === 'blocked' || (result.status === 'failed' && result.formUuid);
107
- }).map(form => form.key));
104
+ const pending = new Set(forms.filter(form => !results[form.key] || results[form.key].status === 'blocked').map(form => form.key));
108
105
  const active = new Map();
109
106
  try {
110
107
  while (pending.size || active.size) {
@@ -117,7 +114,7 @@ async function schedule(forms, concurrency, results, worker, save) {
117
114
  }
118
115
  if (active.size >= concurrency || deps.some(status => status !== 'success')) { continue; }
119
116
  pending.delete(form.key);
120
- results[form.key] = { ...results[form.key], status: 'running' };
117
+ results[form.key] = { status: 'running' };
121
118
  save(); // Record intent before the request; an interrupted create is never retried automatically.
122
119
  const task = Promise.resolve().then(() => worker(form)).then(output => {
123
120
  results[form.key] = { ...output, status: 'success' };
@@ -181,30 +178,16 @@ async function run(args, dependencies = {}) {
181
178
  return matches[0].fieldId;
182
179
  };
183
180
  await schedule(forms, options.concurrency, state.results, async form => {
184
- let formUuid = form.formUuid || state.results[form.key]?.formUuid;
185
- let shouldResume = Boolean(!form.formUuid && formUuid);
186
- const resolvedFields = mapReferences(form.fields, resolve);
181
+ let formUuid = form.formUuid;
187
182
  if (!formUuid) {
188
- const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(resolvedFields), '--no-open'];
183
+ const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(mapReferences(form.fields, resolve)), '--no-open'];
189
184
  for (const key of ['icon', 'locale']) { if (form[key]) { argv.push(`--${key}`, form[key]); } }
190
- try {
191
- const created = await call(argv);
192
- if (typeof created.formUuid !== 'string' || !created.formUuid.startsWith('FORM')) { invalid(`create result: ${form.key}`); }
193
- formUuid = created.formUuid;
194
- } catch (error) {
195
- const createdFormUuid = error.output?.formUuid || error.output?.details?.formUuid;
196
- if (typeof createdFormUuid !== 'string' || !createdFormUuid.startsWith('FORM')) {
197
- throw error;
198
- }
199
- formUuid = createdFormUuid;
200
- shouldResume = true;
201
- }
185
+ const created = await call(argv);
186
+ if (typeof created.formUuid !== 'string' || !created.formUuid.startsWith('FORM')) { invalid(`create result: ${form.key}`); }
187
+ formUuid = created.formUuid;
202
188
  }
203
189
  state.results[form.key].formUuid = formUuid;
204
190
  save(state);
205
- if (shouldResume) {
206
- await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
207
- }
208
191
  const schema = await call(['get-schema', options.appType, formUuid, '--field-map-json']);
209
192
  if (schema.formUuid !== formUuid || !Array.isArray(schema.fields)) { invalid(`schema: ${form.key}`); }
210
193
  return { formUuid, fields: schema.fields };
@@ -6,8 +6,6 @@ function dispatchCreateFormCommand(parsedArgs, authContext, handlers) {
6
6
  switch (parsedArgs.mode) {
7
7
  case 'update':
8
8
  return handlers.update(...args);
9
- case 'resume':
10
- return handlers.resume(...args);
11
9
  case 'patch':
12
10
  return handlers.patch(...args);
13
11
  case 'rule':
@@ -5911,297 +5911,6 @@ async function mainValidation(parsedArgs, authRef) {
5911
5911
 
5912
5912
  // ── update 模式主流程 ─────────────────────────────────
5913
5913
 
5914
- function collectResumeFieldEvidence(fields, output) {
5915
- const evidence = output || [];
5916
- (fields || []).forEach(function (field) {
5917
- if (!field || typeof field !== 'object') {
5918
- return;
5919
- }
5920
- const labelText = extractLabelText(field);
5921
- if (labelText && !isFormPresentationComponent(field.componentName)) {
5922
- evidence.push({
5923
- label: labelText,
5924
- componentName: field.componentName,
5925
- fieldId: field.props && field.props.fieldId ? field.props.fieldId : null,
5926
- });
5927
- }
5928
- if (Array.isArray(field.children)) {
5929
- collectResumeFieldEvidence(field.children.flat(), evidence);
5930
- }
5931
- });
5932
- return evidence;
5933
- }
5934
-
5935
- function desiredResumeFieldIdentity(field) {
5936
- const labelValue = field && field.label;
5937
- const label = typeof labelValue === 'string'
5938
- ? labelValue
5939
- : labelValue && (labelValue.zh_CN || labelValue.ja_JP || labelValue.en_US || labelValue.pureEn_US);
5940
- return {
5941
- label: String(label || '').trim(),
5942
- componentName: normalizeFormDefinitionType(field),
5943
- };
5944
- }
5945
-
5946
- function collectResumeDesiredFields(fields, options) {
5947
- const output = [];
5948
- const verifyNestedFields = Boolean(options && options.verifyNestedFields);
5949
-
5950
- function visit(value) {
5951
- if (Array.isArray(value)) {
5952
- value.forEach(visit);
5953
- return;
5954
- }
5955
- if (!value || typeof value !== 'object') {
5956
- return;
5957
- }
5958
- const componentName = normalizeFormDefinitionType(value);
5959
- if (isSupportedBusinessFieldType(componentName)) {
5960
- output.push(value);
5961
- if (!verifyNestedFields) {
5962
- return;
5963
- }
5964
- }
5965
- if (Array.isArray(value.children)) {
5966
- value.children.forEach(visit);
5967
- }
5968
- }
5969
-
5970
- visit(fields);
5971
- return output;
5972
- }
5973
-
5974
- function isRecoverableBlankResumeSchema(schema) {
5975
- const allowedShellComponents = new Set(['Page', 'RootHeader', 'RootContent', 'RootFooter', 'FooterYida']);
5976
- const componentsTree = schema && schema.pages && schema.pages[0] && schema.pages[0].componentsTree;
5977
- if (!Array.isArray(componentsTree) || componentsTree.length === 0) {
5978
- return true;
5979
- }
5980
- let safe = true;
5981
- function visit(node) {
5982
- if (!node || typeof node !== 'object' || !safe) {
5983
- return;
5984
- }
5985
- if (node.componentName && !allowedShellComponents.has(node.componentName)) {
5986
- safe = false;
5987
- return;
5988
- }
5989
- if (Array.isArray(node.children)) {
5990
- node.children.forEach(visit);
5991
- }
5992
- }
5993
- componentsTree.forEach(visit);
5994
- return safe;
5995
- }
5996
-
5997
- function buildResumeChanges(existingFields, desiredFields) {
5998
- const existingByLabel = new Map();
5999
- existingFields.forEach(function (field) {
6000
- const bucket = existingByLabel.get(field.label) || [];
6001
- bucket.push(field);
6002
- existingByLabel.set(field.label, bucket);
6003
- });
6004
-
6005
- const desiredLabels = new Set();
6006
- const missing = [];
6007
- const existing = [];
6008
- const conflicts = [];
6009
- desiredFields.forEach(function (field) {
6010
- const identity = desiredResumeFieldIdentity(field);
6011
- if (!identity.label || desiredLabels.has(identity.label)) {
6012
- conflicts.push({
6013
- code: identity.label ? 'CREATE_FORM_RESUME_DUPLICATE_DESIRED_FIELD' : 'CREATE_FORM_RESUME_FIELD_LABEL_REQUIRED',
6014
- label: identity.label || null,
6015
- });
6016
- return;
6017
- }
6018
- desiredLabels.add(identity.label);
6019
- const matches = existingByLabel.get(identity.label) || [];
6020
- if (matches.length === 0) {
6021
- missing.push(field);
6022
- return;
6023
- }
6024
- if (matches.length !== 1 || matches[0].componentName !== identity.componentName) {
6025
- conflicts.push({
6026
- code: 'CREATE_FORM_RESUME_FIELD_CONFLICT',
6027
- label: identity.label,
6028
- requestedType: identity.componentName,
6029
- observedTypes: matches.map(function (item) { return item.componentName; }),
6030
- });
6031
- return;
6032
- }
6033
- existing.push({
6034
- label: identity.label,
6035
- componentName: identity.componentName,
6036
- fieldId: matches[0].fieldId,
6037
- });
6038
- });
6039
- return { missing, existing, conflicts };
6040
- }
6041
-
6042
- async function readResumeSchema(authRef, appType, formUuid) {
6043
- const schemaResult = await requestWithAutoLogin(function (auth) {
6044
- return sendGetRequest(
6045
- auth.baseUrl,
6046
- buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
6047
- { formUuid: formUuid, schemaVersion: 'V5' }
6048
- );
6049
- }, authRef);
6050
- if (!schemaResult || schemaResult.success === false || schemaResult.__needLogin || schemaResult.__csrfExpired) {
6051
- throwCreateFormError(t('create_form.resume_readback_failed'), 'CREATE_FORM_RESUME_READBACK_FAILED', {
6052
- appType,
6053
- formUuid,
6054
- result: sanitizeFailureResult(schemaResult),
6055
- });
6056
- }
6057
- if (schemaResult.appType && schemaResult.appType !== appType) {
6058
- throwCreateFormError(t('create_form.resume_ownership_unverified'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
6059
- appType,
6060
- formUuid,
6061
- });
6062
- }
6063
- if (schemaResult.formUuid && schemaResult.formUuid !== formUuid) {
6064
- throwCreateFormError(t('create_form.resume_identity_mismatch'), 'CREATE_FORM_RESUME_OWNERSHIP_UNVERIFIED', {
6065
- appType,
6066
- formUuid,
6067
- });
6068
- }
6069
- const schema = schemaResult.content
6070
- ? (typeof schemaResult.content === 'string' ? JSON.parse(schemaResult.content) : schemaResult.content)
6071
- : schemaResult.pages ? schemaResult : null;
6072
- if (!schema || !Array.isArray(schema.pages) || schema.pages.length === 0) {
6073
- throwCreateFormError(t('create_form.resume_schema_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
6074
- appType,
6075
- formUuid,
6076
- });
6077
- }
6078
- return { schemaResult, schema, version: extractSchemaServerRevision(schemaResult) };
6079
- }
6080
-
6081
- async function mainResume(parsedArgs, authRef) {
6082
- const { appType, formUuid, fieldsJsonOrFile } = parsedArgs;
6083
- assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
6084
- const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
6085
- assertNoEmojiInFormDefinition(formUuid, fields, [], 'create-form resume input');
6086
- validateFormFieldDefinitions(fields);
6087
-
6088
- const firstRead = await readResumeSchema(authRef, appType, formUuid);
6089
- const root = firstRead.schema.pages[0].componentsTree && firstRead.schema.pages[0].componentsTree[0];
6090
- const formContainer = root ? findFormContainer(root) : null;
6091
- const completedStages = ['read_target', 'verify_ownership'];
6092
- let recoveredBlankShell = false;
6093
- let plan;
6094
-
6095
- if (!formContainer) {
6096
- if (!isRecoverableBlankResumeSchema(firstRead.schema)) {
6097
- throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
6098
- appType,
6099
- formUuid,
6100
- retryable: false,
6101
- remoteWrites: 0,
6102
- });
6103
- }
6104
- const corpId = resolveCorpId(authRef.authData);
6105
- const formTitle = firstRead.schemaResult.formTitle || firstRead.schemaResult.title || formUuid;
6106
- firstRead.schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, 'single', 'default', 'top');
6107
- const resumeValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
6108
- if (resumeValidationRules.length > 0) {
6109
- applySmartValidations(firstRead.schema, resumeValidationRules);
6110
- }
6111
- await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
6112
- completedStages.push('rebuild_blank_schema', 'save_schema');
6113
- recoveredBlankShell = true;
6114
- plan = {
6115
- existing: [],
6116
- missing: collectResumeDesiredFields(fields, { verifyNestedFields: true }),
6117
- conflicts: [],
6118
- };
6119
- } else if (!Array.isArray(formContainer.children)) {
6120
- throwCreateFormError(t('create_form.resume_container_invalid'), 'CREATE_FORM_RESUME_SCHEMA_INVALID', {
6121
- appType,
6122
- formUuid,
6123
- retryable: false,
6124
- remoteWrites: 0,
6125
- });
6126
- } else {
6127
- plan = buildResumeChanges(
6128
- collectResumeFieldEvidence(formContainer.children),
6129
- collectResumeDesiredFields(fields)
6130
- );
6131
- if (plan.conflicts.length > 0) {
6132
- throwCreateFormError(t('create_form.resume_field_conflict'), 'CREATE_FORM_RESUME_CONFLICT', {
6133
- appType,
6134
- formUuid,
6135
- retryable: false,
6136
- remoteWrites: 0,
6137
- diagnostics: plan.conflicts,
6138
- });
6139
- }
6140
- completedStages.push('compare_fields');
6141
- }
6142
-
6143
- if (!recoveredBlankShell && plan.missing.length > 0) {
6144
- const applied = applyChangesToSchema(
6145
- firstRead.schema,
6146
- plan.missing.map(function (field) { return { action: 'add', field }; }),
6147
- { verbose: true }
6148
- );
6149
- if ((applied.diagnostics || []).length > 0) {
6150
- throwCreateFormError(t('create_form.resume_field_resolution_failed'), 'CREATE_FORM_RESUME_CONFLICT', {
6151
- appType,
6152
- formUuid,
6153
- retryable: false,
6154
- remoteWrites: 0,
6155
- diagnostics: applied.diagnostics,
6156
- });
6157
- }
6158
- const corpId = resolveCorpId(authRef.authData);
6159
- const updatedContainer = findFormContainer(firstRead.schema.pages[0].componentsTree[0]);
6160
- fillSerialNumberFormulas(updatedContainer.children, corpId, appType, formUuid);
6161
- await saveFormSchema(authRef, appType, formUuid, firstRead.schema, firstRead.version, 4);
6162
- completedStages.push('add_missing_fields', 'save_schema');
6163
- }
6164
-
6165
- const finalRead = await readResumeSchema(authRef, appType, formUuid);
6166
- const finalContainer = findFormContainer(finalRead.schema.pages[0].componentsTree[0]);
6167
- const finalPlan = buildResumeChanges(
6168
- collectResumeFieldEvidence(finalContainer && finalContainer.children),
6169
- collectResumeDesiredFields(fields, { verifyNestedFields: true })
6170
- );
6171
- if (finalPlan.missing.length > 0 || finalPlan.conflicts.length > 0) {
6172
- throwCreateFormError(t('create_form.resume_readback_mismatch'), 'CREATE_FORM_RESUME_READBACK_MISMATCH', {
6173
- appType,
6174
- formUuid,
6175
- retryable: false,
6176
- resultUnknown: true,
6177
- completedStages,
6178
- missingFieldCount: finalPlan.missing.length,
6179
- diagnostics: finalPlan.conflicts,
6180
- });
6181
- }
6182
- completedStages.push('verify_final_schema');
6183
- const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
6184
- const output = {
6185
- success: true,
6186
- appType,
6187
- formUuid,
6188
- completedStages,
6189
- requestedFieldCount: fields.length,
6190
- existingFieldCount: plan.existing.length,
6191
- addedFieldCount: plan.missing.length,
6192
- finalFieldCount: collectResumeFieldEvidence(finalContainer.children).length,
6193
- recoveredBlankShell,
6194
- url: formUrl,
6195
- };
6196
- console.log(JSON.stringify(withBrowserHandoff(
6197
- output,
6198
- formUrl,
6199
- { stage: 'resume_form_success', title: formUuid },
6200
- parsedArgs.browserOpenMode
6201
- )));
6202
- return output;
6203
- }
6204
-
6205
5914
  async function mainUpdate(parsedArgs, authRef) {
6206
5915
  const { appType, formUuid, changesJsonOrFile } = parsedArgs;
6207
5916
 
@@ -6393,7 +6102,6 @@ async function run(args) {
6393
6102
  {
6394
6103
  create: mainCreate,
6395
6104
  update: mainUpdate,
6396
- resume: mainResume,
6397
6105
  patch: mainPatch,
6398
6106
  rule: mainRule,
6399
6107
  validation: mainValidation,
@@ -449,7 +449,6 @@ const COMMAND_SIDE_EFFECTS = new Map([
449
449
  'create-form.bind-datasource',
450
450
  'create-form.create',
451
451
  'create-form.patch',
452
- 'create-form.resume',
453
452
  'create-form.rule',
454
453
  'create-form.update',
455
454
  'create-form.validation',
@@ -690,7 +689,6 @@ const COMMAND_PERMISSIONS = new Map([
690
689
  'create-form.create',
691
690
  'create-form.batch',
692
691
  'create-form.patch',
693
- 'create-form.resume',
694
692
  'create-form.rule',
695
693
  'create-form.update',
696
694
  'create-form.validation',
@@ -1225,8 +1223,7 @@ const COMMAND_GROUPS = [
1225
1223
  command('create-form.validate-fields', ['create-form', 'validate-fields'], 'create-form validate-fields <fieldsJsonOrFile> [--json]', 'help.cmd_validate_form', {
1226
1224
  requiresLogin: false,
1227
1225
  }),
1228
- command('create-form.update', ['create-form', 'update'], 'create-form update <appType> <formUuid> (<changesJsonOrFile> | --data-file <changesJsonOrFile>) [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_update_form'),
1229
- command('create-form.resume', ['create-form', 'resume'], 'create-form resume <appType> <formUuid> <fieldsJsonOrFile> [--json]', 'help.cmd_update_form'),
1226
+ command('create-form.update', ['create-form', 'update'], 'create-form update <appType> ... [--locale zh_CN|en_US|ja_JP] [--open|--no-open]', 'help.cmd_update_form'),
1230
1227
  command('create-form.patch', ['create-form', 'patch'], 'create-form patch <appType> <formUuid> <patchJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
1231
1228
  command('create-form.rule', ['create-form', 'rule'], 'create-form rule <appType> <formUuid> <rulesJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
1232
1229
  command('create-form.validation', ['create-form', 'validation'], 'create-form validation <appType> <formUuid> <validationsJsonOrFile> [--open|--no-open]', 'help.cmd_update_form'),
@@ -827,15 +827,7 @@ Examples:
827
827
  example_update: 'Example: openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"Note"}}]\'',
828
828
  usage_label: 'Usage:',
829
829
  usage_create_short: ' create: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
830
- usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile> or --data-file <changesJsonOrFile>',
831
- resume_readback_failed: 'Unable to read the target form before resume.',
832
- resume_ownership_unverified: 'The target form does not belong to the requested app; resume stopped.',
833
- resume_identity_mismatch: 'The target form identity does not match the readback; resume stopped.',
834
- resume_schema_invalid: 'The target form schema is unavailable.',
835
- resume_container_invalid: 'The target form container is unavailable.',
836
- resume_field_conflict: 'Existing fields conflict with the requested definition; resume stopped.',
837
- resume_field_resolution_failed: 'Resume field resolution failed.',
838
- resume_readback_mismatch: 'Resume readback did not confirm all requested fields.',
830
+ usage_update_short: ' update: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
839
831
  example_label: '\nExamples:',
840
832
  fields_file_not_found: ' ❌ Fields definition file not found: ',
841
833
  fields_format_invalid: 'Invalid fields definition format',
@@ -798,15 +798,7 @@ openyida - 宜搭命令行工具
798
798
  example_update: '示例:openyida create-form update "APP_XXX" "FORM-YYY" \'[{"action":"add","field":{"type":"TextField","label":"备注"}}]\'',
799
799
  usage_label: '用法:',
800
800
  usage_create_short: ' 创建: openyida create-form create <appType> <formTitle> <fieldsJsonFile>',
801
- usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile> 或 --data-file <changesJsonOrFile>',
802
- resume_readback_failed: '恢复前无法回读目标表单。',
803
- resume_ownership_unverified: '目标表单不属于指定应用,已停止恢复。',
804
- resume_identity_mismatch: '目标表单身份与回读结果不一致,已停止恢复。',
805
- resume_schema_invalid: '目标表单 Schema 不可用。',
806
- resume_container_invalid: '目标表单容器不可用。',
807
- resume_field_conflict: '已有字段与请求定义冲突,恢复已停止。',
808
- resume_field_resolution_failed: '恢复字段解析失败。',
809
- resume_readback_mismatch: '恢复后的回读未确认全部请求字段。',
801
+ usage_update_short: ' 更新: openyida create-form update <appType> <formUuid> <changesJsonOrFile>',
810
802
  example_label: '\n示例:',
811
803
  fields_file_not_found: ' ❌ 字段定义文件不存在: ',
812
804
  fields_format_invalid: '字段定义格式不正确',
@@ -361,12 +361,7 @@ async function run(args, options) {
361
361
  error.code || 'CONFIGURE_PROCESS_BUILD_FAILED',
362
362
  t('configure_process.building_json') + ': ' + error.message,
363
363
  'build_definition',
364
- error,
365
- {
366
- retryable: false,
367
- nextAction: 'fix_definition_file',
368
- diagnostics: [summarizeRemoteResult(error)],
369
- }
364
+ error
370
365
  );
371
366
  }
372
367
  stageTracker.complete('build_definition');
@@ -429,18 +424,6 @@ async function run(args, options) {
429
424
  remoteWrites: 0,
430
425
  published: !!versionState.published,
431
426
  saved: !!versionState.saved,
432
- retryable: false,
433
- requiresExplicitReplace: true,
434
- nextAction: 'request_confirmation_then_replace',
435
- suggestedCommand: [
436
- 'openyida configure-process',
437
- appType,
438
- formUuid,
439
- processDefinitionFile,
440
- processCode,
441
- '--replace',
442
- '--json',
443
- ].filter(Boolean).join(' '),
444
427
  }
445
428
  );
446
429
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.9.8-1",
3
+ "version": "2026.9.8",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -34,9 +34,9 @@ HTML 保留“需求总览、数据模型、业务流程、页面规划”四章
34
34
 
35
35
  按 [用户交互契约](../../../yida-design/references/ask-human-interaction-contract.md) 执行:
36
36
 
37
- 1. 在会话中展示“当前这版方案”,并用 3–7 条业务摘要说明方案内容。
38
- 2. 通过同一次结构化提问的 `attachments` 携带可打开的 `prd/<项目名>/build-plan.html`,并将 `revision` 设为当前 `meta.revision`;不得先发普通文本附件、再单独提问。
39
- 3. 结构化交互成功创建后内部记录 `presentedRevision=meta.revision`。询问“确认并开始搭建”或“继续调整”,提交时由宿主原样回传 revision,将确认结果绑定到本次展示版本;收到确认或修改业务事实后再重新生成。用户可见版本称为“第 N 版方案”,展示序号与内部 revision 绑定。
37
+ 1. 在会话中展示“当前这版方案”、3–7 条业务摘要和可打开的 `build-plan.html`。
38
+ 2. 展示成功后内部记录 `presentedRevision=meta.revision`,记录后直接提问;收到确认或修改业务事实后再重新生成。用户可见版本称为“第 N 版方案”,展示序号与内部 revision 绑定。
39
+ 3. 询问“确认并开始搭建”或“继续调整”,将确认结果绑定到本次展示版本。
40
40
 
41
41
  只有以下条件同时成立才交接:
42
42
 
@@ -1,6 +1,6 @@
1
1
  # Step 4:创建或更新表单/流程
2
2
 
3
- 按 PRD 的依赖创建或复用表单和流程。同一轮需要新建两个及以上普通表单时,把独立表单和关联表单写入同一个 `forms.json`,通过 `dependsOn` / `$form` 表达依赖,并且只调用一次 `openyida create-form batch`;由 CLI 内部完成分组、真实 ID 回读和依赖调度,不逐个 create,也不由模型拆成多次 batch。某页所需表单、流程就绪后即可接入该页;不要因其他页面的资源未完成而阻塞无依赖页面开发。
3
+ 按 PRD 的依赖创建或复用表单和流程。独立普通表单通过 [批量命令](../../yida-create-form-page/references/batch-forms.md) 同时创建;关联表单等待前置表单完成。某页所需表单、流程就绪后即可接入该页;不要因其他页面的资源未完成而阻塞无依赖页面开发。
4
4
 
5
5
  拿到真实 `appType` 和已确认的业务契约即可开始本步骤,不等待主题 CSS 上传或应用主题设置回读。主题分支与本步骤并行,按 [主题与业务资源的依赖](parallel-work.md#主题与业务资源的依赖) 汇合。
6
6
 
@@ -15,7 +15,7 @@
15
15
 
16
16
  1. 执行 `use_skill("yida-create-form-page", "创建或更新核心表单字段结构")`,创建或更新普通表单字段结构。
17
17
  2. 已有目标表单时,使用 update/patch/rule/bind-datasource。
18
- 3. 缺少支撑 MVP 的核心普通表单且允许创建时,按上述单批次契约创建;只有一个新表单或 batch 契约无法表达当前依赖时才使用单表单 create,并说明原因。
18
+ 3. 缺少支撑 MVP 的核心普通表单且允许创建时,创建普通表单。
19
19
  4. 字段配置文件写入 `.cache/openyida/<项目名>/`。
20
20
  5. 拿到真实 `formUuid` 后写入资源上下文。
21
21
  6. 批量创建返回的字段映射直接复用。其他页面、数据、流程或公式确需多字段映射时,对每个目标表单最多一次性执行 `openyida get-schema <appType> <formUuid> --field-map-json`,合并写回 `.cache/<项目名>-schema.json`。
@@ -14,8 +14,6 @@
14
14
  - 页面/资源数量完整性风险检查结果。
15
15
  - `requirement-brief.json.constraints.prohibitedActions` 与对应跳过证据。
16
16
 
17
- 主入口与页面入口必须取自本轮 CLI 成功结果返回的 `appUrl`、`workbenchUrl` 或 `url`,并在后续只读回查中保持一致。不得由模型根据 `appType` 自行拼接或猜测链接;下文 URL 规则仅用于校验服务端/CLI 返回值,不是缺失链接时的生成规则。若成功结果没有权威 URL,必须明确交付失败并执行只读回查,不能交付空卡片或用模板补齐。
18
-
19
17
  ## 完成条件核对
20
18
 
21
19
  完整应用默认完成需要同时满足:
@@ -58,7 +58,7 @@ description: 表单页面创建与更新;支持 19 种业务字段和 Divider
58
58
 
59
59
  ## 多表单创建
60
60
 
61
- 同一轮需要新建两个及以上普通表单时,必须按 [并行创建表单](references/batch-forms.md) 把全部表单写入同一个 `forms.json`,并且只调用一次 `openyida create-form batch <appType> <任务文件> --json`。独立表单和关联表单放在同一任务文件中,依赖通过 `dependsOn` / `$form` 表达,由 CLI 在一次 batch 内部完成分组、真实 `formUuid/fieldId` 回读和依赖调度;不要手工拆成多次 batch,也不要逐个调用 `create-form create`。只有修改已有表单、恢复已有 `formUuid`,或当前 batch 契约无法表达依赖时,才走明确的非 batch 路径并说明原因。
61
+ 多个普通表单按 [并行创建表单](references/batch-forms.md) 准备任务,执行 `openyida create-form batch <appType> <任务文件> --json`。CLI 同时创建独立表单,等待关联表单与真实字段就绪后创建依赖表单。
62
62
 
63
63
  ## 官方表单示例范式
64
64
 
@@ -118,7 +118,7 @@ openyida create-form create <appType> <formTitle> <fieldsJsonOrFile> [--layout d
118
118
  # 文件路径示例:.cache/openyida/<项目名或任务名>/<表单名>-fields.json
119
119
  ```
120
120
 
121
- 默认不传 `--icon`,由 CLI 根据表单标题和字段语义选择导航图标,再更新导航节点并回读校验。只有用户明确指定某个图标时才传 `--icon <iconName>`;普通搭建不得调用 `openyida create-form icons` 枚举候选,也不得先猜图标、失败后再探索。`icons` 仅供用户明确指定但值不合法时的人工诊断。表单导航图标是纯图标名(如 `name-card`、`Project`、`Todo`、`clock`),不是应用图标的 `xian-*%%color` 协议。
121
+ 创建成功后 CLI 会根据表单标题和字段语义选择导航图标,再更新导航节点并回读校验。需要指定时传 `--icon <iconName>`;用 `openyida create-form icons --json` 查看与 yida-next 页面导航选择器一致的 86 个可用值。表单导航图标是纯图标名(如 `name-card`、`Project`、`Todo`、`clock`),不是应用图标的 `xian-*%%color` 协议。
122
122
 
123
123
  导航图标更新必须先读取 `getFormNavigationListByOrder.json` 的当前节点,像 yida-next 的 `DB.Nav.update({ ...node, title: JSON.stringify(node.title), formUuid: node.formUuid || 'NAV-SYSTEM-FROM-ME-UUID', icon })` 一样保留 `gmtModified`、`formType`、`isNewForm`、`listOrder` 等原值,再请求带 `_api=Nav.update&_mock=false&_stamp=...` 的 `updateFormNavigation.json`,最后重新读取导航列表校验图标。禁止仅凭 formUuid 拼一个精简更新 payload。
124
124
 
@@ -142,7 +142,7 @@ openyida create-form create <appType> <formTitle> <fieldsJsonOrFile> [--layout d
142
142
  create 命令失败后,不要立刻重复同一条 create:
143
143
 
144
144
  1. 先确认字段 JSON 文件存在,且内容是结构化写入后的最终字段数组/对象,不是半截 JSON、update changes 或 shell 拼接残留。
145
- 2. 运行 `openyida list-forms <appType> --keyword "<表单名>"` 查同名表单;若失败结果已给出本轮创建的 `formUuid`,使用 `openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> --json` 先回读、比较并仅补缺失字段。冲突或结果未知时停止,不能重新 create;普通已知修改仍用 `update` / `patch`。
145
+ 2. 运行 `openyida list-forms <appType> --keyword "<表单名>"` 查同名表单;若本轮刚创建过空白表单或已有同名目标表单,优先走 `create-form update` / `patch` / 后续显式 resume 能力复用,不再 create。
146
146
  3. 只有确认远端没有同名目标表单,并且已经修改输入文件、参数、登录态或组织后,才重试 create。
147
147
  4. 同一 create 命令最多重试 2 次;仍失败时停止并带上完整 stdout/stderr、字段文件路径、appType、表单名和已发现的 formUuid 给用户。
148
148
 
@@ -152,23 +152,9 @@ create 命令失败后,不要立刻重复同一条 create:
152
152
 
153
153
  ```bash
154
154
  openyida create-form update <appType> <formUuid> <changesJsonOrFile>
155
- openyida create-form update <appType> <formUuid> --data-file <changesJsonOrFile>
156
155
  # 文件路径示例:.cache/openyida/<项目名或任务名>/<表单名>-changes.json
157
156
  ```
158
157
 
159
- 位置参数和 `--data-file` 是同一输入的两种写法,不能同时使用。
160
-
161
- ## 半成功 create 恢复
162
-
163
- create 已返回真实 `formUuid`、但后续 schema 保存或回读失败时,使用保守恢复命令:
164
-
165
- ```bash
166
- openyida create-form resume <appType> <formUuid> <fieldsJsonOrFile> --json
167
- ```
168
-
169
- 该命令先回读目标表单并核对字段,只添加可唯一判定的缺失字段,保存后再次回读;同名异类型、重复
170
- 目标字段、归属不匹配或回读不确定时均停止且不写入。它不会新建替代表单,也不会覆盖已有字段。
171
-
172
158
  输出:
173
159
 
174
160
  ```json
@@ -115,13 +115,6 @@ Fast / Plan 是面向用户的模式名称,可以展示。已有详细计划
115
115
  - `allowCustom`:是否允许用户补充自定义答案。
116
116
  - `writeBackPath`:答案写回位置。
117
117
 
118
- 需要让用户查看 workspace 产物后再回答时,同一次 `ask_human` 还必须携带:
119
-
120
- - `attachments`:待查看文件列表;workspace 文件使用 `{ "name": "搭建方案", "path": "prd/<项目名>/build-plan.html" }`。路径必须位于当前项目工作区,不能传运行时内部路径或外部绝对路径。
121
- - `revision`:本次问题绑定的事实版本。宿主提交回答时必须原样回传;版本已变化时拒绝旧回答并重新展示最新版本。
122
-
123
- 附件、问题与 `revision` 是一个原子交互。不得先发普通文本附件、再单独调用 `ask_human`,也不得只在问题文案中写文件路径;否则实时界面和历史回放无法可靠证明用户确认的是哪一版方案。
124
-
125
118
  ## 首次搭建确认
126
119
 
127
120
  先完成需求分析,再按 [首次搭建确认表](../../yida-requirement-analysis/workflow/prepare-brief.md#2-确认首次搭建的未决事项) 询问尚未明确的事项。该表统一维护详细计划复用、搭建方式、业务模块、导航归属与布局、风格和页面范围;已有业务应用的局部增改按本次疑问澄清。
@@ -135,9 +128,9 @@ Fast / Plan 是面向用户的模式名称,可以展示。已有详细计划
135
128
  Plan Design 完成当前版本后,按以下顺序与用户交互:
136
129
 
137
130
  1. 在会话中使用“当前这版方案”或“第 N 版方案”,给出 3-7 条业务摘要;原始 `meta.revision` 仅用于内部状态绑定。
138
- 2. 在“最新搭建计划确认”的同一次 `ask_human` 中,通过 `attachments` 展示可打开的 `prd/<项目名>/build-plan.html`,并以 `revision` 绑定当前 `meta.revision`。
139
- 3. 结构化交互成功创建后令 `meta.planState.presentedRevision` 等于 `meta.revision`;awaiting_confirmation 在生成前写入。
140
- 4. 提供“确认并开始搭建”和“继续调整”两个选择,并等待用户回答。
131
+ 2. 以宿主支持的文件链接、附件或可打开产物形式展示 `prd/<项目名>/build-plan.html`。
132
+ 3. 展示成功后令 `meta.planState.presentedRevision` 等于 `meta.revision`;awaiting_confirmation 在生成前写入,展示记账完成后继续询问。
133
+ 4. 执行“最新搭建计划确认”类型的 `ask_human`,提供“确认并开始搭建”和“继续调整”两个选择。
141
134
 
142
135
  `build-plan.html` 不承载对话控件或确认按钮;用户在会话中完成确认。
143
136
 
@@ -162,14 +155,7 @@ Plan Design 完成当前版本后,按以下顺序与用户交互:
162
155
  }
163
156
  ],
164
157
  "allowCustom": false,
165
- "writeBackPath": "meta.planState",
166
- "attachments": [
167
- {
168
- "name": "当前搭建方案",
169
- "path": "prd/<项目名>/build-plan.html"
170
- }
171
- ],
172
- "revision": "{revision}"
158
+ "writeBackPath": "meta.planState"
173
159
  }
174
160
  ```
175
161