openyida 2026.8.25-1 → 2026.8.26

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.
Files changed (29) hide show
  1. package/README.md +5 -1
  2. package/bin/yida.js +3 -0
  3. package/lib/app/canvas-compile.js +169 -0
  4. package/lib/app/create-form/api-path.js +11 -2
  5. package/lib/app/create-form.js +109 -339
  6. package/lib/app/create-page.js +25 -13
  7. package/lib/app/form-detail-style.js +3 -28
  8. package/lib/app/import-app.js +13 -31
  9. package/lib/app/publish.js +18 -74
  10. package/lib/core/command-manifest.js +9 -0
  11. package/lib/core/locales/en.js +21 -22
  12. package/lib/core/locales/zh.js +21 -20
  13. package/lib/core/query-data.js +111 -12
  14. package/lib/core/utils.js +41 -0
  15. package/lib/integration/integration-create.js +12 -0
  16. package/lib/integration/integration-update.js +113 -0
  17. package/package.json +2 -1
  18. package/yida-skills/references/yida-api.md +0 -29
  19. package/yida-skills/skills/yida-app/references/common-issues.md +2 -0
  20. package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +1 -0
  21. package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +10 -8
  22. package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +8 -6
  23. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +14 -0
  24. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +2 -0
  25. package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +7 -5
  26. package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +1 -0
  27. package/yida-skills/skills/yida-form-detail/references/injection-guide.md +0 -22
  28. package/yida-skills/skills/yida-integration/SKILL.md +23 -5
  29. package/yida-skills/skills/yida-publish-page/SKILL.md +3 -3
package/README.md CHANGED
@@ -349,6 +349,9 @@ openyida connector list
349
349
  openyida integration create APP_XXX FORM_XXX "Sync customer data"
350
350
  openyida integration create APP_XXX FORM_XXX "Approval result notify" \
351
351
  --events processFinish --approval-actions agree,disagree --receivers 123456
352
+ # Capability probe only; currently fails closed before auth/spec reads/remote writes
353
+ openyida integration update APP_XXX FORM_XXX LPROC_XXX \
354
+ --spec .cache/openyida/integration/desired-spec.json
352
355
  openyida create-report APP_XXX "Sales Dashboard" .cache/openyida/reports/charts.json
353
356
  openyida append-chart APP_XXX REPORT_XXX .cache/openyida/reports/chart.json
