# weaver-work-cli

面向泛微工作工具链的可扩展 TypeScript CLI 基础框架。

核心框架保持业务无关；E10 共享能力通过 `src/internal/e10` 隔离提供。新增具体业务 CLI 时，优先放在 `src/shortcuts/<name>` 下，并复用 E10 鉴权、profile、HTTP client 和命令钩子。

## 环境要求

- Node.js >= 18
- npm

## 跨平台命令约定

文档中的 CLI 示例按环境选择执行。完全相同的 `node`、`npm`、`git` 和 `weaver-work-cli` 命令可作为通用示例；涉及 JSON 输入、用户目录、文件删除/比对、Python 启动器或路径分隔符时，同时给 Windows PowerShell 和 macOS/Linux（bash/zsh）两套示例。Agent 应先判断当前环境：Windows 或 PowerShell 用 Windows 示例；macOS/Linux、bash 或 zsh 用 macOS/Linux 示例；不确定时先通过宿主环境或 `node -p "process.platform"` 判断。Windows/PowerShell 下不要使用 `printf`、`$HOME/...`、`~/...`、bash 反斜杠续行、`rm/ls/diff/python3` 等 Unix-only 写法。

简单 JSON 输入优先使用 `--input-json`，避免不同 shell 对管道和引号的处理差异：

Windows PowerShell：

```powershell
weaver-work-cli --json invoice run invoice.list --input-json '{"page_size":10,"start_pos":0}'
```

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json invoice run invoice.list --input-json '{"page_size":10,"start_pos":0}'
```

复杂 JSON 或包含多行内容时，建议先保存为 UTF-8 `.json` 文件，再按平台传给 `--input`：Windows PowerShell 用 `--input "C:\path\to\input.json"`，macOS/Linux 用 `--input "/path/to/input.json"`。当前 CLI 会兼容 Windows PowerShell 常见 UTF-8 BOM 文件。文档、提示词和 Skill Maker 生成内容都要保持这种双平台口径，不把单一平台命令写成唯一默认步骤。

## 下载后快速运行

从代码仓库获取项目：

```text
git clone GIT_REPO_URL weaver-work-cli
cd weaver-work-cli
npm install
npm run build
node dist/index.js --help
```

开发阶段可以直接从源码运行：

```text
npm run dev -- --help
npm run dev -- commands list
```

构建后的本地验证：

```text
npm run build
node dist/index.js --version
node dist/index.js commands list
node dist/index.js doctor --e10
```

## 全局安装

从当前源码目录安装到全局命令：

Windows PowerShell：

```powershell
# 在克隆到本地的仓库根目录执行（将示例路径替换为实际路径）
cd C:\path\to\weaver-work-cli
npm install
npm run build
npm link
weaver-work-cli --help
```

macOS/Linux（bash/zsh）：

```bash
# 在克隆到本地的仓库根目录执行（将示例路径替换为实际路径）
cd /path/to/weaver-work-cli
npm install
npm run build
npm link
weaver-work-cli --help
```

打包后全局安装：

Windows PowerShell：

```powershell
# 在克隆到本地的仓库根目录执行（将示例路径替换为实际路径）
cd C:\path\to\weaver-work-cli
npm run build
npm pack
npm install -g .\weaver-work-cli-0.1.2.tgz
weaver-work-cli --version
```

macOS/Linux（bash/zsh）：

```bash
# 在克隆到本地的仓库根目录执行（将示例路径替换为实际路径）
cd /path/to/weaver-work-cli
npm run build
npm pack
npm install -g ./weaver-work-cli-0.1.2.tgz
weaver-work-cli --version
```

发布到 npm 或公司私有 registry 后，可以直接安装：

```text
npm install -g weaver-work-cli
weaver-work-cli setup invoice
```

推荐把 `weaver-work-cli` 作为唯一必须安装的运行时包。发票业务命令和随包
Agent Skill 文档都包含在这个 npm 包中；普通 CLI 用户不需要再单独安装
发票 zip 包。

首次启用发票业务时：

```text
weaver-work-cli setup invoice
weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --agent_type "<当前智能体名称>"
weaver-work-cli invoice schema
```

如果要让当前 Agent 一键识别发票业务，把随包 Skill 安装到它的 skills 目录：

```text
weaver-work-cli setup invoice --install-skill
# 或只安装 Skill
weaver-work-cli skills install yepiaotong
```

`skills install yepiaotong` 会同时安装 `weaver-e10-shared` 和
`weaver-e10-yepiaotong`。CLI 会优先使用 `--target-dir` 或 `WEAVER_SKILLS_ROOT`，
否则根据当前进程的 Codex/WorkBuddy 环境信号选择当前用户的 skills 目录。
宿主不明确、候选目录冲突或其它 Agent 未提供路径时，CLI 会停止安装并要求显式指定，
避免把 Skill 写到当前 Agent 不会读取的位置：

Windows PowerShell：

```powershell
weaver-work-cli skills install yepiaotong --target-dir "$env:USERPROFILE\.workbuddy\skills"
```

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli skills install yepiaotong --target-dir "$HOME/.workbuddy/skills"
```

