openyida 2026.9.13 → 2026.9.15
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/lib/app/canvas-compile.js +37 -4
- package/lib/app/create-form/batch.js +54 -6
- package/lib/app/create-form.js +89 -11
- package/lib/app/display-page-readback.js +63 -5
- package/lib/app/get-schema.js +158 -5
- package/lib/app/publish.js +127 -5
- package/lib/core/agent-capabilities.js +6 -7
- package/lib/core/command-manifest.js +2 -2
- package/lib/core/locales/en.js +9 -0
- package/lib/core/locales/zh.js +9 -0
- package/lib/core/utils.js +358 -0
- package/package.json +1 -1
- package/yida-skills/skills/yida-app/SKILL.md +11 -5
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +5 -3
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +4 -3
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +5 -2
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +18 -14
- package/yida-skills/skills/yida-create-form-page/SKILL.md +18 -3
- package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +4 -3
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +22 -19
- package/yida-skills/skills/yida-design/references/design-mode.md +1 -1
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -1
- package/yida-skills/skills/yida-integration/SKILL.md +1 -0
- package/yida-skills/skills/yida-nav-shell/SKILL.md +4 -4
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +2 -2
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +1 -1
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -18
package/lib/app/get-schema.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
|
+
const crypto = require('crypto');
|
|
12
13
|
const fs = require('fs');
|
|
13
14
|
const path = require('path');
|
|
14
15
|
const {
|
|
@@ -16,6 +17,7 @@ const {
|
|
|
16
17
|
triggerLogin,
|
|
17
18
|
resolveBaseUrl,
|
|
18
19
|
httpGet,
|
|
20
|
+
httpGetCodeBundleText,
|
|
19
21
|
requestWithAutoLogin,
|
|
20
22
|
} = require('../core/utils');
|
|
21
23
|
const { t } = require('../core/i18n');
|
|
@@ -227,6 +229,122 @@ function resolveSchemaContent(schemaResult) {
|
|
|
227
229
|
return parseJsonObject(content);
|
|
228
230
|
}
|
|
229
231
|
|
|
232
|
+
function validateCodeBundleDescriptor(codeBundle) {
|
|
233
|
+
const bundleId = String(codeBundle && codeBundle.bundleId || '');
|
|
234
|
+
const source = codeBundle && codeBundle.source;
|
|
235
|
+
const runtime = codeBundle && codeBundle.runtime;
|
|
236
|
+
const validSha256 = value => /^[a-f0-9]{64}$/.test(String(value || ''));
|
|
237
|
+
const validSize = value => Number.isFinite(Number(value)) && Number(value) > 0
|
|
238
|
+
&& Number(value) <= 5 * 1024 * 1024;
|
|
239
|
+
if (!validSha256(bundleId)
|
|
240
|
+
|| !source || !runtime
|
|
241
|
+
|| !validSha256(source.sha256) || !validSize(source.size)
|
|
242
|
+
|| !validSha256(runtime.sha256) || !validSize(runtime.size)
|
|
243
|
+
|| Number(source.size) + Number(runtime.size) > 5 * 1024 * 1024) {
|
|
244
|
+
throw new Error('YidaCodeCanvas codeBundle 描述不合法');
|
|
245
|
+
}
|
|
246
|
+
return { bundleId, source, runtime };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function collectCodeBundleCanvases(content) {
|
|
250
|
+
const canvases = [];
|
|
251
|
+
function traverse(node) {
|
|
252
|
+
if (Array.isArray(node)) {
|
|
253
|
+
node.forEach(traverse);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (!node || typeof node !== 'object') {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (node.componentName === 'YidaCodeCanvas' && node.id && node.props
|
|
260
|
+
&& node.props.codeBundle && typeof node.props.codeBundle === 'object') {
|
|
261
|
+
canvases.push({ props: node.props, descriptor: validateCodeBundleDescriptor(node.props.codeBundle) });
|
|
262
|
+
}
|
|
263
|
+
Object.values(node).forEach(traverse);
|
|
264
|
+
}
|
|
265
|
+
traverse(content);
|
|
266
|
+
return canvases;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function downloadCodeBundleArtifact(appType, formUuid, bundleId, artifact, descriptor, authRef) {
|
|
270
|
+
let responseMetadata = null;
|
|
271
|
+
const expectedContentTypes = artifact === 'source'
|
|
272
|
+
? ['text/plain']
|
|
273
|
+
: ['application/javascript', 'text/javascript', 'application/x-javascript'];
|
|
274
|
+
const value = await requestWithAutoLogin((auth) => httpGetCodeBundleText(
|
|
275
|
+
auth.baseUrl,
|
|
276
|
+
`/alibaba/web/${appType}/query/codeBundle/download.json`,
|
|
277
|
+
{ formUuid, bundleId, artifact },
|
|
278
|
+
{
|
|
279
|
+
silentStatus: true,
|
|
280
|
+
maxBytes: 5 * 1024 * 1024,
|
|
281
|
+
expectedContentTypes,
|
|
282
|
+
onResponseMetadata: metadata => {
|
|
283
|
+
responseMetadata = metadata;
|
|
284
|
+
},
|
|
285
|
+
}
|
|
286
|
+
), authRef);
|
|
287
|
+
if (typeof value !== 'string') {
|
|
288
|
+
const message = value && (value.errorMsg || value.message);
|
|
289
|
+
throw new Error(message || `下载 CodeBundle ${artifact} 失败`);
|
|
290
|
+
}
|
|
291
|
+
const size = Buffer.byteLength(value, 'utf8');
|
|
292
|
+
const digest = crypto.createHash('sha256').update(value, 'utf8').digest('hex');
|
|
293
|
+
if (size !== Number(descriptor.size) || digest !== descriptor.sha256) {
|
|
294
|
+
const context = responseMetadata && responseMetadata.context
|
|
295
|
+
? responseMetadata.context
|
|
296
|
+
: `baseUrl=${authRef && authRef.baseUrl || 'unknown'}`;
|
|
297
|
+
const error = new Error(t('common.code_bundle_integrity_failed', artifact, context));
|
|
298
|
+
error.code = 'CODE_BUNDLE_INTEGRITY_MISMATCH';
|
|
299
|
+
error.details = {
|
|
300
|
+
artifact,
|
|
301
|
+
expectedSize: Number(descriptor.size),
|
|
302
|
+
actualSize: size,
|
|
303
|
+
expectedSha256: descriptor.sha256,
|
|
304
|
+
actualSha256: digest,
|
|
305
|
+
...(responseMetadata || {}),
|
|
306
|
+
};
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
return value;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function resolveCodeBundleSchema(schemaResult, appType, formUuid, authRef) {
|
|
313
|
+
const content = resolveSchemaContent(schemaResult);
|
|
314
|
+
if (!content) {
|
|
315
|
+
return schemaResult;
|
|
316
|
+
}
|
|
317
|
+
const canvases = collectCodeBundleCanvases(content);
|
|
318
|
+
if (canvases.length === 0) {
|
|
319
|
+
return schemaResult;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const downloaded = new Map();
|
|
323
|
+
for (const canvas of canvases) {
|
|
324
|
+
const { bundleId, source, runtime } = canvas.descriptor;
|
|
325
|
+
let code = downloaded.get(bundleId);
|
|
326
|
+
if (!code) {
|
|
327
|
+
const [sourceCode, runtimeCode] = await Promise.all([
|
|
328
|
+
downloadCodeBundleArtifact(appType, formUuid, bundleId, 'source', source, authRef),
|
|
329
|
+
downloadCodeBundleArtifact(appType, formUuid, bundleId, 'runtime', runtime, authRef),
|
|
330
|
+
]);
|
|
331
|
+
code = { sourceCode, runtimeCode };
|
|
332
|
+
downloaded.set(bundleId, code);
|
|
333
|
+
}
|
|
334
|
+
canvas.props.code = code.sourceCode;
|
|
335
|
+
canvas.props.runtimeCode = code.runtimeCode;
|
|
336
|
+
delete canvas.props.codeBundle;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (schemaResult && Object.prototype.hasOwnProperty.call(schemaResult, 'content')) {
|
|
340
|
+
schemaResult.content = typeof schemaResult.content === 'string'
|
|
341
|
+
? JSON.stringify(content)
|
|
342
|
+
: content;
|
|
343
|
+
return schemaResult;
|
|
344
|
+
}
|
|
345
|
+
return content;
|
|
346
|
+
}
|
|
347
|
+
|
|
230
348
|
function addImportedModules(target, value) {
|
|
231
349
|
let modules = value;
|
|
232
350
|
if (typeof modules === 'string') {
|
|
@@ -269,7 +387,10 @@ function extractDisplayPageSummary(schemaResult) {
|
|
|
269
387
|
|
|
270
388
|
const displayPage = {
|
|
271
389
|
hasYidaCodeCanvas: false,
|
|
390
|
+
hasCodeBundle: false,
|
|
272
391
|
hasNativeJsx: false,
|
|
392
|
+
codeBundleCount: 0,
|
|
393
|
+
bundleIds: [],
|
|
273
394
|
runtimeCodeBytes: 0,
|
|
274
395
|
sourceCodeBytes: 0,
|
|
275
396
|
compiledCodeBytes: 0,
|
|
@@ -291,10 +412,29 @@ function extractDisplayPageSummary(schemaResult) {
|
|
|
291
412
|
|
|
292
413
|
if (node.componentName === 'YidaCodeCanvas' && isComponentInstance(node)) {
|
|
293
414
|
const props = node.props || {};
|
|
415
|
+
const codeBundle = props.codeBundle && typeof props.codeBundle === 'object'
|
|
416
|
+
? props.codeBundle
|
|
417
|
+
: null;
|
|
294
418
|
displayPage.hasYidaCodeCanvas = true;
|
|
295
419
|
displayPage.componentCount++;
|
|
296
420
|
displayPage.runtimeCodeBytes += codeBytes(props.runtimeCode);
|
|
297
421
|
displayPage.sourceCodeBytes += codeBytes(props.code);
|
|
422
|
+
if (codeBundle) {
|
|
423
|
+
const source = codeBundle.source && typeof codeBundle.source === 'object'
|
|
424
|
+
? codeBundle.source
|
|
425
|
+
: {};
|
|
426
|
+
const runtime = codeBundle.runtime && typeof codeBundle.runtime === 'object'
|
|
427
|
+
? codeBundle.runtime
|
|
428
|
+
: {};
|
|
429
|
+
const bundleId = String(codeBundle.bundleId || '');
|
|
430
|
+
displayPage.hasCodeBundle = true;
|
|
431
|
+
displayPage.codeBundleCount++;
|
|
432
|
+
displayPage.sourceCodeBytes += Number(source.size) || 0;
|
|
433
|
+
displayPage.runtimeCodeBytes += Number(runtime.size) || 0;
|
|
434
|
+
if (bundleId && !displayPage.bundleIds.includes(bundleId)) {
|
|
435
|
+
displayPage.bundleIds.push(bundleId);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
298
438
|
addImportedModules(displayPage.importedModules, props.importedModules);
|
|
299
439
|
} else if (node.componentName === 'Jsx' && isComponentInstance(node)) {
|
|
300
440
|
displayPage.hasNativeJsx = true;
|
|
@@ -581,12 +721,15 @@ async function mapLimit(items, limit, iterator) {
|
|
|
581
721
|
return results;
|
|
582
722
|
}
|
|
583
723
|
|
|
584
|
-
async function fetchSchemaRecord(appType, form, authRef, retries) {
|
|
724
|
+
async function fetchSchemaRecord(appType, form, authRef, retries, options = {}) {
|
|
585
725
|
let lastError = null;
|
|
586
726
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
587
727
|
try {
|
|
588
728
|
const result = await fetchSchema(appType, form.formUuid, authRef);
|
|
589
729
|
if (isSuccessfulSchemaResult(result)) {
|
|
730
|
+
const resolvedResult = options.resolveCodeBundles
|
|
731
|
+
? await resolveCodeBundleSchema(result, appType, form.formUuid, authRef)
|
|
732
|
+
: result;
|
|
590
733
|
const record = {
|
|
591
734
|
formUuid: form.formUuid,
|
|
592
735
|
formName: form.formName,
|
|
@@ -594,10 +737,10 @@ async function fetchSchemaRecord(appType, form, authRef, retries) {
|
|
|
594
737
|
pathName: form.pathName,
|
|
595
738
|
success: true,
|
|
596
739
|
attempts: attempt + 1,
|
|
597
|
-
fieldSummary: extractFieldSummary(
|
|
598
|
-
schema:
|
|
740
|
+
fieldSummary: extractFieldSummary(resolvedResult),
|
|
741
|
+
schema: resolvedResult,
|
|
599
742
|
};
|
|
600
|
-
const displayPage = extractDisplayPageSummary(
|
|
743
|
+
const displayPage = extractDisplayPageSummary(resolvedResult);
|
|
601
744
|
if (displayPage) {
|
|
602
745
|
record.displayPage = displayPage;
|
|
603
746
|
}
|
|
@@ -694,6 +837,10 @@ async function runSingle(parsed, authRef) {
|
|
|
694
837
|
throwCommandError(errorMsg);
|
|
695
838
|
}
|
|
696
839
|
|
|
840
|
+
if (!parsed.field && !parsed.compact && !parsed.summaryJson) {
|
|
841
|
+
result = await resolveCodeBundleSchema(result, parsed.appType, parsed.formUuid, authRef);
|
|
842
|
+
}
|
|
843
|
+
|
|
697
844
|
// 保留既有 --field 返回结构;新 contract 仅由 --compact / --resolve-fields 触发。
|
|
698
845
|
if (parsed.field) {
|
|
699
846
|
const allFieldNodes = collectFieldNodes(result);
|
|
@@ -782,7 +929,9 @@ async function runBatch(parsed, authRef) {
|
|
|
782
929
|
info(` 批量获取 ${forms.length} 个表单 Schema,并发 ${parsed.concurrency},重试 ${parsed.retries}`);
|
|
783
930
|
|
|
784
931
|
const records = await mapLimit(forms, parsed.concurrency, async (form) => {
|
|
785
|
-
const record = await fetchSchemaRecord(parsed.appType, form, authRef, parsed.retries
|
|
932
|
+
const record = await fetchSchemaRecord(parsed.appType, form, authRef, parsed.retries, {
|
|
933
|
+
resolveCodeBundles: !parsed.summaryJson,
|
|
934
|
+
});
|
|
786
935
|
if (record.success && parsed.analysisJson) {
|
|
787
936
|
record.semanticAnalysis = buildSemanticAnalysis(
|
|
788
937
|
parsed.appType,
|
|
@@ -848,5 +997,9 @@ module.exports = {
|
|
|
848
997
|
fetchSchemaRecord,
|
|
849
998
|
collectFieldNodes,
|
|
850
999
|
findFieldNode,
|
|
1000
|
+
validateCodeBundleDescriptor,
|
|
1001
|
+
collectCodeBundleCanvases,
|
|
1002
|
+
downloadCodeBundleArtifact,
|
|
1003
|
+
resolveCodeBundleSchema,
|
|
851
1004
|
run,
|
|
852
1005
|
};
|
package/lib/app/publish.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 流程:
|
|
12
12
|
* 1. 读取源文件,通过内置 babel-transform 编译 + UglifyJS 压缩
|
|
13
|
-
* 2. 用代码动态构建 Schema
|
|
13
|
+
* 2. 用代码动态构建 Schema;Canvas 页面拆出 source/runtime 后统一保存
|
|
14
14
|
* 3. 读取 token session,在写前确认登录态
|
|
15
|
-
* 4.
|
|
15
|
+
* 4. Canvas 调统一保存接口;普通 JSX 页面仍调用原 saveFormSchema
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
const fs = require('fs');
|
|
@@ -21,6 +21,7 @@ const querystring = require('querystring');
|
|
|
21
21
|
const {
|
|
22
22
|
findProjectRoot,
|
|
23
23
|
httpPost,
|
|
24
|
+
httpPostMultipart,
|
|
24
25
|
httpGet,
|
|
25
26
|
requestWithAutoLogin,
|
|
26
27
|
} = require('../core/utils');
|
|
@@ -59,6 +60,8 @@ const {
|
|
|
59
60
|
const SCHEMA_VERSION = 'V5';
|
|
60
61
|
const DOMAIN_CODE = 'tEXDRG';
|
|
61
62
|
const PREFIX = '_view';
|
|
63
|
+
const MAX_CODE_BUNDLE_BYTES = 5 * 1024 * 1024;
|
|
64
|
+
const MAX_CANVAS_SCHEMA_SKELETON_BYTES = 1024 * 1024;
|
|
62
65
|
|
|
63
66
|
// ── 参数解析 ─────────────────────────────────────────
|
|
64
67
|
|
|
@@ -285,6 +288,61 @@ function buildCanvasSchemaContent(sourceCode, runtimeCode, importedModules, form
|
|
|
285
288
|
return buildCanvasPageSchemaContent(sourceCode, runtimeCode, importedModules, formUuid);
|
|
286
289
|
}
|
|
287
290
|
|
|
291
|
+
function buildCanvasSavePayload(schemaContent, sourceCode, runtimeCode) {
|
|
292
|
+
if (typeof sourceCode !== 'string' || sourceCode.length === 0
|
|
293
|
+
|| typeof runtimeCode !== 'string' || runtimeCode.length === 0) {
|
|
294
|
+
throw new Error('Canvas source/runtime 不能为空');
|
|
295
|
+
}
|
|
296
|
+
const totalCodeBytes = Buffer.byteLength(sourceCode, 'utf8')
|
|
297
|
+
+ Buffer.byteLength(runtimeCode, 'utf8');
|
|
298
|
+
if (totalCodeBytes > MAX_CODE_BUNDLE_BYTES) {
|
|
299
|
+
throw new Error('Canvas source/runtime 总大小不能超过 5 MiB');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let schema;
|
|
303
|
+
try {
|
|
304
|
+
schema = JSON.parse(schemaContent);
|
|
305
|
+
} catch {
|
|
306
|
+
throw new Error('Canvas Schema 不是有效 JSON');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const canvases = [];
|
|
310
|
+
function traverse(node) {
|
|
311
|
+
if (Array.isArray(node)) {
|
|
312
|
+
node.forEach(traverse);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (!node || typeof node !== 'object') {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (node.componentName === 'YidaCodeCanvas' && node.id) {
|
|
319
|
+
canvases.push(node);
|
|
320
|
+
}
|
|
321
|
+
Object.values(node).forEach(traverse);
|
|
322
|
+
}
|
|
323
|
+
traverse(schema);
|
|
324
|
+
if (canvases.length !== 1) {
|
|
325
|
+
throw new Error('Canvas Schema 必须且只能包含一个 YidaCodeCanvas 节点');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const canvas = canvases[0];
|
|
329
|
+
canvas.props = canvas.props && typeof canvas.props === 'object' ? canvas.props : {};
|
|
330
|
+
delete canvas.props.code;
|
|
331
|
+
delete canvas.props.runtimeCode;
|
|
332
|
+
delete canvas.props.codeBundle;
|
|
333
|
+
|
|
334
|
+
const skeletonContent = JSON.stringify(schema);
|
|
335
|
+
if (Buffer.byteLength(skeletonContent, 'utf8') > MAX_CANVAS_SCHEMA_SKELETON_BYTES) {
|
|
336
|
+
throw new Error('Canvas Schema 骨架不能超过 1 MiB');
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
canvasNodeId: canvas.id,
|
|
340
|
+
content: skeletonContent,
|
|
341
|
+
sourceCode,
|
|
342
|
+
runtimeCode,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
288
346
|
|
|
289
347
|
// ── 4. 发送 saveFormSchema 请求 ──────────────────────
|
|
290
348
|
|
|
@@ -314,6 +372,40 @@ function sendSaveRequestWithAuth(authRef, schemaContent, appType, formUuid, serv
|
|
|
314
372
|
return httpPost(authRef.baseUrl, saveSchemaPath, postData, { silentStatus: true });
|
|
315
373
|
}
|
|
316
374
|
|
|
375
|
+
function sendCanvasSaveRequestWithAuth(authRef, payload, appType, formUuid, serverRevision) {
|
|
376
|
+
if (
|
|
377
|
+
!authRef ||
|
|
378
|
+
typeof authRef.baseUrl !== 'string' ||
|
|
379
|
+
authRef.baseUrl.length === 0 ||
|
|
380
|
+
!isTokenAuthRef(authRef)
|
|
381
|
+
) {
|
|
382
|
+
const authError = new Error('Publish Canvas write authentication is not ready.');
|
|
383
|
+
authError.code = 'PUBLISH_SCHEMA_WRITE_PRECHECK_FAILED';
|
|
384
|
+
return Promise.reject(authError);
|
|
385
|
+
}
|
|
386
|
+
const gmtModified = requireSchemaServerRevision({ gmtModified: serverRevision });
|
|
387
|
+
const savePath = `/alibaba/web/${appType}/query/codeBundle/save.json?_stamp=${Date.now()}`;
|
|
388
|
+
return httpPostMultipart(authRef.baseUrl, savePath, {
|
|
389
|
+
_csrf_token: authRef.csrfToken,
|
|
390
|
+
formUuid,
|
|
391
|
+
gmtModified,
|
|
392
|
+
canvasNodeId: payload.canvasNodeId,
|
|
393
|
+
content: payload.content,
|
|
394
|
+
importSchema: true,
|
|
395
|
+
}, {
|
|
396
|
+
source: {
|
|
397
|
+
content: payload.sourceCode,
|
|
398
|
+
fileName: 'source.jsx',
|
|
399
|
+
contentType: 'text/plain; charset=UTF-8',
|
|
400
|
+
},
|
|
401
|
+
runtime: {
|
|
402
|
+
content: payload.runtimeCode,
|
|
403
|
+
fileName: 'runtime.js',
|
|
404
|
+
contentType: 'application/javascript; charset=UTF-8',
|
|
405
|
+
},
|
|
406
|
+
}, { silentStatus: true });
|
|
407
|
+
}
|
|
408
|
+
|
|
317
409
|
function walkFiles(dir, results, limit) {
|
|
318
410
|
if (!fs.existsSync(dir) || results.length >= limit) {
|
|
319
411
|
return;
|
|
@@ -624,6 +716,8 @@ async function runMain(argv) {
|
|
|
624
716
|
baseUrl = authRef.baseUrl;
|
|
625
717
|
|
|
626
718
|
let schemaContent;
|
|
719
|
+
let expectedSchemaContent;
|
|
720
|
+
let canvasSavePayload;
|
|
627
721
|
let serverRevision;
|
|
628
722
|
if (isCanvas) {
|
|
629
723
|
try {
|
|
@@ -633,7 +727,18 @@ async function runMain(argv) {
|
|
|
633
727
|
process.exit(1);
|
|
634
728
|
}
|
|
635
729
|
// canvasResult 已在登录前本地编译完成,这里只需据其装配 Schema content。
|
|
636
|
-
|
|
730
|
+
expectedSchemaContent = buildCanvasSchemaContent(
|
|
731
|
+
sourceCode,
|
|
732
|
+
canvasResult.runtimeCode,
|
|
733
|
+
canvasResult.importedModules,
|
|
734
|
+
formUuid
|
|
735
|
+
);
|
|
736
|
+
canvasSavePayload = buildCanvasSavePayload(
|
|
737
|
+
expectedSchemaContent,
|
|
738
|
+
sourceCode,
|
|
739
|
+
canvasResult.runtimeCode
|
|
740
|
+
);
|
|
741
|
+
schemaContent = canvasSavePayload.content;
|
|
637
742
|
} else {
|
|
638
743
|
let existingDataSource = null;
|
|
639
744
|
try {
|
|
@@ -646,6 +751,7 @@ async function runMain(argv) {
|
|
|
646
751
|
}
|
|
647
752
|
baseUrl = authRef.baseUrl;
|
|
648
753
|
schemaContent = buildSchemaContent(sourceCode, compiledCode, formUuid, { existingDataSource });
|
|
754
|
+
expectedSchemaContent = schemaContent;
|
|
649
755
|
}
|
|
650
756
|
success(t('publish.schema_built'));
|
|
651
757
|
|
|
@@ -656,7 +762,15 @@ async function runMain(argv) {
|
|
|
656
762
|
label('Source:', sourcePath);
|
|
657
763
|
label('Compiled:', compiledPath);
|
|
658
764
|
step(3, t('publish.step_publish'));
|
|
659
|
-
const response =
|
|
765
|
+
const response = isCanvas
|
|
766
|
+
? await sendCanvasSaveRequestWithAuth(
|
|
767
|
+
authRef,
|
|
768
|
+
canvasSavePayload,
|
|
769
|
+
appType,
|
|
770
|
+
formUuid,
|
|
771
|
+
serverRevision
|
|
772
|
+
)
|
|
773
|
+
: await sendSaveRequestWithAuth(authRef, schemaContent, appType, formUuid, serverRevision);
|
|
660
774
|
|
|
661
775
|
if (!response || !response.success) {
|
|
662
776
|
const errorMsg = response ? response.errorMsg || t('common.unknown_error') : t('common.request_failed');
|
|
@@ -670,6 +784,9 @@ async function runMain(argv) {
|
|
|
670
784
|
const content = response.content || {};
|
|
671
785
|
const savedFormUuid = content.formUuid || formUuid;
|
|
672
786
|
const version = content.version || 0;
|
|
787
|
+
const storageMode = isCanvas ? content.storageMode || null : undefined;
|
|
788
|
+
const bundleId = isCanvas ? content.bundleId || null : undefined;
|
|
789
|
+
const fallbackReason = isCanvas ? content.fallbackReason || null : undefined;
|
|
673
790
|
success(t('publish.schema_published'));
|
|
674
791
|
label('Form UUID:', savedFormUuid);
|
|
675
792
|
label('Version:', String(version));
|
|
@@ -688,7 +805,7 @@ async function runMain(argv) {
|
|
|
688
805
|
appType,
|
|
689
806
|
savedFormUuid,
|
|
690
807
|
authRef,
|
|
691
|
-
|
|
808
|
+
expectedSchemaContent,
|
|
692
809
|
isCanvas ? 'canvas' : 'native'
|
|
693
810
|
);
|
|
694
811
|
} catch (healthCheckError) {
|
|
@@ -754,6 +871,9 @@ async function runMain(argv) {
|
|
|
754
871
|
version,
|
|
755
872
|
url: pageUrl,
|
|
756
873
|
publishMode,
|
|
874
|
+
storageMode,
|
|
875
|
+
bundleId,
|
|
876
|
+
fallbackReason,
|
|
757
877
|
publishReadbackVerified: !!(healthCheckResult && healthCheckResult.ok === true),
|
|
758
878
|
runtimeSmokeVerified: false,
|
|
759
879
|
runtimeSmokeStatus: 'not_checked',
|
|
@@ -794,5 +914,7 @@ if (require.main === module) {
|
|
|
794
914
|
module.exports.countCustomPageDataSources = countCustomPageDataSources;
|
|
795
915
|
module.exports.buildSchemaContent = buildSchemaContent;
|
|
796
916
|
module.exports.buildCanvasSchemaContent = buildCanvasSchemaContent;
|
|
917
|
+
module.exports.buildCanvasSavePayload = buildCanvasSavePayload;
|
|
797
918
|
module.exports.sendSaveRequestWithAuth = sendSaveRequestWithAuth;
|
|
919
|
+
module.exports.sendCanvasSaveRequestWithAuth = sendCanvasSaveRequestWithAuth;
|
|
798
920
|
}
|
|
@@ -477,22 +477,21 @@ function buildFullAppArtifactRoute(manifest) {
|
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
function buildApplicationEntryPolicy(builderPath, env = process.env) {
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
runtime.tool === 'mulerun';
|
|
480
|
+
void env;
|
|
481
|
+
const auth = builderPath.auth || {};
|
|
482
|
+
const injectedAuthUsable = auth.auth_runtime === 'env_token_bootstrap' &&
|
|
483
|
+
auth.can_auto_use === true;
|
|
485
484
|
|
|
486
485
|
return {
|
|
487
486
|
schema_version: 1,
|
|
488
|
-
environment:
|
|
487
|
+
environment: injectedAuthUsable ? 'managed_cloud_agent' : 'non_cloud_agent',
|
|
489
488
|
delivery_unit: 'single_application_entry_group',
|
|
490
489
|
resource_delivery: 'summary_only',
|
|
491
490
|
internal_artifact_delivery: 'never',
|
|
492
491
|
entries: {
|
|
493
492
|
workbench: 'always',
|
|
494
493
|
custom: 'when_entry_mode_standalone_and_is_render_nav_false_readback',
|
|
495
|
-
admin:
|
|
494
|
+
admin: injectedAuthUsable ? 'omit' : 'include',
|
|
496
495
|
},
|
|
497
496
|
};
|
|
498
497
|
}
|
|
@@ -1569,8 +1569,8 @@ function summarizeLocalizedCommands(commands) {
|
|
|
1569
1569
|
plan_command_ids: ['design-plan.init', 'design-plan.preview', 'design-plan.materialize', 'design-plan.patch'],
|
|
1570
1570
|
theme_command_ids: ['sample', 'create-app', 'update-app'],
|
|
1571
1571
|
navigation_command_ids: { platform: ['update-app', 'nav-group'], custom: ['update-app', 'update-form-config', 'get-form-config'] },
|
|
1572
|
-
navigation_policy: 'Before PRD planning,
|
|
1573
|
-
design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved
|
|
1572
|
+
navigation_policy: 'Before PRD planning in Fast and Plan, the agent determines navigation ownership and layout from business context. Preserve explicit user requirements and existing navigation for local changes; resource-only scope must not trigger app setting changes. Use native navigation for form, approval and data-management workflows; use custom navigation for persistent menus and content state, branded menus or special interactions. Portal/dashboard/homepage labels and page-local tabs alone imply native navigation. Choose layout from module count, hierarchy, switching frequency, content width and device: top for few shallow modules and wide content, side for many frequent modules, L-shaped/mixed for two-level domains, custom dock for lightweight mobile entries. Preserve user-specified layouts. Store resolved type/source/reason/variant in the brief: ai_default for agent ownership decisions, user_selected for explicit user ownership; distinguish user requirements from agent reasoning. Include navigation in the overall Plan confirmation; Fast implements the recorded navigation decision. Platform navigation uses update-app --layout top|side|l_shape --show-app-nav, which writes layoutDirection=top|side|l_shape and hideAppNav=n. Preserve stored navType unchanged; do not derive or invent it and do not add a --nav-type option. Without --layout, normalize stored legacy layoutDirection and navType using the yc-utils application rules (hoz plus top_side means l_shape; missing layout uses top_fold/top_side for top/l_shape). Shell page-level top_fold/none overrides do not belong in app settings. Custom side/top/mixed/dock is a page layout and requires app and per-page navigation hiding with readback. Verify persisted layoutDirection and hideAppNav; updatedFields and themeVerification do not prove navigation persistence. Navigation tone is separate from layout.',
|
|
1573
|
+
design_mode_policy: 'Analyze requirements first. First-time builds include new apps and existing apps without business pages. Reuse detailed supplied plans; if the mode is unspecified, ask whether to prepare a PRD for confirmation (Plan, more detailed and slower) or build from the supplied requirements (Fast). Without detailed requirements, offer Fast and Plan neutrally. Confirm unresolved visual style and page scope before planning; apply navigation_policy. Existing business apps only clarify the current change. A named resource-only request with explicitScope.allowInferredResources=false skips unrelated navigation and visual questions. Standard Plan uses design-plan init parallelTasks, then executes the returned materialize.command exactly once with business-file and visual-file; preview and --from-preview are only for explicitly incremental large plans. Confirm the displayed revision before building, and do not materialize or patch again after confirmation.',
|
|
1574
1574
|
product_design_policy: 'yida-requirement-analysis owns shared facts and first-time intake. yida-prd owns business planning; yida-design owns visual design. Reuse the same confirmed brief and supplied details. Prepare business and base visuals concurrently, then bind visuals to settled page tasks. yida-app merges and checks the artifacts before creating resources. Plan uses the compact authoring contract and selected theme context; the CLI reads full templates and renders all artifacts.',
|
|
1575
1575
|
ui_guidance_policy: 'Page implementation consumes yida-prd prd.md for positioning, information architecture, page prototype, native form entry policy, material strategy, business-specific checks, resource creation order, page implementation delivery order, navigation order, and acceptance criteria; it consumes yida-design design.md for app custom theme CSS delivery, themeColor/navTheme, visual states, visualScaffold, surface material, rounded rules, density rules, components, state styling, and page-level imageNeed. When a page is imageNeed=required, or beneficial with declared image slots, yida-image-assets prepares a traceable asset manifest before that page is implemented; imageNeed=none skips the branch. prd.md and design.md remain the only design sources of truth. Page implementation may extract page-spec.json as a derived implementation handoff from prd.md + design.md; conflicts are resolved by sending business conflicts to yida-prd and visual conflicts to yida-design before regenerating the spec. Core normal forms default to 1-3 business sample records before page implementation, followed by query readback. An explicit opt-out, configuration dictionary, sensitive data, or lack of safely constructible values requires a recorded skip reason. Screenshots, public sharing, data-source deep binding, and fine navigation grouping are optional after explicit user request or PRD acceptance criteria.',
|
|
1576
1576
|
default_nav_order_policy: 'For custom navigation, implement PRD navigation order in the custom shell and verify app/page navigation hiding; skip platform nav-group ordering. For platform navigation: after the primary page is successfully published, perform exactly one navigation order operation. If the PRD names a navigation order, publish without --auto-nav-order and then call openyida nav-group order <appType> <items...>. If the PRD only gives broad groups or is missing navigation order, use openyida publish ... --auto-nav-order and do not call nav-group order or auto-order afterward. Explicit and automatic ordering are mutually exclusive; never generate per-item move loops. The fallback priority is portal/home/workbench entry > business handling > data management > business analytics > system configuration.',
|
package/lib/core/locales/en.js
CHANGED
|
@@ -915,6 +915,15 @@ Examples:
|
|
|
915
915
|
response_body: ' Response body: {0}',
|
|
916
916
|
response_detail: ' Response detail: {0}',
|
|
917
917
|
response_not_json: 'response is not JSON',
|
|
918
|
+
code_bundle_download_failed: 'CodeBundle download failed: {0} ({1})',
|
|
919
|
+
code_bundle_forbidden: 'permission verification or OSS access was denied',
|
|
920
|
+
code_bundle_not_found: 'the file does not exist or has been removed',
|
|
921
|
+
code_bundle_http_error: 'HTTP {0}',
|
|
922
|
+
code_bundle_html_response: 'received an HTML error page; the environment or route may be incorrect',
|
|
923
|
+
code_bundle_json_response: 'the service returned a JSON error response: {0}',
|
|
924
|
+
code_bundle_unexpected_content_type: 'unexpected response Content-Type: {0}',
|
|
925
|
+
code_bundle_invalid_utf8: 'the response is not valid UTF-8 text',
|
|
926
|
+
code_bundle_integrity_failed: 'CodeBundle {0} integrity verification failed ({1})',
|
|
918
927
|
login_expired: ' Login session expired: {0}',
|
|
919
928
|
csrf_expired: ' CSRF token expired: {0}',
|
|
920
929
|
csrf_refreshed: ' csrf_token refreshed',
|
package/lib/core/locales/zh.js
CHANGED
|
@@ -886,6 +886,15 @@ openyida - 宜搭命令行工具
|
|
|
886
886
|
response_body: ' 响应内容: {0}',
|
|
887
887
|
response_detail: ' 响应详情: {0}',
|
|
888
888
|
response_not_json: '响应非 JSON',
|
|
889
|
+
code_bundle_download_failed: 'CodeBundle 下载失败:{0}({1})',
|
|
890
|
+
code_bundle_forbidden: '权限校验或 OSS 访问被拒绝',
|
|
891
|
+
code_bundle_not_found: '文件不存在或已被清理',
|
|
892
|
+
code_bundle_http_error: 'HTTP {0}',
|
|
893
|
+
code_bundle_html_response: '返回了 HTML 错误页,可能是环境或路由错误',
|
|
894
|
+
code_bundle_json_response: '服务端返回 JSON 错误响应:{0}',
|
|
895
|
+
code_bundle_unexpected_content_type: '响应 Content-Type 不符合预期:{0}',
|
|
896
|
+
code_bundle_invalid_utf8: '响应内容不是有效 UTF-8 文本',
|
|
897
|
+
code_bundle_integrity_failed: 'CodeBundle {0} 完整性校验失败({1})',
|
|
889
898
|
login_expired: ' 检测到登录过期: {0}',
|
|
890
899
|
csrf_expired: ' 检测到 csrf_token 过期: {0}',
|
|
891
900
|
csrf_refreshed: ' csrf_token 已刷新',
|