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.
- package/README.md +68 -38
- package/bin/yida.js +164 -761
- package/lib/babel-transform/index.js +244 -0
- package/lib/babel-transform/jsx-utils.js +89 -0
- package/lib/check-update.js +72 -0
- package/lib/copy.js +258 -0
- package/lib/create-app.js +174 -0
- package/lib/create-form.js +2244 -0
- package/lib/create-page.js +89 -0
- package/lib/env.js +164 -0
- package/lib/get-page-config.js +102 -0
- package/lib/get-schema.js +76 -0
- package/lib/login.js +323 -0
- package/lib/publish.js +610 -0
- package/lib/save-share-config.js +268 -0
- package/lib/update-form-config.js +237 -0
- package/lib/utils.js +443 -0
- package/lib/verify-short-url.js +279 -0
- package/package.json +20 -7
- package/project/.cache/demo-schema.json +2353 -0
- package/project/pages/src/demo-birthday-game.js +833 -0
- package/project/pages/src/demo-future-vision-2026.js +1102 -0
- package/project/pages/src/demo-salary-calculator.js +904 -0
- package/project/prd/demo-birthday-game.md +39 -0
- package/project/prd/demo-future-vision-2026.md +78 -0
- package/project/prd/demo-salary-calculator.md +101 -0
- package/scripts/postinstall.js +114 -0
- package/yida-skills/SKILL.md +273 -0
- package/yida-skills/reference/association-form-field.md +469 -0
- package/yida-skills/reference/employee-field.md +17 -0
- package/yida-skills/reference/model-api.md +73 -0
- package/yida-skills/reference/serial-number-field.md +132 -0
- package/yida-skills/reference/yida-api.md +1208 -0
- package/yida-skills/skills/yida-app/SKILL.md +394 -0
- package/yida-skills/skills/yida-create-app/SKILL.md +158 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +598 -0
- package/yida-skills/skills/yida-create-page/SKILL.md +103 -0
- package/yida-skills/skills/yida-custom-page/SKILL.md +533 -0
- package/yida-skills/skills/yida-get-schema/SKILL.md +90 -0
- package/yida-skills/skills/yida-login/SKILL.md +200 -0
- package/yida-skills/skills/yida-logout/SKILL.md +58 -0
- package/yida-skills/skills/yida-page-config/SKILL.md +261 -0
- package/yida-skills/skills/yida-publish-page/SKILL.md +113 -0
- package/.eslintrc.json +0 -25
- package/.github/workflows/ci.yml +0 -123
- package/.github/workflows/publish.yml +0 -105
- package/.github/workflows/update-contributors.yml +0 -151
- package/.openclaw/skills/yida-issue/SKILL.md +0 -27
- package/.openclaw/skills/yida-issue/scripts/create-issue.js +0 -317
- package/CLAUDE.md +0 -168
- package/CONTRIBUTING.md +0 -59
- package/install-skills.ps1 +0 -162
- package/install-skills.sh +0 -175
- package/pages/dist/.gitkeep +0 -0
- package/pages/src/.gitkeep +0 -0
- package/prd/salary-calculator.md +0 -15
- package/tests/cli.test.js +0 -930
- package/tests/install.test.js +0 -277
- package/tests/yida-issue.test.js +0 -314
- /package/{config.json → project/config.json} +0 -0
- /package/{.cache → project/pages/dist}/.gitkeep +0 -0
package/lib/utils.js
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils.js - 宜搭 CLI 公共工具函数
|
|
3
|
+
*
|
|
4
|
+
* 导出函数:
|
|
5
|
+
* findProjectRoot() - 查找项目根目录(兼容悟空环境)
|
|
6
|
+
* extractInfoFromCookies() - 从 Cookie 列表中提取 csrf_token / corp_id / user_id
|
|
7
|
+
* loadCookieData() - 读取 .cache/cookies.json 登录态缓存
|
|
8
|
+
* triggerLogin() - 触发登录(自动区分标准/悟空环境)
|
|
9
|
+
* refreshCsrfToken() - 刷新 csrf_token
|
|
10
|
+
* resolveBaseUrl() - 从 cookieData 中解析 base_url
|
|
11
|
+
* isLoginExpired() - 检测响应体是否表示登录过期
|
|
12
|
+
* isCsrfTokenExpired() - 检测响应体是否表示 csrf_token 过期
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
const os = require("os");
|
|
20
|
+
const { execSync } = require("child_process");
|
|
21
|
+
|
|
22
|
+
// ── 项目根目录查找 ────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 检测当前活跃的 AI 工具。
|
|
26
|
+
* 优先级:环境变量 > 兜底检测
|
|
27
|
+
*
|
|
28
|
+
* 注意:只返回当前"活跃"的工具,不返回已安装但未使用的工具。
|
|
29
|
+
*
|
|
30
|
+
* @returns {{ tool: string, displayName: string, dirName: string, workspaceRoot: string }|null}
|
|
31
|
+
*/
|
|
32
|
+
function detectActiveTool() {
|
|
33
|
+
const env = process.env;
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
const home = os.homedir();
|
|
36
|
+
|
|
37
|
+
// 优先级1:通过环境变量检测
|
|
38
|
+
|
|
39
|
+
// Qoder
|
|
40
|
+
if (env.QODER_IDE || env.QODER_AGENT) {
|
|
41
|
+
return {
|
|
42
|
+
tool: "qoder",
|
|
43
|
+
displayName: "Qoder",
|
|
44
|
+
dirName: ".qoder",
|
|
45
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Claude Code
|
|
50
|
+
if (env.CLAUDE_CODE) {
|
|
51
|
+
return {
|
|
52
|
+
tool: "claude-code",
|
|
53
|
+
displayName: "Claude Code",
|
|
54
|
+
dirName: ".claudecode",
|
|
55
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// OpenCode
|
|
60
|
+
if (env.OPENCODE) {
|
|
61
|
+
return {
|
|
62
|
+
tool: "opencode",
|
|
63
|
+
displayName: "OpenCode",
|
|
64
|
+
dirName: ".opencode",
|
|
65
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Cursor
|
|
70
|
+
if (env.CURSOR_TRACE_ID || (env.VSCODE_GIT_ASKPASS_NODE || "").includes("Cursor")) {
|
|
71
|
+
return {
|
|
72
|
+
tool: "cursor",
|
|
73
|
+
displayName: "Cursor",
|
|
74
|
+
dirName: ".cursor",
|
|
75
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// iFlow
|
|
80
|
+
if (env.IFLOW_ACTIVE || env.IFLOW_IDE) {
|
|
81
|
+
return {
|
|
82
|
+
tool: "iflow",
|
|
83
|
+
displayName: "iFlow",
|
|
84
|
+
dirName: ".iflow",
|
|
85
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 悟空(Wukong)
|
|
90
|
+
if (env.AGENT_WORK_ROOT && env.AGENT_WORK_ROOT.includes(".real")) {
|
|
91
|
+
return {
|
|
92
|
+
tool: "wukong",
|
|
93
|
+
displayName: "悟空(Wukong)",
|
|
94
|
+
dirName: ".real",
|
|
95
|
+
workspaceRoot: path.join(home, ".real", "workspace", "project"),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 优先级2:兜底检测
|
|
100
|
+
|
|
101
|
+
// Aone Copilot - 通过专属配置目录检测(VSCode 环境)
|
|
102
|
+
// Aone Copilot 没有独立的环境变量,但会在 home 目录创建 ~/.aone_copilot/
|
|
103
|
+
if (env.TERM_PROGRAM === "vscode" && fs.existsSync(path.join(home, ".aone_copilot"))) {
|
|
104
|
+
return {
|
|
105
|
+
tool: "aone-copilot",
|
|
106
|
+
displayName: "Aone Copilot",
|
|
107
|
+
dirName: ".aone_copilot",
|
|
108
|
+
workspaceRoot: path.join(cwd, "project"),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 未检测到活跃工具
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 查找项目根目录(project 工作区)。
|
|
118
|
+
*
|
|
119
|
+
* 查找策略:
|
|
120
|
+
* 1. 通过环境变量检测当前活跃的 AI 工具
|
|
121
|
+
* 2. 返回对应工具的项目根目录
|
|
122
|
+
* 3. 兜底:返回 process.cwd()
|
|
123
|
+
*
|
|
124
|
+
* @returns {string} 项目根目录的绝对路径
|
|
125
|
+
*/
|
|
126
|
+
function findProjectRoot() {
|
|
127
|
+
const activeTool = detectActiveTool();
|
|
128
|
+
|
|
129
|
+
if (activeTool) {
|
|
130
|
+
// 如果 project 目录存在,返回它;否则返回当前工作目录
|
|
131
|
+
if (fs.existsSync(activeTool.workspaceRoot)) {
|
|
132
|
+
return activeTool.workspaceRoot;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 兜底:返回当前工作目录
|
|
137
|
+
return process.cwd();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Cookie 解析 ───────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 从 Cookie 列表中提取 csrf_token、corp_id、user_id。
|
|
144
|
+
* @param {Array} cookies
|
|
145
|
+
* @returns {{ csrfToken: string|null, corpId: string|null, userId: string|null }}
|
|
146
|
+
*/
|
|
147
|
+
function extractInfoFromCookies(cookies) {
|
|
148
|
+
let csrfToken = null;
|
|
149
|
+
let corpId = null;
|
|
150
|
+
let userId = null;
|
|
151
|
+
|
|
152
|
+
for (const cookie of cookies) {
|
|
153
|
+
if (cookie.name === "tianshu_csrf_token") {
|
|
154
|
+
csrfToken = cookie.value;
|
|
155
|
+
} else if (cookie.name === "tianshu_corp_user") {
|
|
156
|
+
const lastUnderscore = cookie.value.lastIndexOf("_");
|
|
157
|
+
if (lastUnderscore > 0) {
|
|
158
|
+
corpId = cookie.value.slice(0, lastUnderscore);
|
|
159
|
+
userId = cookie.value.slice(lastUnderscore + 1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return { csrfToken, corpId, userId };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── 登录态缓存读取 ────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 读取 .cache/cookies.json 登录态缓存。
|
|
171
|
+
* @param {string} [projectRoot]
|
|
172
|
+
* @param {string} [defaultBaseUrl]
|
|
173
|
+
* @returns {object|null}
|
|
174
|
+
*/
|
|
175
|
+
function loadCookieData(projectRoot, defaultBaseUrl) {
|
|
176
|
+
const root = projectRoot || findProjectRoot();
|
|
177
|
+
const fallbackBaseUrl = defaultBaseUrl || "https://www.aliwork.com";
|
|
178
|
+
const cookieFile = path.join(root, ".cache", "cookies.json");
|
|
179
|
+
|
|
180
|
+
if (!fs.existsSync(cookieFile)) return null;
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const raw = fs.readFileSync(cookieFile, "utf-8").trim();
|
|
184
|
+
if (!raw) return null;
|
|
185
|
+
|
|
186
|
+
const parsed = JSON.parse(raw);
|
|
187
|
+
let cookieData;
|
|
188
|
+
|
|
189
|
+
if (Array.isArray(parsed)) {
|
|
190
|
+
cookieData = { cookies: parsed, base_url: fallbackBaseUrl };
|
|
191
|
+
} else {
|
|
192
|
+
cookieData = parsed;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (cookieData.cookies && cookieData.cookies.length > 0) {
|
|
196
|
+
const { csrfToken, corpId, userId } = extractInfoFromCookies(cookieData.cookies);
|
|
197
|
+
if (csrfToken) cookieData.csrf_token = csrfToken;
|
|
198
|
+
if (corpId) cookieData.corp_id = corpId;
|
|
199
|
+
if (userId) cookieData.user_id = userId;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return cookieData;
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── 登录触发 ──────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 触发登录(Playwright 扫码模式)。
|
|
212
|
+
* @returns {object} loginResult
|
|
213
|
+
*/
|
|
214
|
+
function triggerLogin() {
|
|
215
|
+
console.error("\n🔐 登录态失效,正在打开浏览器扫码登录...\n");
|
|
216
|
+
const { ensureLogin } = require("./login");
|
|
217
|
+
return ensureLogin();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 刷新 csrf_token(从本地缓存重新提取,无需重新扫码)。
|
|
222
|
+
* @returns {object} loginResult
|
|
223
|
+
*/
|
|
224
|
+
function refreshCsrfToken() {
|
|
225
|
+
console.error("\n🔄 csrf_token 已过期,正在从 Cookie 重新提取...\n");
|
|
226
|
+
const { refreshCsrfFromCache } = require("./login");
|
|
227
|
+
return refreshCsrfFromCache();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── 响应检测 ──────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 检测响应体是否表示登录过期。
|
|
234
|
+
* @param {object} responseJson
|
|
235
|
+
* @returns {boolean}
|
|
236
|
+
*/
|
|
237
|
+
function isLoginExpired(responseJson) {
|
|
238
|
+
return (
|
|
239
|
+
responseJson &&
|
|
240
|
+
responseJson.success === false &&
|
|
241
|
+
(responseJson.errorCode === "307" || responseJson.errorCode === "302")
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 检测响应体是否表示 csrf_token 过期。
|
|
247
|
+
* @param {object} responseJson
|
|
248
|
+
* @returns {boolean}
|
|
249
|
+
*/
|
|
250
|
+
function isCsrfTokenExpired(responseJson) {
|
|
251
|
+
return (
|
|
252
|
+
responseJson &&
|
|
253
|
+
responseJson.success === false &&
|
|
254
|
+
responseJson.errorCode === "TIANSHU_000030"
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ── base_url 解析 ─────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 从 cookieData 中解析 base_url,去除末尾斜杠。
|
|
262
|
+
* @param {object} cookieData
|
|
263
|
+
* @param {string} [defaultBaseUrl]
|
|
264
|
+
* @returns {string}
|
|
265
|
+
*/
|
|
266
|
+
function resolveBaseUrl(cookieData, defaultBaseUrl) {
|
|
267
|
+
const fallback = defaultBaseUrl || "https://www.aliwork.com";
|
|
268
|
+
return ((cookieData && cookieData.base_url) || fallback).replace(/\/+$/, "");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ── HTTP 请求工具 ─────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 发送 HTTP POST 请求(application/x-www-form-urlencoded)
|
|
275
|
+
* @param {string} baseUrl
|
|
276
|
+
* @param {string} requestPath
|
|
277
|
+
* @param {string} postData - querystring 格式
|
|
278
|
+
* @param {Array} cookies
|
|
279
|
+
* @returns {Promise<object>}
|
|
280
|
+
*/
|
|
281
|
+
function httpPost(baseUrl, requestPath, postData, cookies) {
|
|
282
|
+
const https = require("https");
|
|
283
|
+
const http = require("http");
|
|
284
|
+
|
|
285
|
+
return new Promise((resolve, reject) => {
|
|
286
|
+
const cookieHeader = cookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
287
|
+
const parsedUrl = new URL(baseUrl);
|
|
288
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
289
|
+
const requestModule = isHttps ? https : http;
|
|
290
|
+
|
|
291
|
+
const options = {
|
|
292
|
+
hostname: parsedUrl.hostname,
|
|
293
|
+
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
294
|
+
path: requestPath,
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: {
|
|
297
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
298
|
+
"Content-Length": Buffer.byteLength(postData),
|
|
299
|
+
Origin: baseUrl,
|
|
300
|
+
Referer: baseUrl + "/",
|
|
301
|
+
Cookie: cookieHeader,
|
|
302
|
+
},
|
|
303
|
+
timeout: 30000,
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const req = requestModule.request(options, (res) => {
|
|
307
|
+
let data = "";
|
|
308
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
309
|
+
res.on("end", () => {
|
|
310
|
+
console.error(` HTTP 状态码: ${res.statusCode}`);
|
|
311
|
+
try {
|
|
312
|
+
const parsed = JSON.parse(data);
|
|
313
|
+
if (isLoginExpired(parsed)) {
|
|
314
|
+
resolve({ __needLogin: true });
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (isCsrfTokenExpired(parsed)) {
|
|
318
|
+
resolve({ __csrfExpired: true });
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
resolve(parsed);
|
|
322
|
+
} catch {
|
|
323
|
+
console.error(` 响应内容: ${data.substring(0, 500)}`);
|
|
324
|
+
resolve({ success: false, errorMsg: `HTTP ${res.statusCode}: 响应非 JSON` });
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("请求超时")); });
|
|
330
|
+
req.on("error", reject);
|
|
331
|
+
req.write(postData);
|
|
332
|
+
req.end();
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 发送 HTTP GET 请求
|
|
338
|
+
* @param {string} baseUrl
|
|
339
|
+
* @param {string} requestPath
|
|
340
|
+
* @param {object} queryParams
|
|
341
|
+
* @param {Array} cookies
|
|
342
|
+
* @returns {Promise<object>}
|
|
343
|
+
*/
|
|
344
|
+
function httpGet(baseUrl, requestPath, queryParams, cookies) {
|
|
345
|
+
const https = require("https");
|
|
346
|
+
const http = require("http");
|
|
347
|
+
const querystring = require("querystring");
|
|
348
|
+
|
|
349
|
+
return new Promise((resolve, reject) => {
|
|
350
|
+
const cookieHeader = cookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
351
|
+
const parsedUrl = new URL(baseUrl);
|
|
352
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
353
|
+
const requestModule = isHttps ? https : http;
|
|
354
|
+
const fullPath = queryParams ? `${requestPath}?${querystring.stringify(queryParams)}` : requestPath;
|
|
355
|
+
|
|
356
|
+
const options = {
|
|
357
|
+
hostname: parsedUrl.hostname,
|
|
358
|
+
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
359
|
+
path: fullPath,
|
|
360
|
+
method: "GET",
|
|
361
|
+
headers: {
|
|
362
|
+
Origin: baseUrl,
|
|
363
|
+
Referer: baseUrl + "/",
|
|
364
|
+
Cookie: cookieHeader,
|
|
365
|
+
},
|
|
366
|
+
timeout: 30000,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const req = requestModule.request(options, (res) => {
|
|
370
|
+
let data = "";
|
|
371
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
372
|
+
res.on("end", () => {
|
|
373
|
+
console.error(` HTTP 状态码: ${res.statusCode}`);
|
|
374
|
+
try {
|
|
375
|
+
const parsed = JSON.parse(data);
|
|
376
|
+
if (isLoginExpired(parsed)) {
|
|
377
|
+
resolve({ __needLogin: true });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (isCsrfTokenExpired(parsed)) {
|
|
381
|
+
resolve({ __csrfExpired: true });
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
resolve(parsed);
|
|
385
|
+
} catch {
|
|
386
|
+
console.error(` 响应内容: ${data.substring(0, 500)}`);
|
|
387
|
+
resolve({ success: false, errorMsg: `HTTP ${res.statusCode}: 响应非 JSON` });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("请求超时")); });
|
|
393
|
+
req.on("error", reject);
|
|
394
|
+
req.end();
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 带自动重登录的请求封装。
|
|
400
|
+
* @param {Function} requestFn - 接受 authRef 返回 Promise 的工厂函数
|
|
401
|
+
* @param {object} authRef - { csrfToken, cookies, baseUrl, cookieData }
|
|
402
|
+
* @returns {Promise<object>}
|
|
403
|
+
*/
|
|
404
|
+
async function requestWithAutoLogin(requestFn, authRef) {
|
|
405
|
+
let result = await requestFn(authRef);
|
|
406
|
+
|
|
407
|
+
if (result && result.__csrfExpired) {
|
|
408
|
+
const refreshedData = refreshCsrfToken();
|
|
409
|
+
authRef.cookieData = refreshedData;
|
|
410
|
+
authRef.csrfToken = refreshedData.csrf_token;
|
|
411
|
+
authRef.cookies = refreshedData.cookies;
|
|
412
|
+
authRef.baseUrl = resolveBaseUrl(refreshedData);
|
|
413
|
+
console.error(" 🔄 csrf_token 已刷新,重试...");
|
|
414
|
+
result = await requestFn(authRef);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (result && result.__needLogin) {
|
|
418
|
+
const newCookieData = triggerLogin();
|
|
419
|
+
authRef.cookieData = newCookieData;
|
|
420
|
+
authRef.csrfToken = newCookieData.csrf_token;
|
|
421
|
+
authRef.cookies = newCookieData.cookies;
|
|
422
|
+
authRef.baseUrl = resolveBaseUrl(newCookieData);
|
|
423
|
+
console.error(" 🔄 重新登录后重试...");
|
|
424
|
+
result = await requestFn(authRef);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
module.exports = {
|
|
431
|
+
detectActiveTool,
|
|
432
|
+
findProjectRoot,
|
|
433
|
+
extractInfoFromCookies,
|
|
434
|
+
loadCookieData,
|
|
435
|
+
triggerLogin,
|
|
436
|
+
refreshCsrfToken,
|
|
437
|
+
resolveBaseUrl,
|
|
438
|
+
isLoginExpired,
|
|
439
|
+
isCsrfTokenExpired,
|
|
440
|
+
httpPost,
|
|
441
|
+
httpGet,
|
|
442
|
+
requestWithAutoLogin,
|
|
443
|
+
};
|