docz-cli 0.5.0 → 0.7.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/dist/{chunk-EBJ32KUL.js → chunk-BHXHIHWH.js} +366 -109
- package/dist/chunk-BHXHIHWH.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/mcp-AU2BEEPP.js +685 -0
- package/dist/mcp-AU2BEEPP.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-EBJ32KUL.js.map +0 -1
- package/dist/mcp-26HFT6SQ.js +0 -384
- package/dist/mcp-26HFT6SQ.js.map +0 -1
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/client.ts
|
|
4
|
+
var ConflictError = class extends Error {
|
|
5
|
+
constructor(detail) {
|
|
6
|
+
super(
|
|
7
|
+
"Conflict: file has been modified by others. Please re-read the latest content and re-apply your changes."
|
|
8
|
+
);
|
|
9
|
+
this.detail = detail;
|
|
10
|
+
this.name = "ConflictError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
4
13
|
var DocSyncClient = class {
|
|
5
14
|
constructor(baseUrl, token) {
|
|
6
15
|
this.baseUrl = baseUrl;
|
|
@@ -48,12 +57,28 @@ var DocSyncClient = class {
|
|
|
48
57
|
`/api/spaces/${spaceId}/tree?path=${encodeURIComponent(path)}`
|
|
49
58
|
);
|
|
50
59
|
}
|
|
60
|
+
async treeFull(spaceId) {
|
|
61
|
+
return this.request(`/api/spaces/${spaceId}/tree/full`);
|
|
62
|
+
}
|
|
51
63
|
// --- Blob ---
|
|
52
64
|
async cat(spaceId, filepath) {
|
|
53
65
|
return this.request(
|
|
54
66
|
`/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`
|
|
55
67
|
);
|
|
56
68
|
}
|
|
69
|
+
async catWithRef(spaceId, filepath) {
|
|
70
|
+
const res = await fetch(
|
|
71
|
+
`${this.baseUrl}/api/spaces/${spaceId}/blob/${encodeURIComponent(filepath)}`,
|
|
72
|
+
{ headers: { Authorization: `Bearer ${this.token}` } }
|
|
73
|
+
);
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
const body = await res.text().catch(() => "");
|
|
76
|
+
throw new Error(`${res.status} ${res.statusText}: ${body}`.trim());
|
|
77
|
+
}
|
|
78
|
+
const content = await res.text();
|
|
79
|
+
const ref = res.headers.get("X-Git-Ref") ?? "";
|
|
80
|
+
return { content, ref };
|
|
81
|
+
}
|
|
57
82
|
// --- Write ---
|
|
58
83
|
async upload(spaceId, dir, filename, content) {
|
|
59
84
|
const blob = typeof content === "string" ? new Blob([content], { type: "application/octet-stream" }) : new Blob([content], { type: "application/octet-stream" });
|
|
@@ -65,6 +90,31 @@ var DocSyncClient = class {
|
|
|
65
90
|
body: form
|
|
66
91
|
});
|
|
67
92
|
}
|
|
93
|
+
async save(spaceId, path, content, opts) {
|
|
94
|
+
const body = { path, content };
|
|
95
|
+
if (opts?.baseRef) body.base_ref = opts.baseRef;
|
|
96
|
+
if (opts?.message) body.message = opts.message;
|
|
97
|
+
const res = await fetch(
|
|
98
|
+
`${this.baseUrl}/api/spaces/${spaceId}/files/save`,
|
|
99
|
+
{
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: {
|
|
102
|
+
Authorization: `Bearer ${this.token}`,
|
|
103
|
+
"Content-Type": "application/json"
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify(body)
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
if (res.status === 409) {
|
|
109
|
+
const data = await res.json();
|
|
110
|
+
throw new ConflictError(data);
|
|
111
|
+
}
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
const text = await res.text().catch(() => "");
|
|
114
|
+
throw new Error(`${res.status} ${res.statusText}: ${text}`.trim());
|
|
115
|
+
}
|
|
116
|
+
return res.json();
|
|
117
|
+
}
|
|
68
118
|
async mkdir(spaceId, path) {
|
|
69
119
|
await this.request(`/api/spaces/${spaceId}/files/mkdir`, {
|
|
70
120
|
method: "POST",
|
|
@@ -86,6 +136,13 @@ var DocSyncClient = class {
|
|
|
86
136
|
body: JSON.stringify({ old_path: from, new_path: to })
|
|
87
137
|
});
|
|
88
138
|
}
|
|
139
|
+
async rollback(spaceId, filePath, commitHash) {
|
|
140
|
+
await this.request(`/api/spaces/${spaceId}/files/rollback`, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify({ file_path: filePath, commit_hash: commitHash })
|
|
144
|
+
});
|
|
145
|
+
}
|
|
89
146
|
// --- History ---
|
|
90
147
|
async log(spaceId, filepath) {
|
|
91
148
|
const path = filepath ? `/api/spaces/${spaceId}/log/${encodeURIComponent(filepath)}` : `/api/spaces/${spaceId}/log/`;
|
|
@@ -95,6 +152,48 @@ var DocSyncClient = class {
|
|
|
95
152
|
async trash(spaceId) {
|
|
96
153
|
return this.request(`/api/spaces/${spaceId}/trash`);
|
|
97
154
|
}
|
|
155
|
+
async restore(spaceId, path, commit) {
|
|
156
|
+
await this.request(`/api/spaces/${spaceId}/trash/restore`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "Content-Type": "application/json" },
|
|
159
|
+
body: JSON.stringify({ path, commit })
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// --- Comments ---
|
|
163
|
+
async listComments(spaceId, filePath) {
|
|
164
|
+
return this.request(
|
|
165
|
+
`/api/spaces/${spaceId}/comments?path=${encodeURIComponent(filePath)}`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
async createComment(spaceId, filePath, content) {
|
|
169
|
+
return this.request(`/api/spaces/${spaceId}/comments`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: { "Content-Type": "application/json" },
|
|
172
|
+
body: JSON.stringify({ file_path: filePath, content })
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async replyComment(spaceId, commentId, content) {
|
|
176
|
+
return this.request(
|
|
177
|
+
`/api/spaces/${spaceId}/comments/${commentId}/replies`,
|
|
178
|
+
{
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
body: JSON.stringify({ content })
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
async closeComment(spaceId, commentId) {
|
|
186
|
+
return this.request(`/api/spaces/${spaceId}/comments/${commentId}`, {
|
|
187
|
+
method: "PUT",
|
|
188
|
+
headers: { "Content-Type": "application/json" },
|
|
189
|
+
body: JSON.stringify({ is_closed: true })
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async deleteComment(spaceId, commentId) {
|
|
193
|
+
await this.request(`/api/spaces/${spaceId}/comments/${commentId}`, {
|
|
194
|
+
method: "DELETE"
|
|
195
|
+
});
|
|
196
|
+
}
|
|
98
197
|
// --- Share Links ---
|
|
99
198
|
async createShareLink(spaceId, filePath, opts) {
|
|
100
199
|
const body = { file_path: filePath };
|
|
@@ -137,7 +236,9 @@ var DocSyncClient = class {
|
|
|
137
236
|
async diffFile(spaceId, filePath, to, from) {
|
|
138
237
|
const params = new URLSearchParams({ to });
|
|
139
238
|
if (from) params.set("from", from);
|
|
140
|
-
return this.request(
|
|
239
|
+
return this.request(
|
|
240
|
+
`/api/spaces/${spaceId}/diff/${encodeURIComponent(filePath)}?${params}`
|
|
241
|
+
);
|
|
141
242
|
}
|
|
142
243
|
async diffSummary(spaceId, to, from) {
|
|
143
244
|
const params = new URLSearchParams({ to });
|
|
@@ -192,7 +293,7 @@ function getConfigPath() {
|
|
|
192
293
|
|
|
193
294
|
// src/commands.ts
|
|
194
295
|
import { readFileSync as readFileSync2 } from "fs";
|
|
195
|
-
import { basename
|
|
296
|
+
import { basename } from "path";
|
|
196
297
|
function getClient() {
|
|
197
298
|
const token = getToken();
|
|
198
299
|
if (!token) {
|
|
@@ -222,47 +323,76 @@ function formatSize(bytes) {
|
|
|
222
323
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
223
324
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
224
325
|
}
|
|
225
|
-
var SHORT_URL_RE = /\/s\/([^/]+)\/f\/([^/?\s]+)/;
|
|
226
|
-
var SLUG_ONLY_RE = /\/s\/([^/?\s]+)\/?(?:\?.*)?$/;
|
|
227
326
|
var SHARE_URL_RE = /\/share\/([^/?\s]+)/;
|
|
327
|
+
async function resolveSlug(client, slug) {
|
|
328
|
+
try {
|
|
329
|
+
const spaces = await client.listSpaces();
|
|
330
|
+
const found = spaces.find((s) => s.slug === slug);
|
|
331
|
+
if (found) return found;
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
return client.resolveBySlug(slug);
|
|
335
|
+
}
|
|
228
336
|
async function resolveUrl(client, input) {
|
|
229
|
-
|
|
337
|
+
let pathname;
|
|
338
|
+
try {
|
|
339
|
+
pathname = new URL(input).pathname;
|
|
340
|
+
} catch {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
const fileMatch = pathname.match(/^\/s\/([^/]+)\/f\/([^/]+)$/);
|
|
230
344
|
if (fileMatch) {
|
|
231
345
|
const [, slug, fileId] = fileMatch;
|
|
232
346
|
const space = await resolveSlug(client, slug);
|
|
233
347
|
const ref = await client.resolveFileRef(fileId);
|
|
234
348
|
return { spaceId: space.id, path: ref.path };
|
|
235
349
|
}
|
|
236
|
-
const slugMatch =
|
|
350
|
+
const slugMatch = pathname.match(/^\/s\/([^/]+)(\/.*)?$/);
|
|
237
351
|
if (slugMatch) {
|
|
238
|
-
const [, slug] = slugMatch;
|
|
352
|
+
const [, slug, rest] = slugMatch;
|
|
239
353
|
const space = await resolveSlug(client, slug);
|
|
240
|
-
|
|
354
|
+
const filePath = rest ? decodeURIComponent(rest.slice(1)) : "";
|
|
355
|
+
return { spaceId: space.id, path: filePath };
|
|
241
356
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const found = spaces.find((s) => s.slug === slug);
|
|
248
|
-
if (found) return found;
|
|
249
|
-
} catch {
|
|
357
|
+
const legacyMatch = pathname.match(/^\/spaces\/([^/]+)(\/.*)?$/);
|
|
358
|
+
if (legacyMatch) {
|
|
359
|
+
const [, spaceId, rest] = legacyMatch;
|
|
360
|
+
const filePath = rest ? decodeURIComponent(rest.slice(1)) : "";
|
|
361
|
+
return { spaceId, path: filePath };
|
|
250
362
|
}
|
|
251
|
-
return
|
|
363
|
+
return null;
|
|
252
364
|
}
|
|
253
365
|
async function resolveTarget(client, args) {
|
|
254
366
|
const first = args[0];
|
|
255
367
|
if (first && (first.startsWith("http://") || first.startsWith("https://"))) {
|
|
256
368
|
const result = await resolveUrl(client, first);
|
|
257
369
|
if (result) return result;
|
|
370
|
+
throw new Error(
|
|
371
|
+
`Unrecognized DocSync URL: ${first}
|
|
372
|
+
Expected: /s/{slug}[/f/{fileId}], /s/{slug}[/path], or /spaces/{id}[/path]`
|
|
373
|
+
);
|
|
258
374
|
}
|
|
259
375
|
const { space, path } = parseTarget(args);
|
|
260
376
|
const s = await client.resolveSpace(space);
|
|
261
377
|
return { spaceId: s.id, path };
|
|
262
378
|
}
|
|
379
|
+
async function resolveSpaceArg(client, input) {
|
|
380
|
+
if (input.startsWith("http://") || input.startsWith("https://")) {
|
|
381
|
+
const result = await resolveUrl(client, input);
|
|
382
|
+
if (result) return { id: result.spaceId };
|
|
383
|
+
throw new Error(
|
|
384
|
+
`Unrecognized DocSync URL: ${input}
|
|
385
|
+
Expected: /s/{slug}[/f/{fileId}], /s/{slug}[/path], or /spaces/{id}[/path]`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return client.resolveSpace(input);
|
|
389
|
+
}
|
|
263
390
|
function parseExpires(value) {
|
|
264
391
|
const match = value.match(/^(\d+)([dh])$/);
|
|
265
|
-
if (!match)
|
|
392
|
+
if (!match)
|
|
393
|
+
throw new Error(
|
|
394
|
+
`Invalid expires format: "${value}". Use e.g. 7d, 24h, 30d`
|
|
395
|
+
);
|
|
266
396
|
const [, num, unit] = match;
|
|
267
397
|
const ms = unit === "d" ? Number(num) * 864e5 : Number(num) * 36e5;
|
|
268
398
|
return new Date(Date.now() + ms).toISOString();
|
|
@@ -272,6 +402,14 @@ function extractShareToken(input) {
|
|
|
272
402
|
if (m) return m[1];
|
|
273
403
|
return input;
|
|
274
404
|
}
|
|
405
|
+
async function readStdin() {
|
|
406
|
+
const chunks = [];
|
|
407
|
+
for await (const chunk of process.stdin) {
|
|
408
|
+
chunks.push(chunk);
|
|
409
|
+
}
|
|
410
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
411
|
+
}
|
|
412
|
+
var MAX_SAVE_SIZE = 2 * 1024 * 1024;
|
|
275
413
|
function registerCommands(program) {
|
|
276
414
|
program.command("login").description("Configure DocSync credentials").option("-u, --url <url>", "DocSync server URL").option("-t, --token <token>", "API token").action(async (opts) => {
|
|
277
415
|
const url = opts.url ?? getBaseUrl();
|
|
@@ -308,10 +446,10 @@ function registerCommands(program) {
|
|
|
308
446
|
console.log(`${s.name} ${tag} ${s.member_count} members ${s.id}`);
|
|
309
447
|
}
|
|
310
448
|
});
|
|
311
|
-
program.command("ls").description("List files \u2014 docz ls <space>[:<path>] or <url>").argument("<target...>").action(async (args) => {
|
|
449
|
+
program.command("ls").description("List files \u2014 docz ls <space>[:<path>] or <url>").argument("<target...>").option("-R, --recursive", "Recursively list all files").action(async (args, opts) => {
|
|
312
450
|
const client = getClient();
|
|
313
451
|
const { spaceId, path } = await resolveTarget(client, args);
|
|
314
|
-
const entries = await client.ls(spaceId, path);
|
|
452
|
+
const entries = opts.recursive ? await client.treeFull(spaceId) : await client.ls(spaceId, path);
|
|
315
453
|
if (entries.length === 0) {
|
|
316
454
|
console.log("(empty)");
|
|
317
455
|
return;
|
|
@@ -324,7 +462,7 @@ function registerCommands(program) {
|
|
|
324
462
|
}
|
|
325
463
|
}
|
|
326
464
|
});
|
|
327
|
-
program.command("cat").description("Read file content \u2014 docz cat <space>:<path> or <url>").argument("<target...>").action(async (args) => {
|
|
465
|
+
program.command("cat").description("Read file content \u2014 docz cat <space>:<path> or <url>").argument("<target...>").option("--ref", "Also output Git ref to stderr").action(async (args, opts) => {
|
|
328
466
|
const client = getClient();
|
|
329
467
|
const { spaceId, path } = await resolveTarget(client, args);
|
|
330
468
|
if (!path) {
|
|
@@ -333,80 +471,100 @@ function registerCommands(program) {
|
|
|
333
471
|
);
|
|
334
472
|
process.exit(1);
|
|
335
473
|
}
|
|
336
|
-
|
|
337
|
-
|
|
474
|
+
if (opts.ref) {
|
|
475
|
+
const result = await client.catWithRef(spaceId, path);
|
|
476
|
+
console.error(`ref: ${result.ref}`);
|
|
477
|
+
process.stdout.write(result.content);
|
|
478
|
+
} else {
|
|
479
|
+
const content = await client.cat(spaceId, path);
|
|
480
|
+
process.stdout.write(content);
|
|
481
|
+
}
|
|
338
482
|
});
|
|
339
|
-
program.command("upload").description(
|
|
340
|
-
|
|
483
|
+
program.command("upload").description(
|
|
484
|
+
"Upload file \u2014 docz upload <local-file> <space>[:<dir>] or <url>"
|
|
485
|
+
).argument("<file>", "Local file to upload").argument("<target...>").action(async (file, args) => {
|
|
341
486
|
const client = getClient();
|
|
342
|
-
const
|
|
487
|
+
const { spaceId, path: dir } = await resolveTarget(client, args);
|
|
343
488
|
const content = readFileSync2(file);
|
|
344
489
|
const filename = basename(file);
|
|
345
490
|
const targetDir = dir || "";
|
|
346
|
-
const result = await client.upload(
|
|
491
|
+
const result = await client.upload(spaceId, targetDir, filename, content);
|
|
347
492
|
console.log(`Uploaded: ${result.path}`);
|
|
348
493
|
});
|
|
349
|
-
program.command("write").description(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
494
|
+
program.command("write").description(
|
|
495
|
+
"Write content to file \u2014 docz write <space>:<path> <content> or <url> <content>"
|
|
496
|
+
).argument("<target>", "space:dir/filename.md or short URL").argument("<content>", "File content (or - for stdin)").option("--force", "Skip conflict detection").option("-m, --message <msg>", "Custom commit message").action(
|
|
497
|
+
async (target, content, opts) => {
|
|
498
|
+
const client = getClient();
|
|
499
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
500
|
+
if (!path) {
|
|
501
|
+
console.error(
|
|
502
|
+
"Error: path is required. Usage: docz write <space>:<dir/filename> <content>"
|
|
503
|
+
);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
}
|
|
506
|
+
const body = content === "-" ? await readStdin() : content;
|
|
507
|
+
if (Buffer.byteLength(body, "utf-8") > MAX_SAVE_SIZE) {
|
|
508
|
+
console.error(
|
|
509
|
+
"Error: content exceeds 2MB limit. Use `docz upload` for large files."
|
|
510
|
+
);
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
let baseRef;
|
|
514
|
+
if (!opts.force) {
|
|
515
|
+
try {
|
|
516
|
+
const existing = await client.catWithRef(spaceId, path);
|
|
517
|
+
baseRef = existing.ref;
|
|
518
|
+
} catch (err) {
|
|
519
|
+
const msg = err instanceof Error ? err.message : "";
|
|
520
|
+
if (!msg.includes("404")) throw err;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const result = await client.save(spaceId, path, body, {
|
|
525
|
+
baseRef,
|
|
526
|
+
message: opts.message
|
|
527
|
+
});
|
|
528
|
+
console.log(`Written: ${result.path} (ref: ${result.ref})`);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
if (err instanceof ConflictError) {
|
|
531
|
+
console.error(
|
|
532
|
+
"Error: file was modified by someone else. Please re-read the latest content and try again."
|
|
533
|
+
);
|
|
534
|
+
process.exit(1);
|
|
535
|
+
}
|
|
536
|
+
throw err;
|
|
366
537
|
}
|
|
367
|
-
body = Buffer.concat(chunks).toString("utf-8");
|
|
368
|
-
} else {
|
|
369
|
-
body = content;
|
|
370
538
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
body
|
|
376
|
-
);
|
|
377
|
-
console.log(`Written: ${result.path}`);
|
|
378
|
-
});
|
|
379
|
-
program.command("mkdir").description("Create folder \u2014 docz mkdir <space>:<path>").argument("<target...>").action(async (args) => {
|
|
380
|
-
const { space, path } = parseTarget(args);
|
|
539
|
+
);
|
|
540
|
+
program.command("mkdir").description("Create folder \u2014 docz mkdir <space>:<path> or <url>").argument("<target...>").action(async (args) => {
|
|
541
|
+
const client = getClient();
|
|
542
|
+
const { spaceId, path } = await resolveTarget(client, args);
|
|
381
543
|
if (!path) {
|
|
382
544
|
console.error("Error: path is required.");
|
|
383
545
|
process.exit(1);
|
|
384
546
|
}
|
|
385
|
-
|
|
386
|
-
const s = await client.resolveSpace(space);
|
|
387
|
-
await client.mkdir(s.id, path);
|
|
547
|
+
await client.mkdir(spaceId, path);
|
|
388
548
|
console.log(`Created: ${path}`);
|
|
389
549
|
});
|
|
390
|
-
program.command("rm").description("Delete file/folder \u2014 docz rm <space>:<path>").argument("<target...>").action(async (args) => {
|
|
391
|
-
const
|
|
550
|
+
program.command("rm").description("Delete file/folder \u2014 docz rm <space>:<path> or <url>").argument("<target...>").action(async (args) => {
|
|
551
|
+
const client = getClient();
|
|
552
|
+
const { spaceId, path } = await resolveTarget(client, args);
|
|
392
553
|
if (!path) {
|
|
393
554
|
console.error("Error: path is required.");
|
|
394
555
|
process.exit(1);
|
|
395
556
|
}
|
|
396
|
-
|
|
397
|
-
const s = await client.resolveSpace(space);
|
|
398
|
-
await client.rm(s.id, path);
|
|
557
|
+
await client.rm(spaceId, path);
|
|
399
558
|
console.log(`Deleted: ${path} (recoverable from trash for 30 days)`);
|
|
400
559
|
});
|
|
401
|
-
program.command("mv").description("Rename/move \u2014 docz mv <space>:<from> <to>").argument("<target>", "space:old-path").argument("<to>", "new-path").action(async (target, to) => {
|
|
402
|
-
const
|
|
560
|
+
program.command("mv").description("Rename/move \u2014 docz mv <space>:<from> <to> or <url> <to>").argument("<target>", "space:old-path or short URL").argument("<to>", "new-path").action(async (target, to) => {
|
|
561
|
+
const client = getClient();
|
|
562
|
+
const { spaceId, path: from } = await resolveTarget(client, [target]);
|
|
403
563
|
if (!from) {
|
|
404
564
|
console.error("Error: source path is required.");
|
|
405
565
|
process.exit(1);
|
|
406
566
|
}
|
|
407
|
-
|
|
408
|
-
const s = await client.resolveSpace(space);
|
|
409
|
-
await client.mv(s.id, from, to);
|
|
567
|
+
await client.mv(spaceId, from, to);
|
|
410
568
|
console.log(`Moved: ${from} \u2192 ${to}`);
|
|
411
569
|
});
|
|
412
570
|
program.command("log").description("Show change history \u2014 docz log <space>[:<path>] or <url>").argument("<target...>").action(async (args) => {
|
|
@@ -421,9 +579,21 @@ function registerCommands(program) {
|
|
|
421
579
|
console.log(`${l.hash} ${l.date} ${l.message}`);
|
|
422
580
|
}
|
|
423
581
|
});
|
|
582
|
+
program.command("rollback").description(
|
|
583
|
+
"Rollback file to a previous version \u2014 docz rollback <space>:<path> <commit> or <url> <commit>"
|
|
584
|
+
).argument("<target>", "space:path or short URL").argument("<commit>", "Commit hash to rollback to").action(async (target, commit) => {
|
|
585
|
+
const client = getClient();
|
|
586
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
587
|
+
if (!path) {
|
|
588
|
+
console.error("Error: file path is required.");
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
await client.rollback(spaceId, path, commit);
|
|
592
|
+
console.log(`Rolled back: ${path} \u2192 ${commit.substring(0, 7)}`);
|
|
593
|
+
});
|
|
424
594
|
program.command("trash").description("Show deleted files \u2014 docz trash <space>").argument("<space>").action(async (spaceName) => {
|
|
425
595
|
const client = getClient();
|
|
426
|
-
const s = await client
|
|
596
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
427
597
|
const items = await client.trash(s.id);
|
|
428
598
|
if (items.length === 0) {
|
|
429
599
|
console.log("Trash is empty.");
|
|
@@ -435,32 +605,109 @@ function registerCommands(program) {
|
|
|
435
605
|
);
|
|
436
606
|
}
|
|
437
607
|
});
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
608
|
+
program.command("restore").description(
|
|
609
|
+
"Restore file from trash \u2014 docz restore <space>:<path> <commit> or <url> <commit>"
|
|
610
|
+
).argument("<target>", "space:path or short URL").argument("<commit>", "Commit hash from trash listing").action(async (target, commit) => {
|
|
611
|
+
const client = getClient();
|
|
612
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
613
|
+
if (!path) {
|
|
614
|
+
console.error("Error: path is required.");
|
|
615
|
+
process.exit(1);
|
|
616
|
+
}
|
|
617
|
+
await client.restore(spaceId, path, commit);
|
|
618
|
+
console.log(`Restored: ${path}`);
|
|
619
|
+
});
|
|
620
|
+
const comment = program.command("comment").description("Manage file comments");
|
|
621
|
+
comment.command("list").description("List comments \u2014 docz comment list <space>:<path>").argument("<target...>").action(async (args) => {
|
|
622
|
+
const client = getClient();
|
|
623
|
+
const { spaceId, path } = await resolveTarget(client, args);
|
|
624
|
+
if (!path) {
|
|
625
|
+
console.error("Error: file path is required.");
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
const comments = await client.listComments(spaceId, path);
|
|
629
|
+
if (comments.length === 0) {
|
|
630
|
+
console.log("No comments.");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
for (const c of comments) {
|
|
634
|
+
const status = c.is_closed ? " [closed]" : "";
|
|
635
|
+
console.log(`#${c.id} ${c.user_name} (${c.created_at})${status}`);
|
|
636
|
+
console.log(` ${c.content}`);
|
|
637
|
+
for (const r of c.replies) {
|
|
638
|
+
console.log(` \u2192 ${r.user_name}: ${r.content}`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
comment.command("add").description(
|
|
643
|
+
"Add comment \u2014 docz comment add <space>:<path> <content> or <url> <content>"
|
|
644
|
+
).argument("<target>", "space:path or short URL").argument("<content>", "Comment text (or - for stdin)").action(async (target, content) => {
|
|
645
|
+
const client = getClient();
|
|
646
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
441
647
|
if (!path) {
|
|
442
|
-
console.error("Error: file path is required.
|
|
648
|
+
console.error("Error: file path is required.");
|
|
443
649
|
process.exit(1);
|
|
444
650
|
}
|
|
651
|
+
const body = content === "-" ? await readStdin() : content;
|
|
652
|
+
const c = await client.createComment(spaceId, path, body);
|
|
653
|
+
console.log(`Comment #${c.id} created.`);
|
|
654
|
+
});
|
|
655
|
+
comment.command("reply").description(
|
|
656
|
+
"Reply to comment \u2014 docz comment reply <space> <commentId> <content>"
|
|
657
|
+
).argument("<space>").argument("<commentId>").argument("<content>", "Reply text (or - for stdin)").action(async (spaceName, commentId, content) => {
|
|
658
|
+
const client = getClient();
|
|
659
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
660
|
+
const body = content === "-" ? await readStdin() : content;
|
|
661
|
+
const r = await client.replyComment(s.id, Number(commentId), body);
|
|
662
|
+
console.log(`Reply #${r.id} created.`);
|
|
663
|
+
});
|
|
664
|
+
comment.command("close").description("Close comment \u2014 docz comment close <space> <commentId>").argument("<space>").argument("<commentId>").action(async (spaceName, commentId) => {
|
|
665
|
+
const client = getClient();
|
|
666
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
667
|
+
await client.closeComment(s.id, Number(commentId));
|
|
668
|
+
console.log(`Comment #${commentId} closed.`);
|
|
669
|
+
});
|
|
670
|
+
comment.command("rm").description("Delete comment \u2014 docz comment rm <space> <commentId>").argument("<space>").argument("<commentId>").action(async (spaceName, commentId) => {
|
|
445
671
|
const client = getClient();
|
|
446
|
-
const s = await client
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (opts.users) apiOpts.userIds = opts.users.split(",").map((s2) => s2.trim());
|
|
450
|
-
if (opts.groups) apiOpts.groupIds = opts.groups.split(",").map((s2) => s2.trim());
|
|
451
|
-
const link = await client.createShareLink(s.id, path, apiOpts);
|
|
452
|
-
const baseUrl = getBaseUrl();
|
|
453
|
-
console.log(`Created share link:`);
|
|
454
|
-
console.log(` id: ${link.id}`);
|
|
455
|
-
console.log(` token: ${link.token}`);
|
|
456
|
-
console.log(` url: ${baseUrl}/share/${link.token}`);
|
|
457
|
-
console.log(` expires: ${link.expires_at ?? "never"}`);
|
|
458
|
-
if (link.user_ids?.length) console.log(` users: ${link.user_ids.join(", ")}`);
|
|
459
|
-
if (link.group_ids?.length) console.log(` groups: ${link.group_ids.length}`);
|
|
672
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
673
|
+
await client.deleteComment(s.id, Number(commentId));
|
|
674
|
+
console.log(`Comment #${commentId} deleted.`);
|
|
460
675
|
});
|
|
676
|
+
const share = program.command("share").description("Manage share links");
|
|
677
|
+
share.command("create").description(
|
|
678
|
+
"Create share link \u2014 docz share create <space>:<path> or <url>"
|
|
679
|
+
).argument("<target>", "space:path or short URL").option("--expires <duration>", "Expiry duration (e.g. 7d, 24h)").option("--users <emails>", "Comma-separated user emails or IDs").option("--groups <ids>", "Comma-separated group IDs").action(
|
|
680
|
+
async (target, opts) => {
|
|
681
|
+
const client = getClient();
|
|
682
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
683
|
+
if (!path) {
|
|
684
|
+
console.error(
|
|
685
|
+
"Error: file path is required. Usage: docz share create <space>:<path>"
|
|
686
|
+
);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
const apiOpts = {};
|
|
690
|
+
if (opts.expires) apiOpts.expiresAt = parseExpires(opts.expires);
|
|
691
|
+
if (opts.users)
|
|
692
|
+
apiOpts.userIds = opts.users.split(",").map((s) => s.trim());
|
|
693
|
+
if (opts.groups)
|
|
694
|
+
apiOpts.groupIds = opts.groups.split(",").map((s) => s.trim());
|
|
695
|
+
const link = await client.createShareLink(spaceId, path, apiOpts);
|
|
696
|
+
const baseUrl = getBaseUrl();
|
|
697
|
+
console.log("Created share link:");
|
|
698
|
+
console.log(` id: ${link.id}`);
|
|
699
|
+
console.log(` token: ${link.token}`);
|
|
700
|
+
console.log(` url: ${baseUrl}/share/${link.token}`);
|
|
701
|
+
console.log(` expires: ${link.expires_at ?? "never"}`);
|
|
702
|
+
if (link.user_ids?.length)
|
|
703
|
+
console.log(` users: ${link.user_ids.join(", ")}`);
|
|
704
|
+
if (link.group_ids?.length)
|
|
705
|
+
console.log(` groups: ${link.group_ids.length}`);
|
|
706
|
+
}
|
|
707
|
+
);
|
|
461
708
|
share.command("list").description("List share links \u2014 docz share list <space>").argument("<space>").option("--file <path>", "Filter by file path").action(async (spaceName, opts) => {
|
|
462
709
|
const client = getClient();
|
|
463
|
-
const s = await client
|
|
710
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
464
711
|
const links = await client.listShareLinks(s.id, opts.file);
|
|
465
712
|
if (links.length === 0) {
|
|
466
713
|
console.log("No share links.");
|
|
@@ -469,19 +716,25 @@ function registerCommands(program) {
|
|
|
469
716
|
for (const l of links) {
|
|
470
717
|
const expires = l.expires_at ?? "never";
|
|
471
718
|
const creator = l.created_by_name ?? l.created_by_email ?? l.created_by;
|
|
472
|
-
console.log(
|
|
719
|
+
console.log(
|
|
720
|
+
`${l.id} ${l.token} ${l.file_path} ${expires} ${creator}`
|
|
721
|
+
);
|
|
473
722
|
}
|
|
474
723
|
});
|
|
475
|
-
share.command("update").description("Update share link \u2014 docz share update <space> <link-id>").argument("<space>").argument("<linkId>").option("--expires <duration>", "New expiry duration (e.g. 30d)").option("--users <emails>", "Comma-separated user emails or IDs").option("--groups <ids>", "Comma-separated group IDs").action(
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
724
|
+
share.command("update").description("Update share link \u2014 docz share update <space> <link-id>").argument("<space>").argument("<linkId>").option("--expires <duration>", "New expiry duration (e.g. 30d)").option("--users <emails>", "Comma-separated user emails or IDs").option("--groups <ids>", "Comma-separated group IDs").action(
|
|
725
|
+
async (spaceName, linkId, opts) => {
|
|
726
|
+
const client = getClient();
|
|
727
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
728
|
+
const apiOpts = {};
|
|
729
|
+
if (opts.expires) apiOpts.expiresAt = parseExpires(opts.expires);
|
|
730
|
+
if (opts.users)
|
|
731
|
+
apiOpts.userIds = opts.users.split(",").map((s2) => s2.trim());
|
|
732
|
+
if (opts.groups)
|
|
733
|
+
apiOpts.groupIds = opts.groups.split(",").map((s2) => s2.trim());
|
|
734
|
+
await client.updateShareLink(s.id, linkId, apiOpts);
|
|
735
|
+
console.log(`Updated share link: ${linkId}`);
|
|
736
|
+
}
|
|
737
|
+
);
|
|
485
738
|
share.command("cat").description("Read shared file \u2014 docz share cat <token-or-url>").argument("<token>", "Share token or full URL").option("--raw", "Output raw content only").action(async (tokenArg, opts) => {
|
|
486
739
|
const client = getClient();
|
|
487
740
|
const token = extractShareToken(tokenArg);
|
|
@@ -489,7 +742,9 @@ function registerCommands(program) {
|
|
|
489
742
|
try {
|
|
490
743
|
const info = await client.getSharedFileInfo(token);
|
|
491
744
|
console.log(`File: ${info.file_path} (${info.space_name})`);
|
|
492
|
-
console.log(
|
|
745
|
+
console.log(
|
|
746
|
+
`Shared by: ${info.created_by_name} | Expires: ${info.expires_at ?? "never"}`
|
|
747
|
+
);
|
|
493
748
|
console.log("---");
|
|
494
749
|
} catch (err) {
|
|
495
750
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -512,7 +767,7 @@ function registerCommands(program) {
|
|
|
512
767
|
});
|
|
513
768
|
share.command("rm").description("Delete share link \u2014 docz share rm <space> <link-id>").argument("<space>").argument("<linkId>").action(async (spaceName, linkId) => {
|
|
514
769
|
const client = getClient();
|
|
515
|
-
const s = await client
|
|
770
|
+
const s = await resolveSpaceArg(client, spaceName);
|
|
516
771
|
await client.deleteShareLink(s.id, linkId);
|
|
517
772
|
console.log(`Deleted share link: ${linkId}`);
|
|
518
773
|
});
|
|
@@ -528,19 +783,20 @@ function registerCommands(program) {
|
|
|
528
783
|
const ref = await client.getFileRef(spaceId, path);
|
|
529
784
|
console.log(ref.url);
|
|
530
785
|
});
|
|
531
|
-
program.command("diff").description(
|
|
786
|
+
program.command("diff").description(
|
|
787
|
+
"Show changes \u2014 docz diff <space>[:<path>] <commit> [<from>] or <url> <commit> [<from>]"
|
|
788
|
+
).argument("<target>", "space or space:path or short URL").argument("<to>", "Commit hash").argument("[from]", "From commit hash (default: to^)").action(async (target, to, from) => {
|
|
532
789
|
const client = getClient();
|
|
533
|
-
const {
|
|
534
|
-
const s = await client.resolveSpace(space);
|
|
790
|
+
const { spaceId, path } = await resolveTarget(client, [target]);
|
|
535
791
|
if (path) {
|
|
536
|
-
const result = await client.diffFile(
|
|
792
|
+
const result = await client.diffFile(spaceId, path, to, from);
|
|
537
793
|
if (result.diff) {
|
|
538
794
|
process.stdout.write(result.diff);
|
|
539
795
|
} else {
|
|
540
796
|
console.log("No changes.");
|
|
541
797
|
}
|
|
542
798
|
} else {
|
|
543
|
-
const result = await client.diffSummary(
|
|
799
|
+
const result = await client.diffSummary(spaceId, to, from);
|
|
544
800
|
if (result.files.length === 0) {
|
|
545
801
|
console.log("No changes.");
|
|
546
802
|
return;
|
|
@@ -553,10 +809,11 @@ function registerCommands(program) {
|
|
|
553
809
|
}
|
|
554
810
|
|
|
555
811
|
export {
|
|
812
|
+
ConflictError,
|
|
556
813
|
DocSyncClient,
|
|
557
814
|
getBaseUrl,
|
|
558
815
|
getToken,
|
|
559
816
|
parseExpires,
|
|
560
817
|
registerCommands
|
|
561
818
|
};
|
|
562
|
-
//# sourceMappingURL=chunk-
|
|
819
|
+
//# sourceMappingURL=chunk-BHXHIHWH.js.map
|