openyida 0.1.2 → 1.0.0-beta.0

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 (61) hide show
  1. package/README.md +68 -38
  2. package/bin/yida.js +164 -761
  3. package/lib/babel-transform/index.js +244 -0
  4. package/lib/babel-transform/jsx-utils.js +89 -0
  5. package/lib/check-update.js +72 -0
  6. package/lib/copy.js +258 -0
  7. package/lib/create-app.js +174 -0
  8. package/lib/create-form.js +2244 -0
  9. package/lib/create-page.js +89 -0
  10. package/lib/env.js +164 -0
  11. package/lib/get-page-config.js +102 -0
  12. package/lib/get-schema.js +76 -0
  13. package/lib/login.js +323 -0
  14. package/lib/publish.js +610 -0
  15. package/lib/save-share-config.js +268 -0
  16. package/lib/update-form-config.js +237 -0
  17. package/lib/utils.js +443 -0
  18. package/lib/verify-short-url.js +279 -0
  19. package/package.json +20 -7
  20. package/project/.cache/demo-schema.json +2353 -0
  21. package/project/pages/src/demo-birthday-game.js +833 -0
  22. package/project/pages/src/demo-future-vision-2026.js +1102 -0
  23. package/project/pages/src/demo-salary-calculator.js +904 -0
  24. package/project/prd/demo-birthday-game.md +39 -0
  25. package/project/prd/demo-future-vision-2026.md +78 -0
  26. package/project/prd/demo-salary-calculator.md +101 -0
  27. package/scripts/postinstall.js +114 -0
  28. package/yida-skills/SKILL.md +273 -0
  29. package/yida-skills/reference/association-form-field.md +469 -0
  30. package/yida-skills/reference/employee-field.md +17 -0
  31. package/yida-skills/reference/model-api.md +73 -0
  32. package/yida-skills/reference/serial-number-field.md +132 -0
  33. package/yida-skills/reference/yida-api.md +1208 -0
  34. package/yida-skills/skills/yida-app/SKILL.md +394 -0
  35. package/yida-skills/skills/yida-create-app/SKILL.md +158 -0
  36. package/yida-skills/skills/yida-create-form-page/SKILL.md +598 -0
  37. package/yida-skills/skills/yida-create-page/SKILL.md +103 -0
  38. package/yida-skills/skills/yida-custom-page/SKILL.md +533 -0
  39. package/yida-skills/skills/yida-get-schema/SKILL.md +90 -0
  40. package/yida-skills/skills/yida-login/SKILL.md +200 -0
  41. package/yida-skills/skills/yida-logout/SKILL.md +58 -0
  42. package/yida-skills/skills/yida-page-config/SKILL.md +261 -0
  43. package/yida-skills/skills/yida-publish-page/SKILL.md +113 -0
  44. package/.eslintrc.json +0 -25
  45. package/.github/workflows/ci.yml +0 -123
  46. package/.github/workflows/publish.yml +0 -105
  47. package/.github/workflows/update-contributors.yml +0 -151
  48. package/.openclaw/skills/yida-issue/SKILL.md +0 -27
  49. package/.openclaw/skills/yida-issue/scripts/create-issue.js +0 -317
  50. package/CLAUDE.md +0 -168
  51. package/CONTRIBUTING.md +0 -59
  52. package/install-skills.ps1 +0 -162
  53. package/install-skills.sh +0 -175
  54. package/pages/dist/.gitkeep +0 -0
  55. package/pages/src/.gitkeep +0 -0
  56. package/prd/salary-calculator.md +0 -15
  57. package/tests/cli.test.js +0 -930
  58. package/tests/install.test.js +0 -277
  59. package/tests/yida-issue.test.js +0 -314
  60. /package/{config.json → project/config.json} +0 -0
  61. /package/{.cache → project/pages/dist}/.gitkeep +0 -0
package/bin/yida.js CHANGED
@@ -1,811 +1,214 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * OpenYida CLI - 宜搭命令行工具
3
+ * openyida - 宜搭命令行工具
4
4
  *
5
5
  * 安装:npm install -g openyida
6
- * 用法:yida <命令> [参数]
6
+ * 用法:openyida <命令> [参数](别名:yida)
7
7
  *
8
- * 通过调用 .claude/skills/skills/ 下的脚本复用 yida-skills 的能力。
9
- * Skills 安装位置:<项目根目录>/.claude/skills/skills/
8
+ * 命令列表:
9
+ * openyida env 检测当前 AI 工具环境和登录态
10
+ * openyida copy [--force] 复制 project 工作目录到当前 AI 工具环境
11
+ * openyida login 登录态管理
12
+ * openyida logout 退出登录
13
+ * openyida create-app "<名称>" [desc] [icon] [color] 创建应用
14
+ * openyida create-page <appType> "<页面名>" 创建自定义页面
15
+ * openyida create-form create <appType> "<表单名>" <字段JSON> 创建表单页面
16
+ * openyida create-form update <appType> <formUuid> <修改JSON> 更新表单页面
17
+ * openyida get-schema <appType> <formUuid> 获取表单 Schema
18
+ * openyida publish <源文件路径> <appType> <formUuid> 编译并发布自定义页面
19
+ * openyida verify-short-url <appType> <formUuid> <url> 验证短链接 URL 是否可用
20
+ * openyida save-share-config <appType> <formUuid> <url> <isOpen> [openAuth] 保存公开访问/分享配置
21
+ * openyida get-page-config <appType> <formUuid> 查询页面公开访问/分享配置
22
+ * openyida update-form-config <appType> <formUuid> <isRenderNav> <title> 更新表单配置
10
23
  */
11
24
 
12
25
  "use strict";
13
26
 
