从公司 DocSystem / MxsDoc V2.02.71 逆向出 HTTP 接口,封装为零依赖 Python CLI, 让 AI 可以完全通过命令行操作文档系统的文件系统。 功能: - 浏览:ls / tree / repos / auth - 读写:mkdir(-p) / touch / upload / download / cat - 变更:rename / rm / mv / cp - 检索:search(文件名 + 正文) 要点: - 不指定仓库时默认操作「研发管理配置库」(id=2);首个位置参数以 / 开头时自动判定为路径 - 凭证不入库,从 ~/.docsys/config.json 或环境变量读取 - 会话 cookie 缓存于 ~/.docsys/session.json,过期自动重登
160 lines
8.9 KiB
Markdown
160 lines
8.9 KiB
Markdown
---
|
||
name: docsys
|
||
description: 操作公司 MxsDoc / DocSystem 网络文档系统(http://172.1.4.31:8100/DocSystem)的文件系统——仓库浏览、目录创建/删除/重命名、文件上传/删除/重命名/移动/复制、以及按文件名或正文搜索。当用户提到"文档系统""DocSystem""MxsDoc""文档库""上传文件到文档系统""在文档系统里建目录/搜文件"时使用。
|
||
agent_created: true
|
||
---
|
||
|
||
# DocSystem 文档系统操作 Skill
|
||
|
||
通过纯 HTTP 调用 MxsDoc(DocSystem)后端接口,让 AI 直接操作公司文档系统的文件系统:
|
||
浏览、建目录、删除、重命名、移动、复制、上传、下载、搜索。
|
||
|
||
底层是一个零依赖的 Python CLI:只用标准库,不需要 pip 安装任何东西。
|
||
|
||
## 快速开始
|
||
|
||
```bash
|
||
PY="C:/Users/occul/.workbuddy/binaries/python/versions/3.13.12/python.exe"
|
||
SK="C:/Users/occul/WorkBuddy/docsys-skill/docsys"
|
||
|
||
$PY "$SK/scripts/docsys.py" login # 登录并缓存会话(首次执行即可)
|
||
$PY "$SK/scripts/docsys.py" repos # 列出可访问仓库
|
||
$PY "$SK/scripts/docsys.py" ls "意见与反馈" / # 列出仓库根目录
|
||
```
|
||
|
||
会话 cookie 缓存在 `~/.docsys/session.json`,过期时会自动重新登录,不需要每次显式 login。
|
||
|
||
## ⚠️ 默认仓库约定
|
||
|
||
**未指定仓库时一律使用 `研发管理配置库`(id=2)。** 用户说"上传个文件""搜一下 XX"而没提别的库时,不要反问,直接对这个库操作。
|
||
|
||
```bash
|
||
$PY "$SK/scripts/docsys.py" ls # = ls 研发管理配置库 /
|
||
$PY "$SK/scripts/docsys.py" ls /Sunyard # 路径以 / 开头,仓库自动取默认
|
||
$PY "$SK/scripts/docsys.py" ls 1 / # 显式切到其他库才带仓库参数
|
||
$PY "$SK/scripts/docsys.py" search 详细设计 # 默认搜默认库
|
||
$PY "$SK/scripts/docsys.py" search 详细设计 --all-repos # 例外:搜全部库
|
||
```
|
||
|
||
`repos` 命令会给默认库打 `← 默认` 标记。默认库可用环境变量 `DOCSYS_DEFAULT_REPO` 覆盖。
|
||
|
||
**判定规则**:第一个位置参数若以 `/` 或 `./` 开头 → 视为路径,仓库取默认;否则视为仓库名/id。所以 `ls /Sunyard` 和 `ls 研发管理配置库 /Sunyard` 等价。
|
||
内部实现是"位置参数整体左移一位",所以 `touch /a/b.txt "内容"` 这类后面还带可选参数的命令也不会错位。
|
||
|
||
## 命令一览
|
||
|
||
`<repo>` 全部可省略(省略即默认库):
|
||
|
||
| 命令 | 说明 |
|
||
|---|---|
|
||
| `login` / `logout` / `whoami` | 会话管理 |
|
||
| `repos` | 列出可访问仓库(id + 名称,标注默认) |
|
||
| `ls [repo] [path]` | 列出目录内容 |
|
||
| `tree [repo] [path] [--depth N]` | 递归列目录树 |
|
||
| `mkdir [repo] <path> [-p]` | 创建目录;默认要求父目录已存在,`-p` 递归创建中间目录 |
|
||
| `touch [repo] <path> [content]` | 创建文本文件;`content=@file --file x.txt` 从本地读内容 |
|
||
| `rename [repo] <path> <new_name>` | 重命名(不能跨目录) |
|
||
| `rm [repo] <path>` | 删除文件或目录(目录为递归删除) |
|
||
| `mv [repo] <src> <dst>` | 移动/改名。`dst` 是已存在目录则移入其中,否则视为目标完整路径 |
|
||
| `cp [repo] <src> <dst>` | 复制,语义同 `mv` |
|
||
| `upload [repo] <remote_dir> <local_file> [--name X]` | 上传本地文件 |
|
||
| `download [repo] <path> <local_file>` | 下载;`local_file` 为 `-` 时输出到 stdout |
|
||
| `cat [repo] <path>` | 读取文本文件内容 |
|
||
| `search <keyword> [--repo R] [--all-repos] [--path P]` | 按文件名或正文搜索;默认搜默认库,`--all-repos` 搜全部 |
|
||
| `auth [repo] [path]` | 查看当前账号在该目录的权限 |
|
||
|
||
全局参数:`--json`(结构化输出,推荐 AI 解析用)、`--base-url`、`--user`、`--password`。
|
||
|
||
## 用法约定(给 AI 的操作说明)
|
||
|
||
**0. 仓库默认就是「研发管理配置库」(id=2)。** 用户没指名别的仓库时不要反问、不要先跑 `repos` 确认,直接操作。切库时才带 repo 参数。
|
||
|
||
**1. 仓库也可以用 id 或名称指定**,名称支持子串匹配:
|
||
```
|
||
$PY ... ls 1 / # 按 id
|
||
$PY ... ls 常用工具 / # 按名称
|
||
$PY ... ls 研发 / # 子串,唯一匹配
|
||
```
|
||
|
||
**2. 路径一律用 `/a/b/c` 形式**,前导斜杠可省略,仓库根写 `/` 或留空。
|
||
单独给路径时(`ls /Sunyard`)脚本会自动识别为"默认库的该路径",不会误判成仓库名。
|
||
|
||
**3. 要解析用户输入("研发库的 2024 归档目录")时,先用 `repos` + `search` / `tree` 定位,不要猜。**
|
||
|
||
**4. 破坏性操作(`rm`、覆盖式 `mv`/`cp`)执行前先向用户确认**,并优先用 `ls` 或 `auth` 确认目标存在且有权限。
|
||
|
||
**5. 推荐加 `--json`**,输出结构稳定:
|
||
```json
|
||
[{"name":"t1.txt","fullPath":"_SELFTEST/t1.txt","type":"file","size":35,"mtime":"2026-10-20 14:33","docId":202070828624}]
|
||
```
|
||
|
||
## 权限边界(当前账号实测)
|
||
|
||
账号 `qian.huang@sunyard.com` 实测:
|
||
|
||
**权限是逐目录授予的,不是仓库级。** 同一个仓库里不同目录可以完全不同:
|
||
|
||
| 目录 | 访问 | 新增 | 编辑 | 删除 | 下载 |
|
||
|---|---|---|---|---|---|
|
||
| 研发管理配置库 `/`(根) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||
| 研发管理配置库 `/Sunyard/Docs/开发流程` | ✅ | ❌ | ❌ | ❌ | ✅ |
|
||
| **研发管理配置库 `/Sunyard/Dev/产品/系统研发/工具`** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||
| 意见与反馈 `/`(根) | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||
|
||
所以**不要凭仓库名推断权限**,任何写操作前先跑 `auth <path>` 确认。
|
||
已验证可完整 CRUD 的目录:`/Sunyard/Dev/产品/系统研发/工具/测试`(增删改 + 下载全开)。
|
||
|
||
服务端权限不足时返回中文错误,CLI 原样透出:
|
||
```
|
||
[docsys] 您没有该目录的删除权限,请联系管理员
|
||
```
|
||
遇到这类报错不要反复重试、也不要自作主张换到别的目录/仓库,直接告诉用户需要管理员授权。
|
||
|
||
注意:重命名和移动在服务端按"删除 + 新增"鉴权,所以即使有新增权限也可能被拒。
|
||
|
||
## 接口契约(排障用)
|
||
|
||
全部为 `POST {BASE}/...`,`{BASE} = http://172.1.4.31:8100/DocSystem`,表单编码,
|
||
需要带 `X-Requested-With: XMLHttpRequest`,会话靠 cookie(`JSESSIONID` / `dstoken` / `dsuser`)。
|
||
登录密码先做 base64 编码再提交。响应统一为 `{status:"ok"|"fail", data, msgInfo}`。
|
||
|
||
| 接口 | 参数 |
|
||
|---|---|
|
||
| `User/login.do` | `userName`, `pwd`(base64), `rememberMe` |
|
||
| `Repos/getReposList.do` | 无 |
|
||
| `Repos/getSubDocList.do` | `vid`, `docId`, `path`, `name`, `sort`, `needLockState` |
|
||
| `Repos/getUserDocAuth.do` | `docId`, `reposId`, `path`, `name` |
|
||
| `Doc/addDoc.do` | `reposId`, `type`(1文件/2目录), `pid`, `path`, `name`, `content`(**实测 content 被忽略,建出来恒为 0 字节空文件**) |
|
||
| `Doc/renameDoc.do` | `reposId`, `docId`, `pid`, `type`, `level`, `path`, `name`, `dstName` |
|
||
| `Doc/deleteDoc.do` | `reposId`, `docId`, `pid`, `path`, `name` |
|
||
| `Doc/moveDoc.do` | `reposId`, `docId`, `type`, `srcLevel`, `dstLevel`, `srcPid`, `dstPid`, `srcPath`, `srcName`, `dstPath`, `dstName` |
|
||
| `Doc/copyDoc.do` | 同 `moveDoc` |
|
||
| `Doc/uploadDoc.do` | multipart:`reposId`, `path`, `name`, `size`, `checkSum`(md5), `uploadFile`, `commitMsg`, `dirPath` |
|
||
| `Doc/downloadDocPrepare.do` | `reposId`, `docId`, `pid`, `path`, `name`, `downloadType` |
|
||
| `Doc/downloadDoc.do` | GET:`vid`, `path`, `name`, `targetPath`, `targetName`, `deleteFlag`, `encryptEn=1` |
|
||
| `Doc/searchDoc.do` | `searchWord`, `sort`, `reposId`, `path` |
|
||
| `Doc/getDocContent.do` | `reposId`, `docId`, `pid`, `path`, `name`(返回纯文本,响应开头有 `ok` 前缀) |
|
||
|
||
### 路径语义(最容易踩的坑)
|
||
|
||
- 节点字段 `path` 是**它所在目录**的路径:根目录下为 `""`,其余以 `/` 结尾,如 `_SELFTEST/sub/`。
|
||
节点完整路径 = `path + name`。
|
||
- **列举**:`getSubDocList` 只看 `path + name` 拼接结果,`docId` 不参与定位。
|
||
要列 `_SELFTEST` 的子项,必须传 `path=_SELFTEST/`(或 `path=_SELFTEST/` 且 `name=` 空)。
|
||
- **写入**(addDoc / uploadDoc / moveDoc 的 `dstPath`):要传**父目录完整路径 + `/`**,
|
||
例如往 `_SELFTEST` 下建文件,`path` 应为 `_SELFTEST/`,而不是节点自身的 `""`。
|
||
- **删除 / 重命名**:传节点自身的 `path` 与 `name`。
|
||
|
||
### 其他服务端怪癖
|
||
|
||
- **`addDoc.do` 忽略 `content`**:新建文件永远得到 0 字节空文件,响应里 `size=0`,
|
||
`getDocContent` 也读不到内容。要写内容必须走 `uploadDoc.do`(CLI 的 `touch` 已自动改为走上传通道)。
|
||
- **`getDocContent.do` 返回纯文本**且开头带 `ok` 两个字符前缀,解析要剥掉;空文件时只返回 `ok`。
|
||
- **`getDocFileLink.do` 不可用**:链接指向 `tmp/History/rdata/<commitId>/...` 历史目录,文件不存在会 500。
|
||
下载必须走 `downloadDocPrepare.do` → `downloadDoc.do`。
|
||
|
||
## 文件
|
||
|
||
- `scripts/docsys.py` — CLI 实现(Python 3 标准库,零依赖)
|
||
- `README.md` — 逆向出的完整接口文档
|