@bifos/dooray-cli 0.6.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/index.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander46 = require("commander");
27
+ var import_commander47 = require("commander");
28
28
  var import_chalk7 = __toESM(require("chalk"));
29
29
 
30
30
  // src/commands/config.ts
@@ -414,7 +414,7 @@ var DoorayApiClient = class {
414
414
  try {
415
415
  return await this.api.get("common/v1/members/me").json();
416
416
  } catch (e) {
417
- return toDoorayCliError(e);
417
+ return await toDoorayCliError(e);
418
418
  }
419
419
  }
420
420
  // ─── Projects ───────────────────────────────────────
@@ -431,7 +431,7 @@ var DoorayApiClient = class {
431
431
  }
432
432
  }).json();
433
433
  } catch (e) {
434
- return toDoorayCliError(e);
434
+ return await toDoorayCliError(e);
435
435
  }
436
436
  }
437
437
  // ─── Posts ──────────────────────────────────────────
@@ -458,42 +458,42 @@ var DoorayApiClient = class {
458
458
  }
459
459
  }).json();
460
460
  } catch (e) {
461
- return toDoorayCliError(e);
461
+ return await toDoorayCliError(e);
462
462
  }
463
463
  }
464
464
  async getPost(projectId, postId) {
465
465
  try {
466
466
  return await this.api.get(`project/v1/projects/${projectId}/posts/${postId}`).json();
467
467
  } catch (e) {
468
- return toDoorayCliError(e);
468
+ return await toDoorayCliError(e);
469
469
  }
470
470
  }
471
471
  async getPostStandalone(postId) {
472
472
  try {
473
473
  return await this.api.get(`project/v1/posts/${postId}`).json();
474
474
  } catch (e) {
475
- return toDoorayCliError(e);
475
+ return await toDoorayCliError(e);
476
476
  }
477
477
  }
478
478
  async createPost(projectId, body) {
479
479
  try {
480
480
  return await this.api.post(`project/v1/projects/${projectId}/posts`, { json: body }).json();
481
481
  } catch (e) {
482
- return toDoorayCliError(e);
482
+ return await toDoorayCliError(e);
483
483
  }
484
484
  }
485
485
  async updatePost(projectId, postId, body) {
486
486
  try {
487
487
  return await this.api.put(`project/v1/projects/${projectId}/posts/${postId}`, { json: body }).json();
488
488
  } catch (e) {
489
- return toDoorayCliError(e);
489
+ return await toDoorayCliError(e);
490
490
  }
491
491
  }
492
492
  async setPostDone(projectId, postId) {
493
493
  try {
494
494
  return await this.api.post(`project/v1/projects/${projectId}/posts/${postId}/set-done`).json();
495
495
  } catch (e) {
496
- return toDoorayCliError(e);
496
+ return await toDoorayCliError(e);
497
497
  }
498
498
  }
499
499
  async setPostWorkflow(projectId, postId, workflowId) {
@@ -502,7 +502,7 @@ var DoorayApiClient = class {
502
502
  json: { workflowId }
503
503
  }).json();
504
504
  } catch (e) {
505
- return toDoorayCliError(e);
505
+ return await toDoorayCliError(e);
506
506
  }
507
507
  }
508
508
  async setPostParent(projectId, postId, parentPostId) {
@@ -511,7 +511,7 @@ var DoorayApiClient = class {
511
511
  json: { parentPostId }
512
512
  }).json();
513
513
  } catch (e) {
514
- return toDoorayCliError(e);
514
+ return await toDoorayCliError(e);
515
515
  }
516
516
  }
517
517
  // ─── Post Comments ──────────────────────────────────
@@ -525,35 +525,35 @@ var DoorayApiClient = class {
525
525
  }
526
526
  }).json();
527
527
  } catch (e) {
528
- return toDoorayCliError(e);
528
+ return await toDoorayCliError(e);
529
529
  }
530
530
  }
531
531
  async getPostComment(projectId, postId, logId) {
532
532
  try {
533
533
  return await this.api.get(`project/v1/projects/${projectId}/posts/${postId}/logs/${logId}`).json();
534
534
  } catch (e) {
535
- return toDoorayCliError(e);
535
+ return await toDoorayCliError(e);
536
536
  }
537
537
  }
538
538
  async createPostComment(projectId, postId, body) {
539
539
  try {
540
540
  return await this.api.post(`project/v1/projects/${projectId}/posts/${postId}/logs`, { json: body }).json();
541
541
  } catch (e) {
542
- return toDoorayCliError(e);
542
+ return await toDoorayCliError(e);
543
543
  }
544
544
  }
545
545
  async updatePostComment(projectId, postId, logId, body) {
546
546
  try {
547
547
  return await this.api.put(`project/v1/projects/${projectId}/posts/${postId}/logs/${logId}`, { json: body }).json();
548
548
  } catch (e) {
549
- return toDoorayCliError(e);
549
+ return await toDoorayCliError(e);
550
550
  }
551
551
  }
552
552
  async deletePostComment(projectId, postId, logId) {
553
553
  try {
554
554
  return await this.api.delete(`project/v1/projects/${projectId}/posts/${postId}/logs/${logId}`).json();
555
555
  } catch (e) {
556
- return toDoorayCliError(e);
556
+ return await toDoorayCliError(e);
557
557
  }
558
558
  }
559
559
  // ─── Members ────────────────────────────────────────
@@ -567,14 +567,14 @@ var DoorayApiClient = class {
567
567
  }
568
568
  }).json();
569
569
  } catch (e) {
570
- return toDoorayCliError(e);
570
+ return await toDoorayCliError(e);
571
571
  }
572
572
  }
573
573
  async getMemberDetail(memberId) {
574
574
  try {
575
575
  return await this.api.get(`common/v1/members/${memberId}`).json();
576
576
  } catch (e) {
577
- return toDoorayCliError(e);
577
+ return await toDoorayCliError(e);
578
578
  }
579
579
  }
580
580
  async searchMembers(params) {
@@ -591,7 +591,7 @@ var DoorayApiClient = class {
591
591
  }
592
592
  }).json();
593
593
  } catch (e) {
594
- return toDoorayCliError(e);
594
+ return await toDoorayCliError(e);
595
595
  }
596
596
  }
597
597
  // ─── Workflows ──────────────────────────────────────
@@ -599,7 +599,7 @@ var DoorayApiClient = class {
599
599
  try {
600
600
  return await this.api.get(`project/v1/projects/${projectId}/workflows`).json();
601
601
  } catch (e) {
602
- return toDoorayCliError(e);
602
+ return await toDoorayCliError(e);
603
603
  }
604
604
  }
605
605
  // ─── Tags ───────────────────────────────────────────
@@ -612,7 +612,7 @@ var DoorayApiClient = class {
612
612
  }
613
613
  }).json();
614
614
  } catch (e) {
615
- return toDoorayCliError(e);
615
+ return await toDoorayCliError(e);
616
616
  }
617
617
  }
618
618
  // ─── Member Groups ──────────────────────────────────
@@ -625,7 +625,7 @@ var DoorayApiClient = class {
625
625
  }
626
626
  }).json();
627
627
  } catch (e) {
628
- return toDoorayCliError(e);
628
+ return await toDoorayCliError(e);
629
629
  }
630
630
  }
631
631
  // ─── Milestones ─────────────────────────────────────
@@ -638,7 +638,7 @@ var DoorayApiClient = class {
638
638
  }
639
639
  }).json();
640
640
  } catch (e) {
641
- return toDoorayCliError(e);
641
+ return await toDoorayCliError(e);
642
642
  }
643
643
  }
644
644
  // ─── Wiki ───────────────────────────────────────────
@@ -651,7 +651,7 @@ var DoorayApiClient = class {
651
651
  }
652
652
  }).json();
653
653
  } catch (e) {
654
- return toDoorayCliError(e);
654
+ return await toDoorayCliError(e);
655
655
  }
656
656
  }
657
657
  async getWikiPages(wikiId, parentPageId) {
@@ -662,42 +662,42 @@ var DoorayApiClient = class {
662
662
  }
663
663
  }).json();
664
664
  } catch (e) {
665
- return toDoorayCliError(e);
665
+ return await toDoorayCliError(e);
666
666
  }
667
667
  }
668
668
  async getWikiPage(wikiId, pageId) {
669
669
  try {
670
670
  return await this.api.get(`wiki/v1/wikis/${wikiId}/pages/${pageId}`).json();
671
671
  } catch (e) {
672
- return toDoorayCliError(e);
672
+ return await toDoorayCliError(e);
673
673
  }
674
674
  }
675
675
  async createWikiPage(wikiId, body) {
676
676
  try {
677
677
  return await this.api.post(`wiki/v1/wikis/${wikiId}/pages`, { json: body }).json();
678
678
  } catch (e) {
679
- return toDoorayCliError(e);
679
+ return await toDoorayCliError(e);
680
680
  }
681
681
  }
682
682
  async updateWikiPage(wikiId, pageId, body) {
683
683
  try {
684
684
  return await this.api.put(`wiki/v1/wikis/${wikiId}/pages/${pageId}`, { json: body }).json();
685
685
  } catch (e) {
686
- return toDoorayCliError(e);
686
+ return await toDoorayCliError(e);
687
687
  }
688
688
  }
689
689
  async updateWikiPageTitle(wikiId, pageId, body) {
690
690
  try {
691
691
  return await this.api.put(`wiki/v1/wikis/${wikiId}/pages/${pageId}/title`, { json: body }).json();
692
692
  } catch (e) {
693
- return toDoorayCliError(e);
693
+ return await toDoorayCliError(e);
694
694
  }
695
695
  }
696
696
  async updateWikiPageContent(wikiId, pageId, body) {
697
697
  try {
698
698
  return await this.api.put(`wiki/v1/wikis/${wikiId}/pages/${pageId}/content`, { json: body }).json();
699
699
  } catch (e) {
700
- return toDoorayCliError(e);
700
+ return await toDoorayCliError(e);
701
701
  }
702
702
  }
703
703
  // ─── Post Files ─────────────────────────────────────
@@ -705,7 +705,7 @@ var DoorayApiClient = class {
705
705
  try {
706
706
  return await this.api.get(`project/v1/projects/${projectId}/posts/${postId}/files`).json();
707
707
  } catch (e) {
708
- return toDoorayCliError(e);
708
+ return await toDoorayCliError(e);
709
709
  }
710
710
  }
711
711
  async getPostFileMeta(projectId, postId, fileId) {
@@ -714,7 +714,7 @@ var DoorayApiClient = class {
714
714
  searchParams: { media: "meta" }
715
715
  }).json();
716
716
  } catch (e) {
717
- return toDoorayCliError(e);
717
+ return await toDoorayCliError(e);
718
718
  }
719
719
  }
720
720
  async downloadPostFile(projectId, postId, fileId) {
@@ -744,7 +744,7 @@ var DoorayApiClient = class {
744
744
  return { buffer, fileName };
745
745
  } catch (e) {
746
746
  if (e instanceof DoorayCliError) throw e;
747
- return toDoorayCliError(e);
747
+ return await toDoorayCliError(e);
748
748
  }
749
749
  }
750
750
  async uploadPostFile(projectId, postId, filePath) {
@@ -781,14 +781,14 @@ var DoorayApiClient = class {
781
781
  return await res.json();
782
782
  } catch (e) {
783
783
  if (e instanceof DoorayCliError) throw e;
784
- return toDoorayCliError(e);
784
+ return await toDoorayCliError(e);
785
785
  }
786
786
  }
787
787
  async deletePostFile(projectId, postId, fileId) {
788
788
  try {
789
789
  return await this.api.delete(`project/v1/projects/${projectId}/posts/${postId}/files/${fileId}`).json();
790
790
  } catch (e) {
791
- return toDoorayCliError(e);
791
+ return await toDoorayCliError(e);
792
792
  }
793
793
  }
794
794
  };