如果要让 Agent 根据用户提供的新业务模块文档或代码同步生成/更新业务 CLI 和
weaver 风格的业务 Skill，每次创建或更新 Skill 后先询问是否现在进行 Skill
质检，用户确认后通过随包 `weaver-skill-detector-1.0.13` 做静态质检，并在
检测通过后询问是否安装到当前智能体，可安装 Skill Maker：

```text
weaver-work-cli skills install skill-maker
```

各 Agent 环境的 skill 目录、从本地开发仓库升级到最新版的完整流程与验证
步骤见 `docs/agent-skill-install.md`。

卸载全局命令：

```text
npm unlink -g weaver-work-cli
# 或
npm uninstall -g weaver-work-cli
```

## 常用命令

```text
weaver-work-cli --help
weaver-work-cli --version
weaver-work-cli commands list
weaver-work-cli config list
weaver-work-cli docs root
weaver-work-cli docs copy
weaver-work-cli doctor --e10
weaver-work-cli setup invoice
weaver-work-cli skills list
weaver-work-cli skills read weaver-e10-yepiaotong
weaver-work-cli skills install yepiaotong
weaver-work-cli invoice schema
weaver-work-cli --json invoice run invoice.list --input-json '{"page_size":10,"start_pos":0}'
```

为了兼容 E10 使用习惯，`auth` 命令保留在顶层；同时也支持 `e10 auth` 命名空间：

```text
weaver-work-cli auth status
weaver-work-cli e10 auth status
```

## E10 鉴权方式

方式一：浏览器 OAuth2.0 登录，推荐优先使用。

```text
weaver-work-cli auth login --base-url "https://weapp.xxx.cn"
```

等价命令：

```text
weaver-work-cli auth oidc --base-url "https://weapp.xxx.cn"
```

XiaoE 环境保留两种识别入口：桌面客户端由 Agent 根据宿主/任务的明确上下文判断，并在
业务命令上传 `--runtime-agent xiaoe`；网页端沿用原有环境变量规则，成对的
`XIAOE_USER_TOKEN` + `XIAOE_DOMAIN` 会被识别为 XiaoE。判定为 XiaoE 后，Agent 显式执行：

```text
weaver-work-cli auth xiaoe
```

`auth xiaoe` 本身就是认证方式的显式选择。`hasXiaoEEnv` 通过桌面端显式参数或网页端原有
环境变量规则判断；工作目录、客户端文件或历史登录仍不作为依据。CLI 按顺序消费平台凭据、CLI 自带兼容脚本、宿主 `tokens.py`、
桌面 XiaoE 客户端会话和 `WEAVER_XIAOE_*` 映射。`XIAOE_DOMAIN` 是 XiaoE/erpa 换票
服务地址，不是 E10 地址。E10 目标按“显式 `--base-url` → 专用 `WEAVER_XIAOE_*`
E10 配置/映射 → 当前 profile → `https://www.e-cology.com.cn`”解析。

统一登录失败时，所有操作系统都返回结构化的
`authentication/xiaoe_credential_unavailable`，不会自动打开浏览器或降级到
`auth login`。确需浏览器授权时，调用方必须另行显式执行 `auth login`。

