qdmp-cli 0.1.26 → 0.1.28

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/actions.js CHANGED
@@ -5,11 +5,10 @@ import { download } from "./utils/gitClone.js";
5
5
  import { printLogo } from "./utils/logHandler.js";
6
6
  import chalk from "chalk";
7
7
  import table from "table";
8
- import {
9
- inquirerAccountPassword,
10
- inquirerVersionDescription,
11
- } from "./utils/interactive.js";
12
- import { login, getDeveloperId, getUserInfo } from "./api.js";
8
+ import { inquirerVersionDescription } from "./utils/interactive.js";
9
+ import { ensureInteractiveAuth, interactiveLogin } from "./utils/authFlow.js";
10
+ import { agentLogin } from "./utils/agentLogin.js";
11
+ import { getDeveloperId, getUserInfo } from "./api.js";
13
12
  import fs from "fs";
14
13
  import path from "path";
15
14
  import { createRequire } from "module";
@@ -81,19 +80,31 @@ export const listAction = () => {
81
80
 
82
81
  export const loginAction = async (option) => {
83
82
  try {
84
- const { account, password } = await inquirerAccountPassword();
85
- const result = await login(account, password, option.env);
86
- if (result.success) {
87
- success("登录成功");
88
- } else {
89
- error("登录失败");
83
+ if (option.agent) {
84
+ await agentLogin(option.env);
85
+ return;
86
+ }
87
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
88
+ throw new Error(`Agent 对话请执行 qdmp login --agent --env ${option.env} 获取登录链接和图片;CI 请设置 QDMP_TOKEN`);
90
89
  }
90
+ await interactiveLogin(option.env);
91
+ success("登录成功");
91
92
  } catch (e) {
93
+ if (option.agent) {
94
+ console.log(JSON.stringify({ type: "error", env: option.env, message: e.message }));
95
+ process.exitCode = 1;
96
+ return;
97
+ }
92
98
  error(e.message);
99
+ } finally {
100
+ // Inquirer may leave stdin in a flowing state after the localhost callback
101
+ // resolves. This command has no follow-up prompt, so let Node exit cleanly.
102
+ if (process.stdin.isTTY) process.stdin.pause();
93
103
  }
94
104
  };
95
105
  export const getMyInfoAction = async (option) => {
96
106
  try {
107
+ await ensureInteractiveAuth(option.env);
97
108
  const nickname = await getUserInfo(option.env);
98
109
  const appId = await getAppId(option.env);
99
110
  const appInfo = await getAppInfo(appId, option.env);
@@ -106,6 +117,7 @@ export const getMyInfoAction = async (option) => {
106
117
 
107
118
  export async function uploadAction(option) {
108
119
  try {
120
+ await ensureInteractiveAuth(option.env);
109
121
  const did = await getDeveloperId(option.env);
110
122
  const appId = await getAppId(option.env);
111
123
  if (!appId) {
@@ -155,6 +167,7 @@ export async function uploadAction(option) {
155
167
  }
156
168
  export const initConfigAction = async (option) => {
157
169
  try {
170
+ await ensureInteractiveAuth(option.env);
158
171
  const qdmpJsonPath = `${process.cwd()}/qdmp.json`;
159
172
  if (fs.existsSync(qdmpJsonPath)) {
160
173
  return error(
@@ -163,7 +176,7 @@ export const initConfigAction = async (option) => {
163
176
  }
164
177
  const appId = option.appId;
165
178
  if (!appId) return error("未配置应用ID,请使用-a参数指定应用ID");
166
- const appInfo = await getAppInfo(appId);
179
+ const appInfo = await getAppInfo(appId, option.env);
167
180
  const config = {
168
181
  name: appInfo.name,
169
182
  appId: appInfo.appId,
package/api.js CHANGED
@@ -1,7 +1,9 @@
1
- import { getToken, saveToken } from "./utils/authorized.js";
1
+ import { getToken, saveToken, clearToken } from "./utils/authorized.js";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { promisify } from "util";
5
+ import { startLocalhostLogin } from "./utils/localhostLogin.js";
6
+ import { signQiandaoRequest } from "./utils/pigeon.js";
5
7
 
6
8
  const readFile = promisify(fs.readFile);
7
9
 
@@ -18,6 +20,9 @@ const API_CONFIG = {
18
20
  timeout: 10000,
19
21
  headers: {
20
22
  "content-type": "application/json",
23
+ "x-request-package-id": "1036",
24
+ "x-request-version": "1.0.0",
25
+ "x-echoing-env": "test-z",
21
26
  },
22
27
  };
23
28
 
@@ -35,28 +40,40 @@ export const request = async (
35
40
  env = "prod",
36
41
  needAuth = true
37
42
  ) => {
43
+ const { cookieJar, ...requestOptions } = options;
38
44
  const config = {
39
45
  baseURL: resolveAPIBaseURL(env),
40
46
  method: "GET",
41
47
  headers: { ...API_CONFIG.headers },
42
- ...options,
48
+ credentials: "include",
49
+ ...requestOptions,
43
50
  };
51
+ if (cookieJar?.header()) config.headers.cookie = cookieJar.header();
44
52
  // 添加认证头(如果需要且如果有 token)
45
53
  if (needAuth) {
46
- const token = getToken();
54
+ const token = getToken(env);
47
55
  if (!token) {
48
- throw new Error("未授权,执行 qdmp-cli login 完成登录");
56
+ throw Object.assign(new Error("未授权,执行 qdmp-cli login 完成登录"), {
57
+ code: "AUTH_REQUIRED",
58
+ });
49
59
  }
50
60
  config.headers["authorization"] = `Bearer ${token}`;
51
61
  }
52
62
 
63
+ const requestURL = `${config.baseURL}${url}`;
64
+ const signedHeaders = await signQiandaoRequest(requestURL);
65
+ config.headers = { ...config.headers, ...signedHeaders };
66
+
53
67
  try {
54
- const response = await fetch(`${config.baseURL}${url}`, config);
68
+ const response = await fetch(requestURL, config);
69
+ cookieJar?.update(response.headers);
55
70
 
56
71
  // 检查响应状态
57
72
  if (!response.ok) {
58
73
  if (response.status === 401) {
59
- throw new Error("未授权,执行 qdmp-cli login 完成登录");
74
+ throw Object.assign(new Error("未授权,执行 qdmp-cli login 完成登录"), {
75
+ code: "AUTH_REQUIRED",
76
+ });
60
77
  }
61
78
  if (response.status === 403) {
62
79
  throw new Error("你没有该小程序的开发权限,请联系管理员开通后重试");
@@ -179,7 +196,7 @@ export const login = async (username, password, env = "prod") => {
179
196
  false // 登录时不需要认证头
180
197
  );
181
198
  if (result?.data?.token) {
182
- saveToken(result.data.token); // 保存token
199
+ saveToken(result.data.token, env); // 保存指定环境 token
183
200
  return { success: true };
184
201
  } else {
185
202
  throw new Error(result.error || "登录失败");
@@ -188,6 +205,140 @@ export const login = async (username, password, env = "prod") => {
188
205
  throw new Error(error.message);
189
206
  }
190
207
  };
208
+
209
+ const unwrapData = (result) => result?.data || result;
210
+
211
+ const qrcodeLoginURL = (qrcodeId) =>
212
+ `https://qiandao.com/login/scanning/confirm?qrcodeId=${encodeURIComponent(qrcodeId)}`;
213
+
214
+ export const generateLoginQrcode = async (env = "prod", { signal } = {}) => {
215
+ const result = await request(
216
+ "/signin/login/scan/qrcode/generate-id",
217
+ { method: "POST", body: JSON.stringify({}), signal },
218
+ env,
219
+ false,
220
+ );
221
+ const data = unwrapData(result);
222
+ if (!data?.qrcodeId) throw new Error("生成登录二维码失败");
223
+ return { qrcodeId: String(data.qrcodeId), url: qrcodeLoginURL(data.qrcodeId) };
224
+ };
225
+
226
+ export const getLoginQrcodeStatus = async (qrcodeId, env = "prod", { signal } = {}) => {
227
+ const result = await request(
228
+ "/signin/login/scan/qrcode/status",
229
+ { method: "POST", body: JSON.stringify({ qrcodeId }), signal },
230
+ env,
231
+ false,
232
+ );
233
+ const data = unwrapData(result);
234
+ return {
235
+ ...data,
236
+ status: data?.qrcodeStatus || data?.status || "pending",
237
+ token: data?.accessToken || data?.token,
238
+ };
239
+ };
240
+
241
+ export const loginByPlatformURL = async (env = "prod", onUpdate = () => {}) => {
242
+ const session = await startLocalhostLogin({
243
+ env,
244
+ acceptToken: async (token, isActive) => {
245
+ const result = await request("/mp/v1/user/me", {
246
+ headers: { ...API_CONFIG.headers, authorization: `Bearer ${token}` },
247
+ signal: AbortSignal.timeout(10000),
248
+ }, env, false);
249
+ assertSuccessResponse(result, "验证网页登录态");
250
+ if (!isActive()) throw new Error("网页登录会话已结束");
251
+ saveToken(token, env);
252
+ },
253
+ });
254
+ try {
255
+ await onUpdate({ type: "ready", url: session.url });
256
+ return await session.result;
257
+ } finally {
258
+ session.close();
259
+ }
260
+ };
261
+
262
+ export const getQiandaoUser = async (token, env = "prod", { signal } = {}) => {
263
+ const result = await request(
264
+ "/user/users/me",
265
+ { headers: { authorization: `Bearer ${token}` }, signal },
266
+ env,
267
+ false,
268
+ );
269
+ return unwrapData(result);
270
+ };
271
+
272
+ export const exchangeQiandaoLogin = async (token, user, env = "prod", { signal, persist = true } = {}) => {
273
+ const userInfo = unwrapData(user) || {};
274
+ const thirdPartyUserId = userInfo.id ?? userInfo.userId ?? userInfo.openId;
275
+ if (thirdPartyUserId == null || String(thirdPartyUserId).trim() === "") {
276
+ throw new Error("扫码登录未获取到用户信息");
277
+ }
278
+ const result = await request(
279
+ "/mp/v1/user/login/mobile",
280
+ {
281
+ method: "POST",
282
+ signal,
283
+ body: JSON.stringify({
284
+ thirdPartyUserId: String(thirdPartyUserId),
285
+ nickname: userInfo.nickName || userInfo.nickname || userInfo.name,
286
+ avatar: userInfo.avatar || userInfo.avatarUrl,
287
+ }),
288
+ },
289
+ env,
290
+ false,
291
+ );
292
+ const platformToken = unwrapData(result)?.token;
293
+ if (!platformToken) throw new Error("扫码登录未获取到开放平台 Token");
294
+ if (persist) saveToken(platformToken, env);
295
+ return platformToken;
296
+ };
297
+
298
+ export const loginByQrcode = async (env = "prod", onUpdate = () => {}) => {
299
+ const session = await generateLoginQrcode(env);
300
+ await onUpdate({ type: "ready", ...session });
301
+ const deadline = Date.now() + 2 * 60 * 1000;
302
+ let previousStatus = "";
303
+
304
+ while (Date.now() < deadline) {
305
+ const result = await getLoginQrcodeStatus(session.qrcodeId, env);
306
+ if (result.status !== previousStatus) {
307
+ previousStatus = result.status;
308
+ await onUpdate({ type: "status", status: result.status });
309
+ }
310
+ if (["expired", "cancelled", "canceled", "error"].includes(result.status)) {
311
+ throw new Error(result.status === "expired" ? "二维码已过期" : "扫码登录已结束");
312
+ }
313
+ if (result.status === "success") {
314
+ const qdToken = result.token;
315
+ if (!qdToken) throw new Error("扫码登录未返回登录态");
316
+ const user = await getQiandaoUser(qdToken, env);
317
+ await exchangeQiandaoLogin(qdToken, user, env);
318
+ return { ...session, status: "success" };
319
+ }
320
+ await new Promise((resolve) => setTimeout(resolve, 1500));
321
+ }
322
+ throw new Error("二维码登录超时,请重新登录");
323
+ };
324
+
325
+ export const isAuthRequiredError = (error) => error?.code === "AUTH_REQUIRED";
326
+
327
+ export const ensureAuth = async (env = "prod") => {
328
+ const token = getToken(env);
329
+ if (!token) {
330
+ throw Object.assign(new Error("未授权,执行 qdmp-cli login 完成登录"), {
331
+ code: "AUTH_REQUIRED",
332
+ });
333
+ }
334
+ try {
335
+ await request("/mp/v1/user/me", {}, env);
336
+ return token;
337
+ } catch (error) {
338
+ if (isAuthRequiredError(error)) clearToken(env);
339
+ throw error;
340
+ }
341
+ };
191
342
  /**
192
343
  * 获取用户信息
193
344
  * @returns {Promise} 用户信息
package/index.js CHANGED
@@ -52,7 +52,8 @@ program
52
52
  program
53
53
  .command("login")
54
54
  .description("登录")
55
- .option("-e, --env <env>", "当前环境")
55
+ .option("-e, --env <env>", "登录环境:prod 或 dev", "prod")
56
+ .option("--agent", "输出登录链接和 PNG 图片的 JSON 事件,并等待登录(无需终端输入)")
56
57
  .action(loginAction);
57
58
  program
58
59
  .command("getMe")
package/openapi.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  resolveOpenAPIManifest,
7
7
  } from "./utils/openapiManifest.js";
8
8
  import { error, info, success, warn } from "./utils/logHandler.js";
9
+ import { ensureInteractiveAuth } from "./utils/authFlow.js";
9
10
 
10
11
  const openPlatformBaseURL = (env, override) => {
11
12
  const baseURL = override?.trim().replace(/\/+$/, "")
@@ -60,6 +61,7 @@ export const prepareOpenAPIApplication = async ({
60
61
 
61
62
  export const openAPIApplyURLAction = async (option) => {
62
63
  try {
64
+ await ensureInteractiveAuth(option.env);
63
65
  const result = await prepareOpenAPIApplication(option);
64
66
  if (option.json) {
65
67
  console.log(JSON.stringify(result, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdmp-cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "qdmp-cli",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -29,16 +29,26 @@
29
29
  "dependencies": {
30
30
  "@dimina/compiler": "1.0.17",
31
31
  "adm-zip": "^0.5.16",
32
+ "ajax-hook": "2.1.3",
32
33
  "chalk": "^5.4.1",
33
34
  "commander": "^14.0.0",
34
35
  "figlet": "^1.8.1",
35
36
  "inquirer": "^12.6.3",
36
37
  "is-unicode-supported": "^2.1.0",
38
+ "js-base64": "3.7.8",
37
39
  "ora": "^8.2.0",
40
+ "qrcode": "^1.5.4",
41
+ "qrcode-terminal": "^0.12.0",
42
+ "query-string": "7.1.3",
38
43
  "shelljs": "^0.10.0",
39
- "table": "^6.9.0"
44
+ "table": "^6.9.0",
45
+ "use-singleton": "0.3.0"
40
46
  },
41
47
  "engines": {
42
48
  "node": "^20.19.0 || >=22.12.0"
49
+ },
50
+ "publishConfig": {
51
+ "registry": "https://registry.npmjs.org/",
52
+ "access": "public"
43
53
  }
44
54
  }
@@ -0,0 +1,106 @@
1
+ import { mkdtemp, chmod, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { setTimeout as delay } from "node:timers/promises";
5
+ import QRCode from "qrcode";
6
+ import { generateLoginQrcode, getLoginQrcodeStatus, getQiandaoUser, exchangeQiandaoLogin, request } from "../api.js";
7
+ import { saveToken } from "./authorized.js";
8
+ import { normalizeLoginEnv } from "./loginEnvironment.js";
9
+ import { startLocalhostLogin } from "./localhostLogin.js";
10
+
11
+ // NDJSON keeps stdout machine-readable. The caller renders ready.markdown while
12
+ // this process keeps the loopback callback and App status polling alive.
13
+ export const agentLogin = async (env = "prod", {
14
+ emit = event => console.log(JSON.stringify(event)),
15
+ timeoutMs = 120000,
16
+ pollIntervalMs = 1500,
17
+ } = {}) => {
18
+ normalizeLoginEnv(env);
19
+ const controller = new AbortController();
20
+ const { signal } = controller;
21
+ const expiresAt = new Date(Date.now() + timeoutMs).toISOString();
22
+ const timer = setTimeout(() => controller.abort(new Error("登录超时,请重新执行登录命令")), timeoutMs);
23
+ const interrupt = () => controller.abort(new Error("已取消登录"));
24
+ process.once("SIGINT", interrupt);
25
+ process.once("SIGTERM", interrupt);
26
+ let browserSession;
27
+ let imageDirectory;
28
+ let committed = false;
29
+ const closeBrowser = () => browserSession?.close();
30
+ signal.addEventListener("abort", closeBrowser, { once: true });
31
+ const commit = token => {
32
+ signal.throwIfAborted();
33
+ if (committed) throw new Error("登录已完成");
34
+ saveToken(token, env, { quiet: true });
35
+ committed = true;
36
+ };
37
+ const requestSignal = () => AbortSignal.any([signal, AbortSignal.timeout(10000)]);
38
+ try {
39
+ browserSession = await startLocalhostLogin({
40
+ env, timeoutMs,
41
+ acceptToken: async (token, isActive) => {
42
+ const result = await request("/mp/v1/user/me", {
43
+ headers: { authorization: `Bearer ${token}` }, signal: requestSignal(),
44
+ }, env, false);
45
+ if (Number(result?.code || 0) !== 0) throw new Error("验证网页登录态失败");
46
+ if (!isActive()) throw new Error("网页登录会话已结束");
47
+ commit(token);
48
+ },
49
+ });
50
+ signal.throwIfAborted();
51
+ const qr = await generateLoginQrcode(env, { signal: requestSignal() });
52
+ imageDirectory = await mkdtemp(join(tmpdir(), "qdmp-login-"));
53
+ const qrImagePath = join(imageDirectory, "qrcode.png");
54
+ await QRCode.toFile(qrImagePath, qr.url, { type: "png", width: 320, margin: 4 });
55
+ await chmod(qrImagePath, 0o600);
56
+ signal.throwIfAborted();
57
+ const loginUrl = browserSession.url;
58
+ await emit({
59
+ type: "ready", env, loginUrl, qrImagePath, expiresAt,
60
+ message: "点击链接前往浏览器登录,或用千岛 App 扫码登录。",
61
+ markdown: `[点击链接前往浏览器登录](${loginUrl}),或用千岛 App 扫码登录。\n\n![千岛 App 扫码登录](<${qrImagePath}>)`,
62
+ });
63
+ const poll = async () => {
64
+ let previousStatus;
65
+ while (!signal.aborted && !committed) {
66
+ const result = await getLoginQrcodeStatus(qr.qrcodeId, env, { signal: requestSignal() });
67
+ signal.throwIfAborted();
68
+ if (result.status === "success") {
69
+ if (!result.token) throw new Error("扫码登录未返回登录态");
70
+ const user = await getQiandaoUser(result.token, env, { signal: requestSignal() });
71
+ const token = await exchangeQiandaoLogin(result.token, user, env, { signal: requestSignal(), persist: false });
72
+ commit(token);
73
+ return "qrcode";
74
+ }
75
+ if (["expired", "canceled", "cancelled", "error"].includes(result.status)) {
76
+ throw new Error("二维码已过期或登录已取消,可继续使用浏览器链接");
77
+ }
78
+ if (previousStatus !== result.status) {
79
+ previousStatus = result.status;
80
+ await emit({ type: "status", env, method: "qrcode", status: result.status });
81
+ }
82
+ await delay(pollIntervalMs, undefined, { signal });
83
+ }
84
+ throw new Error("扫码登录会话已结束");
85
+ };
86
+ const lane = (promise, method) => promise.catch(async () => {
87
+ if (!signal.aborted && !committed) await emit({ type: "unavailable", env, method });
88
+ throw new Error(`${method} 登录未完成`);
89
+ });
90
+ const method = await Promise.any([
91
+ lane(browserSession.result.then(() => "browser"), "browser"),
92
+ lane(poll(), "qrcode"),
93
+ ]).catch(() => {
94
+ throw signal.reason || new Error("登录未完成,请重新执行登录命令");
95
+ });
96
+ await emit({ type: "success", env, method });
97
+ } finally {
98
+ controller.abort();
99
+ clearTimeout(timer);
100
+ closeBrowser();
101
+ process.removeListener("SIGINT", interrupt);
102
+ process.removeListener("SIGTERM", interrupt);
103
+ signal.removeEventListener("abort", closeBrowser);
104
+ if (imageDirectory) await rm(imageDirectory, { recursive: true, force: true });
105
+ }
106
+ };
@@ -0,0 +1,97 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { error, info, warn } from "./logHandler.js";
3
+ import { inquirerAccountPassword, inquirerLoginMethod } from "./interactive.js";
4
+ import {
5
+ ensureAuth,
6
+ isAuthRequiredError,
7
+ login,
8
+ loginByQrcode,
9
+ loginByPlatformURL,
10
+ } from "../api.js";
11
+
12
+ import { normalizeLoginEnv } from "./loginEnvironment.js";
13
+
14
+ const openLoginURL = (url) => {
15
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
16
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
17
+ try {
18
+ execFileSync(command, args, { stdio: "ignore" });
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ };
24
+
25
+ const renderTerminalQRCode = async (url) => {
26
+ try {
27
+ const module = await import("qrcode-terminal");
28
+ const renderer = module.default || module;
29
+ renderer.generate(url, { small: true });
30
+ return true;
31
+ } catch {
32
+ warn("当前环境未安装二维码终端渲染依赖,请复制下方登录 URL 完成登录。");
33
+ return false;
34
+ }
35
+ };
36
+
37
+ const printLoginURL = (url) => {
38
+ console.log("\n登录地址(可复制):");
39
+ console.log(url);
40
+ console.log("完成登录后,CLI 会自动接收登录结果,无需回到终端确认。\n");
41
+ };
42
+
43
+ export const interactiveLogin = async (env = "prod") => {
44
+ normalizeLoginEnv(env);
45
+ info(`当前登录环境:${env}`);
46
+ const method = await inquirerLoginMethod();
47
+ if (method === "password") {
48
+ const { account, password } = await inquirerAccountPassword();
49
+ await login(account, password, env);
50
+ return;
51
+ }
52
+
53
+ const onUpdate = async (event) => {
54
+ if (event.type === "ready") {
55
+ if (method === "qrcode") {
56
+ console.log("\n请使用千岛 App 扫描下方二维码登录:\n");
57
+ const rendered = await renderTerminalQRCode(event.url);
58
+ printLoginURL(event.url);
59
+ if (!rendered) {
60
+ info("二维码未能在终端显示,请使用上面的 URL 登录。");
61
+ }
62
+ } else {
63
+ printLoginURL(event.url);
64
+ if (openLoginURL(event.url)) {
65
+ info("已尝试打开浏览器,请完成登录;成功后 CLI 会自动退出。");
66
+ } else {
67
+ info("未能自动打开浏览器,请复制上面的 URL 手动打开。");
68
+ }
69
+ }
70
+ }
71
+ if (event.type === "status" && event.status === "scanned") {
72
+ info("已扫码,请在手机或浏览器中确认登录。");
73
+ }
74
+ };
75
+
76
+ if (method === "qrcode") {
77
+ // 与 miniapp-h5 的 QRCodeLogin 保持一致:App 扫描后直接进入确认登录弹框。
78
+ await loginByQrcode(env, onUpdate);
79
+ } else {
80
+ await loginByPlatformURL(env, onUpdate);
81
+ }
82
+ };
83
+
84
+ export const ensureInteractiveAuth = async (env = "prod") => {
85
+ try {
86
+ return await ensureAuth(env);
87
+ } catch (cause) {
88
+ if (!isAuthRequiredError(cause)) throw cause;
89
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
90
+ throw new Error(`当前命令需要登录。Agent 对话请执行 qdmp login --agent --env ${normalizeLoginEnv(env)} 获取登录链接和图片,登录后重试原命令;CI 请设置 QDMP_TOKEN`);
91
+ }
92
+ await interactiveLogin(env);
93
+ return ensureAuth(env);
94
+ }
95
+ };
96
+
97
+ export const reportAuthError = (cause) => error(cause?.message || String(cause));
@@ -1,26 +1,40 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import os from "os";
4
+ import { normalizeLoginEnv } from "./loginEnvironment.js";
4
5
  import { error, info } from "./logHandler.js";
5
6
 
6
7
  // 获取配置文件路径
7
8
  const getConfigPath = () => {
8
- return path.join(os.homedir(), ".qdtool.config.json");
9
+ return process.env.QDMP_CONFIG_PATH?.trim() ||
10
+ path.join(os.homedir(), ".qdtool.config.json");
9
11
  };
10
12
 
11
- /**
12
- * 保存认证令牌
13
- * @param {string} token - 认证令牌
14
- */
15
- export const saveToken = (token) => {
13
+ const readConfig = () => {
16
14
  const configPath = getConfigPath();
17
- const config = { token };
15
+ try {
16
+ if (!fs.existsSync(configPath)) return {};
17
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
18
+ } catch {
19
+ return {};
20
+ }
21
+ };
18
22
 
23
+ export const saveToken = (token, env = "prod", { quiet = false } = {}) => {
24
+ normalizeLoginEnv(env);
25
+ const configPath = getConfigPath();
26
+ const config = readConfig();
27
+ const nextConfig = {
28
+ ...config,
29
+ tokens: { ...config.tokens, [env]: token },
30
+ ...(env === "prod" ? { token } : {}),
31
+ };
19
32
  try {
20
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
21
- info("🔑 认证令牌已安全存储");
33
+ fs.writeFileSync(configPath, JSON.stringify(nextConfig, null, 2), { mode: 0o600 });
34
+ fs.chmodSync(configPath, 0o600);
35
+ if (!quiet) info(`🔑 ${env} 环境认证令牌已安全存储`);
22
36
  } catch (err) {
23
- error("保存认证令牌失败:", err.message);
37
+ throw new Error(`保存 ${env} 环境认证令牌失败:${err.message}`);
24
38
  }
25
39
  };
26
40
 
@@ -28,17 +42,24 @@ export const saveToken = (token) => {
28
42
  * 获取认证令牌
29
43
  * @returns {string} - 认证令牌
30
44
  */
31
- export const getToken = () => {
45
+ export const getToken = (env = "prod") => {
46
+ normalizeLoginEnv(env);
32
47
  const environmentToken = process.env.QDMP_TOKEN?.trim();
33
48
  if (environmentToken) return environmentToken;
49
+ const config = readConfig();
50
+ return config.tokens?.[env] || (env === "prod" ? config.token : null) || null;
51
+ };
52
+
53
+ export const clearToken = (env = "prod") => {
54
+ if (process.env.QDMP_TOKEN?.trim()) return;
55
+ normalizeLoginEnv(env);
34
56
  const configPath = getConfigPath();
57
+ const config = readConfig();
58
+ if (config.tokens) delete config.tokens[env];
59
+ if (env === "prod") delete config.token;
35
60
  try {
36
- if (fs.existsSync(configPath)) {
37
- const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
38
- return config.token || null;
39
- }
61
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
40
62
  } catch (err) {
41
- return null;
63
+ error(`清理 ${env} 环境认证令牌失败:${err.message}`);
42
64
  }
43
- return null;
44
65
  };
@@ -35,6 +35,22 @@ export const inquirerAccountPassword = async (message) => {
35
35
  return { account, password };
36
36
  };
37
37
 
38
+ export const inquirerLoginMethod = async () => {
39
+ const { method } = await inquirer.prompt([
40
+ {
41
+ type: "list",
42
+ name: "method",
43
+ message: "请选择登录方式",
44
+ choices: [
45
+ { name: "扫码登录(终端显示二维码)", value: "qrcode" },
46
+ { name: "手动登录 URL(显示可复制地址)", value: "url" },
47
+ { name: "账号密码登录", value: "password" },
48
+ ],
49
+ },
50
+ ]);
51
+ return method;
52
+ };
53
+
38
54
  /**
39
55
  * 输入版本描述
40
56
  * @returns {Promise<string>} 版本描述
@@ -0,0 +1,107 @@
1
+ import { createServer } from "node:http";
2
+ import { randomBytes } from "node:crypto";
3
+ import { normalizeLoginEnv, platformLoginURL } from "./loginEnvironment.js";
4
+
5
+ // Token travels in the fragment (never in HTTP URLs). The landing page removes
6
+ // it from browser history and submits it to this same-origin, single-use endpoint.
7
+ export const startLocalhostLogin = async ({ env = "prod", timeoutMs = 120000, acceptToken }) => {
8
+ normalizeLoginEnv(env);
9
+ const state = randomBytes(32).toString("hex");
10
+ const nonce = randomBytes(24).toString("base64");
11
+ let origin;
12
+ let settled = false;
13
+ let accepting = false;
14
+ let timer;
15
+ let resolveResult;
16
+ let rejectResult;
17
+ const result = new Promise((resolve, reject) => {
18
+ resolveResult = resolve;
19
+ rejectResult = reject;
20
+ });
21
+ // The timeout may fire while the browser opener is still running.
22
+ result.catch(() => {});
23
+ const finish = (error) => {
24
+ if (settled) return;
25
+ settled = true;
26
+ clearTimeout(timer);
27
+ process.removeListener("SIGINT", onInterrupt);
28
+ process.removeListener("SIGTERM", onInterrupt);
29
+ server.close();
30
+ server.closeIdleConnections();
31
+ if (error) rejectResult(error);
32
+ else resolveResult({ status: "confirmed" });
33
+ };
34
+ const onInterrupt = () => finish(new Error("已取消网页登录"));
35
+ const server = createServer(async (req, res) => {
36
+ res.setHeader("Cache-Control", "no-store");
37
+ res.setHeader("Referrer-Policy", "no-referrer");
38
+ res.setHeader("X-Content-Type-Options", "nosniff");
39
+ res.setHeader("Content-Security-Policy", `default-src 'none'; style-src 'nonce-${nonce}'; script-src 'nonce-${nonce}'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'`);
40
+ const reply = (status, text) => { res.writeHead(status); res.end(text); };
41
+ if (req.headers.host !== new URL(origin).host) return reply(403, "Invalid host");
42
+ if (settled) return reply(410, "Login expired");
43
+ const url = new URL(req.url, origin);
44
+ if (req.method === "GET" && url.pathname === "/callback" && url.searchParams.get("state") === state) {
45
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
46
+ return reply(200, `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>千岛 CLI 登录</title><style nonce="${nonce}">
47
+ * { box-sizing: border-box; }
48
+ html, body { margin: 0; min-height: 100%; }
49
+ body { background: #fff; color: #202123; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
50
+ .page { min-height: 100vh; display: flex; flex-direction: column; }
51
+ .brand { padding: 24px 28px; font-size: 18px; font-weight: 650; letter-spacing: -.02em; }
52
+ .content { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 24px; transform: translateY(-5vh); text-align: center; }
53
+ .mark { width: 42px; height: 42px; margin-bottom: 18px; border: 1px solid #e5e7eb; border-radius: 50%; color: #6b7280; display: grid; place-items: center; font-size: 22px; line-height: 1; }
54
+ .mark.success { border-color: #b7e4c7; background: #f0fdf4; color: #15803d; }
55
+ .mark.error { border-color: #fecaca; background: #fef2f2; color: #dc2626; }
56
+ .status { margin: 0; font-size: 16px; line-height: 1.5; font-weight: 500; }
57
+ .status.success { color: #202123; }
58
+ .status.error { color: #b91c1c; }
59
+ .hint { margin: 10px 0 0; color: #6b7280; font-size: 14px; line-height: 1.5; }
60
+ </style></head><body><div class="page"><header class="brand">千岛开放平台</header><main class="content"><div id="mark" class="mark" aria-hidden="true">…</div><p id="status" class="status" aria-live="polite">正在完成登录…</p><p id="hint" class="hint">请稍候,登录状态正在同步到 CLI。</p></main></div><script nonce="${nonce}">
61
+ const token = new URLSearchParams(location.hash.slice(1)).get('token');
62
+ history.replaceState(null, '', '/callback');
63
+ fetch('/complete', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({state: '${state}', env: '${env}', token})})
64
+ .then(async response => { if (!response.ok) throw new Error(); document.getElementById('mark').textContent = '✓'; document.getElementById('mark').className = 'mark success'; document.getElementById('status').textContent = '你已登录,可以关闭此标签页'; document.getElementById('status').className = 'status success'; document.getElementById('hint').textContent = 'CLI 已获取登录状态,请返回终端继续操作。'; })
65
+ .catch(() => { document.getElementById('mark').textContent = '!'; document.getElementById('mark').className = 'mark error'; document.getElementById('status').textContent = '登录失败或已超时'; document.getElementById('status').className = 'status error'; document.getElementById('hint').textContent = '请返回终端重新发起登录。'; });
66
+ </script></body></html>`);
67
+ }
68
+ if (req.method !== "POST" || url.pathname !== "/complete") return reply(404, "Not found");
69
+ if (req.headers.origin !== origin || req.headers["content-type"] !== "application/json") return reply(403, "Invalid origin");
70
+ if (accepting) return reply(409, "Login in progress");
71
+ try {
72
+ let body = "";
73
+ for await (const chunk of req) {
74
+ body += chunk;
75
+ if (Buffer.byteLength(body) > 16384) return reply(413, "Request too large");
76
+ }
77
+ const data = JSON.parse(body);
78
+ if (data.state !== state || data.env !== env || typeof data.token !== "string" || !data.token.trim() || data.token.length > 8192) {
79
+ return reply(400, "Invalid login callback");
80
+ }
81
+ if (settled || accepting) return reply(409, "Login no longer available");
82
+ accepting = true;
83
+ await acceptToken(data.token, () => !settled);
84
+ if (settled) return reply(410, "Login expired");
85
+ reply(200, "OK");
86
+ finish();
87
+ } catch (error) {
88
+ reply(400, "Login failed");
89
+ if (accepting) finish(error);
90
+ }
91
+ });
92
+ server.requestTimeout = 10000;
93
+ server.headersTimeout = 10000;
94
+ await new Promise((resolve, reject) => {
95
+ server.once("error", reject);
96
+ server.listen(0, "127.0.0.1", resolve);
97
+ });
98
+ // Use the stable localhost name in the browser callback. The listener is
99
+ // still bound to IPv4 loopback, so the callback never exposes a LAN port.
100
+ origin = `http://localhost:${server.address().port}`;
101
+ process.once("SIGINT", onInterrupt);
102
+ process.once("SIGTERM", onInterrupt);
103
+ timer = setTimeout(() => finish(new Error("网页登录超时,请重新登录")), timeoutMs);
104
+ const url = new URL(platformLoginURL(env));
105
+ url.search = new URLSearchParams({ cli_callback: `${origin}/callback`, cli_state: state, _env: env }).toString();
106
+ return { url: url.toString(), result, close: () => finish(new Error("已关闭网页登录会话")) };
107
+ };
@@ -0,0 +1,11 @@
1
+ export const normalizeLoginEnv = (env = "prod") => {
2
+ if (env !== "prod" && env !== "dev") {
3
+ throw new Error("登录环境仅支持 prod 或 dev");
4
+ }
5
+ return env;
6
+ };
7
+
8
+ export const platformLoginURL = (env = "prod") =>
9
+ normalizeLoginEnv(env) === "dev"
10
+ ? "https://dev-open.qiandao.com/login"
11
+ : "https://open.qiandao.com/login";
@@ -0,0 +1,57 @@
1
+ import { fileURLToPath } from "node:url";
2
+
3
+ let signerPromise;
4
+
5
+ const installBrowserGlobals = () => {
6
+ if (!globalThis.window) {
7
+ globalThis.window = {
8
+ location: {
9
+ protocol: "https:",
10
+ host: "open.qiandao.com",
11
+ hostname: "open.qiandao.com",
12
+ },
13
+ };
14
+ }
15
+ if (!globalThis.document) globalThis.document = {};
16
+ if (globalThis.XMLHttpRequest) return;
17
+
18
+ // @frontend/pigeon 在初始化时会读取 api.qiandao.cn/now 获取时钟偏移。
19
+ // CLI 使用本地时间即可完成签名,避免为一个时间接口引入额外网络依赖。
20
+ globalThis.XMLHttpRequest = class NodeXMLHttpRequest {
21
+ open(method, url, async = true) {
22
+ this.method = method;
23
+ this.url = url;
24
+ this.async = async;
25
+ }
26
+
27
+ setRequestHeader() {}
28
+
29
+ send() {
30
+ queueMicrotask(() => {
31
+ const response = JSON.stringify({
32
+ data: { timestamp: Math.floor(Date.now() / 1000) },
33
+ });
34
+ this.status = 200;
35
+ this.readyState = 4;
36
+ this.response = response;
37
+ this.responseText = response;
38
+ this.onreadystatechange?.();
39
+ this.onload?.();
40
+ this.onloadend?.();
41
+ });
42
+ }
43
+
44
+ abort() {}
45
+ };
46
+ };
47
+
48
+ export const signQiandaoRequest = async (url) => {
49
+ if (!/^https?:\/\/(?:dev-)?api\.qiandao\.com\//.test(url)) return {};
50
+ if (!signerPromise) {
51
+ installBrowserGlobals();
52
+ const signerURL = new URL("./vendor/pigeon/browser-base.esm.js", import.meta.url);
53
+ signerPromise = import(fileURLToPath(signerURL)).then(({ sign }) => sign);
54
+ }
55
+ const sign = await signerPromise;
56
+ return sign(url);
57
+ };
@@ -0,0 +1 @@
1
+ const t=g;!function(t,n){const r=g,e=tt();for(;;)try{if(639296===-parseInt(r(596))/1*(parseInt(r(416))/2)+parseInt(r(585))/3+-parseInt(r(538))/4*(parseInt(r(678))/5)+-parseInt(r(499))/6+-parseInt(r(526))/7+-parseInt(r(649))/8+-parseInt(r(552))/9*(-parseInt(r(558))/10))break;e.push(e.shift())}catch(t){e.push(e.shift())}}();var n=function(t,{withKey:n=!1,immediate:r=!1}={}){const e={};let o,a=e;function s(r){return(a===e||(s=r,!(!n||void 0===s||s===o)))&&(o=r,a=t(o)),a;var s}return r&&s(),s},r={},e=n=>encodeURIComponent(n)[t(588)](/[!'()*]/g,(n=>"%"+n[t(543)](0)[t(482)](16)[t(458)]())),o=new RegExp("%[a-f0-9]{2}","gi"),a=new RegExp("(%[a-f0-9]{2})+","gi");function s(n,r){const e=t;try{return decodeURIComponent(n[e(688)](""))}catch(t){}if(1===n[e(701)])return n;r=r||1;var o=n.slice(0,r),a=n[e(628)](r);return Array[e(689)][e(494)].call([],s(o),s(a))}function c(n){const r=t;try{return decodeURIComponent(n)}catch(t){for(var e=n.match(o),a=1;a<e[r(701)];a++)e=(n=s(e,a)[r(688)]("")).match(o);return n}}var i=function(n){const r=t;if("string"!=typeof n)throw new TypeError(r(432)+typeof n+"`");try{return n=n[r(588)](/\+/g," "),decodeURIComponent(n)}catch(r){return function(n){const r=t;for(var e={"%FE%FF":"\ufffd\ufffd","%FF%FE":"\ufffd\ufffd"},o=a[r(507)](n);o;){try{e[o[0]]=decodeURIComponent(o[0])}catch(t){var s=c(o[0]);s!==o[0]&&(e[o[0]]=s)}o=a[r(507)](n)}e["%C2"]="\ufffd";for(var i=Object[r(677)](e),u=0;u<i[r(701)];u++){var p=i[u];n=n[r(588)](new RegExp(p,"g"),e[p])}return n}(n)}},u=(n,r)=>{const e=t;if(typeof n!==e(444)||typeof r!==e(444))throw new TypeError("Expected the arguments to be of type `string`");if(""===r)return[n];const o=n[e(631)](r);return-1===o?[n]:[n[e(628)](0,o),n[e(628)](o+r[e(701)])]},p=function(n,r){const e=t;for(var o={},a=Object[e(677)](n),s=Array[e(452)](r),c=0;c<a[e(701)];c++){var i=a[c],u=n[i];(s?-1!==r.indexOf(i):r(i,u,n))&&(o[i]=u)}return o};!function(n){const r=t,o=e,a=i,s=u,c=p,f=Symbol(r(517));function l(t){const n=r;if(typeof t!==n(444)||1!==t[n(701)])throw new TypeError(n(504))}function I(t,n){return n[r(424)]?n.strict?o(t):encodeURIComponent(t):t}function y(t,n){return n[r(437)]?a(t):t}function d(t){const n=r;return Array[n(452)](t)?t.sort():"object"==typeof t?d(Object.keys(t)).sort(((t,n)=>Number(t)-Number(n)))[n(582)]((n=>t[n])):t}function m(t){const n=t[r(631)]("#");return-1!==n&&(t=t.slice(0,n)),t}function g(t){const n=r,e=(t=m(t))[n(631)]("?");return-1===e?"":t[n(628)](e+1)}function h(t,n){const e=r;return n[e(454)]&&!Number[e(629)](Number(t))&&typeof t===e(444)&&""!==t[e(706)]()?t=Number(t):n[e(670)]&&null!==t&&("true"===t[e(563)]()||"false"===t[e(563)]())&&(t=t[e(563)]()===e(698)),t}function b(t,n){const e=r;l((n=Object[e(682)]({decode:!0,sort:!0,arrayFormat:"none",arrayFormatSeparator:",",parseNumbers:!1,parseBooleans:!1},n))[e(434)]);const o=function(t){const n=r;let e;switch(t[n(430)]){case n(428):return(t,r,o)=>{e=/\[(\d*)\]$/[n(507)](t),t=t.replace(/\[\d*\]$/,""),e?(void 0===o[t]&&(o[t]={}),o[t][e[1]]=r):o[t]=r};case n(441):return(t,r,o)=>{const a=n;e=/(\[\])$/[a(507)](t),t=t.replace(/\[\]$/,""),e?void 0!==o[t]?o[t]=[][a(494)](o[t],r):o[t]=[r]:o[t]=r};case"colon-list-separator":return(t,r,o)=>{const a=n;e=/(:list)$/[a(507)](t),t=t[a(588)](/:list$/,""),e?void 0!==o[t]?o[t]=[].concat(o[t],r):o[t]=[r]:o[t]=r};case n(607):case n(667):return(r,e,o)=>{const a=n,s="string"==typeof e&&e[a(502)](t.arrayFormatSeparator),c=typeof e===a(444)&&!s&&y(e,t).includes(t[a(434)]);e=c?y(e,t):e;const i=s||c?e.split(t.arrayFormatSeparator).map((n=>y(n,t))):null===e?e:y(e,t);o[r]=i};case n(463):return(r,e,o)=>{const a=n,s=/(\[\])$/[a(549)](r);if(r=r[a(588)](/\[\]$/,""),!s)return void(o[r]=e?y(e,t):e);const c=null===e?[]:e[a(528)](t[a(434)])[a(582)]((n=>y(n,t)));void 0!==o[r]?o[r]=[].concat(o[r],c):o[r]=c};default:return(t,r,e)=>{const o=n;void 0!==e[t]?e[t]=[][o(494)](e[t],r):e[t]=r}}}(n),a=Object[e(660)](null);if(typeof t!==e(444))return a;if(!(t=t[e(706)]()[e(588)](/^[?#&]/,"")))return a;for(const r of t[e(528)]("&")){if(""===r)continue;let[t,c]=s(n[e(437)]?r[e(588)](/\+/g," "):r,"=");c=void 0===c?null:[e(607),e(667),e(463)][e(502)](n[e(430)])?c:y(c,n),o(y(t,n),c,a)}for(const t of Object.keys(a)){const r=a[t];if(typeof r===e(614)&&null!==r)for(const t of Object[e(677)](r))r[t]=h(r[t],n);else a[t]=h(r,n)}return!1===n[e(545)]?a:(!0===n.sort?Object[e(677)](a)[e(545)]():Object[e(677)](a)[e(545)](n.sort))[e(594)](((t,n)=>{const r=e,o=a[n];return Boolean(o)&&typeof o===r(614)&&!Array[r(452)](o)?t[n]=d(o):t[n]=o,t}),Object[e(660)](null))}n[r(663)]=g,n[r(630)]=b,n[r(643)]=(t,n)=>{const e=r;if(!t)return"";l((n=Object[e(682)]({encode:!0,strict:!0,arrayFormat:e(534),arrayFormatSeparator:","},n)).arrayFormatSeparator);const o=function(t){const n=r;switch(t.arrayFormat){case n(428):return r=>(e,o)=>{const a=n,s=e[a(701)];return void 0===o||t.skipNull&&null===o||t[a(615)]&&""===o?e:null===o?[...e,[I(r,t),"[",s,"]"][a(688)]("")]:[...e,[I(r,t),"[",I(s,t),"]=",I(o,t)].join("")]};case n(441):return r=>(e,o)=>{const a=n;return void 0===o||t[a(598)]&&null===o||t[a(615)]&&""===o?e:null===o?[...e,[I(r,t),"[]"][a(688)]("")]:[...e,[I(r,t),a(609),I(o,t)].join("")]};case n(571):return r=>(e,o)=>{const a=n;return void 0===o||t[a(598)]&&null===o||t[a(615)]&&""===o?e:null===o?[...e,[I(r,t),a(533)][a(688)]("")]:[...e,[I(r,t),a(533),I(o,t)][a(688)]("")]};case n(607):case n(667):case"bracket-separator":{const r=t.arrayFormat===n(463)?n(609):"=";return e=>(o,a)=>{const s=n;return void 0===a||t[s(598)]&&null===a||t.skipEmptyString&&""===a?o:(a=null===a?"":a,0===o[s(701)]?[[I(e,t),r,I(a,t)][s(688)]("")]:[[o,I(a,t)][s(688)](t[s(434)])])}}default:return r=>(e,o)=>{const a=n;return void 0===o||t.skipNull&&null===o||t[a(615)]&&""===o?e:null===o?[...e,I(r,t)]:[...e,[I(r,t),"=",I(o,t)][a(688)]("")]}}}(n),a={};for(const r of Object[e(677)](t))s=r,!(n[e(598)]&&null==t[s]||n[e(615)]&&""===t[s])&&(a[r]=t[r]);var s;const c=Object.keys(a);return!1!==n.sort&&c[e(545)](n[e(545)]),c[e(582)]((r=>{const a=e,s=t[r];return void 0===s?"":null===s?I(r,n):Array[a(452)](s)?0===s[a(701)]&&"bracket-separator"===n[a(430)]?I(r,n)+"[]":s.reduce(o(r),[]).join("&"):I(r,n)+"="+I(s,n)}))[e(696)]((t=>t[e(701)]>0))[e(688)]("&")},n[r(562)]=(t,n)=>{const e=r;n=Object[e(682)]({decode:!0},n);const[o,a]=s(t,"#");return Object[e(682)]({url:o.split("?")[0]||"",query:b(g(t),n)},n&&n.parseFragmentIdentifier&&a?{fragmentIdentifier:y(a,n)}:{})},n[r(648)]=(t,e)=>{const o=r;e=Object[o(682)]({encode:!0,strict:!0,[f]:!0},e);const a=m(t.url)[o(528)]("?")[0]||"",s=n[o(663)](t[o(489)]),c=n[o(630)](s,{sort:!1}),i=Object[o(682)](c,t[o(474)]);let u=n[o(643)](i,e);u&&(u="?"+u);let p=function(t){const n=r;let e="";const o=t[n(631)]("#");return-1!==o&&(e=t[n(628)](o)),e}(t[o(489)]);return t[o(690)]&&(p="#"+(e[f]?I(t[o(690)],e):t[o(690)])),""+a+u+p},n[r(676)]=(t,e,o)=>{const a=r;o=Object.assign({parseFragmentIdentifier:!0,[f]:!1},o);const{url:s,query:i,fragmentIdentifier:u}=n[a(562)](t,o);return n[a(648)]({url:s,query:c(i,e),fragmentIdentifier:u},o)},n[r(492)]=(t,e,o)=>{const a=r,s=Array.isArray(e)?t=>!e[a(502)](t):(t,n)=>!e(t,n);return n[a(676)](t,s,o)}}(r);const f=typeof btoa===t(659),l=typeof Buffer===t(659);typeof TextDecoder===t(659)&&new TextDecoder;const I=typeof TextEncoder===t(659)?new TextEncoder:void 0,y=t(410),d=Array[t(689)][t(628)][t(439)](y);(n=>{let r={};d[t(567)](((t,n)=>r[t]=n))})();const m=String[t(496)][t(479)](String);function g(t,n){const r=tt();return(g=function(t,n){return r[t-=408]})(t,n)}typeof Uint8Array[t(423)]===t(659)&&Uint8Array[t(423)].bind(Uint8Array);const h=f?t=>btoa(t):l?n=>Buffer.from(n,"binary").toString(t(623)):n=>{const r=t;let e,o,a,s,c="";const i=n.length%3;for(let t=0;t<n[r(701)];){if((o=n[r(543)](t++))>255||(a=n[r(543)](t++))>255||(s=n.charCodeAt(t++))>255)throw new TypeError(r(684));e=o<<16|a<<8|s,c+=d[e>>18&63]+d[e>>12&63]+d[e>>6&63]+d[63&e]}return i?c[r(628)](0,i-3)+r(522)[r(581)](i):c},b=l?n=>Buffer.from(n)[t(482)]("base64"):n=>{const r=t;let e=[];for(let t=0,o=n[r(701)];t<o;t+=4096)e[r(557)](m[r(436)](null,n[r(675)](t,t+4096)));return h(e.join(""))},v=n=>{const r=t;if(n[r(701)]<2)return(e=n[r(543)](0))<128?n:e<2048?m(192|e>>>6)+m(128|63&e):m(224|e>>>12&15)+m(128|e>>>6&63)+m(128|63&e);var e=65536+1024*(n[r(543)](0)-55296)+(n[r(543)](1)-56320);return m(240|e>>>18&7)+m(128|e>>>12&63)+m(128|e>>>6&63)+m(128|63&e)},w=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,k=l?n=>Buffer[t(423)](n,t(580))[t(482)](t(623)):I?n=>b(I[t(424)](n)):n=>h(n[t(588)](w,v)),T=(n,r=!1)=>r?k(n)[t(588)](/=/g,"").replace(/[+\/]/g,(t=>"+"==t?"-":"_")):k(n);function F(){const n=t;var r=[n(559),n(656),n(535),n(592),n(469),n(477),n(500),"len","22833PggbRz",n(681),n(579),n(624),n(613),n(639),n(605),n(604),"012",n(446),n(652),n(687),"cat",n(508),"mCh",n(601),n(468)];return(F=function(){return r})()}!function(n,r){const e=t;for(var o=S,a=F();;)try{if(382809===-parseInt(o(135))/1+-parseInt(o(124))/2+parseInt(o(134))/3*(-parseInt(o(126))/4)+parseInt(o(141))/5+parseInt(o(137))/6+-parseInt(o(136))/7+-parseInt(o(145))/8*(-parseInt(o(147))/9))break;a[e(557)](a.shift())}catch(t){a[e(557)](a[e(658)]())}}();var q=0,j=function(n,r){return function(n){const r=t;var e=S;for(var o,a=q?e(142)+r(685)+e(128)+r(626)+e(143)+"F":r(566)+r(685)+r(535)+r(486)+e(144)+"f",s="",c=0;c<n[r(523)+e(139)];c++)o=n[r(468)+r(656)+e(129)+"t"](c),s+=a[e(125)+e(138)](o>>>4&15)+a["cha"+e(138)](15&o);return s}(function(n,r){const e=t;var o=S,a=R(n);a[o(133)+o(139)]>16&&(a=x(a,8*n[o(133)+o(139)]));for(var s=Array(16),c=Array(16),i=0;i<16;i++)s[i]=909522486^a[i],c[i]=1549556828^a[i];var u=x(s[o(131)+e(542)](R(r)),512+8*r[o(133)+o(139)]);return function(n){const r=t;for(var e=S,o="",a=0;a<32*n[e(133)+e(139)];a+=8)o+=String[e(130)+r(427)+e(140)+e(132)](n[a>>5]>>>24-a%32&255);return o}(x(c[o(131)+o(146)](u),768))}(O(n),O(r)))};function O(n){const r=t;for(var e,o,a=S,s="",c=-1;++c<n[a(133)+a(139)];)e=n[a(125)+a(127)+a(129)+"t"](c),o=c+1<n[a(133)+a(139)]?n[r(468)+a(127)+a(129)+"t"](c+1):0,55296<=e&&e<=56319&&56320<=o&&o<=57343&&(e=65536+((1023&e)<<10)+(1023&o),c++),e<=127?s+=String[a(130)+a(123)+r(605)+"ode"](e):e<=2047?s+=String[a(130)+a(123)+r(605)+a(132)](192|e>>>6&31,128|63&e):e<=65535?s+=String[a(130)+a(123)+r(605)+r(500)](224|e>>>12&15,128|e>>>6&63,128|63&e):e<=2097151&&(s+=String[a(130)+a(123)+a(140)+a(132)](240|e>>>18&7,128|e>>>12&63,128|e>>>6&63,128|63&e));return s}function R(n){const r=t;var e=S,o=Array(n[r(523)+e(139)]>>2);for(let t=0;t<o[r(523)+e(139)];t++)o[t]=0;for(let t=0;t<8*n[e(133)+e(139)];t+=8)o[t>>5]|=(255&n[e(125)+e(127)+r(592)+"t"](t/8))<<24-t%32;return o}function S(t,n){var r=F();return(S=function(t,n){return r[t-=123]})(t,n)}function C(t,n){return t>>>n|t<<32-n}function A(t,n){return t>>>n}var U=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,-778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998];function x(t,n){var r,e,o,a,s,c,i,u,p,f,l,I,y,d,m,g,h,b,v,w,k=S,T=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225],F=new Array(64);for(t[n>>5]|=128<<24-n%32,t[15+(n+64>>9<<4)]=n,p=0;p<t[k(133)+k(139)];p+=16){for(r=T[0],e=T[1],o=T[2],a=T[3],s=T[4],c=T[5],i=T[6],u=T[7],f=0;f<64;f++)F[f]=f<16?t[f+p]:M(M(M(C(w=F[f-2],17)^C(w,19)^A(w,10),F[f-7]),C(v=F[f-15],7)^C(v,18)^A(v,3)),F[f-16]),l=M(M(M(M(u,C(b=s,6)^C(b,11)^C(b,25)),(h=s)&c^~h&i),U[f]),F[f]),I=M(C(g=r,2)^C(g,13)^C(g,22),(y=r)&(d=e)^y&(m=o)^d&m),u=i,i=c,c=s,s=M(a,l),a=o,o=e,e=r,r=M(l,I);T[0]=M(r,T[0]),T[1]=M(e,T[1]),T[2]=M(o,T[2]),T[3]=M(a,T[3]),T[4]=M(s,T[4]),T[5]=M(c,T[5]),T[6]=M(i,T[6]),T[7]=M(u,T[7])}return T}function M(t,n){var r=(65535&t)+(65535&n);return(t>>16)+(n>>16)+(r>>16)<<16|65535&r}const N=E;function E(t,n){const r=z();return(E=function(t,n){return r[t-=445]})(t,n)}!function(n,r){const e=t,o=E,a=z();for(;;)try{if(394227===-parseInt(o(446))/1*(parseInt(o(452))/2)+-parseInt(o(461))/3*(parseInt(o(447))/4)+-parseInt(o(458))/5+-parseInt(o(453))/6+parseInt(o(449))/7*(parseInt(o(450))/8)+parseInt(o(459))/9+parseInt(o(451))/10)break;a[e(557)](a.shift())}catch(t){a[e(557)](a.shift())}}();const B=/^((http[s]?|ftp):\/)?\/?([^:/\s]+)((\/\w+)*\/)([\w\-.]+[^#?\s]+)(.*)?(#[\w-]+)?$/,Z=[N(456),null,N(448)+N(455)+"ol",N(445)+"t",t(707)+"h",null,t(449)+"e","query",t(447)+"h"];function z(){const n=t,r=[n(453),n(489),n(620),n(560),n(586),n(529),"3009UAAwrI",n(587),n(555),n(570),"pro",n(461),"8FIcGbg",n(440),n(577),n(429),"exe"];return(z=function(){return r})()}const D=L;function L(t,n){const r=Y();return(L=function(t,n){return r[t-=128]})(t,n)}!function(n,r){const e=t,o=L,a=Y();for(;;)try{if(850613===-parseInt(o(158))/1+parseInt(o(144))/2*(parseInt(o(133))/3)+-parseInt(o(139))/4+parseInt(o(148))/5+parseInt(o(152))/6*(-parseInt(o(131))/7)+-parseInt(o(142))/8*(-parseInt(o(130))/9)+parseInt(o(154))/10)break;a[e(557)](a[e(658)]())}catch(t){a[e(557)](a[e(658)]())}}();const V=["qia"+D(143)+D(128)+t(448)+"om",D(150)+t(425)+D(155)+"n",D(153)+t(640)+t(606),D(151)+D(163)+D(156),t(433)+"jie"+D(136)+D(157)+"m",D(150)+D(143)+D(155)+"om",D(134)+"uoa"+D(138)+D(147),D(159)+D(161)+t(679)+"om",D(159)+"iar"+D(132)+"n",D(159)+"i.cn",D(137)+D(149)+D(140)+"t",D(159)+t(637)+D(146)+D(129)];function Y(){const n=t,r=["qia",n(651),"144642nNFKAO",n(606),n(539),n(603),n(488),".co",n(451),n(680),n(503),n(637),n(587),"alh","oap",n(710),"35001zLLvdL","427DJUmdn","t.c","322914qCHpIY",n(612),n(419),n(633),n(654),n(521),n(591),".ar",n(655),n(455),n(425),"4hdWIOs",n(435),"t.w","net",n(497),n(683)];return(Y=function(){return r})()}function J(t,n){var r=W();return(J=function(t,n){return r[t-=416]})(t,n)}function X({signVersion:n,signType:r}){const e=t;var o=J;return{"X-Request-Sign":o(418)+o(440)+o(437)+o(429)+e(411)+"iYW"+o(447)+o(421)+o(420)+"3NT"+o(422)+e(622)+o(423)+o(416)+o(435)+o(436)+e(556)+o(432)+o(427)+o(442)+e(597)+o(425)+o(443)+o(446)+o(430)+o(426)+o(433)+o(417)+o(444)+"=","X-Request-Timestamp":(new Date)[o(438)+"ueOf"]()+"","X-Request-Package-Sign-Version":n,"X-Request-Sign-Version":"v1","X-Request-Sign-Type":r}}function W(){const n=t;var r=[n(565),n(465),"15292jCTRCL",n(694),n(661),n(420),"www",n(617),n(472),n(473),n(491),n(561),n(702),n(665),n(568),n(464),n(530),n(450),n(611),n(506),n(512),n(645),n(509),n(650),"VjY",n(632),n(693),n(669),n(619),n(708),n(476),n(625)];return(W=function(){return r})()}function H(){const n=t;var r=[n(527),"2456JLZcaJ",n(671),n(664),n(431),"177551wLbUqr",n(590),n(578),n(456),"170pKeqPU",n(525),n(510),n(627),n(520),"597WMqdTk",n(475),n(618)];return(H=function(){return r})()}function K(t,n){var r=H();return(K=function(t,n){return r[t-=221]})(t,n)}function P(t,n){const r=Q();return(P=function(t,n){return r[t-=103]})(t,n)}function G(t,n){return t+"">n+""?-1:1}function Q(){const n=t,r=[n(519),n(707),"10bXItph",n(515),n(621),"isA",n(610),"val",n(672),n(691),n(703),"l0b",n(484),n(683),"44ElgMcX",n(695),n(583),n(459),n(408),n(470),n(599),n(595),n(711),n(483),n(642),"ing",n(589),n(657),"web",n(662),n(674),n(699),n(490),n(553)];return(Q=function(){return r})()}!function(n,r){const e=t;for(var o=J,a=W();;)try{if(544237===-parseInt(o(431))/1+parseInt(o(445))/2+parseInt(o(439))/3*(parseInt(o(424))/4)+-parseInt(o(419))/5+parseInt(o(428))/6+parseInt(o(434))/7+parseInt(o(441))/8)break;a[e(557)](a[e(658)]())}catch(t){a[e(557)](a[e(658)]())}}(),function(n,r){const e=t;for(var o=K,a=H();;)try{if(635303===parseInt(o(227))/1*(-parseInt(o(232))/2)+parseInt(o(229))/3*(-parseInt(o(233))/4)+-parseInt(o(225))/5+parseInt(o(222))/6*(-parseInt(o(234))/7)+parseInt(o(230))/8*(parseInt(o(226))/9)+-parseInt(o(224))/10*(parseInt(o(237))/11)+parseInt(o(236))/12)break;a[e(557)](a[e(658)]())}catch(t){a[e(557)](a[e(658)]())}}(),function(n,r){const e=t,o=P,a=Q();for(;;)try{if(843148===parseInt(o(116))/1*(-parseInt(o(112))/2)+-parseInt(o(127))/3*(-parseInt(o(131))/4)+parseInt(o(114))/5*(-parseInt(o(125))/6)+parseInt(o(126))/7+parseInt(o(106))/8*(parseInt(o(136))/9)+parseInt(o(119))/10*(-parseInt(o(135))/11)+-parseInt(o(123))/12)break;a[e(557)](a[e(658)]())}catch(t){a[e(557)](a[e(658)]())}}();function $(t,n){var r=_();return($=function(t,n){return r[t-=147]})(t,n)}function _(){const n=t;var r=[n(438),n(453),"ati",n(442),n(460),n(576),n(524),n(478),n(569),n(546),"sta",n(544),n(646),n(572),n(412),n(593),n(541),n(493),n(651),"pro",n(564),n(467),n(587)];return(_=function(){return r})()}function tt(){const t=["toLowerCase","7979590QFRJdx","JmZ","012","forEach","lYm","21763FKmLpL","304VwiPjv","colon-list-separator","63040510jzjdUv","pi.","one","2TJ","rts","2nDvaaQ","96NrlQDu","4406871TspeYB","utf8","substring","map","ueO","7350EtwmCd","3706851tbCTOD","6376005dODHoR","hos","replace","ogr","duc","2979880tFJwxB","deA","tes","reduce","\u83b7\u53d6s","2961aqyZHV","NTB","skipNull","str","L2n","1036796BOvlFR","als","o.c","1082225ejLpKH","arC","ech","comma","4WUMxTc","[]=","5793132tZSiMb","TA2","qih","rAt","object","skipEmptyString","vK1","OTY","elo","169731pxxpby","Eac","ipr","WM1","base64","501186ZYgCfk","zQ1","9AB","4SGrkBk","slice","isNaN","parse","indexOf","WZh","app","cyv","res","olv","iar","1Tf","gth","o.t","1kSrAxh","red","stringify","HMA","wM2","55185hfjBOs","hRT","stringifyUrl","5598512YLokdl","5412974stsFgr","loc","cde","fun","ten","tna","rCo","ify","shift","function","create","I4Z","430iNiuFd","extract","pme","72zEUTwC","que","separator","3045504LvtPoH","val","parseBooleans","22778UNMRNJ","6LkZoEy","ede","par","subarray","pick","keys","5ZzbQHk","t.c","tus","417069jwMega","assign","sor","invalid character found","345","Iao","112MveLPu","join","prototype","fragmentIdentifier","8968365kiexgV","GI9","fxZ","TY2","uce","filter","74031RzAUpt","true","1243420DYKDlM","sen","length","N2Y","49713TzkqTb","12060351mIbsyz","C_S","trim","pat","yNj","7fDgZeQ","ork","cke","5728591nDpjrU","n/n","ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=","NWR","86Zsztdi","Tim","7VwUbSI","ign","452vSrZBM","rro","1561311POyGFH","cti","1Y2","err","591116JWPjST","from","encode","nda","LUX","mCh","index","2791014XLpOMX","arrayFormat","11648592gnQwCo","Expected `encodedURI` to be of type `string`, got `","lin","arrayFormatSeparator","som","apply","decode","10740etFGCo","call","7731790JTnElv","bracket","36xWcGcc","4066835lFyuBl","string","1311185OoTqii","CDE","has","p.c","fil","1354548ZMaTVQ","848660TicgRU","isArray","toc","parseNumbers","3488YktLKF","tio","htt","toUpperCase","spl","9aytTvJ","1389661SpRBic","YAO","bracket-separator","wXW","ww=","8129bllqUF","2488qUpRiz","cha","fro","9JMGpYT","3qw","5173880HTCqWt","MzA","query","862472xbRmfr","4182008MukRkk","con","Wit","bind","nti","P0G","toString","9270904giMZcI","rep","ore","9ab","wit","ost","url","key","Dkw","exclude","404335TOMKjW","concat","pro","fromCharCode","2609375gvhchA","3Kh","805758NdnuRZ","ode","MW3","includes","ati","arrayFormatSeparator must be single character string","pon","YTE","exec","1426293jfojbI","Rww","81DesQir","0.0","968810OKPvbX","HA2","GJq","min","abs","encodeFragmentIdentifier","1171308LGdQBk","bra","dev","pp.","===","len","36FCqHMB","1497615lzHlLN","3233139RxaMWU","278762oJMPYH","split","for","ZhY","UIg","1931373VbIoJF",":list=","none","678","get","6337128WRzZzl","4173140BgVVUM","14820150XtcHBp","onl","6633jgCBuq","cat","charCodeAt","27832UgGzRT","sort","lac","ps:","qia","test","MMt","834978helUYJ","28341uqVzKg","2451bLnzaK","web","605621bpvhPH","MjQ","push","7660GfqKfO","300ZaSRth","694510IzOVCO","JlY","parseUrl"];return(tt=function(){return t})()}function nt(t,n){const r=rt();return(nt=function(t,n){return r[t-=389]})(t,n)}function rt(){const n=t,r=[n(466),"7632084ZIZxVJ",n(418),n(608),n(443),n(537),n(593),n(709),n(704),n(584),"74cZKQrB",n(697)];return(rt=function(){return r})()}!function(n,r){const e=t;for(var o=$,a=_();;)try{if(697194===parseInt(o(148))/1*(-parseInt(o(168))/2)+-parseInt(o(169))/3*(parseInt(o(163))/4)+parseInt(o(151))/5*(-parseInt(o(160))/6)+parseInt(o(165))/7*(-parseInt(o(155))/8)+parseInt(o(161))/9*(-parseInt(o(154))/10)+-parseInt(o(150))/11*(parseInt(o(157))/12)+parseInt(o(147))/13)break;a[e(557)](a[e(658)]())}catch(t){a[e(557)](a[e(658)]())}}(),function(n,r){const e=t,o=nt,a=rt();for(;;)try{if(901448===-parseInt(o(389))/1+-parseInt(o(397))/2*(-parseInt(o(398))/3)+parseInt(o(390))/4*(-parseInt(o(391))/5)+-parseInt(o(400))/6*(-parseInt(o(394))/7)+-parseInt(o(392))/8+parseInt(o(395))/9+-parseInt(o(396))/10*(-parseInt(o(399))/11))break;a[e(557)](a[e(658)]())}catch(t){a.push(a[e(658)]())}}();const et=ot;function ot(t,n){const r=lt();return(ot=function(t,n){return r[t-=476]})(t,n)}!function(n,r){const e=t,o=ot,a=lt();for(;;)try{if(256664===parseInt(o(480))/1*(parseInt(o(523))/2)+parseInt(o(521))/3+-parseInt(o(528))/4+-parseInt(o(520))/5+-parseInt(o(506))/6*(parseInt(o(497))/7)+parseInt(o(503))/8+parseInt(o(489))/9)break;a[e(557)](a[e(658)]())}catch(t){a.push(a[e(658)]())}}();const at=({fetchSkey:e,platform:o,signVersion:a,signType:s})=>{let c=0,i="0";const u=n((async()=>{const t=g,n=P;try{const r=await e();if(!r)throw new Error(n(104)+t(490)+"\u5931\u8d25");return r}catch(t){throw i=Number(i)+1+"",t}}),{withKey:!0});return{setTimeOffset:function(t){c=t},refreshSkey:async function(){const t=P;return await u((new Date)[t(124)+t(133)+"f"]()+"")},sign:async function({url:n,requestId:e="",timestamp:p,skey:f="",query:l}){const I=g,y=P;switch(o){case y(120)+y(121)+y(109)+"am":if(!function(){const n=L;return typeof getApp==t(653)+n(135)+"on"}())return X({signType:s,signVersion:a});break;case y(111):if(!function(){const t=D;return window&&V[t(145)+"e"]((n=>{const r=t,e=window[g(651)+r(160)+"on"][r(162)+r(141)+"me"];return e===n||e.endsWith("."+n)}))}())return X({signType:s,signVersion:a});break;case y(128)+"0b":break;default:return X({signType:s,signVersion:a})}const d=(p||(new Date)[I(536)+I(413)+"e"]()+c)+"",m=function({url:n}){const r=t;var e=K;return/^(https?:)?\/\/(dev-|local|test-[a-z]-)/[r(593)+"t"](n)?e(228)+e(231)+e(235)+"nt":r(495)+e(221)+e(223)+"n"}({url:n}),h=f||(await async function(){return await u(i)}())[m],b=(t=>{const n=N,r=B[n(454)+"c"](t),e={};return r?Z[n(460)+n(457)+"h"](((t,n)=>{t&&(e[t]=r[n]||"")})):Z[n(460)+"Each"]((t=>{t&&(e[t]="")})),e})(n),v={...r[y(113)+"se"](b[I(666)+"ry"][y(129)+I(546)+"e"](/^\?/,""),{parseNumbers:!1,arrayFormat:y(117)+y(105)+"t"}),...r[y(113)+"se"](r[y(103)+y(108)+I(657)](l||{},{strict:!1,arrayFormat:y(117)+y(105)+"t"}),{arrayFormat:I(519)+y(105)+"t"})},w=r[y(103)+y(108)+y(110)](Object[y(115)+"s"](v)[y(107)+y(132)](((t,n)=>{const r=y;let e=v[n];return Array[r(122)+"rray"](e)&&(e=e[r(130)+"t"](G)),t[n]=e,t}),{}),{arrayFormat:y(117)+y(105)+"t",strict:!1,sort:G}),k=e||"",F=b[y(118)+"h"]+b[I(449)+"e"]+function(t){const n=g,r=P;return t?t[r(134)+"it"]("&")[n(642)+r(132)](((t,n,e)=>{const o=r;let[a,s]=n[o(134)+"it"]("=");return a=encodeURIComponent(a),s=s||"",t+(e>0?"&":"")+a+"="+s}),""):""}(w)+k+d;return{"X-Request-Sign":T(j(h,F)),"X-Request-Timestamp":d,"X-Request-Package-Sign-Version":a,"X-Request-Sign-Version":"v1","X-Request-Sign-Type":s}}}},st={production:et(477)+t(647)+et(516)+et(525)+et(517)+et(515)+et(513)+et(496)+et(508)+et(490)+"kC",development:et(518)+t(616)+et(514)+t(481)+et(499)+et(500)+t(692)+et(478)+t(634)+t(426)+"0q"};let ct=!1;const{sign:it,setTimeOffset:ut}=at({fetchSkey:async function(){return st},platform:et(512),signVersion:et(485)+".1",signType:et(495)+et(524)+et(505)+"56"});const pt=n((async()=>{const n=t,r=et;try{const t=await(e=r(491)+r(488)+r(482)+r(479)+r(484)+n(425)+r(502)+n(409)+"ow",new Promise(((t,n)=>{const r=g,o=ot,a=new XMLHttpRequest;a[o(501)+o(486)+r(673)+r(480)+o(527)]=!1,a[o(507)+"n"](o(476),e,!0),a[o(509)+o(483)+"r"]=function(t){n(t)},a[o(526)+o(492)]=function(){const n=r,e=o;t(JSON[e(519)+"se"](a[e(510)+n(505)+"se"]))},a[r(700)+"d"]()}))),{data:o}=t,{timestamp:a}=o,s=1e3*a-(new Date)[r(522)+r(529)+"e"]();Math[n(516)](s)>6e4&&ut(s)}catch(t){console[r(494)+"or"](t)}var e}),{immediate:!0}),ft=async(n,r)=>{const e=et;return function(t){const n=nt;return e=t,/^(https?:)?\/\/(dev-|test-[a-z]-)?((sankeng|gapi|api|local|gapi-cdn|om|wms|api-admin)\.(qiandaoapp\.com|echoing\.tech|qiandao\.cn|pgscard\.com|echo\.tech|qiandaoapp\.net|qiandao\.com|qihuoapp\.net|tusiart\.com|tusiart\.cn|tensor\.art|tusi\.cn)|localhost)/[n(393)+"t"](e)&&(r=t,!/((\/(now|airline|collect))|\.json)(\?|$)/[n(393)+"t"](r));var r,e}(n=function(n){const r=t;var e=$;return/[a-zA-Z]+:/[e(149)+"t"](n)?n:n[e(167)+e(162)+e(164)+"h"]("//")?""+window[e(152)+e(159)+"on"][e(153)+r(453)+"ol"]+n:window[e(152)+e(159)+"on"][r(495)+e(158)+"ol"]+"//"+window[e(152)+e(159)+"on"][e(156)+"t"]+"/"+n[r(484)+e(166)+"e"](/^\//,"")}(n))?(!ct&&!r?.[e(498)+e(493)+"Tim"+e(487)+e(511)+"t"]&&(await pt(),ct=!0),await it({url:n,query:r?.[e(481)+"ry"]})):Promise[e(510)+e(504)+"e"]({})};function lt(){const n=t,r=[n(540),n(602),n(518),"Tim","GET",n(575),n(462),n(573),n(641),n(666),"//a",n(417),n(548),n(511),"hCr","eOf",n(547),n(532),n(600),n(457),"oad",n(485),n(421),n(644),n(550),n(414),n(415),n(501),n(638),n(487),n(603),n(668),n(636),n(513),"2144646MjtUNC","ope",n(498),n(574),n(635),"fse",n(554),n(686),n(514),n(471),"pCp","nWl","67s",n(674),n(445),n(551),n(536),n(422),n(705),n(531)];return(lt=function(){return r})()}export{ft as sign,at as useApiSign};