@@ -1743,6 +1743,80 @@ var postGetCommand = new import_commander12.Command("get").description("\uC5C5\u
1743
1743
  // src/commands/post/edit.ts
1744
1744
  var import_commander13 = require("commander");
1745
1745
 
1746
+ // src/utils/mention.ts
1747
+ function buildMemberMention(m, me) {
1748
+ const title = m.memberId === me.id ? "me" : "member";
1749
+ return `[@${m.name}](dooray://${me.orgId}/members/${m.memberId} "${title}")`;
1750
+ }
1751
+ function buildGroupMention(g, me) {
1752
+ return `[@${g.projectCode}/${g.code}](dooray://${me.orgId}/member-groups/${g.groupId})`;
1753
+ }
1754
+ function prependMentions(body, members, groups, me) {
1755
+ const parts = [];
1756
+ for (const m of members) parts.push(buildMemberMention(m, me));
1757
+ for (const g of groups) parts.push(buildGroupMention(g, me));
1758
+ if (parts.length === 0) return body;
1759
+ return parts.join(" ") + " " + body;
1760
+ }
1761
+
1762
+ // src/utils/task-link.ts
1763
+ function escapeLinkText(text) {
1764
+ return text.replace(/&/g, "&").replace(/\[/g, "[").replace(/\]/g, "]").replace(/—/g, "—");
1765
+ }
1766
+ function buildTaskLink(t, me) {
1767
+ const text = `${t.projectCode}/${t.number} ${escapeLinkText(t.subject)}`;
1768
+ const url = `dooray://${me.orgId}/tasks/${t.postId}`;
1769
+ if (t.workflowClass) {
1770
+ const safeClass = t.workflowClass.replace(/"/g, """);
1771
+ return `[${text}](${url} "${safeClass}")`;
1772
+ }
1773
+ return `[${text}](${url})`;
1774
+ }
1775
+ function parseLinkRef(ref) {
1776
+ if (/^[0-9]{15,}$/.test(ref)) return { idOpt: ref };
1777
+ if (ref.includes("/")) {
1778
+ const [p, n] = ref.split("/");
1779
+ if (!p || !n) {
1780
+ throw new DoorayCliError(
1781
+ `--link-task \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: "${ref}". <project>/<number> \uB610\uB294 postId\uB97C \uC785\uB825\uD558\uC138\uC694.`,
1782
+ EXIT_PARAM_ERROR
1783
+ );
1784
+ }
1785
+ return { projectArg: p, postNumberArg: n };
1786
+ }
1787
+ throw new DoorayCliError(
1788
+ `--link-task \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: "${ref}". <project>/<number> \uB610\uB294 postId\uB97C \uC785\uB825\uD558\uC138\uC694.`,
1789
+ EXIT_PARAM_ERROR
1790
+ );
1791
+ }
1792
+ function appendTaskLinks(body, links, me) {
1793
+ if (links.length === 0) return body;
1794
+ const rendered = links.map((l) => buildTaskLink(l, me)).join("\n");
1795
+ if (!body) return rendered;
1796
+ return body.replace(/\n*$/, "") + "\n\n" + rendered;
1797
+ }
1798
+
1799
+ // src/resolvers/task-link.ts
1800
+ async function resolveTaskLinks(client, linkInputs) {
1801
+ return Promise.all(
1802
+ linkInputs.map(async (ref) => {
1803
+ const { projectArg, postNumberArg, idOpt } = parseLinkRef(ref);
1804
+ const { projectId, postId, projectCode, postNumber } = await resolvePostInput(
1805
+ client,
1806
+ { projectArg, postNumberArg, idOpt }
1807
+ );
1808
+ const detail = await client.getPost(projectId, postId);
1809
+ return {
1810
+ projectCode,
1811
+ number: postNumber,
1812
+ postId,
1813
+ subject: detail.result.subject,
1814
+ workflowClass: detail.result.workflowClass
1815
+ };
1816
+ })
1817
+ );
1818
+ }
1819
+
1746
1820
  // src/editor/index.ts
1747
1821
  var import_node_child_process = require("child_process");
1748
1822
  var import_promises6 = __toESM(require("fs/promises"));
@@ -1789,11 +1863,11 @@ function openInEditor(content) {
1789
1863
  }
1790
1864
  });
1791
1865
  }