业务命令不会自动建立 XiaoE 登录态。桌面客户端需要 XiaoE 专属业务行为时，把全局参数
放在业务子命令之前，例如：

```text
weaver-work-cli --runtime-agent xiaoe workflow ...
```

网页端不要求补这个参数，仍由环境变量规则识别。`--runtime-agent` 只表示当前运行时类型，
不携带也不选择凭据。`auth login` / `auth oidc`
始终是纯浏览器 OAuth 入口，不会读取 XiaoE 凭据。

`auth xiaoe` 会先输出不含凭证的 preflight 摘要，显示 `authMode=xiaoe`、
`credentialSourceDetected`、`attempted` 及各凭据源检测结果；命中 `tokens.py` 或内置
Node 客户端会话路径时还会输出明确的尝试日志。统一登录成功后，登录结果以及后续
`auth status` 会显示 `credentialSourceStep`、`credentialSourceType`、
`credentialSourceLabel`，可据此确认实际凭证来源；`3 / xiaoe_client / XiaoE client settings`
表示未依赖 `tokens.py`，由 Node 直接换票。

默认本地回调地址为 `http://127.0.0.1:19800/callback`。如果端口被占用：

```text
weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --port 19801
```

需要区分客户端来源时，可以指定 `agent_type`。未显式传入时，CLI 会尝试从
`WEAVER_AGENT_TYPE`、`WORKBUDDY_AGENT_TYPE`、`CODEX_AGENT_TYPE` 以及
`WORKBUDDY_*` / `CODEX_*` 环境变量推断当前智能体，最后回落为 `weaver-work-cli`。
浏览器授权地址会同时携带 `agent_type=<value>` 和 `from=<value>`，其中 `from`
用于 E10 授权确认页展示客户端名称：

```text
weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --agent_type workbuddy
```

浏览器授权成功后，本地回调页会为已识别的桌面智能体显示应用打开弹层。CLI 内置
WorkBuddy（`workbuddy://`）、智能小 E（`xiaoe-client://`）和 Codex（`codex://`）映射；
其他智能体在 `agent_type` 本身是合法协议名时，默认按 `<agent_type>://` 唤起。弹层显示
“打开客户端 / 始终打开客户端 / 取消”。用户确认后，页面通过一次性令牌请求本地 CLI，
由 CLI 调用系统注册的桌面协议；浏览器不直接访问自定义
协议，因此不会再出现 Chrome/Edge 无法定制的外部协议确认框。Windows/macOS 安装包都
需要注册该协议。如果宿主要返回当前会话等更精确的位置，可传入宿主
注册的深链（仅接受桌面应用自定义协议，不接受网页、脚本或本地文件 URL）：

```text
weaver-work-cli auth login --base-url "https://weapp.xxx.cn" --agent_type workbuddy --agent-return-url "workbuddy://<宿主提供的路径>"
```

宿主也可以注入 `WEAVER_AGENT_RETURN_URL`，命令行参数优先于环境变量；客户端协议名与
`agent_type` 不一致时也应使用该覆盖。“始终打开”偏好
保存在当前本地回调地址的浏览器存储中；未识别到可用返回协议时，回调页保持原有提示，
不显示应用打开弹层。本地 CLI 打开通道默认保留 15 分钟；超过时间再点击时会自动退回
浏览器自定义协议打开方式，仍可打开应用，但浏览器可能重新显示原生协议确认框。

方式二：手动写入浏览器中的 `ETEAMSID`。

```text
weaver-work-cli auth set --eteamsid "PCACCOUNT_xxx" --base-url "https://weapp.xxx.cn"
```

如果 E10 环境需要区分 agent：

```text
weaver-work-cli auth set --eteamsid "PCACCOUNT_xxx" --base-url "https://weapp.xxx.cn" --agent_type workbuddy
```

查看登录状态：

```text
weaver-work-cli auth status
weaver-work-cli auth status --no-check
weaver-work-cli --json auth status --no-check
```

查看当前用户：

```text
weaver-work-cli auth whoami
weaver-work-cli auth whoami --live
weaver-work-cli auth whoami --json
```

