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
@@ -0,0 +1,279 @@
1
+ /**
2
+ * verify-short-url.js - 宜搭公开访问/分享 URL 验证命令
3
+ *
4
+ * 用法:yidacli verify-short-url <appType> <formUuid> <url>
5
+ *
6
+ * 参数:
7
+ * appType - 应用 ID(必填),如 APP_XXX
8
+ * formUuid - 表单 UUID(必填),如 FORM-XXX
9
+ * url - 公开访问或分享路径(必填),如 /o/xxx 或 /s/xxx
10
+ *
11
+ * URL 格式要求:
12
+ * - /o/xxx:公开访问(对外)
13
+ * - /s/xxx:组织内分享(对内)
14
+ * - 路径部分只支持英文、数字、- 和 _
15
+ *
16
+ * 示例:
17
+ * yidacli verify-short-url APP_XXX FORM-XXX /o/myapp
18
+ */
19
+
20
+ "use strict";
21
+
22
+ const https = require("https");
23
+ const http = require("http");
24
+ const fs = require("fs");
25
+ const path = require("path");
26
+ const querystring = require("querystring");
27
+
28
+ const { findProjectRoot, loadCookieData, triggerLogin, refreshCsrfToken, resolveBaseUrl, isLoginExpired, isCsrfTokenExpired } = require('./utils');
29
+
30
+ // ── 配置读取 ──────────────────────────────────────────
31
+ const PROJECT_ROOT = findProjectRoot();
32
+ const CONFIG_PATH = path.resolve(PROJECT_ROOT, "config.json");
33
+ const CONFIG = fs.existsSync(CONFIG_PATH) ? JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")) : {};
34
+ const DEFAULT_BASE_URL = CONFIG.defaultBaseUrl || "https://www.aliwork.com";
35
+
36
+ // ── 参数解析 ─────────────────────────────────────────
37
+
38
+ function parseArgs() {
39
+ const args = process.argv.slice(2);
40
+ if (args.length < 3) {
41
+ console.error("用法: node verify-short-url.js <appType> <formUuid> <url>");
42
+ console.error("示例: node .claude/skills/yida-verify-short-url/scripts/verify-short-url.js \"APP_XXX\" \"FORM-XXX\" \"/o/aaa\"");
43
+ console.error(" 支持两种格式:");
44
+ console.error(" /o/xxx - 公开访问(对外)");
45
+ console.error(" /s/xxx - 组织内分享(对内)");
46
+ process.exit(1);
47
+ }
48
+ const url = args[2];
49
+ const urlType = url.startsWith("/o/") ? "open" : url.startsWith("/s/") ? "share" : null;
50
+ return {
51
+ appType: args[0],
52
+ formUuid: args[1],
53
+ url: url,
54
+ urlType: urlType,
55
+ };
56
+ }
57
+
58
+ /**
59
+ * 验证 URL 格式
60
+ * - /o/xxx - 公开访问(对外)
61
+ * - /s/xxx - 组织内分享(对内)
62
+ */
63
+ function validateUrl(url, urlType) {
64
+ if (!urlType) {
65
+ throw new Error(`URL 必须以 /o/ 或 /s/ 开头,当前值: ${url}`);
66
+ }
67
+ const pathPart = url.slice(3);
68
+ if (!/^[a-zA-Z0-9_-]+$/.test(pathPart)) {
69
+ throw new Error(`URL 路径部分只支持 a-z A-Z 0-9 _ -,当前值: ${url}`);
70
+ }
71
+ if (pathPart.length === 0) {
72
+ throw new Error(`URL 路径部分不能为空: ${url}`);
73
+ }
74
+ return true;
75
+ }
76
+
77
+ // ── 登录态管理 ───────────────────────────────────────
78
+
79
+
80
+ // ── 发送 GET 请求(支持 302 自动重登录) ─────────────
81
+
82
+ function sendGetRequest(baseUrl, cookies, requestPath, queryParams) {
83
+ return new Promise((resolve, reject) => {
84
+ const queryString = querystring.stringify(queryParams);
85
+ const fullPath = `${requestPath}?${queryString}`;
86
+
87
+ const cookieHeader = cookies
88
+ .map((cookie) => `${cookie.name}=${cookie.value}`)
89
+ .join("; ");
90
+
91
+ const parsedUrl = new URL(baseUrl);
92
+ const isHttps = parsedUrl.protocol === "https:";
93
+ const requestModule = isHttps ? https : http;
94
+
95
+ const requestOptions = {
96
+ hostname: parsedUrl.hostname,
97
+ port: parsedUrl.port || (isHttps ? 443 : 80),
98
+ path: fullPath,
99
+ method: "GET",
100
+ headers: {
101
+ Origin: baseUrl,
102
+ Referer: baseUrl + "/",
103
+ Cookie: cookieHeader,
104
+ Accept: "application/json, text/json",
105
+ "x-requested-with": "XMLHttpRequest",
106
+ },
107
+ timeout: 30000,
108
+ };
109
+
110
+ const request = requestModule.request(requestOptions, (response) => {
111
+ let responseData = "";
112
+ response.on("data", (chunk) => { responseData += chunk; });
113
+ response.on("end", () => {
114
+ console.error(` HTTP 状态码: ${response.statusCode}`);
115
+ let parsed;
116
+ try {
117
+ parsed = JSON.parse(responseData);
118
+ } catch (parseError) {
119
+ console.error(` 响应内容: ${responseData.substring(0, 500)}`);
120
+ resolve({ success: false, errorMsg: `HTTP ${response.statusCode}: 响应非 JSON` });
121
+ return;
122
+ }
123
+ // 检测登录过期(errorCode: "307")
124
+ if (isLoginExpired(parsed)) {
125
+ console.error(` 检测到登录过期: ${parsed.errorMsg}`);
126
+ resolve({ __needLogin: true });
127
+ return;
128
+ }
129
+ // 检测 csrf_token 过期(errorCode: "TIANSHU_000030")
130
+ if (isCsrfTokenExpired(parsed)) {
131
+ console.error(` 检测到 csrf_token 过期: ${parsed.errorMsg}`);
132
+ resolve({ __csrfExpired: true });
133
+ return;
134
+ }
135
+ resolve(parsed);
136
+ });
137
+ });
138
+
139
+ request.on("timeout", () => {
140
+ console.error(" ❌ 请求超时");
141
+ request.destroy();
142
+ reject(new Error("请求超时"));
143
+ });
144
+
145
+ request.on("error", (requestError) => {
146
+ reject(requestError);
147
+ });
148
+
149
+ request.end();
150
+ });
151
+ }
152
+
153
+ // ── 主流程 ────────────────────────────────────────────
154
+
155
+ async function main() {
156
+ const { appType, formUuid, url, urlType } = parseArgs();
157
+ const urlLabel = urlType === "open" ? "公开访问路径" : "组织内分享路径";
158
+
159
+ console.error("=".repeat(50));
160
+ console.error(" verify-short-url - 宜搭 URL 验证工具");
161
+ console.error("=".repeat(50));
162
+ console.error(`\n 应用 ID: ${appType}`);
163
+ console.error(` 表单 UUID: ${formUuid}`);
164
+ console.error(` ${urlLabel}: ${url}`);
165
+
166
+ // Step 0: 验证 URL 格式
167
+ console.error("\n📋 Step 0: 验证 URL 格式");
168
+ try {
169
+ validateUrl(url, urlType);
170
+ console.error(" ✅ 格式验证通过");
171
+ } catch (err) {
172
+ console.error(` ❌ 格式验证失败: ${err.message}`);
173
+ process.exit(1);
174
+ }
175
+
176
+ // Step 1: 读取本地登录态
177
+ console.error("\n🔑 Step 1: 读取登录态");
178
+ let cookieData = loadCookieData();
179
+ if (!cookieData) {
180
+ console.error(" ⚠️ 未找到本地登录态,触发登录...");
181
+ cookieData = triggerLogin();
182
+ }
183
+ let { cookies } = cookieData;
184
+ let baseUrl = resolveBaseUrl(cookieData);
185
+ console.error(` ✅ 登录态已就绪(${baseUrl})`);
186
+
187
+ // Step 2: 验证 URL
188
+ console.error("\n🔍 Step 2: 验证 URL");
189
+ console.error(" 发送 verifyShortUrl 请求...");
190
+ let { csrf_token: csrfToken } = cookieData;
191
+
192
+ // 构建请求参数(根据 URL 类型选择参数名)
193
+ const requestParams = {
194
+ _api: "App.verifyShortUrlForm",
195
+ formUuid: formUuid,
196
+ _csrf_token: csrfToken,
197
+ _locale_time_zone_offset: "28800000",
198
+ _stamp: Date.now().toString(),
199
+ };
200
+
201
+ if (urlType === "open") {
202
+ requestParams.openUrl = url;
203
+ } else {
204
+ requestParams.shareUrl = url;
205
+ }
206
+
207
+ let result = await sendGetRequest(
208
+ baseUrl,
209
+ cookies,
210
+ `/dingtalk/web/${appType}/query/formdesign/verifyShortUrl.json`,
211
+ requestParams
212
+ );
213
+
214
+ if (result && result.__csrfExpired) {
215
+ cookieData = refreshCsrfToken();
216
+ csrfToken = cookieData.csrf_token;
217
+ cookies = cookieData.cookies;
218
+ baseUrl = resolveBaseUrl(cookieData);
219
+ requestParams._csrf_token = csrfToken;
220
+ requestParams._stamp = Date.now().toString();
221
+ console.error(" 🔄 重新发送 verifyShortUrl 请求(csrf_token 已刷新)...");
222
+ result = await sendGetRequest(
223
+ baseUrl,
224
+ cookies,
225
+ `/dingtalk/web/${appType}/query/formdesign/verifyShortUrl.json`,
226
+ requestParams
227
+ );
228
+ }
229
+
230
+ if (result && result.__needLogin) {
231
+ cookieData = triggerLogin();
232
+ csrfToken = cookieData.csrf_token;
233
+ cookies = cookieData.cookies;
234
+ baseUrl = resolveBaseUrl(cookieData);
235
+ requestParams._csrf_token = csrfToken;
236
+ requestParams._stamp = Date.now().toString();
237
+ console.error(" 🔄 重新发送 verifyShortUrl 请求...");
238
+ result = await sendGetRequest(
239
+ baseUrl,
240
+ cookies,
241
+ `/dingtalk/web/${appType}/query/formdesign/verifyShortUrl.json`,
242
+ requestParams
243
+ );
244
+ }
245
+
246
+ // 输出结果
247
+ console.error("\n" + "=".repeat(50));
248
+ if (result && !result.__needLogin && !result.__csrfExpired) {
249
+ if (result.success && result.content) {
250
+ console.error(" ✅ URL 可用!");
251
+ console.error("=".repeat(50));
252
+ console.log(JSON.stringify({
253
+ available: true,
254
+ url: url,
255
+ urlType: urlType,
256
+ message: urlType === "open" ? "该公开访问路径可用" : "该组织内分享路径可用"
257
+ }, null, 2));
258
+ } else {
259
+ console.error(" ❌ URL 被占用");
260
+ console.error("=".repeat(50));
261
+ console.log(JSON.stringify({
262
+ available: false,
263
+ url: url,
264
+ urlType: urlType,
265
+ message: result.errorMsg || "该短链接已被占用",
266
+ errorCode: result.errorCode
267
+ }, null, 2));
268
+ }
269
+ } else {
270
+ console.error(" ❌ 验证请求失败");
271
+ console.error("=".repeat(50));
272
+ process.exit(1);
273
+ }
274
+ }
275
+
276
+ main().catch((error) => {
277
+ console.error(`\n❌ 验证异常: ${error.message}`);
278
+ process.exit(1);
279
+ });
package/package.json CHANGED
@@ -1,20 +1,33 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "0.1.2",
4
- "description": "OpenYida CLI - 宜搭命令行工具",
3
+ "version": "1.0.0-beta.0",
4
+ "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
- "yida": "./bin/yida.js",
7
- "openyida": "./bin/yida.js"
6
+ "openyida": "./bin/yida.js",
7
+ "yida": "./bin/yida.js"
8
8
  },
9
+ "files": [
10
+ "bin/",
11
+ "lib/",
12
+ "project/",
13
+ "yida-skills/",
14
+ "scripts/",
15
+ "config.json",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
9
19
  "scripts": {
10
20
  "test": "jest",
11
- "test:coverage": "jest --coverage"
21
+ "test:coverage": "jest --coverage",
22
+ "postinstall": "node scripts/postinstall.js"
12
23
  },
13
- "keywords": ["yida", "aliwork", "dingtalk", "cli", "openyida"],
24
+ "keywords": ["yida", "aliwork", "dingtalk", "cli", "openyida", "low-code", "ai"],
14
25
  "author": "OpenYida Contributors",
15
26
  "license": "MIT",
16
27
  "dependencies": {
17
- "commander": "^12.0.0"
28
+ "@babel/standalone": "^7.15.1",
29
+ "playwright": "^1.40.0",
30
+ "uglify-js": "^3.19.3"
18
31
  },
19
32
  "devDependencies": {
20
33
  "jest": "^29.0.0"