@kevisual/cnb 0.0.77 → 0.0.79

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.
@@ -0,0 +1,23 @@
1
+ import { downloadByCNBApi } from "@/repo/download.ts";
2
+
3
+ import { app } from '../../app.ts';
4
+ import { z } from 'zod';
5
+
6
+ // pnpm cli cnb download -- url=https://
7
+ app.route({
8
+ path: 'cnb',
9
+ key: 'download',
10
+ description: '下载文件',
11
+ middleware: ['auth'],
12
+ metadata: {
13
+ args: {
14
+ url: z.string().describe('文件下载链接'),
15
+ },
16
+ }
17
+ }).define(async (ctx) => {
18
+ const url = ctx.query?.url;
19
+ if (!url) {
20
+ ctx.throw(400, '缺少 url 参数');
21
+ }
22
+ //
23
+ }).addTo(app);
@@ -1,3 +1,4 @@
1
1
  import './list.ts'
2
2
  import './repo.ts'
3
- import './repo-label.ts'
3
+ import './repo-label.ts'
4
+ import './download.ts'
package/dist/cli.js CHANGED
@@ -63216,6 +63216,24 @@ app.route({
63216
63216
  ctx.forward(res);
63217
63217
  }).addTo(app);
63218
63218
 
63219
+ // agent/routes/repo/download.ts
63220
+ app.route({
63221
+ path: "cnb",
63222
+ key: "download",
63223
+ description: "下载文件",
63224
+ middleware: ["auth"],
63225
+ metadata: {
63226
+ args: {
63227
+ url: exports_external2.string().describe("文件下载链接")
63228
+ }
63229
+ }
63230
+ }).define(async (ctx) => {
63231
+ const url4 = ctx.query?.url;
63232
+ if (!url4) {
63233
+ ctx.throw(400, "缺少 url 参数");
63234
+ }
63235
+ }).addTo(app);
63236
+
63219
63237
  // agent/routes/workspace/skills.ts
