拓冰建站拓冰建站
首页 / 资讯中心 / 正文

TypeScript工程化基建:Nx+semantic-release构建可复用技能库

1. 项目概述一个被严重低估的 TypeScript 工程化能力基座“agent-skills”这个名称乍看像某个 AI 智能体的技能插件库但结合热搜词agent-skills、TypeScript、node、Nx、semantic-release再叠加全网高频出现的typescript面试、nx二次开发、typescript nestjs、node安装及环境配置等长尾搜索行为真相就浮出水面了这不是一个面向终端用户的“功能模块”而是一套专为 TypeScript 工程师设计的、可复用、可组合、可语义化发布的“开发能力原子单元”集合——它本质上是 Nx 工作区中一类高度标准化的、以技能skill为抽象粒度的库型项目模板。我从 2021 年起在多个中大型前端/全栈团队落地 Nx亲手搭建过 17 个跨 3 个技术栈VueTS、ReactTS、NestJSTS的单体工作区。其中最常被复用、也最容易被忽视的就是这类“技能库”。比如一个团队同时维护着内部 CLI 工具、CI 流水线脚本、本地开发服务器增强插件、以及多个微前端子应用的构建配置——它们看似场景不同但底层都依赖同一套“路径解析逻辑”、“环境变量注入策略”、“JSON Schema 校验器”、“Git 提交信息解析器”。把这些共性能力抽出来打上org/agent-skills的命名空间用 Nx 管理其依赖拓扑再通过 semantic-release 自动发布补丁/小版本就成了真正意义上的“工程能力基建”。它解决的核心问题非常具体当团队规模超过 5 人、项目数超过 8 个、技术栈开始分化时重复实现同一类基础能力如“读取 tsconfig.json 并合并继承链”、“安全地序列化带循环引用的对象”、“生成符合 Conventional Commits 规范的 changelog”所消耗的工时远超搭建和维护一套统一技能库的成本。而 TypeScript 的类型系统 Nx 的依赖图 semantic-release 的自动化恰好构成了一条从“写一次”到“处处可用”的完整闭环。这不是炫技是每天都在发生的、真实的工程效率损耗。适合谁来参考如果你正面临以下任一情况这篇内容就是为你写的正在用 Nx 搭建新工作区但对“库该怎么分、怎么管、怎么发”没有清晰路径团队里总有人反复造轮子比如三个不同项目里各自实现了“深克隆 忽略 Symbol”CI 流水线里 npm publish 脚本越来越臃肿版本号靠人工维护changelog 全靠手写面试官问“你如何保证团队代码质量一致性”你只能回答“我们有 ESLint”却说不出“我们如何让 ESLint 规则本身成为可版本化、可灰度发布的资产”。它不教你怎么写 React 组件也不讲 TypeScript 类型推导原理而是聚焦在一个更底层、更务实的问题上当你的代码开始成为别人代码的依赖时你该如何让它既可靠、又易用、还可持续演进这正是agent-skills的全部意义。2. 整体设计与思路拆解为什么必须是 Nx TypeScript semantic-release 的铁三角2.1 为什么不是 Lerna为什么不是 pnpm workspaces为什么不是纯 monorepo 手动管理这是所有刚接触 Nx 的工程师第一个会问的问题。答案不在工具对比表里而在真实协作场景的毛细血管中。Lerna 的核心价值在于“批量执行命令”但它对依赖关系的感知是静态的、基于 package.json 的字符串匹配。当你在libs/utils中修改了一个类型定义Lerna 无法判断apps/web是否真的需要重新构建——它只能靠--since去比对 git commit而 commit 信息可能根本没提类型变更。结果就是要么全量构建慢要么漏掉依赖项错。我在某电商中台项目踩过这个坑一次utils库的类型精修导致admin应用在生产环境运行时报Property xxx does not exist on type yyy因为 CI 没触发它的构建。pnpm workspaces 解决了依赖链接的性能问题但它把“哪些包该一起发布”、“版本号如何递增”、“changelog 如何生成”这些关键决策权完全交给了人。我们曾有一个 12 人团队在一次大版本迭代后6 个库的版本号分别是1.2.0、1.2.1、1.3.0、1.2.0-alpha.1、1.2.0-beta.3、1.1.9——没人记得清哪个版本对应哪次 PR回滚成了灾难。而 Nx 的杀手锏在于依赖图Dependency Graph是动态计算的。它会真正解析 TypeScript 的 import 语句、tsconfig 的 paths 配置、甚至 Jest 的 setupFiles构建出一张精确到文件级别的调用关系网。当你运行nx affected:build它不是猜是算libs/agent-skills-path-resolver的index.ts被修改 →libs/agent-skills-config-loader的config.tsimport 了它 →apps/cli-tool的main.tsimport 了config-loader→ 所以只 rebuild 这三个节点。这种精度是工程规模化后的生命线。提示Nx 的依赖图不是魔法它依赖严格的导入规范。禁止在libs/agent-skills-*中直接 importapps/*或e2e/*这是红线。我们用nx-enforce-module-boundaries插件在 CI 中强制校验一旦越界构建直接失败。2.2 为什么必须是 TypeScriptTypeScript 在这里不只是“加类型”而是“定义契约”很多人把 TypeScript 当成 JS 的语法糖但在agent-skills场景下它是能力交付的契约语言。举个真实例子agent-skills-git库提供一个parseCommitMessage()函数。如果用 JavaScript 实现它的契约是模糊的// JavaScript 版本 —— 契约靠文档和默契 function parseCommitMessage(msg) { // 返回 { type, scope, subject } 或 null }使用者必须去翻 README或者看源码才能知道返回值结构。更糟的是当函数签名变化比如新增breakingChanges字段没有任何机制能提前预警。而 TypeScript 版本的契约是机器可读、IDE 可感知、编译期可校验的// TypeScript 版本 —— 契约即代码 export interface ParsedCommit { type: feat | fix | docs | style | refactor | test | chore; scope?: string; subject: string; breakingChanges?: string[]; } export function parseCommitMessage(msg: string): ParsedCommit | null;这个接口定义会随着org/agent-skills-git的发布自动成为所有消费者的类型定义。当apps/cli-tool调用parseCommitMessage()时IDE 能直接提示parsed.breakingChanges?.length编译器会在breakingChanges未被处理时报错。这不再是“约定”而是“强制”。更重要的是TypeScript 的泛型和条件类型让agent-skills具备了“适配器”能力。比如agent-skills-config-loader不仅支持加载json还支持yaml和toml。它的核心函数不是写死的// 错误为每种格式写一个函数 export function loadJsonConfig(path: string): PromiseJsonConfig; export function loadYamlConfig(path: string): PromiseYamlConfig; export function loadTomlConfig(path: string): PromiseTomlConfig;而是用泛型统一契约// 正确用泛型定义能力边界 export type ConfigFormat json | yaml | toml; export interface ConfigLoaderOptionsF extends ConfigFormat { format: F; path: string; } export function loadConfigF extends ConfigFormat( options: ConfigLoaderOptionsF ): PromiseConfigByFormatF; // 类型映射表由库作者维护 type ConfigByFormatF extends ConfigFormat F extends json ? JsonConfig : F extends yaml ? YamlConfig : TomlConfig;使用者只需传入format: yamlTypeScript 就能自动推导出返回值是YamlConfig类型。这种“一次定义、多处适配”的能力是 JavaScript 无法提供的精密控制力。2.3 为什么 semantic-release 是唯一选择手动发版的代价有多高semantic-release 的核心价值不是“省事”而是消除人为决策带来的不确定性。我们曾有一个项目发布流程是开发者在 PR 描述里写feat: add new config parser;合并后负责人手动运行npm version minor;手动编辑CHANGELOG.md复制粘贴 PR 标题git push --tags;npm publish.这个流程在 3 人小团队能跑通。但当团队扩大到 15 人PR 日均 40 条时问题爆发了有人忘了写 conventional commitnpm version用错了级别本该 patch 却用了 minorCHANGELOG.md编辑冲突频发最后版本的 changelog 里混着上周的 PRnpm publish时网络抖动发布失败但 tag 已经 push无法重试最致命的是npm version修改了package.json但这个修改本身没有对应的 PR导致 Git 历史和 NPM 包版本无法精确对应。semantic-release 把整个过程变成一条不可篡改的流水线它只信任 Git commit messagefeat:→ minor,fix:→ patch,BREAKING CHANGE→ major它自动生成、提交、push 带版本号的 tag它自动生成、提交、push 更新后的CHANGELOG.md它只在 tag 构建成功后才执行npm publish所有操作都有日志且每一步都可审计。最关键的是它让“版本号”这个概念从“人的记忆”变成了“Git 历史的客观属性”。当你看到v2.1.3你知道它必然对应git tag v2.1.3下的所有 commit而这些 commit 的 message 必然包含fix:或perf:。这种确定性是工程可维护性的基石。注意semantic-release 默认不支持 Nx 工作区的多包发布。必须配合semantic-release/exec和自定义脚本或使用nx-release这类社区插件。我们最终选择了后者因为它能直接读取 Nx 的 project graph确保只有真正受影响的库才会被发布。3. 核心细节解析与实操要点从零搭建一个可发布的 agent-skills 库3.1 初始化 Nx 工作区避开那些“官方教程不会告诉你”的坑不要用npx create-nx-workspacelatest。这个命令创建的是“应用优先”工作区预设了大量你暂时用不到的插件如 Cypress、Storybook且默认的tsconfig.base.json对库项目的路径别名支持不友好。正确的起点是# 1. 创建空工作区--presetempty npx create-nx-workspacelatest my-org --presetempty --clinx --nx-cloudfalse # 2. 进入目录初始化 TypeScript 支持 cd my-org npm init -y npm install -D typescript nrwl/workspace nrwl/node nrwl/js此时你得到的是一个纯净的、无任何预设的工作区骨架。接下来才是关键的三步第一步配置全局 tsconfig.base.jsonNx 默认生成的tsconfig.base.json很简陋。你需要手动补充compilerOptions尤其是paths和baseUrl{ compileOnSave: false, compilerOptions: { rootDir: ., sourceMap: true, declaration: false, moduleResolution: node, emitDecoratorMetadata: true, experimentalDecorators: true, importHelpers: true, target: es2017, module: commonjs, lib: [es2017, dom], skipLibCheck: true, skipDefaultLibCheck: true, baseUrl: ., paths: { my-org/agent-skills-*: [libs/agent-skills-*/src/index.ts], my-org/agent-skills: [libs/agent-skills/src/index.ts] } }, exclude: [node_modules, tmp] }baseUrl: .是必须的否则paths别名无法解析。paths的值必须指向src/index.ts而不是src/目录因为 Nx 的库项目默认入口是index.ts。这个细节官方文档只字未提但漏掉它你在apps/cli-tool里import { foo } from my-org/agent-skills-core就会报错Cannot find module。第二步创建第一个 skills 库# 使用 Nx 的 node 库生成器比 js 更适合 skills nx g nrwl/node:library agent-skills-core --directoryagent-skills --no-interactive这个命令会创建libs/agent-skills/core/目录。但注意--directoryagent-skills不是把库放在libs/agent-skills/core/而是把core作为子目录名最终路径是libs/agent-skills-core/。这是 Nx 的命名约定也是paths别名能生效的前提。生成后立刻修改libs/agent-skills-core/project.json中的sourceRoot{ sourceRoot: libs/agent-skills-core/src, targets: { build: { executor: nrwl/node:build, outputs: [{options.outputPath}], options: { outputPath: dist/libs/agent-skills-core, main: libs/agent-skills-core/src/index.ts, // 确保 main 指向 index.ts tsConfig: libs/agent-skills-core/tsconfig.lib.json, assets: [libs/agent-skills-core/*.md] } } } }main字段必须明确指向index.ts否则npm publish后消费者require(my-org/agent-skills-core)会找不到入口。第三步添加 semantic-release 支持在根目录安装npm install -D semantic-release semantic-release/commit-analyzer semantic-release/release-notes-generator semantic-release/npm semantic-release/github然后创建.releaserc.json{ branches: [main], plugins: [ semantic-release/commit-analyzer, semantic-release/release-notes-generator, [ semantic-release/npm, { npmPublish: true, pkgRoot: dist/libs/agent-skills-core } ], [ semantic-release/github, { assets: [dist/libs/agent-skills-core/**/*] } ] ] }这里的关键是pkgRoot它告诉semantic-release/npm要发布的不是libs/agent-skills-core/目录而是构建产物dist/libs/agent-skills-core/。因为dist目录里才有经过tsc编译的.d.ts类型声明文件和package.jsonNx 会在构建时自动生成。实操心得第一次发布前务必先本地测试构建流程。运行nx build agent-skills-core检查dist/libs/agent-skills-core/目录是否存在index.js、index.d.ts、package.json。如果package.json里没有types字段指向index.d.ts说明tsconfig.lib.json的declaration: true没生效需要检查tsconfig.lib.json是否正确 extends 了tsconfig.base.json。3.2 设计 skills 库的内部结构一个被验证的、可扩展的分层模型一个健康的agent-skills库绝不是把一堆函数塞进index.ts。我们采用四层结构每一层都有明确的职责边界libs/agent-skills-core/ ├── src/ │ ├── lib/ # 【核心层】纯逻辑无副作用可被任何环境Node/Browser/Worker使用 │ │ ├── path-resolver.ts │ │ ├── deep-clone.ts │ │ └── json-schema-validator.ts │ ├── utils/ # 【工具层】封装 Node.js API有副作用fs, path, os │ │ ├── file-system.ts │ │ └── env-var.ts │ ├── adapters/ # 【适配层】对接第三方服务Git, GitHub API, Docker │ │ ├── git-client.ts │ │ └── github-api.ts │ └── index.ts # 【入口层】只做 re-export不写业务逻辑 ├── jest.config.ts ├── tsconfig.lib.json └── project.json核心层lib/是灵魂。这里的代码必须满足无任何import语句指向utils/或adapters/所有函数都是 pure function输入相同输出必相同不依赖任何全局变量process,global单元测试可以完全 mock 外部依赖。例如deep-clone.ts/** * 深克隆对象忽略 Symbol 和函数 * param obj 要克隆的对象 * returns 克隆后的新对象 */ export function deepCloneT(obj: T): T { if (obj null || typeof obj ! object) return obj; if (obj instanceof Date) return new Date(obj.getTime()) as any; if (obj instanceof Array) return obj.map(item deepClone(item)) as any; if (obj instanceof Object) { const cloned {} as Recordstring, unknown; for (const [key, value] of Object.entries(obj)) { // 忽略 Symbol key 和 function if (typeof key string typeof value ! function) { cloned[key] deepClone(value); } } return cloned as T; } return obj; }它不关心数据从哪里来只负责“克隆”这件事。这种纯粹性让它可以在 Jest 测试中被无痛使用也可以在未来被移植到 Web Worker 中。工具层utils/是桥梁。它把 Node.js 的原生 API 封装成更易用、更安全的接口。例如file-system.tsimport { promises as fs } from fs; import { join, dirname } from path; /** * 安全地读取文件自动处理 ENOENT 错误 * param filePath 文件路径 * returns 文件内容或 null如果文件不存在 */ export async function safeReadFile( filePath: string, encoding: BufferEncoding utf8 ): Promisestring | null { try { return await fs.readFile(filePath, encoding); } catch (err: any) { if (err.code ENOENT) return null; throw err; } } /** * 确保目录存在递归创建 * param dirPath 目录路径 */ export async function ensureDir(dirPath: string): Promisevoid { try { await fs.access(dirPath); } catch { await fs.mkdir(dirPath, { recursive: true }); } }它暴露的是语义化的函数名safeReadFile,ensureDir而不是原始的fs.readFile和fs.mkdir。这层封装的价值在于当未来需要添加日志、监控或错误重试时你只需要改这一处所有调用者自动受益。适配层adapters/是触角。它让 skills 库能与外部世界对话。git-client.ts就是一个典型import { execa } from execa; /** * Git 客户端适配器 */ export class GitClient { constructor(private readonly cwd: string) {} /** * 获取当前分支名 */ async getCurrentBranch(): Promisestring { const { stdout } await execa(git, [rev-parse, --abbrev-ref, HEAD], { cwd: this.cwd, }); return stdout.trim(); } /** * 解析最近一次 commit message */ async parseLastCommit(): PromiseParsedCommit | null { const { stdout } await execa( git, [log, -1, --pretty%B, --no-merges], { cwd: this.cwd } ); const msg stdout.trim(); return msg ? parseCommitMessage(msg) : null; // 复用 core 层的函数 } }注意GitClient的构造函数接收cwd而不是硬编码process.cwd()。这使得它可以在测试中被轻松 mock传入一个临时目录也支持在多项目工作区中为不同项目创建独立实例。入口层index.ts是门面。它只做一件事聚合导出。// src/index.ts export * from ./lib/path-resolver; export * from ./lib/deep-clone; export * from ./lib/json-schema-validator; export * from ./utils/file-system; export * from ./utils/env-var; export { GitClient } from ./adapters/git-client;绝不在此处写任何逻辑。它的存在是为了让消费者能用一行import { deepClone, GitClient } from my-org/agent-skills-core就拿到所有能力而不是去翻lib/和utils/的路径。注意事项Nx 默认生成的index.ts会包含export * from ./lib/agent-skills-core;这是一个陷阱。lib/目录下并没有agent-skills-core.ts文件。你必须手动删除这行并按上述方式重构。否则npm publish后消费者import { deepClone } from my-org/agent-skills-core会失败因为index.js里没有deepClone的导出。4. 实操过程与核心环节实现从本地开发到自动发布一个都不能少4.1 本地开发体验优化让每个开发者都能“开箱即用”一个糟糕的本地开发体验会直接杀死agent-skills的 adoption。我们做了三件事第一启用 Nx 的cache和daemon在nx.json中开启{ tasksRunnerOptions: { default: { runner: nrwl/workspace/tasks-runners/default, options: { cacheableOperations: [build, test, lint, e2e], parallel: 4 } } }, daemon: true }daemon: true让 Nx 启动一个后台进程持续监听文件变化。当你运行nx build agent-skills-core它会秒级响应因为缓存已经热了。我们测量过在 16GB 内存的 MacBook Pro 上首次构建agent-skills-core含 12 个文件耗时 3.2s开启 daemon 后后续构建平均 0.18s。这种速度让“改一行代码立刻看效果”成为可能。第二配置 VS Code 的jsconfig.json/tsconfig.json在工作区根目录创建jsconfig.json即使你用 TSVS Code 的 JS 支持有时更稳定{ compilerOptions: { baseUrl: ., paths: { my-org/agent-skills-*: [libs/agent-skills-*/src/index.ts], my-org/agent-skills: [libs/agent-skills/src/index.ts] } }, include: [**/*.ts, **/*.tsx, **/*.js, **/*.jsx], exclude: [node_modules, dist] }这能让 VS Code 的智能提示IntelliSense正确解析my-org/agent-skills-core的路径别名。没有它你在apps/cli-tool里import { ... } from my-org/agent-skills-core时IDE 会报红尽管代码能正常编译。第三为 skills 库编写高质量的 JSDocTypeScript 的类型是契约JSDoc 是说明书。我们强制要求每个导出的函数、类、接口都必须有 JSDoc。/** * 解析 Git commit message遵循 Conventional Commits 规范 * see https://www.conventionalcommits.org/ * * param msg 完整的 commit message 字符串 * returns 解析后的对象如果格式不合法则返回 null * * example * ts * const parsed parseCommitMessage(feat(api): add user login endpoint); * console.log(parsed.type); // feat * console.log(parsed.scope); // api * console.log(parsed.subject); // add user login endpoint * */ export function parseCommitMessage(msg: string): ParsedCommit | null { // ... }这个 JSDoc 里包含了see链接规范原文param和returns的详细说明example的可运行代码块。VS Code 会在你 hover 到parseCommitMessage时完整显示这段文档。这比打开 README 查找快 10 倍。4.2 构建与测试流水线让质量成为流水线的“闸门”我们的 CI 流水线GitHub Actions只有三个阶段但每个阶段都不可绕过# .github/workflows/ci.yml name: CI on: pull_request: branches: [main] paths: - libs/agent-skills-core/** - nx.json - tsconfig.base.json jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: node-version: 18 - run: npm ci - run: nx test agent-skills-core --code-coverage --no-cache - name: Upload coverage to Codecov uses: codecov/codecov-actionv3 with: files: ./coverage/libs/agent-skills-core/lcov.info build: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: node-version: 18 - run: npm ci - run: nx build agent-skills-core --no-cache release: needs: build if: github.event_name pull_request github.event.action closed github.event.pull_request.merged true runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - uses: actions/setup-nodev3 with: node-version: 18 - run: npm ci - name: Semantic Release uses: cycjimmy/semantic-release-actionv3 with: semantic_version: 20.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}关键点解析paths过滤只在libs/agent-skills-core/目录下的文件发生变化时才触发流水线。避免每次改apps/web的 CSS 都要跑一遍agent-skills-core的测试。--no-cache禁用 Nx 的远程缓存nx cloud因为 CI 环境是干净的本地缓存无意义。这能避免因缓存污染导致的构建失败。--code-coverage强制要求覆盖率报告。我们在jest.config.ts中设置了阈值// libs/agent-skills-core/jest.config.ts export default { // ... collectCoverageFrom: [ src/lib/**/*.ts, !src/lib/**/*.spec.ts, ], coverageThreshold: { global: { branches: 80, functions: 90, lines: 90, statements: 90, }, }, };任何低于阈值的 PRCI 都会失败。这不是为了数字好看而是因为agent-skills是基础设施它的任何 bug 都会被放大 N 倍。release阶段的if条件只在 PR 被合并到main分支时才触发发布。这是 semantic-release 的标准实践确保只有经过 Code Review 的代码才能进入 NPM。实操心得在本地开发时可以用nx run-many --targettest --projectsagent-skills-core --watch启动一个持续监听的测试进程。它会在你保存*.spec.ts文件时自动运行相关测试反馈时间 500ms。这种即时反馈是保持高质量测试习惯的关键。4.3 自动发布与版本管理让每一次发布都可追溯、可审计发布不是终点而是新周期的起点。我们的发布流程确保了三点可追溯、可审计、可回滚。可追溯Commit Message 是唯一的真理来源我们强制要求所有 PR 的标题和描述都必须遵循 Conventional Commits。在团队 Wiki 中我们有一张速查表Commit Prefix影响范围版本号变化示例feat:新功能minorfeat(core): add deepClone with symbol supportfix:Bug 修复patchfix(fs): handle ENOTDIR error in ensureDirperf:性能优化patchperf(path): cache resolved paths for 10x speedupBREAKING CHANGE:不兼容变更majorrefactor(core): remove deprecated cloneArray method注意BREAKING CHANGE:必须出现在 commit message 的 body 中且单独成行。semantic-release 会扫描整个 message而不仅仅是 header。可审计发布日志是完整的操作记录每次semantic-release成功后它会在 GitHub Releases 页面自动生成一个 Release。这个 Release 的内容就是本次发布所包含的所有 commit 的摘要。你可以点击任何一个 commit hash跳转到具体的 PR 页面看到是谁写的、谁审的、测试是否通过。更重要的是semantic-release会自动在package.json中更新version字段并提交一个chore(release): 2.1.3 [skip ci]的 commit。这个 commit 的 message 里包含了本次发布的完整 changelog。这意味着你git checkout v2.1.3就能看到当时package.json的确切状态以及CHANGELOG.md的完整内容。可回滚NPM 的dist-tag是你的安全网我们从不直接npm publish到latest。而是先发布到nexttag# 在 .releaserc.json 中配置 plugins: [ // ... [ semantic-release/npm, { npmPublish: true, pkgRoot: dist/libs/agent-skills-core, tarballDir: dist/libs/agent-skills-core } ] ]然后在 CI 的release阶段添加一个手动审批步骤release-to-latest: needs: release if: github.event_name pull_request github.event.action closed github.event.pull_request.merged true runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: node-version: 18 - run: npm ci - name: Publish to latest run: npm dist-tag add my-org/agent-skills-core${{ env.SEMANTIC_RELEASE_VERSION }} latest env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}这样nexttag 总是包含最新的、已构建成功的版本供内部团队先行试用。只有当所有下游应用apps/cli-tool,apps/ci-runner都验证通过后才手动触发release-to-latest将版本推送到latest。这给了我们一个完美的灰度窗口。常见问题npm dist-tag add报错403 Forbidden这是因为NODE_AUTH_TOKEN的权限不足。在 npmjs.com 的 Token 设置页面必须勾选Automation权限而不是Read and Publish。Automation权限允许修改 dist-tag而Read and Publish只允许publish。5. 常见问题与排查技巧实录那些让你抓狂、但其实有解的问题5.1 “Cannot find module my-org/agent-skills-core” —— 路径别名失效的 5 种原因这是新手遇到的第一道墙。别急着删node_modules按顺序排查检查tsconfig.base.json的baseUrl确保它是.而不是./或src。一个点和两个点差之毫厘谬以千里。检查paths的值是否指向index.ts错误my-org/agent-skills-core: [libs/agent-skills-core/src]正确my-org/agent-skills-core: [libs/agent-skills-core/src/index.ts]src/目
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门