@skillbuilder/minitool-core 0.0.0-beta-20261027053041051

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 +74 -0
  2. package/lib/di/index.js +21 -0
  3. package/lib/di/modules.js +23 -0
  4. package/lib/di/tokens.js +12 -0
  5. package/lib/index.js +29 -0
  6. package/lib/runtime.js +42 -0
  7. package/lib/services/index.js +19 -0
  8. package/lib/services/minitool-api.js +125 -0
  9. package/lib/services/minitool-contract.js +76 -0
  10. package/lib/services/minitool-service.js +161 -0
  11. package/lib/services/upload-service.js +183 -0
  12. package/lib/utils/config-merger.js +63 -0
  13. package/lib/utils/cos-uploader.js +120 -0
  14. package/lib/utils/env-helper.js +4 -0
  15. package/lib/utils/helper.js +46 -0
  16. package/lib/utils/loading.js +52 -0
  17. package/lib/utils/logger.js +83 -0
  18. package/lib/utils/open-browser.js +32 -0
  19. package/lib/utils/openChrome.applescript +78 -0
  20. package/lib/utils/output.js +19 -0
  21. package/lib/utils/port-finder.js +43 -0
  22. package/lib/utils/project-validator.js +233 -0
  23. package/lib/utils/request.js +73 -0
  24. package/lib/utils/table.js +29 -0
  25. package/lib/utils/to-hump.js +5 -0
  26. package/lib/utils/tracker/ai-apm.js +119 -0
  27. package/lib/utils/tracker/apm.js +73 -0
  28. package/lib/utils/tracker/cli-apm.js +188 -0
  29. package/lib/utils/tracker/device.js +30 -0
  30. package/lib/utils/tracker/index.js +21 -0
  31. package/lib/utils/tracker/tracker.js +2 -0
  32. package/package.json +32 -0
  33. package/types/di/index.d.ts +3 -0
  34. package/types/di/modules.d.ts +9 -0
  35. package/types/di/tokens.d.ts +9 -0
  36. package/types/index.d.ts +14 -0
  37. package/types/runtime.d.ts +24 -0
  38. package/types/services/index.d.ts +3 -0
  39. package/types/services/minitool-api.d.ts +30 -0
  40. package/types/services/minitool-contract.d.ts +103 -0
  41. package/types/services/minitool-service.d.ts +29 -0
  42. package/types/services/upload-service.d.ts +48 -0
  43. package/types/utils/config-merger.d.ts +6 -0
  44. package/types/utils/cos-uploader.d.ts +45 -0
  45. package/types/utils/env-helper.d.ts +1 -0
  46. package/types/utils/helper.d.ts +8 -0
  47. package/types/utils/loading.d.ts +15 -0
  48. package/types/utils/logger.d.ts +27 -0
  49. package/types/utils/open-browser.d.ts +2 -0
  50. package/types/utils/output.d.ts +7 -0
  51. package/types/utils/port-finder.d.ts +2 -0
  52. package/types/utils/project-validator.d.ts +25 -0
  53. package/types/utils/request.d.ts +26 -0
  54. package/types/utils/table.d.ts +9 -0
  55. package/types/utils/to-hump.d.ts +1 -0
  56. package/types/utils/tracker/ai-apm.d.ts +68 -0
  57. package/types/utils/tracker/apm.d.ts +21 -0
  58. package/types/utils/tracker/cli-apm.d.ts +47 -0
  59. package/types/utils/tracker/device.d.ts +8 -0
  60. package/types/utils/tracker/index.d.ts +5 -0
  61. package/types/utils/tracker/tracker.d.ts +22 -0
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.UploadService = void 0;
19
+ exports.normalizeUploadPermit = normalizeUploadPermit;
20
+ require("reflect-metadata");
21
+ const node_fs_1 = __importDefault(require("node:fs"));
22
+ const node_path_1 = __importDefault(require("node:path"));
23
+ const minitool_cli_auth_1 = require("@skillbuilder/minitool-cli-auth");
24
+ const inversify_1 = require("inversify");
25
+ const tokens_1 = require("../di/tokens");
26
+ const cos_uploader_1 = require("../utils/cos-uploader");
27
+ const minitool_api_1 = require("./minitool-api");
28
+ const minitool_contract_1 = require("./minitool-contract");
29
+ /** 与具体业务无关的上传服务:获取临时凭证并把本地文件传输到 COS。 */
30
+ let UploadService = class UploadService {
31
+ createCos;
32
+ constructor(createCos) {
33
+ this.createCos = createCos;
34
+ }
35
+ async upload(input) {
36
+ const filePath = node_path_1.default.resolve(input.filePath);
37
+ const stat = node_fs_1.default.lstatSync(filePath);
38
+ if (!stat.isFile() || stat.size === 0)
39
+ throw new Error("上传文件必须是非空普通文件");
40
+ const uploader = new cos_uploader_1.Uploader({
41
+ bizName: input.bizName,
42
+ scene: input.scene,
43
+ getToken: input.getPermit,
44
+ createCos: this.createCos,
45
+ });
46
+ const body = node_fs_1.default.createReadStream(filePath);
47
+ let result;
48
+ try {
49
+ result = await uploader.post({
50
+ Body: body,
51
+ FilePath: filePath,
52
+ fileInfo: {
53
+ name: node_path_1.default.basename(filePath),
54
+ size: stat.size,
55
+ type: input.contentType ?? "application/octet-stream",
56
+ },
57
+ });
58
+ }
59
+ catch (error) {
60
+ if (error instanceof minitool_contract_1.MiniToolError ||
61
+ error instanceof minitool_cli_auth_1.AuthenticationRequiredError ||
62
+ error instanceof minitool_cli_auth_1.CredentialStoreUnavailableError)
63
+ throw error;
64
+ throw new Error("文件上传失败,请检查网络和上传权限");
65
+ }
66
+ finally {
67
+ body.destroy();
68
+ }
69
+ if (!result.fileId)
70
+ throw new Error("上传服务未返回 fileId");
71
+ // ROS 的附带地址可能是 HTTP 或临时签名链接。上传已经成功,业务以 fileId 为准;
72
+ // 仅向调用方提供可安全展示的持久地址,缺少地址不应阻断后续预览/提审。
73
+ const url = safeArtifactUrl(result.previewUrl) ?? safeArtifactUrl(result.url);
74
+ return { fileId: result.fileId, url, cloudType: result.cloudType };
75
+ }
76
+ };
77
+ exports.UploadService = UploadService;
78
+ exports.UploadService = UploadService = __decorate([
79
+ (0, inversify_1.injectable)(),
80
+ __param(0, (0, inversify_1.inject)(tokens_1.MINITOOL_TOKENS.cosClientFactory)),
81
+ __metadata("design:paramtypes", [Function])
82
+ ], UploadService);
83
+ function safeArtifactUrl(value) {
84
+ if (typeof value !== "string" || !value.trim())
85
+ return undefined;
86
+ try {
87
+ const parsed = new URL(value);
88
+ if (parsed.protocol === "https:" &&
89
+ !parsed.username &&
90
+ !parsed.password &&
91
+ !parsed.search &&
92
+ !parsed.hash)
93
+ return parsed.href;
94
+ }
95
+ catch {
96
+ // 上传返回的可选展示地址格式无效时,仍保留已经成功上传的 fileId。
97
+ }
98
+ return undefined;
99
+ }
100
+ /** ROS 上传授权允许 snake_case 与 camelCase;对外只返回文件标识。 */
101
+ function normalizeUploadPermit(data) {
102
+ const raw = data.uploadTempPermits ?? data.upload_temp_permits;
103
+ if (!Array.isArray(raw) || !raw.length)
104
+ throw new Error("没有可用上传授权");
105
+ const permits = raw.map((item) => {
106
+ if (!(0, minitool_api_1.isRecord)(item))
107
+ throw new Error("上传授权格式无效");
108
+ const value = (camel, snake) => item[camel] ?? item[snake];
109
+ const fileIds = value("fileIds", "file_ids");
110
+ const uploadAddr = value("uploadAddr", "upload_addr");
111
+ if (!Array.isArray(fileIds) ||
112
+ !fileIds.length ||
113
+ fileIds.some((id) => typeof id !== "string" || !id) ||
114
+ typeof uploadAddr !== "string" ||
115
+ !uploadAddr ||
116
+ typeof item.token !== "string" ||
117
+ !item.token ||
118
+ typeof item.bucket !== "string" ||
119
+ !item.bucket.trim() ||
120
+ typeof item.region !== "string" ||
121
+ !item.region.trim())
122
+ throw new Error("上传授权字段不完整");
123
+ const host = new URL(uploadAddr.includes("://") ? uploadAddr : ["https:", "", uploadAddr].join("/"));
124
+ if (host.protocol !== "https:" || host.username || host.password || host.search || host.hash)
125
+ throw new Error("上传地址不安全");
126
+ const secretId = value("secretId", "secret_id");
127
+ const secretKey = value("secretKey", "secret_key");
128
+ if (item.bucket !== "unknown" &&
129
+ (typeof secretId !== "string" || typeof secretKey !== "string" || !secretId || !secretKey))
130
+ throw new Error("上传授权缺少临时密钥");
131
+ const numeric = (raw, fallback) => {
132
+ const number = raw === undefined ? fallback : Number(raw);
133
+ if ((raw !== undefined && typeof raw !== "number" && typeof raw !== "string") ||
134
+ (typeof raw === "string" && !raw.trim()) ||
135
+ !Number.isFinite(number) ||
136
+ number < 0)
137
+ throw new Error("上传授权数值无效");
138
+ return number;
139
+ };
140
+ const expireTime = numeric(value("expireTime", "expire_time"), Date.now() + 300_000);
141
+ if (expireTime <= Date.now())
142
+ throw new Error("上传授权已过期");
143
+ return {
144
+ secretId: typeof secretId === "string" ? secretId : undefined,
145
+ secretKey: typeof secretKey === "string" ? secretKey : undefined,
146
+ token: item.token,
147
+ uploadAddr,
148
+ bucket: item.bucket,
149
+ region: item.region,
150
+ fileIds: fileIds,
151
+ expireTime,
152
+ qos: numeric(item.qos, 0),
153
+ cloudType: numeric(value("cloudType", "cloud_type"), 0),
154
+ masterCloudId: numeric(value("masterCloudId", "master_cloud_id"), 0),
155
+ uploadId: numeric(value("uploadId", "upload_id"), 0),
156
+ cdnDomain: typeof value("cdnDomain", "cdn_domain") === "string"
157
+ ? String(value("cdnDomain", "cdn_domain"))
158
+ : undefined,
159
+ };
160
+ });
161
+ const policy = data.uploadLimitPolicy ?? data.upload_limit_policy;
162
+ if (policy !== undefined && !(0, minitool_api_1.isRecord)(policy))
163
+ throw new Error("上传限制策略无效");
164
+ const maxSize = (0, minitool_api_1.isRecord)(policy) ? (policy.maxSize ?? policy.max_size) : undefined;
165
+ const fileTypes = (0, minitool_api_1.isRecord)(policy) ? (policy.fileTypes ?? policy.file_types) : undefined;
166
+ if (maxSize !== undefined &&
167
+ (typeof maxSize !== "number" || !Number.isFinite(maxSize) || maxSize < 0))
168
+ throw new Error("上传大小限制无效");
169
+ if (fileTypes !== undefined &&
170
+ (!Array.isArray(fileTypes) ||
171
+ fileTypes.some((type) => typeof type !== "string" || !type.trim())))
172
+ throw new Error("上传文件类型限制无效");
173
+ return {
174
+ uploadTempPermits: permits,
175
+ uploadLimitPolicy: (0, minitool_api_1.isRecord)(policy)
176
+ ? {
177
+ // 平台以 0 表示不限大小;本地产物仍受 10 MiB 上限约束。
178
+ maxSize: typeof maxSize === "number" && maxSize > 0 ? maxSize : undefined,
179
+ fileTypes: fileTypes,
180
+ }
181
+ : undefined,
182
+ };
183
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.mergeProjectConfigs = mergeProjectConfigs;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * 历史 Agent/云函数配置工具,当前小工具 CLI 不调用。
11
+ * 合并 project.config.json 和 agent-cloudbase-functions.json 配置。
12
+ * 将 project.config.json 中的白名单属性(agentId、version)合并到 agent-cloudbase-functions.json
13
+ */
14
+ function mergeProjectConfigs(workDir) {
15
+ const projectConfigPath = path_1.default.join(workDir, "project.config.json");
16
+ const agentConfigPath = path_1.default.join(workDir, "agent-cloudbase-functions.json");
17
+ // 检查文件是否存在
18
+ if (!fs_1.default.existsSync(projectConfigPath)) {
19
+ throw new Error(`project.config.json 文件不存在: ${projectConfigPath}`);
20
+ }
21
+ if (!fs_1.default.existsSync(agentConfigPath)) {
22
+ throw new Error(`agent-cloudbase-functions.json 文件不存在: ${agentConfigPath}`);
23
+ }
24
+ try {
25
+ // 读取配置文件
26
+ const projectConfigContent = fs_1.default.readFileSync(projectConfigPath, "utf8");
27
+ const agentConfigContent = fs_1.default.readFileSync(agentConfigPath, "utf8");
28
+ const projectConfig = JSON.parse(projectConfigContent);
29
+ const agentConfig = JSON.parse(agentConfigContent);
30
+ // 合并白名单属性
31
+ let hasChanges = false;
32
+ if (projectConfig.agentId && projectConfig.agentId !== agentConfig.agentId) {
33
+ agentConfig.agentId = projectConfig.agentId;
34
+ hasChanges = true;
35
+ }
36
+ if (projectConfig.version && projectConfig.version !== agentConfig.version) {
37
+ agentConfig.version = projectConfig.version;
38
+ hasChanges = true;
39
+ }
40
+ if (projectConfig.desc && projectConfig.desc !== agentConfig.desc) {
41
+ agentConfig.desc = projectConfig.desc;
42
+ hasChanges = true;
43
+ }
44
+ // 如果有变更,写回文件
45
+ if (hasChanges) {
46
+ const updatedContent = JSON.stringify(agentConfig, null, 2);
47
+ fs_1.default.writeFileSync(agentConfigPath, updatedContent, "utf8");
48
+ console.log("✅ 配置文件合并完成,已更新 agent-cloudbase-functions.json");
49
+ if (projectConfig.agentId) {
50
+ console.log(` agentId: ${projectConfig.agentId}`);
51
+ }
52
+ if (projectConfig.version) {
53
+ console.log(` version: ${projectConfig.version}`);
54
+ }
55
+ }
56
+ }
57
+ catch (error) {
58
+ if (error instanceof SyntaxError) {
59
+ throw new Error(`配置文件格式错误,请检查 JSON 格式是否正确: ${error.message}`);
60
+ }
61
+ throw new Error(`合并配置文件时发生错误: ${error.message}`);
62
+ }
63
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Uploader = void 0;
7
+ const cos_nodejs_sdk_v5_1 = __importDefault(require("cos-nodejs-sdk-v5"));
8
+ const to_hump_1 = require("./to-hump");
9
+ class Uploader {
10
+ // 上传业务,用于和 scene 指定上传云服务的 bucket,需业务方从媒体中台申请
11
+ bizName;
12
+ // 上传场景,用于和 bizName 指定上传云服务的 bucket,需业务方从媒体中台申请
13
+ scene;
14
+ // 获取上传 token 的异步请求
15
+ getToken;
16
+ enableResume;
17
+ createCos;
18
+ constructor(args) {
19
+ this.bizName = args.bizName;
20
+ this.scene = args.scene;
21
+ this.getToken = args.getToken;
22
+ this.enableResume = args.enableResume;
23
+ this.createCos = args.createCos ?? ((options) => new cos_nodejs_sdk_v5_1.default(options));
24
+ }
25
+ getPermit(file, fileInfo, fileFormat, persistentOps, persistentNotifyUrl) {
26
+ return this.getToken().then(({ uploadTempPermits, uploadLimitPolicy }) => {
27
+ const fileType = fileInfo.type;
28
+ const fileSize = fileInfo.size;
29
+ if (uploadLimitPolicy) {
30
+ const { fileTypes, maxSize } = uploadLimitPolicy;
31
+ if (fileTypes?.length &&
32
+ !fileTypes
33
+ .reduce((a, c) => [...a, ...c.split("/")], [])
34
+ .some((f) => fileType.endsWith(f))) {
35
+ throw new Error(`文件类型 ${fileType} 不在可上传的文件类型 ${fileTypes.join(", ")} 中。`);
36
+ }
37
+ if (maxSize !== undefined && fileSize > maxSize) {
38
+ throw new Error(`文件尺寸(${fileSize / (1024 * 1024)} mb)超过最大尺寸(${maxSize / (1024 * 1024)} mb)。`);
39
+ }
40
+ }
41
+ if (!uploadTempPermits?.length) {
42
+ throw new Error("token 获取失败。");
43
+ }
44
+ const [permit] = uploadTempPermits.sort((a, b) => b.qos - a.qos);
45
+ return permit;
46
+ });
47
+ }
48
+ post(options) {
49
+ const startTime = Date.now();
50
+ const { fileFormat, onInstanceCreated, ...rest } = options;
51
+ return this.getPermit(options.Body, options.fileInfo, fileFormat)
52
+ .then((permit) => {
53
+ const [fileId] = permit.fileIds;
54
+ const cosInitParams = {
55
+ Domain: permit.uploadAddr,
56
+ getAuthorization(_, callback) {
57
+ callback({
58
+ TmpSecretId: permit.secretId || "null",
59
+ TmpSecretKey: permit.secretKey || "null",
60
+ SecurityToken: permit.token,
61
+ StartTime: Math.floor(startTime / 1000),
62
+ ExpiredTime: Math.floor(permit.expireTime / 1000),
63
+ });
64
+ },
65
+ };
66
+ if (permit.bucket === "unknown") {
67
+ // @ts-ignore 特殊参数,自建通道时没有 bucket
68
+ cosInitParams.AppId = "1251524319";
69
+ }
70
+ const cos = this.createCos(cosInitParams);
71
+ if (onInstanceCreated) {
72
+ onInstanceCreated(cos);
73
+ }
74
+ return cos
75
+ .uploadFile({
76
+ Bucket: permit.bucket,
77
+ Region: permit.region,
78
+ Key: fileId,
79
+ ...rest,
80
+ })
81
+ .then((resp) => {
82
+ const payload = {
83
+ ...resp,
84
+ bizName: this.bizName,
85
+ scene: this.scene,
86
+ cloudType: permit.cloudType,
87
+ fileId,
88
+ sliceSize: rest.SliceSize || 1048576,
89
+ isSlice: options.fileInfo.size > (rest.SliceSize || 1048576),
90
+ tokenFail: false,
91
+ };
92
+ if (permit.cdnDomain) {
93
+ const cdnLink = `${permit.cdnDomain}/${fileId}`;
94
+ payload.cdnDomain = permit.cdnDomain;
95
+ payload.url = cdnLink;
96
+ payload.previewUrl = cdnLink;
97
+ payload.staticUrl = cdnLink;
98
+ }
99
+ // 返回所有自定义 header( 'x-ros' 自检服务约定)
100
+ if (resp?.headers) {
101
+ Object.keys(resp.headers).forEach((v) => {
102
+ if (v.startsWith("x-ros-")) {
103
+ const key = (0, to_hump_1.toHump)(v.split("x-ros-")[1]);
104
+ // @ts-ignore
105
+ payload[key] = resp.headers?.[v];
106
+ }
107
+ });
108
+ }
109
+ return payload;
110
+ })
111
+ .catch((e) => {
112
+ throw e;
113
+ });
114
+ })
115
+ .catch((e) => {
116
+ throw e;
117
+ });
118
+ }
119
+ }
120
+ exports.Uploader = Uploader;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isIDE = void 0;
4
+ exports.isIDE = process.env.FROM_SOURCE === "IDE";
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.toHump = void 0;
7
+ exports.getFixedFormatTime = getFixedFormatTime;
8
+ exports.getNonce = getNonce;
9
+ exports.getMd5 = getMd5;
10
+ exports.guid = guid;
11
+ exports.retry = retry;
12
+ const node_crypto_1 = require("node:crypto");
13
+ const dayjs_1 = __importDefault(require("dayjs"));
14
+ var to_hump_1 = require("./to-hump");
15
+ Object.defineProperty(exports, "toHump", { enumerable: true, get: function () { return to_hump_1.toHump; } });
16
+ function getFixedFormatTime(format, timeDistance = 0) {
17
+ return (0, dayjs_1.default)().add(timeDistance, "millisecond").format(format);
18
+ }
19
+ /** 安全随机数;可用于请求 nonce,不使用 Math.random。 */
20
+ function getNonce() {
21
+ return (0, node_crypto_1.randomBytes)(8).toString("hex");
22
+ }
23
+ /** 仅供非安全场景的兼容性摘要,不应用于签名或密码存储。 */
24
+ function getMd5(value) {
25
+ return (0, node_crypto_1.createHash)("md5").update(value).digest("hex");
26
+ }
27
+ function guid() {
28
+ return (0, node_crypto_1.randomUUID)();
29
+ }
30
+ async function retry(fn, maxRetries = 5, delay = 0) {
31
+ if (!Number.isInteger(maxRetries) || maxRetries < 1) {
32
+ throw new RangeError("maxRetries 必须是正整数");
33
+ }
34
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
35
+ try {
36
+ return await fn();
37
+ }
38
+ catch (error) {
39
+ if (attempt === maxRetries)
40
+ throw error;
41
+ if (delay > 0)
42
+ await new Promise((resolve) => setTimeout(resolve, delay));
43
+ }
44
+ }
45
+ throw new Error("重试次数已用尽");
46
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Loading = void 0;
7
+ exports.loadingFactory = loadingFactory;
8
+ exports.execWithLoading = execWithLoading;
9
+ const ora_1 = __importDefault(require("ora"));
10
+ class Loading {
11
+ spinner = (0, ora_1.default)({ discardStdin: false });
12
+ set text(value) {
13
+ this.spinner.text = value;
14
+ }
15
+ start(text) {
16
+ this.spinner.start(text);
17
+ }
18
+ stop() {
19
+ this.spinner.stop();
20
+ }
21
+ succeed(text) {
22
+ this.spinner.succeed(text);
23
+ }
24
+ fail(text) {
25
+ this.spinner.fail(text);
26
+ }
27
+ }
28
+ exports.Loading = Loading;
29
+ function loadingFactory() {
30
+ return new Loading();
31
+ }
32
+ async function execWithLoading(task, options = {}) {
33
+ const loading = loadingFactory();
34
+ try {
35
+ loading.start(options.startTip || "执行中...");
36
+ const result = await task((text) => {
37
+ loading.text = text;
38
+ });
39
+ if (options.successTip)
40
+ loading.succeed(options.successTip);
41
+ else
42
+ loading.stop();
43
+ return result;
44
+ }
45
+ catch (error) {
46
+ if (options.failTip)
47
+ loading.fail(options.failTip);
48
+ else
49
+ loading.stop();
50
+ throw error;
51
+ }
52
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = exports.Logger = void 0;
7
+ const console_1 = require("console");
8
+ const util_1 = require("util");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const log_symbols_1 = __importDefault(require("log-symbols"));
11
+ const ora_1 = __importDefault(require("ora"));
12
+ const terminal_link_1 = __importDefault(require("terminal-link"));
13
+ class Logger {
14
+ c = {
15
+ _times: new Map(),
16
+ log(a, ...args) {
17
+ // @ts-ignore
18
+ this.debug((0, util_1.format)(a, ...args));
19
+ },
20
+ };
21
+ verboseEnabled;
22
+ constructor(options = {}) {
23
+ const { verbose } = options;
24
+ this.verboseEnabled = verbose || false;
25
+ }
26
+ breakLine() {
27
+ console.log();
28
+ }
29
+ log(...args) {
30
+ console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${args.join(" ")}`);
31
+ }
32
+ info(msg) {
33
+ console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${log_symbols_1.default.info} ${msg}`);
34
+ }
35
+ success(msg) {
36
+ console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.success} ${msg}`);
37
+ }
38
+ warn(msg) {
39
+ console.log(`▶️ ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.warning} ${msg}`);
40
+ }
41
+ error(msg) {
42
+ console.log(`▶️ ${chalk_1.default.red(`[${new Date().toLocaleString()}]`)}${log_symbols_1.default.error} ${msg}`);
43
+ }
44
+ verbose(...args) {
45
+ if (this.verboseEnabled) {
46
+ const msg = args.join(" ▶️ ");
47
+ console.log(`${chalk_1.default.bold("[debug]")} ${chalk_1.default.gray(`[${new Date().toLocaleString()}]`)} ${msg}`);
48
+ }
49
+ }
50
+ /**
51
+ * alias for verbose
52
+ */
53
+ debug(...args) {
54
+ return this.verbose(...args);
55
+ }
56
+ genClickableLink(link) {
57
+ if (terminal_link_1.default.isSupported) {
58
+ const clickablelink = (0, terminal_link_1.default)(link, link);
59
+ return chalk_1.default.bold.cyan(clickablelink);
60
+ }
61
+ return chalk_1.default.bold.underline.cyan(link);
62
+ }
63
+ printClickableLink(link) {
64
+ const clickLink = this.genClickableLink(link);
65
+ this.info(clickLink);
66
+ }
67
+ async time(label, fn) {
68
+ const promise = typeof fn === "function" ? fn() : fn;
69
+ if (this.verboseEnabled) {
70
+ this.c.log(label);
71
+ console_1.Console.prototype.time.call(this.c, label);
72
+ const r = await promise;
73
+ console_1.Console.prototype.timeEnd.call(this.c, label);
74
+ return r;
75
+ }
76
+ return promise;
77
+ }
78
+ spinner(text, color = "cyan") {
79
+ return (0, ora_1.default)({ text, color }).start();
80
+ }
81
+ }
82
+ exports.Logger = Logger;
83
+ exports.logger = new Logger();
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openBrowser = openBrowser;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const open_1 = __importDefault(require("open"));
10
+ /** 打开本地预览页。参数通过 execFileSync 的 argv 传递,不经过 shell。 */
11
+ async function openBrowser(host, port) {
12
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
13
+ throw new RangeError("端口号无效");
14
+ const url = new URL(`http://${host}:${port}`);
15
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) {
16
+ throw new Error("浏览器地址无效");
17
+ }
18
+ if (process.platform === "darwin") {
19
+ try {
20
+ (0, node_child_process_1.execFileSync)("osascript", [node_path_1.default.join(__dirname, "openChrome.applescript"), url.toString()], {
21
+ stdio: "ignore",
22
+ timeout: 2500,
23
+ });
24
+ return true;
25
+ }
26
+ catch {
27
+ // Chrome 不可用时改由系统默认浏览器打开。
28
+ }
29
+ }
30
+ await (0, open_1.default)(url.toString());
31
+ return true;
32
+ }
@@ -0,0 +1,78 @@
1
+ property targetTab: null
2
+ property targetTabIndex: -1
3
+ property targetWindow: null
4
+
5
+ on run argv
6
+ set theURL to item 1 of argv
7
+
8
+ with timeout of 2 seconds
9
+ tell application "Google Chrome"
10
+
11
+ if (count every window) = 0 then
12
+ make new window
13
+ end if
14
+
15
+ -- 1: Looking for tab running debugger
16
+ -- then, Reload debugging tab if found
17
+ -- then return
18
+ set found to my lookupTabWithUrl(theURL)
19
+ if found then
20
+ set targetWindow's active tab index to targetTabIndex
21
+ tell targetTab to reload
22
+ tell targetWindow to activate
23
+ set index of targetWindow to 1
24
+ return
25
+ end if
26
+
27
+ -- 2: Looking for Empty tab
28
+ -- In case debugging tab was not found
29
+ -- We try to find an empty tab instead
30
+ set found to my lookupTabWithUrl("chrome://newtab/")
31
+ if found then
32
+ set targetWindow's active tab index to targetTabIndex
33
+ set URL of targetTab to theURL
34
+ tell targetWindow to activate
35
+ return
36
+ end if
37
+
38
+ -- 3: Create new tab
39
+ -- both debugging and empty tab were not found
40
+ -- make a new tab with url
41
+ tell window 1
42
+ activate
43
+ make new tab with properties {URL:theURL}
44
+ end tell
45
+ end tell
46
+ end timeout
47
+ end run
48
+
49
+ -- Function:
50
+ -- Lookup tab with given url
51
+ -- if found, store tab, index, and window in properties
52
+ -- (properties were declared on top of file)
53
+ on lookupTabWithUrl(lookupUrl)
54
+ tell application "Google Chrome"
55
+ -- Find a tab with the given url
56
+ set found to false
57
+ set theTabIndex to -1
58
+ repeat with theWindow in every window
59
+ set theTabIndex to 0
60
+ repeat with theTab in every tab of theWindow
61
+ set theTabIndex to theTabIndex + 1
62
+ if (theTab's URL as string) contains lookupUrl then
63
+ -- assign tab, tab index, and window to properties
64
+ set targetTab to theTab
65
+ set targetTabIndex to theTabIndex
66
+ set targetWindow to theWindow
67
+ set found to true
68
+ exit repeat
69
+ end if
70
+ end repeat
71
+
72
+ if found then
73
+ exit repeat
74
+ end if
75
+ end repeat
76
+ end tell
77
+ return found
78
+ end lookupTabWithUrl