清理当前 profile 的登录状态：

```text
weaver-work-cli auth logout
weaver-work-cli --profile PROFILE_NAME auth logout
```

`auth logout` 只删除本地 auth 文件，不删除 profile 配置和 keychain 密钥。

需要给子进程使用 E10 登录态时优先使用 `weaver-work-cli run ...`，两端一致可用。`weaver-work-cli auth export-weaver-env` 可按 shell 输出当前会话可执行的环境变量设置语句，默认 `--shell auto` 会在 Windows 输出 PowerShell 语法，在 macOS/Linux 输出 POSIX shell 语法；也可显式使用 `--shell powershell` 或 `--shell posix`。不要手工打印或转贴 `WEAVER_*` 值。

导出的环境变量包括：

- `WEAVER_BASE_URL`
- `WEAVER_ETEAMSID`
- `WEAVER_COOKIE`
- `WEAVER_USER_AGENT`
- `WEAVER_AGENT_TYPE`

通过 `weaver-work-cli run` 执行外部命令时，CLI 会在前置钩子中自动注入上述 `WEAVER_*` 变量：

```text
weaver-work-cli run node -e "console.log(process.env.WEAVER_BASE_URL || 'no e10 auth')"
```

如必须导出到当前 shell，按环境选择：

Windows PowerShell：

```powershell
Invoke-Expression (weaver-work-cli auth export-weaver-env --shell powershell)
```

macOS/Linux（bash/zsh）：

```bash
eval "$(weaver-work-cli auth export-weaver-env --shell posix)"
```

## Profile 管理

鉴权文件默认由 `weaver-work-cli` 独立管理：

- 鉴权根目录：通过 `weaver-work-cli auth root` 查看；默认位于用户主目录下的 `.weaver-work-cli/e10`（Windows PowerShell：`$env:USERPROFILE\.weaver-work-cli\e10`；macOS/Linux：`$HOME/.weaver-work-cli/e10`）
- Profile 目录：`profiles/<profile>`（位于鉴权根目录下）
- 当前激活 profile：`profile`（位于鉴权根目录下）
- Keychain service/account：`weaver-work-cli` / `auth-key`

CLI 默认不读取、不迁移 `e10-login` / `e10-cli` 的旧 `.e10-cli` 登录态；
打包给用户后，用户必须通过 `weaver-work-cli auth login` 或
`weaver-work-cli auth set` 建立本插件自己的登录状态。确需复用外部 auth 文件时，
可以显式使用 `--auth <path>` 或 `E10_AUTH_PATH`。

常用 profile 命令：

```text
weaver-work-cli auth profile list
weaver-work-cli auth profile current
weaver-work-cli auth profile use PROFILE_NAME
weaver-work-cli --profile PROFILE_NAME auth status
```

支持的鉴权相关环境变量：

- `E10_AUTH_PATH`：指定 auth 文件路径
- `WEAVER_WORK_CLI_AUTH_ROOT`：覆盖 `weaver-work-cli` 的独立鉴权根目录
- `E10_ACCOUNT` / `E10_PASSWORD`：session 过期且需要 RSA 自动重登时使用

## 目录结构

```text
src/
  index.ts                   CLI bin 入口。
  cmd/                       Commander root、注册协议和顶层命令。
    index.ts                 汇总内置 cmd。
    root.ts                  程序创建、全局 flags 和版本信息。
    register.ts              命令注册、hooks 注册和 action 包装。
    action.ts                统一错误处理和命令生命周期。
    config/                  通用本地配置命令。
    docs/                    包内文档和模板复制命令。
    doctor/                  环境检查命令。
    commands/                已注册命令查看命令，兼容 module 别名。
    run/                     带前置/后置钩子的外部命令执行器。
    e10/                     E10 auth/profile 命令和 run-hook 环境注入。
    skills/                  包内 Agent Skill 读取命令。
  core/                      业务无关的上下文、钩子、配置、输出和错误处理。
  internal/                  只供本包复用的实现细节。
    e10/                     E10 session、auth 文件、API client。
    skills/                  Skill 内容读取和路径保护。
  shortcuts/                 面向人和 Agent 的高层业务命令。
    index.ts                 汇总内置 shortcuts。
    invoice/                 E10 业票通发票 shortcut、operation manifest 和业务执行器。
      operations/            按业务执行逻辑拆分的 operation class。
  services/                  可复用的非业务服务。
docs/                        随包发布的 CLI 文档。
skills/                      随包发布的 Agent Skills；入口 SKILL.md 做路由，细节放 references/。
  weaver-e10-shared/    所有业务 Skill 共享的安装、认证、JSON 和写入规则。
  weaver-e10-yepiaotong/   业票通业务 Skill。
skill-template/              新增或重写业务 Skill 时使用的模板来源，参考 lark-cli 的 skill-template。
templates/                   后续 shortcut 脚手架模板。
scripts/                     维护脚本。
test/                        Node 内置测试运行器测试。
```