14
- const { Command } = require("commander");
15
- const { execSync, spawn } = require("child_process");
16
- const path = require("path");
17
- const fs = require("fs");
27
+ const { checkUpdate } = require('../lib/check-update');
28
+ const { version: currentVersion } = require('../package.json');
29
+
30
+ // 异步检查更新,fire-and-forget,不阻塞主流程
31
+ const updateCheckPromise = checkUpdate(currentVersion);
32
+
33
+ const command = process.argv[2];
34
+ const args = process.argv.slice(3);
35
+
36
+ function printHelp() {
37
+ console.log(`
38
+ openyida - 宜搭命令行工具
39
+
40
+ 用法:
41
+ openyida <命令> [参数...](别名:yida)
42
+
43
+ 命令:
44
+ env 检测当前 AI 工具环境和登录态
45
+ copy [--force] 复制 project 工作目录到当前 AI 工具环境
46
+ login 登录态管理(优先缓存,否则扫码)
47
+ logout 退出登录 / 切换账号
48
+ create-app "<名称>" [描述] [图标] [颜色] 创建应用,输出 appType
49
+ create-page <appType> "<页面名>" 创建自定义页面,输出 pageId
50
+ create-form create <appType> "<表单名>" <字段JSON> 创建表单页面
51
+ create-form update <appType> <formUuid> <修改JSON> 更新表单页面
52
+ get-schema <appType> <formUuid> 获取表单 Schema
53
+ publish <源文件路径> <appType> <formUuid> 编译并发布自定义页面
54
+ verify-short-url <appType> <formUuid> <url> 验证短链接 URL 是否可用
55
+ save-share-config <appType> <formUuid> <url> <isOpen> [auth] 保存公开访问/分享配置
56
+ get-page-config <appType> <formUuid> 查询页面公开访问/分享配置
57
+ update-form-config <appType> <formUuid> <isRenderNav> <title> 更新表单配置
18
58
 
19
- const program = new Command();
20
-
21
- // ── 工具函数 ──────────────────────────────────────────────────────────
22
-
23
- /**
24
- * 查找项目根目录(向上查找 config.json .git)
25
- * CLI 在项目目录内运行时,需要定位到包含 .claude/skills/skills 的根目录
26
- */
27
- function findProjectRoot() {
28
- let currentDir = process.cwd();
29
- while (currentDir !== path.dirname(currentDir)) {
30
- if (
31
- fs.existsSync(path.join(currentDir, "config.json")) ||
32
- fs.existsSync(path.join(currentDir, ".git"))
33
- ) {
34
- return currentDir;
35
- }
36
- currentDir = path.dirname(currentDir);
37
- }
38
- return process.cwd();
59
+ 示例:
60
+ openyida login
61
+ openyida logout
62
+ openyida create-app "考勤管理"
63
+ openyida create-page APP_XXX "游戏主页"
64
+ openyida create-form create APP_XXX "员工信息" fields.json
65
+ openyida create-form update APP_XXX FORM-XXX '[{"action":"add","field":{"type":"TextField","label":"备注"}}]'
66
+ openyida get-schema APP_XXX FORM-XXX
67
+ openyida publish pages/src/home.jsx APP_XXX FORM-XXX
68
+ openyida verify-short-url APP_XXX FORM-XXX /o/myapp
69
+ openyida save-share-config APP_XXX FORM-XXX /o/myapp y n
70
+ openyida get-page-config APP_XXX FORM-XXX
71
+ openyida update-form-config APP_XXX FORM-XXX false "页面标题"
72
+ `);
39
73
  }
40
74
 