354
357
  ```
@@ -477,6 +480,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
477
480
  | Command | Description |
478
481
  |---------|-------------|
479
482
  | `openyida integration create <appType> ... [--spec file.json]` | Create integration automation flow |
483
+ | `openyida integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]` | Probe integration update capability (currently blocked without full readback) |
480
484
  | `openyida integration list <appType> [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
481
485
  | `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
482
486
  | `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
@@ -537,7 +541,7 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
537
541
 
538
542
  #### Workflow, Reports, and Integrations
539
543
 
540
- `openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`.
544
+ `openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`. Passing `--process-code` selects a full graph replacement and now requires explicit `--replace`; it is not a safe update. `integration update` currently performs capability detection only: because full platform `processJson` + `viewJson` readback is unproven, it writes a redacted local probe artifact and returns `PLATFORM_PROBE_REQUIRED` before authentication, spec reading, or remote writes. It does not edit a flow.
541
545
 
542
546
  ## Agent Skills
543
547
 
package/bin/yida.js CHANGED
@@ -1173,6 +1173,9 @@ async function main() {
1173
1173
  if (subCommand === 'create') {
1174
1174
  const { run: runIntegration } = require('../lib/integration/integration-create');
1175
1175
  await runIntegration(subArgs);
1176
+ } else if (subCommand === 'update') {
1177
+ const { run: runIntegrationUpdate } = require('../lib/integration/integration-update');
1178
+ await runIntegrationUpdate(subArgs);
1176
1179
  } else if (subCommand === 'list') {
1177
1180
  const { runList } = require('../lib/integration/integration-list');
1178
1181
  await runList(subArgs);
@@ -29,6 +29,7 @@
29
29
  */
30
30
 
31
31
  const Babel = require('@babel/standalone');
32
+ const globals = require('globals');
32
33
  const {
33
34
  assertNoEmojiInArtifactName,
34
35
  assertNoEmojiInText,
@@ -46,6 +47,7 @@ const {
46
47
  findFormDetailLinkIssues,
47
48
  formatFormDetailLinkMessage,
48
49
  } = require('./form-detail-link-guard');
50
+ const { t } = require('../core/i18n');
49
51
 
50
52
  /**
51
53
  * 依赖 → window 别名白名单,用于把 import 改写到运行时全局对象。
@@ -132,6 +134,15 @@ const BARE_DEPENDENCY_GLOBALS = Object.freeze({
132
134
  },
133
135
  });
134
136
 
137
+ // 以标准 ECMAScript + Browser 环境定义为完整白名单;Canvas 装配器额外
138
+ // 注入 iframeWindow / parentWindow。Node 专属全局不属于页面运行时,不能放行。
139
+ const CANVAS_RUNTIME_GLOBAL_IDENTIFIERS = new Set([
140
+ ...Object.keys(globals.builtin),
141
+ ...Object.keys(globals.browser),
142
+ 'iframeWindow',
143
+ 'parentWindow',
144
+ ]);
145
+
135
146
  const LUCIDE_REACT_PACKAGE = 'lucide-react';
136
147
  const LUCIDE_DYNAMIC_ICON_ALIAS = 'DynamicIcon';
137
148
  const LUCIDE_MODULE_ALIAS = 'LucideReact';
@@ -267,6 +278,159 @@ function assertNoBareDependencyGlobals(source, options = {}) {
267
278
  });
268
279
  }
269
280
 
281
+ function findUnboundIdentifierIssues(source, options = {}) {
282
+ const issuesByName = new Map();
283
+ const allowedIdentifiers = new Set(CANVAS_RUNTIME_GLOBAL_IDENTIFIERS);
284
+ (options.implicitIdentifiers || []).forEach((name) => allowedIdentifiers.add(name));
285
+
286
+ const guardPlugin = () => ({
287
+ name: 'yida-canvas-unbound-identifier-guard',
288
+ visitor: {
289
+ ReferencedIdentifier(path) {
290
+ const name = path.node.name;
291
+ if (allowedIdentifiers.has(name) || path.scope.hasBinding(name) || issuesByName.has(name)) {
292
+ return;
293
+ }
294
+ issuesByName.set(name, {
295
+ name,
296
+ line: path.node.loc && path.node.loc.start ? path.node.loc.start.line : 0,
297
+ column: path.node.loc && path.node.loc.start ? path.node.loc.start.column : 0,
298
+ });
299
+ },
300
+ },
301
+ });
302
+
303
+ Babel.transform(source, {
304
+ filename: 'canvas.tsx',
305
+ presets: [
306
+ ['typescript', { allExtensions: true, isTSX: true, allowDeclareFields: true }],
307
+ ['react', { runtime: 'classic' }],
308
+ ],
309
+ plugins: [guardPlugin],
310
+ sourceType: 'module',
311
+ compact: false,
312
+ babelrc: false,
313
+ configFile: false,
314
+ });
315
+
316
+ return Array.from(issuesByName.values());
317
+ }
318
+
319
+ function formatUnboundIdentifierMessage(issues) {
320
+ const summary = issues
321
+ .map(issue => `${issue.name} (${issue.line}:${issue.column + 1})`)
322
+ .join(', ');
323
+ return t('publish.canvas_unbound_identifiers', summary);
324
+ }
325
+
326
+ function assertNoUnboundIdentifiers(source, options = {}) {
327
+ const issues = findUnboundIdentifierIssues(source, options);
328
+ if (issues.length === 0) {
329
+ return;
330
+ }
331
+ throw new CliError(formatUnboundIdentifierMessage(issues), {
332
+ code: 'OPENYIDA_CANVAS_UNBOUND_IDENTIFIER',
333
+ details: {
334
+ stage: 'canvas_compile',
335
+ sourcePath: options.sourcePath || '',
336
+ issues,
337
+ },
338
+ });
339
+ }
340
+
341
+ function findUnavailableCanvasInstanceApiIssues(source) {
342
+ const issues = [];
343
+ const seen = new Set();
344
+
345
+ function addIssue(path, api) {
346
+ if (seen.has(api)) {
347
+ return;
348
+ }
349
+ seen.add(api);
350
+ issues.push({
351
+ api,
352
+ line: path.node.loc && path.node.loc.start ? path.node.loc.start.line : 0,
353
+ column: path.node.loc && path.node.loc.start ? path.node.loc.start.column : 0,
354
+ });
355
+ }
356
+
357
+ const guardPlugin = ({ types: types }) => ({
358
+ name: 'yida-canvas-instance-api-guard',
359
+ visitor: {
360
+ MemberExpression(path) {
361
+ const object = path.node.object;
362
+ const property = path.node.property;
363
+ const propertyName = path.node.computed && types.isStringLiteral(property)
364
+ ? property.value
365
+ : (types.isIdentifier(property) ? property.name : '');
366
+
367
+ if (types.isMemberExpression(object)) {
368
+ const owner = object.object;
369
+ const ownerProperty = object.property;
370
+ const ownerPropertyName = object.computed && types.isStringLiteral(ownerProperty)
371
+ ? ownerProperty.value
372
+ : (types.isIdentifier(ownerProperty) ? ownerProperty.name : '');
373
+ if (
374
+ types.isIdentifier(owner, { name: 'props' }) &&
375
+ ownerPropertyName === 'utils' &&
376
+ propertyName === 'getDataList'
377
+ ) {
378
+ addIssue(path, 'props.utils.getDataList');
379
+ return;
380
+ }
381
+ if (
382
+ types.isThisExpression(owner) &&
383
+ ownerPropertyName === 'utils' &&
384
+ propertyName === 'yida'
385
+ ) {
386
+ addIssue(path, 'this.utils.yida');
387
+ return;
388
+ }
389
+ }
390
+ if (!types.isThisExpression(object)) {
391
+ return;
392
+ }
393
+ if (propertyName === 'dataSourceMap') {
394
+ addIssue(path, 'this.dataSourceMap');
395
+ } else if (propertyName === '$') {
396
+ addIssue(path, 'this.$');
397
+ }
398
+ },
399
+ },
400
+ });
401
+
402
+ Babel.transform(source, {
403
+ filename: 'canvas.tsx',
404
+ presets: [
405
+ ['typescript', { allExtensions: true, isTSX: true, allowDeclareFields: true }],
406
+ ['react', { runtime: 'classic' }],
407
+ ],
408
+ plugins: [guardPlugin],
409
+ sourceType: 'module',
410
+ compact: false,
411
+ babelrc: false,
412
+ configFile: false,
413
+ });
414
+
415
+ return issues;
416
+ }
417
+
418
+ function assertNoUnavailableCanvasInstanceApis(source, options = {}) {
419
+ const issues = findUnavailableCanvasInstanceApiIssues(source);
420
+ if (issues.length === 0) {
421
+ return;
422
+ }
423
+ const summary = issues.map(issue => issue.api).join(', ');
424
+ throw new CliError(t('publish.canvas_instance_api_unavailable', summary), {
425
+ code: 'OPENYIDA_CANVAS_INSTANCE_API_UNAVAILABLE',
426
+ details: {
427
+ stage: 'canvas_compile',
428
+ sourcePath: options.sourcePath || '',
429
+ issues,
430
+ },
431
+ });
432
+ }
433
+
270
434
  /**
271
435
  * 从源码里正则抽取裸包名(过滤相对/绝对路径),去重排序。
272
436
  * 对齐画布运行时的依赖抽取行为。
@@ -617,6 +781,7 @@ function compileCanvasLocal(source, options = {}) {
617
781
  },
618
782
  });
619
783
  }
784
+ assertNoUnavailableCanvasInstanceApis(source, options);
620
785
  assertNoBareDependencyGlobals(source, options);
621
786
  assertNoManualDependencyGlobals(source, options);
622
787
 
@@ -643,6 +808,10 @@ function compileCanvasLocal(source, options = {}) {
643
808
  // 'react' 进入依赖清单(运行时据此注入 React UMD)。
644
809
  const usesJsxRuntime = /\bReact\.createElement\b|\bReact\.Fragment\b/.test(intermediate);
645
810
  const hasReactBinding = /\b(var|let|const)\s+React\b/.test(intermediate) || /\bimport\s+React\b/.test(intermediate);
811
+ assertNoUnboundIdentifiers(source, {
812
+ sourcePath: options.sourcePath,
813
+ implicitIdentifiers: usesJsxRuntime ? ['React'] : [],
814
+ });
646
815
  if (usesJsxRuntime && !hasReactBinding) {
647
816
  intermediate = "import React from 'react';\n" + intermediate;
648
817
  if (!importedModules.includes('react')) {
@@ -10,14 +10,23 @@
10
10
  * @param {object} [options] 可选项
11
11
  * @param {string} [options.prefix] 额外的路径前缀(会以 / 拼接)
12
12
  * @param {string} [options.namespace] 命名空间,默认 dingtalk
13
+ * @param {'formdesign'|'formnav'} [options.queryModule] 查询模块,默认 formdesign
13
14
  * @param {boolean} [options.addTimestamp] 是否追加 _stamp 时间戳查询参数
14
15
  * @returns {string} 拼接完成的 API 请求路径
15
16
  */
16
17
  function buildApiPath(appType, apiName, options = {}) {
17
- const { prefix = '', namespace = 'dingtalk', addTimestamp = false } = options;
18
+ const {
19
+ prefix = '',
20
+ namespace = 'dingtalk',
21
+ queryModule = 'formdesign',
22
+ addTimestamp = false,
23
+ } = options;
24
+ if (queryModule !== 'formdesign' && queryModule !== 'formnav') {
25
+ throw new Error(`Unsupported query module: ${queryModule}`);
26
+ }
18
27
  const prefixPath = prefix ? `/${prefix}` : '';
19
28
  const timestamp = addTimestamp ? `?_stamp=${Date.now()}` : '';
20
- return `/${namespace}/web/${appType}${prefixPath}/query/formdesign/${apiName}.json${timestamp}`;
29
+ return `/${namespace}/web/${appType}${prefixPath}/query/${queryModule}/${apiName}.json${timestamp}`;
21
30
  }
22
31
 
23
32
  module.exports = {