## 命令分层

`weaver-work-cli` 采用与 `lark-cli` 接近的三层组织：

- `cmd`：稳定的系统/平台命令，例如 `config`、`doctor`、`run`、`e10 auth`、`skills list/read`。
- `shortcuts`：业务友好的高层命令，例如 `invoice list`、`invoice import prepare`；按 `src/shortcuts/<business-module>` 分模块维护，模块内每个业务 operation 用相对独立的 class 封装。
- `skills`：Agent 可读的使用说明和引用文件；按 `skills/weaver-e10-<business-module>` 分模块维护。业务入口 `SKILL.md` 只放路由、前置要求和执行原则，具体 operation、工作流、错误处理放到 `references/*.md`；公共规则只放在 `weaver-e10-shared`。
- `skill-template`：维护者新增业务 Skill 时的模板来源，作用类似飞书 CLI 的 `skill-template`。`master-skill-template.md` 用于未来聚合入口，`skill-template.md` 用于单业务 Skill，`business-info.json` 记录路由关键词，`domains/*.md` 记录当前业务域应拆出的 reference 与 Agent 调优重点。

## 新增业务 Shortcut

业务 shortcut 统一创建在 `src/shortcuts/<name>` 下，不和框架内置命令、E10 共享适配混放。
配套 Agent Skill 统一创建在 `skills/weaver-e10-<name>`：

```text
skills/weaver-e10-<name>/
  SKILL.md                  入口路由：共享 Skill、schema、reference 索引、禁用边界。
  product.json              可选：业务 Skill 包元信息。
  references/
    <name>-agent-entry.md   CLI 入口、schema、stdin JSON、输出约定。
    <name>-read.md          查询/详情类只读 operation。
    <name>-write.md         prepare/apply 写入协议、确认和回查。
```

新增业务模块时默认同步维护 `src/shortcuts/<name>` 和 `skills/weaver-e10-<name>`。Skill Maker 会参考 `skill-template/skill-template.md`、`skill-template/domains/<name>.md` 和自身 CLI 生成规范；入口文件不要复制大段接口细节，让 Agent 按用户意图读取最小必要 reference。

当前已内置 `yepiaotong` shortcut，用于 E10 业票通发票管理。典型调用：

```text
weaver-work-cli --json invoice list --input-json '{"page_size":10,"start_pos":0}'
weaver-work-cli --json invoice enterprise list --input-json '{"page_size":10,"start_pos":0}'
weaver-work-cli --json invoice get --fid INVOICE_FID
weaver-work-cli --json invoice upload --file ./invoice.pdf
weaver-work-cli --json invoice ocr preview --file ./invoice.pdf
weaver-work-cli --json invoice validate preview --fid INVOICE_FID --scope personal
weaver-work-cli --json invoice download --fid INVOICE_FID --file-id FILE_ID --output ./invoice.pdf
```

写操作固定使用 `prepare -> apply`：

Windows PowerShell：

```powershell
weaver-work-cli --json invoice run invoice.ocr.preview --input-json '{"file":"./invoice.pdf"}'

weaver-work-cli --json invoice import prepare --input-json '{"file":"./invoice.pdf","validate":true,"syncToOa":true}'

weaver-work-cli --json invoice import apply --input-json '{"file":"./invoice.pdf","continuation":"PREPARE_CONTINUATION","confirm":true}'
```

