zero-ai 1.0.75 → 1.0.79

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 (45) hide show
  1. package/package.json +1 -1
  2. package/r2mo-init/.obsidian/app.json +1 -0
  3. package/r2mo-init/.obsidian/appearance.json +10 -0
  4. package/r2mo-init/.obsidian/community-plugins.json +7 -0
  5. package/r2mo-init/.obsidian/core-plugins.json +33 -0
  6. package/r2mo-init/.obsidian/plugins/dataview/main.js +20876 -0
  7. package/r2mo-init/.obsidian/plugins/dataview/manifest.json +11 -0
  8. package/r2mo-init/.obsidian/plugins/dataview/styles.css +141 -0
  9. package/r2mo-init/.obsidian/plugins/obsidian-excalidraw-plugin/data.json +815 -0
  10. package/r2mo-init/.obsidian/plugins/obsidian-excalidraw-plugin/main.js +10 -0
  11. package/r2mo-init/.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json +12 -0
  12. package/r2mo-init/.obsidian/plugins/obsidian-excalidraw-plugin/styles.css +1 -0
  13. package/r2mo-init/.obsidian/plugins/obsidian-kanban/main.js +153 -0
  14. package/r2mo-init/.obsidian/plugins/obsidian-kanban/manifest.json +11 -0
  15. package/r2mo-init/.obsidian/plugins/obsidian-kanban/styles.css +1 -0
  16. package/r2mo-init/.obsidian/plugins/obsidian-plantuml/main.js +7732 -0
  17. package/r2mo-init/.obsidian/plugins/obsidian-plantuml/manifest.json +10 -0
  18. package/r2mo-init/.obsidian/plugins/obsidian-plantuml/styles.css +38 -0
  19. package/r2mo-init/.obsidian/plugins/obsidian-tasks-plugin/main.js +504 -0
  20. package/r2mo-init/.obsidian/plugins/obsidian-tasks-plugin/manifest.json +12 -0
  21. package/r2mo-init/.obsidian/plugins/obsidian-tasks-plugin/styles.css +1 -0
  22. package/r2mo-init/.obsidian/snippets/body-font.css +9 -0
  23. package/r2mo-init/.obsidian/themes/Comfort/manifest.json +11 -0
  24. package/r2mo-init/.obsidian/themes/Comfort/theme.css +218 -0
  25. package/r2mo-init/.obsidian/themes/Primary/manifest.json +9 -0
  26. package/r2mo-init/.obsidian/themes/Primary/theme.css +3878 -0
  27. package/r2mo-init/.obsidian/themes/Retro Windows/manifest.json +7 -0
  28. package/r2mo-init/.obsidian/themes/Retro Windows/theme.css +582 -0
  29. package/r2mo-init/.obsidian/themes/RetroOS 98/manifest.json +9 -0
  30. package/r2mo-init/.obsidian/themes/RetroOS 98/theme.css +2566 -0
  31. package/r2mo-init/.obsidian/themes/Serenity/manifest.json +7 -0
  32. package/r2mo-init/.obsidian/themes/Serenity/theme.css +7258 -0
  33. package/r2mo-init/.obsidian/themes/W95/manifest.json +8 -0
  34. package/r2mo-init/.obsidian/themes/W95/theme.css +768 -0
  35. package/r2mo-init/.obsidian/types.json +28 -0
  36. package/r2mo-init/task/command/ex-api.yaml.example +13 -0
  37. package/r2mo-init/task/task-001.md +144 -0
  38. package/r2mo-init/task/task-002.md +4 -0
  39. package/r2mo-init/task/task-003.md +4 -0
  40. package/r2mo-init/task/thread +1 -0
  41. package/src/commander/ex-api.json +1 -2
  42. package/src/commander/ex-crud.json +1 -1
  43. package/src/commander-ai/fn.ex.api.js +192 -103
  44. package/src/commander-ai/fn.ex.crud.js +102 -58
  45. package/.cursor/rules/test.mdc +0 -4
@@ -8,7 +8,7 @@ const yaml = require("js-yaml");
8
8
  const inquirer = require("inquirer");
9
9
  const { v4: uuidv4 } = require("uuid");
10
10
 
11
- const CONFIG_PATH = ".r2mo/task/command/ex-crud.yaml";
11
+ const CONFIG_DIR = ".r2mo/task/command/ex-crud";
12
12
  const REQUIRED_ENV_DB = ["Z_DB_TYPE", "Z_DB_HOST", "Z_DB_PORT", "Z_DBS_INSTANCE", "Z_DB_APP_USER", "Z_DB_APP_PASS"];
