@xinhe-oa/cli 0.0.0-stage → 0.2.0
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/README.md +159 -2
- package/bin/xinhe-oa-cli.js +13 -0
- package/package.json +40 -4
- package/src/commands/application.js +244 -0
- package/src/commands/approval.js +275 -0
- package/src/commands/attachment.js +202 -0
- package/src/commands/bitable.js +3040 -0
- package/src/commands/config.js +186 -0
- package/src/commands/dashboard.js +132 -0
- package/src/commands/doctor.js +975 -0
- package/src/commands/event.js +151 -0
- package/src/commands/file.js +1474 -0
- package/src/commands/index.js +78 -0
- package/src/commands/investor.js +179 -0
- package/src/commands/member.js +1341 -0
- package/src/commands/org.js +399 -0
- package/src/commands/report.js +908 -0
- package/src/commands/schema.js +75 -0
- package/src/commands/session.js +516 -0
- package/src/commands/skills.js +1117 -0
- package/src/commands/tenant.js +202 -0
- package/src/commands/workflow.js +176 -0
- package/src/core/args.js +901 -0
- package/src/core/auth.js +349 -0
- package/src/core/checkpoint.js +226 -0
- package/src/core/config.js +407 -0
- package/src/core/error.js +217 -0
- package/src/core/http.js +903 -0
- package/src/core/import.js +753 -0
- package/src/core/output.js +300 -0
- package/src/core/runtime.js +302 -0
- package/src/core/util.js +195 -0
- package/src/core/values.js +278 -0
- package/src/core/xlsx.js +321 -0
- package/src/main.js +205 -0
- package/src/spec/agent_guide.md +55 -0
- package/src/spec/command_surface.json +27073 -0
- package/src/spec/embedded_skills.json +19 -0
- package/src/version.js +16 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_by": "cli-node/scripts/gen-embedded-skills.py",
|
|
3
|
+
"note": "由 cli-node/scripts/gen-embedded-skills.py 生成,**不要手改**:改正文请改仓库 skills/<名字>/SKILL.md,然后重跑生成器(--check 会挡住漂移)。",
|
|
4
|
+
"count": 2,
|
|
5
|
+
"skills": [
|
|
6
|
+
{
|
|
7
|
+
"name": "xo-admin",
|
|
8
|
+
"path": "skills/xo-admin/SKILL.md",
|
|
9
|
+
"bytes": 7929,
|
|
10
|
+
"content": "# xo-admin · xinhe-oa-cli 使用说明(super_admin 版)\n\n**这个技能解决什么**:平台管理员怎么建企业、怎么任命企业负责人、怎么调整某人的权限、\n怎么看全平台。**只有 `super_admin` 拿得到这一份**(其他人看到的是\"不可用\")。\n\n> 日常操作(登录、多维表格、传数据、附件)在 `xo-core` 里 —— 那一份**所有人都有**。\n> 本技能只讲平台级的事。\n\n---\n\n## 1. 你的身份决定了你能碰什么\n\nsuper_admin 是**平台运维**,不是\"权限更大的企业负责人\"。差别在于:\n\n| | 企业负责人 `tenant_owner` | 平台管理员 `super_admin` |\n|---|---|---|\n| 范围 | **一家企业**(自己的) | **全平台**(所有企业) |\n| 建企业 | ✗ | ✓ |\n| 任命企业负责人 | ✗ | ✓ |\n| 跨企业查看 | ✗ | ✓ |\n| 审计检索 | 本企业 | 全平台 |\n| 数据面 | 本企业全量读写 | 全量 |\n\n**一道刻意的边界**:企业负责人**不能**给自己或别人提升成 super_admin —— 平台级能力不下放。\n企业里的事务(建人、赋角、背书)**应当由企业负责人自己做**,你不要替他做;\n你的职责是\"把企业和他的人建起来,然后交出去\"。\n\n---\n\n## 2. 建一家企业\n\n```bash\nxinhe-oa-cli tenant --create --id kashu --name 开叔企业 --store \"S01:总院\" --json\nxinhe-oa-cli tenant --list --json # 你能看到哪些企业\nxinhe-oa-cli tenant --show kashu --json # 某家企业的详情(含门店)\n```\n\n- `--id` 是**企业 id**(小写字母/数字/下划线),它会成为所有数据的归属键,**建后不可改**,选的时候想清楚;\n- `--store` 可重复:`S01:总院`(编码:显示名)。门店编码会被拼成门店 id `<企业id>-<S01>`;\n- **幂等**:同一个 id 重复建返回 `created=false`,不会重复建;改名会被拒绝(企业名出现在所有历史归属上,静默改名就是历史漂移)。\n\n---\n\n## 3. 建人并赋角(一步完成)\n\n```bash\n# 建企业负责人\nxinhe-oa-cli member --create --phone 13900000001 --name 开叔 \\\n --role tenant_owner --tenant kashu --json\n\n# 建投资人(只声明\"他是投资人\",还看不到任何数据 —— 见 §4)\nxinhe-oa-cli member --create --phone 13900000002 --name 秦总 \\\n --role investor --tenant kashu --json\n\n# 也可以建平台管理员(慎用)\nxinhe-oa-cli member --create --phone <号码> --name <姓名> --role super_admin --json\n```\n\n**四个角色的口径**:\n\n| `--role` | 能力 | 典型用途 |\n|---|---|---|\n| `super_admin` | 全平台一切 | 你的同事(**少给**) |\n| `tenant_owner` | 本企业全量读写 + **管人** | 企业负责人 |\n| `tenant_member` | 本企业**读/写/建表**,**不能管人、不能导出** | 业务员、财务 |\n| `tenant_operator` | 只读写**被点名的表**(可限门店) | 只看某块数据的人 |\n| `investor` | 只能读**被企业背书**的那家企业(只读) | 投资人 |\n\n返回值里有 **`user_id`** —— 后面赋角/背书/撤权都要用它。\n\n```bash\nxinhe-oa-cli member --list --json # 全平台的人(super_admin 视角;上限 500 条,truncated=true = 可能被截断)\nxinhe-oa-cli member --list --user <user_id> --json # 看某人展开后的全部授权\n# 找人不靠\"拉全量再本地筛\":只记得姓名 / 号码 / 部门时用检索\nxinhe-oa-cli member --search --name 小唐 --json\nxinhe-oa-cli member --search --phone 13800000001 --json # 号码只做精确匹配,响应只回脱敏值\nxinhe-oa-cli member --search --name 张伟 --dept <dept_id> --json # 同名多人时用部门收窄\n```\n\n---\n\n## 4. 调整权限\n\n### 4.1 改角色(最常用)\n\n```bash\n# 换角色(会先撤掉旧角色模板带来的授权,再按新角色展开)\nxinhe-oa-cli member --revoke-role tenant_member --user <user_id> --tenant kashu --reason \"转岗\" --json\nxinhe-oa-cli member --list --user <user_id> --json # 确认展开结果\n```\n\n**撤角会同时吊销那个人的会话链** —— 他手上的令牌立刻失效。这是有意的(撤权要立即生效)。\n\n### 4.2 投资人:两道门\n\n投资人的可见性 = **平台声明**(你赋 `investor` 角色)∩ **企业背书**(企业负责人签发)。\n\n```bash\nxinhe-oa-cli investor --list --json # 谁在等企业确认 / 已生效\nxinhe-oa-cli investor --endorse --user <user_id> --tenant kashu \\\n --reason \"投资人可查看全部数据\" --json # ← 这是\"代企业确认\"\n```\n\n**代企业确认是例外路径**:正常流程是**企业负责人自己**在 `xo-core` §7 里签发。\n只有在企业还没建好人、或企业明确要求你代办时才用,并且要在 `--reason` 里写清楚是谁授权的 ——\n这条会写进审计,事后要能回答\"谁批准的\"。\n\n### 4.3 收回\n\n```bash\nxinhe-oa-cli investor --revoke --user <user_id> --tenant kashu --reason \"合作结束\" --json\nxinhe-oa-cli member --remove <user_id> --reason \"离职\" --json # 移除成员(历史保留)\n```\n\n---\n\n## 5. 看全平台(审计与排障)\n\n```bash\nxinhe-oa-cli event --list --json # 变更事件流\nxinhe-oa-cli event --subscriptions --json # 谁订阅了什么\nxinhe-oa-cli tenant --list --json # 企业全景\nxinhe-oa-cli doctor --json # 本机客户端自检\n```\n\n**每一次读写都留痕**,包括**被拒绝的越权尝试**(那是安全信号)。\n有人报\"看不到数据\"时,别猜:让对方跑 `me --json` 把 `roles` / `capabilities` / `scope` 贴给你,\n再对照 `member --list --user <user_id>` 的展开结果 —— 差异一眼可见。\n\n---\n\n## 6. 建完企业之后的交接(标准动作)\n\n一个新企业的上线顺序,照着做就不会漏:\n\n```bash\n# ① 建企业(含门店)\nxinhe-oa-cli tenant --create --id <企业id> --name <企业名> --store \"S01:总部\" --json\n\n# ② 建企业负责人(拿返回里的 user_id)\nxinhe-oa-cli member --create --phone <负责人手机号> --name <姓名> \\\n --role tenant_owner --tenant <企业id> --json\n\n# ③ 告诉他三句话:\n# - 用 xinhe-oa-cli login --phone <他的手机号> 登录;\n# - 本企业的人由他自己用 member --create 建(角色选 tenant_member / tenant_operator);\n# - 投资人要看他家的数据,由他 investor --endorse 点头。\n```\n\n**之后这家企业的日常事务就是他的了。** 你的介入面只剩:跨企业查看、审计、以及企业负责人离职时的换人。\n\n---\n\n## 7. 红线\n\n- **不要替企业做决定**:企业里谁能看什么,是那家企业负责人的事(背书就是为此存在的);\n- **不要滥用 `tenant_owner`/`super_admin` 的授予**:多一个负责人就多一把能开全部数据的钥匙;\n- **不要用 `--reason` 走形式**:撤权、背书、代确认都要写清\"谁批准的\",那是事后追责的唯一凭据;\n- 高风险写操作要显式 `--yes`(那是给人留的确认点);\n- 令牌不进 argv、不进日志(与 `xo-core` §0 同一条纪律)。\n\n---\n\n## 8. 退出码\n\n与 `xo-core` §8 完全一致(`0` 成功 / `2` 用法 / `3` 未登录 / `4` 权限不足 / `5` 冲突 /\n`6` 服务端 / `7` 网络 / `8` 版本过旧)。**super_admin 也会拿到 `4`** ——\n当某条能力是平台不下放的(例如数据面的某些写动作被 deny 规则挡住),照样会被拒。\n\n拿到 `8`(`cli.too_old`)时的动作和别的角色一样:`xinhe-oa-cli doctor --upgrade`\n(用已有登录凭据下载 + sha256 校验 + 原地替换;**退出码 0 不代表升成功**,\n看 `data.upgrade.replaced`)。想先看清楚会发生什么就加 `--dry-run` —— 那是**预演**,\n只问一次只读的 `/cli/latest`,不下载、也不替换自己。口径见 `xo-core` §9.1。\n"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "xo-core",
|
|
14
|
+
"path": "skills/xo-core/SKILL.md",
|
|
15
|
+
"bytes": 24188,
|
|
16
|
+
"content": "# xo-core · xinhe-oa-cli 使用说明(所有角色)\n\n**这个技能解决什么**:告诉你这台机器上唯一的客户端 `xinhe-oa-cli` 怎么用 ——\n怎么登录、怎么看自己的权限、怎么建多维表格、怎么把文件里的数据传上去、怎么装别的技能。\n**读完这一份,日常的活你就能干了。**\n\n> 本技能对**所有已登录用户**可用。平台层面的操作(建企业、任命企业负责人、跨企业查看)\n> 在 `xo-admin` 里,只有 super_admin 拿得到。\n\n---\n\n## 0. 三条铁律(先读这个)\n\n1. **只有命令面**:一切操作都通过 `xinhe-oa-cli` 的子命令。**没有**裸调接口、**没有** SQL 逃生口、\n 没有任意路径 —— 想干的事如果命令面里没有,就是平台不提供,不要试图绕。\n2. **令牌你看不到**:登录后凭据由 CLI 自己维护(`~/.config/xinhe-oa-cli/credentials.json`,0600)。\n 不要试图读它、不要打印它、不要写进任何文件或提示词。\n3. **权限跟着令牌走**:你能看到什么,取决于你这个身份被授了什么。\n 被拒不是 bug —— 退出码 `4` 就是\"去要权限\"的信号(下面 §6 有怎么要)。\n\n---\n\n## 1. 第一次使用\n\n```bash\n# ① 指向后端(唯一必须人工提供的东西)\nxinhe-oa-cli config --set base_url=http://<后端地址>:8100\n\n# ② 登录(两步:发码 → 校验)\nxinhe-oa-cli login --phone <手机号> # 返回里有 dev_code(开发模式回显)\nxinhe-oa-cli login --phone <手机号> --code <验证码> # 成功后凭据落盘,之后不用再登\n\n# ③ 我是谁、能做什么、缺什么\nxinhe-oa-cli me --json\n\n# ④ 装技能(把服务端发布的技能拉到本地,供你读)\nxinhe-oa-cli skills --install --dest ./skills\nxinhe-oa-cli skills --list # 看有哪些;内嵌的那份不需要联网\nxinhe-oa-cli skills --read xo-core # 直接读某一份\n```\n\n**access 令牌只活 10 分钟**,CLI 会自动续期。续不回来时(refresh 也过期)会返回**退出码 3**,\n提示里直接给出重新登录的命令 —— 照着做即可,不要反复重试。\n\n---\n\n## 2. 我是谁、我有什么权限\n\n```bash\nxinhe-oa-cli me --json\n```\n\n返回里要紧的几块:\n\n| 字段 | 含义 |\n|---|---|\n| `user.roles` | 我的角色(`super_admin` / `tenant_owner` / `tenant_member` / `investor`) |\n| `scope.tenants` | 我能看到哪些企业 |\n| `scope.stores` | 我能看到哪些门店 |\n| `capabilities` | 我能做哪些动作(决定命令面里哪些能用) |\n\n**看不到的东西不会告诉你\"存在\"** —— 这是刻意的(避免用报错探测别人的数据)。\n\n---\n\n## 3. 多维表格(主命令面)\n\n企业自建的多维表格承担\"数据库\"的角色。层次是:**库(base) → 表(table) → 字段(field) → 记录(record)**,\n外加**视图(view)** 与**表单(form)** 两种使用界面。\n\n### 3.1 建结构\n\n```bash\nxinhe-oa-cli bitable base --create --name 财务数据 --key finance --json\nxinhe-oa-cli bitable table --create --base <base_id> --name 公账流水 --key bank_flow --json\nxinhe-oa-cli bitable field --create --table <table_id> --name 金额 --type currency --semantic-key amount --json\n```\n\n> `--semantic-key` 是**机器名**(建后不可变),`--name` 是显示名(可改)。\n> 下游脚本与 AI 认语义键,所以**改名不会打断任何东西**。\n\n字段类型(`--type`):`text` `number` `currency` `date` `single_select` `multi_select` `boolean` `person` `attachment_ref` `relation`。\n\n### 3.2 写与读\n\n```bash\n# 写一条\nxinhe-oa-cli bitable record --create --table <table_id> --set 金额=12000 --set 摘要=\"诊疗收入\" --json\n\n# 读:默认只取一页;--all 自动翻完;要自己翻页用 --limit + --offset\n# (返回里有 next_offset / has_more:下一把直接拿 next_offset 当 --offset)\n# 排序不用自己拼\"打破并列\"的键:CLI 会自动补唯一的 id(返回里的 order 就是实际发出去的那份)。\n# --all 翻完会自检:有重复行 / 与 total 对不上 → 退出码 5、cli.paging_unstable\n# (多半是翻页期间有人在写这张表,重跑一次即可;要一致的快照用视图的游标分页)\nxinhe-oa-cli bitable record --list --table <table_id> --all --json\nxinhe-oa-cli bitable record --list --table <table_id> --limit 1000 --offset 1000 --json\n\n# 按字段筛行(例:按日期区间取数,1 万行也一样):过滤在**客户端**做(表取数接口没有过滤参数,\n# --all 先翻完再筛),字段写 **semantic_key**(`bitable field --list` 里的机器名 —— 写显示名会**静默**筛出 0 条);\n# 日期字段的值是 ISO UTC(`2026-01-31T00:00:00+00:00`)、比较按**字典序**,所以区间一律写**半开**:\n# 起点 gte + 次日 lt(写成 lte:2026-01-31 会漏掉 1/31 当天)。\nxinhe-oa-cli bitable record --list --table <table_id> \\\n --all --limit 1000 --workers 4 \\\n --filter 'visit_date:gte:2026-01-01' --filter 'visit_date:lt:2026-02-01' --json\n\n# 改 / 删\nxinhe-oa-cli bitable record --update <record_id> --set 摘要=\"已核对\" --json\nxinhe-oa-cli bitable record --delete <record_id> --json # 高风险:要 --yes\n```\n\n### 3.3 视图与表单\n\n```bash\nxinhe-oa-cli bitable view --create --table <table_id> --name 收入视图 --json\nxinhe-oa-cli bitable view --query --view <view_id> --all --json\n# 按字段筛行走**服务端**(只回命中行、游标分页不重不漏;字段用 semantic_key)\nxinhe-oa-cli bitable view --query --view <view_id> \\\n --filter 'visit_date:gte:2026-01-01' --filter 'visit_date:lt:2026-02-01' \\\n --page-size 1000 --all --json\nxinhe-oa-cli bitable view --aggregate --view <view_id> --measure 金额:sum --group-by 科室 --json\nxinhe-oa-cli bitable form --submit --form <form_id> --set 摘要=\"报销\" --json\n```\n\n> **视图取数是\"服务端过滤 + 游标分页\"的快路径**(0.1.5 起):过滤下推到服务端(写法与 §3.2 相同,\n> 字段同样是 semantic_key),只要命中行;`--all` 撞上 200 页上限会**告警**并给\n> `truncated: true` + `next_cursor`,拿它 `--cursor` 接着取(不会假装翻完了)。\n> ⚠️ **0.1.4 及更早有缺陷**:CLI 读 `data.records`、服务端回 `data.items`,`view --query`\n> 与 `record --list --view` 恒返回空行且 `ok:true` —— 那个版本请改用 §3.2 的 `record --list`。\n\n### 3.4 表默认是**私有**的:看不见 ≠ 不存在\n\n> **新建的库与表默认 `private`** —— **只有建它的人看得到**。别人(含同部门同事)\n> 既读不到、列表里也不会出现,只以 `hidden_count` 计数。\n> **别把\"查不到\"当成\"表不存在\"**:先 `--visibility --show` 看档位与分享。\n\n```bash\n# 看档位(回的是**声明档位** + **夹紧后的有效档位**)\nxinhe-oa-cli bitable table --visibility <table_id> --show --json\n# → {visibility, effective_visibility, clamped_by, owner_user_id, readable_by_me, ...}\n\n# 改档位(高风险:要 --yes):private | dept | internal\nxinhe-oa-cli bitable table --visibility <table_id> --set internal --yes --json\nxinhe-oa-cli bitable table --visibility <table_id> --set dept --dept <ou_id> --yes --json\nxinhe-oa-cli bitable table --move <table_id> --to-dept <ou_id> --yes --json # 换归属=重算继承\n\n# 分享:**默认只给读**;要别人能改,必须显式 --acl-action write(默认 90 天到期)\nxinhe-oa-cli bitable table --share <table_id> --to-user <uid> --json\nxinhe-oa-cli bitable table --share <table_id> --to-user <uid> --acl-action write --json\nxinhe-oa-cli bitable table --share <table_id> --to-dept <ou_id> --json\nxinhe-oa-cli bitable table --acl-list <table_id> --json\nxinhe-oa-cli bitable table --acl-revoke <table_id> --from-user <uid> --acl-action read --json\n\n# 库侧同一组命令(`bitable base --visibility|--share|--acl-list|--move …`)\n# 投资人逐表放行(TABLE-002;高风险)\nxinhe-oa-cli bitable table --investor-visibility <table_id> --investor-set open --yes --json\n```\n\n> **分享表之前先分享库**:库的档位是表的**上限** —— 库没给出去,表分享了也读不到\n> (`--visibility --show` 的 `clamped_by` 会告诉你是被哪个库夹住的)。\n\n| 口径 | 说明 |\n|---|---|\n| **默认私有** | 新库 / 新表都是 `private`;存量表由迁移标成 `internal`(升级前的等效语义) |\n| **库是表的上限** | 库 `private` ⇒ 库里 `internal` 的表**实际按 private 判**(`effective_visibility` 会告诉你被谁夹住) |\n| **分享默认读** | 不给 `--write` 就只能看;**只被分享了 read 的人改不动记录**(403,不是 404) |\n| **写分享有到期** | `--write` 默认 90 天,**不给永久**(与文件域同口径) |\n| **`public` 不可写** | 本期没有对外分享入口,写入直接 400 |\n| **企业负责人看得到全部** | 但每次**纯治理读**都留一条 `object.admin_read` 审计 |\n| **投资人默认看不到** | 企业要逐表 `--investor-visibility --set open` 才可见(TABLE-002) |\n\n> **别用大范围档位图省事**:`--set internal` 等于\"全企业可读可写(模板自带 write)\"。\n> 只让某几个人看,用 `--share`(先 `--share` 库、再 `--share` 表 —— 库是上限)。\n\n---\n\n## 4. 把文件里的数据传上去(最常用的一招)\n\n**解析在客户端完成**,平台只存结果 —— 所以你不需要先把 Excel 转成别的格式。\n\n```bash\nxinhe-oa-cli bitable table --import \\\n --file ./公账流水.csv \\\n --base <base_id> \\\n --name 公账流水 --json\n```\n\n它会:读表头 → 推断每列类型(金额带千分位、日期、百分比都能认)→ 建表 → 建字段 → **分批写记录**(默认每批 200 条)。\n`.csv` 与 `.xlsx` 都支持(xlsx 默认读第一张 sheet,可用 `--sheet <名字>` 指定)。\n\n**大文件(几千行以上)加三个参数**(2026-09-29 起支持:实测 5,000 行从 5 分钟降到 33 秒):\n\n```bash\nxinhe-oa-cli bitable table --import --file ./大表.csv --base <base_id> --name 大表 \\\n --batch-size 500 --workers 4 --json\n```\n\n- `--batch-size 500`:每批最多 500 行(服务端 `row_ops_batch_max` 也是 500;宽表会按请求体\n 大小自动再切小)。批越大,每批那点固定开销摊得越薄;\n- `--workers 4`:并发发批(默认 3,上限 16)。服务端喊\"慢一点\"(429/5xx)时它会自己降并发;\n- **中途断了直接重跑同一条命令**:进度落在 `<配置目录>/checkpoints/`,只补剩余批次,\n 不会重复写入(`--fresh` 才是从头来)。断了的那次会明确告诉你\"已写入 N 行、怎么续\"。\n\n导入过程每批往 stderr 打一行进度(`batch 12/29 · 2400/5611 行 · elapsed 132s · 18.2 行/秒`),\n不想看加全局 `--quiet`(注意:告警不会被 `--quiet` 吞掉)。\n\n返回里给你 `table_id` / `records_created` / `rows_in_file` —— **对一下这两个数**,\n不一致就说明有行没读进来(空行会被跳过,`skipped_empty_rows` 会告诉你跳了多少)。\n\n想手工指定某列类型,用 `--type 列名=类型`(可重复)。想把原始文件也留档,加 `--keep-source`。\n\n---\n\n## 5. 附件\n\n附件是**独立资源**:上传拿到 `attachment_id`,再显式挂到某条记录的附件字段上。\n\n```bash\nxinhe-oa-cli attachment --upload --file ./单据.pdf --tenant <企业id> --name 单据.pdf --json\nxinhe-oa-cli attachment --link --id <attachment_id> --resource-type record --resource-id <record_id> --field <field_id> --json\nxinhe-oa-cli attachment --download <attachment_id> --out ./下载.pdf --json\n```\n\n---\n\n## 6. 被拒绝怎么办(退出码 4)\n\nCLI 会把服务端的原话透出来,`error.details.missing_scopes` 里就是**你缺的那条权限**:\n\n```json\n{\"code\": \"authz.missing_scope\",\n \"details\": {\"missing_scopes\": [\"data:kashu:*:*:write\"]},\n \"hint\": \"响应 details.missing_scopes 里是缺的 scope,拿它去找 super_admin 走 POST /admin/grants 授权\"}\n```\n\n**正确做法**:拿着这条 scope 去找**企业负责人**(本企业的事)或 **super_admin**(平台的事)要权限,\n而不是换个命令反复试、更不是试图绕。反复越权探测本身会被审计记录。\n\n---\n\n## 7. 企业负责人:管理本企业的人\n\n> 只有 `tenant_owner` 能做。其余角色做这些会得到退出码 `4`。\n\n```bash\n# 给本企业加人并赋角(一步完成;不写 --tenant 就是你自己那家企业)\nxinhe-oa-cli member --create --phone 13900000003 --name 小唐 --role tenant_member --json\n\n# 看本企业有哪些人、各自什么角色\nxinhe-oa-cli member --list --json\nxinhe-oa-cli member --role <user_id> --json\n\n# 撤角(撤权会同时吊销那个人的会话链)\nxinhe-oa-cli member --revoke-role <user_id> --reason \"离职\" --json\n```\n\n**角色怎么选**:\n\n| 角色 | 能干什么 | 用在哪 |\n|---|---|---|\n| `tenant_member` | 本企业**读 / 写 / 建表**(传数据的人) | 业务员、财务 |\n| `tenant_operator` | 只能读写**被点名的那几张表**(可限门店) | 只看某块数据的人 |\n| `tenant_owner` | 本企业**全量** + 管人(建人/赋角) | 企业负责人 |\n\n### 让投资人能看本企业的数据\n\n投资人的可见性是**两道门**:平台声明(super_admin 赋 `investor` 角色)+ **企业背书**(你点头)。\n缺任何一道,他都看不到 —— 这是刻意的:**能不能看你的数,由你说了算**。\n\n```bash\nxinhe-oa-cli investor --list --json # 看谁在等你确认\nxinhe-oa-cli investor --endorse --user <user_id> --reason \"投资人可查看全部数据\" --json\nxinhe-oa-cli investor --revoke --user <user_id> --reason \"合作结束\" --json # 随时收回\n```\n\n---\n\n## 8. 日报 / 周报(汇报)\n\n> **汇报不是\"一张表\"**:规则是**配置面**(谁在什么周期写给谁看),报告是**运行面**。\n> **没有规则就没有日报** —— 员工 `report --due` 会是空的。\n\n```bash\n# ① 建规则(企业负责人;--fillers 是**谁要交**,不给就谁都不用交)\nxinhe-oa-cli report rule --create --key work_daily --name 工作日报 --cadence daily \\\n --template '[{\"key\":\"done\",\"label\":\"今天做了什么\",\"type\":\"text\",\"required\":true},\n {\"key\":\"plan\",\"label\":\"明天计划\",\"type\":\"text\",\"required\":true}]' \\\n --fillers '{\"roles\":[\"tenant_member\",\"tenant_operator\"],\"only_employed\":true}' \\\n --recipients '[{\"type\":\"direct_leader\"},{\"type\":\"dept_leader\"}]' --json\n\n# 返回里的 period.period_key(如 D:2026-10-15)就是**当前周期**:\n# 服务端在建规则时已经自动开窗,不用等定时任务。\n\n# ② 预演(不落库):谁要填 / 谁收到 / 谁没解析到 —— 配错了在这里就看得见\nxinhe-oa-cli report rule --preview --rule work_daily --json\n\n# ③ 员工:看该填什么 → 存草稿 → 提交\nxinhe-oa-cli report --due --json\nxinhe-oa-cli report --save --rule work_daily --period D:2026-10-15 --content '{\"done\":\"走访 3 家\"}' --json\nxinhe-oa-cli report --submit --rule work_daily --period D:2026-10-15 --content '{\"done\":\"走访 3 家\",\"plan\":\"回访\"}' --json\n\n# ④ 上级 / 企业负责人:看汇总与未交\nxinhe-oa-cli report --list --team --json # 我的汇报子树(含间接下属)\nxinhe-oa-cli report --stats --rule work_daily --period D:2026-10-15 --include-sub --json\nxinhe-oa-cli report --confirm <report_id> --yes --json # 也可用 approval --approve(同一底座)\n```\n\n**四条必须知道的口径**:\n\n| 口径 | 说明 |\n|---|---|\n| **谁要交** | `--fillers` 支持 `depts`(按部门,**只认主部门**)/ `roles` / `users`。**空 `fillers` = 谁都不用交**,这是最常见的\"日报坏了\" |\n| **汇报给谁** | `--recipients` 是**解析器**(`direct_leader` / `dept_leader` / `all_leaders` / `named_user` / …),不写死人名 —— 调岗后自动跟着变;都没配到就落到**企业负责人** |\n| **谁能看** | 默认 `participants`(作者 + 汇报对象 + 抄送),**同事互相看不到**;上级与**企业负责人**默认看得到(负责人每次读都留审计)。要全公司可见必须显式改 `visibility` |\n| **提交后不可撤回** | 要改只能 `report --reopen <id> --note <原因> --yes`(管理员操作,留痕) |\n\n> 只开**当前周期**,历史补开由服务端定时任务负责(`report_tick`)。\n> 建规则时若返回 `period.assignments_created: 0`,说明 `fillers` 一个人都没展开出来 —— 先回去改它。\n\n---\n\n## 9. 组织架构与汇报关系(`org` 域)\n\n> **汇报线只存一处**:组织主数据上的两个字段(直属上级、部门负责人)。\n> 报告本身**不存**\"我上级是谁\" —— 改一处,全局实时生效。\n> 读只要登录;**写要 `admin:member`(企业负责人)且必须自然人**。\n\n```bash\n# ① 看部门树与成员归属\nxinhe-oa-cli org --tree --json # 树(含 depth / path / 人数)\nxinhe-oa-cli org --show <dept_id> --json # 单个部门详情\nxinhe-oa-cli org --explain-scope --user <uid> --json # 他为什么看得到/看不到(声明+锚点+子树规模)\n\n# ② 建部门 / 改名 / 移动 / 归档(写,要 --yes 的是移动与归档)\nxinhe-oa-cli org --create --name 销售部 --key sales --json\nxinhe-oa-cli org --create --name 华东区 --parent <dept_id> --json\nxinhe-oa-cli org --rename <dept_id> --name 销售中心 --json\nxinhe-oa-cli org --move <dept_id> --parent <new_parent_id> --yes --json\nxinhe-oa-cli org --leader-set <dept_id> --user <uid> --json # 部门负责人\n\n# ③ 把人挂进部门 / 设直属上级(汇报关系的两个字段就在这里)\nxinhe-oa-cli member --set-dept <dept_id> --user <uid> --primary --json # 首个部门自动为主部门\nxinhe-oa-cli member --set-manager <上级uid> --user <uid> --json\nxinhe-oa-cli member --clear-manager --user <uid> --json\nxinhe-oa-cli member --search --dept <dept_id> --json # 按部门看人(精确,不含下级;分页)\n```\n\n> **找人只用 `member --search`**(MEMBER-001 / issue #99):按 `--name`(子串,2..=32 字)/\n> `--phone`(**精确 11 位**,不做前缀 / 号段 / 模糊)/ `--dept` 三选一或组合,\n> 范围永远是\"我能管的企业\"。同名多人会**全部返回**,靠每行的 `phone_masked`(脱敏号)+\n> `departments` 区分;`duplicate_name=true` 时用 `--dept` 或 `--phone` 收窄。\n> 跨企业的号码与\"不存在的号码\"**响应完全一样**(问不出\"这个号在不在平台上\")。\n> **别用 `member --list` 拉全量再本地筛**:服务端上限 500 条(`truncated: true` = 可能被截断),\n> 且 `--list` 上带 `--name` / `--phone` / `--dept` 现在是**用法错误(退出码 2,不发请求)**。\n\n**六条必须知道的口径**:\n\n| 口径 | 说明 |\n|---|---|\n| **一人多部门只认主部门** | 上级链、按部门展开的填写范围、行级部门归属**都锚定主部门**;兼任部门不上链、不参与数据范围 |\n| **`--set-manager` 落在主部门行** | 换主部门后要重新确认上级;服务端校验同企业、不放成环 |\n| **负责人 ≠ 权限** | 部门负责人可以是外部人员,**不因此获得任何数据面权限**(职位与权限解耦);他读得到下级的日报,但要读下级的**表**仍需单独授权 |\n| **写要自然人** | `org` 的写操作与 `/admin/*` 同口径:AI 代跑令牌一律拒 |\n| **移动 = 重算继承** | 整棵子树换父,权限**不保留**(继承只能更严);归档要求子树为空且无在职成员 |\n| **`--key` 企业内唯一且不可改** | `--name` 才是显示名 |\n\n> **汇报关系不需要在报告里配**:建规则时 `--recipients` 用解析器\n> (`direct_leader` / `dept_leader` / `all_leaders` / `dept_leader_chain`),\n> 解析发生在开窗与提交时 —— 谁调岗、谁换上级,下个周期自动跟着变。\n\n---\n\n## 10. 退出码速查\n\n| 码 | 含义 | 你该做什么 |\n|---|---|---|\n| `0` | 成功 | — |\n| `2` | 用法错误(参数拼错/缺必填) | 看 `--help`;本地就拦下了,没发请求 |\n| `3` | 未登录 / 凭据失效 | 按提示 `login --phone <号码>` |\n| `4` | 权限不足 | 看 `details.missing_scopes`,去找人要权限(§6) |\n| `5` | 冲突 / 幂等 / 乐观锁 | 数据被改过或重复提交,重新读一次再改 |\n| `6` | 服务端错误 | 稍后重试;持续失败找运维 |\n| `7` | 网络不可达 | `doctor --json` 看网络段;内网直连时 `config --set proxy=none` |\n| `8` | CLI 版本过旧 | 自更新:`xinhe-oa-cli doctor --upgrade`(见 §9.1),完了重开终端再重试刚才那条命令 |\n\n---\n\n## 11. 出问题时先跑这个\n\n```bash\nxinhe-oa-cli doctor --json\n```\n\n它会检查:版本、配置目录与权限、凭据是否存在/是否过期、设备指纹、**连通性与代理判定**、\n磁盘、内嵌技能数量,并给出 `next_steps`。**报错前先跑它**,多数问题它能直接说清。\n\n### 11.1 版本旧了怎么升(自更新,别去手工下二进制)\n\n```bash\nxinhe-oa-cli doctor --upgrade --dry-run --json # 预演:只问一次 /cli/latest,不下载、不替换、不写任何文件\nxinhe-oa-cli doctor --upgrade --json # 问服务端发布了什么 → 下载 → 校验 → 原地替换自己\nxinhe-oa-cli doctor --upgrade --out /新位置/xinhe-oa-cli # 只要产物,不动当前这个文件\n```\n\n- **`--dry-run` 是预演,不是\"轻量地升一次\"**:它止步于只读的 `/cli/latest`(报告里\n `data.upgrade.dry_run=true`),**一个字节都不写** —— 别拿它当\"先试试\",\n 真要升就去掉它。看到 `dry_run=true` 就说明本次**没有**发生任何替换。\n- **`--out <路径>` 与\"本机是不是最新\"无关**:它要的是\"服务端当前发布的那一份产物\"\n (装到别的机器 / 排障 / 核对 sha256),所以本机恰好是最新时也照样下载落盘;\n `data.upgrade.target=\"out\"`、`current_binary_touched=false` 表示**本体没被动过**。\n- **用你已有的登录凭据下载**(`/cli/download/<os>/<arch>` 要令牌):所以**没登录升不了**,\n 会回 `auth.missing_token` —— 先按 §1 登录。\n- **先校验 sha256 再落盘**:对不上就一个字节都不换,当前版本照常可用。\n- ⚠️ **退出码 0 不代表升级成功**:`doctor` 是自检,升级失败只写在报告里。\n 要判\"升没升\"看 `data.upgrade.replaced` / `data.upgrade.to` / `data.upgrade.current_binary_touched`,或直接 `xinhe-oa-cli --version`。\n- 升完**重开终端**(或重新执行命令)用的才是新版本;旧文件留在同名 `.old`,下次启动自动清理。\n- 服务端没发你这个平台的产物 → `cli.artifact_missing`(`error.details` 里列了有哪些平台)。\n 这种重试没用,报给运维(例:macOS 产物本期未发布)。\n- **旧到读不到版本清单时**:如果 `doctor --upgrade` 自己就回了 `cli.too_old`(版本门禁只在\n `/cli/latest` 上),说明这个版本已经旧到没法和新服务端协商 —— 此时按\n `error.details.next_step` 用安装脚本重装一遍,之后日常升级再用 `doctor --upgrade`。\n- 什么时候该升:退出码 `8`、`doctor` 提示版本旧、或服务端 `notes` 里有你要用的新能力。\n\n---\n\n## 12. 红线(会被审计)\n\n- 不要试图绕过命令面(没有裸调、没有 SQL);\n- 不要把令牌写进任何文件、日志、提示词或命令行参数;\n- 不要用\"反复试\"来探测别人的数据 —— 越权探测会被记录在审计里;\n- 高风险写操作(`record --delete`、`application --copy`、`workflow --run`、`event --replay`)\n 必须显式加 `--yes`,那是给人留的确认点,**不要自动加**。\n\n> 需要平台级操作(建企业、任命企业负责人、跨企业查看、审计检索)→ 见 `xo-admin`(仅 super_admin)。\n"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/src/version.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// CLI 版本(`--version`、`meta.cli_version`、`X-CLI-Version` 头都用它)。
|
|
2
|
+
//
|
|
3
|
+
// 注意 `User-Agent` **不含**版本号(固定 `xinhe-oa-cli/1`):
|
|
4
|
+
// 服务端把 refresh 绑在 `HMAC(UA + X-Device-Id)` 上,UA 里带版本号会让用户
|
|
5
|
+
// 一升级 CLI 就 `auth.device_mismatch`、被迫重新短信登录。
|
|
6
|
+
//
|
|
7
|
+
// 这个常量与 `package.json` 的 `version` **必须一致**:`test/version.test.js`
|
|
8
|
+
// 会逐字比对,`npm version` 之后忘了改这里就会红。
|
|
9
|
+
export const VERSION = '0.2.0';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* npm 包名(升级指引 `npm i -g <包名>@latest` 与 `doctor` 的 npm 探测都用它)。
|
|
13
|
+
*
|
|
14
|
+
* 与 `package.json` 的 `name` **必须一致**:`test/schema-parity.test.js` 会比对。
|
|
15
|
+
*/
|
|
16
|
+
export const PACKAGE_NAME = '@xinhe-oa/cli';
|