openxiangda 1.0.94 → 1.0.96

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/cli.js CHANGED
@@ -24,7 +24,9 @@ const { assertCanInitializeWorkspace, initWorkspace } = require('./workspace-ini
24
24
  const { bootstrapWorkspace } = require('./workspace-bootstrap');
25
25
  const {
26
26
  getDesignGates,
27
+ getDesignTopicCatalog,
27
28
  getResourceExplain,
29
+ renderDesignHelp,
28
30
  renderDesignGatesText,
29
31
  renderDesignTemplate,
30
32
  renderResourceExplain,
@@ -49,6 +51,9 @@ const RUNTIME_UPLOAD_CONCURRENCY = 3;
49
51
 
50
52
  async function main(argv) {
51
53
  const [command, ...rest] = argv;
54
+ if (command === 'version' || command === '--version' || command === '-v') {
55
+ return version(rest);
56
+ }
52
57
  if (!command || command === 'help' || command === '--help' || command === '-h') {
53
58
  printHelp();
54
59
  return;
@@ -91,6 +96,7 @@ function printHelp() {
91
96
  print(`OpenXiangda CLI
92
97
 
93
98
  Usage:
99
+ openxiangda version [--json]
94
100
  openxiangda login <platform-url> [--profile name]
95
101
  openxiangda update check|install [--json] [--registry https://registry.npmjs.org]
96
102
  openxiangda platform add <name> <platform-url>
@@ -153,6 +159,7 @@ Usage:
153
159
 
154
160
  OpenXiangda 使用普通用户登录 token,不需要 AK/SK。
155
161
  表单页、流程表单页和代码页的主链路是 sy-lowcode-app-workspace + openxiangda workspace publish。
162
+ 新 React SPA 公开访问使用 public-access 命令和 src/resources/public-access;settings public-access 仅用于旧表单公开设置兼容/修复。
156
163
  JS_CODE V2 使用 trusted_node;AI 源码必须写在 src/js-code-nodes/<scriptCode>/index.ts,代码自动化源码写在 src/automations/<resourceCode>/index.ts。definition-json 中的 sourceFile.localPath 会在 validate/create/publish 时先 TS 校验、再构建并上传为快照。`);
157
164
  }
158
165
 
@@ -233,6 +240,17 @@ function wantsSubcommandHelp(subcommand, flags) {
233
240
  );
234
241
  }
235
242
 
243
+ async function version(args) {
244
+ const { flags } = parseArgs(args);
245
+ const result = {
246
+ name: NPM_PACKAGE_NAME,
247
+ version: CURRENT_VERSION,
248
+ updateCheckCommand: 'openxiangda update check --json',
249
+ };
250
+ if (flags.json) return writeJson(result);
251
+ print(CURRENT_VERSION);
252
+ }
253
+
236
254
  async function update(args) {
237
255
  const requestedSubcommand = args[0] && !args[0].startsWith('--') ? args[0] : 'check';
238
256
  const parsedArgs = requestedSubcommand === args[0] ? args.slice(1) : args;
@@ -435,7 +453,8 @@ async function design(args) {
435
453
  const { flags, positional } = parseArgs(rest);
436
454
  const topic = flags.topic || positional[0];
437
455
  if (wantsSubcommandHelp(subcommand, flags)) {
438
- print('用法: openxiangda design gates|template [--topic new-app,public-access] [--json]');
456
+ if (flags.json) return writeJson(getDesignTopicCatalog());
457
+ print(renderDesignHelp());
439
458
  return;
440
459
  }
441
460
 
@@ -466,7 +485,7 @@ async function doctor(args) {
466
485
  return;
467
486
  }
468
487
  const config = loadConfig();
469
- const profileName = flags.profile || config.currentProfile;
488
+ const profileName = resolveDoctorProfileName(config, flags);
470
489
  const result = {
471
490
  cli: {
472
491
  version: CURRENT_VERSION,
@@ -561,6 +580,24 @@ async function doctor(args) {
561
580
  printDoctorReport(result);
562
581
  }
563
582
 
583
+ function resolveDoctorProfileName(config, flags) {
584
+ if (flags.profile) return flags.profile;
585
+ const state = loadProjectState();
586
+ const boundEntries = Object.entries(state.profiles || {}).filter(([, bound]) => bound?.appType);
587
+ const requestedAppType = flags['app-type'];
588
+ if (requestedAppType) {
589
+ const matched = boundEntries.find(([, bound]) => bound.appType === requestedAppType);
590
+ if (matched) return matched[0];
591
+ }
592
+ if (config.currentProfile && state.profiles?.[config.currentProfile]?.appType) {
593
+ return config.currentProfile;
594
+ }
595
+ if (boundEntries.length === 1) {
596
+ return boundEntries[0][0];
597
+ }
598
+ return config.currentProfile;
599
+ }
600
+
564
601
  function printDoctorReport(result) {
565
602
  const lines = [
566
603
  `OpenXiangda doctor v${result.cli.version}`,
@@ -3998,7 +4035,9 @@ async function commands(args) {
3998
4035
  const { flags } = parseArgs(args);
3999
4036
  const manifest = {
4000
4037
  name: 'openxiangda',
4038
+ version: CURRENT_VERSION,
4001
4039
  commands: [
4040
+ 'version [--json]',
4002
4041
  'login <platform-url> [--profile name]',
4003
4042
  'update check|install',
4004
4043
  'platform add|list|use|remove',
@@ -4025,10 +4064,19 @@ async function commands(args) {
4025
4064
  'permission form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions',
4026
4065
  'settings get|save|indexes|indexes-save|data-management|data-management-save|public-access|public-access-save|public-access-delete',
4027
4066
  'resource validate|plan|publish|pull|typegen|explain',
4067
+ 'runtime deploy|releases|activate',
4028
4068
  'inspect app|form|workflow|automation|permissions',
4029
4069
  'feedback preview|submit',
4030
4070
  'skill install|status|bootstrap',
4031
4071
  ],
4072
+ designTopics: getDesignTopicCatalog(),
4073
+ resourceNotes: [
4074
+ 'For formal multi-resource development, prefer src/resources/** + openxiangda resource validate|plan|publish.',
4075
+ 'Use first-class route/public-access/auth-config/function/connector/notification/data-view/menu/permission commands for discovery, diagnosis, dry-run, and small live fixes.',
4076
+ 'public-access is the new React SPA public policy resource for /view/:appType/public/* routes.',
4077
+ 'settings public-access is legacy form public-access compatibility/repair and should not be used for new React SPA apps.',
4078
+ 'Direct live mutation commands should use --dry-run first and --write-manifest when the repository should remain source of truth.',
4079
+ ],
4032
4080
  };
4033
4081
  if (flags.json) return writeJson(manifest);
4034
4082
  print(JSON.stringify(manifest, null, 2));
@@ -5047,6 +5095,9 @@ function validateWorkspaceResources(manifest) {
5047
5095
  const errors = [];
5048
5096
  const warnings = [];
5049
5097
  const counts = {};
5098
+ if (!fs.existsSync(manifest.baseDir)) {
5099
+ warnings.push('未发现 src/resources;多资源正式开发请在该目录声明资源,或使用一等 CLI 子命令进行只读诊断/小步维护');
5100
+ }
5050
5101
  for (const spec of RESOURCE_SPECS) {
5051
5102
  const items = manifest[spec.key] || [];
5052
5103
  counts[spec.key] = items.length;
@@ -219,19 +219,112 @@ const DESIGN_GATE_TOPICS = [
219
219
  },
220
220
  ];
221
221
 
222
+ const DESIGN_GATE_TOPIC_ALIASES = {
223
+ app: 'new-app',
224
+ application: 'new-app',
225
+ dashboard: 'complex-page',
226
+ page: 'complex-page',
227
+ pages: 'complex-page',
228
+ login: 'auth',
229
+ register: 'auth',
230
+ registration: 'auth',
231
+ sso: 'auth',
232
+ public: 'public-access',
233
+ guest: 'public-access',
234
+ visitor: 'public-access',
235
+ visitors: 'public-access',
236
+ permission: 'permissions',
237
+ role: 'permissions',
238
+ roles: 'permissions',
239
+ workflow: 'workflow-automation',
240
+ automation: 'workflow-automation',
241
+ function: 'workflow-automation',
242
+ functions: 'workflow-automation',
243
+ 'app-function': 'workflow-automation',
244
+ js_code: 'workflow-automation',
245
+ jscode: 'workflow-automation',
246
+ connector: 'connector-notification',
247
+ connectors: 'connector-notification',
248
+ notification: 'connector-notification',
249
+ notifications: 'connector-notification',
250
+ integration: 'connector-notification',
251
+ integrations: 'connector-notification',
252
+ webhook: 'connector-notification',
253
+ resource: 'resource-maintenance',
254
+ resources: 'resource-maintenance',
255
+ maintenance: 'resource-maintenance',
256
+ };
257
+
258
+ function resolveTopicSelection(topicCode) {
259
+ if (!topicCode || topicCode === 'all') {
260
+ return { requested: ['all'], wanted: null, unknown: [] };
261
+ }
262
+ const knownCodes = new Set(DESIGN_GATE_TOPICS.map(topic => topic.code));
263
+ const requested = String(topicCode)
264
+ .split(/[,/|+\s]+/)
265
+ .map(item => item.trim())
266
+ .filter(Boolean);
267
+ const wanted = new Set();
268
+ const unknown = [];
269
+ for (const item of requested) {
270
+ const normalized = item.toLowerCase();
271
+ const code = DESIGN_GATE_TOPIC_ALIASES[normalized] || normalized;
272
+ if (knownCodes.has(code)) {
273
+ wanted.add(code);
274
+ } else {
275
+ unknown.push(item);
276
+ }
277
+ }
278
+ return { requested, wanted, unknown };
279
+ }
280
+
222
281
  function selectTopics(topicCode) {
223
- if (!topicCode || topicCode === 'all') return DESIGN_GATE_TOPICS;
224
- const wanted = new Set(String(topicCode).split(',').map(item => item.trim()).filter(Boolean));
225
- return DESIGN_GATE_TOPICS.filter(topic => wanted.has(topic.code));
282
+ const selection = resolveTopicSelection(topicCode);
283
+ if (!selection.wanted) return DESIGN_GATE_TOPICS;
284
+ return DESIGN_GATE_TOPICS.filter(topic => selection.wanted.has(topic.code));
226
285
  }
227
286
 
228
287
  function getDesignGates(topicCode) {
288
+ const selection = resolveTopicSelection(topicCode);
229
289
  return {
230
290
  hardRule: DESIGN_GATE_HARD_RULE,
291
+ requestedTopics: selection.requested,
292
+ unknownTopics: selection.unknown,
231
293
  topics: selectTopics(topicCode),
232
294
  };
233
295
  }
234
296
 
297
+ function getDesignTopicCatalog() {
298
+ return {
299
+ hardRule: DESIGN_GATE_HARD_RULE,
300
+ topics: DESIGN_GATE_TOPICS.map(topic => ({
301
+ code: topic.code,
302
+ title: topic.title,
303
+ triggers: topic.triggers,
304
+ })),
305
+ aliases: DESIGN_GATE_TOPIC_ALIASES,
306
+ };
307
+ }
308
+
309
+ function renderDesignHelp() {
310
+ const catalog = getDesignTopicCatalog();
311
+ const lines = [
312
+ '用法: openxiangda design gates|template [--topic code[,code...]] [--json]',
313
+ '',
314
+ catalog.hardRule,
315
+ '',
316
+ 'Topics:',
317
+ ];
318
+ for (const topic of catalog.topics) {
319
+ lines.push(`- ${topic.code}: ${topic.title} (${topic.triggers.join('、')})`);
320
+ }
321
+ lines.push('', 'Aliases:');
322
+ for (const [alias, code] of Object.entries(catalog.aliases).sort()) {
323
+ lines.push(`- ${alias} -> ${code}`);
324
+ }
325
+ return lines.join('\n');
326
+ }
327
+
235
328
  function renderDesignGatesText(topicCode) {
236
329
  const gates = getDesignGates(topicCode);
237
330
  const lines = [gates.hardRule, ''];
@@ -441,8 +534,11 @@ function renderResourceExplain(type) {
441
534
  module.exports = {
442
535
  DESIGN_GATE_HARD_RULE,
443
536
  DESIGN_GATE_TOPICS,
537
+ DESIGN_GATE_TOPIC_ALIASES,
444
538
  getDesignGates,
539
+ getDesignTopicCatalog,
445
540
  getResourceExplain,
541
+ renderDesignHelp,
446
542
  renderDesignGatesText,
447
543
  renderDesignTemplate,
448
544
  renderResourceExplain,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.94",
3
+ "version": "1.0.96",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {