docz-cli 0.1.1 → 0.2.1

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 CHANGED
@@ -1,118 +1,169 @@
1
+ <div align="center">
2
+
1
3
  # docz-cli
2
4
 
3
- DocSync 命令行工具在终端读写公司文档。
5
+ **DocSync CLI & MCP Server read and write company documents from terminal and AI agents**
4
6
 
5
- ## 安装
7
+ [![npm version](https://img.shields.io/npm/v/docz-cli.svg)](https://www.npmjs.com/package/docz-cli)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
9
 
7
- ```bash
8
- pnpm install && pnpm build
9
- ```
10
+ </div>
10
11
 
11
- 全局使用:
12
+ ---
13
+
14
+ ## Quick Start
12
15
 
13
16
  ```bash
14
- npm link
15
- # 或直接
16
- alias docz="node /path/to/docz-cli/dist/index.js"
17
+ # Login
18
+ npx docz-cli login --token <your-token>
19
+
20
+ # Browse
21
+ npx docz-cli spaces
22
+ npx docz-cli ls 研发
23
+ npx docz-cli cat 研发:docs/guide.md
24
+
25
+ # Write
26
+ npx docz-cli write 吴鹏飞:notes/todo.md '# TODO List'
17
27
  ```
18
28
 
19
- ## 配置
29
+ ## Features
20
30
 
21
- 两种方式任选:
31
+ - **Simple addressing** — `<space>:<path>` format, Space supports name or ID
32
+ - **Full file operations** — ls, cat, upload, write, mkdir, rm, mv
33
+ - **Git-backed** — every write creates a commit, built-in version history
34
+ - **Trash recovery** — deleted files recoverable within 30 days
35
+ - **MCP Server** — built-in stdio MCP server for AI agent integration
36
+ - **Zero config** — single token, works immediately
22
37
 
23
- ```bash
24
- # 方式一:环境变量
25
- export DOCSYNC_API_TOKEN=<your-token>
38
+ ## Installation
26
39
 
27
- # 方式二:login 命令(保存到 ~/.docz/config.json)
28
- docz login --token <your-token>
40
+ **Requirements:** Node.js >= 22.0.0
41
+
42
+ ```bash
43
+ npx docz-cli <command> # Use directly via npx
44
+ npm install -g docz-cli # Or global install, then use `docz` shorthand
29
45
  ```
30
46
 
31
- Token 获取方式:
47
+ > Global install registers both `docz-cli` and `docz` commands. Examples below use `docz-cli`; replace with `docz` if installed globally.
32
48
 
33
- 1. 登录 https://docz.zhenguanyu.com(SSO 自动登录)
34
- 2. 点击左下角用户邮箱旁的 **`···`** 按钮,或直接访问 https://docz.zhenguanyu.com/settings
35
- 3. 在 **Settings → Account** 页面找到 **API Tokens** 区域
36
- 4. 点击 **New Token**,输入名称(如 `my-cli`),确认创建
37
- 5. 复制生成的 token(只会显示一次)
49
+ ## Authentication
38
50
 
39
- ## 使用
51
+ Get your API Token:
40
52
 
41
- 所有命令的寻址格式统一为 `<space>:<path>`,Space 支持名称或 ID。
53
+ 1. Login to https://docz.zhenguanyu.com (SSO)
54
+ 2. Go to **Settings → Account → API Tokens** (or visit `/settings` directly)
55
+ 3. Click **New Token**, name it, copy the token (shown only once)
42
56
 
43
- ### 浏览
57
+ Then configure:
44
58
 
45
59
  ```bash
46
- docz spaces # 列出所有 Space
47
- docz ls 研发 # 列出根目录
48
- docz ls 研发:docs # 列出子目录
49
- docz cat 研发:docs/guide.md # 读取文件内容
60
+ # Option 1: login command (saved to ~/.docz/config.json)
61
+ docz-cli login --token <your-token>
62
+
63
+ # Option 2: environment variable
64
+ export DOCSYNC_API_TOKEN=<your-token>
50
65
  ```
51
66
 
52
- ### 写入
67
+ ## Commands
53
68
 
54
- ```bash
55
- # 直接写内容
56
- docz write 吴鹏飞:notes/todo.md '# TODO List'
69
+ | Command | Description |
70
+ |---------|-------------|
71
+ | `login --token <t>` | Configure credentials |
72
+ | `whoami` | Show current user |
73
+ | `spaces` | List all accessible spaces |
74
+ | `ls <space>[:<path>]` | List files and folders |
75
+ | `cat <space>:<path>` | Read file content |
76
+ | `upload <file> <space>[:<dir>]` | Upload local file |
77
+ | `write <space>:<path> <content>` | Write content to file (`-` for stdin) |
78
+ | `mkdir <space>:<path>` | Create folder |
79
+ | `rm <space>:<path>` | Delete file/folder (30-day trash) |
80
+ | `mv <space>:<from> <to>` | Rename or move |
81
+ | `log <space>[:<path>]` | Show change history |
82
+ | `trash <space>` | Show deleted files |
83
+ | `mcp` | Start MCP stdio server |
57
84
 
58
- # stdin 写入
59
- echo '# Generated Report' | docz write 吴鹏飞:reports/daily.md -
85
+ ## Usage Examples
60
86
 
61
- # 上传本地文件
62
- docz upload ./report.pdf 研发:reports
87
+ ### Browse
63
88
 
64
- # 创建文件夹
65
- docz mkdir 研发:new-project
89
+ ```bash
90
+ docz-cli spaces # List all spaces
91
+ docz-cli ls 研发 # List root directory
92
+ docz-cli ls 研发:docs # List subdirectory
93
+ docz-cli cat 研发:docs/guide.md # Read file content
66
94
  ```
67
95
 
68
- ### 管理
96
+ ### Write
69
97
 
70
98
  ```bash
71
- docz mv 研发:old-name.md new-name.md # 重命名/移动
72
- docz rm 研发:deprecated.md # 删除(30 天内可从回收站恢复)
73
- docz log 研发 # Space 操作历史
74
- docz log 研发:docs/guide.md # 单文件变更历史
75
- docz trash 研发 # 查看回收站
99
+ docz-cli write 吴鹏飞:notes/todo.md '# TODO List' # Write content
100
+ echo '# Report' | docz-cli write 吴鹏飞:reports/daily.md - # From stdin
101
+ docz-cli upload ./report.pdf 研发:reports # Upload file
102
+ docz-cli mkdir 研发:new-project # Create folder
76
103
  ```
77
104
 
78
- ### 其他
105
+ ### Manage
79
106
 
80
107
  ```bash
81
- docz whoami # 当前登录用户
82
- docz --help # 完整帮助
83
- docz ls --help # 单命令帮助
108
+ docz-cli mv 研发:old.md new.md # Rename
109
+ docz-cli rm 研发:deprecated.md # Delete (recoverable)
110
+ docz-cli log 研发 # Space history
111
+ docz-cli log 研发:docs/guide.md # File history
112
+ docz-cli trash 研发 # View trash
84
113
  ```
85
114
 
86
- ## AI Agent 中使用
115
+ ## MCP Server
87
116
 
88
- docz-cli 设计为终端原生工具,任何能执行 shell 命令的 AI Agent 都能直接调用:
117
+ Built-in MCP server for AI agent integration (Claude Code, Cursor, etc.).
89
118
 
90
- ```
91
- 用户:帮我看看研发 Space 里有什么文档
92
- Agent:执行 docz ls 研发
93
- Agent:研发 Space 下有 3 个文件...
119
+ ### Configuration
94
120
 
95
- 用户:把这份分析报告上传到我的 Space
96
- Agent:执行 docz write 吴鹏飞:reports/analysis.md '<内容>'
97
- Agent:已上传到 reports/analysis.md
121
+ Add to your MCP settings:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "docsync": {
127
+ "command": "npx",
128
+ "args": ["-y", "docz-cli", "mcp"],
129
+ "env": {
130
+ "DOCSYNC_API_TOKEN": "<your-token>"
131
+ }
132
+ }
133
+ }
134
+ }
98
135
  ```
99
136
 
100
- 环境变量 `DOCSYNC_API_TOKEN` 配置好后,Agent 的 Bash 工具可以直接使用所有命令。
137
+ ### MCP Tools
138
+
139
+ | Tool | Description |
140
+ |------|-------------|
141
+ | `docsync_list_spaces` | List all accessible spaces |
142
+ | `docsync_list_files` | List files in a directory |
143
+ | `docsync_read_file` | Read file content |
144
+ | `docsync_upload_file` | Upload/create a file |
145
+ | `docsync_mkdir` | Create a folder |
146
+ | `docsync_delete` | Delete file/folder |
147
+ | `docsync_file_history` | View change history |
101
148
 
102
- ## DocSync API
149
+ ## API Reference
103
150
 
104
- docz-cli 封装了以下 DocSync REST API
151
+ docz-cli wraps the DocSync REST API:
105
152
 
106
- | 命令 | API |
107
- |------|-----|
153
+ | Command | API Endpoint |
154
+ |---------|-------------|
108
155
  | `spaces` | `GET /api/spaces` |
109
156
  | `ls` | `GET /api/spaces/{id}/tree?path=` |
110
157
  | `cat` | `GET /api/spaces/{id}/blob/{path}` |
111
- | `upload` / `write` | `POST /api/spaces/{id}/files/upload` (FormData) |
158
+ | `upload` / `write` | `POST /api/spaces/{id}/files/upload` |
112
159
  | `mkdir` | `POST /api/spaces/{id}/files/mkdir` |
113
160
  | `rm` | `POST /api/spaces/{id}/files/delete` |
114
161
  | `mv` | `POST /api/spaces/{id}/files/rename` |
115
162
  | `log` | `GET /api/spaces/{id}/log/[{path}]` |
116
163
  | `trash` | `GET /api/spaces/{id}/trash` |
117
164
 
118
- 认证统一使用 `Authorization: Bearer <token>`。DocSync 底层基于 Git,所有写操作自动产生 commit,天然有版本历史。
165
+ Auth: `Authorization: Bearer <token>`. Backend is Git — every write is a commit.
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/client.ts
4
+ var DocSyncClient = class {
5
+ constructor(baseUrl, token) {
6
+ this.baseUrl = baseUrl;
7
+ this.token = token;
8
+ this.baseUrl = baseUrl.replace(/\/$/, "");
9
+ }
10
+ async request(path, init) {
11
+ const res = await fetch(`${this.baseUrl}${path}`, {
12
+ ...init,
13
+ headers: {
14
+ Authorization: `Bearer ${this.token}`,
15
+ ...init?.headers
16
+ }
17
+ });
18
+ if (!res.ok) {
19
+ const body = await res.text().catch(() => "");
20
+ throw new Error(`${res.status} ${res.statusText}: ${body}`.trim());
21
+ }
22
+ const ct = res.headers.get("content-type") ?? "";
23
+ if (ct.includes("application/json")) return res.json();
24
+ return res.text();
25
+ }
26
+ // --- Auth ---
27
+ async me() {
28
+ return this.request("/api/auth/me");
29
+ }
30
+ // --- Spaces ---
31
+ async listSpaces() {
32
+ return this.request("/api/spaces");
33
+ }
34
+ async resolveSpace(nameOrId) {
35
+ const spaces = await this.listSpaces();
36
+ const s = spaces.find(
37
+ (s2) => s2.id === nameOrId || s2.name === nameOrId || s2.name.toLowerCase() === nameOrId.toLowerCase()
38
+ );
39
+ if (!s) {
40
+ const available = spaces.map((s2) => s2.name).join(", ");
41
+ throw new Error(`Space "${nameOrId}" not found. Available: ${available}`);
42
+ }
43
+ return s;
44
+ }
45
+ // --- Tree ---
46
+ async ls(spaceId, path = "") {
47
+ return this.request(
48
+ `/api/spaces/${spaceId}/tree?path=${encodeURIComponent(path)}`
49
+ );
50
+ }
51
+ // --- Blob ---
52
+ async cat(spaceId, filepath) {
53
+ return this.request(
54
+ `/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`
55
+ );
56
+ }
57
+ // --- Write ---
58
+ async upload(spaceId, dir, filename, content) {
59
+ const blob = typeof content === "string" ? new Blob([content], { type: "application/octet-stream" }) : new Blob([content], { type: "application/octet-stream" });
60
+ const form = new FormData();
61
+ form.append("file", blob, filename);
62
+ form.append("path", dir);
63
+ return this.request(`/api/spaces/${spaceId}/files/upload`, {
64
+ method: "POST",
65
+ body: form
66
+ });
67
+ }
68
+ async mkdir(spaceId, path) {
69
+ await this.request(`/api/spaces/${spaceId}/files/mkdir`, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({ path })
73
+ });
74
+ }
75
+ async rm(spaceId, path) {
76
+ await this.request(`/api/spaces/${spaceId}/files/delete`, {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/json" },
79
+ body: JSON.stringify({ path })
80
+ });
81
+ }
82
+ async mv(spaceId, from, to) {
83
+ await this.request(`/api/spaces/${spaceId}/files/rename`, {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify({ old_path: from, new_path: to })
87
+ });
88
+ }
89
+ // --- History ---
90
+ async log(spaceId, filepath) {
91
+ const path = filepath ? `/api/spaces/${spaceId}/log/${encodeURIComponent(filepath)}` : `/api/spaces/${spaceId}/log/`;
92
+ return this.request(path);
93
+ }
94
+ // --- Trash ---
95
+ async trash(spaceId) {
96
+ return this.request(`/api/spaces/${spaceId}/trash`);
97
+ }
98
+ };
99
+
100
+ // src/config.ts
101
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
102
+ import { homedir } from "os";
103
+ import { join } from "path";
104
+ var CONFIG_DIR = join(homedir(), ".docz");
105
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
106
+ function readConfig() {
107
+ if (!existsSync(CONFIG_FILE)) return {};
108
+ try {
109
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
110
+ } catch {
111
+ return {};
112
+ }
113
+ }
114
+ function writeConfig(config) {
115
+ mkdirSync(CONFIG_DIR, { recursive: true });
116
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
117
+ }
118
+ function getBaseUrl() {
119
+ return process.env.DOCSYNC_BASE_URL ?? readConfig().base_url ?? "https://docz.zhenguanyu.com";
120
+ }
121
+ function getToken() {
122
+ return process.env.DOCSYNC_API_TOKEN ?? readConfig().token;
123
+ }
124
+ function saveConfig(baseUrl, token) {
125
+ writeConfig({ base_url: baseUrl, token });
126
+ }
127
+ function getConfigPath() {
128
+ return CONFIG_FILE;
129
+ }
130
+
131
+ export {
132
+ DocSyncClient,
133
+ getBaseUrl,
134
+ getToken,
135
+ saveConfig,
136
+ getConfigPath
137
+ };
138
+ //# sourceMappingURL=chunk-N4DS7U4I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/config.ts"],"sourcesContent":["/**\n * DocSync API Client\n *\n * DocSync 底层是 Git 仓库,每个 Space = 一个 Git repo。\n * 认证:Authorization: Bearer {token}(支持 JWT 和永久 API Token)。\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Space {\n id: string;\n name: string;\n owner_id: string;\n is_private: boolean;\n created_at: string;\n member_count: number;\n}\n\nexport interface TreeEntry {\n name: string;\n type: 'blob' | 'tree';\n size: number;\n}\n\nexport interface LogEntry {\n hash: string;\n author: string;\n message: string;\n date: string;\n num_files: number;\n}\n\nexport interface TrashEntry {\n path: string;\n deleted_by: string;\n deleted_at: string;\n commit: string;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n is_admin: boolean;\n is_active: boolean;\n created_at: string;\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\nexport class DocSyncClient {\n constructor(\n private baseUrl: string,\n private token: string\n ) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n }\n\n private async request<T>(path: string, init?: RequestInit): Promise<T> {\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n Authorization: `Bearer ${this.token}`,\n ...init?.headers,\n },\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`${res.status} ${res.statusText}: ${body}`.trim());\n }\n\n const ct = res.headers.get('content-type') ?? '';\n if (ct.includes('application/json')) return res.json() as Promise<T>;\n return res.text() as unknown as T;\n }\n\n // --- Auth ---\n async me(): Promise<User> {\n return this.request('/api/auth/me');\n }\n\n // --- Spaces ---\n async listSpaces(): Promise<Space[]> {\n return this.request('/api/spaces');\n }\n\n async resolveSpace(nameOrId: string): Promise<Space> {\n const spaces = await this.listSpaces();\n const s = spaces.find(\n (s) =>\n s.id === nameOrId ||\n s.name === nameOrId ||\n s.name.toLowerCase() === nameOrId.toLowerCase()\n );\n if (!s) {\n const available = spaces.map((s) => s.name).join(', ');\n throw new Error(`Space \"${nameOrId}\" not found. Available: ${available}`);\n }\n return s;\n }\n\n // --- Tree ---\n async ls(spaceId: string, path = ''): Promise<TreeEntry[]> {\n return this.request(\n `/api/spaces/${spaceId}/tree?path=${encodeURIComponent(path)}`\n );\n }\n\n // --- Blob ---\n async cat(spaceId: string, filepath: string): Promise<string> {\n return this.request(\n `/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`\n );\n }\n\n // --- Write ---\n async upload(\n spaceId: string,\n dir: string,\n filename: string,\n content: string | Buffer\n ): Promise<{ path: string }> {\n const blob =\n typeof content === 'string'\n ? new Blob([content], { type: 'application/octet-stream' })\n : new Blob([content], { type: 'application/octet-stream' });\n const form = new FormData();\n form.append('file', blob, filename);\n form.append('path', dir);\n return this.request(`/api/spaces/${spaceId}/files/upload`, {\n method: 'POST',\n body: form,\n });\n }\n\n async mkdir(spaceId: string, path: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/mkdir`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path }),\n });\n }\n\n async rm(spaceId: string, path: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path }),\n });\n }\n\n async mv(spaceId: string, from: string, to: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/rename`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ old_path: from, new_path: to }),\n });\n }\n\n // --- History ---\n async log(spaceId: string, filepath?: string): Promise<LogEntry[]> {\n const path = filepath\n ? `/api/spaces/${spaceId}/log/${encodeURIComponent(filepath)}`\n : `/api/spaces/${spaceId}/log/`;\n return this.request(path);\n }\n\n // --- Trash ---\n async trash(spaceId: string): Promise<TrashEntry[]> {\n return this.request(`/api/spaces/${spaceId}/trash`);\n }\n}\n","/**\n * 配置管理\n *\n * 优先级:环境变量 > ~/.docz/config.json\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst CONFIG_DIR = join(homedir(), '.docz');\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.json');\n\ninterface Config {\n base_url?: string;\n token?: string;\n}\n\nfunction readConfig(): Config {\n if (!existsSync(CONFIG_FILE)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nfunction writeConfig(config: Config): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\\n');\n}\n\nexport function getBaseUrl(): string {\n return (\n process.env.DOCSYNC_BASE_URL ??\n readConfig().base_url ??\n 'https://docz.zhenguanyu.com'\n );\n}\n\nexport function getToken(): string | undefined {\n return process.env.DOCSYNC_API_TOKEN ?? readConfig().token;\n}\n\nexport function saveConfig(baseUrl: string, token: string): void {\n writeConfig({ base_url: baseUrl, token });\n}\n\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n"],"mappings":"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","names":["s"]}
package/dist/index.js CHANGED
@@ -1,141 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ DocSyncClient,
4
+ getBaseUrl,
5
+ getConfigPath,
6
+ getToken,
7
+ saveConfig
8
+ } from "./chunk-N4DS7U4I.js";
2
9
 