13
13
  const REQUIRED_ENV_APP = ["Z_APP_ID", "Z_TENANT", "Z_SIGMA"];
14
14
 
@@ -261,23 +261,35 @@ async function copyTemplateWithReplace(templateDir, destDir, meta, skipNames) {
261
261
  }
262
262
  }
263
263
 
264
- /** 解析 ex-crud.yaml 路径:先 cwd,再上级目录(与 ex-api 一致,便于在 -api 子目录执行时找到带 target 的配置) */
265
- function resolveExCrudConfigPath(cwd) {
266
- const primary = path.resolve(cwd, CONFIG_PATH);
267
- if (fs.existsSync(primary)) return primary;
268
- const parent = path.resolve(cwd, "..", CONFIG_PATH);
269
- if (fs.existsSync(parent)) return parent;
270
- const grand = path.resolve(cwd, "..", "..", CONFIG_PATH);
271
- if (fs.existsSync(grand)) return grand;
264
+ /** 校验 ex-crud metadata:至少 keyword、identifier 非空 */
265
+ function validateExCrudMetadata(config) {
266
+ if (!config || !config.metadata || typeof config.metadata !== "object") return { valid: false, error: "缺少 metadata" };
267
+ const m = config.metadata;
268
+ const keyword = m.keyword != null ? String(m.keyword).trim() : "";
269
+ const identifier = m.identifier != null ? String(m.identifier).trim() : "";
270
+ if (!keyword) return { valid: false, error: "metadata.keyword 为空" };
271
+ if (!identifier) return { valid: false, error: "metadata.identifier 为空" };
272
+ if (!/^[a-zA-Z0-9._-]+$/.test(identifier)) return { valid: false, error: "metadata.identifier 仅允许字母数字、点、下划线、横线" };
273
+ return { valid: true };
274
+ }
275
+
276
+ /** 解析 ex-crud 配置目录:cwd / 上级 / 上上级 */
277
+ function resolveExCrudConfigDir(cwd) {
278
+ const primary = path.resolve(cwd, CONFIG_DIR);
279
+ if (fs.existsSync(primary) && fs.statSync(primary).isDirectory()) return primary;
280
+ const parent = path.resolve(cwd, "..", CONFIG_DIR);
281
+ if (fs.existsSync(parent) && fs.statSync(parent).isDirectory()) return parent;
282
+ const grand = path.resolve(cwd, "..", "..", CONFIG_DIR);
283
+ if (fs.existsSync(grand) && fs.statSync(grand).isDirectory()) return grand;
272
284
  return primary;
273
285
  }
274
286
 
275
287
  module.exports = async (options) => {
276
288
  const cwd = process.cwd();
277
- const configFullPath = resolveExCrudConfigPath(cwd);
278
- if (!fs.existsSync(configFullPath)) {
279
- const configDir = path.dirname(configFullPath);
280
- if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
289
+ const configDir = resolveExCrudConfigDir(cwd);
290
+ if (!fs.existsSync(configDir)) {
291
+ fs.mkdirSync(configDir, { recursive: true });
292
+ const templatePath = path.join(configDir, "ex-crud.yaml");
281
293
  const template = `# ai ex-crud 使用此配置,请按项目修改
282
294
  metadata:
283
295
  keyword: "log"
@@ -290,56 +302,45 @@ metadata:
290
302
  # root: "ZERO_MODULE"
291
303
  # module: "ambient"
292
304
  `;
293
- fs.writeFileSync(configFullPath, template, "utf-8");
294
- Ec.info("配置文件缺失,已在下列路径写入模板:" + configFullPath);
305
+ fs.writeFileSync(templatePath, template, "utf-8");
306
+ Ec.info("配置目录缺失,已创建并写入模板:" + templatePath);
295
307
  Ec.info("请编辑后重新执行: ai ex-crud");
296
308
  process.exit(1);
297
309
  }
298
310
 
299
- let config;
300
- try {
301
- config = yaml.load(fs.readFileSync(configFullPath, "utf-8"));
302
- } catch (e) {
303
- Ec.error("ex-crud.yaml 解析失败:" + e.message);
304
- process.exit(1);
305
- }
306
- if (!config || !config.metadata) {
307
- Ec.error("ex-crud.yaml 需包含 metadata 节点");
308
- process.exit(1);
311
+ const backupDir = path.join(configDir, "backup");
312
+ const allEntries = fs.readdirSync(configDir, { withFileTypes: true });
313
+ const yamlFiles = allEntries.filter((e) => !e.isDirectory() && e.isFile() && (e.name.endsWith(".yaml") || e.name.endsWith(".yml")));
314
+ const entries = [];
315
+ for (const e of yamlFiles) {
316
+ const f = e.name;
317
+ const full = path.join(configDir, f);
318
+ try {
319
+ const config = yaml.load(fs.readFileSync(full, "utf-8"));
320
+ const valid = validateExCrudMetadata(config);
321
+ if (!valid.valid) {
322
+ Ec.info("[ex-crud] 警告(metadata 不合法,已跳过):" + f + "," + (valid.error || ""));
323
+ continue;
324
+ }
325
+ const label = (config.metadata.identifier || f) + " | " + (config.metadata.keyword || "") + (config.metadata.name ? " " + config.metadata.name : "");
326
+ entries.push({ path: full, config, label });
327
+ } catch (err) {
328
+ Ec.info("[ex-crud] 跳过(解析失败):" + f + "," + (err && err.message));
329
+ }
309
330
  }
310
331
 
311
- const metadata = config.metadata;
312
- // target ex-api 一致:root + module 存在时分流到 zero-exmodule-{module}
313
- let target = config.target;
314
- if (target && typeof target === "object") {
315
- const root = target.root != null ? String(target.root).trim() : "";
316
- const moduleName = target.module != null ? String(target.module).trim() : "";
317
- if (root && moduleName) target = { root, module: moduleName };
318
- else target = null;
319
- } else {
320
- target = null;
332
+ if (entries.length === 0) {
333
+ Ec.error("[ex-crud] 无有效配置:请在 " + configDir + " 下添加含合法 metadata(keyword、identifier)的 yaml");
334
+ process.exit(1);
321
335
  }
322
- Ec.info("[ex-crud] 配置:" + configFullPath + (target ? ",target=" + target.module : ",无 target"));
323
- const meta = {
324
- keyword: metadata.keyword != null ? String(metadata.keyword).trim() : "",
325
- identifier: metadata.identifier != null ? String(metadata.identifier).trim() : "",
326
- actor: metadata.actor != null ? String(metadata.actor).trim() : "",
327
- name: metadata.name != null ? String(metadata.name).trim() : "",
328
- type: metadata.type != null ? String(metadata.type).trim() : ""
329
- };
330
336
 
331
- if (target) {
332
- const zeroModule = process.env.ZERO_MODULE;
333
- if (!zeroModule || !String(zeroModule).trim()) {
334
- Ec.error("存在 target 配置时,环境变量 ZERO_MODULE 必须已设置");
335
- process.exit(1);
336
- }
337
- const dpaRoot = path.resolve(zeroModule || "", `zero-exmodule-${target.module}`);
338
- if (!fs.existsSync(dpaRoot) || !isDpaRoot(dpaRoot)) {
339
- Ec.error(`ZERO_MODULE 下 DPA 目录不是标准架构:${dpaRoot}`);
340
- Ec.info("需存在 pom.xml 且包含 xxx-api、xxx-domain 子目录");
341
- process.exit(1);
342
- }
337
+ const answer = await inquirer.prompt([
338
+ { type: "checkbox", name: "selected", message: "选择要执行的 CRUD 配置(多选)", choices: entries.map((e) => ({ name: e.label, value: e.path })) }
339
+ ]);
340
+ const selectedPaths = answer && answer.selected && Array.isArray(answer.selected) ? answer.selected : [];
341
+ if (selectedPaths.length === 0) {
342
+ Ec.info("未选择任何项,退出");
343
+ process.exit(0);
343
344
  }
344
345
 
345
346
  const parsed = Ut.parseArgument(options);
@@ -358,8 +359,27 @@ metadata:
358
359
 
359
360
  Ec.execute("ai ex-crud:配置已加载。");
360
361
 
361
- // 1. 模板目录(R2MO-INIT 包内)与输出目录(与 ex-api 一致:有 target 时分流到 zero-exmodule-{module},无 target 时为 zero-launcher-configuration)
362
362
  const templateDir = path.resolve(__dirname, "..", "_template", "EXCEL", "ex-crud");
363
+ const loadedConfigs = selectedPaths.map((p) => ({ path: p, config: yaml.load(fs.readFileSync(p, "utf-8")) }));
364
+ const first = loadedConfigs[0];
365
+ let target = first.config && first.config.target && typeof first.config.target === "object"
366
+ ? (first.config.target.root && first.config.target.module ? { root: String(first.config.target.root).trim(), module: String(first.config.target.module).trim() } : null)
367
+ : null;
368
+
369
+ if (target) {
370
+ const zeroModule = process.env.ZERO_MODULE;
371
+ if (!zeroModule || !String(zeroModule).trim()) {
372
+ Ec.error("存在 target 配置时,环境变量 ZERO_MODULE 必须已设置");
373
+ process.exit(1);
374
+ }
375
+ const dpaRoot = path.resolve(zeroModule || "", `zero-exmodule-${target.module}`);
376
+ if (!fs.existsSync(dpaRoot) || !isDpaRoot(dpaRoot)) {
377
+ Ec.error(`ZERO_MODULE 下 DPA 目录不是标准架构:${dpaRoot}`);
378
+ Ec.info("需存在 pom.xml 且包含 xxx-api、xxx-domain 子目录");
379
+ process.exit(1);
380
+ }
381
+ }
382
+
363
383
  const excelRoot = resolveExcelRoot(cwd, target);
364
384
  const domainName = target && target.module ? `zero-exmodule-${target.module}-domain` : null;
365
385
  const pluginsBase = domainName
@@ -376,7 +396,31 @@ metadata:
376
396
  Ec.info("[ex-crud] RBAC_CRUD:" + rbacCrudDir);
377
397
  Ec.info("[ex-crud] RBAC_ROLE :" + rbacRoleDir);
378
398
 
379
- await copyTemplateWithReplace(templateDir, rbacCrudDir, meta, ["ex-crud.yaml", "README.md", "template-RBAC_ROLE.xlsx", ".DS_Store"]);
399
+ for (const { path: configPath, config } of loadedConfigs) {
400
+ const valid = validateExCrudMetadata(config);
401
+ if (!valid.valid) {
402
+ Ec.info("[ex-crud] 警告(metadata 不合法,跳过执行):" + path.basename(configPath) + "," + (valid.error || ""));
403
+ continue;
404
+ }
405
+ const metadata = config.metadata;
406
+ const meta = {
407
+ keyword: metadata.keyword != null ? String(metadata.keyword).trim() : "",
408
+ identifier: metadata.identifier != null ? String(metadata.identifier).trim() : "",
409
+ actor: metadata.actor != null ? String(metadata.actor).trim() : "",
410
+ name: metadata.name != null ? String(metadata.name).trim() : "",
411
+ type: metadata.type != null ? String(metadata.type).trim() : ""
412
+ };
413
+ Ec.info("[ex-crud] 处理:" + (metadata.identifier || path.basename(configPath)));
414
+ try {
415
+ await copyTemplateWithReplace(templateDir, rbacCrudDir, meta, ["ex-crud.yaml", "README.md", "template-RBAC_ROLE.xlsx", ".DS_Store"]);
416
+ if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true });
417
+ const bakPath = path.join(backupDir, path.basename(configPath) + ".bak");
418
+ fs.renameSync(configPath, bakPath);
419
+ Ec.info("[ex-crud] 已备份:" + path.basename(configPath) + " -> backup/" + path.basename(configPath) + ".bak");
420
+ } catch (err) {
421
+ Ec.info("[ex-crud] 生成或备份失败(未备份):" + path.basename(configPath) + "," + (err && err.message));
422
+ }
423
+ }
380
424
 
381
425
  Ec.info("[ex-crud] 已生成 CRUD 文件到 RBAC_CRUD");
382
426
 
@@ -436,7 +480,7 @@ metadata:
436
480
  const rolePermsToWrite = roleIds.flatMap((rid) => permissionIds.map((pid) => ({ ROLE_ID: rid, PERM_ID: pid })));
437
481
  if (!fs.existsSync(rbacRoleDir)) fs.mkdirSync(rbacRoleDir, { recursive: true });
438
482
  const ExcelJS = require("exceljs");
439
- const roleFileName = "falcon-crud-" + (meta.identifier || "default").replace(/[^a-zA-Z0-9._-]/g, "_") + ".xlsx";
483
+ const roleFileName = "falcon-crud-" + (first.config.metadata && first.config.metadata.identifier ? String(first.config.metadata.identifier) : "batch").replace(/[^a-zA-Z0-9._-]/g, "_") + ".xlsx";
440
484
  const outRolePath = path.join(rbacRoleDir, roleFileName);
441
485
  const templatePath = path.resolve(__dirname, "..", "_template", "EXCEL", "ex-crud", "template-RBAC_ROLE.xlsx");
442
486
  let roleWorkbook;
@@ -1,4 +0,0 @@
1
- ---
2
- description: "This rule provides standards for frontend components and API validation"
3
- alwaysApply: false
4
- ---