macOS/Linux（bash/zsh）：

```bash
weaver-work-cli --json invoice run invoice.ocr.preview --input-json '{"file":"./invoice.pdf"}'

weaver-work-cli --json invoice import prepare --input-json '{"file":"./invoice.pdf","validate":true,"syncToOa":true}'

weaver-work-cli --json invoice import apply --input-json '{"file":"./invoice.pdf","continuation":"PREPARE_CONTINUATION","confirm":true}'
```

Agent 应优先调用 `weaver-work-cli --json invoice run <operation> --input-json '<json>'`，复杂输入或文件化输入使用 `--input <path>`，并读取 `weaver-work-cli invoice schema` 获取 operation 契约。共享、转让和标签接口因缺少稳定 ID 来源和写后回查契约，当前不暴露给 Agent。

输出处理参考飞书 CLI 的 Agent 友好实践，但当前项目仍是轻量输出层：`ctx.output.write` 在 JSON 模式下直接把完整 envelope 打到 stdout，失败写 stderr；暂未提供通用 `--jq`、`--format table/ndjson` 或 `--page-all`。因此业务 Skill 会预先要求 Agent 控制响应量：列表默认 `page_size=10`，按业务分页字段继续；给用户只渲染摘要、关键字段、数量和下一步，超长详情/OCR/调试 JSON 优先落本地文件并返回路径。

如需交付通用 Agent Skill 包：

```text
npm run build:skill -- invoice
# 或保留的 invoice 便捷命令
npm run build:yepiaotong-skill
```

输出文件默认在 `dist/weaver-e10-yepiaotong-skill.zip`。该 zip 主要用于
Codex/Agent 平台导入、离线分发或版本归档；日常推荐直接通过 npm 包内置内容
执行 `weaver-work-cli skills install yepiaotong`。zip 包包含
`weaver-e10-yepiaotong` 和共享的 `weaver-e10-shared`，要求用户本机已安装
可用的 `weaver-work-cli`。
如需打包全部 Skill：

```text
npm run build:skill
```

示例：新增 `workflow` shortcut。

```text
src/shortcuts/workflow/
  index.ts
  operations/
    list.ts
```

`src/shortcuts/workflow/index.ts`：

```ts
import { WorkflowListOperation } from './operations/list.js';
import type { CliCommand } from '../../types.js';

const listOperation = new WorkflowListOperation();

export const workflowShortcut: CliCommand = {
  name: 'workflow',
  description: '流程业务命令',
  category: 'shortcut',
  register({ program, action }) {
    const workflow = program.command('workflow').description('流程业务命令');

    workflow.command('list')
      .description('查询流程列表')
      .action(action(async (ctx) => {
        ctx.output.write(await listOperation.execute(ctx));
      }));
  },
};
```

`src/shortcuts/workflow/operations/list.ts`：

```ts
import { requireE10Client } from '../../../internal/e10/context.js';
import type { CliContext } from '../../../core/context.js';

export class WorkflowListOperation {
  async execute(ctx: CliContext): Promise<unknown> {
    const client = await requireE10Client(ctx);
    return client.post('/api/xxx/workflow/list', {});
  }
}
```

然后在 `src/shortcuts/index.ts` 中注册：

```ts
import { invoiceShortcut } from './invoice/index.js';
import { workflowShortcut } from './workflow/index.js';
import type { CliCommand } from '../types.js';

export const builtinShortcuts: CliCommand[] = [
  invoiceShortcut,
  workflowShortcut,
];
```

验证命令是否注册成功：

```text
npm run build
node dist/index.js commands list
node dist/index.js workflow --help
```

如果业务 shortcut 不依赖 E10，只使用 `ctx.output`、`ctx.config`、`ctx.hooks` 等通用能力即可；如果依赖 E10，优先复用 `requireE10Client(ctx)` 或 `requireE10Session(ctx)`，不要在 shortcut 里重复实现登录态读取、cookie 拼接或 HTTP header 注入。

## 发布前验证

```text
npm run build
npm test
npm pack --dry-run
```