63220
63238
  app.route({
63221
63239
  path: "cnb",
@@ -65701,7 +65719,7 @@ app.route({
65701
65719
  };
65702
65720
  }).addTo(app);
65703
65721
 
65704
- // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.95_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
65722
+ // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.96_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
65705
65723
  var import_oidc = __toESM(require_dist(), 1);
65706
65724
  var import_oidc2 = __toESM(require_dist(), 1);
65707
65725
  var marker17 = "vercel.ai.gateway.error";
@@ -66997,7 +67015,7 @@ async function getVercelRequestId() {
66997
67015
  var _a92;
66998
67016
  return (_a92 = import_oidc.getContext().headers) == null ? undefined : _a92["x-vercel-id"];
66999
67017
  }
67000
- var VERSION3 = "3.0.95";
67018
+ var VERSION3 = "3.0.96";
67001
67019
  var AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
67002
67020
  function createGatewayProvider(options = {}) {
67003
67021
  var _a92, _b92;
@@ -67184,7 +67202,7 @@ async function getGatewayAuthToken(options) {
67184
67202
  };
67185
67203
  }
67186
67204
 
67187
- // ../../node_modules/.pnpm/ai@6.0.158_zod@4.3.6/node_modules/ai/dist/index.mjs
67205
+ // ../../node_modules/.pnpm/ai@6.0.159_zod@4.3.6/node_modules/ai/dist/index.mjs
67188
67206
  var import_api = __toESM(require_src(), 1);
67189
67207
  var import_api2 = __toESM(require_src(), 1);
67190
67208
  var __defProp4 = Object.defineProperty;
@@ -67755,7 +67773,7 @@ function detectMediaType({
67755
67773
  }
67756
67774
  return;
67757
67775
  }
67758
- var VERSION4 = "6.0.158";
67776
+ var VERSION4 = "6.0.159";
67759
67777
  var download = async ({
67760
67778
  url: url4,
67761
67779
  maxBytes,
package/dist/npc.js CHANGED
@@ -61157,6 +61157,24 @@ app.route({
61157
61157
  ctx.forward(res);
61158
61158
  }).addTo(app);
61159
61159
 
61160
+ // agent/routes/repo/download.ts
61161
+ app.route({
61162
+ path: "cnb",
61163
+ key: "download",
61164
+ description: "下载文件",
61165
+ middleware: ["auth"],
61166
+ metadata: {
61167
+ args: {
61168
+ url: exports_external2.string().describe("文件下载链接")
61169
+ }
61170
+ }
61171
+ }).define(async (ctx) => {
61172
+ const url4 = ctx.query?.url;
61173
+ if (!url4) {
61174
+ ctx.throw(400, "缺少 url 参数");
61175
+ }
61176
+ }).addTo(app);
61177
+
61160
61178
  // agent/routes/workspace/skills.ts
61161
61179
  app.route({
61162
61180
  path: "cnb",
@@ -63642,7 +63660,7 @@ app.route({
63642
63660
  };
63643
63661
  }).addTo(app);
63644
63662
 
63645
- // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.95_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63663
+ // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.96_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63646
63664
  var import_oidc = __toESM(require_dist(), 1);
63647
63665
  var import_oidc2 = __toESM(require_dist(), 1);
63648
63666
  var marker17 = "vercel.ai.gateway.error";
@@ -64938,7 +64956,7 @@ async function getVercelRequestId() {
64938
64956
  var _a92;
64939
64957
  return (_a92 = import_oidc.getContext().headers) == null ? undefined : _a92["x-vercel-id"];
64940
64958
  }
64941
- var VERSION3 = "3.0.95";
64959
+ var VERSION3 = "3.0.96";
64942
64960
  var AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
64943
64961
  function createGatewayProvider(options = {}) {
64944
64962
  var _a92, _b92;
@@ -65125,7 +65143,7 @@ async function getGatewayAuthToken(options) {
65125
65143
  };
65126
65144
  }
65127
65145
 
65128
- // ../../node_modules/.pnpm/ai@6.0.158_zod@4.3.6/node_modules/ai/dist/index.mjs
65146
+ // ../../node_modules/.pnpm/ai@6.0.159_zod@4.3.6/node_modules/ai/dist/index.mjs
65129
65147
  var import_api = __toESM(require_src(), 1);
65130
65148
  var import_api2 = __toESM(require_src(), 1);
65131
65149
  var __defProp4 = Object.defineProperty;
@@ -65696,7 +65714,7 @@ function detectMediaType({
65696
65714
  }
65697
65715
  return;
65698
65716
  }
65699
- var VERSION4 = "6.0.158";
65717
+ var VERSION4 = "6.0.159";
65700
65718
  var download = async ({
65701
65719
  url: url4,
65702
65720
  maxBytes,
package/dist/opencode.js CHANGED
@@ -61123,6 +61123,24 @@ app.route({
61123
61123
  ctx.forward(res);
61124
61124
  }).addTo(app);
61125
61125
 
61126
+ // agent/routes/repo/download.ts
61127
+ app.route({
61128
+ path: "cnb",
61129
+ key: "download",
61130
+ description: "下载文件",
61131
+ middleware: ["auth"],
61132
+ metadata: {
61133
+ args: {
61134
+ url: exports_external2.string().describe("文件下载链接")
61135
+ }
61136
+ }
61137
+ }).define(async (ctx) => {
61138
+ const url4 = ctx.query?.url;
61139
+ if (!url4) {
61140
+ ctx.throw(400, "缺少 url 参数");
61141
+ }
61142
+ }).addTo(app);
61143
+
61126
61144
  // agent/routes/workspace/skills.ts
61127
61145
  app.route({
61128
61146
  path: "cnb",
@@ -63608,7 +63626,7 @@ app.route({
63608
63626
  };
63609
63627
  }).addTo(app);
63610
63628
 
63611
- // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.95_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63629
+ // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.96_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63612
63630
  var import_oidc = __toESM(require_dist(), 1);
63613
63631
  var import_oidc2 = __toESM(require_dist(), 1);
63614
63632
  var marker17 = "vercel.ai.gateway.error";
@@ -64904,7 +64922,7 @@ async function getVercelRequestId() {
64904
64922
  var _a92;
64905
64923
  return (_a92 = import_oidc.getContext().headers) == null ? undefined : _a92["x-vercel-id"];
64906
64924
  }
64907
- var VERSION3 = "3.0.95";
64925
+ var VERSION3 = "3.0.96";
64908
64926
  var AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
64909
64927
  function createGatewayProvider(options = {}) {
64910
64928
  var _a92, _b92;
@@ -65091,7 +65109,7 @@ async function getGatewayAuthToken(options) {
65091
65109
  };
65092
65110
  }
65093
65111
 
65094
- // ../../node_modules/.pnpm/ai@6.0.158_zod@4.3.6/node_modules/ai/dist/index.mjs
65112
+ // ../../node_modules/.pnpm/ai@6.0.159_zod@4.3.6/node_modules/ai/dist/index.mjs
65095
65113
  var import_api = __toESM(require_src(), 1);
65096
65114
  var import_api2 = __toESM(require_src(), 1);
65097
65115
  var __defProp4 = Object.defineProperty;
@@ -65662,7 +65680,7 @@ function detectMediaType({
65662
65680
  }
65663
65681
  return;
65664
65682
  }
65665
- var VERSION4 = "6.0.158";
65683
+ var VERSION4 = "6.0.159";
65666
65684
  var download = async ({
65667
65685
  url: url4,
65668
65686
  maxBytes,
package/dist/routes.js CHANGED
@@ -61123,6 +61123,24 @@ app.route({
61123
61123
  ctx.forward(res);
61124
61124
  }).addTo(app);
61125
61125
 
61126
+ // agent/routes/repo/download.ts
61127
+ app.route({
61128
+ path: "cnb",
61129
+ key: "download",
61130
+ description: "下载文件",
61131
+ middleware: ["auth"],
61132
+ metadata: {
61133
+ args: {
61134
+ url: exports_external2.string().describe("文件下载链接")
61135
+ }
61136
+ }
61137
+ }).define(async (ctx) => {
61138
+ const url4 = ctx.query?.url;
61139
+ if (!url4) {
61140
+ ctx.throw(400, "缺少 url 参数");
61141
+ }
61142
+ }).addTo(app);
61143
+
61126
61144
  // agent/routes/workspace/skills.ts
61127
61145
  app.route({
61128
61146
  path: "cnb",
@@ -63608,7 +63626,7 @@ app.route({
63608
63626
  };
63609
63627
  }).addTo(app);
63610
63628
 
63611
- // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.95_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63629
+ // ../../node_modules/.pnpm/@ai-sdk+gateway@3.0.96_zod@4.3.6/node_modules/@ai-sdk/gateway/dist/index.mjs
63612
63630
  var import_oidc = __toESM(require_dist(), 1);
63613
63631
  var import_oidc2 = __toESM(require_dist(), 1);
63614
63632
  var marker17 = "vercel.ai.gateway.error";
@@ -64904,7 +64922,7 @@ async function getVercelRequestId() {
64904
64922
  var _a92;
64905
64923
  return (_a92 = import_oidc.getContext().headers) == null ? undefined : _a92["x-vercel-id"];
64906
64924
  }
64907
- var VERSION3 = "3.0.95";
64925
+ var VERSION3 = "3.0.96";
64908
64926
  var AI_GATEWAY_PROTOCOL_VERSION = "0.0.1";
64909
64927
  function createGatewayProvider(options = {}) {
64910
64928
  var _a92, _b92;
@@ -65091,7 +65109,7 @@ async function getGatewayAuthToken(options) {
65091
65109
  };
65092
65110
  }
65093
65111
 
65094
- // ../../node_modules/.pnpm/ai@6.0.158_zod@4.3.6/node_modules/ai/dist/index.mjs
65112
+ // ../../node_modules/.pnpm/ai@6.0.159_zod@4.3.6/node_modules/ai/dist/index.mjs
65095
65113
  var import_api = __toESM(require_src(), 1);
65096
65114
  var import_api2 = __toESM(require_src(), 1);
65097
65115
  var __defProp4 = Object.defineProperty;
@@ -65662,7 +65680,7 @@ function detectMediaType({
65662
65680
  }
65663
65681
  return;
65664
65682
  }
65665
- var VERSION4 = "6.0.158";
65683
+ var VERSION4 = "6.0.159";
65666
65684
  var download = async ({
65667
65685
  url: url4,
65668
65686
  maxBytes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/cnb",
3
- "version": "0.0.77",
3
+ "version": "0.0.79",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "basename": "/root/cnb",
package/src/git/index.ts CHANGED
@@ -69,11 +69,12 @@ export type Content = {
69
69
  html_url: string;
70
70
  git_url: string;
71
71
  download_url: string;
72
- type: string;
72
+ type: 'lfs' | 'blob';
73
73
  content?: string;
74
- encoding?: string;
74
+ encoding?: 'base64' | 'utf-8';
75
75
  };
76
76
 
77
+
77
78
  /**
78
79
  * Git Blob 对象
79
80
  */
@@ -307,7 +308,7 @@ export class Git extends CNBCore {
307
308
  * @param params 查询参数
308
309
  * @returns 文件或目录内容
309
310
  */
310
- async getContent(repo: string, params?: GetContentParams): Promise<Result<Content>> {
311
+ async getContent(repo: string, params?: GetContentParams): Promise<Result<FileListContent | FileContent>> {
311
312
  const url = `/${repo}/-/git/contents`;
312
313
  return this.get({ url, params });
313
314
  }
@@ -319,11 +320,46 @@ export class Git extends CNBCore {
319
320
  * @param params 查询参数
320
321
  * @returns 文件内容
321
322
  */
322
- async getContentWithPath(repo: string, filePath: string, params?: GetContentWithPathParams): Promise<Result<Content>> {
323
+ async getContentWithPath(repo: string, filePath: string, params?: GetContentWithPathParams): Promise<Result<FileListContent | FileContent>> {
323
324
  const url = `/${repo}/-/git/contents/${filePath}`;
324
325
  return this.get({ url, params });
325
326
  }
326
327
 
328
+ async getAllContentWithPath(repo: string, filePath: string = '', opts: { recursive?: boolean } & GetContentParams): Promise<FileListContent['entries']> {
329
+ try {
330
+ const { recursive, ...rest } = opts;
331
+ const res = await this.getContentWithPath(repo, filePath, rest);
332
+ if (res.code !== 200) {
333
+ console.log('获取文件列表失败', repo, filePath, res);
334
+ return [];
335
+ } else if (!res.data) {
336
+ console.log('列表为空', res);
337
+ return [];
338
+ }
339
+ const fileList = res.data as unknown as FileListContent;
340
+ const entries = fileList.entries || [];
341
+ console.log('文件列表', fileList);
342
+ const treeList = entries.filter(item => item.type === 'tree');
343
+ const fileListResult = entries.filter(item => item.type === 'blob');
344
+ if (recursive) {
345
+ for (const tree of treeList) {
346
+ let treePath = tree.path;
347
+ if (tree.path.includes('/')) {
348
+ treePath = tree.name;
349
+ }
350
+ let newPath = filePath ? `${filePath}/${treePath}` : treePath;
351
+ const subFileList = await this.getAllContentWithPath(`${repo}`, newPath, { recursive });
352
+ fileListResult.push(...subFileList);
353
+ }
354
+ }
355
+ return fileListResult;
356
+ }
357
+ catch (error) {
358
+ console.error('获取仓库文件列表失败', error);
359
+ return [];
360
+ }
361
+ }
362
+
327
363
  /**
328
364
  * 获取原始文件内容
329
365
  * @param repo 仓库名称,格式:组织名称/仓库名称
@@ -674,3 +710,42 @@ type PutTagAnnotationsData = {
674
710
  /** 注释键值对 */
675
711
  annotations: Record<string, string>;
676
712
  };
713
+
714
+
715
+ export type FileListContent = {
716
+ type: 'blob' | 'tree';
717
+ size: number;
718
+ path: string;
719
+ name: string;
720
+ sha: string;
721
+ url: string;
722
+ entries?: FIleEntry[];
723
+ }
724
+ type FIleEntry = {
725
+ type: 'blob' | 'tree';
726
+ sha: string;
727
+ path: string;
728
+ name: string;
729
+ }
730
+
731
+ type FileContent = FileContentBase64 | FileContentLFS;
732
+ type FileContentBase64 = {
733
+ type: 'blob';
734
+ size: number;
735
+ path: string;
736
+ name: string;
737
+ sha: string;
738
+ encoding: 'base64';
739
+ content: string;
740
+ }
741
+
742
+ type FileContentLFS = {
743
+ type: 'lfs';
744
+ size: number;
745
+ path: string;
746
+ name: string;
747
+ sha: string;
748
+ lfs_oid: string;
749
+ lfs_size: number;
750
+ lfs_download_url: string;
751
+ }
@@ -1,4 +1,8 @@
1
- type DownloadFn = (url: string, opts?: { headers?: any, hash?: string, token?: string }) => Promise<{ code: number; buffer: Buffer | null }>
1
+ type DownloadFn = (url: string, opts?: { headers?: any, hash?: string, token?: string, stream?: boolean }) => Promise<{
2
+ code: number;
3
+ buffer: Buffer | null;
4
+ stream?: ReadableStream<Uint8Array> | null;
5
+ }>;
2
6
 
3
7
  /**
4
8
  * 下载文件内容,使用CNB API,支持文件未修改时返回304状态码
@@ -17,6 +21,7 @@ export const downloadByCNBApi: DownloadFn = async (url, opts) => {
17
21
  const res = await fetch(url, {
18
22
  headers: headers
19
23
  });
24
+ const stream = opts?.stream || false;
20
25
  type CNBResponse = {
21
26
  type?: 'blob' | 'file' | 'dir' | 'lfs';
22
27
  size?: number;
@@ -48,6 +53,15 @@ export const downloadByCNBApi: DownloadFn = async (url, opts) => {
48
53
  return { code: 304, buffer: null };
49
54
  }
50
55
  const buffer = Buffer.from(resJson.content, 'base64');
56
+ if (stream) {
57
+ const readableStream = new ReadableStream<Uint8Array>({
58
+ start(controller) {
59
+ controller.enqueue(buffer);
60
+ controller.close();
61
+ }
62
+ });
63
+ return { code: 200, buffer: null, stream: readableStream };
64
+ }
51
65
  return { code: 200, buffer };
52
66
  }
53
67
  }
@@ -63,6 +77,10 @@ export const downloadByCNBApi: DownloadFn = async (url, opts) => {
63
77
  // 文件未修改,跳过下载
64
78
  return { code: 304, buffer: null };
65
79
  }
80
+ if (stream) {
81
+ const readableStream = lfsRes.body;
82
+ return { code: 200, buffer: null, stream: readableStream };
83
+ }
66
84
  const arrayBuffer = await lfsRes.arrayBuffer();
67
85
  return { code: 200, buffer: Buffer.from(arrayBuffer) };
68
86
  }
@@ -75,4 +93,99 @@ export const downloadByCNBApi: DownloadFn = async (url, opts) => {
75
93
  console.error(`下载失败 ${url}: ${res.statusText}`, resJson);
76
94
  return { code: res.status, buffer: null };
77
95
  }
96
+ }
97
+ /**
98
+ *
99
+ * @param url
100
+ * @param opts
101
+ * @returns
102
+ */
103
+ export const downloadByLFSUrl: DownloadFn = async (url, opts) => {
104
+ const headers = {
105
+ Accept: "application/vnd.cnb.api+json",
106
+ // Authorization: `${CNB_API_KEY}`
107
+ ...(opts?.token ? { Authorization: `${opts.token}` } : {}),
108
+ ...opts?.headers
109
+ }
110
+ const res = await fetch(url, { headers });
111
+ const stream = opts?.stream || false;
112
+ if (res.ok) {
113
+ if (stream) {
114
+ const readableStream = res.body;
115
+ return { code: 200, buffer: null, stream: readableStream };
116
+ }
117
+ const arrayBuffer = await res.arrayBuffer();
118
+ return { code: 200, buffer: Buffer.from(arrayBuffer) };
119
+ }
120
+ console.error(`下载失败 ${url}: ${res.statusText}`);
121
+ return { code: res.status, buffer: null };
122
+ }
123
+
124
+
125
+ export const downloadResource: DownloadFn = async (url, opts) => {
126
+ const headers = opts?.headers || {};
127
+ const res = await fetch(url, { headers });
128
+ const stream = opts?.stream || false;
129
+ const etag = res.headers.get('ETag');
130
+ if (opts?.hash && etag === opts.hash) {
131
+ // 文件未修改,跳过下载
132
+ return { code: 304, buffer: null };
133
+ }
134
+ if (res.ok) {
135
+ if (stream) {
136
+ const readableStream = res.body;
137
+ return { code: 200, buffer: null, stream: readableStream };
138
+ }
139
+ const arrayBuffer = await res.arrayBuffer();
140
+ return { code: 200, buffer: Buffer.from(arrayBuffer) };
141
+ }
142
+ console.error(`下载失败 ${url}: ${res.statusText}`);
143
+ return { code: res.status, buffer: null };
144
+ }
145
+
146
+ export const isCNBOpenApi = (url: string) => { return url.startsWith('https://api.cnb.cool') }
147
+
148
+ export const download: DownloadFn = async (url, opts) => {
149
+ const isCNB = isCNBOpenApi(url);
150
+ if (isCNB) {
151
+ return await downloadByCNBApi(url, opts);
152
+ } else {
153
+ return await downloadResource(url, opts);
154
+ }
155
+ }
156
+
157
+ export const getLfsDownloadUrl = async (url: string, opts?: { headers?: any, token?: string }) => {
158
+ const headers = {
159
+ Accept: "application/vnd.cnb.api+json",
160
+ ...(opts?.token ? { Authorization: `${opts.token}` } : {}),
161
+ ...opts?.headers
162
+ }
163
+ const res = await fetch(url, {
164
+ headers: headers
165
+ });
166
+ type CNBResponse = {
167
+ type?: 'blob' | 'file' | 'dir' | 'lfs';
168
+ size?: number;
169
+ path: string;
170
+ name: string;
171
+ sha?: string;
172
+ encoding?: string;
173
+ content?: string;
174
+
175
+ lfs_oid?: string;
176
+ lfs_size?: number;
177
+ lfs_download_url?: string;
178
+ }
179
+ let resJson: CNBResponse;
180
+ try {
181
+ resJson = await res.json() as CNBResponse;
182
+ } catch (e) {
183
+ console.error(`获取LFS下载链接失败 ${url}: ${res.statusText}`, e);
184
+ return null;
185
+ }
186
+ if (res.ok && resJson.type === 'lfs' && resJson.lfs_download_url) {
187
+ return resJson.lfs_download_url;
188
+ }
189
+ console.error(`获取LFS下载链接失败 ${url}: ${res.statusText}`, resJson);
190
+ return null;
78
191
  }