@kevisual/cnb 0.0.1 → 0.0.2

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/agent/app.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { QueryRouterServer as App } from '@kevisual/router'
2
+ import { useContextKey } from '@kevisual/context'
3
+ import { useConfig } from '@kevisual/use-config'
4
+ import { CNB } from '../src/index.ts';
5
+ import { nanoid } from 'nanoid';
6
+
7
+ export const config = useConfig()
8
+ export const cnb = useContextKey<CNB>('cnb', () => {
9
+ return new CNB({ token: config.CNB_TOKEN, cookie: config.CNB_COOKIE, group: config.CNB_GROUP });
10
+ })
11
+ export const appId = nanoid();
12
+ export const app = useContextKey<App>('app', () => {
13
+ return new App()
14
+ })
package/agent/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './app.ts'
2
+ import './routes/index.ts'
@@ -0,0 +1,35 @@
1
+ import { tool } from "@opencode-ai/plugin/tool"
2
+ import { type Plugin } from "@opencode-ai/plugin"
3
+ import { app, cnb, appId } from './index.ts';
4
+
5
+ // opencode run "请使用 cnb-login-verify 工具验证登录信信息,检查cookie"
6
+ export const CnbPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
7
+ return {
8
+ 'tool': {
9
+ "cnb-login-verify": {
10
+ name: "CNB 登录验证信息",
11
+ description: "验证 CNB 登录信息是否有效",
12
+ args: {
13
+ checkToken: tool.schema.boolean().describe("是否检查 Token 的有效性").default(true),
14
+ checkCookie: tool.schema.boolean().describe("是否检查 Cookie 的有效性").default(false),
15
+ },
16
+ async execute(args) {
17
+ const res = await app.run({
18
+ path: 'cnb',
19
+ key: 'user-check',
20
+ payload: {
21
+ ...args
22
+ }
23
+ }, { appId });
24
+ if (res.code === 200) {
25
+ return res.data?.output;
26
+ }
27
+ return '无法获取登录状态,请检查配置。';
28
+ },
29
+ },
30
+ },
31
+ 'tool.execute.before': async (opts) => {
32
+ // console.log('CnbPlugin: tool.execute.before', opts.tool);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1 @@
1
+ // 根据环境变量获取当前的 cnb 启动环境
@@ -0,0 +1,36 @@
1
+ import { app, appId } from '@/agent/app.ts';
2
+ import './user/check.ts'
3
+
4
+ const checkAppId = (ctx: any, appId: string) => {
5
+ const _appId = ctx?.appId;
6
+ if (_appId) {
7
+ if (_appId !== appId) {
8
+ ctx.throw(403, 'Invalid App ID');
9
+ }
10
+ return true;
11
+ }
12
+ return false;
13
+ }
14
+
15
+ if (!app.hasRoute('auth')) {
16
+ app.route({
17
+ id: 'auth',
18
+ path: 'auth',
19
+ }).define(async (ctx) => {
20
+ // ctx.body = 'Auth Route';
21
+ if (checkAppId(ctx, appId)) {
22
+ return;
23
+ }
24
+ }).addTo(app);
25
+
26
+ app.route({
27
+ id: 'admin-auth',
28
+ path: 'admin-auth',
29
+ middleware: ['auth'],
30
+ }).define(async (ctx) => {
31
+ // ctx.body = 'Admin Auth Route';
32
+ if (checkAppId(ctx, appId)) {
33
+ return;
34
+ }
35
+ }).addTo(app);
36
+ }
@@ -0,0 +1,53 @@
1
+ import { app, cnb } from '@/agent/app.ts';
2
+
3
+ app.route({
4
+ path: 'cnb',
5
+ key: 'repo-create',
6
+ description: '创建代码仓库, 参数name, visibility, description',
7
+ middleware: ['auth'],
8
+ metadata: {
9
+ tags: ['opencode']
10
+ }
11
+ }).define(async (ctx) => {
12
+ const name = ctx.query?.name;
13
+ const visibility = ctx.query?.visibility ?? 'private';
14
+ const description = ctx.query?.description ?? '';
15
+
16
+ if (!name) {
17
+ ctx.throw(400, '缺少参数 name');
18
+ }
19
+
20
+ const res = await cnb.repo.createRepo(cnb.group, {
21
+ name,
22
+ visibility,
23
+ description,
24
+ });
25
+ ctx.forward(res);
26
+ }).addTo(app);
27
+
28
+ app.route({
29
+ path: 'cnb',
30
+ key: 'repo-create-file',
31
+ description: '在代码仓库中创建文件, 参数repoName, path, content, encoding',
32
+ middleware: ['auth'],
33
+ metadata: {
34
+ tags: ['opencode']
35
+ }
36
+ }).define(async (ctx) => {
37
+ const repoName = ctx.query?.repoName;
38
+ const path = ctx.query?.path;
39
+ const content = ctx.query?.content;
40
+ const encoding = ctx.query?.encoding ?? 'raw';
41
+
42
+ if (!repoName || !path || !content) {
43
+ ctx.throw(400, '缺少参数 repoName, path 或 content');
44
+ }
45
+
46
+ const res = await cnb.repo.createCommit(repoName, {
47
+ message: `添加文件 ${path} 通过 API `,
48
+ files: [
49
+ { path, content, encoding },
50
+ ],
51
+ });
52
+ ctx.forward(res);
53
+ }).addTo(app);
@@ -0,0 +1,33 @@
1
+ import { app, cnb } from '@/agent/app.ts';
2
+
3
+
4
+ app.route({
5
+ path: 'cnb',
6
+ key: 'user-check',
7
+ description: '检查用户登录状态,参数checkToken,default true; checkCookie, default false',
8
+ middleware: ['auth'],
9
+ metadata: {
10
+ tags: ['opencode']
11
+ }
12
+ }).define(async (ctx) => {
13
+ const checkToken = ctx.query?.checkToken ?? true;
14
+ const checkCookie = ctx.query?.checkCookie ?? false;
15
+ let output = '';
16
+ if (checkToken) {
17
+ const res = await cnb.user.getUser();
18
+ if (res?.code !== 200) {
19
+ output += `Token 无效,请检查 CNB_TOKEN 配置。\n`;
20
+ } else {
21
+ output += `Token 有效,Token用户昵称:${res.data?.nickname}\n`;
22
+ }
23
+ }
24
+ if (checkCookie) {
25
+ const res = await cnb.user.getCurrentUser();
26
+ if (res?.code !== 200) {
27
+ output += `Cookie 无效,请检查 CNB_COOKIE 配置。\n`;
28
+ } else {
29
+ output += `Cookie 有效,当前Cookie用户:${res.data?.nickname}\n`;
30
+ }
31
+ }
32
+ ctx.body = { output };
33
+ }).addTo(app);
@@ -0,0 +1,23 @@
1
+ import { app, cnb } from '@/agent/app.ts';
2
+
3
+ app.route({
4
+ path: 'cnb',
5
+ key: 'start-workspace',
6
+ description: '启动开发工作空间, 参数 repo',
7
+ middleware: ['auth'],
8
+ metadata: {
9
+ tags: ['opencode']
10
+ }
11
+ }).define(async (ctx) => {
12
+ const repo = ctx.query?.repo;
13
+ const branch = ctx.query?.branch;
14
+ const ref = ctx.query?.ref;
15
+ if (!repo) {
16
+ ctx.throw(400, '缺少参数 repo');
17
+ }
18
+ const res = await cnb.workspace.startWorkspace(repo, {
19
+ branch,
20
+ ref
21
+ });
22
+ ctx.forward(res);
23
+ }).addTo(app);
package/package.json CHANGED
@@ -1,27 +1,51 @@
1
1
  {
2
2
  "name": "@kevisual/cnb",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
- "main": "index.js",
5
+ "main": "mod.ts",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "code": "opencode web --hostname 0.0.0.0"
8
8
  },
9
9
  "keywords": [],
10
10
  "files": [
11
11
  "src",
12
12
  "mod.ts",
13
- "agents"
13
+ "agent"
14
14
  ],
15
15
  "author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
16
16
  "license": "MIT",
17
- "packageManager": "pnpm@10.25.0",
17
+ "packageManager": "pnpm@10.28.0",
18
18
  "type": "module",
19
19
  "devDependencies": {
20
- "@types/bun": "^1.3.4",
21
- "@types/node": "^25.0.2",
20
+ "@kevisual/context": "^0.0.4",
21
+ "@kevisual/types": "^0.0.10",
22
+ "@opencode-ai/plugin": "^1.1.13",
23
+ "@types/bun": "^1.3.5",
24
+ "@types/node": "^25.0.6",
22
25
  "dotenv": "^17.2.3"
23
26
  },
24
27
  "publishConfig": {
25
28
  "access": "public"
29
+ },
30
+ "exports": {
31
+ ".": {
32
+ "import": "./mod.ts",
33
+ "types": "./mod.d.ts"
34
+ },
35
+ "./agent": {
36
+ "import": "./agent/index.ts",
37
+ "types": "./agent/index.d.ts"
38
+ },
39
+ "./opencode": {
40
+ "import": "./agent/opencode-plugin.ts",
41
+ "types": "./agent/opencode-plugin.d.ts"
42
+ }
43
+ },
44
+ "dependencies": {
45
+ "@kevisual/query": "^0.0.35",
46
+ "@kevisual/router": "^0.0.52",
47
+ "@kevisual/use-config": "^1.0.24",
48
+ "es-toolkit": "^1.43.0",
49
+ "nanoid": "^5.1.6"
26
50
  }
27
51
  }
package/readme.md CHANGED
@@ -2,4 +2,16 @@
2
2
 
3
3
  ## 简介
4
4
 
5
- 纯粹调用api的模式去使用cnb.cool,自动化方案。
5
+ 纯粹调用api的模式去使用cnb.cool,自动化方案。
6
+
7
+ ## workspace
8
+
9
+ 工作区快速打开,编辑代码,然后运行和执行
10
+
11
+ ## 代码仓库
12
+
13
+ 快速创建代码仓库,然后打开工作区,然后运行
14
+
15
+ ## kevisual assistant app
16
+
17
+ 借用模块进行开发项目
package/src/ai/index.ts CHANGED
@@ -1,16 +1,12 @@
1
1
  import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
2
- type AiOptions = CNBCoreOptions<{
3
- group?: string;
4
- }>
2
+
5
3
  class AiBase extends CNBCore {
6
4
  group: string;
7
- constructor(options: AiOptions) {
5
+ constructor(options: CNBCoreOptions) {
8
6
  super({ token: options.token, cookie: options.cookie });
9
- this.group = options.group || '';
10
7
  }
11
8
  autoPr(repo: string, data: { body: string, branch?: string, title: string }): Promise<Result<any>> {
12
- const group = this.group || '';
13
- const url = `/${group}/${repo}/-/build/ai/auto-pr`;
9
+ const url = `/${repo}/-/build/ai/auto-pr`;
14
10
  const postData = {
15
11
  ...data,
16
12
  branch: data.branch || 'refs/heads/main',
@@ -0,0 +1,47 @@
1
+ import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
2
+
3
+
4
+ export class Build extends CNBCore {
5
+ constructor(options: CNBCoreOptions ) {
6
+ super({ token: options.token, cookie: options.cookie });
7
+ }
8
+ startBuild(repo: string, data: StartBuildData): Promise<any> {
9
+ const url = `/${repo}/-/build/start`;
10
+ let postData: StartBuildData = {
11
+ ...data,
12
+ branch: data.branch || 'main',
13
+ };
14
+ return this.post({ url, data: postData });
15
+ }
16
+ }
17
+
18
+ type StartBuildData = {
19
+ /**
20
+ * 触发分支,默认为主分支
21
+ */
22
+ branch?: string;
23
+ /**
24
+ * 指定配置文件内容,yaml 格式
25
+ */
26
+ config?: string;
27
+ /**
28
+ * 环境变量,对象格式
29
+ */
30
+ env?: Record<string, string>;
31
+ /**
32
+ * 事件名,必须是 api_trigger 或以 api_trigger_ 开头,默认为 api_trigger
33
+ */
34
+ event?: string;
35
+ /**
36
+ * commit id ,优先级比 tag 高,默认为分支最新提交记录
37
+ */
38
+ sha?: string;
39
+ /**
40
+ * 是否等待构建正式触发,为false时会立刻返回 sn 和 buildLogUrl
41
+ */
42
+ sync?: string;
43
+ /**
44
+ * 触发 tag,优先级比 branch 高
45
+ */
46
+ tag?: string;
47
+ }
package/src/cnb-core.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export type CNBCoreOptions<T = {}> = {
2
2
  token: string;
3
+ /**
4
+ * 对 cnb 界面操作定制模块功能时需要传入 cookie
5
+ */
3
6
  cookie?: string;
7
+ cnb?: CNBCore;
4
8
  } & T;
5
9
 
6
10
  export type RequestOptions = {
@@ -15,11 +19,19 @@ export type RequestOptions = {
15
19
  };
16
20
  export class CNBCore {
17
21
  baseURL = 'https://api.cnb.cool';
18
- token: string;
19
- cookie?: string;
22
+ public token: string;
23
+ public cookie?: string;
20
24
  constructor(options: CNBCoreOptions) {
21
25
  this.token = options.token;
22
26
  this.cookie = options.cookie;
27
+ if (options?.cnb) {
28
+ if (!options.token) {
29
+ this.token = options.cnb.token;
30
+ }
31
+ if (!options.cookie) {
32
+ this.cookie = options.cnb.cookie;
33
+ }
34
+ }
23
35
  }
24
36
 
25
37
  async request({ url, method = 'GET', data, params, headers, body, useCookie, useOrigin }: RequestOptions): Promise<any> {
package/src/index.ts CHANGED
@@ -3,23 +3,64 @@ import { Workspace } from "./workspace.ts";
3
3
  import { KnowledgeBase } from "./knowledge/index.ts";
4
4
  import { Repo } from "./repo/index.ts";
5
5
  import { User } from "./user/index.ts";
6
+ import { Build } from "./build/index.ts";
7
+ import { Issue } from "./issue/index.ts";
8
+ import { Mission } from "./mission/index.ts";
9
+ import { AiBase } from "./ai/index.ts";
6
10
 
7
- type CNBOptions = CNBCoreOptions<{}>;
11
+ type CNBOptions = CNBCoreOptions<{
12
+ group?: string;
13
+ }>;
8
14
 
9
15
  export class CNB extends CNBCore {
10
16
  workspace!: Workspace;
11
17
  knowledgeBase!: KnowledgeBase;
12
18
  repo!: Repo;
13
19
  user!: User;
20
+ build!: Build;
21
+ issue!: Issue;
22
+ mission!: Mission;
23
+ ai!: AiBase;
24
+ group!: string;
14
25
  constructor(options: CNBOptions) {
15
- super({ token: options.token, cookie: options.cookie });
26
+ super({ token: options.token, cookie: options.cookie, cnb: options.cnb });
16
27
  this.init(options);
17
28
  }
18
- init(options: CNBOptions) {
29
+ init(cnbOptions?: CNBOptions) {
30
+ const token = this.token;
31
+ const cookie = this.cookie;
32
+ const options = { token, cookie };
19
33
  this.workspace = new Workspace(options.token);
34
+ const group = cnbOptions?.group || '';
20
35
  this.knowledgeBase = new KnowledgeBase({ token: options.token, cookie: options.cookie });
21
36
  this.repo = new Repo({ token: options.token, cookie: options.cookie });
22
37
  this.user = new User({ token: options.token, cookie: options.cookie });
38
+ this.build = new Build({ token: options.token, cookie: options.cookie });
39
+ this.issue = new Issue({ token: options.token, cookie: options.cookie });
40
+ this.mission = new Mission({ token: options.token, cookie: options.cookie });
41
+ this.ai = new AiBase({ token: options.token, cookie: options.cookie });
42
+ this.group = group;
43
+ }
44
+ setGroup(group: string) {
45
+ this.group = group;
46
+ }
47
+ setToken(token: string) {
48
+ this.token = token;
49
+ this.knowledgeBase.token = token;
50
+ this.repo.token = token;
51
+ this.user.token = token;
52
+ this.build.token = token;
53
+ this.issue.token = token;
54
+ this.mission.token = token;
55
+ }
56
+ setCookie(cookie: string) {
57
+ this.cookie = cookie;
58
+ this.knowledgeBase.cookie = cookie;
59
+ this.repo.cookie = cookie;
60
+ this.user.cookie = cookie;
61
+ this.build.cookie = cookie;
62
+ this.issue.cookie = cookie;
63
+ this.mission.cookie = cookie;
23
64
  }
24
65
  }
25
66
 
@@ -27,3 +68,8 @@ export * from './workspace.ts'
27
68
  export * from './cnb-core.ts'
28
69
  export * from './knowledge/index.ts'
29
70
  export * from './repo/index.ts'
71
+ export * from './user/index.ts'
72
+ export * from './build/index.ts'
73
+ export * from './issue/index.ts'
74
+ export * from './mission/index.ts'
75
+ export * from './ai/index.ts'
@@ -0,0 +1,121 @@
1
+ import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
2
+
3
+ export type IssueAssignee = {
4
+ nickname: string;
5
+ username: string;
6
+ };
7
+
8
+ export type IssueLabel = {
9
+ color: string;
10
+ description: string;
11
+ id: string;
12
+ name: string;
13
+ };
14
+ export type IssueState = 'open' | 'closed';
15
+
16
+ export type IssueAuthor = {
17
+ name: string;
18
+ body: string;
19
+ comment_count: number;
20
+ created_at: string;
21
+ ended_at: string;
22
+ };
23
+
24
+ export type IssueItem = {
25
+ assignees: IssueAssignee[];
26
+ author: IssueAuthor;
27
+ labels: IssueLabel[];
28
+
29
+ last_acted_at: string;
30
+ number: string;
31
+ priority: string;
32
+ started_at: string;
33
+ state: string;
34
+ state_reason: string;
35
+ title: string;
36
+ updated_at: string;
37
+ };
38
+ export class Issue extends CNBCore {
39
+ constructor(options: CNBCoreOptions) {
40
+ super({ token: options.token, cookie: options.cookie });
41
+ }
42
+
43
+ createIssue(repo: string, data: Partial<IssueItem>): Promise<any> {
44
+ const url = `/${repo}/-/issues`;
45
+ let postData = {
46
+ ...data,
47
+ };
48
+ return this.post({ url, data: postData });
49
+ }
50
+ updateIssue(repo: string, issueNumber: string | number, data: Partial<IssueItem>): Promise<any> {
51
+ const url = `/${repo}/-/issues/${issueNumber}`;
52
+ let postData = {
53
+ ...data,
54
+ };
55
+ return this.patch({ url, data: postData });
56
+ }
57
+ getItem(repo: string, issueNumber: string | number): Promise<Result<IssueItem>> {
58
+ const url = `/${repo}/-/issues/${issueNumber}`;
59
+ return this.get({
60
+ url,
61
+ headers: {
62
+ 'Accept': 'application/vnd.cnb.api+json',
63
+ }
64
+ });
65
+ }
66
+
67
+ getList(repo: string, params?: Partial<GetListParams>): Promise<Result<IssueItem[]>> {
68
+ const url = `/${repo}/-/issues`;
69
+ return this.get({
70
+ url,
71
+ params,
72
+ headers: {
73
+ 'Accept': 'application/vnd.cnb.api+json',
74
+ }
75
+ });
76
+ }
77
+ getCommentList(repo: string, issueNumber: string | number): Promise<any> {
78
+ const url = `/${repo}/-/issues/${issueNumber}/comments`;
79
+ return this.get({
80
+ url,
81
+ });
82
+ }
83
+ setIssueProperty(repo: string, issueNumber: string | number, properties: { [key: string]: any }[]): Promise<any> {
84
+ const url = `/${repo}/-/issues/${issueNumber}/property`;
85
+ let postData = {
86
+ properties: properties,
87
+ };
88
+ return this.post({ url, data: postData });
89
+ }
90
+ }
91
+
92
+ type GetListParams = {
93
+ /** 问题指派人名称,例如: 张三, 李四, -; - 表示不指派给任何人 */
94
+ assignees?: string;
95
+ /** 问题作者名称,例如: 张三, 李四 */
96
+ authors?: string;
97
+ /** 问题关闭时间过滤-开始,例如: 2022-01-31 */
98
+ close_time_begin?: string;
99
+ /** 问题关闭时间过滤-结束,例如: 2022-01-31 */
100
+ close_time_end?: string;
101
+ /** 问题搜索关键词 */
102
+ keyword?: string;
103
+ /** 问题标签,例如: git, bug, feature */
104
+ labels?: string;
105
+ /** 问题标签操作符,例如: contains_any, contains_all,默认: contains_any */
106
+ labels_operator?: string;
107
+ /** 问题排序,例如: created_at, -updated_at, reference_count;'-' 前缀表示降序 */
108
+ order_by?: string;
109
+ /** 分页页码。如果值小于 1,将自动调整为 1,默认: 1 */
110
+ page?: number;
111
+ /** 分页每页大小。如果值大于 100,将自动调整为 100,默认: 30 */
112
+ page_size?: number;
113
+ /** 问题优先级,例如: -1P, -2P, P0, P1, P2, P3 */
114
+ priority?: string;
115
+ /** 问题状态:open 或 closed */
116
+ state?: string;
117
+ /** 问题更新时间过滤-开始,例如: 2022-01-31 */
118
+ updated_time_begin?: string;
119
+ /** 问题更新时间过滤-结束,例如: 2022-01-31 */
120
+ updated_time_end?: string;
121
+ }
@@ -1,13 +1,8 @@
1
- import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
1
+ import { CNBCore, CNBCoreOptions, Result } from "../cnb-core.ts";
2
2
 
3
- type RepoOptions = CNBCoreOptions<{
4
- group?: string;
5
- }>
6
3
  export class KnowledgeBase extends CNBCore {
7
- group: string;
8
- constructor(options: RepoOptions) {
4
+ constructor(options: CNBCoreOptions) {
9
5
  super({ token: options.token, cookie: options.cookie });
10
- this.group = options.group || '';
11
6
  }
12
7
 
13
8
  queryKnowledgeBase(repo: string, data: {
@@ -16,28 +11,24 @@ export class KnowledgeBase extends CNBCore {
16
11
  top_k?: number,
17
12
  metadata_filtering_conditions?: MetadataFilteringConditions
18
13
  }): Promise<any> {
19
- const group = this.group || '';
20
- const url = `/${group}/${repo}/-/knowledge/query`;
14
+ const url = `/${repo}/-/knowledge/base/query`;
21
15
  let postData = {
22
16
  query: data.query,
23
17
  };
24
18
  return this.post({ url, data: postData });
25
19
  }
26
20
  getEmbeddingModels(repo: string): Promise<Result<Array<{ name: string, dimensions: number }>>> {
27
- const group = this.group || '';
28
- const url = `/${group}/${repo}/-/knowledge/embedding/models`;
21
+ const url = `/${repo}/-/knowledge/embedding/models`;
29
22
  // bg3-m3 1024
30
23
  // hunyuan 1024
31
24
  return this.get({ url });
32
25
  }
33
26
  getBase(repo: string): Promise<Result<any>> {
34
- const group = this.group || '';
35
- const url = `/${group}/${repo}/-/knowledge/base`;
27
+ const url = `/${repo}/-/knowledge/base`;
36
28
  return this.get({ url });
37
29
  }
38
30
  deleteBase(repo: string): Promise<Result<any>> {
39
- const group = this.group || '';
40
- const url = `/${group}/${repo}/-/knowledge/base`;
31
+ const url = `/${repo}/-/knowledge/base`;
41
32
  return this.request({ url, method: 'DELETE' });
42
33
  }
43
34
  }
@@ -0,0 +1,190 @@
1
+ import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
2
+
3
+ export class Mission extends CNBCore {
4
+ constructor(options: CNBCoreOptions) {
5
+ super({ token: options.token, cookie: options.cookie });
6
+ }
7
+
8
+ /**
9
+ * 查询组织下用户有权限查看的任务集
10
+ * @param params 查询参数
11
+ * @returns 任务集列表
12
+ */
13
+ getMissions(group: string, params?: GetMissionsParams): Promise<Result<Missions4User[]>> {
14
+ const url = `/${group}/-/missions`;
15
+ return this.get({
16
+ url,
17
+ params: {
18
+ page: 1,
19
+ page_size: 10,
20
+ ...params,
21
+ },
22
+ });
23
+ }
24
+
25
+ /**
26
+ * 创建任务集
27
+ * @param data 创建任务集数据
28
+ * @returns 创建结果
29
+ */
30
+ createMission(repo: string, data: CreateMissionData): Promise<Result<any>> {
31
+ const url = `/${repo}/-/missions`;
32
+ return this.post({ url, data });
33
+ }
34
+
35
+ /**
36
+ * 删除任务集
37
+ * @param mission 任务集路径
38
+ * @param identityTicket 微信身份验证票据
39
+ * @returns 删除结果
40
+ */
41
+ deleteMission(mission: string, identityTicket?: string): Promise<Result<any>> {
42
+ const url = `/${mission}`;
43
+ return this.delete({
44
+ url,
45
+ headers: identityTicket ? { 'x-cnb-identity-ticket': identityTicket } : undefined,
46
+ });
47
+ }
48
+
49
+ /**
50
+ * 获取任务集视图配置
51
+ * @param mission 任务集 slug
52
+ * @param viewId 视图 ID
53
+ * @returns 视图配置
54
+ */
55
+ getMissionViewConfig(mission: string, viewId: string): Promise<Result<MissionViewConfig>> {
56
+ const url = `/${mission}/-/mission/view`;
57
+ return this.get({
58
+ url,
59
+ params: { id: viewId },
60
+ });
61
+ }
62
+
63
+ /**
64
+ * 设置任务集视图配置
65
+ * @param mission 任务集 slug
66
+ * @param config 视图配置
67
+ * @returns 设置结果
68
+ */
69
+ setMissionViewConfig(mission: string, config: MissionViewConfig): Promise<Result<any>> {
70
+ const url = `/${mission}/-/mission/view`;
71
+ return this.post({ url, data: config });
72
+ }
73
+
74
+ /**
75
+ * 获取任务集视图列表
76
+ * @param mission 任务集 slug
77
+ * @returns 视图列表
78
+ */
79
+ getMissionViewList(mission: string): Promise<Result<MissionView[]>> {
80
+ const url = `/${mission}/-/mission/view-list`;
81
+ return this.get({
82
+ url,
83
+ headers: {
84
+ 'Accept': 'application/vnd.cnb.api+json',
85
+ }
86
+ });
87
+ }
88
+
89
+ /**
90
+ * 添加或修改任务集视图
91
+ * @param mission 任务集 slug
92
+ * @param view 视图数据
93
+ * @returns 操作结果
94
+ */
95
+ putMissionView(mission: string, view: MissionView): Promise<Result<any>> {
96
+ const url = `/${mission}/-/mission/view-list`;
97
+ return this.put({ url, data: view });
98
+ }
99
+
100
+ /**
101
+ * 排序任务集视图
102
+ * @param mission 任务集 slug
103
+ * @param data 排序数据
104
+ * @returns 操作结果
105
+ */
106
+ sortMissionViews(mission: string, data: MissionPostViewReq): Promise<Result<any>> {
107
+ const url = `/${mission}/-/mission/view-list`;
108
+ return this.post({ url, data });
109
+ }
110
+
111
+ /**
112
+ * 改变任务集可见性
113
+ * @param mission 任务集路径
114
+ * @param visibility 可见性 (Private 或 Public)
115
+ * @returns 操作结果
116
+ */
117
+ setVisibility(mission: string, visibility: 'Private' | 'Public'): Promise<Result<any>> {
118
+ const url = `/${mission}/-/settings/set_visibility`;
119
+ return this.post({
120
+ url,
121
+ params: { visibility },
122
+ });
123
+ }
124
+ }
125
+
126
+ // ==================== 类型定义 ====================
127
+
128
+ type GetMissionsParams = {
129
+ page?: number;
130
+ page_size?: number;
131
+ filter_type?: 'private' | 'public';
132
+ order_by?: 'created_at' | 'name';
133
+ desc?: boolean;
134
+ descendant?: 'all' | 'sub' | 'grand';
135
+ search?: string;
136
+ };
137
+
138
+ type CreateMissionData = {
139
+ name: string;
140
+ description?: string;
141
+ visibility?: 'Private' | 'Public';
142
+ };
143
+
144
+ type Missions4User = {
145
+ id: string;
146
+ name: string;
147
+ slug_path: string;
148
+ description: string;
149
+ visibility: 'Private' | 'Public';
150
+ created_at: string;
151
+ updated_at: string;
152
+ web_url: string;
153
+ };
154
+
155
+ type MissionView = {
156
+ id: string;
157
+ name: string;
158
+ description?: string;
159
+ config?: MissionViewConfig;
160
+ created_at?: string;
161
+ updated_at?: string;
162
+ };
163
+
164
+ type MissionViewConfig = {
165
+ id: string;
166
+ name: string;
167
+ description?: string;
168
+ columns?: MissionViewColumn[];
169
+ filters?: MissionViewFilter[];
170
+ sort_by?: string;
171
+ sort_order?: 'asc' | 'desc';
172
+ };
173
+
174
+ type MissionViewColumn = {
175
+ key: string;
176
+ label: string;
177
+ width?: number;
178
+ visible?: boolean;
179
+ sortable?: boolean;
180
+ };
181
+
182
+ type MissionViewFilter = {
183
+ key: string;
184
+ operator?: 'eq' | 'ne' | 'contains' | 'gt' | 'lt' | 'gte' | 'lte';
185
+ value?: any;
186
+ };
187
+
188
+ type MissionPostViewReq = {
189
+ view_ids: string[];
190
+ };
package/src/repo/index.ts CHANGED
@@ -1,13 +1,8 @@
1
1
  import { CNBCore, CNBCoreOptions, RequestOptions, Result } from "../cnb-core.ts";
2
2
 
3
- type RepoOptions = CNBCoreOptions<{
4
- group?: string;
5
- }>
6
3
  export class Repo extends CNBCore {
7
- group: string;
8
- constructor(options: RepoOptions) {
4
+ constructor(options: CNBCoreOptions) {
9
5
  super({ token: options.token, cookie: options.cookie });
10
- this.group = options.group || '';
11
6
  }
12
7
  /**
13
8
  * 创建代码仓库
@@ -15,8 +10,7 @@ export class Repo extends CNBCore {
15
10
  * @param data
16
11
  * @returns
17
12
  */
18
- createRepo(data: CreateRepoData): Promise<any> {
19
- const group = this.group || '';
13
+ createRepo(group: string, data: CreateRepoData): Promise<any> {
20
14
  const url = `/${group}/-/repos`;
21
15
  let postData: CreateRepoData = {
22
16
  ...data,
@@ -28,7 +22,6 @@ export class Repo extends CNBCore {
28
22
  return this.post({ url, data: postData });
29
23
  }
30
24
  async createCommit(repo: string, data: CreateCommitData): Promise<any> {
31
- const group = this.group || '';
32
25
  const commitList = await this.getCommitList(repo, {
33
26
  page: 1,
34
27
  page_size: 1,
@@ -40,7 +33,7 @@ export class Repo extends CNBCore {
40
33
  if (!data.parent_commit_sha && preCommitSha) {
41
34
  data.parent_commit_sha = preCommitSha;
42
35
  }
43
- const url = `https://cnb.cool/${group}/${repo}/-/git/commits`;
36
+ const url = `https://cnb.cool/${repo}/-/git/commits`;
44
37
  const postData: CreateCommitData = {
45
38
  ...data,
46
39
  base_branch: data.base_branch || 'refs/heads/main',
@@ -56,8 +49,7 @@ export class Repo extends CNBCore {
56
49
  return this.post({ url, data: postData, useCookie: true, });
57
50
  }
58
51
  createBlobs(repo: string, data: { content: string, encoding?: 'utf-8' | 'base64' }): Promise<any> {
59
- const group = this.group || '';
60
- const url = `/${group}/${repo}/-/git/blobs`;
52
+ const url = `/${repo}/-/git/blobs`;
61
53
  const postData = {
62
54
  content: data.content,
63
55
  encoding: data.encoding || 'utf-8',
@@ -65,13 +57,11 @@ export class Repo extends CNBCore {
65
57
  return this.post({ url, data: postData });
66
58
  }
67
59
  uploadFiles(repo: string, data: { ext?: any, name?: string, path?: string, size?: number }): Promise<any> {
68
- const group = this.group || '';
69
- const url = `/${group}/${repo}/-/upload/files`
60
+ const url = `/${repo}/-/upload/files`
70
61
  return this.post({ url, data });
71
62
  }
72
63
  getCommitList(repo: string, params: { author?: string, commiter?: string, page?: number, page_size?: number, sha?: string, since?: string, until?: string }, opts?: RequestOptions): Promise<any> {
73
- const group = this.group || '';
74
- const url = `/${group}/${repo}/-/git/commits`;
64
+ const url = `/${repo}/-/git/commits`;
75
65
  return this.get({ url, params, ...opts });
76
66
  }
77
67
  getRepoList(params: {
package/src/user/index.ts CHANGED
@@ -1,21 +1,29 @@
1
1
  import { CNBCore, CNBCoreOptions } from "../cnb-core.ts";
2
-
2
+ import { Result } from "@kevisual/query";
3
3
  export class User extends CNBCore {
4
4
  constructor(options: CNBCoreOptions<{}>) {
5
5
  super({ token: options.token, cookie: options.cookie });
6
6
  }
7
7
 
8
8
  /**
9
- * 获取当前用户信息
10
- * @returns
9
+ * 获取当前用户信息,使用 Cookie 认证
10
+ * @returns
11
11
  */
12
- getCurrentUser(): Promise<any> {
12
+ getCurrentUser(): Promise<Result<UserInfo>> {
13
13
  const url = `https://cnb.cool/user`;
14
14
  return this.get({
15
15
  url,
16
16
  useCookie: true,
17
17
  });
18
18
  }
19
+ /**
20
+ * 使用 Token 获取用户信息
21
+ * @returns
22
+ */
23
+ getUser(): Promise<Result<UserInfo>> {
24
+ const url = '/user';
25
+ return this.get({ url });
26
+ }
19
27
  createAccessToken(data?: { description?: string, scope?: string }): Promise<AccessTokenResponse> {
20
28
  const scope = data?.scope || 'mission-manage:rw,mission-delete:rw,group-delete:rw,group-manage:rw,group-resource:rw,account-engage:rw,account-email:r,account-profile:rw,registry-delete:rw,registry-manage:rw,registry-package-delete:rw,registry-package:rw,repo-security:r,repo-delete:rw,repo-manage:rw,repo-basic-info:r,repo-cnb-detail:rw,repo-cnb-history:r,repo-cnb-trigger:rw,repo-commit-status:rw,repo-contents:rw,repo-notes:rw,repo-issue:rw,repo-pr:rw,repo-code:rw'
21
29
  const url = 'https://cnb.cool/user/personal_access_tokens'
@@ -52,4 +60,57 @@ export class User extends CNBCore {
52
60
  type AccessTokenResponse = {
53
61
  token: string;
54
62
  description: string;
63
+ }
64
+
65
+ type UserInfo = {
66
+ id: string;
67
+ username: string;
68
+ nickname: string;
69
+ type: number;
70
+ verified: number;
71
+ verified_expire_in: string;
72
+ created_at: string;
73
+ freeze: boolean;
74
+ locked: boolean;
75
+ avatar: string;
76
+ email: string;
77
+ next_updated_name_at: string;
78
+ updated_name_at: string;
79
+ updated_nick_at: string;
80
+ editable: {
81
+ avatar: boolean;
82
+ email: boolean;
83
+ logoff: boolean;
84
+ nickname: boolean;
85
+ 'sync-data': boolean;
86
+ username: boolean;
87
+ };
88
+ language: string;
89
+ appearance: string;
90
+ gender: number;
91
+ bio: string;
92
+ company: string;
93
+ location: string;
94
+ site: string;
95
+ address: string;
96
+ wechat_mp: string;
97
+ wechat_mp_qrcode: string;
98
+ appreciate_status: number;
99
+ follow_count: number;
100
+ follower_count: number;
101
+ reward_count: number;
102
+ reward_amount: number;
103
+ stars_count: number;
104
+ group_count: number;
105
+ repo_count: number;
106
+ mission_count: number;
107
+ registry_count: number;
108
+ public_repo_count: number;
109
+ public_mission_count: number;
110
+ public_registry_count: number;
111
+ follow_repo_count: number;
112
+ follow_mission_count: number;
113
+ readme_repo_path: string;
114
+ last_login_at: string;
115
+ last_login_ip: string;
55
116
  }
package/src/workspace.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * @createdAt 2025-12-04
6
6
  */
7
7
 
8
+ import { Result } from "@kevisual/query/query";
8
9
  import { CNBCore } from "./cnb-core.ts";
9
10
 
10
11
  /**
@@ -75,8 +76,6 @@ export class Workspace extends CNBCore {
75
76
  return this.post({ url: '/workspace/stop', data });
76
77
  }
77
78
 
78
-
79
-
80
79
  /**
81
80
  * 根据流水线 SN 查询云原生开发访问地址
82
81
  * @param repo 仓库路径,例如:groupname/reponame
@@ -102,7 +101,7 @@ export class Workspace extends CNBCore {
102
101
  * @param params 启动参数
103
102
  * @returns 返回启动结果,包含以下字段:
104
103
  */
105
- async startWorkspace(repo: string, params: { branch?: string; ref?: string }): Promise<{
104
+ async startWorkspace(repo: string, params: { branch?: string; ref?: string }): Promise<Result<{
106
105
  /** 仅新创建开发环境时返回,表示创建开发环境的流水线日志地址 */
107
106
  buildLogUrl?: string;
108
107
  /** 仅新创建开发环境时返回,表示创建开发环境的提示信息 */
@@ -111,7 +110,7 @@ export class Workspace extends CNBCore {
111
110
  sn?: string;
112
111
  /** 如果存在开发环境,则返回 WebIDE 访问 url;如果不存在开发环境,则返回启动云原生开发的 loading 页面 url 地址 */
113
112
  url: string;
114
- }> {
113
+ }>> {
115
114
  const data: { branch?: string; ref?: string } = {};
116
115
 
117
116
  if (params.branch) {