@bifos/dooray-cli 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +113 -40
- package/package.json +1 -1
- package/skills/dooray-cli/SKILL.md +7 -1
- package/skills/dooray-cli/references/post.md +8 -2
package/README.md
CHANGED
|
@@ -110,7 +110,8 @@ dooray post comment file upload <project> <number> <comment-id> <path>
|
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
이미지 확장자는 이미지 마크다운으로, 그 외 파일은 일반 링크로 댓글 본문에 추가한다.
|
|
113
|
-
`comment file list`는 웹 UI
|
|
113
|
+
`comment file list`는 웹 UI 첨부와 CLI 업로드 파일을 함께 보여주며 `출처` 열로 구분한다.
|
|
114
|
+
CLI로 올린 파일은 댓글의 첨부 카드가 아니라 본문 링크로 표시된다.
|
|
114
115
|
|
|
115
116
|
### 삭제 명령의 확인
|
|
116
117
|
|
package/dist/index.js
CHANGED
|
@@ -385,7 +385,7 @@ var import_node_path3 = __toESM(require("path"));
|
|
|
385
385
|
var import_node_os3 = require("os");
|
|
386
386
|
|
|
387
387
|
// src/version.ts
|
|
388
|
-
var CLI_VERSION = true ? "0.
|
|
388
|
+
var CLI_VERSION = true ? "0.17.0" : "0.0.0-dev";
|
|
389
389
|
|
|
390
390
|
// src/skill/context.ts
|
|
391
391
|
function resolveSkillDataRoot(homeDir, xdgDataHome = process.env.XDG_DATA_HOME) {
|
|
@@ -3151,14 +3151,21 @@ async function readStdin() {
|
|
|
3151
3151
|
|
|
3152
3152
|
// src/utils/attachment-check.ts
|
|
3153
3153
|
var import_node_readline = __toESM(require("readline"));
|
|
3154
|
-
function
|
|
3155
|
-
const
|
|
3156
|
-
const
|
|
3154
|
+
function extractAttachmentReferences(body) {
|
|
3155
|
+
const references = [];
|
|
3156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3157
|
+
const re = /!?\[([^\]]*)\]\(\/files\/([^\s)?#]+)/g;
|
|
3157
3158
|
let m;
|
|
3158
3159
|
while ((m = re.exec(body)) !== null) {
|
|
3159
|
-
|
|
3160
|
+
const id = m[2];
|
|
3161
|
+
if (seen.has(id)) continue;
|
|
3162
|
+
seen.add(id);
|
|
3163
|
+
references.push({ id, label: m[1] });
|
|
3160
3164
|
}
|
|
3161
|
-
return
|
|
3165
|
+
return references;
|
|
3166
|
+
}
|
|
3167
|
+
function extractAttachmentFileIds(body) {
|
|
3168
|
+
return new Set(extractAttachmentReferences(body).map((reference) => reference.id));
|
|
3162
3169
|
}
|
|
3163
3170
|
function findDroppedAttachments(oldBody, newBody, attachments) {
|
|
3164
3171
|
const oldIds = extractAttachmentFileIds(oldBody);
|
|
@@ -4117,6 +4124,14 @@ var commentDeleteCommand = new import_commander23.Command("delete").description(
|
|
|
4117
4124
|
// src/commands/post/comment/get.ts
|
|
4118
4125
|
var import_commander24 = require("commander");
|
|
4119
4126
|
|
|
4127
|
+
// src/utils/format-size.ts
|
|
4128
|
+
function formatSize(bytes) {
|
|
4129
|
+
if (bytes == null) return "-";
|
|
4130
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
4131
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
4132
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
4133
|
+
}
|
|
4134
|
+
|
|
4120
4135
|
// src/formatters/comment.ts
|
|
4121
4136
|
function formatCommentDetail(comment, opts) {
|
|
4122
4137
|
if (opts.json) {
|
|
@@ -4140,7 +4155,7 @@ function formatCommentDetail(comment, opts) {
|
|
|
4140
4155
|
process.stdout.write("\n[Attachments]\n");
|
|
4141
4156
|
printTable(
|
|
4142
4157
|
["Name", "Size", "ID"],
|
|
4143
|
-
comment.files.map((f) => [f.name,
|
|
4158
|
+
comment.files.map((f) => [f.name ?? "-", formatSize(f.size), f.id])
|
|
4144
4159
|
);
|
|
4145
4160
|
}
|
|
4146
4161
|
}
|
|
@@ -4296,13 +4311,54 @@ async function resolveCommentFileInput(client, args) {
|
|
|
4296
4311
|
return { projectId, postId, commentId, secondary };
|
|
4297
4312
|
}
|
|
4298
4313
|
|
|
4314
|
+
// src/utils/comment-file-merge.ts
|
|
4315
|
+
function labelOrNull(label) {
|
|
4316
|
+
if (label === void 0 || label.length === 0) return null;
|
|
4317
|
+
return label;
|
|
4318
|
+
}
|
|
4319
|
+
function mergeCommentFiles(input2) {
|
|
4320
|
+
const postFilesById = new Map(input2.postFiles.map((file) => [file.id, file]));
|
|
4321
|
+
const bodyRefsById = /* @__PURE__ */ new Map();
|
|
4322
|
+
for (const reference of input2.bodyRefs) {
|
|
4323
|
+
if (!bodyRefsById.has(reference.id)) bodyRefsById.set(reference.id, reference);
|
|
4324
|
+
}
|
|
4325
|
+
const merged = [];
|
|
4326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4327
|
+
for (const commentFile of input2.commentFiles) {
|
|
4328
|
+
if (seen.has(commentFile.id)) continue;
|
|
4329
|
+
seen.add(commentFile.id);
|
|
4330
|
+
const postFile = postFilesById.get(commentFile.id);
|
|
4331
|
+
const bodyRef = bodyRefsById.get(commentFile.id);
|
|
4332
|
+
merged.push({
|
|
4333
|
+
id: commentFile.id,
|
|
4334
|
+
name: postFile?.name ?? commentFile.name ?? labelOrNull(bodyRef?.label),
|
|
4335
|
+
size: postFile?.size ?? null,
|
|
4336
|
+
mimeType: postFile?.mimeType ?? null,
|
|
4337
|
+
source: bodyRef ? "both" : "attachment"
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
for (const bodyRef of input2.bodyRefs) {
|
|
4341
|
+
if (seen.has(bodyRef.id)) continue;
|
|
4342
|
+
seen.add(bodyRef.id);
|
|
4343
|
+
const postFile = postFilesById.get(bodyRef.id);
|
|
4344
|
+
merged.push({
|
|
4345
|
+
id: bodyRef.id,
|
|
4346
|
+
name: postFile?.name ?? labelOrNull(bodyRef.label),
|
|
4347
|
+
size: postFile?.size ?? null,
|
|
4348
|
+
mimeType: postFile?.mimeType ?? null,
|
|
4349
|
+
source: "body-link"
|
|
4350
|
+
});
|
|
4351
|
+
}
|
|
4352
|
+
return merged;
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4299
4355
|
// src/commands/post/comment/file/list.ts
|
|
4300
|
-
function
|
|
4301
|
-
if (
|
|
4302
|
-
if (
|
|
4303
|
-
return
|
|
4356
|
+
function formatSource(source) {
|
|
4357
|
+
if (source === "attachment") return "\uCCA8\uBD80";
|
|
4358
|
+
if (source === "body-link") return "\uBCF8\uBB38 \uB9C1\uD06C";
|
|
4359
|
+
return "\uB458 \uB2E4";
|
|
4304
4360
|
}
|
|
4305
|
-
var listCommentFileCommand = new import_commander25.Command("list").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 (positional \uBAA8\uB4DC)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
|
|
4361
|
+
var listCommentFileCommand = new import_commander25.Command("list").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C\uACFC \uBCF8\uBB38 \uD30C\uC77C \uB9C1\uD06C \uBAA9\uB85D \uD1B5\uD569 \uC870\uD68C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 (positional \uBAA8\uB4DC)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
|
|
4306
4362
|
const globalOpts = listCommentFileCommand.optsWithGlobals();
|
|
4307
4363
|
const config = await getConfigOrThrow();
|
|
4308
4364
|
const client = new DoorayApiClient(config.apiKey, config.baseUrl);
|
|
@@ -4316,23 +4372,50 @@ var listCommentFileCommand = new import_commander25.Command("list").description(
|
|
|
4316
4372
|
requireSecondary: false
|
|
4317
4373
|
});
|
|
4318
4374
|
startSpinner("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
if (
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4375
|
+
try {
|
|
4376
|
+
const res = await client.getPostComment(projectId, postId, commentId);
|
|
4377
|
+
const commentFiles = res.result.files ?? [];
|
|
4378
|
+
const bodyRefs = extractAttachmentReferences(res.result.body.content);
|
|
4379
|
+
if (commentFiles.length === 0 && bodyRefs.length === 0) {
|
|
4380
|
+
stopSpinner(true, "\uCCA8\uBD80 \uD30C\uC77C 0\uAC1C");
|
|
4381
|
+
if (globalOpts.json) {
|
|
4382
|
+
process.stdout.write("[]\n");
|
|
4383
|
+
} else if (!globalOpts.quiet) {
|
|
4384
|
+
process.stdout.write("\uCCA8\uBD80 \uC5C6\uC74C\n");
|
|
4385
|
+
}
|
|
4386
|
+
return;
|
|
4327
4387
|
}
|
|
4328
|
-
|
|
4388
|
+
let postFiles = [];
|
|
4389
|
+
let metadataLookupError = null;
|
|
4390
|
+
try {
|
|
4391
|
+
const postFilesRes = await client.getPostFiles(projectId, postId);
|
|
4392
|
+
postFiles = postFilesRes.result;
|
|
4393
|
+
} catch (error) {
|
|
4394
|
+
metadataLookupError = error instanceof Error ? error.message : String(error);
|
|
4395
|
+
}
|
|
4396
|
+
const merged = mergeCommentFiles({ commentFiles, bodyRefs, postFiles });
|
|
4397
|
+
stopSpinner(true, `\uCCA8\uBD80 \uD30C\uC77C ${merged.length}\uAC1C`);
|
|
4398
|
+
if (metadataLookupError !== null) {
|
|
4399
|
+
process.stderr.write(
|
|
4400
|
+
`\u26A0 \uC5C5\uBB34 \uCCA8\uBD80 \uC774\uB984\xB7\uD06C\uAE30 \uBCF4\uAC15 \uC2E4\uD328 (${metadataLookupError}) \u2014 \uC77C\uBD80 \uD56D\uBAA9\uC758 \uD30C\uC77C\uBA85\xB7\uD06C\uAE30\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
|
|
4401
|
+
`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
output(globalOpts, {
|
|
4405
|
+
headers: ["\uD30C\uC77C\uBA85", "\uD06C\uAE30", "\uCD9C\uCC98", "ID"],
|
|
4406
|
+
rows: merged.map((file) => [
|
|
4407
|
+
file.name == null ? "-" : sanitizeFileName(file.name),
|
|
4408
|
+
formatSize(file.size),
|
|
4409
|
+
formatSource(file.source),
|
|
4410
|
+
file.id
|
|
4411
|
+
]),
|
|
4412
|
+
raw: merged,
|
|
4413
|
+
ids: merged.map((file) => file.id)
|
|
4414
|
+
});
|
|
4415
|
+
} catch (error) {
|
|
4416
|
+
stopSpinner(false);
|
|
4417
|
+
throw error;
|
|
4329
4418
|
}
|
|
4330
|
-
output(globalOpts, {
|
|
4331
|
-
headers: ["\uD30C\uC77C\uBA85", "\uD06C\uAE30", "ID"],
|
|
4332
|
-
rows: files.map((f) => [f.name, formatSize(f.size), f.id]),
|
|
4333
|
-
raw: files,
|
|
4334
|
-
ids: files.map((f) => f.id)
|
|
4335
|
-
});
|
|
4336
4419
|
});
|
|
4337
4420
|
|
|
4338
4421
|
// src/commands/post/comment/file/upload.ts
|
|
@@ -4359,7 +4442,7 @@ function removeFileReference(body, fileId) {
|
|
|
4359
4442
|
}
|
|
4360
4443
|
|
|
4361
4444
|
// src/commands/post/comment/file/upload.ts
|
|
4362
|
-
var uploadCommentFileCommand = new import_commander26.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (\
|
|
4445
|
+
var uploadCommentFileCommand = new import_commander26.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (\uCCA8\uBD80 \uCE74\uB4DC\uAC00 \uC544\uB2CC \uBCF8\uBB38 \uB9C1\uD06C\uB85C \uD45C\uC2DC)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C \uACBD\uB85C (positional \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, arg4, opts) => {
|
|
4363
4446
|
const globalOpts = uploadCommentFileCommand.optsWithGlobals();
|
|
4364
4447
|
const config = await getConfigOrThrow();
|
|
4365
4448
|
const client = new DoorayApiClient(config.apiKey, config.baseUrl);
|
|
@@ -4502,11 +4585,6 @@ var commentFileCommand = new import_commander29.Command("file").description("\uB
|
|
|
4502
4585
|
|
|
4503
4586
|
// src/commands/post/file/list.ts
|
|
4504
4587
|
var import_commander30 = require("commander");
|
|
4505
|
-
function formatSize2(bytes) {
|
|
4506
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
4507
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
4508
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
4509
|
-
}
|
|
4510
4588
|
var fileListCommand = new import_commander30.Command("list").description("\uC5C5\uBB34 \uCCA8\uBD80\uD30C\uC77C \uBAA9\uB85D \uC870\uD68C").argument("[project]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (\uB610\uB294 \uCCAB \uC778\uC790\uC5D0 Dooray URL)").argument("[post-number]", "\uC5C5\uBB34 \uBC88\uD638 (project\uC640 \uD568\uAED8 \uC0AC\uC6A9)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").action(async (project, postNumberStr, opts) => {
|
|
4511
4589
|
const globalOpts = fileListCommand.optsWithGlobals();
|
|
4512
4590
|
const config = await getConfigOrThrow();
|
|
@@ -4525,7 +4603,7 @@ var fileListCommand = new import_commander30.Command("list").description("\uC5C5
|
|
|
4525
4603
|
rows: res.result.map((f) => [
|
|
4526
4604
|
f.id,
|
|
4527
4605
|
f.name,
|
|
4528
|
-
|
|
4606
|
+
formatSize(f.size),
|
|
4529
4607
|
f.mimeType,
|
|
4530
4608
|
f.createdAt
|
|
4531
4609
|
]),
|
|
@@ -5190,11 +5268,6 @@ var import_commander47 = require("commander");
|
|
|
5190
5268
|
|
|
5191
5269
|
// src/commands/wiki/page-file/list.ts
|
|
5192
5270
|
var import_commander42 = require("commander");
|
|
5193
|
-
function formatSize3(bytes) {
|
|
5194
|
-
if (bytes < 1024) return `${bytes}B`;
|
|
5195
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
5196
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
5197
|
-
}
|
|
5198
5271
|
var wikiPageFileListCommand = new import_commander42.Command("list").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uBAA9\uB85D \uC870\uD68C (general + inline image)").argument("[project]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (\uB610\uB294 \uCCAB \uC778\uC790\uC5D0 Dooray Wiki URL)").argument("[page-id]", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (project\uC640 \uD568\uAED8 \uC0AC\uC6A9)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (--project \uB3D9\uBC18 \uD544\uC694)").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").action(async (project, pageIdArg, opts) => {
|
|
5199
5272
|
const globalOpts = wikiPageFileListCommand.optsWithGlobals();
|
|
5200
5273
|
const config = await getConfigOrThrow();
|
|
@@ -5215,7 +5288,7 @@ var wikiPageFileListCommand = new import_commander42.Command("list").description
|
|
|
5215
5288
|
stopSpinner(true, `\uCCA8\uBD80\uD30C\uC77C ${merged.length}\uAC1C (general ${files.length} + inline ${images.length})`);
|
|
5216
5289
|
output(globalOpts, {
|
|
5217
5290
|
headers: ["ID", "Type", "\uD30C\uC77C\uBA85", "\uD06C\uAE30"],
|
|
5218
|
-
rows: merged.map((f) => [f.id, f.type, f.name,
|
|
5291
|
+
rows: merged.map((f) => [f.id, f.type, f.name, formatSize(f.size)]),
|
|
5219
5292
|
raw: merged,
|
|
5220
5293
|
ids: merged.map((f) => f.id)
|
|
5221
5294
|
});
|
package/package.json
CHANGED
|
@@ -159,7 +159,13 @@ NHN Dooray REST API 를 래핑한 CLI 다. 이 파일은 라우터이므로, 작
|
|
|
159
159
|
| 댓글 첨부 삭제 | `dooray post comment file delete <project> <number> <comment-id> <file-id>` — 확인 있음, `-y`/`--yes`로 생략 |
|
|
160
160
|
|
|
161
161
|
- 댓글 파일 업로드는 이미지 확장자면 이미지 마크다운을, 그 외에는 일반 링크를 만든다.
|
|
162
|
-
- `comment file list
|
|
162
|
+
- `comment file list`의 `출처`는 다음과 같다.
|
|
163
|
+
- `attachment` (`첨부`): 웹 UI 첨부
|
|
164
|
+
- `body-link` (`본문 링크`): CLI 업로드 링크
|
|
165
|
+
- `both` (`둘 다`): 양쪽에 있는 파일
|
|
166
|
+
- `--json` 항목은 `{ id, name, size, mimeType, source }` 형식이다.
|
|
167
|
+
`source`는 위 값 중 하나다.
|
|
168
|
+
- 메타데이터를 채우지 못하면 `name`, `size`, `mimeType`은 `null`이다.
|
|
163
169
|
|
|
164
170
|
## 위키
|
|
165
171
|
|
|
@@ -104,8 +104,14 @@ dooray post comment get <project> <number> <comment-id> --json | jq -r '.body.co
|
|
|
104
104
|
|
|
105
105
|
첨부를 정말 떼려는 것이면 `--no-confirm` 으로 진행한다.
|
|
106
106
|
|
|
107
|
-
`comment file list`는
|
|
108
|
-
|
|
107
|
+
`comment file list`는 웹 UI 첨부와 CLI 업로드 링크를 함께 보여주고 `출처` 열로 구분한다.
|
|
108
|
+
|
|
109
|
+
- `attachment`: 웹 UI 첨부
|
|
110
|
+
- `body-link`: CLI 업로드 링크
|
|
111
|
+
- `both`: 양쪽에 있는 파일
|
|
112
|
+
|
|
113
|
+
`--json` 항목은 `{ id, name, size, mimeType, source }` 형식이다.
|
|
114
|
+
메타데이터를 채우지 못하면 `name`, `size`, `mimeType`은 `null`이다.
|
|
109
115
|
|
|
110
116
|
## 이름이 겹칠 때
|
|
111
117
|
|