@kevisual/cnb 0.0.64 → 0.0.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/cnb",
3
- "version": "0.0.64",
3
+ "version": "0.0.66",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "basename": "/root/cnb",
@@ -20,6 +20,7 @@
20
20
  "bin": {
21
21
  "cnb": "bin/index.js",
22
22
  "cloud": "bin/index.js",
23
+ "cloud-live": "bin/live.js",
23
24
  "cloud-npc": "bin/npc.js"
24
25
  },
25
26
  "files": [
@@ -32,7 +33,7 @@
32
33
  "packageManager": "pnpm@10.33.0",
33
34
  "type": "module",
34
35
  "devDependencies": {
35
- "@ai-sdk/openai-compatible": "^2.0.37",
36
+ "@ai-sdk/openai-compatible": "^2.0.38",
36
37
  "@kevisual/ai": "^0.0.28",
37
38
  "@kevisual/api": "^0.0.65",
38
39
  "@kevisual/code-builder": "^0.0.7",
@@ -40,14 +41,15 @@
40
41
  "@kevisual/dts": "^0.0.4",
41
42
  "@kevisual/remote-app": "^0.0.7",
42
43
  "@kevisual/types": "^0.0.12",
43
- "@opencode-ai/plugin": "^1.3.13",
44
+ "@opencode-ai/plugin": "^1.3.15",
44
45
  "@types/bun": "^1.3.11",
45
- "@types/node": "^25.5.0",
46
+ "@types/node": "^25.5.2",
46
47
  "@types/ws": "^8.18.1",
47
- "ai": "^6.0.143",
48
+ "ai": "^6.0.146",
48
49
  "commander": "^14.0.3",
49
50
  "dayjs": "^1.11.20",
50
- "dotenv": "^17.4.0",
51
+ "dotenv": "^17.4.1",
52
+ "fast-glob": "^3.3.3",
51
53
  "zod": "^4.3.6"
52
54
  },
53
55
  "publishConfig": {
@@ -60,8 +62,9 @@
60
62
  "@kevisual/query": "^0.0.55",
61
63
  "@kevisual/router": "^0.2.5",
62
64
  "@kevisual/use-config": "^1.0.30",
63
- "@opencode-ai/sdk": "^1.3.13",
65
+ "@opencode-ai/sdk": "^1.3.15",
64
66
  "es-toolkit": "^1.45.1",
67
+ "form-data": "^4.0.5",
65
68
  "nanoid": "^5.1.7",
66
69
  "unstorage": "^1.17.5",
67
70
  "ws": "npm:@kevisual/ws"
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ import { AiBase } from "./ai/index.ts";
10
10
  import { RepoLabel, IssueLabel } from "./labels/index.ts";
11
11
  import { RegistryPackage, PackageManagement } from "./package/index.ts";
12
12
  import { Organization } from "./org/index.ts";
13
+ import { Release } from "./release/index.ts";
13
14
 
14
15
  type CNBOptions = CNBCoreOptions<{
15
16
  }>;
@@ -33,6 +34,7 @@ export class CNB extends CNBCore {
33
34
  };
34
35
  org!: Organization;
35
36
  dashboard!: Dashboard;
37
+ release!: Release;
36
38
  constructor(options: CNBOptions) {
37
39
  super({ ...options, token: options.token, cookie: options.cookie, cnb: options.cnb });
38
40
  this.init(options);
@@ -61,6 +63,7 @@ export class CNB extends CNBCore {
61
63
  };
62
64
  this.org = new Organization(options);
63
65
  this.dashboard = new Dashboard(options);
66
+ this.release = new Release(options);
64
67
  }
65
68
  setToken(token: string) {
66
69
  this.token = token;
@@ -76,6 +79,7 @@ export class CNB extends CNBCore {
76
79
  this.packages.package.token = token;
77
80
  this.org.token = token;
78
81
  this.dashboard.token = token;
82
+ this.release.token = token;
79
83
  }
80
84
  setCookie(cookie: string) {
81
85
  this.cookie = cookie;
@@ -91,6 +95,7 @@ export class CNB extends CNBCore {
91
95
  this.packages.package.cookie = cookie;
92
96
  this.org.cookie = cookie;
93
97
  this.dashboard.cookie = cookie;
98
+ this.release.cookie = cookie;
94
99
  }
95
100
  getCNBVersion = getCNBVersion
96
101
  }
@@ -118,5 +123,6 @@ type VersionInfo = {
118
123
 
119
124
  export * from './issue/npc/env.ts'
120
125
  export * from './labels/index.ts'
126
+ export * from './release/index.ts'
121
127
  export * from './package/index.ts'
122
128
  export * from './org/index.ts'
@@ -0,0 +1,254 @@
1
+ import { Result } from '@kevisual/query';
2
+ import { CNBCore, CNBCoreOptions } from "../cnb-core.ts";
3
+
4
+ export type UserInfo = {
5
+ /** 用户登录名 */
6
+ login: string;
7
+ /** 用户 ID */
8
+ id: string;
9
+ /** 用户头像 URL */
10
+ avatar_url: string;
11
+ };
12
+
13
+ export type ReleaseAsset = {
14
+ /** 附件唯一标识符 */
15
+ id: string;
16
+ /** 附件名称 */
17
+ name: string;
18
+ /** 附件路径 */
19
+ path: string;
20
+ /** 附件大小(字节) */
21
+ size: number;
22
+ /** 附件内容类型 */
23
+ content_type: string;
24
+ /** 下载次数 */
25
+ download_count: number;
26
+ /** 附件哈希算法 */
27
+ hash_algo: string;
28
+ /** 附件哈希值 */
29
+ hash_value: string;
30
+ /** API 下载 URL(通过 API 域名,用于程序化下载) */
31
+ url: string;
32
+ /** 浏览器下载 URL(通过主域名,用于用户直接访问) */
33
+ brower_download_url: string;
34
+ /** 附件上传者信息 */
35
+ uploader: UserInfo;
36
+ /** 创建时间 */
37
+ created_at: string;
38
+ /** 更新时间 */
39
+ updated_at: string;
40
+ };
41
+
42
+ export type ReleaseItem = {
43
+ /** 版本唯一标识符 */
44
+ id: string;
45
+ /** 版本标题 */
46
+ name: string;
47
+ /** 标签名称 */
48
+ tag_name: string;
49
+ /** 标签与提交标识符 */
50
+ tag_commitish: string;
51
+ /** 版本描述 */
52
+ body: string;
53
+ /** 是否为草稿版本 */
54
+ draft: boolean;
55
+ /** 是否为预发布版本 */
56
+ prerelease: boolean;
57
+ /** 是否为最新版本 */
58
+ is_latest: boolean;
59
+ /** 作者信息 */
60
+ author: UserInfo;
61
+ /** 附件列表 */
62
+ assets: ReleaseAsset[];
63
+ /** 创建时间 */
64
+ created_at: string;
65
+ /** 版本发布时间 */
66
+ published_at: string;
67
+ /** 更新时间 */
68
+ updated_at: string;
69
+ };
70
+
71
+ export type PostReleaseForm = {
72
+ /** 标签名称 */
73
+ tag_name: string;
74
+ /** 目标提交哈希或分支名称 */
75
+ target_commitish?: string;
76
+ /** 版本标题 */
77
+ name?: string;
78
+ /** 版本描述 */
79
+ body?: string;
80
+ /** 是否为草稿版本 */
81
+ draft?: boolean;
82
+ /** 是否为预发布版本 */
83
+ prerelease?: boolean;
84
+ /** 是否设置为最新版本。可选值:`true`, `false`, `legacy` */
85
+ make_latest?: string;
86
+ };
87
+
88
+ export type PatchReleaseForm = {
89
+ /** 版本标题 */
90
+ name?: string;
91
+ /** 版本描述 */
92
+ body?: string;
93
+ /** 是否为草稿版本 */
94
+ draft?: boolean;
95
+ /** 是否为预发布版本 */
96
+ prerelease?: boolean;
97
+ /** 是否设置为最新版本。可选值:`true`, `false`, `legacy` */
98
+ make_latest?: string;
99
+ };
100
+
101
+ export type PostReleaseAssetUploadURLForm = {
102
+ /** 附件名称 */
103
+ asset_name: string;
104
+ /** 附件大小,单位为字节 */
105
+ size: number;
106
+ /** 附件存在时间,单位为天 */
107
+ ttl?: number;
108
+ /** 是否覆盖同名附件 */
109
+ overwrite?: boolean;
110
+ };
111
+
112
+ export type ReleaseAssetUploadURL = {
113
+ /** 附件上传 URL */
114
+ upload_url: string;
115
+ /** 附件上传确认验证 URL */
116
+ verify_url: string;
117
+ /** URL 过期时间,单位为秒 */
118
+ expires_in_sec: number;
119
+ };
120
+
121
+ export type ListReleasesParams = {
122
+ /** 分页页码,默认 1 */
123
+ page?: number;
124
+ /** 分页页大小,默认 10 */
125
+ page_size?: number;
126
+ query?: string;
127
+ };
128
+
129
+ export class Release extends CNBCore {
130
+ constructor(options: CNBCoreOptions) {
131
+ super(options);
132
+ }
133
+
134
+ /**
135
+ * 查询 release 列表
136
+ * @param repo 仓库路径
137
+ * @param params 分页参数
138
+ */
139
+ list(repo: string, params?: ListReleasesParams): Promise<Result<ReleaseItem[]>> {
140
+ return this.get({ url: `/${repo}/-/releases`, params });
141
+ }
142
+
143
+ /**
144
+ * 新增一个 release
145
+ * @param repo 仓库路径
146
+ * @param data 创建表单
147
+ */
148
+ create(repo: string, data: PostReleaseForm): Promise<Result<ReleaseItem>> {
149
+ return this.post({ url: `/${repo}/-/releases`, data });
150
+ }
151
+
152
+ /**
153
+ * 查询最新的 release
154
+ * @param repo 仓库路径
155
+ */
156
+ getLatest(repo: string): Promise<Result<ReleaseItem>> {
157
+ return this.get({ url: `/${repo}/-/releases/latest` });
158
+ }
159
+
160
+ /**
161
+ * 通过 tag 查询指定 release,包含附件信息
162
+ * @param repo 仓库路径
163
+ * @param tag 标签名称
164
+ */
165
+ getByTag(repo: string, tag: string): Promise<Result<ReleaseItem>> {
166
+ return this.get({ url: `/${repo}/-/releases/tags/${tag}` });
167
+ }
168
+
169
+ /**
170
+ * 根据 id 查询指定 release,包含附件信息
171
+ * @param repo 仓库路径
172
+ * @param releaseId 版本唯一标识符
173
+ */
174
+ getById(repo: string, releaseId: string): Promise<Result<ReleaseItem>> {
175
+ return this.get({ url: `/${repo}/-/releases/${releaseId}` });
176
+ }
177
+
178
+ /**
179
+ * 更新 release
180
+ * @param repo 仓库路径
181
+ * @param releaseId 版本唯一标识符
182
+ * @param data 更新表单
183
+ */
184
+ update(repo: string, releaseId: string, data: PatchReleaseForm): Promise<Result<any>> {
185
+ return this.patch({ url: `/${repo}/-/releases/${releaseId}`, data });
186
+ }
187
+
188
+ /**
189
+ * 删除指定的 release
190
+ * @param repo 仓库路径
191
+ * @param releaseId 版本唯一标识符
192
+ */
193
+ deleteRelease(repo: string, releaseId: string): Promise<Result<any>> {
194
+ return this.delete({ url: `/${repo}/-/releases/${releaseId}` });
195
+ }
196
+
197
+ /**
198
+ * 获取 release 附件上传 URL
199
+ * @param repo 仓库路径
200
+ * @param releaseId 版本唯一标识符
201
+ * @param data 附件上传表单
202
+ */
203
+ getAssetUploadURL(repo: string, releaseId: string, data: PostReleaseAssetUploadURLForm): Promise<Result<ReleaseAssetUploadURL>> {
204
+ return this.post({ url: `/${repo}/-/releases/${releaseId}/asset-upload-url`, data });
205
+ }
206
+
207
+ /**
208
+ * 确认 release 附件上传完成
209
+ * @param repo 仓库路径
210
+ * @param releaseId 版本唯一标识符
211
+ * @param uploadToken verify_url 字段提取的 upload_token
212
+ * @param assetPath verify_url 字段提取的 asset_path
213
+ * @param ttl 附件保持的天数(0 表示永久,最大 180)
214
+ */
215
+ confirmAssetUpload(repo: string, releaseId: string, uploadToken: string, assetPath: string, ttl?: number): Promise<Result<any>> {
216
+ const params = ttl !== undefined ? { ttl: String(ttl) } : undefined;
217
+ return this.post({ url: `/${repo}/-/releases/${releaseId}/asset-upload-confirmation/${uploadToken}/${assetPath}`, params });
218
+ }
219
+ async verifyUrlAsstetupload(url: string): Promise<Result<any>> {
220
+ return this.post({ url: url })
221
+ }
222
+
223
+ /**
224
+ * 查询指定的 release 附件
225
+ * @param repo 仓库路径
226
+ * @param releaseId 版本唯一标识符
227
+ * @param assetId 附件唯一标识符
228
+ */
229
+ getAsset(repo: string, releaseId: string, assetId: string): Promise<Result<ReleaseAsset>> {
230
+ return this.get({ url: `/${repo}/-/releases/${releaseId}/assets/${assetId}` });
231
+ }
232
+
233
+ /**
234
+ * 删除指定的 release 附件
235
+ * @param repo 仓库路径
236
+ * @param releaseId 版本唯一标识符
237
+ * @param assetId 附件唯一标识符
238
+ */
239
+ deleteAsset(repo: string, releaseId: string, assetId: string): Promise<Result<any>> {
240
+ return this.delete({ url: `/${repo}/-/releases/${releaseId}/assets/${assetId}` });
241
+ }
242
+
243
+ /**
244
+ * 获取 release 附件下载地址(302 重定向)
245
+ * @param repo 仓库路径
246
+ * @param tag 标签名称
247
+ * @param filename 文件名称
248
+ * @param share 是否生成可分享的下载地址(有效期 12 小时,最多下载 10 次)
249
+ */
250
+ getAssetDownloadURL(repo: string, tag: string, filename: string, share?: boolean): string {
251
+ const base = `${this.baseURL}/${repo}/-/releases/download/${tag}/${filename}`;
252
+ return share ? `${base}?share=true` : base;
253
+ }
254
+ }
@@ -0,0 +1,132 @@
1
+ import fs from 'node:fs';
2
+ import dayjs from 'dayjs';
3
+ import { CNB, ReleaseItem } from '../index.ts';
4
+ import { CustomError } from '@kevisual/router';
5
+ import { getFileSize, upload } from '@/upload/upload-base.ts';
6
+
7
+ type FileItemFile = {
8
+ type: 'file',
9
+ /**
10
+ * 文件绝对路径
11
+ */
12
+ filepath: string;
13
+ name: string
14
+ }
15
+ type FileItemContent = {
16
+ type: 'content',
17
+ content: string | Buffer;
18
+ name: string
19
+ }
20
+ type FileItem = FileItemFile | FileItemContent;
21
+
22
+ type ReleaseUploadOpts = {
23
+ files: FileItem[]
24
+ cnb: CNB
25
+ repo: string;
26
+ version?: string
27
+ }
28
+ export const createBodyByFileItem = (files: FileItem[]) => {
29
+ const createdAt = dayjs().format('YYYY-MM-DD HH:mm:ss');
30
+ const summary = `创建时间:${createdAt},文件数量:${files.length}`;
31
+ const header = '| name | type |\n| --- | --- |';
32
+ const rows = files.map(f => `| ${f.name} | ${f.type} |`).join('\n');
33
+ const content = `${summary}\n\n${header}\n${rows}`;
34
+ return content;
35
+ }
36
+ export const uploadReleaseFiles = async (opts: ReleaseUploadOpts) => {
37
+ // 第一步, 获取最近 10 个,看看是否存在已有版本,存在,则获取 id;
38
+ const cnb = opts.cnb;
39
+ const repo = opts.repo;
40
+ const version = opts.version || '1.0.0';
41
+ const files = opts.files || []
42
+ let body = createBodyByFileItem(files)
43
+ const release = await getReleases({ cnb, version, repo, body });
44
+ if (!release) {
45
+ throw new CustomError({ code: 500, message: 'release id 不存在' })
46
+ }
47
+ const releaseId = release?.id;
48
+ type UploadResult = {
49
+ name: string;
50
+ uploadSuccess: boolean;
51
+ message?: string;
52
+ }
53
+ let result: UploadResult[] = [];
54
+ for (const file of files) {
55
+ try {
56
+ let size = 0;
57
+ let _fileContent: string | Buffer | File;
58
+ if (file.type === 'file') {
59
+ const stat = fs.statSync(file.filepath);
60
+ size = stat.size;
61
+ _fileContent = fs.createReadStream(file.filepath) as unknown as File;
62
+ } else {
63
+ size = getFileSize(file.content)
64
+ _fileContent = file.content as unknown as string;
65
+ }
66
+ const res = await cnb.release.getAssetUploadURL(repo, releaseId, {
67
+ asset_name: file.name,
68
+ size: size,
69
+ overwrite: false
70
+ })
71
+ const uploadUrl = res.data?.upload_url || '';
72
+ const verifyUrl = res.data?.verify_url || '';
73
+ const uploadRes = await upload({ url: uploadUrl, file: _fileContent })
74
+ const verifyRes = await cnb.release.verifyUrlAsstetupload(verifyUrl);
75
+ result.push({
76
+ name: file.name,
77
+ uploadSuccess: uploadRes.code === 200 && verifyRes.code === 200,
78
+ })
79
+ } catch (e) {
80
+ console.error('error:', file.name, e)
81
+ result.push({
82
+ name: file.name,
83
+ uploadSuccess: false,
84
+ message: e.message || '上传失败'
85
+ })
86
+ continue;
87
+ }
88
+ }
89
+ return result;
90
+ }
91
+
92
+
93
+
94
+ const getReleases = async (opts: { cnb: CNB, version?: string, repo: string, body?: string }) => {
95
+ const cnb = opts.cnb;
96
+ const repo = opts.repo;
97
+ const version = opts.version || '1.0.0';
98
+ const body = opts?.body ?? '自动部署的前端页面'
99
+ const res = await cnb.release.list(repo, { page: 1, page_size: 10 });
100
+ let releases: ReleaseItem[] = []
101
+ if (res.code === 200) {
102
+ releases = res.data || [];
103
+ }
104
+ const exist = releases.find(r => r.tag_name === version);
105
+ if (exist) {
106
+ if (exist.body !== body) {
107
+ await cnb.release.update(repo, exist.id, {
108
+ body
109
+ })
110
+ }
111
+ return exist;
112
+ }
113
+ // 不存在创建一个
114
+ const commistRes = await cnb.repo.getLastCommit(repo);
115
+ let sha = '';
116
+ if (commistRes.code === 200) {
117
+ sha = commistRes.data?.sha || '';
118
+ }
119
+ const releaseRes = await cnb.release.create(repo, {
120
+ tag_name: version,
121
+ name: version,
122
+ target_commitish: sha,
123
+ body: body,
124
+ draft: false,
125
+ prerelease: false,
126
+ make_latest: "true",
127
+ });
128
+ if (releaseRes.code === 200) {
129
+ return releaseRes.data;
130
+ }
131
+ return null;
132
+ }
package/src/repo/index.ts CHANGED
@@ -37,13 +37,14 @@ export class Repo extends CNBCore {
37
37
  * @returns
38
38
  */
39
39
  async createCommit(repo: string, data: CreateCommitData): Promise<any> {
40
- const commitList = await this.getCommitList(repo, {
40
+ const res = await this.getCommitList(repo, {
41
41
  page: 1,
42
42
  page_size: 1,
43
- }, { useOrigin: true }).catch((err) => {
44
- // console.error("Error fetching commit list:", err);
45
- return []
46
43
  });
44
+ let commitList: CommitInfo[] = [];
45
+ if (res.code === 200) {
46
+ commitList = res.data;
47
+ }
47
48
  const preCommitSha = commitList.length > 0 ? commitList[0].sha : undefined;
48
49
  if (!data.parent_commit_sha && preCommitSha) {
49
50
  data.parent_commit_sha = preCommitSha;
@@ -89,7 +90,19 @@ export class Repo extends CNBCore {
89
90
  const url = `/${repo}/-/upload/files`
90
91
  return this.post({ url, data });
91
92
  }
92
- getCommitList(repo: string, params: { author?: string, commiter?: string, page?: number, page_size?: number, sha?: string, since?: string, until?: string }, opts?: RequestOptions): Promise<any> {
93
+ async getLastCommit(repo: string, opts?: RequestOptions): Promise<Result<CommitInfo>> {
94
+ const res = await this.getCommitList(repo, { page: 1, page_size: 1 }, opts);
95
+ let commitList: CommitInfo[] = [];
96
+ if (res.code === 200) {
97
+ commitList = res.data;
98
+ }
99
+ return {
100
+ code: res.code,
101
+ message: res.message,
102
+ data: commitList.length > 0 ? commitList[0] : null,
103
+ }
104
+ }
105
+ getCommitList(repo: string, params?: { author?: string, commiter?: string, page?: number, page_size?: number, sha?: string, since?: string, until?: string }, opts?: RequestOptions): Promise<Result<CommitInfo[]>> {
93
106
  const url = `/${repo}/-/git/commits`;
94
107
  return this.get({ url, params, ...opts });
95
108
  }
@@ -196,4 +209,41 @@ export type RepoItem = {
196
209
  star_time: string;
197
210
  pinned: boolean;
198
211
  pinned_time: string;
212
+ }
213
+
214
+ type CommitInfo = {
215
+ sha: string;
216
+ commit: {
217
+ author?: {
218
+ name: string;
219
+ email: string;
220
+ date: string;
221
+ },
222
+ committer: {
223
+ name: string;
224
+ email: string;
225
+ date: string;
226
+ },
227
+ message: string;
228
+ tree: { sha: string }
229
+ comment_count: number;
230
+ verification: {
231
+ verified: boolean;
232
+ reason: string;
233
+ signature: string;
234
+ payload: string;
235
+ verified_at: string;
236
+ }
237
+ },
238
+ author: {
239
+ nickname: string;
240
+ email: string;
241
+ is_npc: boolean
242
+ },
243
+ committer: {
244
+ nickname: string;
245
+ email: string;
246
+ is_npc: boolean
247
+ },
248
+ parents: { sha: string }[];
199
249
  }
@@ -0,0 +1,95 @@
1
+ import FormData from 'form-data';
2
+
3
+ export const DefaultIgnore = [
4
+ 'node_modules/**/*',
5
+ '.git/**/*',
6
+ '.DS_Store',
7
+ '.pnpm-store/**/*'
8
+ ]
9
+ export const handleResponse = async (err: any, res: any, opts?: { noResponse: boolean }) => {
10
+ const noResponse = opts?.noResponse || false;
11
+ return new Promise((resolve) => {
12
+ if (err) {
13
+ console.error('Upload failed:', err);
14
+ resolve({ code: 500, message: err });
15
+ return;
16
+ }
17
+ // 处理服务器响应
18
+ let body = '';
19
+ res.on('data', (chunk) => {
20
+ body += chunk;
21
+ });
22
+ res.on('end', () => {
23
+ if (noResponse) {
24
+ resolve({ code: 200, data: { status: res.statusCode }, message: res.statusMessage });
25
+ return;
26
+ }
27
+ try {
28
+ const res = JSON.parse(body);
29
+ resolve(res);
30
+ } catch (e) {
31
+ console.error('Failed to parse response:', e, body);
32
+ resolve({ code: 500, message: body });
33
+ }
34
+ });
35
+ });
36
+ };
37
+ export const getFormParams = (opts: UploadOptions, headers: any): FormData.SubmitOptions => {
38
+ const url = new URL(opts.url);
39
+ const value: FormData.SubmitOptions = {
40
+ path: url.pathname + url.search,
41
+ host: url.hostname,
42
+ method: 'POST',
43
+ protocol: url.protocol === 'https:' ? 'https:' : 'http:',
44
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
45
+ headers: {
46
+ ...headers,
47
+ },
48
+ };
49
+ return value;
50
+ };
51
+ type UploadOptions = {
52
+ url: string | URL;
53
+ file?: string | Buffer | File;
54
+ form?: FormData;
55
+ };
56
+ /**
57
+ * 单个文件上传
58
+ * @param opts
59
+ * @param opts.url 上传地址
60
+ * @param opts.file 文件路径或Buffer
61
+ * @param opts.form form对象
62
+ * @returns
63
+ */
64
+ export const upload = (opts: UploadOptions): Promise<{ code?: number; message?: string;[key: string]: any }> => {
65
+ const form = opts?.form || new FormData();
66
+ if (!opts.form) {
67
+ let value: any;
68
+ let type = 'string';
69
+ if (typeof opts.file === 'string') {
70
+ value = Buffer.from(opts.file);
71
+ } else {
72
+ type = 'buffer';
73
+ value = opts.file;
74
+ }
75
+ form.append('file', value);
76
+ // const fileSize = Buffer.byteLength(value);
77
+ }
78
+ const headers = form.getHeaders();
79
+ return new Promise((resolve) => {
80
+ form.submit(getFormParams(opts, headers), (err, res) => {
81
+ handleResponse(err, res, { noResponse: true }).then(resolve);
82
+ });
83
+ });
84
+ };
85
+
86
+ export const getFileSize = (file: string | Buffer | File): number => {
87
+ if (typeof file === 'string') {
88
+ return Buffer.byteLength(file);
89
+ } else if (file instanceof Buffer) {
90
+ return file.length;
91
+ } else if (file instanceof File) {
92
+ return file.size;
93
+ }
94
+ return 0;
95
+ }
@@ -21,7 +21,7 @@ const keepAliveFilePath = path.join(baseDir, 'keepAliveCache.json');
21
21
 
22
22
  export const runLive = (filePath: string, pm2Name: string) => {
23
23
  // 使用 npx 运行命令
24
- const cmdArgs = `cnb live -c ${filePath}`;
24
+ const cmdArgs = `live -c ${filePath}`;
25
25
 
26
26
  // 先停止已存在的同名 pm2 进程
27
27
  const stopCmd = `pm2 delete ${pm2Name} 2>/dev/null || true`;
@@ -33,14 +33,14 @@ export const runLive = (filePath: string, pm2Name: string) => {
33
33
  }
34
34
 
35
35
  // 使用pm2启动
36
- const pm2Cmd = `pm2 start ev --name ${pm2Name} --no-autorestart -- ${cmdArgs}`;
36
+ const pm2Cmd = `pm2 start cloud-live --name ${pm2Name} --no-autorestart -- ${cmdArgs}`;
37
37
  console.log('执行命令:', pm2Cmd);
38
38
  try {
39
39
  const result = execSync(pm2Cmd, { stdio: 'pipe', encoding: 'utf8' });
40
40
  console.log(result);
41
41
  } catch (error) {
42
42
  console.error("状态码:", error.status);
43
- console.error("错误详情:", error.stderr.toString()); // 这里会显示 ev 命令报的具体错误
43
+ console.error("错误详情:", error.stderr.toString()); // 这里会显示 cloud-live 命令报的具体错误
44
44
  }
45
45
  }
46
46