@ doist/ cli- core (英语)
DOist CLI项目的共有核心公用事业待办事宜- cli, (中文(简体) ), 扭曲曲线, (中文(简体) ), 大纲- cli, (中文(简体) ), 通讯cli) (中文(简体) ).
TypeScript, 仅限无害环境管理, 第24节, npm 第11节.
安装
npm install @doist/cli-core
里面有什么东西
| 模块 | 主要出口 | 目的 |
|---|---|---|
auth (顺从) | attachLoginCommand, (中文(简体) ), attachLogoutCommand, (中文(简体) ), attachStatusCommand, (中文(简体) ), attachTokenViewCommand, (中文(简体) ), attachRefreshTokenViewCommand, (中文(简体) ), attachAccountListCommand, (中文(简体) ), attachAccountUseCommand, (中文(简体) ), attachAccountCurrentCommand, (中文(简体) ), attachAccountRemoveCommand, (中文(简体) ), runOAuthFlow, (中文(简体) ), refreshAccessToken, (中文(简体) ), createPkceProvider, (中文(简体) ), createDcrProvider, (中文(简体) ), createSecureStore, (中文(简体) ), createKeyringTokenStore, (中文(简体) ), migrateLegacyAuth, (中文(简体) ), persistBundle, (中文(简体) ), bundleFromExchangePKCE的帮手们, AuthProvider 编号 : TokenStore 编号 : TokenBundle 编号 : SetBundleOptions 编号 : ActiveBundleSnapshot 编号 : RefreshInput 编号 : RefreshHandshake 编号 : RefreshHandshakeContext 编号 : TokenRefreshOptions 编号 : AccountRef 编号 : ClearedAccount 编号 : SecureStore 编号 : UserRecordStore 编号 : CredentialStore 类型, AttachLogoutRevokeContext 编号 : AttachAccountListContext 编号 : AttachAccountCurrentContext 编号 : AttachAccountRemoveContext | OAuth 运行时加上指挥官随从 <cli> [auth] login 编号 : logout 编号 : status 编号 : token 编号 : refresh-token view 财务报告和审定财务报表 <cli> account list 编号 : use 编号 : current 编号 : remove。 。 。 . attachLogoutCommand 接受可选选项 revokeToken 取消最佳服务器侧符号的钩子 。 标准公共客户-PKCE流量(createPkceProvider,带有RFC 8707资源指标和客户端ID元文件客户端ID),RFC 7591动态客户端注册流(createDcrProvider,带有可选的 RFC 8707 资源指标,更新支持,以及 loadClient编号 :saveClient 客户端缓存),是一款薄的跨平台OS键环包(英语:createSecureStore)和多账户密钥环支持 TokenStore (单位:千美元)createKeyringTokenStore),具有严格的系统,明确的平面文字,以及倒置存储政策. 商店合同支持可选 setBundle(account, bundle) 写法( 需要使用 KeyringTokenStore因此,消费者需要更新的持久性可以选择通过 TokenBundle· ; active() 保持收缩( 访问令牌 + 只账户) , 因此不需要刷新状态的呼叫者不会支付额外的密钥环 IPC . AuthProvider 财务报告和审定财务报表 TokenStore 仍为完全开口后端的逃生舱口(装置代码、魔法链接.). logout 编号 : status 编号 : token 编号 : refresh-token view 总是附加 --user <ref> 将解析的校正线线线线线线线线线线线线线线线线线到匹配的商店(store.active(ref), (中文(简体) ), store.activeBundle(ref),或 store.clear(ref)) (中文(简体) ). status 财务报告和审定财务报表 token 选择 refresh (单位:千美元)TokenRefreshOptions)所以一个已到期的访问符通过 refreshAccessToken 在探测或打印之前. commander (当使用接线器), open (浏览器发射), @napi-rs/keyring (当使用时) createSecureStore 键盘 TokenStore),以及 oauth4webapi (当消费者选择无声清新或使用时) createDcrProvider)是可选的对等/可选分解. |
command-token | findCommandToken, (中文(简体) ), needsExtensionLookup, (中文(简体) ), CommandToken | 查找哪个 argv 条目在未解析其它命令的情况下命名命令, 跳过取值根旗(--user, (中文(简体) ), --progress-jsonl ),并决定是否需要查阅已安装的扩展. |
commands (顺从) | registerChangelogCommand, (中文(简体) ), registerUpdateCommand (+ semver 帮助者) | Cli-core标准命令(如:. <cli> changelog, (中文(简体) ), <cli> update, (中文(简体) ), <cli> update switch) (中文(简体) ). 要求数 commander 作为可选的同龄人. |
config | getConfigPath, (中文(简体) ), readConfig, (中文(简体) ), readConfigStrict, (中文(简体) ), writeConfig, (中文(简体) ), updateConfig, (中文(简体) ), CoreConfig, (中文(简体) ), UpdateChannel | 读取/写入每 CLI JSON 配置文件并输入出错代码 ; CoreConfig 是cli-core本身拥有的字段形状(扩展为每个CLI字段). |
empty | printEmpty | 打印一个已锁定的空状态信件 --json 编号 : --ndjson 编号 : --ids-only 所以机器消费者从来没有看到 人类的弦 stdout. |
errors | CliError | 输入的 CLI 错误类 code 和退出代码映射. |
extensions (顺从) | createExtensionManager, (中文(简体) ), registerExtensionCommands, (中文(简体) ), registerExtensionGroup, (中文(简体) ), registerExtensionPassThrough, (中文(简体) ), createExtension, (中文(简体) ), listTemplates, (中文(简体) ), defaultTemplatesDir, (中文(简体) ), satisfiesRange, (中文(简体) ), findManifestProblems, (中文(简体) ), mapWithConcurrency, (中文(简体) ), ExtensionManagerOptions 编号 : ExtensionManager 编号 : Extension 编号 : ExtensionListing 编号 : ExtensionCommands 编号 : ExtensionErrorCode 类型 | A 类 gh- 任意 CLI 的样式扩展系统: 发现 <bin>-* 数据 dir 下的目录, 从 GitHub 发布、 git 克隆或本地路径安装、 升级、 删除、 从已运出模板中脚手架以及发送 <bin> <name> … 到可执行文件 <PREFIX>_* 环境合同. 要求数 commander 财务报告和审定财务报表 zod 作为可选的对等程序. |
global-args | parseGlobalArgs, (中文(简体) ), stripUserFlag, (中文(简体) ), createGlobalArgsStore, (中文(简体) ), createAccessibleGate, (中文(简体) ), createSpinnerGate, (中文(简体) ), getProgressJsonlPath, (中文(简体) ), isProgressJsonlEnabled | 解析出全球知名旗帜(--json, (中文(简体) ), --ndjson, (中文(简体) ), --ids-only, (中文(简体) ), --quiet, (中文(简体) ), --verbose, (中文(简体) ), --accessible, (中文(简体) ), --no-spinner, (中文(简体) ), --progress-jsonl, (中文(简体) ), --user <ref>),并从中衍生出上游. stripUserFlag 删除 --user 从 argv 得到的符号, 这样当旗帜没有根程序附件时, 清除的阵列就可以转发给指挥官 . |
ids | formatIds, (中文(简体) ), outputIds | 每行格式或发出一个稳定字符串或数字ID。 空结果保持沉默;可选 pagination 通知到 stderr . |
json | formatJson, (中文(简体) ), formatNdjson | 稳定 JSON / 新行限制 JSON 为 stdout 格式化 . |
markdown (顺从) | preloadMarkdown, (中文(简体) ), renderMarkdown, (中文(简体) ), TerminalRendererOptions | Lazy-init终端 标记倒计时器. 要求数 marked 财务报告和审定财务报表 marked-terminal-renderer 作为同位素- 只有当您的 CLI 使用此子路径时才会安装 . |
options | OUTPUT_MODES, (中文(简体) ), OutputMode, (中文(简体) ), ViewOptions, (中文(简体) ), ListViewOptions, (中文(简体) ), resolveOutputMode | (一) 集体产出模式合同; ListViewOptions 添加 idsOnly?,解决者拒绝相互冲突的机器输出旗帜. |
paths | getDataDir, (中文(简体) ), getStateDir | 用户数据和应用程序名称的状态目录, XDG 变量优先, 平台默认第二, 同时 getConfigPath。 。 。 . |
spinner | createSpinner | 装入自旋器工厂包装 yocto-spinner 带有禁用门. |
stdin | readStdin, (中文(简体) ), StdinErrorCode | 为 a 读取已接通的 stdin 尾端 --stdin 旗; 掷出 STDIN_IS_TTY 在交互式终端上,而不是等待EOF. |
terminal | isCI, (中文(简体) ), isStderrTTY, (中文(简体) ), isStdinTTY, (中文(简体) ), isStdoutTTY | TTY / CI 检测助手. |
testing (顺从) | describeEmptyMachineOutput, (中文(简体) ), createTestProgram, (中文(简体) ), captureConsole, (中文(简体) ), captureStream, (中文(简体) ), mockStdin, (中文(简体) ), writeFixtureExtension, (中文(简体) ), writeFakeGitRepo, (中文(简体) ), buildTokenStore, (中文(简体) ), buildSingleEntryStore, (中文(简体) ), ingenEntries, (中文(简体) ), alanGrant 编号 : ellieSattler 编号 : ianMalcolm, (中文(简体) ), TestAccount 编号 : StoreEntry 编号 : TokenStoreHarness 编号 : MatchAccount 编号 : MockStdinOptions 类型 | 通过消耗 CLIs 重新使用 Vitest 辅助器件 + 固定器件: 被默认的空状态套件( )--json 编号 : --ndjson /人类模式); 指挥官测试-程序构建器(createTestProgram; 整个子路径 需求 commander (a) 控制台/stdout-stder间谍,使其沉默+自动恢复(captureConsole 编号 : captureStream,在测试中呼叫或 beforeEach(三); mockStdin交换 process.stdin 用于管道(或终端) isTTY: true以验后还;以克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克 TokenStore 模拟加共享账户固定装置buildTokenStore 编号 : buildSingleEntryStore) 建模 createKeyringTokenStore默认选择合同——通过 matchAccount 以镜像消费者自己的反比(数字标识/大小写不敏感标签);以及 writeFixtureExtension 编号 : writeFakeGitRepo,在磁盘上设置了可运行的扩展 extensions 子路径可以通过一个真实的过程来行使. |
usage-tracking | createUsageTracking, (中文(简体) ), UsageTracking, (中文(简体) ), UsageTrackingOptions, (中文(简体) ), UsageHeadersOptions, (中文(简体) ), CommandLike | 请求头条每个Doist CLI发送 所以后端可以属性 API流量: User-Agent: <cli>/<version>, (中文(简体) ), doist-platform: cli, (中文(简体) ), doist-version, (中文(简体) ), doist-os (单位:千美元)macos编号 :linux编号 :windows编号 :unknown),逐项请求 request-id UUID, a (美国) session-id 每个跟踪器的UUID(每个进程创建一个跟踪器,这样它就可以分组一个CLI运行), cli-command (主动命令作为低心率标签,如 task:add),以及 cli-agent (运行 CLI 的 AI 编码代理, 创建 tracker 时从环境中读取一次 : claude-code, (中文(简体) ), codex, (中文(简体) ), cursor, (中文(简体) ), gemini, (中文(简体) ), copilot, (中文(简体) ), opencode, (中文(简体) ), other - 对于清单以外的代理人,或 none) (中文(简体) ). 打电话 setCommand(actionCommand) 从a preAction 钩子( 它需要任何 CommandLike,所以根条目不需要 commander),然后通过 headers() 给 SDK 客户端或 mergeHeaders(init) 变成生 fetch。 。 。 . request-id 和SDK的分开 X-Request-Id,这是只通过一些写作发送的一能键. |
使用量
全球参数 + 旋转门
import { createGlobalArgsStore, createSpinnerGate, createSpinner } from '@doist/cli-core'
const store = createGlobalArgsStore()
export const isJsonMode = () => store.get().json
const shouldDisableSpinner = createSpinnerGate({
envVar: 'TD_SPINNER',
getArgs: store.get,
})
const { withSpinner } = createSpinner({ isDisabled: shouldDisableSpinner })
空状态打印
import { printEmpty } from '@doist/cli-core'
if (tasks.length === 0) {
printEmpty({ options, message: 'No tasks found.' })
return
}
读取管状 stdin
readStdin 备份命令 --stdin 旗帜。 当无一物被吹入其中时,它就抛出 STDIN_IS_TTY 而不是等待永不降临的EOF. 它返回未调整的输入,因此命令决定了空输入的含义.
import { CliError, readStdin } from '@doist/cli-core'
if (options.stdin && options.description !== undefined) {
throw new CliError('CONFLICTING_OPTIONS', 'Cannot use both --description and --stdin')
}
const description = options.stdin ? await readStdin() : options.description
在测试中, mockStdin 从 @doist/cli-core/testing 代表烟斗:
mockStdin('# Notes\n\nBody.\n') // or mockStdin('', { isTTY: true }) for a terminal
测试助手( 子路径)
重用共享的vitest脚手架而不是按 CLI 手卷:
import { attachStatusCommand } from '@doist/cli-core/auth'
import {
alanGrant,
buildSingleEntryStore,
captureConsole,
createTestProgram,
} from '@doist/cli-core/testing'
it('greets the active account', async () => {
const logSpy = captureConsole() // silences console.log, auto-restores
const { store } = buildSingleEntryStore({ token: 'tok', account: alanGrant })
const program = createTestProgram((p) =>
attachStatusCommand(p, { store, renderText: (ctx) => `Signed in as ${ctx.account.email}` }),
)
await program.parseAsync(['node', 'cli', 'status'])
expect(logSpy).toHaveBeenCalledWith('Signed in as [email protected]')
})
buildTokenStore 是一个状态的记忆 TokenStore 模拟。 对于一个CLI,它的商店匹配 refs
不同于默认的 id/ email/ label 规则, pass matchAccount 所以模拟解析 恰好
像生产:
import { buildTokenStore } from '@doist/cli-core/testing'
import { matchTwistAccount } from './lib/auth-provider.js'
const { store } = buildTokenStore<TwistAccount>({
entries: [{ account: ACCOUNT_ALAN, isDefault: true }],
matchAccount: matchTwistAccount,
})
整个 @doist/cli-core/testing 入口链接 commander (桶再出口)
createTestProgram和 vitest 在导入时, 安装两个 消耗性CLI 已有它们
并仅从测试文件导入此子路径 .
Markdown 渲染( 可选子路径)
在消耗的 CLI 中安装对等求解 :
npm install marked marked-terminal-renderer
然后说:
import { preloadMarkdown, renderMarkdown } from '@doist/cli-core/markdown'
if (!options.json && !options.raw) {
await preloadMarkdown()
}
console.log(await renderMarkdown(comment.body))
如果同位素缺失, preloadMarkdown 向安装命令投掷一个明确的错误。 TypeScript 也会在等同位素被安装之前无法解决子路径的类型.
标准命令( 可选子路径)
在消耗的 CLI 中安装对等点 :
npm install commander
然后电线 <cli> changelog 在一个电话中:
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { registerChangelogCommand } from '@doist/cli-core/commands'
import packageJson from '../package.json' with { type: 'json' }
registerChangelogCommand(program, {
path: join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'CHANGELOG.md'),
repoUrl: 'https://github.com/todoist/todoist-cli',
version: packageJson.version,
})
帮手扔出 CliError (单位:千美元)INVALID_TYPE 为了一个坏 --count, (中文(简体) ), FILE_READ_ERROR 如果文件无法被读取),所以CLI的顶级错误处理器格式和退出.
线 <cli> update 财务报告和审定财务报表 <cli> update switch 同样:
import { createSpinner, getConfigPath } from '@doist/cli-core'
import { registerUpdateCommand } from '@doist/cli-core/commands'
import packageJson from '../package.json' with { type: 'json' }
const { withSpinner } = createSpinner()
registerUpdateCommand(program, {
packageName: '@doist/todoist-cli',
currentVersion: packageJson.version,
configPath: getConfigPath('todoist-cli'),
changelogCommandName: 'td changelog',
brewFormula: 'doist/tap/todoist-cli',
withSpinner,
})
以自己的针锋相对的形式发布的CLI
registerUpdateCommand(program, {
packageName: '@doist/automations-cli',
currentVersion: packageJson.version,
configPath: getConfigPath('tda'),
distTag: 'internal',
withSpinner,
})
update 检查已配置的频道的 npm dist-tag(stable → latest, (中文(简体) ), pre-release → next页:1 currentVersion外壳 npm i -g (或 pnpm add -g 若为 npm_execpath 说明pnpm。 当CLI通过Homebrew安装时(其二进制解为酿酒) Cellar,它运行 brew upgrade <brewFormula> 相反——设定 brewFormula 在酿酒分配的CLIs上(酿酒公式可能落后于npm发布,因此在公式被撞出之前升级可以是一个不开放的. update switch --stable | --pre-release 翻转所坚持的 update_channel 字段通过 updateConfig保存任何兄弟的钥匙 两个子命令都接受 --json 编号 : --ndjson错误为 CliError (单位:千美元)INVALID_FLAGS, (中文(简体) ), UPDATE_CHECK_FAILED, (中文(简体) ), UPDATE_INSTALL_FAILED,或犬类 CONFIG_* 如果配置文件被损坏,则代码).
distTag 替换在自己的标签下发布的 CLI 频道映射( 仅邀请的 CLI on) internal说吧 它从被绑的标签上安装, update switch 财务报告和审定财务报表 --channel 未注册, 配置文件从不读取, 人类输出和机器记录都携带 distTag 否则它们将携带 channel。 。 。 .
两个动作均为 --json 编号 : --ndjson 所以无论消费者在它的"根"程序上声明还是"让" update 拥有它们。 没有其它东西是继承的:一个恰好被命名的根选项 --check 或者说 --channel 没有任何影响 update。 。 。 .
施舍者parseVersion, (中文(简体) ), compareVersions, (中文(简体) ), isNewer, (中文(简体) ), getInstallTag, (中文(简体) ), fetchLatestVersion, (中文(简体) ), getConfiguredUpdateChannel)也导出,用于注册命令以外的临时用途.
扩展( 可选子路径)
给一个CLI gh- 样式扩展系统: <bin> extension list | create | install | upgrade | remove | exec附加时 <bin> <name> … 用于每个已安装的扩展名。 扩展名是一个名为 <bin>-<name> 持有同名可执行文件;主机以 stdio 继承它并用它的退出代码退出它。 在消耗的 CLI 中安装对等求解 :
npm install commander zod
所有特定主机都通过 ExtensionManagerOptions数字 :
import { realpathSync } from 'node:fs'
import { dirname } from 'node:path'
import { getConfigPath, getDataDir, getStateDir } from '@doist/cli-core'
import { createExtensionManager, registerExtensionCommands } from '@doist/cli-core/extensions'
import chalk from 'chalk'
import packageJson from '../package.json' with { type: 'json' }
const APP_NAME = 'todoist-cli'
function buildExtensionManager(program: Command) {
// Snapshot the built-in names before any extension is registered, so an
// extension can never be measured as shadowing itself.
const reserved = new Set([
...program.commands.flatMap((command) => [command.name(), ...command.aliases()]),
'extension',
'ext',
'completion-server',
])
return createExtensionManager({
binName: 'td',
envPrefix: 'TD',
version: packageJson.version,
dataDir: getDataDir(APP_NAME),
stateDir: getStateDir(APP_NAME),
configDir: dirname(getConfigPath(APP_NAME)),
hostPath: realpathSync(process.argv[1] ?? ''),
reservedNames: () => reserved,
officialSource: { host: 'github.com', owner: 'todoist' },
officialLabel: 'Todoist',
// Kept away from npm lifecycle scripts when an extension's
// dependencies are installed, on top of GH_TOKEN and GITHUB_TOKEN.
secretEnvVars: ['TODOIST_API_TOKEN'],
isAccessible,
theme: { dim: chalk.dim, bold: chalk.bold, green: chalk.green, yellow: chalk.yellow },
})
}
registerExtensionCommands(program, buildExtensionManager(program))
registerExtensionCommands 登记 extension 组(别名) ext)和每个安装的扩展下一个通过命令 Extensions: 帮助组。 与内置连接的扩展名报告为: shadowed 内 list 并保持可达到通过 extension exec。 。 。 .
懒惰地装入命令的CLI通常需要将两者分开. registerExtensionGroup 这是 extension 组,只装入 <bin> extension …。 。 。 . registerExtensionPassThrough 盘索夫