41
- /**
42
- * 获取 skill 脚本路径,并检查是否已安装
43
- */
44
- function getSkillScript(skillName, scriptFile) {
45
- const projectRoot = findProjectRoot();
46
- const scriptPath = path.join(projectRoot, ".claude", "skills", "skills", skillName, "scripts", scriptFile);
47
-
48
- if (!fs.existsSync(scriptPath)) {
49
- console.error(`\n❌ 未找到 skill 脚本:${scriptPath}`);
50
- console.error(`\n请先运行安装脚本:`);
51
- console.error(` Mac/Linux:bash install-skills.sh`);
52
- console.error(` Windows: .\\install-skills.ps1`);
53
- console.error(`\n或手动克隆 yida-skills:`);
54
- console.error(` git clone --branch main --depth 1 https://github.com/openyida/yida-skills.git .claude/skills`);
55
- process.exit(1);
75
+ async function main() {
76
+ if (!command || command === '--help' || command === '-h') {
77
+ printHelp();
78
+ process.exit(0);
56
79
  }
57
80
 
58
- return scriptPath;
59
- }
60
-
61
- /**
62
- * 运行 Node.js skill 脚本,将 stdout/stderr 透传到终端
63
- */
64
- function runNodeScript(scriptPath, args = []) {
65
- const child = spawn("node", [scriptPath, ...args], {
66
- stdio: "inherit",
67
- cwd: findProjectRoot(),
68
- });
69
-
70
- child.on("close", (exitCode) => {
71
- process.exit(exitCode ?? 0);
72
- });
73
-
74
- child.on("error", (error) => {
75
- console.error(`\n❌ 执行失败:${error.message}`);
76
- process.exit(1);
77
- });
78
- }
79
-
80
- /**
81
- * 运行 Python skill 脚本,将 stdout/stderr 透传到终端
82
- */
83
- function runPythonScript(scriptPath, args = []) {
84
- const child = spawn("python3", [scriptPath, ...args], {
85
- stdio: "inherit",
86
- cwd: findProjectRoot(),
87
- });
88
-
89
- child.on("close", (exitCode) => {
90
- process.exit(exitCode ?? 0);
91
- });
81
+ if (command === '--version' || command === '-v') {
82
+ console.log(currentVersion);
83
+ process.exit(0);
84
+ }
92
85
 
93
- child.on("error", (error) => {
94
- if (error.code === "ENOENT") {
95
- console.error(`\n❌ 未找到 python3,请先安装 Python 3.8+`);
96
- } else {
97
- console.error(`\n❌ 执行失败:${error.message}`);
86
+ switch (command) {
87
+ case 'env': {
88
+ const { run } = require('../lib/env');
89
+ run();
90
+ break;
98
91
  }
99
- process.exit(1);
100
- });
101
- }
102
-
103
- // ── CLI 配置 ──────────────────────────────────────────────────────────
104
-
105
- program
106
- .name("yida / openyida")
107
- .description("OpenYida CLI - 宜搭命令行工具")
108
- .version("0.1.0");
109
-
110
- // ── yida login ────────────────────────────────────────────────────────
111
-
112
- program
113
- .command("login")
114
- .description("扫码登录宜搭(打开浏览器扫码)")
115
- .action(() => {
116
- console.log("🔐 正在启动登录流程,请扫码...\n");
117
- const scriptPath = getSkillScript("yida-login", "login.py");
118
- runPythonScript(scriptPath);
119
- });
120
-
121
- // ── yida logout ───────────────────────────────────────────────────────
122
-
123
- program
124
- .command("logout")
125
- .description("退出登录,清除本地登录态")
126
- .action(() => {
127
- console.log("👋 正在退出登录...\n");
128
- const scriptPath = getSkillScript("yida-logout", "logout.py");
129
- runPythonScript(scriptPath);
130
- });
131
-
132
- // ── yida create-app ───────────────────────────────────────────────────
133
-
134
- program
135
- .command("create-app <name>")
136
- .description("创建宜搭应用")
137
- .option("-d, --description <desc>", "应用描述(默认同应用名称)")
138
- .option("-i, --icon <icon>", "图标标识(默认 xian-yingyong)", "xian-yingyong")
139
- .option("-c, --color <color>", "图标颜色(默认 #0089FF)", "#0089FF")
140
- .addHelpText("after", `
141
- 示例:
142
- $ yida create-app "考勤管理"
143
- $ yida create-app "考勤管理" -d "员工考勤打卡系统" -i xian-daka -c "#00B853"
144
-
145
- 可用图标:
146
- xian-xinwen, xian-zhengfu, xian-yingyong, xian-xueshimao, xian-qiye,
147
- xian-danju, xian-shichang, xian-jingli, xida-falv, xian-baogao,
148
- huoche, xian-shenbao, xian-diqiu, xian-qiche, xian-feiji,
149
- xian-diannao, xian-gongzuozheng, xian-gouwuche, xian-xinyongka,
150
- xian-huodong, xian-jiangbei, xian-liucheng, xian-chaxun, xian-daka
151
-
152
- 可用颜色:
153
- #0089FF #00B853 #FFA200 #FF7357 #5C72FF
154
- #85C700 #FFC505 #FF6B7A #8F66FF #14A9FF`)
155
- .action((name, options) => {
156
- const scriptPath = getSkillScript("yida-create-app", "create-app.js");
157
- const args = [name];
158
- if (options.description) args.push(options.description);
159
- else args.push(name); // 默认 description 同 name
160
- args.push(options.icon);
161
- args.push(options.color);
162
- runNodeScript(scriptPath, args);
163
- });
164
-
165
- // ── yida create-page ──────────────────────────────────────────────────
166
-
167
- program
168
- .command("create-page")
169
- .description("在指定应用中创建自定义展示页面")
170
- .argument("<app>", "应用 ID(如 APP_XXXXXXXXXXXXX)")
171
- .argument("<name>", "页面名称")
172
- .addHelpText("after", `
173
- 示例:
174
- $ yida create-page APP_XXXXXXXXXXXXX "游戏主页"
175
- $ yida create-page APP_XXXXXXXXXXXXX "数据看板"`)
176
- .action((app, name) => {
177
- const scriptPath = getSkillScript("yida-create-page", "create-page.js");
178
- runNodeScript(scriptPath, [app, name]);
179
- });
180
-
181
- // ── yida create-form ──────────────────────────────────────────────────
182
-
183
- program
184
- .command("create-form")
185
- .description("在指定应用中创建表单页面")
186
- .argument("<app>", "应用 ID(如 APP_XXXXXXXXXXXXX)")
187
- .argument("<name>", "表单名称")
188
- .argument("<fields>", "字段定义(JSON 字符串或 JSON 文件路径)")
189
- .addHelpText("after", `
190
- 示例:
191
- $ yida create-form APP_XXX "客户信息" fields.json
192
- $ yida create-form APP_XXX "客户信息" '[{"type":"TextField","label":"姓名"}]'`)
193
- .action((app, name, fields) => {
194
- const scriptPath = getSkillScript("yida-create-form-page", "create-form-page.js");
195
- runNodeScript(scriptPath, [app, name, fields]);
196
- });
197
-
198
- // ── yida publish ──────────────────────────────────────────────────────
199
-
200
- program
201
- .command("publish")
202
- .description("编译并发布自定义页面到宜搭")
203
- .argument("<file>", "源文件路径(如 pages/src/myapp.js)")
204
- .argument("<app>", "应用 ID(如 APP_XXXXXXXXXXXXX)")
205
- .argument("<form>", "表单/页面 UUID(如 FORM-XXXXXXXXXXXXX)")
206
- .addHelpText("after", `
207
- 示例:
208
- $ yida publish pages/src/myapp.js APP_XXXXXXXXXXXXX FORM-XXXXXXXXXXXXX`)
209
- .action((file, app, form) => {
210
- const scriptPath = getSkillScript("yida-publish-page", "publish.js");
211
- runNodeScript(scriptPath, [app, form, file]);
212
- });
213
-
214
- // ── yida get-schema ───────────────────────────────────────────────────
215
-
216
- program
217
- .command("get-schema")
218
- .description("获取表单/页面的完整 Schema 结构")
219
- .argument("<app>", "应用 ID(如 APP_XXXXXXXXXXXXX)")
220
- .argument("<form>", "表单 UUID(如 FORM-XXXXXXXXXXXXX)")
221
- .addHelpText("after", `
222
- 示例:
223
- $ yida get-schema APP_XXXXXXXXXXXXX FORM-XXXXXXXXXXXXX
224
- $ yida get-schema APP_XXXXXXXXXXXXX FORM-XXXXXXXXXXXXX > schema.json`)
225
- .action((app, form) => {
226
- const scriptPath = getSkillScript("yida-get-schema", "get-schema.js");
227
- runNodeScript(scriptPath, [app, form]);
228
- });
229
-
230
- // ── yida config ───────────────────────────────────────────────────────
231
92
 
232
- program
233
- .command("config")
234
- .description("查看、校验或回滚当前项目配置")
235
- .option("--validate", "校验 config.json 格式和必填字段")
236
- .option("--rollback", "回滚到上一个备份配置(.cache/config.backup.json)")
237
- .option("--show", "显示当前配置(同默认行为)")
238
- .addHelpText("after", `
239
- 示例:
240
- $ yida config # 查看配置和环境状态
241
- $ yida config --validate # 校验配置格式
242
- $ yida config --rollback # 回滚到备份配置`)
243
- .action((options) => {
244
- const projectRoot = findProjectRoot();
245
- const configPath = path.join(projectRoot, "config.json");
246
- const backupPath = path.join(projectRoot, ".cache", "config.backup.json");
247
-
248
- // --rollback:回滚到备份配置
249
- if (options.rollback) {
250
- if (!fs.existsSync(backupPath)) {
251
- console.error("❌ 未找到备份配置文件:.cache/config.backup.json");
252
- console.error(" 请先运行 yida config 生成备份,或手动创建 config.json");
253
- process.exit(1);
254
- }
255
- try {
256
- const backupContent = fs.readFileSync(backupPath, "utf-8");
257
- JSON.parse(backupContent); // 验证备份文件是合法 JSON
258
- fs.writeFileSync(configPath, backupContent, "utf-8");
259
- console.log("✅ 已回滚到备份配置:");
260
- const config = JSON.parse(backupContent);
261
- Object.entries(config).forEach(([key, value]) => {
262
- console.log(` ${key}: ${value}`);
263
- });
264
- } catch (err) {
265
- console.error(`❌ 回滚失败:${err.message}`);
266
- process.exit(1);
267
- }
268
- return;
93
+ case 'copy': {
94
+ const { run } = require('../lib/copy');
95
+ run();
96
+ break;
269
97
  }
270
98
 
271
- // --validate:校验配置
272
- if (options.validate) {
273
- const validationErrors = validateConfig(configPath);
274
- if (validationErrors.length === 0) {
275
- console.log("✅ config.json 校验通过");
99
+ case 'login': {
100
+ const { ensureLogin, checkLoginOnly } = require('../lib/login');
101
+ if (args[0] === '--check-only') {
102
+ const result = checkLoginOnly();
103
+ console.log(JSON.stringify(result, null, 2));
276
104
  } else {
277
- console.error("❌ config.json 校验失败:");
278
- validationErrors.forEach((err) => console.error(` - ${err}`));
279
- process.exit(1);
105
+ const result = ensureLogin();
106
+ console.log(JSON.stringify(result));
280
107
  }
281
- return;
108
+ break;
282
109
  }
283
110
 
284
- // 默认:显示配置状态,并自动备份当前有效配置
285
- console.log(`📁 项目根目录:${projectRoot}`);
286
- console.log(`📄 配置文件:${configPath}`);
287
- console.log("");
288
-
289
- if (!fs.existsSync(configPath)) {
290
- console.log("⚠️ 未找到 config.json,使用默认配置");
291
- console.log(" defaultBaseUrl: https://www.aliwork.com");
292
- console.log("");
293
- console.log("💡 运行 yida doctor --repair 自动创建配置模板");
294
- return;
111
+ case 'logout': {
112
+ const { logout } = require('../lib/login');
113
+ logout();
114
+ break;
295
115
  }
296
116
 
297
- try {
298
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
299
- console.log("当前配置:");
300
- Object.entries(config).forEach(([key, value]) => {
301
- console.log(` ${key}: ${value}`);
302
- });
303
-
304
- // 自动备份有效配置
305
- const cacheDir = path.join(projectRoot, ".cache");
306
- if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
307
- fs.copyFileSync(configPath, backupPath);
308
- } catch {
309
- console.error("❌ 读取配置文件失败(JSON 格式错误)");
310
- console.error(" 运行 yida config --rollback 回滚到上一个有效配置");
117
+ case 'create-app': {
118
+ const { run } = require('../lib/create-app');
119
+ await run(args);
120
+ break;
311
121
  }
312
122
 
313
- // 检查登录态
314
- const cookiePath = path.join(projectRoot, ".cache", "cookies.json");
315
- console.log("");
316
- if (fs.existsSync(cookiePath)) {
317
- try {
318
- const cookieData = JSON.parse(fs.readFileSync(cookiePath, "utf-8"));
319
- const cookies = Array.isArray(cookieData) ? cookieData : cookieData.cookies || [];
320
- const hasToken = cookies.some((c) => c.name === "tianshu_csrf_token");
321
- console.log(`🔑 登录态:${hasToken ? "✅ 已登录" : "⚠️ Cookie 存在但可能已过期"}`);
322
- } catch {
323
- console.log("🔑 登录态:⚠️ Cookie 文件损坏");
324
- }
325
- } else {
326
- console.log("🔑 登录态:❌ 未登录(运行 yida login 登录)");
123
+ case 'create-page': {
124
+ const { run } = require('../lib/create-page');
125
+ await run(args);
126
+ break;
327
127
  }
328
128
 
329
- // 检查 skills 安装
330
- const skillsPath = path.join(projectRoot, ".claude", "skills", "skills");
331
- console.log("");
332
- if (fs.existsSync(skillsPath)) {
333
- const skills = fs.readdirSync(skillsPath).filter((name) =>
334
- fs.statSync(path.join(skillsPath, name)).isDirectory()
335
- );
336
- console.log(`📦 已安装 Skills(${skills.length} 个):`);
337
- skills.forEach((skill) => console.log(` - ${skill}`));
338
- } else {
339
- console.log("📦 Skills:❌ 未安装(运行 bash install-skills.sh 安装)");
129
+ case 'create-form': {
130
+ // create-form.js 通过 process.argv.slice(2) 读取参数,注入子命令及其参数
131
+ process.argv = [process.argv[0], process.argv[1], ...args];
132
+ require('../lib/create-form');
133
+ break;
340
134
  }
341
- });
342
-
343
- // ── yida doctor ───────────────────────────────────────────────────────
344
-
345
- program
346
- .command("doctor")
347
- .description("检查 OpenYida 环境依赖,发现问题并给出修复建议")
348
- .option("--repair", "自动修复可修复的问题(如创建 config.json 模板)")
349
- .addHelpText("after", `
350
- 示例:
351
- $ yida doctor # 检查环境
352
- $ yida doctor --repair # 检查并自动修复`)
353
- .action((options) => {
354
- runDoctorCheck(options.repair);
355
- });
356
-
357
- // ── yida completion ───────────────────────────────────────────────────
358
-
359
- program
360
- .command("completion")
361
- .description("输出 shell 自动补全脚本(bash/zsh/fish)")
362
- .argument("<shell>", "目标 shell 类型:bash | zsh | fish")
363
- .addHelpText("after", `
364
- 安装方法:
365
- bash: yida completion bash >> ~/.bashrc && source ~/.bashrc
366
- zsh: yida completion zsh >> ~/.zshrc && source ~/.zshrc
367
- fish: yida completion fish > ~/.config/fish/completions/yida.fish`)
368
- .action((shellType) => {
369
- printCompletionScript(shellType);
370
- });
371
-
372
- // ── yida shell ────────────────────────────────────────────────────────
373
-
374
- program
375
- .command("shell")
376
- .description("进入交互式 REPL 模式")
377
- .action(() => {
378
- const readline = require("readline");
379
-
380
- const rl = readline.createInterface({
381
- input: process.stdin,
382
- output: process.stdout,
383
- prompt: "yida> ",
384
- historySize: 100,
385
- });
386
-
387
- console.log("🤖 OpenYida Shell(输入 help 查看命令,输入 exit 退出)\n");
388
- rl.prompt();
389
-
390
- rl.on("line", (line) => {
391
- const input = line.trim();
392
135
 
393
- if (!input) {
394
- rl.prompt();
395
- return;
396
- }
397
-
398
- if (input === "exit" || input === "quit") {
399
- console.log("👋 再见!");
400
- rl.close();
401
- return;
402
- }
403
-
404
- if (input === "help") {
405
- console.log(`
406
- 可用命令:
407
- login 扫码登录
408
- logout 退出登录
409
- create-app <name> 创建应用
410
- create-page <app> <name> 创建自定义页面
411
- create-form <app> <name> <fields> 创建表单页面
412
- publish <file> <app> <form> 发布页面
413
- get-schema <app> <form> 获取表单 Schema
414
- config 查看配置
415
- exit / quit 退出 Shell
416
- `);
417
- rl.prompt();
418
- return;
419
- }
420
-
421
- // 将输入解析为 argv 并交给 Commander 处理
422
- const args = parseShellArgs(input);
423
- try {
424
- // 暂停 readline,等待子命令完成后恢复
425
- rl.pause();
426
- const child = spawn(process.execPath, [process.argv[1], ...args], {
427
- stdio: "inherit",
428
- cwd: process.cwd(),
429
- });
430
- child.on("close", () => {
431
- rl.resume();
432
- rl.prompt();
433
- });
434
- child.on("error", (error) => {
435
- console.error(`\n❌ 执行失败:${error.message}`);
436
- rl.resume();
437
- rl.prompt();
438
- });
439
- } catch (error) {
440
- console.error(`\n❌ 错误:${error.message}`);
441
- rl.prompt();
442
- }
443
- });
444
-
445
- rl.on("close", () => {
446
- process.exit(0);
447
- });
448
- });
449
-
450
- // ── doctor / completion / config 辅助函数 ────────────────────────────
451
-
452
- /**
453
- * 校验 config.json 格式和必填字段
454
- * @param {string} configPath - config.json 的完整路径
455
- * @returns {string[]} 错误列表,空数组表示校验通过
456
- */
457
- function validateConfig(configPath) {
458
- const errors = [];
459
- if (!fs.existsSync(configPath)) {
460
- errors.push("config.json 不存在");
461
- return errors;
462
- }
463
- let config;
464
- try {
465
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
466
- } catch {
467
- errors.push("config.json 不是合法的 JSON 格式");
468
- return errors;
469
- }
470
- if (!config.loginUrl) {
471
- errors.push("缺少必填字段:loginUrl(宜搭登录页面地址)");
472
- } else if (!/^https?:\/\/.+/.test(config.loginUrl)) {
473
- errors.push("loginUrl 不是合法的 URL");
474
- }
475
- if (!config.defaultBaseUrl) {
476
- errors.push("缺少必填字段:defaultBaseUrl(API 请求基础地址)");
477
- } else if (!/^https?:\/\/.+/.test(config.defaultBaseUrl)) {
478
- errors.push("defaultBaseUrl 不是合法的 URL");
479
- }
480
- return errors;
481
- }
482
-
483
- /**
484
- * 执行 yida doctor 环境检查
485
- * @param {boolean} repair - 是否自动修复可修复的问题
486
- */
487
- function runDoctorCheck(repair) {
488
- const projectRoot = findProjectRoot();
489
- const configPath = path.join(projectRoot, "config.json");
490
- const cookiePath = path.join(projectRoot, ".cache", "cookies.json");
491
- const skillsPath = path.join(projectRoot, ".claude", "skills", "skills");
492
-
493
- console.log("🔍 检查 OpenYida 环境依赖...\n");
494
-
495
- const issues = [];
496
-
497
- // 1. Node.js 版本
498
- const nodeVersion = process.versions.node;
499
- const nodeMajor = parseInt(nodeVersion.split(".")[0], 10);
500
- if (nodeMajor >= 16) {
501
- console.log(`✅ Node.js v${nodeVersion}(要求 ≥ 16)`);
502
- } else {
503
- console.log(`❌ Node.js v${nodeVersion}(要求 ≥ 16,请升级)`);
504
- issues.push({ type: "error", msg: `Node.js 版本过低(${nodeVersion}),请升级到 v16+` });
505
- }
506
-
507
- // 2. Python 版本
508
- try {
509
- const pythonVersion = execSync("python3 --version 2>&1", { encoding: "utf-8" }).trim();
510
- const versionMatch = pythonVersion.match(/Python (\d+)\.(\d+)/);
511
- if (versionMatch) {
512
- const major = parseInt(versionMatch[1], 10);
513
- const minor = parseInt(versionMatch[2], 10);
514
- if (major > 3 || (major === 3 && minor >= 10)) {
515
- console.log(`✅ ${pythonVersion}(要求 ≥ 3.10)`);
516
- } else {
517
- console.log(`❌ ${pythonVersion}(要求 ≥ 3.10,请升级)`);
518
- issues.push({ type: "error", msg: `Python 版本过低(${pythonVersion}),请升级到 3.10+` });
519
- }
136
+ case 'get-schema': {
137
+ const { run } = require('../lib/get-schema');
138
+ await run(args);
139
+ break;
520
140
  }
521
- } catch {
522
- console.log("❌ 未找到 python3,请安装 Python 3.10+");
523
- issues.push({ type: "error", msg: "未找到 python3,请安装:https://www.python.org/" });
524
- }
525
-
526
- // 3. Playwright 安装
527
- try {
528
- execSync("python3 -c \"import playwright\"", { encoding: "utf-8", stdio: "pipe" });
529
- console.log("✅ Playwright 已安装");
530
- } catch {
531
- console.log("❌ Playwright 未安装");
532
- issues.push({ type: "fix", msg: "Playwright 未安装", fix: "pip install playwright" });
533
- }
534
141
 
535
- // 4. Playwright Chromium
536
- try {
537
- execSync(
538
- "python3 -c \"from playwright.sync_api import sync_playwright; p = sync_playwright().start(); p.stop()\"",
539
- { encoding: "utf-8", stdio: "pipe", timeout: 10_000 }
540
- );
541
- console.log("✅ Playwright Chromium 已安装");
542
- } catch {
543
- console.log("⚠️ Playwright Chromium 可能未安装");
544
- issues.push({ type: "fix", msg: "Playwright Chromium 未安装", fix: "playwright install chromium" });
545
- }
546
-
547
- // 5. gh CLI 安装
548
- try {
549
- const ghVersion = execSync("gh --version 2>&1", { encoding: "utf-8" }).split("\n")[0].trim();
550
- console.log(`✅ ${ghVersion}`);
551
- } catch {
552
- console.log("❌ gh CLI 未安装");
553
- issues.push({ type: "error", msg: "gh CLI 未安装,请安装:https://cli.github.com/" });
554
- }
555
-
556
- // 6. gh CLI 登录态
557
- try {
558
- execSync("gh auth status 2>&1", { encoding: "utf-8", stdio: "pipe" });
559
- console.log("✅ gh CLI 已登录");
560
- } catch {
561
- console.log("⚠️ gh CLI 未登录");
562
- issues.push({ type: "fix", msg: "gh CLI 未登录", fix: "gh auth login" });
563
- }
564
-
565
- // 7. config.json
566
- const configErrors = validateConfig(configPath);
567
- if (configErrors.length === 0) {
568
- console.log("✅ config.json 存在且格式正确");
569
- } else {
570
- const isNotExist = configErrors[0].includes("不存在");
571
- console.log(`${isNotExist ? "⚠️ " : "❌"} config.json:${configErrors.join(";")}`);
572
- issues.push({
573
- type: isNotExist ? "fix" : "error",
574
- msg: `config.json 问题:${configErrors.join(";")}`,
575
- fix: isNotExist ? "create-config" : null,
576
- });
577
- }
578
-
579
- // 8. Skills 安装
580
- if (fs.existsSync(skillsPath)) {
581
- const skills = fs.readdirSync(skillsPath).filter((name) =>
582
- fs.statSync(path.join(skillsPath, name)).isDirectory()
583
- );
584
- console.log(`✅ Skills 已安装(${skills.length} 个)`);
585
- } else {
586
- console.log("⚠️ Skills 未安装");
587
- issues.push({ type: "warn", msg: "Skills 未安装,运行 bash install-skills.sh 安装" });
588
- }
589
-
590
- // 9. 宜搭登录态
591
- if (fs.existsSync(cookiePath)) {
592
- try {
593
- const cookieData = JSON.parse(fs.readFileSync(cookiePath, "utf-8"));
594
- const cookies = Array.isArray(cookieData) ? cookieData : cookieData.cookies || [];
595
- const hasToken = cookies.some((c) => c.name === "tianshu_csrf_token");
596
- console.log(`${hasToken ? "✅" : "⚠️ "} 宜搭登录态:${hasToken ? "已登录" : "Cookie 存在但可能已过期"}`);
597
- } catch {
598
- console.log("⚠️ 宜搭登录态:Cookie 文件损坏");
142
+ case 'publish': {
143
+ // 参数顺序:<源文件路径> <appType> <formUuid>
144
+ // publish.js 内部读取顺序:argv[2]=appType, argv[3]=formUuid, argv[4]=sourceFile
145
+ if (args.length < 3) {
146
+ console.error('用法: openyida publish <源文件路径> <appType> <formUuid>');
147
+ console.error('示例: openyida publish pages/src/home.jsx APP_XXX FORM-XXX');
148
+ process.exit(1);
149
+ }
150
+ const [sourceFile, appType, formUuid] = args;
151
+ process.argv = [process.argv[0], process.argv[1], appType, formUuid, sourceFile];
152
+ require('../lib/publish');
153
+ break;
599
154
  }
600
- } else {
601
- console.log("⚠️ 宜搭登录态:未登录(运行 yida login 登录)");
602
- }
603
155
 
604
- // 汇总
605
- console.log("");
606
- const errorCount = issues.filter((i) => i.type === "error").length;
607
- const fixableCount = issues.filter((i) => i.type === "fix").length;
608
- const warnCount = issues.filter((i) => i.type === "warn").length;
609
-
610
- if (issues.length === 0) {
611
- console.log("🎉 所有检查通过,环境配置完整!");
612
- return;
613
- }
614
-
615
- console.log(`发现 ${issues.length} 个问题(${errorCount} 个错误,${fixableCount} 个可自动修复,${warnCount} 个警告)`);
616
-
617
- if (repair) {
618
- console.log("\n🔧 正在自动修复...\n");
619
- let fixedCount = 0;
620
- for (const issue of issues) {
621
- if (issue.type !== "fix") continue;
622
- if (issue.fix === "create-config") {
623
- const template = {
624
- loginUrl: "https://www.aliwork.com/workPlatform",
625
- defaultBaseUrl: "https://www.aliwork.com",
626
- };
627
- fs.writeFileSync(configPath, JSON.stringify(template, null, 2), "utf-8");
628
- console.log("✅ 已创建 config.json 模板,请根据实际情况修改 loginUrl");
629
- fixedCount++;
630
- } else if (issue.fix) {
631
- console.log(` 💡 请手动运行:${issue.fix}`);
156
+ case 'verify-short-url': {
157
+ if (args.length < 3) {
158
+ console.error('用法: openyida verify-short-url <appType> <formUuid> <url>');
159
+ console.error('示例: openyida verify-short-url APP_XXX FORM-XXX /o/myapp');
160
+ process.exit(1);
632
161
  }
162
+ process.argv = [process.argv[0], process.argv[1], ...args];
163
+ require('../lib/verify-short-url');
164
+ break;
633
165
  }
634
- if (fixedCount > 0) {
635
- console.log(`\n✅ 自动修复了 ${fixedCount} 个问题`);
636
- }
637
- const remaining = issues.filter(
638
- (i) => i.type === "error" || (i.type === "fix" && i.fix !== "create-config")
639
- );
640
- if (remaining.length > 0) {
641
- console.log("\n需要手动处理的问题:");
642
- remaining.forEach((i) => console.log(` - ${i.msg}${i.fix ? `(运行:${i.fix})` : ""}`));
643
- }
644
- } else {
645
- console.log(`\n运行 yida doctor --repair 自动修复可修复的问题`);
646
- }
647
- }
648
-
649
- /**
650
- * 输出 shell 自动补全脚本
651
- * @param {string} shellType - bash | zsh | fish
652
- */
653
- function printCompletionScript(shellType) {
654
- const allCommands = [
655
- "login", "logout", "create-app", "create-page", "create-form",
656
- "publish", "get-schema", "config", "doctor", "completion", "shell",
657
- ];
658
- const commandsStr = allCommands.join(" ");
659
-
660
- switch (shellType.toLowerCase()) {
661
- case "bash":
662
- console.log(`# OpenYida CLI bash completion
663
- # 安装:yida completion bash >> ~/.bashrc && source ~/.bashrc
664
-
665
- _yida_completion() {
666
- local cur prev words cword
667
- _init_completion || return
668
-
669
- local commands="${commandsStr}"
670
-
671
- case "$prev" in
672
- yida)
673
- COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
674
- return ;;
675
- config)
676
- COMPREPLY=( $(compgen -W "--validate --rollback --show" -- "$cur") )
677
- return ;;
678
- doctor)
679
- COMPREPLY=( $(compgen -W "--repair" -- "$cur") )
680
- return ;;
681
- completion)
682
- COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") )
683
- return ;;
684
- create-app)
685
- COMPREPLY=( $(compgen -W "--description --icon --color" -- "$cur") )
686
- return ;;
687
- esac
688
- }
689
166
 
690
- complete -F _yida_completion yida`);
167
+ case 'save-share-config': {
168
+ if (args.length < 4) {
169
+ console.error('用法: openyida save-share-config <appType> <formUuid> <url> <isOpen> [openAuth]');
170
+ console.error('示例: openyida save-share-config APP_XXX FORM-XXX /o/myapp y n');
171
+ process.exit(1);
172
+ }
173
+ process.argv = [process.argv[0], process.argv[1], ...args];
174
+ require('../lib/save-share-config');
691
175
  break;
176
+ }
692
177
 
693
- case "zsh":
694
- console.log(`# OpenYida CLI zsh completion
695
- # 安装:yida completion zsh >> ~/.zshrc && source ~/.zshrc
696
-
697
- _yida() {
698
- local -a commands
699
- commands=(
700
- 'login:扫码登录宜搭'
701
- 'logout:退出登录'
702
- 'create-app:创建宜搭应用'
703
- 'create-page:创建自定义页面'
704
- 'create-form:创建表单页面'
705
- 'publish:发布页面到宜搭'
706
- 'get-schema:获取表单 Schema'
707
- 'config:查看/校验/回滚配置'
708
- 'doctor:检查环境依赖'
709
- 'completion:输出 shell 补全脚本'
710
- 'shell:进入交互式 REPL 模式'
711
- )
712
-
713
- _arguments -C \\\\
714
- '1: :->command' \\\\
715
- '*: :->args'
716
-
717
- case $state in
718
- command)
719
- _describe 'yida commands' commands ;;
720
- args)
721
- case $words[2] in
722
- config)
723
- _arguments '--validate[校验配置]' '--rollback[回滚配置]' '--show[显示配置]' ;;
724
- doctor)
725
- _arguments '--repair[自动修复]' ;;
726
- completion)
727
- _arguments '1: :(bash zsh fish)' ;;
728
- create-app)
729
- _arguments '--description[应用描述]' '--icon[图标]' '--color[颜色]' ;;
730
- esac ;;
731
- esac
732
- }
733
-
734
- # 仅在交互式 shell 中注册补全(避免非交互式 shell 报错)
735
- if [[ -n \${ZSH_VERSION-} ]] && [[ \$- == *i* ]]; then
736
- compdef _yida yida
737
- fi`);
178
+ case 'get-page-config': {
179
+ if (args.length < 2) {
180
+ console.error('用法: openyida get-page-config <appType> <formUuid>');
181
+ console.error('示例: openyida get-page-config APP_XXX FORM-XXX');
182
+ process.exit(1);
183
+ }
184
+ process.argv = [process.argv[0], process.argv[1], ...args];
185
+ require('../lib/get-page-config');
738
186
  break;
187
+ }
739
188
 
740
- case "fish":
741
- console.log(`# OpenYida CLI fish completion
742
- # 安装:yida completion fish > ~/.config/fish/completions/yida.fish
743
-
744
- complete -c yida -e
745
- complete -c yida -f -n '__fish_use_subcommand' -a 'login' -d '扫码登录宜搭'
746
- complete -c yida -f -n '__fish_use_subcommand' -a 'logout' -d '退出登录'
747
- complete -c yida -f -n '__fish_use_subcommand' -a 'create-app' -d '创建宜搭应用'
748
- complete -c yida -f -n '__fish_use_subcommand' -a 'create-page' -d '创建自定义页面'
749
- complete -c yida -f -n '__fish_use_subcommand' -a 'create-form' -d '创建表单页面'
750
- complete -c yida -f -n '__fish_use_subcommand' -a 'publish' -d '发布页面到宜搭'
751
- complete -c yida -f -n '__fish_use_subcommand' -a 'get-schema' -d '获取表单 Schema'
752
- complete -c yida -f -n '__fish_use_subcommand' -a 'config' -d '查看/校验/回滚配置'
753
- complete -c yida -f -n '__fish_use_subcommand' -a 'doctor' -d '检查环境依赖'
754
- complete -c yida -f -n '__fish_use_subcommand' -a 'completion' -d '输出 shell 补全脚本'
755
- complete -c yida -f -n '__fish_use_subcommand' -a 'shell' -d '进入交互式 REPL 模式'
756
- complete -c yida -f -n '__fish_seen_subcommand_from config' -l validate -d '校验配置格式'
757
- complete -c yida -f -n '__fish_seen_subcommand_from config' -l rollback -d '回滚到备份配置'
758
- complete -c yida -f -n '__fish_seen_subcommand_from config' -l show -d '显示当前配置'
759
- complete -c yida -f -n '__fish_seen_subcommand_from doctor' -l repair -d '自动修复可修复的问题'
760
- complete -c yida -f -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish'
761
- complete -c yida -f -n '__fish_seen_subcommand_from create-app' -l description -d '应用描述'
762
- complete -c yida -f -n '__fish_seen_subcommand_from create-app' -l icon -d '图标标识'
763
- complete -c yida -f -n '__fish_seen_subcommand_from create-app' -l color -d '图标颜色'`);
189
+ case 'update-form-config': {
190
+ if (args.length < 4) {
191
+ console.error('用法: openyida update-form-config <appType> <formUuid> <isRenderNav> <title>');
192
+ console.error('示例: openyida update-form-config APP_XXX FORM-XXX false "页面标题"');
193
+ process.exit(1);
194
+ }
195
+ process.argv = [process.argv[0], process.argv[1], ...args];
196
+ require('../lib/update-form-config');
764
197
  break;
198
+ }
765
199
 
766
- default:
767
- console.error(`❌ 不支持的 shell 类型:${shellType}`);
768
- console.error(" 支持的类型:bash | zsh | fish");
200
+ default: {
201
+ console.error(`未知命令: ${command}`);
202
+ console.error('运行 openyida --help 查看帮助');
769
203
  process.exit(1);
770
- }
771
- }
772
-
773
- /**
774
- * 简单的 shell 参数解析(支持引号包裹的参数)
775
- */
776
- function parseShellArgs(input) {
777
- const args = [];
778
- let current = "";
779
- let inQuote = false;
780
- let quoteChar = "";
781
-
782
- for (const char of input) {
783
- if (inQuote) {
784
- if (char === quoteChar) {
785
- inQuote = false;
786
- } else {
787
- current += char;
788
- }
789
- } else if (char === '"' || char === "'") {
790
- inQuote = true;
791
- quoteChar = char;
792
- } else if (char === " ") {
793
- if (current) {
794
- args.push(current);
795
- current = "";
796
- }
797
- } else {
798
- current += char;
799
204
  }
800
205
  }
801
-
802
- if (current) {
803
- args.push(current);
804
- }
805
-
806
- return args;
807
206
  }
808
207
 
809
- // ── 解析并执行 ────────────────────────────────────────────────────────
208
+ main()
209
+ .then(() => updateCheckPromise)
210
+ .catch((err) => {
211
+ console.error(`\n❌ 执行失败: ${err.message}`);
212
+ process.exit(1);
213
+ });
810
214
 
811
- program.parse(process.argv);