1792
- function memberIdToEmail(memberId, members) {
1866
+ function memberIdToName(memberId, members) {
1793
1867
  const member = members.find(
1794
1868
  (m) => m.organizationMemberId === memberId
1795
1869
  );
1796
- return member?.emailAddress ?? memberId;
1870
+ return member?.name ?? memberId;
1797
1871
  }
1798
1872
  function serializePostFrontmatter(post, members) {
1799
1873
  const frontmatter = {
@@ -1801,12 +1875,12 @@ function serializePostFrontmatter(post, members) {
1801
1875
  priority: post.priority,
1802
1876
  due_date: post.dueDate ?? null,
1803
1877
  to: post.users.to.map((u) => {
1804
- if (u.member) return memberIdToEmail(u.member.organizationMemberId, members);
1878
+ if (u.member) return memberIdToName(u.member.organizationMemberId, members);
1805
1879
  if (u.emailUser) return u.emailUser.emailAddress;
1806
1880
  return "";
1807
1881
  }).filter(Boolean),
1808
1882
  cc: post.users.cc.map((u) => {
1809
- if (u.member) return memberIdToEmail(u.member.organizationMemberId, members);
1883
+ if (u.member) return memberIdToName(u.member.organizationMemberId, members);
1810
1884
  if (u.emailUser) return u.emailUser.emailAddress;
1811
1885
  return "";
1812
1886
  }).filter(Boolean)
@@ -1902,20 +1976,88 @@ async function readStdin() {
1902
1976
  return Buffer.concat(chunks).toString("utf-8");
1903
1977
  }
1904
1978
 
1979
+ // src/utils/attachment-check.ts
1980
+ var import_node_readline = __toESM(require("readline"));
1981
+ function extractAttachmentFileIds(body) {
1982
+ const ids = /* @__PURE__ */ new Set();
1983
+ const re = /!?\[[^\]]*\]\(\/files\/([^\s)?#]+)/g;
1984
+ let m;
1985
+ while ((m = re.exec(body)) !== null) {
1986
+ ids.add(m[1]);
1987
+ }
1988
+ return ids;
1989
+ }
1990
+ function findDroppedAttachments(oldBody, newBody, attachments) {
1991
+ const oldIds = extractAttachmentFileIds(oldBody);
1992
+ const newIds = extractAttachmentFileIds(newBody);
1993
+ const dropped = [];
1994
+ for (const att of attachments) {
1995
+ if (oldIds.has(att.id) && !newIds.has(att.id)) {
1996
+ dropped.push({ id: att.id, name: att.name });
1997
+ }
1998
+ }
1999
+ return dropped;
2000
+ }
2001
+ function sanitizeFileName(name) {
2002
+ return name.replace(/[\x00-\x1F\x7F]/g, "?");
2003
+ }
2004
+ function printDroppedWarning(dropped) {
2005
+ process.stderr.write(`\u26A0 \uC0C8 \uBCF8\uBB38\uC5D0\uC11C ${dropped.length}\uAC1C attachment reference \uAC00 \uBE60\uC9D1\uB2C8\uB2E4:
2006
+ `);
2007
+ for (const a of dropped) {
2008
+ const safe = a.name ? sanitizeFileName(a.name) : void 0;
2009
+ process.stderr.write(` - /files/${a.id} ${safe ? "\u2190 " + safe : ""}
2010
+ `);
2011
+ }
2012
+ process.stderr.write(` (attachment \uC790\uCCB4\uB294 \uC11C\uBC84\uC5D0 \uB0A8\uC9C0\uB9CC \uBCF8\uBB38\uC5D0\uC11C \uC0AC\uB77C\uC838 \uBCF4\uC785\uB2C8\uB2E4.)
2013
+ `);
2014
+ }
2015
+ async function confirmDropped() {
2016
+ const rl = import_node_readline.default.createInterface({ input: process.stdin, output: process.stderr });
2017
+ const answer = await new Promise((resolve) => {
2018
+ rl.question("\uACC4\uC18D \uC9C4\uD589\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? (y/N) ", resolve);
2019
+ });
2020
+ rl.close();
2021
+ return /^y(es)?$/i.test(answer.trim());
2022
+ }
2023
+ async function checkAndGuardDropped(oldBody, newBody, attachments, noConfirm) {
2024
+ const dropped = findDroppedAttachments(oldBody, newBody, attachments);
2025
+ if (dropped.length === 0) return;
2026
+ printDroppedWarning(dropped);
2027
+ if (noConfirm) {
2028
+ process.stderr.write(` --no-confirm \uD50C\uB798\uADF8\uB85C \uADF8\uB300\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4.
2029
+ `);
2030
+ return;
2031
+ }
2032
+ if (!process.stdin.isTTY) {
2033
+ throw new DoorayCliError(
2034
+ "non-TTY \uD658\uACBD\uC5D0\uC11C \uB204\uB77D attachment \uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC758\uB3C4\uD55C \uBCC0\uACBD\uC774\uBA74 --no-confirm \uD50C\uB798\uADF8\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
2035
+ EXIT_PARAM_ERROR
2036
+ );
2037
+ }
2038
+ const confirmed = await confirmDropped();
2039
+ if (!confirmed) {
2040
+ throw new DoorayCliError("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
2041
+ }
2042
+ }
2043
+
1905
2044
  // src/commands/post/edit.ts
1906
- async function resolveUsers(client, projectId, emails) {
2045
+ async function resolveUsers(client, projectId, names) {
1907
2046
  const users = [];
1908
- for (const email of emails) {
1909
- const memberId = await resolveMember(client, projectId, email);
2047
+ for (const name of names) {
2048
+ const memberId = await resolveMember(client, projectId, name);
1910
2049
  users.push({ type: "member", member: { organizationMemberId: memberId } });
1911
2050
  }
1912
2051
  return users;
1913
2052
  }
1914
- var postEditCommand = new import_commander13.Command("edit").description("\uC5C5\uBB34 \uC218\uC815 ($EDITOR \uB610\uB294 --title/--body \uC635\uC158)").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)").option("--title <title>", "\uC81C\uBAA9 \uBCC0\uACBD (non-interactive)").option("--subject <subject>", "--title\uC758 deprecated alias").option("--body <text>", "\uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").action(async (project, postNumberStr, opts) => {
2053
+ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5\uBB34 \uC218\uC815 ($EDITOR \uB610\uB294 --title/--body \uC635\uC158)").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)").option("--title <title>", "\uC81C\uBAA9 \uBCC0\uACBD (non-interactive)").option("--subject <subject>", "--title\uC758 deprecated alias").option("--body <text>", "\uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--mention <name>", "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--mention-group <code>", "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").option("--no-confirm", "\uB204\uB77D attachment \uACBD\uACE0 \uC2DC confirm \uC5C6\uC774 \uC9C4\uD589 (\uC790\uB3D9\uD654\uC6A9)").action(async (project, postNumberStr, opts) => {
1915
2054
  const config = await getConfigOrThrow();
1916
2055
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2056
+ const mentionInputs = (opts.mention ?? []).filter((s) => s.length > 0);
2057
+ const groupInputs = (opts.mentionGroup ?? []).filter((s) => s.length > 0);
2058
+ const linkInputs = (opts.linkTask ?? []).filter((s) => s.length > 0);
1917
2059
  startSpinner("\uC5C5\uBB34 \uC870\uD68C \uC911...");
1918
- const { projectId, postId, postNumber } = await resolvePostInput(client, {
2060
+ const { projectId, postId, postNumber, projectCode } = await resolvePostInput(client, {
1919
2061
  projectArg: project,
1920
2062
  postNumberArg: postNumberStr,
1921
2063
  idOpt: opts.id,
@@ -1933,7 +2075,47 @@ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5
1933
2075
  }
1934
2076
  const nonInteractive = title || opts.body || opts.bodyFile;
1935
2077
  if (nonInteractive) {
1936
- const newBody = await readBodyInputOrNull(opts);
2078
+ let newBody = await readBodyInputOrNull(opts);
2079
+ if (newBody != null && !opts.dryRun) {
2080
+ const attachments = (post.files ?? []).map((f) => ({ id: f.id, name: f.name }));
2081
+ await checkAndGuardDropped(post.body.content, newBody, attachments, !opts.confirm);
2082
+ }
2083
+ if (mentionInputs.length > 0 || groupInputs.length > 0) {
2084
+ const effectiveBody = newBody ?? post.body.content;
2085
+ const me = await ensureMe(client);
2086
+ const memberIds = await Promise.all(
2087
+ mentionInputs.map((name) => resolveMember(client, projectId, name))
2088
+ );
2089
+ const nameMap = await buildMemberNameMap(client, projectId);
2090
+ const members2 = memberIds.map((memberId) => ({
2091
+ memberId,
2092
+ name: nameMap.get(memberId) ?? memberId
2093
+ }));
2094
+ const groups = await Promise.all(
2095
+ groupInputs.map(async (code) => {
2096
+ const g = await resolveMemberGroup(client, projectId, code);
2097
+ return { groupId: g.id, code: g.code, projectCode };
2098
+ })
2099
+ );
2100
+ newBody = prependMentions(effectiveBody, members2, groups, me);
2101
+ }
2102
+ if (linkInputs.length > 0) {
2103
+ const effectiveBody = newBody ?? post.body.content;
2104
+ const me = await ensureMe(client);
2105
+ const links = await resolveTaskLinks(client, linkInputs);
2106
+ newBody = appendTaskLinks(effectiveBody, links, me);
2107
+ }
2108
+ if (opts.dryRun) {
2109
+ stopSpinner(false);
2110
+ const globalOpts = postEditCommand.optsWithGlobals();
2111
+ const previewBody = newBody ?? post.body.content;
2112
+ if (globalOpts.json) {
2113
+ process.stdout.write(JSON.stringify({ body: previewBody }) + "\n");
2114
+ } else {
2115
+ process.stdout.write(previewBody + "\n");
2116
+ }
2117
+ return;
2118
+ }
1937
2119
  startSpinner("\uC5C5\uBB34 \uC218\uC815 \uC911...");
1938
2120
  const toUsers = post.users.to.map((u) => ({
1939
2121
  type: u.type,
@@ -1960,6 +2142,16 @@ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5
1960
2142
  });
1961
2143
  stopSpinner(true, "\uC5C5\uBB34 \uC218\uC815 \uC644\uB8CC");
1962
2144
  } else {
2145
+ if (mentionInputs.length > 0 || groupInputs.length > 0) {
2146
+ process.stderr.write(
2147
+ "\u26A0 --mention/--mention-group \uC740 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
2148
+ );
2149
+ }
2150
+ if (linkInputs.length > 0) {
2151
+ process.stderr.write(
2152
+ "\u26A0 --link-task \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
2153
+ );
2154
+ }
1963
2155
  const original = serializePostFrontmatter(post, members);
1964
2156
  const edited = await openInEditor(original);
1965
2157
  if (original === edited) {
@@ -1967,6 +2159,8 @@ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5
1967
2159
  return;
1968
2160
  }
1969
2161
  const parsed = parsePostFrontmatter(edited);
2162
+ const attachmentsInteractive = (post.files ?? []).map((f) => ({ id: f.id, name: f.name }));
2163
+ await checkAndGuardDropped(post.body.content, parsed.body, attachmentsInteractive, !opts.confirm);
1970
2164
  startSpinner("\uC5C5\uBB34 \uC218\uC815 \uC911...");
1971
2165
  const toUsers = await resolveUsers(client, projectId, parsed.to);
1972
2166
  const ccUsers = await resolveUsers(client, projectId, parsed.cc);
@@ -2040,7 +2234,7 @@ async function resolveUsers2(client, projectId, inputs) {
2040
2234
  }
2041
2235
  return users;
2042
2236
  }
2043
- var postCreateCommand = new import_commander14.Command("create").description("\uC5C5\uBB34 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--title <title>", "\uC5C5\uBB34 \uC81C\uBAA9").option("--subject <subject>", "--title\uC758 deprecated alias").option("--to <members...>", "\uB2F4\uB2F9\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--cc <members...>", "\uCC38\uC870\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--priority <level>", "\uC6B0\uC120\uC21C\uC704 (highest, high, normal, low, lowest)", "normal").option("--due-date <date>", "\uB9C8\uAC10\uC77C (ISO 8601 \uD615\uC2DD)").option("--tag <name>", "\uD0DC\uADF8 \uC774\uB984 (\uBC18\uBCF5 \uAC00\uB2A5)", (value, prev) => [...prev, value], []).option("--parent <ref>", "\uBD80\uBAA8 \uC5C5\uBB34 (project/number \uB610\uB294 postId)").option("--workflow <name>", "\uCD08\uAE30 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC774\uB984 \uB610\uB294 class").option("--milestone <name>", "\uB9C8\uC77C\uC2A4\uD1A4 \uC774\uB984").action(async (project, opts) => {
2237
+ var postCreateCommand = new import_commander14.Command("create").description("\uC5C5\uBB34 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--title <title>", "\uC5C5\uBB34 \uC81C\uBAA9").option("--subject <subject>", "--title\uC758 deprecated alias").option("--to <members...>", "\uB2F4\uB2F9\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--cc <members...>", "\uCC38\uC870\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--priority <level>", "\uC6B0\uC120\uC21C\uC704 (highest, high, normal, low, lowest)", "normal").option("--due-date <date>", "\uB9C8\uAC10\uC77C (ISO 8601 \uD615\uC2DD)").option("--tag <name>", "\uD0DC\uADF8 \uC774\uB984 (\uBC18\uBCF5 \uAC00\uB2A5)", (value, prev) => [...prev, value], []).option("--mention <name>", "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--mention-group <code>", "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--parent <ref>", "\uBD80\uBAA8 \uC5C5\uBB34 (project/number \uB610\uB294 postId)").option("--workflow <name>", "\uCD08\uAE30 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC774\uB984 \uB610\uB294 class").option("--milestone <name>", "\uB9C8\uC77C\uC2A4\uD1A4 \uC774\uB984").option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").action(async (project, opts) => {
2044
2238
  const globalOpts = postCreateCommand.optsWithGlobals();
2045
2239
  const config = await getConfigOrThrow();
2046
2240
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -2056,9 +2250,55 @@ var postCreateCommand = new import_commander14.Command("create").description("\u
2056
2250
  "\u26A0 --subject\uB294 deprecated\uC785\uB2C8\uB2E4. \uB300\uC2E0 --title\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.\n"
2057
2251
  );
2058
2252
  }
2059
- const bodyContent = await readBodyInput(opts);
2253
+ const mentionInputs = (opts.mention ?? []).filter((s) => s.length > 0);
2254
+ const groupInputs = (opts.mentionGroup ?? []).filter((s) => s.length > 0);
2255
+ const linkInputs = (opts.linkTask ?? []).filter((s) => s.length > 0);
2256
+ let bodyContent = await readBodyInput(opts);
2060
2257
  startSpinner("\uC5C5\uBB34 \uC0DD\uC131 \uC911...");
2061
2258
  const projectId = await resolveProject(client, project);
2259
+ let projectCode;
2260
+ if (groupInputs.length > 0) {
2261
+ const projects = await ensureProjects(client);
2262
+ projectCode = projects.find((p) => p.id === projectId)?.code;
2263
+ if (!projectCode) {
2264
+ throw new DoorayCliError(
2265
+ `--mention-group \uC740 \uACF5\uAC1C \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C\uB9CC \uC9C0\uC6D0\uB429\uB2C8\uB2E4. dooray project list \uB85C \uCE90\uC2DC \uAC31\uC2E0 \uD6C4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694.`,
2266
+ EXIT_PARAM_ERROR
2267
+ );
2268
+ }
2269
+ }
2270
+ if (mentionInputs.length > 0 || groupInputs.length > 0) {
2271
+ const me = await ensureMe(client);
2272
+ const memberIds = await Promise.all(
2273
+ mentionInputs.map((name) => resolveMember(client, projectId, name))
2274
+ );
2275
+ const nameMap = await buildMemberNameMap(client, projectId);
2276
+ const members = memberIds.map((memberId) => ({
2277
+ memberId,
2278
+ name: nameMap.get(memberId) ?? memberId
2279
+ }));
2280
+ const groups = await Promise.all(
2281
+ groupInputs.map(async (code) => {
2282
+ const g = await resolveMemberGroup(client, projectId, code);
2283
+ return { groupId: g.id, code: g.code, projectCode: projectCode ?? "" };
2284
+ })
2285
+ );
2286
+ bodyContent = prependMentions(bodyContent, members, groups, me);
2287
+ }
2288
+ if (linkInputs.length > 0) {
2289
+ const me = await ensureMe(client);
2290
+ const links = await resolveTaskLinks(client, linkInputs);
2291
+ bodyContent = appendTaskLinks(bodyContent, links, me);
2292
+ }
2293
+ if (opts.dryRun) {
2294
+ stopSpinner(false);
2295
+ if (globalOpts.json) {
2296
+ process.stdout.write(JSON.stringify({ body: bodyContent }) + "\n");
2297
+ } else {
2298
+ process.stdout.write(bodyContent + "\n");
2299
+ }
2300
+ return;
2301
+ }
2062
2302
  const toUsers = opts.to ? await resolveUsers2(client, projectId, opts.to) : [];
2063
2303
  const ccUsers = opts.cc ? await resolveUsers2(client, projectId, opts.cc) : [];
2064
2304
  const tagInputs = (opts.tag ?? []).filter((s) => s.length > 0);
@@ -2336,24 +2576,6 @@ var commentLatestCommand = new import_commander18.Command("latest").description(
2336
2576
 
2337
2577
  // src/commands/post/comment/add.ts
2338
2578
  var import_commander19 = require("commander");
2339
-
2340
- // src/utils/mention.ts
2341
- function buildMemberMention(m, me) {
2342
- const title = m.memberId === me.id ? "me" : "member";
2343
- return `[@${m.name}](dooray://${me.orgId}/members/${m.memberId} "${title}")`;
2344
- }
2345
- function buildGroupMention(g, me) {
2346
- return `[@${g.projectCode}/${g.code}](dooray://${me.orgId}/member-groups/${g.groupId})`;
2347
- }
2348
- function prependMentions(body, members, groups, me) {
2349
- const parts = [];
2350
- for (const m of members) parts.push(buildMemberMention(m, me));
2351
- for (const g of groups) parts.push(buildGroupMention(g, me));
2352
- if (parts.length === 0) return body;
2353
- return parts.join(" ") + " " + body;
2354
- }
2355
-
2356
- // src/commands/post/comment/add.ts
2357
2579
  var commentAddCommand = new import_commander19.Command("add").description("\uB313\uAE00 \uCD94\uAC00").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)").option("--body <text>", "\uB313\uAE00 \uBCF8\uBB38 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option(
2358
2580
  "--mention <name>",
2359
2581
  "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)",
@@ -2364,7 +2586,7 @@ var commentAddCommand = new import_commander19.Command("add").description("\uB31
2364
2586
  "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)",
2365
2587
  (value, prev) => [...prev, value],
2366
2588
  []
2367
- ).action(async (project, postNumberStr, opts) => {
2589
+ ).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").action(async (project, postNumberStr, opts) => {
2368
2590
  const globalOpts = commentAddCommand.optsWithGlobals();
2369
2591
  const config = await getConfigOrThrow();
2370
2592
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -2385,6 +2607,7 @@ var commentAddCommand = new import_commander19.Command("add").description("\uB31
2385
2607
  });
2386
2608
  const mentionInputs = (opts.mention ?? []).filter((s) => s.length > 0);
2387
2609
  const groupInputs = (opts.mentionGroup ?? []).filter((s) => s.length > 0);
2610
+ const linkInputs = (opts.linkTask ?? []).filter((s) => s.length > 0);
2388
2611
  if (mentionInputs.length > 0 || groupInputs.length > 0) {
2389
2612
  const me = await ensureMe(client);
2390
2613
  const memberIds = await Promise.all(
@@ -2403,6 +2626,20 @@ var commentAddCommand = new import_commander19.Command("add").description("\uB31
2403
2626
  );
2404
2627
  bodyContent = prependMentions(bodyContent, members, groups, me);
2405
2628
  }
2629
+ if (linkInputs.length > 0) {
2630
+ const me = await ensureMe(client);
2631
+ const links = await resolveTaskLinks(client, linkInputs);
2632
+ bodyContent = appendTaskLinks(bodyContent, links, me);
2633
+ }
2634
+ if (opts.dryRun) {
2635
+ stopSpinner(false);
2636
+ if (globalOpts.json) {
2637
+ process.stdout.write(JSON.stringify({ body: bodyContent }) + "\n");
2638
+ } else {
2639
+ process.stdout.write(bodyContent + "\n");
2640
+ }
2641
+ return;
2642
+ }
2406
2643
  const res = await client.createPostComment(projectId, postId, {
2407
2644
  body: { mimeType: "text/x-markdown", content: bodyContent }
2408
2645
  });
@@ -2419,7 +2656,7 @@ var commentAddCommand = new import_commander19.Command("add").description("\uB31
2419
2656
 
2420
2657
  // src/commands/post/comment/edit.ts
2421
2658
  var import_commander20 = require("commander");
2422
- var commentEditCommand = new import_commander20.Command("edit").description("\uB313\uAE00 \uC218\uC815 ($EDITOR \uB610\uB294 --body \uC635\uC158)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray URL / \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").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 <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--body <text>", "\uB313\uAE00 \uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").option(
2659
+ var commentEditCommand = new import_commander20.Command("edit").description("\uB313\uAE00 \uC218\uC815 ($EDITOR \uB610\uB294 --body \uC635\uC158)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray URL / \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").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 <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--body <text>", "\uB313\uAE00 \uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--no-confirm", "\uB204\uB77D attachment \uACBD\uACE0 \uC2DC confirm \uC5C6\uC774 \uC9C4\uD589 (\uC790\uB3D9\uD654\uC6A9)").option(
2423
2660
  "--mention <name>",
2424
2661
  "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)",
2425
2662
  (value, prev) => [...prev, value],
@@ -2429,7 +2666,7 @@ var commentEditCommand = new import_commander20.Command("edit").description("\uB
2429
2666
  "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)",
2430
2667
  (value, prev) => [...prev, value],
2431
2668
  []
2432
- ).action(async (arg1, arg2, arg3, opts) => {
2669
+ ).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").action(async (arg1, arg2, arg3, opts) => {
2433
2670
  const config = await getConfigOrThrow();
2434
2671
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2435
2672
  let projectArg;
@@ -2488,6 +2725,7 @@ var commentEditCommand = new import_commander20.Command("edit").description("\uB
2488
2725
  }
2489
2726
  const mentionInputs = (opts.mention ?? []).filter((s) => s.length > 0);
2490
2727
  const groupInputs = (opts.mentionGroup ?? []).filter((s) => s.length > 0);
2728
+ const linkInputs = (opts.linkTask ?? []).filter((s) => s.length > 0);
2491
2729
  let mentionPrefix = "";
2492
2730
  if (mentionInputs.length > 0 || groupInputs.length > 0) {
2493
2731
  const me = await ensureMe(client);
@@ -2519,6 +2757,23 @@ var commentEditCommand = new import_commander20.Command("edit").description("\uB
2519
2757
  } else if (mentionPrefix) {
2520
2758
  edited = mentionPrefix + " " + edited;
2521
2759
  }
2760
+ if (linkInputs.length > 0) {
2761
+ const me = await ensureMe(client);
2762
+ const links = await resolveTaskLinks(client, linkInputs);
2763
+ edited = appendTaskLinks(edited, links, me);
2764
+ }
2765
+ if (opts.dryRun) {
2766
+ stopSpinner(false);
2767
+ const globalOpts = commentEditCommand.optsWithGlobals();
2768
+ if (globalOpts.json) {
2769
+ process.stdout.write(JSON.stringify({ body: edited }) + "\n");
2770
+ } else {
2771
+ process.stdout.write(edited + "\n");
2772
+ }
2773
+ return;
2774
+ }
2775
+ const attachments = (comment.files ?? []).map((f) => ({ id: f.id, name: f.name }));
2776
+ await checkAndGuardDropped(comment.body.content, edited, attachments, !opts.confirm);
2522
2777
  startSpinner("\uB313\uAE00 \uC218\uC815 \uC911...");
2523
2778
  await client.updatePostComment(projectId, postId, commentId, {
2524
2779
  body: { mimeType: "text/x-markdown", content: edited }
@@ -2585,64 +2840,209 @@ var commentDeleteCommand = new import_commander21.Command("delete").description(
2585
2840
  `);
2586
2841
  });
2587
2842
 
2588
- // src/commands/post/comment/file/index.ts
2589
- var import_commander26 = require("commander");
2590
-
2591
- // src/commands/post/comment/file/list.ts
2843
+ // src/commands/post/comment/get.ts
2592
2844
  var import_commander22 = require("commander");
2593
- function formatSize(bytes) {
2594
- if (bytes < 1024) return `${bytes}B`;
2595
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
2596
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
2845
+
2846
+ // src/formatters/comment.ts
2847
+ function formatCommentDetail(comment, opts) {
2848
+ if (opts.json) {
2849
+ printJson(comment);
2850
+ return;
2851
+ }
2852
+ if (opts.quiet) {
2853
+ process.stdout.write(comment.id + "\n");
2854
+ return;
2855
+ }
2856
+ const metaRows = [
2857
+ ["ID", comment.id],
2858
+ ["\uC791\uC131\uC790", comment.creator.member?.name ?? ""],
2859
+ ["\uC0DD\uC131", comment.createdAt],
2860
+ ...comment.modifiedAt ? [["\uC218\uC815", comment.modifiedAt]] : [],
2861
+ ["mimeType", comment.body.mimeType]
2862
+ ];
2863
+ printTable(["Field", "Value"], metaRows);
2864
+ process.stdout.write("\n" + comment.body.content + "\n");
2865
+ if (comment.files && comment.files.length > 0) {
2866
+ process.stdout.write("\n[Attachments]\n");
2867
+ printTable(
2868
+ ["Name", "Size", "ID"],
2869
+ comment.files.map((f) => [f.name, String(f.size), f.id])
2870
+ );
2871
+ }
2597
2872
  }
2598
- var listCommentFileCommand = new import_commander22.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) => {
2599
- const globalOpts = listCommentFileCommand.optsWithGlobals();
2600
- const config = await getConfigOrThrow();
2601
- const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2602
- let projectArg;
2603
- let postNumberArg;
2604
- let logId = opts.commentId;
2605
- if (opts.id || opts.url) {
2606
- if (arg2 || arg3) {
2873
+
2874
+ // src/commands/post/comment/get.ts
2875
+ function parseGetArgs(arg1, arg2, arg3, opts) {
2876
+ const hasPositional = arg1 !== void 0 || arg2 !== void 0 || arg3 !== void 0;
2877
+ const hasIdUrl = !!(opts.id || opts.url);
2878
+ if (hasPositional && hasIdUrl) {
2879
+ throw new DoorayCliError(
2880
+ "positional \uC778\uC790\uC640 --id/--url \uC635\uC158\uC740 \uB3D9\uC2DC\uC5D0 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2881
+ EXIT_PARAM_ERROR
2882
+ );
2883
+ }
2884
+ if (hasIdUrl) {
2885
+ if (!opts.commentId) {
2607
2886
  throw new DoorayCliError(
2608
- "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uB313\uAE00 ID \uC678 \uCD94\uAC00 positional \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. --comment-id \uC635\uC158 \uC0AC\uC6A9\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
2887
+ "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 --comment-id \uC635\uC158\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
2609
2888
  EXIT_PARAM_ERROR
2610
2889
  );
2611
2890
  }
2612
- logId = logId ?? arg1;
2613
- } else if (arg3) {
2614
- projectArg = arg1;
2615
- postNumberArg = arg2;
2616
- logId = logId ?? arg3;
2617
- } else if (arg1 && !arg2) {
2618
- projectArg = arg1;
2619
- if (!logId) {
2891
+ return { commentId: opts.commentId, idOpt: opts.id, urlOpt: opts.url };
2892
+ }
2893
+ if (arg3) {
2894
+ if (opts.commentId) {
2620
2895
  throw new DoorayCliError(
2621
- "URL/--id \uBAA8\uB4DC\uC5D0\uC11C\uB294 --comment-id \uC635\uC158\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
2896
+ "positional \uB313\uAE00 ID \uC640 --comment-id \uC635\uC158\uC740 \uB3D9\uC2DC\uC5D0 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2622
2897
  EXIT_PARAM_ERROR
2623
2898
  );
2624
2899
  }
2625
- } else {
2626
- projectArg = arg1;
2627
- postNumberArg = arg2;
2628
- if (!logId) {
2900
+ return {
2901
+ projectArg: arg1,
2902
+ postNumberArg: arg2,
2903
+ commentId: arg3
2904
+ };
2905
+ }
2906
+ if (arg1 && arg2 && opts.commentId) {
2907
+ return {
2908
+ projectArg: arg1,
2909
+ postNumberArg: arg2,
2910
+ commentId: opts.commentId
2911
+ };
2912
+ }
2913
+ throw new DoorayCliError(
2914
+ "<comment-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 3\uBC88\uC9F8 \uB610\uB294 --comment-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2915
+ EXIT_PARAM_ERROR
2916
+ );
2917
+ }
2918
+ var commentGetCommand = new import_commander22.Command("get").description("\uB2E8\uC77C \uB313\uAE00 \uBCF8\uBB38 + \uBA54\uD0C0 + attachments \uC870\uD68C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray URL (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 (\uBAA8\uB4DC\uBCC4)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (positional \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (positional \uB300\uC2E0)").option("--comment-id <id>", "\uB313\uAE00 ID (arg3 \uB300\uC2E0)").action(async (arg1, arg2, arg3, opts) => {
2919
+ const parsed = parseGetArgs(arg1, arg2, arg3, opts);
2920
+ const config = await getConfigOrThrow();
2921
+ const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2922
+ const globalOpts = commentGetCommand.optsWithGlobals();
2923
+ startSpinner("\uB313\uAE00 \uC870\uD68C \uC911...");
2924
+ let projectId;
2925
+ let comment;
2926
+ try {
2927
+ const resolved = await resolvePostInput(client, {
2928
+ projectArg: parsed.projectArg,
2929
+ postNumberArg: parsed.postNumberArg,
2930
+ idOpt: parsed.idOpt,
2931
+ urlOpt: parsed.urlOpt
2932
+ });
2933
+ projectId = resolved.projectId;
2934
+ const detail = await client.getPostComment(resolved.projectId, resolved.postId, parsed.commentId);
2935
+ comment = detail.result;
2936
+ stopSpinner(true, "\uB313\uAE00 \uC870\uD68C \uC644\uB8CC");
2937
+ } catch (e) {
2938
+ stopSpinner(false);
2939
+ throw e;
2940
+ }
2941
+ if (!globalOpts.json) {
2942
+ let nameMap = /* @__PURE__ */ new Map();
2943
+ try {
2944
+ nameMap = await buildMemberNameMap(client, projectId);
2945
+ } catch {
2946
+ }
2947
+ comment = enrichCommentCreators([comment], nameMap)[0];
2948
+ }
2949
+ formatCommentDetail(comment, globalOpts);
2950
+ });
2951
+
2952
+ // src/commands/post/comment/file/index.ts
2953
+ var import_commander27 = require("commander");
2954
+
2955
+ // src/commands/post/comment/file/list.ts
2956
+ var import_commander23 = require("commander");
2957
+
2958
+ // src/resolvers/comment-file-input.ts
2959
+ var FILE_ID_SECONDARY_LABEL = {
2960
+ positional: "4\uBC88\uC9F8",
2961
+ option: "--file-id",
2962
+ identifier: "<fileId>"
2963
+ };
2964
+ function assertNonEmpty(value, fieldName) {
2965
+ if (value.trim() === "") {
2966
+ throw new DoorayCliError(
2967
+ `${fieldName} \uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`,
2968
+ EXIT_PARAM_ERROR
2969
+ );
2970
+ }
2971
+ }
2972
+ function parseImpl(args) {
2973
+ const isOptionMode = !!(args.idOpt || args.urlOpt);
2974
+ let projectArg;
2975
+ let postNumberArg;
2976
+ let commentId = args.commentIdOpt;
2977
+ let secondary = args.secondaryOpt;
2978
+ if (isOptionMode) {
2979
+ if (!commentId) commentId = args.arg1;
2980
+ if (!secondary) secondary = args.arg2;
2981
+ if (args.arg3 || args.arg4) {
2629
2982
  throw new DoorayCliError(
2630
- "<comment-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 3\uBC88\uC9F8 \uB610\uB294 --comment-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2983
+ "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uCD94\uAC00 positional \uC778\uC790\uAC00 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
2631
2984
  EXIT_PARAM_ERROR
2632
2985
  );
2633
2986
  }
2987
+ } else {
2988
+ projectArg = args.arg1;
2989
+ postNumberArg = args.arg2;
2990
+ if (!commentId) commentId = args.arg3;
2991
+ if (!secondary) secondary = args.arg4;
2634
2992
  }
2635
- if (!logId) {
2636
- throw new DoorayCliError("<comment-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
2993
+ if (!commentId) {
2994
+ throw new DoorayCliError(
2995
+ "<comment-id> \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 3\uBC88\uC9F8 \uB610\uB294 --comment-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2996
+ EXIT_PARAM_ERROR
2997
+ );
2637
2998
  }
2638
- startSpinner("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C \uC911...");
2999
+ assertNonEmpty(commentId, "<comment-id>");
3000
+ if (args.requireSecondary) {
3001
+ if (!secondary) {
3002
+ const { identifier, positional, option } = args.secondaryLabel;
3003
+ throw new DoorayCliError(
3004
+ `${identifier} \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional ${positional} \uB610\uB294 ${option} \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`,
3005
+ EXIT_PARAM_ERROR
3006
+ );
3007
+ }
3008
+ assertNonEmpty(secondary, args.secondaryLabel.identifier);
3009
+ } else if (secondary !== void 0) {
3010
+ assertNonEmpty(secondary, "<secondary>");
3011
+ }
3012
+ return { projectArg, postNumberArg, commentId, secondary };
3013
+ }
3014
+ async function resolveCommentFileInput(client, args) {
3015
+ const { projectArg, postNumberArg, commentId, secondary } = parseImpl(args);
2639
3016
  const { projectId, postId } = await resolvePostInput(client, {
2640
3017
  projectArg,
2641
3018
  postNumberArg,
3019
+ idOpt: args.idOpt,
3020
+ urlOpt: args.urlOpt
3021
+ });
3022
+ return { projectId, postId, commentId, secondary };
3023
+ }
3024
+
3025
+ // src/commands/post/comment/file/list.ts
3026
+ function formatSize(bytes) {
3027
+ if (bytes < 1024) return `${bytes}B`;
3028
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
3029
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
3030
+ }
3031
+ var listCommentFileCommand = new import_commander23.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) => {
3032
+ const globalOpts = listCommentFileCommand.optsWithGlobals();
3033
+ const config = await getConfigOrThrow();
3034
+ const client = new DoorayApiClient(config.apiKey, config.baseUrl);
3035
+ const { projectId, postId, commentId } = await resolveCommentFileInput(client, {
3036
+ arg1,
3037
+ arg2,
3038
+ arg3,
2642
3039
  idOpt: opts.id,
2643
- urlOpt: opts.url
3040
+ urlOpt: opts.url,
3041
+ commentIdOpt: opts.commentId,
3042
+ requireSecondary: false
2644
3043
  });
2645
- const res = await client.getPostComment(projectId, postId, logId);
3044
+ startSpinner("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C \uC911...");
3045
+ const res = await client.getPostComment(projectId, postId, commentId);
2646
3046
  const files = res.result.files ?? [];
2647
3047
  stopSpinner(true, `\uCCA8\uBD80 \uD30C\uC77C ${files.length}\uAC1C`);
2648
3048
  if (files.length === 0) {
@@ -2662,7 +3062,7 @@ var listCommentFileCommand = new import_commander22.Command("list").description(
2662
3062
  });
2663
3063
 
2664
3064
  // src/commands/post/comment/file/upload.ts
2665
- var import_commander23 = require("commander");
3065
+ var import_commander24 = require("commander");
2666
3066
  var import_node_path4 = require("path");
2667
3067
 
2668
3068
  // src/utils/comment-files.ts
@@ -2683,55 +3083,21 @@ function removeFileReference(body, fileId) {
2683
3083
  }
2684
3084
 
2685
3085
  // src/commands/post/comment/file/upload.ts
2686
- var uploadCommentFileCommand = new import_commander23.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (post-level \uD30C\uC77C \uC5C5\uB85C\uB4DC + \uB313\uAE00 reference \uCD94\uAC00, ADR-024)").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) => {
3086
+ var uploadCommentFileCommand = new import_commander24.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (post-level \uD30C\uC77C \uC5C5\uB85C\uB4DC + \uB313\uAE00 reference \uCD94\uAC00, ADR-024)").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) => {
2687
3087
  const globalOpts = uploadCommentFileCommand.optsWithGlobals();
2688
3088
  const config = await getConfigOrThrow();
2689
3089
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2690
- let projectArg;
2691
- let postNumberArg;
2692
- let logId = opts.commentId;
2693
- let filePath = opts.file;
2694
- if (opts.id || opts.url) {
2695
- if (arg3 || arg4) {
2696
- throw new DoorayCliError(
2697
- "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uB313\uAE00 ID \uC640 \uD30C\uC77C \uACBD\uB85C \uC678 \uCD94\uAC00 positional \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
2698
- EXIT_PARAM_ERROR
2699
- );
2700
- }
2701
- logId = logId ?? arg1;
2702
- filePath = filePath ?? arg2;
2703
- } else if (arg4) {
2704
- projectArg = arg1;
2705
- postNumberArg = arg2;
2706
- logId = logId ?? arg3;
2707
- filePath = filePath ?? arg4;
2708
- } else if (arg3) {
2709
- projectArg = arg1;
2710
- postNumberArg = arg2;
2711
- logId = logId ?? arg3;
2712
- } else if (arg1 && !arg2) {
2713
- projectArg = arg1;
2714
- } else {
2715
- projectArg = arg1;
2716
- postNumberArg = arg2;
2717
- }
2718
- if (!logId) {
2719
- throw new DoorayCliError(
2720
- "<comment-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 3\uBC88\uC9F8 \uB610\uB294 --comment-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2721
- EXIT_PARAM_ERROR
2722
- );
2723
- }
2724
- if (!filePath) {
2725
- throw new DoorayCliError(
2726
- "<path>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 4\uBC88\uC9F8 \uB610\uB294 --file \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2727
- EXIT_PARAM_ERROR
2728
- );
2729
- }
2730
- const { projectId, postId } = await resolvePostInput(client, {
2731
- projectArg,
2732
- postNumberArg,
3090
+ const { projectId, postId, commentId, secondary: filePath } = await resolveCommentFileInput(client, {
3091
+ arg1,
3092
+ arg2,
3093
+ arg3,
3094
+ arg4,
2733
3095
  idOpt: opts.id,
2734
- urlOpt: opts.url
3096
+ urlOpt: opts.url,
3097
+ commentIdOpt: opts.commentId,
3098
+ secondaryOpt: opts.file,
3099
+ requireSecondary: true,
3100
+ secondaryLabel: { positional: "4\uBC88\uC9F8", option: "--file", identifier: "<path>" }
2735
3101
  });
2736
3102
  startSpinner("\uD30C\uC77C \uC5C5\uB85C\uB4DC \uC911...");
2737
3103
  const uploadRes = await client.uploadPostFile(projectId, postId, filePath);
@@ -2740,10 +3106,10 @@ var uploadCommentFileCommand = new import_commander23.Command("upload").descript
2740
3106
  stopSpinner(true, "\uC5C5\uB85C\uB4DC \uC644\uB8CC");
2741
3107
  startSpinner("\uB313\uAE00 reference \uCD94\uAC00 \uC911...");
2742
3108
  try {
2743
- const commentRes = await client.getPostComment(projectId, postId, logId);
3109
+ const commentRes = await client.getPostComment(projectId, postId, commentId);
2744
3110
  const currentBody = commentRes.result.body.content;
2745
3111
  const newBody = appendFileReference(currentBody, fileName, fileId);
2746
- await client.updatePostComment(projectId, postId, logId, {
3112
+ await client.updatePostComment(projectId, postId, commentId, {
2747
3113
  body: { mimeType: "text/x-markdown", content: newBody }
2748
3114
  });
2749
3115
  stopSpinner(true, "reference \uCD94\uAC00 \uC644\uB8CC");
@@ -2755,7 +3121,7 @@ var uploadCommentFileCommand = new import_commander23.Command("upload").descript
2755
3121
  );
2756
3122
  }
2757
3123
  if (globalOpts.json) {
2758
- printJson({ fileId, fileName, commentId: logId });
3124
+ printJson({ fileId, fileName, commentId });
2759
3125
  } else if (globalOpts.quiet) {
2760
3126
  process.stdout.write(`${fileId}
2761
3127
  `);
@@ -2766,55 +3132,27 @@ var uploadCommentFileCommand = new import_commander23.Command("upload").descript
2766
3132
  });
2767
3133
 
2768
3134
  // src/commands/post/comment/file/download.ts
2769
- var import_commander24 = require("commander");
3135
+ var import_commander25 = require("commander");
2770
3136
  var import_promises8 = require("fs/promises");
2771
3137
  var import_node_path5 = require("path");
2772
- var downloadCommentFileCommand = new import_commander24.Command("download").description(
3138
+ var downloadCommentFileCommand = new import_commander25.Command("download").description(
2773
3139
  "\uB313\uAE00\uC5D0 \uCCA8\uBD80\uB41C \uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC (\uD604\uC7AC comment-id \uB294 \uBBF8\uC0AC\uC6A9 \u2014 \uBA58\uD0C8 \uBAA8\uB378 \uC77C\uAD00\uC131, \uBBF8\uB798 \uB313\uAE00 \uB2E8\uC704 \uAD8C\uD55C \uD638\uD658\uC6A9)"
2774
3140
  ).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 ID").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C ID (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-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("--out <path>", "\uC800\uC7A5 \uACBD\uB85C (\uBBF8\uC9C0\uC815 \uC2DC \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC6D0\uBCF8 \uD30C\uC77C\uBA85\uC73C\uB85C \uC800\uC7A5)", ".").action(async (arg1, arg2, arg3, arg4, opts) => {
2775
3141
  const config = await getConfigOrThrow();
2776
3142
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2777
- let projectArg;
2778
- let postNumberArg;
2779
- let _commentId = opts.commentId;
2780
- let fileId = opts.fileId;
2781
- if (opts.id || opts.url) {
2782
- if (arg3 || arg4) {
2783
- throw new DoorayCliError(
2784
- "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uB313\uAE00 ID \uC640 \uD30C\uC77C ID \uC678 \uCD94\uAC00 positional \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
2785
- EXIT_PARAM_ERROR
2786
- );
2787
- }
2788
- _commentId = _commentId ?? arg1;
2789
- fileId = fileId ?? arg2;
2790
- } else if (arg4) {
2791
- projectArg = arg1;
2792
- postNumberArg = arg2;
2793
- _commentId = _commentId ?? arg3;
2794
- fileId = fileId ?? arg4;
2795
- } else if (arg3) {
2796
- projectArg = arg1;
2797
- postNumberArg = arg2;
2798
- _commentId = _commentId ?? arg3;
2799
- } else if (arg1 && !arg2) {
2800
- projectArg = arg1;
2801
- } else {
2802
- projectArg = arg1;
2803
- postNumberArg = arg2;
2804
- }
2805
- if (!fileId) {
2806
- throw new DoorayCliError(
2807
- "<file-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 4\uBC88\uC9F8 \uB610\uB294 --file-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2808
- EXIT_PARAM_ERROR
2809
- );
2810
- }
2811
- startSpinner("\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC \uC911...");
2812
- const { projectId, postId } = await resolvePostInput(client, {
2813
- projectArg,
2814
- postNumberArg,
3143
+ const { projectId, postId, secondary: fileId } = await resolveCommentFileInput(client, {
3144
+ arg1,
3145
+ arg2,
3146
+ arg3,
3147
+ arg4,
2815
3148
  idOpt: opts.id,
2816
- urlOpt: opts.url
3149
+ urlOpt: opts.url,
3150
+ commentIdOpt: opts.commentId,
3151
+ secondaryOpt: opts.fileId,
3152
+ requireSecondary: true,
3153
+ secondaryLabel: FILE_ID_SECONDARY_LABEL
2817
3154
  });
3155
+ startSpinner("\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC \uC911...");
2818
3156
  const { buffer, fileName } = await client.downloadPostFile(projectId, postId, fileId);
2819
3157
  const safeName = (0, import_node_path5.basename)(fileName);
2820
3158
  const outputPath = (0, import_node_path5.join)(opts.out, safeName);
@@ -2825,50 +3163,22 @@ var downloadCommentFileCommand = new import_commander24.Command("download").desc
2825
3163
  });
2826
3164
 
2827
3165
  // src/commands/post/comment/file/delete.ts
2828
- var import_commander25 = require("commander");
2829
- var deleteCommentFileCommand = new import_commander25.Command("delete").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C (\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 + \uD30C\uC77C \uC0AD\uC81C, ADR-024)").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 ID").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C ID (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-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C").action(async (arg1, arg2, arg3, arg4, opts) => {
3166
+ var import_commander26 = require("commander");
3167
+ var deleteCommentFileCommand = new import_commander26.Command("delete").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C (\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 + \uD30C\uC77C \uC0AD\uC81C, ADR-024)").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 ID").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C ID (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-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C").action(async (arg1, arg2, arg3, arg4, opts) => {
2830
3168
  const config = await getConfigOrThrow();
2831
3169
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2832
- let projectArg;
2833
- let postNumberArg;
2834
- let logId = opts.commentId;
2835
- let fileId = opts.fileId;
2836
- if (opts.id || opts.url) {
2837
- if (arg3 || arg4) {
2838
- throw new DoorayCliError(
2839
- "--id/--url \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uB313\uAE00 ID \uC640 \uD30C\uC77C ID \uC678 \uCD94\uAC00 positional \uC778\uC790\uB97C \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
2840
- EXIT_PARAM_ERROR
2841
- );
2842
- }
2843
- logId = logId ?? arg1;
2844
- fileId = fileId ?? arg2;
2845
- } else if (arg4) {
2846
- projectArg = arg1;
2847
- postNumberArg = arg2;
2848
- logId = logId ?? arg3;
2849
- fileId = fileId ?? arg4;
2850
- } else if (arg3) {
2851
- projectArg = arg1;
2852
- postNumberArg = arg2;
2853
- logId = logId ?? arg3;
2854
- } else if (arg1 && !arg2) {
2855
- projectArg = arg1;
2856
- } else {
2857
- projectArg = arg1;
2858
- postNumberArg = arg2;
2859
- }
2860
- if (!logId) {
2861
- throw new DoorayCliError(
2862
- "<comment-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 3\uBC88\uC9F8 \uB610\uB294 --comment-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2863
- EXIT_PARAM_ERROR
2864
- );
2865
- }
2866
- if (!fileId) {
2867
- throw new DoorayCliError(
2868
- "<file-id>\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. positional 4\uBC88\uC9F8 \uB610\uB294 --file-id \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
2869
- EXIT_PARAM_ERROR
2870
- );
2871
- }
3170
+ const { projectId, postId, commentId, secondary: fileId } = await resolveCommentFileInput(client, {
3171
+ arg1,
3172
+ arg2,
3173
+ arg3,
3174
+ arg4,
3175
+ idOpt: opts.id,
3176
+ urlOpt: opts.url,
3177
+ commentIdOpt: opts.commentId,
3178
+ secondaryOpt: opts.fileId,
3179
+ requireSecondary: true,
3180
+ secondaryLabel: FILE_ID_SECONDARY_LABEL
3181
+ });
2872
3182
  if (!opts.yes) {
2873
3183
  const { confirm: confirm2 } = await import("@inquirer/prompts");
2874
3184
  const ok = await confirm2({
@@ -2879,18 +3189,12 @@ var deleteCommentFileCommand = new import_commander25.Command("delete").descript
2879
3189
  return;
2880
3190
  }
2881
3191
  }
2882
- const { projectId, postId } = await resolvePostInput(client, {
2883
- projectArg,
2884
- postNumberArg,
2885
- idOpt: opts.id,
2886
- urlOpt: opts.url
2887
- });
2888
3192
  startSpinner("\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 \uC911...");
2889
3193
  try {
2890
- const commentRes = await client.getPostComment(projectId, postId, logId);
3194
+ const commentRes = await client.getPostComment(projectId, postId, commentId);
2891
3195
  const currentBody = commentRes.result.body.content;
2892
3196
  const newBody = removeFileReference(currentBody, fileId);
2893
- await client.updatePostComment(projectId, postId, logId, {
3197
+ await client.updatePostComment(projectId, postId, commentId, {
2894
3198
  body: { mimeType: "text/x-markdown", content: newBody }
2895
3199
  });
2896
3200
  stopSpinner(true, "reference \uC81C\uAC70 \uC644\uB8CC");
@@ -2912,21 +3216,21 @@ var deleteCommentFileCommand = new import_commander25.Command("delete").descript
2912
3216
  EXIT_API_ERROR
2913
3217
  );
2914
3218
  }
2915
- process.stdout.write(`\uD30C\uC77C(${fileId})\uC774 \uB313\uAE00(${logId})\uC5D0\uC11C \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
3219
+ process.stdout.write(`\uD30C\uC77C(${fileId})\uC774 \uB313\uAE00(${commentId})\uC5D0\uC11C \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
2916
3220
  `);
2917
3221
  });
2918
3222
 
2919
3223
  // src/commands/post/comment/file/index.ts
2920
- var commentFileCommand = new import_commander26.Command("file").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uAD00\uB9AC (post-level files API + \uB313\uAE00 PUT \uD569\uC131, ADR-024)").addCommand(listCommentFileCommand).addCommand(uploadCommentFileCommand).addCommand(downloadCommentFileCommand).addCommand(deleteCommentFileCommand);
3224
+ var commentFileCommand = new import_commander27.Command("file").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uAD00\uB9AC (post-level files API + \uB313\uAE00 PUT \uD569\uC131, ADR-024)").addCommand(listCommentFileCommand).addCommand(uploadCommentFileCommand).addCommand(downloadCommentFileCommand).addCommand(deleteCommentFileCommand);
2921
3225
 
2922
3226
  // src/commands/post/file/list.ts
2923
- var import_commander27 = require("commander");
3227
+ var import_commander28 = require("commander");
2924
3228
  function formatSize2(bytes) {
2925
3229
  if (bytes < 1024) return `${bytes}B`;
2926
3230
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
2927
3231
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
2928
3232
  }
2929
- var fileListCommand = new import_commander27.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) => {
3233
+ var fileListCommand = new import_commander28.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) => {
2930
3234
  const globalOpts = fileListCommand.optsWithGlobals();
2931
3235
  const config = await getConfigOrThrow();
2932
3236
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -2954,10 +3258,10 @@ var fileListCommand = new import_commander27.Command("list").description("\uC5C5
2954
3258
  });
2955
3259
 
2956
3260
  // src/commands/post/file/download.ts
2957
- var import_commander28 = require("commander");
3261
+ var import_commander29 = require("commander");
2958
3262
  var import_promises9 = require("fs/promises");
2959
3263
  var import_node_path6 = require("path");
2960
- var fileDownloadCommand = new import_commander28.Command("download").description("\uCCA8\uBD80\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (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("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("-o, --output <dir>", "\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC", ".").action(async (arg1, arg2, arg3, opts) => {
3264
+ var fileDownloadCommand = new import_commander29.Command("download").description("\uCCA8\uBD80\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (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("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("-o, --output <dir>", "\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC", ".").action(async (arg1, arg2, arg3, opts) => {
2961
3265
  const config = await getConfigOrThrow();
2962
3266
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
2963
3267
  let projectArg;
@@ -3012,10 +3316,10 @@ var fileDownloadCommand = new import_commander28.Command("download").description
3012
3316
  });
3013
3317
 
3014
3318
  // src/commands/post/file/download-all.ts
3015
- var import_commander29 = require("commander");
3319
+ var import_commander30 = require("commander");
3016
3320
  var import_promises10 = require("fs/promises");
3017
3321
  var import_node_path7 = require("path");
3018
- var fileDownloadAllCommand = new import_commander29.Command("download-all").description("\uC5C5\uBB34\uC758 \uBAA8\uB4E0 \uCCA8\uBD80\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC").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)").option("-o, --output <dir>", "\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC", ".").action(async (project, postNumberStr, opts) => {
3322
+ var fileDownloadAllCommand = new import_commander30.Command("download-all").description("\uC5C5\uBB34\uC758 \uBAA8\uB4E0 \uCCA8\uBD80\uD30C\uC77C \uB2E4\uC6B4\uB85C\uB4DC").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)").option("-o, --output <dir>", "\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC", ".").action(async (project, postNumberStr, opts) => {
3019
3323
  const config = await getConfigOrThrow();
3020
3324
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
3021
3325
  const spinner = startSpinner("\uCCA8\uBD80\uD30C\uC77C \uBAA9\uB85D \uC870\uD68C \uC911...");
@@ -3048,9 +3352,9 @@ var fileDownloadAllCommand = new import_commander29.Command("download-all").desc
3048
3352
  });
3049
3353
 
3050
3354
  // src/commands/post/file/upload.ts
3051
- var import_commander30 = require("commander");
3355
+ var import_commander31 = require("commander");
3052
3356
  var import_node_path8 = require("path");
3053
- var fileUploadCommand = new import_commander30.Command("upload").description("\uCCA8\uBD80\uD30C\uC77C \uC5C5\uB85C\uB4DC").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\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("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
3357
+ var fileUploadCommand = new import_commander31.Command("upload").description("\uCCA8\uBD80\uD30C\uC77C \uC5C5\uB85C\uB4DC").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\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("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
3054
3358
  const globalOpts = fileUploadCommand.optsWithGlobals();
3055
3359
  const config = await getConfigOrThrow();
3056
3360
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3111,8 +3415,8 @@ var fileUploadCommand = new import_commander30.Command("upload").description("\u
3111
3415
  });
3112
3416
 
3113
3417
  // src/commands/post/file/delete.ts
3114
- var import_commander31 = require("commander");
3115
- var fileDeleteCommand = new import_commander31.Command("delete").description("\uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C 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("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
3418
+ var import_commander32 = require("commander");
3419
+ var fileDeleteCommand = new import_commander32.Command("delete").description("\uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C 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("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
3116
3420
  const config = await getConfigOrThrow();
3117
3421
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
3118
3422
  let projectArg;
@@ -3165,7 +3469,7 @@ var fileDeleteCommand = new import_commander31.Command("delete").description("\u
3165
3469
  });
3166
3470
 
3167
3471
  // src/commands/wiki/list.ts
3168
- var import_commander32 = require("commander");
3472
+ var import_commander33 = require("commander");
3169
3473
 
3170
3474
  // src/formatters/wiki.ts
3171
3475
  function formatWikiList(wikis, opts) {
@@ -3208,7 +3512,7 @@ function formatWikiPageDetail(page, opts) {
3208
3512
  }
3209
3513
 
3210
3514
  // src/commands/wiki/list.ts
3211
- var wikiListCommand = new import_commander32.Command("list").description("\uC704\uD0A4 \uBAA9\uB85D \uC870\uD68C").option("--page <number>", "\uD398\uC774\uC9C0 \uBC88\uD638", "0").option("--size <number>", "\uD398\uC774\uC9C0 \uD06C\uAE30", "20").action(async (opts) => {
3515
+ var wikiListCommand = new import_commander33.Command("list").description("\uC704\uD0A4 \uBAA9\uB85D \uC870\uD68C").option("--page <number>", "\uD398\uC774\uC9C0 \uBC88\uD638", "0").option("--size <number>", "\uD398\uC774\uC9C0 \uD06C\uAE30", "20").action(async (opts) => {
3212
3516
  const globalOpts = wikiListCommand.optsWithGlobals();
3213
3517
  const config = await getConfigOrThrow();
3214
3518
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3222,7 +3526,7 @@ var wikiListCommand = new import_commander32.Command("list").description("\uC704
3222
3526
  });
3223
3527
 
3224
3528
  // src/commands/wiki/pages.ts
3225
- var import_commander33 = require("commander");
3529
+ var import_commander34 = require("commander");
3226
3530
 
3227
3531
  // src/resolvers/wiki.ts
3228
3532
  async function resolveWiki(client, projectCode) {
@@ -3266,7 +3570,7 @@ async function resolveWikiHomePageId(client, wikiId) {
3266
3570
  }
3267
3571
 
3268
3572
  // src/commands/wiki/pages.ts
3269
- var wikiPagesCommand = new import_commander33.Command("pages").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uBAA9\uB85D \uC870\uD68C").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--parent <page-id>", "\uBD80\uBAA8 \uD398\uC774\uC9C0 ID").action(async (project, opts) => {
3573
+ var wikiPagesCommand = new import_commander34.Command("pages").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uBAA9\uB85D \uC870\uD68C").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--parent <page-id>", "\uBD80\uBAA8 \uD398\uC774\uC9C0 ID").action(async (project, opts) => {
3270
3574
  const globalOpts = wikiPagesCommand.optsWithGlobals();
3271
3575
  const config = await getConfigOrThrow();
3272
3576
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3278,8 +3582,8 @@ var wikiPagesCommand = new import_commander33.Command("pages").description("\uC7
3278
3582
  });
3279
3583
 
3280
3584
  // src/commands/wiki/page-get.ts
3281
- var import_commander34 = require("commander");
3282
- var wikiPageGetCommand = new import_commander34.Command("get").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC0C1\uC138 \uC870\uD68C").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").argument("<page-id>", "\uD398\uC774\uC9C0 ID").action(async (project, pageId) => {
3585
+ var import_commander35 = require("commander");
3586
+ var wikiPageGetCommand = new import_commander35.Command("get").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC0C1\uC138 \uC870\uD68C").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").argument("<page-id>", "\uD398\uC774\uC9C0 ID").action(async (project, pageId) => {
3283
3587
  const globalOpts = wikiPageGetCommand.optsWithGlobals();
3284
3588
  const config = await getConfigOrThrow();
3285
3589
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3291,8 +3595,8 @@ var wikiPageGetCommand = new import_commander34.Command("get").description("\uC7
3291
3595
  });
3292
3596
 
3293
3597
  // src/commands/wiki/page-create.ts
3294
- var import_commander35 = require("commander");
3295
- var wikiPageCreateCommand = new import_commander35.Command("create").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").requiredOption("--title <title>", "\uD398\uC774\uC9C0 \uC81C\uBAA9").option("--parent <page-id>", "\uBD80\uBAA8 \uD398\uC774\uC9C0 ID").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").action(async (project, opts) => {
3598
+ var import_commander36 = require("commander");
3599
+ var wikiPageCreateCommand = new import_commander36.Command("create").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").requiredOption("--title <title>", "\uD398\uC774\uC9C0 \uC81C\uBAA9").option("--parent <page-id>", "\uBD80\uBAA8 \uD398\uC774\uC9C0 ID").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").action(async (project, opts) => {
3296
3600
  const globalOpts = wikiPageCreateCommand.optsWithGlobals();
3297
3601
  const config = await getConfigOrThrow();
3298
3602
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3317,8 +3621,8 @@ var wikiPageCreateCommand = new import_commander35.Command("create").description
3317
3621
  });
3318
3622
 
3319
3623
  // src/commands/wiki/page-edit.ts
3320
- var import_commander36 = require("commander");
3321
- var wikiPageEditCommand = new import_commander36.Command("edit").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC218\uC815 (\uD50C\uB798\uADF8 \uC5C6\uC73C\uBA74 $EDITOR)").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").argument("<page-id>", "\uD398\uC774\uC9C0 ID").option("--title <title>", "\uD398\uC774\uC9C0 \uC81C\uBAA9 (\uC9C0\uC815 \uC2DC $EDITOR \uC0DD\uB7B5)").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").action(async (project, pageId, opts) => {
3624
+ var import_commander37 = require("commander");
3625
+ var wikiPageEditCommand = new import_commander37.Command("edit").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC218\uC815 (\uD50C\uB798\uADF8 \uC5C6\uC73C\uBA74 $EDITOR)").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").argument("<page-id>", "\uD398\uC774\uC9C0 ID").option("--title <title>", "\uD398\uC774\uC9C0 \uC81C\uBAA9 (\uC9C0\uC815 \uC2DC $EDITOR \uC0DD\uB7B5)").option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").action(async (project, pageId, opts) => {
3322
3626
  const config = await getConfigOrThrow();
3323
3627
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
3324
3628
  const hasTitle = opts.title != null;
@@ -3371,10 +3675,10 @@ var wikiPageEditCommand = new import_commander36.Command("edit").description("\u
3371
3675
  });
3372
3676
 
3373
3677
  // src/commands/member/index.ts
3374
- var import_commander40 = require("commander");
3678
+ var import_commander41 = require("commander");
3375
3679
 
3376
3680
  // src/commands/member/get.ts
3377
- var import_commander37 = require("commander");
3681
+ var import_commander38 = require("commander");
3378
3682
 
3379
3683
  // src/formatters/member.ts
3380
3684
  function formatMemberDetail(member, opts) {
@@ -3420,7 +3724,7 @@ function formatMemberSearchResults(members, opts) {
3420
3724
  }
3421
3725
 
3422
3726
  // src/commands/member/get.ts
3423
- var memberGetCommand = new import_commander37.Command("get").description("\uBA64\uBC84 \uC0C1\uC138 \uC815\uBCF4 \uC870\uD68C (organizationMemberId)").argument("<member-id>", "\uC870\uD68C\uD560 organization member ID").action(async (memberId) => {
3727
+ var memberGetCommand = new import_commander38.Command("get").description("\uBA64\uBC84 \uC0C1\uC138 \uC815\uBCF4 \uC870\uD68C (organizationMemberId)").argument("<member-id>", "\uC870\uD68C\uD560 organization member ID").action(async (memberId) => {
3424
3728
  const globalOpts = memberGetCommand.optsWithGlobals();
3425
3729
  const config = await getConfigOrThrow();
3426
3730
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3429,8 +3733,8 @@ var memberGetCommand = new import_commander37.Command("get").description("\uBA64
3429
3733
  });
3430
3734
 
3431
3735
  // src/commands/member/list.ts
3432
- var import_commander38 = require("commander");
3433
- var memberListCommand = new import_commander38.Command("list").description("\uD504\uB85C\uC81D\uD2B8 \uBA64\uBC84 \uBAA9\uB85D").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").action(async (project) => {
3736
+ var import_commander39 = require("commander");
3737
+ var memberListCommand = new import_commander39.Command("list").description("\uD504\uB85C\uC81D\uD2B8 \uBA64\uBC84 \uBAA9\uB85D").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").action(async (project) => {
3434
3738
  const globalOpts = memberListCommand.optsWithGlobals();
3435
3739
  const config = await getConfigOrThrow();
3436
3740
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -3445,8 +3749,8 @@ var memberListCommand = new import_commander38.Command("list").description("\uD5
3445
3749
  });
3446
3750
 
3447
3751
  // src/commands/member/search.ts
3448
- var import_commander39 = require("commander");
3449
- var memberSearchCommand = new import_commander39.Command("search").description("organization \uC804\uCCB4\uC5D0\uC11C \uBA64\uBC84 \uAC80\uC0C9").argument("[keyword]", "\uC774\uB984 \uAC80\uC0C9\uC5B4 (--email/--user-code \uBBF8\uC9C0\uC815 \uC2DC name\uC73C\uB85C \uAC80\uC0C9)").option("--email <email>", "\uC678\uBD80 \uC774\uBA54\uC77C (\uCF64\uB9C8 \uAD6C\uBD84 \uAC00\uB2A5, exact match)").option("--user-code <code>", "\uC0AC\uBC88 like \uAC80\uC0C9").option("--user-code-exact <code>", "\uC0AC\uBC88 exact match").option("--page <n>", "\uD398\uC774\uC9C0 (\uAE30\uBCF8 0)", "0").option("--size <n>", "\uD398\uC774\uC9C0 \uD06C\uAE30 (\uAE30\uBCF8 20, max 100)", "20").action(async (keyword, opts) => {
3752
+ var import_commander40 = require("commander");
3753
+ var memberSearchCommand = new import_commander40.Command("search").description("organization \uC804\uCCB4\uC5D0\uC11C \uBA64\uBC84 \uAC80\uC0C9").argument("[keyword]", "\uC774\uB984 \uAC80\uC0C9\uC5B4 (--email/--user-code \uBBF8\uC9C0\uC815 \uC2DC name\uC73C\uB85C \uAC80\uC0C9)").option("--email <email>", "\uC678\uBD80 \uC774\uBA54\uC77C (\uCF64\uB9C8 \uAD6C\uBD84 \uAC00\uB2A5, exact match)").option("--user-code <code>", "\uC0AC\uBC88 like \uAC80\uC0C9").option("--user-code-exact <code>", "\uC0AC\uBC88 exact match").option("--page <n>", "\uD398\uC774\uC9C0 (\uAE30\uBCF8 0)", "0").option("--size <n>", "\uD398\uC774\uC9C0 \uD06C\uAE30 (\uAE30\uBCF8 20, max 100)", "20").action(async (keyword, opts) => {
3450
3754
  const globalOpts = memberSearchCommand.optsWithGlobals();
3451
3755
  const filterCount = [keyword, opts.email, opts.userCode, opts.userCodeExact].filter(Boolean).length;
3452
3756
  if (filterCount === 0) {
@@ -3479,13 +3783,13 @@ var memberSearchCommand = new import_commander39.Command("search").description("
3479
3783
  });
3480
3784
 
3481
3785
  // src/commands/member/index.ts
3482
- var memberCommand = new import_commander40.Command("member").description("Dooray \uBA64\uBC84 \uC870\uD68C");
3786
+ var memberCommand = new import_commander41.Command("member").description("Dooray \uBA64\uBC84 \uC870\uD68C");
3483
3787
  memberCommand.addCommand(memberGetCommand);
3484
3788
  memberCommand.addCommand(memberListCommand);
3485
3789
  memberCommand.addCommand(memberSearchCommand);
3486
3790
 
3487
3791
  // src/commands/mail/list.ts
3488
- var import_commander41 = require("commander");
3792
+ var import_commander42 = require("commander");
3489
3793
 
3490
3794
  // src/api/imapClient.ts
3491
3795
  var import_imapflow = require("imapflow");
@@ -3526,7 +3830,7 @@ async function listMails(config, opts) {
3526
3830
  if (opts.search) query.subject = opts.search;
3527
3831
  if (Object.keys(query).length === 0) query.all = true;
3528
3832
  const uids = await client.search(query, { uid: true });
3529
- if (uids.length === 0) return [];
3833
+ if (!Array.isArray(uids) || uids.length === 0) return [];
3530
3834
  const sorted = uids.sort((a, b) => b - a).slice(0, limit);
3531
3835
  const uidSet = sorted.join(",");
3532
3836
  const messages = [];
@@ -3535,15 +3839,17 @@ async function listMails(config, opts) {
3535
3839
  flags: true,
3536
3840
  envelope: true
3537
3841
  }, { uid: true })) {
3842
+ const { envelope, flags } = msg;
3843
+ if (!envelope || !flags) continue;
3538
3844
  messages.push({
3539
3845
  uid: msg.uid,
3540
- subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
3541
- from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
3542
- to: (msg.envelope.to ?? []).map(
3846
+ subject: envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
3847
+ from: envelope.from?.[0] ? `${envelope.from[0].name || ""} <${envelope.from[0].address || ""}>` : "(unknown)",
3848
+ to: (envelope.to ?? []).map(
3543
3849
  (t) => `${t.name || ""} <${t.address || ""}>`
3544
3850
  ),
3545
- date: msg.envelope.date ?? null,
3546
- isRead: msg.flags.has("\\Seen")
3851
+ date: envelope.date ?? null,
3852
+ isRead: flags.has("\\Seen")
3547
3853
  });
3548
3854
  }
3549
3855
  messages.sort((a, b) => b.uid - a.uid);
@@ -3570,17 +3876,24 @@ async function getMail(config, uid) {
3570
3876
  if (!msg) {
3571
3877
  throw new DoorayCliError(`\uBA54\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: UID ${uid}`, 1);
3572
3878
  }
3573
- const parsed = await (0, import_mailparser.simpleParser)(msg.source);
3574
- const body = parsed.text ?? parsed.html ?? "(\uBCF8\uBB38 \uC5C6\uC74C)";
3879
+ const { envelope, flags, source } = msg;
3880
+ if (!envelope || !flags || !source) {
3881
+ throw new DoorayCliError(
3882
+ `\uBA54\uC77C \uBA54\uD0C0\uB370\uC774\uD130\uAC00 \uBD88\uC644\uC804\uD569\uB2C8\uB2E4 (UID ${uid}): envelope/flags/source \uB204\uB77D`,
3883
+ 1
3884
+ );
3885
+ }
3886
+ const parsed = await (0, import_mailparser.simpleParser)(source);
3887
+ const body = parsed.text || (parsed.html || "") || "(\uBCF8\uBB38 \uC5C6\uC74C)";
3575
3888
  return {
3576
3889
  uid: msg.uid,
3577
- subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
3578
- from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
3579
- to: (msg.envelope.to ?? []).map(
3890
+ subject: envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
3891
+ from: envelope.from?.[0] ? `${envelope.from[0].name || ""} <${envelope.from[0].address || ""}>` : "(unknown)",
3892
+ to: (envelope.to ?? []).map(
3580
3893
  (t) => `${t.name || ""} <${t.address || ""}>`
3581
3894
  ),
3582
- date: msg.envelope.date ?? null,
3583
- isRead: msg.flags.has("\\Seen"),
3895
+ date: envelope.date ?? null,
3896
+ isRead: flags.has("\\Seen"),
3584
3897
  body
3585
3898
  };
3586
3899
  } finally {
@@ -3593,7 +3906,7 @@ async function getMail(config, uid) {
3593
3906
 
3594
3907
  // src/commands/mail/list.ts
3595
3908
  var import_chalk5 = __toESM(require("chalk"));
3596
- var mailListCommand = new import_commander41.Command("list").description("\uBA54\uC77C \uBAA9\uB85D \uC870\uD68C").option("--unread", "\uC548\uC77D\uC740 \uBA54\uC77C\uB9CC").option("--search <keyword>", "\uC81C\uBAA9 \uAC80\uC0C9").option("--size <number>", "\uC870\uD68C \uAC1C\uC218", "20").action(async (opts) => {
3909
+ var mailListCommand = new import_commander42.Command("list").description("\uBA54\uC77C \uBAA9\uB85D \uC870\uD68C").option("--unread", "\uC548\uC77D\uC740 \uBA54\uC77C\uB9CC").option("--search <keyword>", "\uC81C\uBAA9 \uAC80\uC0C9").option("--size <number>", "\uC870\uD68C \uAC1C\uC218", "20").action(async (opts) => {
3597
3910
  const globalOpts = mailListCommand.optsWithGlobals();
3598
3911
  const config = await getConfigOrThrow();
3599
3912
  startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
@@ -3625,9 +3938,9 @@ var mailListCommand = new import_commander41.Command("list").description("\uBA54
3625
3938
  });
3626
3939
 
3627
3940
  // src/commands/mail/get.ts
3628
- var import_commander42 = require("commander");
3941
+ var import_commander43 = require("commander");
3629
3942
  var import_chalk6 = __toESM(require("chalk"));
3630
- var mailGetCommand = new import_commander42.Command("get").description("\uBA54\uC77C \uC0C1\uC138 \uC870\uD68C").argument("<uid>", "\uBA54\uC77C UID").action(async (uid) => {
3943
+ var mailGetCommand = new import_commander43.Command("get").description("\uBA54\uC77C \uC0C1\uC138 \uC870\uD68C").argument("<uid>", "\uBA54\uC77C UID").action(async (uid) => {
3631
3944
  const globalOpts = mailGetCommand.optsWithGlobals();
3632
3945
  const config = await getConfigOrThrow();
3633
3946
  startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
@@ -3658,7 +3971,7 @@ ${mail.body}
3658
3971
  });
3659
3972
 
3660
3973
  // src/commands/mail/send.ts
3661
- var import_commander43 = require("commander");
3974
+ var import_commander44 = require("commander");
3662
3975
  var import_promises11 = require("fs/promises");
3663
3976
 
3664
3977
  // src/api/smtpClient.ts
@@ -3708,7 +4021,7 @@ async function sendMail(config, opts) {
3708
4021
  }
3709
4022
 
3710
4023
  // src/commands/mail/send.ts
3711
- var mailSendCommand = new import_commander43.Command("send").description("\uBA54\uC77C \uBC1C\uC1A1").requiredOption("--to <addresses...>", "\uBC1B\uB294\uC0AC\uB78C \uC774\uBA54\uC77C (\uBCF5\uC218 \uAC00\uB2A5)").requiredOption("--subject <title>", "\uC81C\uBAA9").option("--body <text>", "\uBCF8\uBB38").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--bcc <addresses...>", "\uC228\uC740\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (opts) => {
4024
+ var mailSendCommand = new import_commander44.Command("send").description("\uBA54\uC77C \uBC1C\uC1A1").requiredOption("--to <addresses...>", "\uBC1B\uB294\uC0AC\uB78C \uC774\uBA54\uC77C (\uBCF5\uC218 \uAC00\uB2A5)").requiredOption("--subject <title>", "\uC81C\uBAA9").option("--body <text>", "\uBCF8\uBB38").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--bcc <addresses...>", "\uC228\uC740\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (opts) => {
3712
4025
  const globalOpts = mailSendCommand.optsWithGlobals();
3713
4026
  const config = await getConfigOrThrow();
3714
4027
  let body = opts.body ?? "";
@@ -3743,7 +4056,7 @@ var mailSendCommand = new import_commander43.Command("send").description("\uBA54
3743
4056
  });
3744
4057
 
3745
4058
  // src/commands/mail/reply.ts
3746
- var import_commander44 = require("commander");
4059
+ var import_commander45 = require("commander");
3747
4060
  var import_promises12 = require("fs/promises");
3748
4061
  var import_imapflow2 = require("imapflow");
3749
4062
  async function getMessageId(config, uid) {
@@ -3763,7 +4076,8 @@ async function getMessageId(config, uid) {
3763
4076
  uid: true,
3764
4077
  envelope: true
3765
4078
  }, { uid: true });
3766
- return msg?.envelope.messageId ?? null;
4079
+ if (!msg) return null;
4080
+ return msg.envelope?.messageId ?? null;
3767
4081
  } finally {
3768
4082
  lock.release();
3769
4083
  }
@@ -3771,7 +4085,7 @@ async function getMessageId(config, uid) {
3771
4085
  await client.logout();
3772
4086
  }
3773
4087
  }
3774
- var mailReplyCommand = new import_commander44.Command("reply").description("\uBA54\uC77C \uB2F5\uC7A5").argument("<uid>", "\uC6D0\uBCF8 \uBA54\uC77C UID").option("--body <text>", "\uB2F5\uC7A5 \uBCF8\uBB38").option("--body-file <path>", "\uB2F5\uC7A5 \uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (uid, opts) => {
4088
+ var mailReplyCommand = new import_commander45.Command("reply").description("\uBA54\uC77C \uB2F5\uC7A5").argument("<uid>", "\uC6D0\uBCF8 \uBA54\uC77C UID").option("--body <text>", "\uB2F5\uC7A5 \uBCF8\uBB38").option("--body-file <path>", "\uB2F5\uC7A5 \uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (uid, opts) => {
3775
4089
  const globalOpts = mailReplyCommand.optsWithGlobals();
3776
4090
  const config = await getConfigOrThrow();
3777
4091
  let body = opts.body ?? "";
@@ -3812,7 +4126,7 @@ var mailReplyCommand = new import_commander44.Command("reply").description("\uBA
3812
4126
  });
3813
4127
 
3814
4128
  // src/commands/feedback.ts
3815
- var import_commander45 = require("commander");
4129
+ var import_commander46 = require("commander");
3816
4130
  var import_node_child_process2 = require("child_process");
3817
4131
  var import_node_util = require("util");
3818
4132
  var import_promises15 = require("fs/promises");
@@ -3923,7 +4237,7 @@ async function readBody(opts) {
3923
4237
  if (opts.bodyFile) return await (0, import_promises15.readFile)(opts.bodyFile, "utf-8");
3924
4238
  return void 0;
3925
4239
  }
3926
- var feedbackCommand = new import_commander45.Command("feedback").description("dooray-cli\uC5D0 \uB300\uD55C GitHub issue \uB4F1\uB85D (gh CLI \uC704\uC784)").option("--title <text>", "\uC774\uC288 \uC81C\uBAA9 (\uC5C6\uC73C\uBA74 \uC778\uD130\uB799\uD2F0\uBE0C)").option("--body <text>", "\uC774\uC288 \uBCF8\uBB38").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option(
4240
+ var feedbackCommand = new import_commander46.Command("feedback").description("dooray-cli\uC5D0 \uB300\uD55C GitHub issue \uB4F1\uB85D (gh CLI \uC704\uC784)").option("--title <text>", "\uC774\uC288 \uC81C\uBAA9 (\uC5C6\uC73C\uBA74 \uC778\uD130\uB799\uD2F0\uBE0C)").option("--body <text>", "\uC774\uC288 \uBCF8\uBB38").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option(
3927
4241
  "--label <name>",
3928
4242
  "\uB77C\uBCA8 (\uBC18\uBCF5 \uAC00\uB2A5)",
3929
4243
  (value, prev) => [...prev, value],
@@ -4086,8 +4400,8 @@ function sanitizeArgv(argv) {
4086
4400
  }
4087
4401
 
4088
4402
  // src/index.ts
4089
- var program = new import_commander46.Command();
4090
- program.name("dooray").description("Dooray REST API CLI").version("0.6.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "ID\uB9CC \uCD9C\uB825").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654");
4403
+ var program = new import_commander47.Command();
4404
+ program.name("dooray").description("Dooray REST API CLI").version("0.7.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "ID\uB9CC \uCD9C\uB825").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654");
4091
4405
  program.hook("preAction", () => {
4092
4406
  const opts = program.opts();
4093
4407
  if (opts.color === false || process.env.NO_COLOR) {
@@ -4097,13 +4411,13 @@ program.hook("preAction", () => {
4097
4411
  setQuiet(true);
4098
4412
  }
4099
4413
  });
4100
- var projectCommand = new import_commander46.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
4414
+ var projectCommand = new import_commander47.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
4101
4415
  projectCommand.addCommand(projectListCommand);
4102
4416
  projectCommand.addCommand(projectMembersCommand);
4103
4417
  projectCommand.addCommand(projectWorkflowsCommand);
4104
4418
  projectCommand.addCommand(projectGroupsCommand);
4105
4419
  projectCommand.addCommand(projectTagsCommand);
4106
- var postCommand = new import_commander46.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
4420
+ var postCommand = new import_commander47.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
4107
4421
  postCommand.addCommand(postListCommand);
4108
4422
  postCommand.addCommand(postSearchCommand);
4109
4423
  postCommand.addCommand(postGetCommand);
@@ -4111,30 +4425,31 @@ postCommand.addCommand(postEditCommand);
4111
4425
  postCommand.addCommand(postCreateCommand);
4112
4426
  postCommand.addCommand(postDoneCommand);
4113
4427
  postCommand.addCommand(postWorkflowCommand);
4114
- var commentCommand = new import_commander46.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
4428
+ var commentCommand = new import_commander47.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
4115
4429
  commentCommand.addCommand(commentListCommand);
4116
4430
  commentCommand.addCommand(commentLatestCommand);
4431
+ commentCommand.addCommand(commentGetCommand);
4117
4432
  commentCommand.addCommand(commentAddCommand);
4118
4433
  commentCommand.addCommand(commentEditCommand);
4119
4434
  commentCommand.addCommand(commentDeleteCommand);
4120
4435
  commentCommand.addCommand(commentFileCommand);
4121
4436
  postCommand.addCommand(commentCommand);
4122
- var fileCommand = new import_commander46.Command("file").description("\uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
4437
+ var fileCommand = new import_commander47.Command("file").description("\uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
4123
4438
  fileCommand.addCommand(fileListCommand);
4124
4439
  fileCommand.addCommand(fileDownloadCommand);
4125
4440
  fileCommand.addCommand(fileDownloadAllCommand);
4126
4441
  fileCommand.addCommand(fileUploadCommand);
4127
4442
  fileCommand.addCommand(fileDeleteCommand);
4128
4443
  postCommand.addCommand(fileCommand);
4129
- var wikiCommand = new import_commander46.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
4444
+ var wikiCommand = new import_commander47.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
4130
4445
  wikiCommand.addCommand(wikiListCommand);
4131
4446
  wikiCommand.addCommand(wikiPagesCommand);
4132
- var wikiPageCommand = new import_commander46.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
4447
+ var wikiPageCommand = new import_commander47.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
4133
4448
  wikiPageCommand.addCommand(wikiPageGetCommand);
4134
4449
  wikiPageCommand.addCommand(wikiPageCreateCommand);
4135
4450
  wikiPageCommand.addCommand(wikiPageEditCommand);
4136
4451
  wikiCommand.addCommand(wikiPageCommand);
4137
- var mailCommand = new import_commander46.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
4452
+ var mailCommand = new import_commander47.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
4138
4453
  mailCommand.addCommand(mailListCommand);
4139
4454
  mailCommand.addCommand(mailGetCommand);
4140
4455
  mailCommand.addCommand(mailSendCommand);