import { app } from '../../app.ts'; import { useKey } from '@kevisual/context' import { getLiveMdContent } from './live/live-content.ts'; import z from 'zod'; const notCNBCheck = (ctx: any) => { const isCNB = useKey('CNB'); if (!isCNB) { ctx.body = { title: '非 cnb-board 环境', list: [] } return true; } return false; } app.route({ path: 'cnb_board', key: 'live', description: '获取cnb-board live的mdContent内容', middleware: ['auth-admin'], metadata: { args: { more: z.boolean().optional().describe('是否获取更多系统信息,默认false'), } } }).define(async (ctx) => { const more = ctx.query?.more ?? false if (notCNBCheck(ctx)) return; const list = getLiveMdContent({ more: more }); ctx.body = { title: '开发环境模式配置', list, }; }).addTo(app); app.route({ path: 'cnb_board', key: 'live_repo_info', description: '获取cnb-board live的repo信息', middleware: ['auth-admin'] }).define(async (ctx) => { const repoSlug = useKey('CNB_REPO_SLUG') || ''; const repoName = useKey('CNB_REPO_NAME') || ''; const repoId = useKey('CNB_REPO_ID') || ''; const repoUrlHttps = useKey('CNB_REPO_UR if (notCNBCheck(ctx)) return;L_HTTPS') || ''; if (notCNBCheck(ctx)) return; // 从 repoSlug 提取仓库名称 const repoNameFromSlug = repoSlug.split('/').pop() || ''; const labels = [ { title: 'CNB_REPO_SLUG', value: repoSlug, description: '目标仓库路径,格式为 group_slug / repo_name,group_slug / sub_gourp_slug /.../repo_name' }, { title: 'CNB_REPO_SLUG_LOWERCASE', value: repoSlug.toLowerCase(), description: '目标仓库路径小写格式' }, { title: 'CNB_REPO_NAME', value: repoName || repoNameFromSlug, description: '目标仓库名称' }, { title: 'CNB_REPO_NAME_LOWERCASE', value: (repoName || repoNameFromSlug).toLowerCase(), description: '目标仓库名称小写格式' }, { title: 'CNB_REPO_ID', value: repoId, description: '目标仓库的 id' }, { title: 'CNB_REPO_URL_HTTPS', value: repoUrlHttps, description: '目标仓库 https 地址' } ] ctx.body = { title: 'CNB_BOARD_LIVE_REPO_INFO', list: labels }; }).addTo(app); // 构建类变量 app.route({ path: 'cnb_board', key: 'live_build_info', description: '获取cnb-board live的构建信息', middleware: ['auth-admin'] }).define(async (ctx) => { if (notCNBCheck(ctx)) return; const labels = [ { title: 'CNB_BUILD_ID', value: useKey('CNB_BUILD_ID') || '', description: '当前构建的流水号,全局唯一' }, { title: 'CNB_BUILD_WEB_URL', value: useKey('CNB_BUILD_WEB_URL') || '', description: '当前构建的日志地址' }, { title: 'CNB_BUILD_START_TIME', value: useKey('CNB_BUILD_START_TIME') || '', description: '当前构建的开始时间,UTC 格式,示例 2025-08-21T09:13:45.803Z' }, { title: 'CNB_BUILD_USER', value: useKey('CNB_BUILD_USER') || '', description: '当前构建的触发者用户名' }, { title: 'CNB_BUILD_USER_NICKNAME', value: useKey('CNB_BUILD_USER_NICKNAME') || '', description: '当前构建的触发者昵称' }, { title: 'CNB_BUILD_USER_EMAIL', value: useKey('CNB_BUILD_USER_EMAIL') || '', description: '当前构建的触发者邮箱' }, { title: 'CNB_BUILD_USER_ID', value: useKey('CNB_BUILD_USER_ID') || '', description: '当前构建的触发者 id' }, { title: 'CNB_BUILD_USER_NPC_SLUG', value: useKey('CNB_BUILD_USER_NPC_SLUG') || '', description: '当前构建若为 NPC 触发,则为 NPC 所属仓库的路径' }, { title: 'CNB_BUILD_USER_NPC_NAME', value: useKey('CNB_BUILD_USER_NPC_NAME') || '', description: '当前构建若为 NPC 触发,则为 NPC 角色名' }, { title: 'CNB_BUILD_STAGE_NAME', value: useKey('CNB_BUILD_STAGE_NAME') || '', description: '当前构建的 stage 名称' }, { title: 'CNB_BUILD_JOB_NAME', value: useKey('CNB_BUILD_JOB_NAME') || '', description: '当前构建的 job 名称' }, { title: 'CNB_BUILD_JOB_KEY', value: useKey('CNB_BUILD_JOB_KEY') || '', description: '当前构建的 job key,同 stage 下唯一' }, { title: 'CNB_BUILD_WORKSPACE', value: useKey('CNB_BUILD_WORKSPACE') || '', description: '自定义 shell 脚本执行的工作空间根目录' }, { title: 'CNB_BUILD_FAILED_MSG', value: useKey('CNB_BUILD_FAILED_MSG') || '', description: '流水线构建失败的错误信息,可在 failStages 中使用' }, { title: 'CNB_BUILD_FAILED_STAGE_NAME', value: useKey('CNB_BUILD_FAILED_STAGE_NAME') || '', description: '流水线构建失败的 stage 的名称,可在 failStages 中使用' }, { title: 'CNB_PIPELINE_NAME', value: useKey('CNB_PIPELINE_NAME') || '', description: '当前 pipeline 的 name,没声明时为空' }, { title: 'CNB_PIPELINE_KEY', value: useKey('CNB_PIPELINE_KEY') || '', description: '当前 pipeline 的索引 key,例如 pipeline-0' }, { title: 'CNB_PIPELINE_ID', value: useKey('CNB_PIPELINE_ID') || '', description: '当前 pipeline 的 id,全局唯一字符串' }, { title: 'CNB_PIPELINE_DOCKER_IMAGE', value: useKey('CNB_PIPELINE_DOCKER_IMAGE') || '', description: '当前 pipeline 所使用的 docker image,如:alpine:latest' }, { title: 'CNB_PIPELINE_STATUS', value: useKey('CNB_PIPELINE_STATUS') || '', description: '当前流水线的构建状态,可在 endStages 中查看,其可能的值包括:success、error、cancel' }, { title: 'CNB_PIPELINE_MAX_RUN_TIME', value: useKey('CNB_PIPELINE_MAX_RUN_TIME') || '', description: '流水线最大运行时间,单位为毫秒' }, { title: 'CNB_RUNNER_IP', value: useKey('CNB_RUNNER_IP') || '', description: '当前 pipeline 所在 Runner 的 ip' }, { title: 'CNB_CPUS', value: useKey('CNB_CPUS') || '', description: '当前构建流水线可以使用的最大 CPU 核数' }, { title: 'CNB_MEMORY', value: useKey('CNB_MEMORY') || '', description: '当前构建流水线可以使用的最大内存大小,单位为 GiB' }, { title: 'CNB_IS_RETRY', value: useKey('CNB_IS_RETRY') || '', description: '当前构建是否由 rebuild 触发' }, { title: 'HUSKY_SKIP_INSTALL', value: useKey('HUSKY_SKIP_INSTALL') || '', description: '兼容 ci 环境下 husky' } ] ctx.body = { title: 'CNB_BOARD_LIVE_BUILD_INFO', list: labels }; }).addTo(app); // PR/合并类变量 app.route({ path: 'cnb_board', key: 'live_pull_info', description: '获取cnb-board live的PR信息', middleware: ['auth-admin'] }).define(async (ctx) => { const labels = [ { title: 'CNB_PULL_REQUEST', value: useKey('CNB_PULL_REQUEST') || '', description: '对于由 pull_request、pull_request.update、pull_request.target 触发的构建,值为 true,否则为 false' }, { title: 'CNB_PULL_REQUEST_LIKE', value: useKey('CNB_PULL_REQUEST_LIKE') || '', description: '对于由 合并类事件 触发的构建,值为 true,否则为 false' }, { title: 'CNB_PULL_REQUEST_PROPOSER', value: useKey('CNB_PULL_REQUEST_PROPOSER') || '', description: '对于由 合并类事件 触发的构建,值为提出 PR 者名称,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_TITLE', value: useKey('CNB_PULL_REQUEST_TITLE') || '', description: '对于由 合并类事件 触发的构建,值为提 PR 时候填写的标题,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_BRANCH', value: useKey('CNB_PULL_REQUEST_BRANCH') || '', description: '对于由 合并类事件 触发的构建,值为发起 PR 的源分支名称,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_SHA', value: useKey('CNB_PULL_REQUEST_SHA') || '', description: '对于由 合并类事件 触发的构建,值为当前 PR 源分支最新的提交 sha,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_TARGET_SHA', value: useKey('CNB_PULL_REQUEST_TARGET_SHA') || '', description: '对于由 合并类事件 触发的构建,值为当前 PR 目标分支最新的提交 sha,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_MERGE_SHA', value: useKey('CNB_PULL_REQUEST_MERGE_SHA') || '', description: '对于由 pull_request.merged 触发的构建,值为合并后的 sha;对于 pull_request 等触发的构建,值为预合并后的 sha,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_SLUG', value: useKey('CNB_PULL_REQUEST_SLUG') || '', description: '对于由 合并类事件 触发的构建,值为源仓库的仓库 slug,如 group_slug/repo_name,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_ACTION', value: useKey('CNB_PULL_REQUEST_ACTION') || '', description: '对于由 合并类事件 触发的构建,可能的值有:created(新建PR)、code_update(源分支push)、status_update(评审通过或CI状态变更),否则为空字符串' }, { title: 'CNB_PULL_REQUEST_ID', value: useKey('CNB_PULL_REQUEST_ID') || '', description: '对于由 合并类事件 触发的构建,值为当前或者关联 PR 的全局唯一 id,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_IID', value: useKey('CNB_PULL_REQUEST_IID') || '', description: '对于由 合并类事件 触发的构建,值为当前或者关联 PR 在仓库中的编号 iid,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_REVIEWERS', value: useKey('CNB_PULL_REQUEST_REVIEWERS') || '', description: '对于由 合并类事件 触发的构建,值为评审人列表,多个以 , 分隔,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_REVIEW_STATE', value: useKey('CNB_PULL_REQUEST_REVIEW_STATE') || '', description: '对于由 合并类事件 触发的构建,有评审者且有人通过评审为 approve,有评审者但无人通过评审为 unapprove,否则为空字符串' }, { title: 'CNB_REVIEW_REVIEWED_BY', value: useKey('CNB_REVIEW_REVIEWED_BY') || '', description: '对于由 合并类事件 触发的构建,值为同意评审的评审人列表,多个以 , 分隔,否则为空字符串' }, { title: 'CNB_REVIEW_LAST_REVIEWED_BY', value: useKey('CNB_REVIEW_LAST_REVIEWED_BY') || '', description: '对于由 合并类事件 触发的构建,值为最后一个同意评审的评审人,否则为空字符串' }, { title: 'CNB_PULL_REQUEST_IS_WIP', value: useKey('CNB_PULL_REQUEST_IS_WIP') || '', description: '对于由 合并类事件 触发的构建,值为 true、false,表示 PR 是否被设置为 [WIP],否则为空字符串' } ] ctx.body = { title: 'CNB_BOARD_LIVE_PULL_INFO', list: labels }; }).addTo(app); // NPC 类变量 app.route({ path: 'cnb_board', key: 'live_npc_info', description: '获取cnb-board live的NPC信息', middleware: ['auth-admin'] }).define(async (ctx) => { if (notCNBCheck(ctx)) return; const labels = [ { title: 'CNB_NPC_SLUG', value: useKey('CNB_NPC_SLUG') || '', description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 所属仓库路径,否则为空字符串' }, { title: 'CNB_NPC_NAME', value: useKey('CNB_NPC_NAME') || '', description: '对于 NPC 事件触发的构建,值为 NPC 角色名,否则为空字符串' }, { title: 'CNB_NPC_SHA', value: useKey('CNB_NPC_SHA') || '', description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 所属仓库默认分支最新提交的 sha,否则为空字符串' }, { title: 'CNB_NPC_PROMPT', value: useKey('CNB_NPC_PROMPT') || '', description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 角色 Prompt,否则为空字符串' }, { title: 'CNB_NPC_AVATAR', value: useKey('CNB_NPC_AVATAR') || '', description: '对于 @ 知识库角色触发的 NPC 事件,值为 NPC 角色头像,否则为空字符串' }, { title: 'CNB_NPC_ENABLE_THINKING', value: useKey('CNB_NPC_ENABLE_THINKING') || '', description: '对于 @npc 事件触发的构建,值为 NPC 角色是否开启思考,否则为空字符串' } ] ctx.body = { title: 'CNB_BOARD_LIVE_NPC_INFO', list: labels }; }).addTo(app); // 评论类变量 app.route({ path: 'cnb_board', key: 'live_comment_info', description: '获取cnb-board live的评论信息', middleware: ['auth-admin'] }).define(async (ctx) => { if (notCNBCheck(ctx)) return; const labels = [ { title: 'CNB_COMMENT_ID', value: useKey('CNB_COMMENT_ID') || '', description: '对于评论事件触发的构建,值为评论全局唯一 ID,否则为空字符串' }, { title: 'CNB_COMMENT_BODY', value: useKey('CNB_COMMENT_BODY') || '', description: '对于评论事件触发的构建,值为评论内容,否则为空字符串' }, { title: 'CNB_COMMENT_TYPE', value: useKey('CNB_COMMENT_TYPE') || '', description: '对于 PR 代码评审评论,值为 diff_note;对于 PR 非代码评审评论以及 Issue 评论,值为 note;否则为空字符串' }, { title: 'CNB_COMMENT_FILE_PATH', value: useKey('CNB_COMMENT_FILE_PATH') || '', description: '对于 PR 代码评审评论,值为评论所在文件,否则为空字符串' }, { title: 'CNB_COMMENT_RANGE', value: useKey('CNB_COMMENT_RANGE') || '', description: '对于 PR 代码评审评论,值为评论所在代码行。如,单行为 L12,多行为 L13-L16,否则为空字符串' }, { title: 'CNB_REVIEW_ID', value: useKey('CNB_REVIEW_ID') || '', description: '对于 PR 代码评审,值为评审 ID,否则为空字符串' } ] ctx.body = { title: 'CNB_BOARD_LIVE_COMMENT_INFO', list: labels }; }).addTo(app);