3
10
  // src/index.ts
4
11
  import { Command } from "commander";
5
12
 
6
13
  // src/commands.ts
7
- import { readFileSync as readFileSync2 } from "fs";
14
+ import { readFileSync } from "fs";
8
15
  import { basename, dirname } from "path";
9
-
10
- // src/client.ts
11
- var DocSyncClient = class {
12
- constructor(baseUrl, token) {
13
- this.baseUrl = baseUrl;
14
- this.token = token;
15
- this.baseUrl = baseUrl.replace(/\/$/, "");
16
- }
17
- async request(path, init) {
18
- const res = await fetch(`${this.baseUrl}${path}`, {
19
- ...init,
20
- headers: {
21
- Authorization: `Bearer ${this.token}`,
22
- ...init?.headers
23
- }
24
- });
25
- if (!res.ok) {
26
- const body = await res.text().catch(() => "");
27
- throw new Error(`${res.status} ${res.statusText}: ${body}`.trim());
28
- }
29
- const ct = res.headers.get("content-type") ?? "";
30
- if (ct.includes("application/json")) return res.json();
31
- return res.text();
32
- }
33
- // --- Auth ---
34
- async me() {
35
- return this.request("/api/auth/me");
36
- }
37
- // --- Spaces ---
38
- async listSpaces() {
39
- return this.request("/api/spaces");
40
- }
41
- async resolveSpace(nameOrId) {
42
- const spaces = await this.listSpaces();
43
- const s = spaces.find(
44
- (s2) => s2.id === nameOrId || s2.name === nameOrId || s2.name.toLowerCase() === nameOrId.toLowerCase()
45
- );
46
- if (!s) {
47
- const available = spaces.map((s2) => s2.name).join(", ");
48
- throw new Error(`Space "${nameOrId}" not found. Available: ${available}`);
49
- }
50
- return s;
51
- }
52
- // --- Tree ---
53
- async ls(spaceId, path = "") {
54
- return this.request(
55
- `/api/spaces/${spaceId}/tree?path=${encodeURIComponent(path)}`
56
- );
57
- }
58
- // --- Blob ---
59
- async cat(spaceId, filepath) {
60
- return this.request(
61
- `/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`
62
- );
63
- }
64
- // --- Write ---
65
- async upload(spaceId, dir, filename, content) {
66
- const blob = typeof content === "string" ? new Blob([content], { type: "application/octet-stream" }) : new Blob([content], { type: "application/octet-stream" });
67
- const form = new FormData();
68
- form.append("file", blob, filename);
69
- form.append("path", dir);
70
- return this.request(`/api/spaces/${spaceId}/files/upload`, {
71
- method: "POST",
72
- body: form
73
- });
74
- }
75
- async mkdir(spaceId, path) {
76
- await this.request(`/api/spaces/${spaceId}/files/mkdir`, {
77
- method: "POST",
78
- headers: { "Content-Type": "application/json" },
79
- body: JSON.stringify({ path })
80
- });
81
- }
82
- async rm(spaceId, path) {
83
- await this.request(`/api/spaces/${spaceId}/files/delete`, {
84
- method: "POST",
85
- headers: { "Content-Type": "application/json" },
86
- body: JSON.stringify({ path })
87
- });
88
- }
89
- async mv(spaceId, from, to) {
90
- await this.request(`/api/spaces/${spaceId}/files/rename`, {
91
- method: "POST",
92
- headers: { "Content-Type": "application/json" },
93
- body: JSON.stringify({ old_path: from, new_path: to })
94
- });
95
- }
96
- // --- History ---
97
- async log(spaceId, filepath) {
98
- const path = filepath ? `/api/spaces/${spaceId}/log/${encodeURIComponent(filepath)}` : `/api/spaces/${spaceId}/log/`;
99
- return this.request(path);
100
- }
101
- // --- Trash ---
102
- async trash(spaceId) {
103
- return this.request(`/api/spaces/${spaceId}/trash`);
104
- }
105
- };
106
-
107
- // src/config.ts
108
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
109
- import { homedir } from "os";
110
- import { join } from "path";
111
- var CONFIG_DIR = join(homedir(), ".docz");
112
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
113
- function readConfig() {
114
- if (!existsSync(CONFIG_FILE)) return {};
115
- try {
116
- return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
117
- } catch {
118
- return {};
119
- }
120
- }
121
- function writeConfig(config) {
122
- mkdirSync(CONFIG_DIR, { recursive: true });
123
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
124
- }
125
- function getBaseUrl() {
126
- return process.env.DOCSYNC_BASE_URL ?? readConfig().base_url ?? "https://docz.zhenguanyu.com";
127
- }
128
- function getToken() {
129
- return process.env.DOCSYNC_API_TOKEN ?? readConfig().token;
130
- }
131
- function saveConfig(baseUrl, token) {
132
- writeConfig({ base_url: baseUrl, token });
133
- }
134
- function getConfigPath() {
135
- return CONFIG_FILE;
136
- }
137
-
138
- // src/commands.ts
139
16
  function getClient() {
140
17
  const token = getToken();
141
18
  if (!token) {
@@ -235,7 +112,7 @@ function registerCommands(program2) {
235
112
  const { space, path: dir } = parseTarget(args);
236
113
  const client = getClient();
237
114
  const s = await client.resolveSpace(space);
238
- const content = readFileSync2(file);
115
+ const content = readFileSync(file);
239
116
  const filename = basename(file);
240
117
  const targetDir = dir || "";
241
118
  const result = await client.upload(s.id, targetDir, filename, content);
@@ -335,7 +212,11 @@ function registerCommands(program2) {
335
212
 
336
213
  // src/index.ts
337
214
  var program = new Command();
338
- program.name("docz").description("DocSync CLI \u2014 read and write company documents").version("0.1.0");
215
+ program.name("docz").description("DocSync CLI \u2014 read and write company documents").version("0.1.1");
339
216
  registerCommands(program);
217
+ program.command("mcp").description("Start MCP stdio server for AI agent integration").action(async () => {
218
+ const { startMcpServer } = await import("./mcp-EJZJGAX6.js");
219
+ await startMcpServer();
220
+ });
340
221
  program.parse();
341
222
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands.ts","../src/client.ts","../src/config.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { registerCommands } from './commands.js';\n\nconst program = new Command();\n\nprogram\n .name('docz')\n .description('DocSync CLI — read and write company documents')\n .version('0.1.0');\n\nregisterCommands(program);\n\nprogram.parse();\n","/**\n * CLI Commands\n */\n\nimport { readFileSync } from 'node:fs';\nimport { basename, dirname } from 'node:path';\nimport type { Command } from 'commander';\nimport { DocSyncClient } from './client.js';\nimport { getBaseUrl, getConfigPath, getToken, saveConfig } from './config.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getClient(): DocSyncClient {\n const token = getToken();\n if (!token) {\n console.error(\n 'Error: No token configured.\\n' +\n 'Run `docz login` or set DOCSYNC_API_TOKEN environment variable.'\n );\n process.exit(1);\n }\n return new DocSyncClient(getBaseUrl(), token);\n}\n\n/** Parse \"space:path\" or \"space path\" format */\nfunction parseTarget(args: string[]): { space: string; path: string } {\n if (args.length === 0) {\n console.error(\n 'Error: space is required. Usage: docz <cmd> <space>[:<path>]'\n );\n process.exit(1);\n }\n const first = args[0];\n if (first.includes(':')) {\n const [space, ...rest] = first.split(':');\n return { space, path: rest.join(':') };\n }\n return { space: first, path: args.slice(1).join(' ') };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport function registerCommands(program: Command): void {\n // --- login ---\n program\n .command('login')\n .description('Configure DocSync credentials')\n .option('-u, --url <url>', 'DocSync server URL')\n .option('-t, --token <token>', 'API token')\n .action(async (opts) => {\n const url = opts.url ?? getBaseUrl();\n const token = opts.token;\n if (!token) {\n console.error(\n 'Error: --token is required.\\n' +\n 'Get one at: ' +\n url +\n ' → Settings → API Tokens'\n );\n process.exit(1);\n }\n // Verify token\n const client = new DocSyncClient(url, token);\n try {\n const user = await client.me();\n saveConfig(url, token);\n console.log(`Logged in as ${user.name} (${user.email})`);\n console.log(`Config saved to ${getConfigPath()}`);\n } catch (err) {\n console.error(\n `Error: ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n });\n\n // --- whoami ---\n program\n .command('whoami')\n .description('Show current user')\n .action(async () => {\n const client = getClient();\n const user = await client.me();\n console.log(`${user.name} (${user.email})`);\n });\n\n // --- spaces ---\n program\n .command('spaces')\n .description('List all accessible spaces')\n .action(async () => {\n const client = getClient();\n const spaces = await client.listSpaces();\n for (const s of spaces) {\n const tag = s.is_private ? 'private' : 'team';\n console.log(`${s.name}\\t${tag}\\t${s.member_count} members\\t${s.id}`);\n }\n });\n\n // --- ls ---\n program\n .command('ls')\n .description('List files — docz ls <space>[:<path>]')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const entries = await client.ls(s.id, path);\n if (entries.length === 0) {\n console.log('(empty)');\n return;\n }\n for (const e of entries) {\n if (e.type === 'tree') {\n console.log(`${e.name}/`);\n } else {\n console.log(`${e.name}\\t${formatSize(e.size)}`);\n }\n }\n });\n\n // --- cat ---\n program\n .command('cat')\n .description('Read file content — docz cat <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error(\n 'Error: file path is required. Usage: docz cat <space>:<path>'\n );\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n const content = await client.cat(s.id, path);\n process.stdout.write(content);\n });\n\n // --- upload ---\n program\n .command('upload')\n .description('Upload file — docz upload <local-file> <space>[:<dir>]')\n .argument('<file>', 'Local file to upload')\n .argument('<target...>')\n .action(async (file: string, args: string[]) => {\n const { space, path: dir } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const content = readFileSync(file);\n const filename = basename(file);\n const targetDir = dir || '';\n const result = await client.upload(s.id, targetDir, filename, content);\n console.log(`Uploaded: ${result.path}`);\n });\n\n // --- write ---\n program\n .command('write')\n .description('Write content to file — docz write <space>:<path> <content>')\n .argument('<target>', 'space:dir/filename.md')\n .argument('<content>', 'File content (or - for stdin)')\n .action(async (target: string, content: string) => {\n const { space, path } = parseTarget([target]);\n if (!path) {\n console.error(\n 'Error: path is required. Usage: docz write <space>:<dir/filename> <content>'\n );\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n const filename = basename(path);\n const dir = dirname(path);\n let body: string;\n if (content === '-') {\n // Read from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n body = Buffer.concat(chunks).toString('utf-8');\n } else {\n body = content;\n }\n const result = await client.upload(\n s.id,\n dir === '.' ? '' : dir,\n filename,\n body\n );\n console.log(`Written: ${result.path}`);\n });\n\n // --- mkdir ---\n program\n .command('mkdir')\n .description('Create folder — docz mkdir <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error('Error: path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.mkdir(s.id, path);\n console.log(`Created: ${path}`);\n });\n\n // --- rm ---\n program\n .command('rm')\n .description('Delete file/folder — docz rm <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error('Error: path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.rm(s.id, path);\n console.log(`Deleted: ${path} (recoverable from trash for 30 days)`);\n });\n\n // --- mv ---\n program\n .command('mv')\n .description('Rename/move — docz mv <space>:<from> <to>')\n .argument('<target>', 'space:old-path')\n .argument('<to>', 'new-path')\n .action(async (target: string, to: string) => {\n const { space, path: from } = parseTarget([target]);\n if (!from) {\n console.error('Error: source path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.mv(s.id, from, to);\n console.log(`Moved: ${from} → ${to}`);\n });\n\n // --- log ---\n program\n .command('log')\n .description('Show change history — docz log <space>[:<path>]')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const logs = await client.log(s.id, path || undefined);\n if (logs.length === 0) {\n console.log('No history.');\n return;\n }\n for (const l of logs) {\n console.log(`${l.hash.substring(0, 7)} ${l.date} ${l.message}`);\n }\n });\n\n // --- trash ---\n program\n .command('trash')\n .description('Show deleted files — docz trash <space>')\n .argument('<space>')\n .action(async (spaceName: string) => {\n const client = getClient();\n const s = await client.resolveSpace(spaceName);\n const items = await client.trash(s.id);\n if (items.length === 0) {\n console.log('Trash is empty.');\n return;\n }\n for (const t of items) {\n console.log(\n `${t.path}\\tdeleted ${t.deleted_at}\\t${t.commit.substring(0, 7)}`\n );\n }\n });\n}\n","/**\n * DocSync API Client\n *\n * DocSync 底层是 Git 仓库,每个 Space = 一个 Git repo。\n * 认证:Authorization: Bearer {token}(支持 JWT 和永久 API Token)。\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Space {\n id: string;\n name: string;\n owner_id: string;\n is_private: boolean;\n created_at: string;\n member_count: number;\n}\n\nexport interface TreeEntry {\n name: string;\n type: 'blob' | 'tree';\n size: number;\n}\n\nexport interface LogEntry {\n hash: string;\n author: string;\n message: string;\n date: string;\n num_files: number;\n}\n\nexport interface TrashEntry {\n path: string;\n deleted_by: string;\n deleted_at: string;\n commit: string;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n is_admin: boolean;\n is_active: boolean;\n created_at: string;\n}\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\nexport class DocSyncClient {\n constructor(\n private baseUrl: string,\n private token: string\n ) {\n this.baseUrl = baseUrl.replace(/\\/$/, '');\n }\n\n private async request<T>(path: string, init?: RequestInit): Promise<T> {\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...init,\n headers: {\n Authorization: `Bearer ${this.token}`,\n ...init?.headers,\n },\n });\n\n if (!res.ok) {\n const body = await res.text().catch(() => '');\n throw new Error(`${res.status} ${res.statusText}: ${body}`.trim());\n }\n\n const ct = res.headers.get('content-type') ?? '';\n if (ct.includes('application/json')) return res.json() as Promise<T>;\n return res.text() as unknown as T;\n }\n\n // --- Auth ---\n async me(): Promise<User> {\n return this.request('/api/auth/me');\n }\n\n // --- Spaces ---\n async listSpaces(): Promise<Space[]> {\n return this.request('/api/spaces');\n }\n\n async resolveSpace(nameOrId: string): Promise<Space> {\n const spaces = await this.listSpaces();\n const s = spaces.find(\n (s) =>\n s.id === nameOrId ||\n s.name === nameOrId ||\n s.name.toLowerCase() === nameOrId.toLowerCase()\n );\n if (!s) {\n const available = spaces.map((s) => s.name).join(', ');\n throw new Error(`Space \"${nameOrId}\" not found. Available: ${available}`);\n }\n return s;\n }\n\n // --- Tree ---\n async ls(spaceId: string, path = ''): Promise<TreeEntry[]> {\n return this.request(\n `/api/spaces/${spaceId}/tree?path=${encodeURIComponent(path)}`\n );\n }\n\n // --- Blob ---\n async cat(spaceId: string, filepath: string): Promise<string> {\n return this.request(\n `/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`\n );\n }\n\n // --- Write ---\n async upload(\n spaceId: string,\n dir: string,\n filename: string,\n content: string | Buffer\n ): Promise<{ path: string }> {\n const blob =\n typeof content === 'string'\n ? new Blob([content], { type: 'application/octet-stream' })\n : new Blob([content], { type: 'application/octet-stream' });\n const form = new FormData();\n form.append('file', blob, filename);\n form.append('path', dir);\n return this.request(`/api/spaces/${spaceId}/files/upload`, {\n method: 'POST',\n body: form,\n });\n }\n\n async mkdir(spaceId: string, path: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/mkdir`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path }),\n });\n }\n\n async rm(spaceId: string, path: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path }),\n });\n }\n\n async mv(spaceId: string, from: string, to: string): Promise<void> {\n await this.request(`/api/spaces/${spaceId}/files/rename`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ old_path: from, new_path: to }),\n });\n }\n\n // --- History ---\n async log(spaceId: string, filepath?: string): Promise<LogEntry[]> {\n const path = filepath\n ? `/api/spaces/${spaceId}/log/${encodeURIComponent(filepath)}`\n : `/api/spaces/${spaceId}/log/`;\n return this.request(path);\n }\n\n // --- Trash ---\n async trash(spaceId: string): Promise<TrashEntry[]> {\n return this.request(`/api/spaces/${spaceId}/trash`);\n }\n}\n","/**\n * 配置管理\n *\n * 优先级:环境变量 > ~/.docz/config.json\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst CONFIG_DIR = join(homedir(), '.docz');\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.json');\n\ninterface Config {\n base_url?: string;\n token?: string;\n}\n\nfunction readConfig(): Config {\n if (!existsSync(CONFIG_FILE)) return {};\n try {\n return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nfunction writeConfig(config: Config): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\\n');\n}\n\nexport function getBaseUrl(): string {\n return (\n process.env.DOCSYNC_BASE_URL ??\n readConfig().base_url ??\n 'https://docz.zhenguanyu.com'\n );\n}\n\nexport function getToken(): string | undefined {\n return process.env.DOCSYNC_API_TOKEN ?? readConfig().token;\n}\n\nexport function saveConfig(baseUrl: string, token: string): void {\n writeConfig({ base_url: baseUrl, token });\n}\n\nexport function getConfigPath(): string {\n return CONFIG_FILE;\n}\n"],"mappings":"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","names":["readFileSync","s","program","readFileSync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { registerCommands } from './commands.js';\n\nconst program = new Command();\n\nprogram\n .name('docz')\n .description('DocSync CLI — read and write company documents')\n .version('0.1.1');\n\nregisterCommands(program);\n\nprogram\n .command('mcp')\n .description('Start MCP stdio server for AI agent integration')\n .action(async () => {\n const { startMcpServer } = await import('./mcp.js');\n await startMcpServer();\n });\n\nprogram.parse();\n","/**\n * CLI Commands\n */\n\nimport { readFileSync } from 'node:fs';\nimport { basename, dirname } from 'node:path';\nimport type { Command } from 'commander';\nimport { DocSyncClient } from './client.js';\nimport { getBaseUrl, getConfigPath, getToken, saveConfig } from './config.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getClient(): DocSyncClient {\n const token = getToken();\n if (!token) {\n console.error(\n 'Error: No token configured.\\n' +\n 'Run `docz login` or set DOCSYNC_API_TOKEN environment variable.'\n );\n process.exit(1);\n }\n return new DocSyncClient(getBaseUrl(), token);\n}\n\n/** Parse \"space:path\" or \"space path\" format */\nfunction parseTarget(args: string[]): { space: string; path: string } {\n if (args.length === 0) {\n console.error(\n 'Error: space is required. Usage: docz <cmd> <space>[:<path>]'\n );\n process.exit(1);\n }\n const first = args[0];\n if (first.includes(':')) {\n const [space, ...rest] = first.split(':');\n return { space, path: rest.join(':') };\n }\n return { space: first, path: args.slice(1).join(' ') };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\nexport function registerCommands(program: Command): void {\n // --- login ---\n program\n .command('login')\n .description('Configure DocSync credentials')\n .option('-u, --url <url>', 'DocSync server URL')\n .option('-t, --token <token>', 'API token')\n .action(async (opts) => {\n const url = opts.url ?? getBaseUrl();\n const token = opts.token;\n if (!token) {\n console.error(\n 'Error: --token is required.\\n' +\n 'Get one at: ' +\n url +\n ' → Settings → API Tokens'\n );\n process.exit(1);\n }\n // Verify token\n const client = new DocSyncClient(url, token);\n try {\n const user = await client.me();\n saveConfig(url, token);\n console.log(`Logged in as ${user.name} (${user.email})`);\n console.log(`Config saved to ${getConfigPath()}`);\n } catch (err) {\n console.error(\n `Error: ${err instanceof Error ? err.message : String(err)}`\n );\n process.exit(1);\n }\n });\n\n // --- whoami ---\n program\n .command('whoami')\n .description('Show current user')\n .action(async () => {\n const client = getClient();\n const user = await client.me();\n console.log(`${user.name} (${user.email})`);\n });\n\n // --- spaces ---\n program\n .command('spaces')\n .description('List all accessible spaces')\n .action(async () => {\n const client = getClient();\n const spaces = await client.listSpaces();\n for (const s of spaces) {\n const tag = s.is_private ? 'private' : 'team';\n console.log(`${s.name}\\t${tag}\\t${s.member_count} members\\t${s.id}`);\n }\n });\n\n // --- ls ---\n program\n .command('ls')\n .description('List files — docz ls <space>[:<path>]')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const entries = await client.ls(s.id, path);\n if (entries.length === 0) {\n console.log('(empty)');\n return;\n }\n for (const e of entries) {\n if (e.type === 'tree') {\n console.log(`${e.name}/`);\n } else {\n console.log(`${e.name}\\t${formatSize(e.size)}`);\n }\n }\n });\n\n // --- cat ---\n program\n .command('cat')\n .description('Read file content — docz cat <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error(\n 'Error: file path is required. Usage: docz cat <space>:<path>'\n );\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n const content = await client.cat(s.id, path);\n process.stdout.write(content);\n });\n\n // --- upload ---\n program\n .command('upload')\n .description('Upload file — docz upload <local-file> <space>[:<dir>]')\n .argument('<file>', 'Local file to upload')\n .argument('<target...>')\n .action(async (file: string, args: string[]) => {\n const { space, path: dir } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const content = readFileSync(file);\n const filename = basename(file);\n const targetDir = dir || '';\n const result = await client.upload(s.id, targetDir, filename, content);\n console.log(`Uploaded: ${result.path}`);\n });\n\n // --- write ---\n program\n .command('write')\n .description('Write content to file — docz write <space>:<path> <content>')\n .argument('<target>', 'space:dir/filename.md')\n .argument('<content>', 'File content (or - for stdin)')\n .action(async (target: string, content: string) => {\n const { space, path } = parseTarget([target]);\n if (!path) {\n console.error(\n 'Error: path is required. Usage: docz write <space>:<dir/filename> <content>'\n );\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n const filename = basename(path);\n const dir = dirname(path);\n let body: string;\n if (content === '-') {\n // Read from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n body = Buffer.concat(chunks).toString('utf-8');\n } else {\n body = content;\n }\n const result = await client.upload(\n s.id,\n dir === '.' ? '' : dir,\n filename,\n body\n );\n console.log(`Written: ${result.path}`);\n });\n\n // --- mkdir ---\n program\n .command('mkdir')\n .description('Create folder — docz mkdir <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error('Error: path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.mkdir(s.id, path);\n console.log(`Created: ${path}`);\n });\n\n // --- rm ---\n program\n .command('rm')\n .description('Delete file/folder — docz rm <space>:<path>')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n if (!path) {\n console.error('Error: path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.rm(s.id, path);\n console.log(`Deleted: ${path} (recoverable from trash for 30 days)`);\n });\n\n // --- mv ---\n program\n .command('mv')\n .description('Rename/move — docz mv <space>:<from> <to>')\n .argument('<target>', 'space:old-path')\n .argument('<to>', 'new-path')\n .action(async (target: string, to: string) => {\n const { space, path: from } = parseTarget([target]);\n if (!from) {\n console.error('Error: source path is required.');\n process.exit(1);\n }\n const client = getClient();\n const s = await client.resolveSpace(space);\n await client.mv(s.id, from, to);\n console.log(`Moved: ${from} → ${to}`);\n });\n\n // --- log ---\n program\n .command('log')\n .description('Show change history — docz log <space>[:<path>]')\n .argument('<target...>')\n .action(async (args: string[]) => {\n const { space, path } = parseTarget(args);\n const client = getClient();\n const s = await client.resolveSpace(space);\n const logs = await client.log(s.id, path || undefined);\n if (logs.length === 0) {\n console.log('No history.');\n return;\n }\n for (const l of logs) {\n console.log(`${l.hash.substring(0, 7)} ${l.date} ${l.message}`);\n }\n });\n\n // --- trash ---\n program\n .command('trash')\n .description('Show deleted files — docz trash <space>')\n .argument('<space>')\n .action(async (spaceName: string) => {\n const client = getClient();\n const s = await client.resolveSpace(spaceName);\n const items = await client.trash(s.id);\n if (items.length === 0) {\n console.log('Trash is empty.');\n return;\n }\n for (const t of items) {\n console.log(\n `${t.path}\\tdeleted ${t.deleted_at}\\t${t.commit.substring(0, 7)}`\n );\n }\n });\n}\n"],"mappings":"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","names":["program"]}
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DocSyncClient,
4
+ getBaseUrl,
5
+ getToken
6
+ } from "./chunk-N4DS7U4I.js";
7
+
8
+ // src/mcp.ts
9
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import {
12
+ CallToolRequestSchema,
13
+ ListToolsRequestSchema
14
+ } from "@modelcontextprotocol/sdk/types.js";
15
+ function getClient() {
16
+ const token = getToken();
17
+ if (!token) {
18
+ throw new Error(
19
+ "DOCSYNC_API_TOKEN not set. Run `docz login --token <token>` or set the env var."
20
+ );
21
+ }
22
+ return new DocSyncClient(getBaseUrl(), token);
23
+ }
24
+ async function resolveSpaceId(client, nameOrId) {
25
+ const space = await client.resolveSpace(nameOrId);
26
+ return space.id;
27
+ }
28
+ function formatSize(bytes) {
29
+ if (bytes < 1024) return `${bytes} B`;
30
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
31
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
32
+ }
33
+ function ok(text) {
34
+ return { content: [{ type: "text", text }] };
35
+ }
36
+ function fail(err) {
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`
42
+ }
43
+ ],
44
+ isError: true
45
+ };
46
+ }
47
+ async function startMcpServer() {
48
+ const server = new Server(
49
+ { name: "docsync", version: "0.1.0" },
50
+ { capabilities: { tools: {} } }
51
+ );
52
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
53
+ tools: [
54
+ {
55
+ name: "docsync_list_spaces",
56
+ description: "\u5217\u51FA\u6240\u6709\u53EF\u8BBF\u95EE\u7684 DocSync Space\uFF08\u4E2A\u4EBA\u7A7A\u95F4\u548C\u56E2\u961F\u7A7A\u95F4\uFF09",
57
+ inputSchema: { type: "object", properties: {} }
58
+ },
59
+ {
60
+ name: "docsync_list_files",
61
+ description: "\u5217\u51FA DocSync Space \u4E2D\u6307\u5B9A\u76EE\u5F55\u7684\u6587\u4EF6\u548C\u6587\u4EF6\u5939",
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ space: {
66
+ type: "string",
67
+ description: "Space \u540D\u79F0\u6216 ID"
68
+ },
69
+ path: {
70
+ type: "string",
71
+ description: "\u76EE\u5F55\u8DEF\u5F84\uFF0C\u7A7A\u5B57\u7B26\u4E32\u8868\u793A\u6839\u76EE\u5F55",
72
+ default: ""
73
+ }
74
+ },
75
+ required: ["space"]
76
+ }
77
+ },
78
+ {
79
+ name: "docsync_read_file",
80
+ description: "\u8BFB\u53D6 DocSync \u6587\u4EF6\u5185\u5BB9\uFF08Markdown\u3001CSV\u3001HTML \u7B49\u6587\u672C\u6587\u4EF6\uFF09",
81
+ inputSchema: {
82
+ type: "object",
83
+ properties: {
84
+ space: {
85
+ type: "string",
86
+ description: "Space \u540D\u79F0\u6216 ID"
87
+ },
88
+ path: { type: "string", description: "\u6587\u4EF6\u8DEF\u5F84" }
89
+ },
90
+ required: ["space", "path"]
91
+ }
92
+ },
93
+ {
94
+ name: "docsync_upload_file",
95
+ description: "\u4E0A\u4F20/\u521B\u5EFA\u6587\u4EF6\u5230 DocSync Space",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: {
99
+ space: {
100
+ type: "string",
101
+ description: "Space \u540D\u79F0\u6216 ID"
102
+ },
103
+ path: {
104
+ type: "string",
105
+ description: "\u76EE\u6807\u76EE\u5F55\u8DEF\u5F84\uFF08\u5982 reports\uFF09"
106
+ },
107
+ filename: {
108
+ type: "string",
109
+ description: "\u6587\u4EF6\u540D\uFF08\u5982 summary.md\uFF09"
110
+ },
111
+ content: { type: "string", description: "\u6587\u4EF6\u5185\u5BB9" }
112
+ },
113
+ required: ["space", "filename", "content"]
114
+ }
115
+ },
116
+ {
117
+ name: "docsync_mkdir",
118
+ description: "\u5728 DocSync Space \u4E2D\u521B\u5EFA\u6587\u4EF6\u5939",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ space: {
123
+ type: "string",
124
+ description: "Space \u540D\u79F0\u6216 ID"
125
+ },
126
+ path: {
127
+ type: "string",
128
+ description: "\u8981\u521B\u5EFA\u7684\u6587\u4EF6\u5939\u8DEF\u5F84"
129
+ }
130
+ },
131
+ required: ["space", "path"]
132
+ }
133
+ },
134
+ {
135
+ name: "docsync_delete",
136
+ description: "\u5220\u9664\u6587\u4EF6\u6216\u6587\u4EF6\u5939\uFF08\u8FDB\u5165\u56DE\u6536\u7AD9\uFF0C30 \u5929\u5185\u53EF\u6062\u590D\uFF09",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ space: {
141
+ type: "string",
142
+ description: "Space \u540D\u79F0\u6216 ID"
143
+ },
144
+ path: { type: "string", description: "\u8981\u5220\u9664\u7684\u8DEF\u5F84" }
145
+ },
146
+ required: ["space", "path"]
147
+ }
148
+ },
149
+ {
150
+ name: "docsync_file_history",
151
+ description: "\u67E5\u770B\u6587\u4EF6\u7684\u53D8\u66F4\u5386\u53F2\uFF08\u57FA\u4E8E Git \u7248\u672C\u63A7\u5236\uFF09",
152
+ inputSchema: {
153
+ type: "object",
154
+ properties: {
155
+ space: {
156
+ type: "string",
157
+ description: "Space \u540D\u79F0\u6216 ID"
158
+ },
159
+ path: { type: "string", description: "\u6587\u4EF6\u8DEF\u5F84" }
160
+ },
161
+ required: ["space"]
162
+ }
163
+ }
164
+ ]
165
+ }));
166
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
167
+ const args = request.params.arguments ?? {};
168
+ try {
169
+ const client = getClient();
170
+ switch (request.params.name) {
171
+ case "docsync_list_spaces": {
172
+ const spaces = await client.listSpaces();
173
+ const lines = spaces.map(
174
+ (s) => `${s.name} (${s.is_private ? "\u79C1\u6709" : "\u56E2\u961F"}, ${s.member_count} \u4EBA) id=${s.id}`
175
+ );
176
+ return ok(lines.join("\n"));
177
+ }
178
+ case "docsync_list_files": {
179
+ const sid = await resolveSpaceId(client, String(args.space));
180
+ const entries = await client.ls(sid, String(args.path ?? ""));
181
+ if (entries.length === 0) return ok("\uFF08\u7A7A\u76EE\u5F55\uFF09");
182
+ const lines = entries.map((e) => {
183
+ const icon = e.type === "tree" ? "\u{1F4C1}" : "\u{1F4C4}";
184
+ const size = e.type === "blob" ? ` (${formatSize(e.size)})` : "";
185
+ return `${icon} ${e.name}${size}`;
186
+ });
187
+ return ok(lines.join("\n"));
188
+ }
189
+ case "docsync_read_file": {
190
+ const sid = await resolveSpaceId(client, String(args.space));
191
+ const content = await client.cat(sid, String(args.path));
192
+ return ok(content);
193
+ }
194
+ case "docsync_upload_file": {
195
+ const sid = await resolveSpaceId(client, String(args.space));
196
+ const result = await client.upload(
197
+ sid,
198
+ String(args.path ?? ""),
199
+ String(args.filename),
200
+ String(args.content)
201
+ );
202
+ return ok(`\u5DF2\u4E0A\u4F20: ${result.path}`);
203
+ }
204
+ case "docsync_mkdir": {
205
+ const sid = await resolveSpaceId(client, String(args.space));
206
+ await client.mkdir(sid, String(args.path));
207
+ return ok(`\u5DF2\u521B\u5EFA: ${args.path}`);
208
+ }
209
+ case "docsync_delete": {
210
+ const sid = await resolveSpaceId(client, String(args.space));
211
+ await client.rm(sid, String(args.path));
212
+ return ok(`\u5DF2\u5220\u9664: ${args.path}\uFF0830 \u5929\u5185\u53EF\u4ECE\u56DE\u6536\u7AD9\u6062\u590D\uFF09`);
213
+ }
214
+ case "docsync_file_history": {
215
+ const sid = await resolveSpaceId(client, String(args.space));
216
+ const logs = await client.log(
217
+ sid,
218
+ args.path ? String(args.path) : void 0
219
+ );
220
+ if (logs.length === 0) return ok("\u6CA1\u6709\u53D8\u66F4\u5386\u53F2\u3002");
221
+ const lines = logs.map(
222
+ (l) => `${l.hash.substring(0, 7)} ${l.date} ${l.message}`
223
+ );
224
+ return ok(lines.join("\n"));
225
+ }
226
+ default:
227
+ return fail(`Unknown tool: ${request.params.name}`);
228
+ }
229
+ } catch (err) {
230
+ return fail(err);
231
+ }
232
+ });
233
+ const transport = new StdioServerTransport();
234
+ await server.connect(transport);
235
+ }
236
+ export {
237
+ startMcpServer
238
+ };
239
+ //# sourceMappingURL=mcp-EJZJGAX6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/**\n * DocSync MCP Server (stdio transport)\n *\n * Usage:\n * docz mcp\n *\n * 环境变量:\n * DOCSYNC_API_TOKEN — 必须\n * DOCSYNC_BASE_URL — 可选,默认 https://docz.zhenguanyu.com\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { DocSyncClient } from './client.js';\nimport { getBaseUrl, getToken } from './config.js';\n\nfunction getClient(): DocSyncClient {\n const token = getToken();\n if (!token) {\n throw new Error(\n 'DOCSYNC_API_TOKEN not set. Run `docz login --token <token>` or set the env var.'\n );\n }\n return new DocSyncClient(getBaseUrl(), token);\n}\n\nasync function resolveSpaceId(\n client: DocSyncClient,\n nameOrId: string\n): Promise<string> {\n const space = await client.resolveSpace(nameOrId);\n return space.id;\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction ok(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nfunction fail(err: unknown) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n}\n\nexport async function startMcpServer(): Promise<void> {\n const server = new Server(\n { name: 'docsync', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: 'docsync_list_spaces',\n description: '列出所有可访问的 DocSync Space(个人空间和团队空间)',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'docsync_list_files',\n description: '列出 DocSync Space 中指定目录的文件和文件夹',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: {\n type: 'string',\n description: '目录路径,空字符串表示根目录',\n default: '',\n },\n },\n required: ['space'],\n },\n },\n {\n name: 'docsync_read_file',\n description: '读取 DocSync 文件内容(Markdown、CSV、HTML 等文本文件)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: { type: 'string', description: '文件路径' },\n },\n required: ['space', 'path'],\n },\n },\n {\n name: 'docsync_upload_file',\n description: '上传/创建文件到 DocSync Space',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: {\n type: 'string',\n description: '目标目录路径(如 reports)',\n },\n filename: {\n type: 'string',\n description: '文件名(如 summary.md)',\n },\n content: { type: 'string', description: '文件内容' },\n },\n required: ['space', 'filename', 'content'],\n },\n },\n {\n name: 'docsync_mkdir',\n description: '在 DocSync Space 中创建文件夹',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: {\n type: 'string',\n description: '要创建的文件夹路径',\n },\n },\n required: ['space', 'path'],\n },\n },\n {\n name: 'docsync_delete',\n description: '删除文件或文件夹(进入回收站,30 天内可恢复)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: { type: 'string', description: '要删除的路径' },\n },\n required: ['space', 'path'],\n },\n },\n {\n name: 'docsync_file_history',\n description: '查看文件的变更历史(基于 Git 版本控制)',\n inputSchema: {\n type: 'object' as const,\n properties: {\n space: {\n type: 'string',\n description: 'Space 名称或 ID',\n },\n path: { type: 'string', description: '文件路径' },\n },\n required: ['space'],\n },\n },\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const args = (request.params.arguments ?? {}) as Record<string, unknown>;\n\n try {\n const client = getClient();\n\n switch (request.params.name) {\n case 'docsync_list_spaces': {\n const spaces = await client.listSpaces();\n const lines = spaces.map(\n (s) =>\n `${s.name} (${s.is_private ? '私有' : '团队'}, ${s.member_count} 人) id=${s.id}`\n );\n return ok(lines.join('\\n'));\n }\n\n case 'docsync_list_files': {\n const sid = await resolveSpaceId(client, String(args.space));\n const entries = await client.ls(sid, String(args.path ?? ''));\n if (entries.length === 0) return ok('(空目录)');\n const lines = entries.map((e) => {\n const icon = e.type === 'tree' ? '📁' : '📄';\n const size = e.type === 'blob' ? ` (${formatSize(e.size)})` : '';\n return `${icon} ${e.name}${size}`;\n });\n return ok(lines.join('\\n'));\n }\n\n case 'docsync_read_file': {\n const sid = await resolveSpaceId(client, String(args.space));\n const content = await client.cat(sid, String(args.path));\n return ok(content);\n }\n\n case 'docsync_upload_file': {\n const sid = await resolveSpaceId(client, String(args.space));\n const result = await client.upload(\n sid,\n String(args.path ?? ''),\n String(args.filename),\n String(args.content)\n );\n return ok(`已上传: ${result.path}`);\n }\n\n case 'docsync_mkdir': {\n const sid = await resolveSpaceId(client, String(args.space));\n await client.mkdir(sid, String(args.path));\n return ok(`已创建: ${args.path}`);\n }\n\n case 'docsync_delete': {\n const sid = await resolveSpaceId(client, String(args.space));\n await client.rm(sid, String(args.path));\n return ok(`已删除: ${args.path}(30 天内可从回收站恢复)`);\n }\n\n case 'docsync_file_history': {\n const sid = await resolveSpaceId(client, String(args.space));\n const logs = await client.log(\n sid,\n args.path ? String(args.path) : undefined\n );\n if (logs.length === 0) return ok('没有变更历史。');\n const lines = logs.map(\n (l) => `${l.hash.substring(0, 7)} ${l.date} ${l.message}`\n );\n return ok(lines.join('\\n'));\n }\n\n default:\n return fail(`Unknown tool: ${request.params.name}`);\n }\n } catch (err) {\n return fail(err);\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docz-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "DocSync CLI — read and write company documents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,6 +37,7 @@
37
37
  "clean": "rm -rf dist node_modules"
38
38
  },
39
39
  "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.28.0",
40
41
  "commander": "^13.1.0"
41
42
  },
42
43
  "devDependencies": {